diff --git a/.github/workflows/build-and-push.yaml b/.github/workflows/build-and-push.yaml new file mode 100644 index 0000000..64a5bfb --- /dev/null +++ b/.github/workflows/build-and-push.yaml @@ -0,0 +1,95 @@ +name: build-and-push + +on: + pull_request: + push: + branches: + - main + +env: + PLATFORM: "linux/amd64" + +jobs: + docker: + runs-on: ubuntu-latest + + strategy: + matrix: + base: + - "alpine:3.18.5@sha256:d695c3de6fcd8cfe3a6222b0358425d40adfd129a8a47c3416faff1a8aece389" + criu: + - "3.19" + nftables: + - "with-nftables" + - "without-nftables" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Get base image version + id: base-image-version + run: | + base_image_version=$(echo ${{ matrix.base }} | cut -d@ -f1 | tr ':' '-') + tag="avmnusng/criu-${{ matrix.criu }}-${{ matrix.nftables }}:${base_image_version}" + + echo "TAG=${tag}" >> $GITHUB_ENV + echo "TAG_TEST=${tag}-test" >> $GITHUB_ENV + + - name: Build + uses: docker/build-push-action@v5 + with: + context: "." + file: ${{ matrix.nftables }}.Dockerfile + platforms: ${{ env.PLATFORM }} + load: true + tags: ${{ env.TAG_TEST }} + build-args: | + PLATFORM=${{ env.PLATFORM }} + BASE=${{ matrix.base }} + CRIU_VERSION=${{ matrix.criu }} + + - name: Test + run: docker run --rm --privileged ${{ env.TAG_TEST }} criu check + + - name: Test multi-stage build + run: | + cat <> ${{ matrix.nftables }}-multi-stage.Dockerfile + FROM --platform=${{ env.PLATFORM }} ${{ matrix.base }} + COPY --from=${{ env.TAG_TEST }} /criu/ / + EOF + + if [[ $? != 0 ]]; then + exit 0 + fi + + docker build \ + -t ${{ matrix.nftables }}-multi-stage:test \ + -f ${{ matrix.nftables }}-multi-stage.Dockerfile \ + . && \ + + docker run --rm --privileged ${{ matrix.nftables }}-multi-stage:test criu check + + - name: Login to Docker Hub + if: github.event_name == 'push' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + if: github.event_name == 'push' + uses: docker/build-push-action@v5 + with: + context: "." + file: ${{ matrix.nftables }}.Dockerfile + platforms: ${{ env.PLATFORM }} + push: true + tags: ${{ env.TAG }} + build-args: | + PLATFORM=${{ env.PLATFORM }} + BASE=${{ matrix.base }} + CRIU_VERSION=${{ matrix.criu }} diff --git a/README.md b/README.md index 0bde7b0..853dbe6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ -# criu-alpine -CRIU Dockerimage for multi-stage builds +## CRIU Alpine + +Use this dockerimage to add [criu](https://github.com/checkpoint-restore/criu) as +part of multi-stage build. To use in multistage build copy all the contents from +the `/criu` directory to `/` (root) directory. + +Ensure to use same alpine image for the final stage, i.e., if we use `alpine:3.18.5` +with digest `sha256:d695c3de6fcd8cfe3a6222b0358425d40adfd129a8a47c3416faff1a8aece389`, +then: + +```dockerfile +FROM --platform=amd64 alpine:3.18.5@sha256:d695c3de6fcd8cfe3a6222b0358425d40adfd129a8a47c3416faff1a8aece389 + +COPY --from=criu-3.19-with-nftables:alpine-3.18.5 /criu/ / +``` diff --git a/multi-stage-setup.py b/multi-stage-setup.py new file mode 100644 index 0000000..d30e074 --- /dev/null +++ b/multi-stage-setup.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +from pathlib import Path +from shutil import copy2 +from subprocess import run +from sys import exit + +CRIU_ROOT = '/criu' +CRIU_BIN_DIR = f'{CRIU_ROOT}/usr/bin' + + +def setup_criu_root(): + Path(CRIU_ROOT).mkdir(parents=True, exist_ok=False) + Path(CRIU_BIN_DIR).mkdir(parents=True, exist_ok=False) + + if not run(f'cp `which criu` {CRIU_BIN_DIR}', shell=True).returncode == 0: + exit(1) + + +def copy_dep(dep): + dep_dir = str(Path(CRIU_ROOT).joinpath(Path(dep).parent.relative_to('/'))) + Path(dep_dir).mkdir(parents=True, exist_ok=True) + + copy2(dep, dep_dir) + + +if __name__ == '__main__': + setup_criu_root() + + res = run('ldd `which criu`', capture_output=True, shell=True) + if not res.returncode == 0: + exit(1) + + deps = res.stdout.decode().strip().split("\n") + + for dep in deps: + parts = dep.strip().split(' => ') + if len(parts) not in (1, 2): + exit(1) + + part1 = None + part2 = None + + if len(parts) == 1: + part1 = parts[0].split(' (0x')[0] + part2 = part1 + else: + part2 = parts[1].split(' (0x')[0] + part1 = str(Path(part2).parent.joinpath(parts[0])) + + if not part1 or not part2: + exit(1) + + copy_dep(part1) + + if part1 != part2: + copy_dep(part2) diff --git a/with-nftables.Dockerfile b/with-nftables.Dockerfile new file mode 100644 index 0000000..e4385fa --- /dev/null +++ b/with-nftables.Dockerfile @@ -0,0 +1,42 @@ +ARG PLATFORM +ARG BASE + +FROM --platform=$PLATFORM $BASE + +ARG CRIU_VERSION + +RUN apk --update --no-cache add \ + bash \ + build-base \ + coreutils \ + procps \ + gcc \ + git \ + gnutls-dev \ + libaio-dev \ + libcap-dev \ + libnet-dev \ + libnl3-dev \ + nftables \ + nftables-dev \ + pkgconfig \ + protobuf-c-dev \ + protobuf-dev \ + py3-pip \ + py3-protobuf \ + python3 \ + libcap-utils \ + libdrm-dev \ + util-linux + +WORKDIR /tmp + +RUN wget "http://github.com/checkpoint-restore/criu/archive/v${CRIU_VERSION}/criu-${CRIU_VERSION}.tar.gz" && \ + tar -xzf "criu-${CRIU_VERSION}.tar.gz" && \ + cd "criu-${CRIU_VERSION}" && \ + make -j $(nproc) install-criu + +COPY multi-stage-setup.py multi-stage-setup.py + +RUN chmod +x multi-stage-setup.py && \ + ./multi-stage-setup.py diff --git a/without-nftables.Dockerfile b/without-nftables.Dockerfile new file mode 100644 index 0000000..4f5996b --- /dev/null +++ b/without-nftables.Dockerfile @@ -0,0 +1,40 @@ +ARG PLATFORM +ARG BASE + +FROM --platform=$PLATFORM $BASE + +ARG CRIU_VERSION + +RUN apk --update --no-cache add \ + bash \ + build-base \ + coreutils \ + procps \ + gcc \ + git \ + gnutls-dev \ + libaio-dev \ + libcap-dev \ + libnet-dev \ + libnl3-dev \ + pkgconfig \ + protobuf-c-dev \ + protobuf-dev \ + py3-pip \ + py3-protobuf \ + python3 \ + libcap-utils \ + libdrm-dev \ + util-linux + +WORKDIR /tmp + +RUN wget "http://github.com/checkpoint-restore/criu/archive/v${CRIU_VERSION}/criu-${CRIU_VERSION}.tar.gz" && \ + tar -xzf "criu-${CRIU_VERSION}.tar.gz" && \ + cd "criu-${CRIU_VERSION}" && \ + make -j $(nproc) install-criu + +COPY multi-stage-setup.py multi-stage-setup.py + +RUN chmod +x multi-stage-setup.py && \ + ./multi-stage-setup.py