Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multi-arch support #4

Merged
merged 12 commits into from
May 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 0 additions & 37 deletions .github/workflows/build-and-push-image.yml

This file was deleted.

69 changes: 69 additions & 0 deletions .github/workflows/build-test-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: build, test and push multi-arch Docker container images

on:
pull_request:
branches: main
paths:
- Dockerfile
- .github/workflows/build-test-and-push.yml
push:
branches: main
paths:
- Dockerfile
- .github/workflows/build-test-and-push.yml

env:
GITHUB_SHA: ${{ github.sha }}
DOCKERHUB_TARGET_REPO: kathoef/dfjghsdfg

jobs:
build_test_push:
runs-on: ubuntu-latest
steps:
- name: Specify tags
run: |
CALVER="$( date -u '+%Y.%m.%d' )"
SHA7="${GITHUB_SHA::7}"
CALVER_TARGET=${DOCKERHUB_TARGET_REPO}:v${CALVER}-${SHA7}
LATEST_TARGET=${DOCKERHUB_TARGET_REPO}:latest
echo "CALVER_TARGET=${CALVER_TARGET}" >> $GITHUB_ENV
echo "LATEST_TARGET=${LATEST_TARGET}" >> $GITHUB_ENV
- name: Set up QEMU
if: ${{ startsWith(github.ref, 'refs/heads/main')}}
uses: docker/setup-qemu-action@v2
- name: Set up Buildx
if: ${{ startsWith(github.ref, 'refs/heads/main')}}
uses: docker/setup-buildx-action@v2
- name: Show available Buildx platforms
if: ${{ startsWith(github.ref, 'refs/heads/main')}}
run: docker buildx ls
- name: Checkout Github repository
uses: actions/checkout@v2
- name: Build default arch Docker image
uses: docker/build-push-action@v3
with:
load: true # publish as local Docker image
tags: "${{env.CALVER_TARGET}}, ${{env.LATEST_TARGET}}"
- name: "Test default arch Docker image: singularity pull"
run: |
docker run -v $PWD:/output --rm "${{env.LATEST_TARGET}}" singularity pull alpine_latest.sif docker://alpine:latest
ls && test -f alpine_latest.sif && rm alpine_latest.sif
- name: "Test default arch Docker image: singularity build"
run: |
echo 'FROM alpine:latest' > Dockerfile
docker build -f Dockerfile -t local/from_local_build_workflow .
docker run -v $PWD:/output -v /var/run/docker.sock:/var/run/docker.sock:ro --rm "${{env.LATEST_TARGET}}" singularity build local.sif docker-daemon://local/from_local_build_workflow:latest
ls && test -f local.sif && rm local.sif
- name: Login to DockerHub
if: ${{ startsWith(github.ref, 'refs/heads/main')}}
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_LOGIN_USER }}
password: ${{ secrets.DOCKERHUB_LOGIN_TOKEN }}
- name: Build and push the multi-arch Docker images
if: ${{ startsWith(github.ref, 'refs/heads/main')}}
uses: docker/build-push-action@v3
with:
platforms: linux/amd64, linux/arm64
tags: "${{env.CALVER_TARGET}}, ${{env.LATEST_TARGET}}"
push: true
27 changes: 0 additions & 27 deletions .github/workflows/test-docker-image.yml

This file was deleted.

27 changes: 27 additions & 0 deletions .github/workflows/test-dockerhub-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: test default architecture Docker image

on:
push:
branches: main
paths-ignore: Dockerfile
workflow_dispatch:

env:
TESTING_IMAGE: kathoef/docker2singularity:latest

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Pull default architecture Docker image
run: docker pull ${{env.TESTING_IMAGE}}
- name: Test singularity pull
run: |
docker run -v $PWD:/output --rm ${{env.TESTING_IMAGE}} singularity pull alpine_latest.sif docker://alpine:latest
test -f alpine_latest.sif && rm alpine_latest.sif
- name: Test singularity build
run: |
echo 'FROM alpine:latest' > Dockerfile
docker build -f Dockerfile -t local/from_local_build_workflow .
docker run -v $PWD:/output -v /var/run/docker.sock:/var/run/docker.sock:ro --rm ${{env.TESTING_IMAGE}} singularity build local.sif docker-daemon://local/from_local_build_workflow:latest
test -f local.sif && rm local.sif
65 changes: 35 additions & 30 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,65 +1,70 @@
FROM ubuntu:20.10 AS base
FROM ubuntu:22.04 AS base

RUN apt-get update && apt-get install --yes --no-install-recommends \
# support singularity build/pull workflows
RUN apt update && apt install --yes --no-install-recommends \
# add singularity build and singularity pull OS dependencies
ca-certificates squashfs-tools \
&& rm -rf /var/lib/apt/lists/*
&& apt-get clean && rm -rf /var/lib/apt/lists/*

FROM base AS builder

# https://sylabs.io/guides/3.7/admin-guide/installation.html#installation-on-linux
# https://apptainer.org/docs/admin/main/installation.html#install-from-source

RUN apt-get update && apt-get install --yes --no-install-recommends \
RUN apt update && apt install --yes --no-install-recommends \
build-essential \
libssl-dev \
uuid-dev \
libgpgme11-dev \
libgpgme-dev \
squashfs-tools \
libseccomp-dev \
wget ca-certificates \
wget \
pkg-config \
git \
cryptsetup \
&& rm -rf /var/lib/apt/lists/*
cryptsetup-bin

RUN export VERSION=1.16.4 \
&& wget --quiet https://golang.org/dl/go${VERSION}.linux-amd64.tar.gz \
&& tar -C /usr/local -xzf go${VERSION}.linux-amd64.tar.gz \
&& rm /go${VERSION}.linux-amd64.tar.gz
ARG TARGETPLATFORM
ARG BUILDPLATFORM

SHELL ["/bin/bash", "-c"]

RUN export VERSION=1.18.2 \
&& export ARCH=linux-${TARGETPLATFORM#'linux/'} \
&& wget --quiet https://go.dev/dl/go${VERSION}.${ARCH}.tar.gz \
&& tar -C /usr/local -xzf go${VERSION}.${ARCH}.tar.gz

ENV PATH=$PATH:/usr/local/go/bin

RUN export VERSION=3.7.3 \
RUN export VERSION=1.0.2 \
&& cd /tmp \
&& wget --quiet https://github.com/hpcng/singularity/releases/download/v${VERSION}/singularity-${VERSION}.tar.gz \
&& tar -xzf singularity-${VERSION}.tar.gz \
&& cd singularity \
&& ./mconfig --prefix=/singularity \
&& wget --quiet https://github.com/apptainer/apptainer/releases/download/v${VERSION}/apptainer-${VERSION}.tar.gz \
&& tar -xzf apptainer-${VERSION}.tar.gz \
&& mv apptainer-${VERSION} apptainer \
&& cd apptainer \
&& ./mconfig --prefix=/apptainer \
&& make -C builddir \
&& make -C builddir install

FROM base

# Original Singularity information.
# Add Apptainer information.

COPY --from=builder /tmp/singularity/LICENSE.md /singularity/LICENSE.md
COPY --from=builder /tmp/singularity/README.md /singularity/README.md
COPY --from=builder /tmp/apptainer/LICENSE*.md /apptainer/
COPY --from=builder /tmp/apptainer/README.md /apptainer/README.md

# This repository's information.
# Add this Github repository's information.

ADD README.md LICENSE Dockerfile /

# Singularity executable.
# Apptainer executable.

# Full install...
#COPY --from=builder /singularity /singularity
#COPY --from=builder /apptainer /apptainer

# Minimal install... supports singularity pull/build workflows.
COPY --from=builder /singularity/bin/singularity /singularity/bin/singularity
COPY --from=builder /singularity/etc/singularity/singularity.conf /singularity/etc/singularity/singularity.conf
COPY --from=builder /apptainer/bin/apptainer /apptainer/bin/apptainer
COPY --from=builder /apptainer/bin/singularity /apptainer/bin/singularity
COPY --from=builder /apptainer/etc/apptainer/apptainer.conf /apptainer/etc/apptainer/apptainer.conf

# Conveniences.
# Docker image conveniences.

ENV PATH=$PATH:/singularity/bin
ENV PATH=$PATH:/apptainer/bin
RUN mkdir /output
WORKDIR /output
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Katharina Höflich
Copyright (c) 2021-2022 Katharina Höflich

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
62 changes: 40 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
# docker2singularity

[![](https://github.com/kathoef/docker2singularity/actions/workflows/test-docker-image.yml/badge.svg?branch=main)](https://github.com/kathoef/docker2singularity/blob/main/.github/workflows/test-docker-image.yml)
[![](https://github.com/kathoef/docker2singularity/actions/workflows/test-dockerhub-image.yml/badge.svg?branch=main)](https://github.com/kathoef/docker2singularity/blob/main/.github/workflows/test-dockerhub-image.yml)
[![](https://shields.io/docker/image-size/kathoef/docker2singularity/latest)](https://hub.docker.com/r/kathoef/docker2singularity)

This is an alternative implementation of [docker2singularity](https://github.com/singularityhub/docker2singularity) that does not rely on Docker in Docker and having to grant the container host device root capabilities via the `--privileged` flag.
(Which should in general be done only if absolutely necessary, could be considered bad practice, and turned out not to be necessary for the local container build workflow that is described below.)
This is an alternative implementation of [docker2singularity](https://github.com/singularityhub/docker2singularity) that does not rely on Docker in Docker and granting the container full host device root capabilities via the `--privileged` flag.

The Docker image provided here was originally specified for [container image portability tests](https://github.com/ExaESM-WP4/Batch-scheduler-Singularity-bindings/blob/e4be0220f8938b9cc3275267bc44be44e925b3ea/test_image_compatibility/) and to have a fully controllable Singularity pull environment available.
It turned out that my local Docker image Singularity build tasks also worked quite well and only required the Docker socket to be mounted.
(No tinkering with default Docker run privileges necessary.)
(Which should in general be done only if absolutely necessary, could be considered bad practice, and turned out not to be necessary for the local container build workflows described below.)

As I use these Docker-based fully local Singularity container image build pipelines quite often (mainly because CI and/or hub-based workflows add complexity to a single-user project that feels unnecessary and also because I have seen `singularity pull` attempts on the big machines failing) I thought I'd provide a bit more of a structured ground to this workflow here.
## Use cases

Maybe it's useful to others as well, feedback is welcome.
### Singularity build

## Use case

Build a Singularity image from a Docker image that was built locally on your system,
Build a Singularity image from a locally build Docker image,

```
$ ls -l Dockerfile
-rw-rw-r-- 1 kathoef kathoef 58 Mai 15 17:14 Dockerfile
$ docker build -t localhost/test .
$ docker pull kathoef/docker2singularity:latest
$ docker build -f Dockerfile -t localhost/test .
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro -v ${PWD}:/output \
kathoef/docker2singularity singularity build test.sif docker-daemon://localhost/test:latest
```

### Singularity pull

Build a Singularity image from a remotely hosted Docker image,

```
$ docker pull kathoef/docker2singularity:latest
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}:/output \
kathoef/docker2singularity singularity build test.sif docker-daemon://localhost/test:latest
$ docker run --rm -v ${PWD}:/output \
kathoef/docker2singularity singularity pull alpine_latest.sif docker://alpine:latest
```

This was tested on Linux, MacOS Mojave and Windows 10 (w/ Hyper-V backend) and [Docker Desktop](https://www.docker.com/products/docker-desktop) with Docker Engine v20.10.6 installed.
### Compatibility

These workflows were tested on Linux, MacOS Mojave and Windows 10 (w/ Hyper-V backend) and [Docker Desktop](https://www.docker.com/products/docker-desktop) with Docker Engine v20.10.6 installed.

### For Linux
### For Linux hosts

You might want to change the Singularity image's file ownership afterwards,
You might want to fix the Singularity image file ownership after conversion,

```
$ ls -l test.sif
Expand All @@ -44,9 +46,25 @@ $ ls -l test.sif
-rwxr-xr-x 1 kathoef kathoef 2777088 Mai 15 17:19 test.sif
```

## Background information

The Docker image provided here was originally specified for [container image portability tests](https://github.com/ExaESM-WP4/Batch-scheduler-Singularity-bindings/blob/e4be0220f8938b9cc3275267bc44be44e925b3ea/test_image_compatibility/) in order to have a fully controllable Singularity pull environment available.
It turned out that my local Docker image Singularity build tasks also worked quite well and only required the Docker socket to be mounted as read-only.

Since I use these Docker-based local Singularity container image build workflows quite often [^1] I thought I'd provide a bit more of a structured ground to this approach here.

Maybe it happens to be useful to others, feedback is welcome!

[^1]: mainly since CI and/or manual DockerHub-based workflows add complexity to a single-user data analysis project that seems unnecessary and also because I have seen `singularity pull` attempts on e.g. HPC machines failing

## References

* https://github.com/singularityhub/docker2singularity
Singularity/Apptainer,
* https://github.com/singularityhub/docker2singularity (the original!)
* https://sylabs.io/guides/3.7/user-guide/singularity_and_docker.html#locally-available-images-cached-by-docker
* https://sylabs.io/guides/3.7/admin-guide/installation.html#installation-on-linux
* https://github.com/hpcng/singularity
* https://github.com/apptainer/singularity

Multi-architecture build,
* https://docs.docker.com/buildx/working-with-buildx/
* https://github.com/docker/setup-buildx-action#with-qemu
* https://github.com/docker/build-push-action/blob/c5e6528d5ddefc82f682165021e05edf58044bce/docs/advanced/test-before-push.md