feat: build multi-arch docker images for amd64 and arm64
- using debian as base image
- do cross compile for build arm64 image
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..59a75b5
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+.git/
+config-ui/
+grafana/
+releases/
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a0957a5..bd01c65 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,9 +17,6 @@
name: Build-Images-Push-Docker
env:
- IMAGE_LAKE: ${{ secrets.DOCKERHUB_OWNER }}/devlake
- IMAGE_CONFIG_UI: ${{ secrets.DOCKERHUB_OWNER }}/devlake-config-ui
- IMAGE_GRAFANA: ${{ secrets.DOCKERHUB_OWNER }}/devlake-dashboard
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
on:
@@ -27,79 +24,197 @@
tags:
- 'v*'
jobs:
- build-lake:
- name: Build and Push lake image
- runs-on: ubuntu-20.04
+ build-and-push-builder:
+ name: Build and Push devlake builder
+ runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - name: Cache Build-Images-Push-Docker
- id: cache-Build-Images-Push-Docker
- uses: actions/cache@v3
+ - uses: actions/checkout@v3
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Login to DockerHub
+ uses: docker/login-action@v2
with:
- path: Build-Images-Push-Docker
- key: ${{ runner.os }}-Build-Images-Push-Docker
+ username: ${{ env.DOCKERHUB_USERNAME }}
+ password: ${{ env.DOCKERHUB_TOKEN }}
+ - name: Build and push lake image
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: true
+ target: builder
+ tags: ${{ secrets.DOCKERHUB_OWNER }}/devlake:amd64-builder
+ platforms: linux/amd64
+ cache-from: ${{ secrets.DOCKERHUB_OWNER }}/devlake:amd64-builder
+ cache-to: ${{ secrets.DOCKERHUB_OWNER }}/devlake:amd64-builder
+ build-and-push-base:
+ name: Build and Push devlake base
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Login to DockerHub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ env.DOCKERHUB_USERNAME }}
+ password: ${{ env.DOCKERHUB_TOKEN }}
+ - name: Build and push lake image
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: true
+ target: base
+ tags: ${{ secrets.DOCKERHUB_OWNER }}/devlake:base
+ platforms: linux/amd64,linux/arm64
+ cache-from: ${{ secrets.DOCKERHUB_OWNER }}/devlake:base
+ cache-to: ${{ secrets.DOCKERHUB_OWNER }}/devlake:base
+ build-devlake:
+ needs: build-and-push-builder
+ name: Build and cache devlake
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ platform: ['arm64', 'amd64']
+ steps:
+ - uses: actions/checkout@v3
+ - name: Get short sha
+ id: get_short_sha
+ run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Login to DockerHub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ env.DOCKERHUB_USERNAME }}
+ password: ${{ env.DOCKERHUB_TOKEN }}
+ - uses: actions/cache@v3
+ with:
+ path: /tmp/devlake-build-cache-${{ matrix.platform }}
+ key: buildx-devlake-build-cache-${{ github.run_id }}-${{ matrix.platform }}
+ - name: Build and cache lake build
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: false
+ target: build
+ tags: ${{ secrets.DOCKERHUB_OWNER }}/devlake:build-cache-${{ matrix.platform }}
+ platforms: linux/${{ matrix.platform }}
+ cache-from: ${{ secrets.DOCKERHUB_OWNER }}/devlake:amd64-builder
+ cache-to: type=local,mode=min,dest=/tmp/devlake-build-cache-${{ matrix.platform }}
+ build-args: |
+ TAG=${{ github.ref_name }}
+ SHA=${{ steps.get_short_sha.outputs.SHORT_SHA }}
+ build-and-push-devlake:
+ needs: [build-devlake, build-and-push-base]
+ name: Build and Push devlake image
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Get short sha
+ id: get_short_sha
+ run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Login to DockerHub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ env.DOCKERHUB_USERNAME }}
+ password: ${{ env.DOCKERHUB_TOKEN }}
+ - uses: actions/cache@v3
+ with:
+ path: /tmp/devlake-build-cache-amd64
+ key: buildx-devlake-build-cache-${{ github.run_id }}-amd64
+ - uses: actions/cache@v3
+ with:
+ path: /tmp/devlake-build-cache-arm64
+ key: buildx-devlake-build-cache-${{ github.run_id }}-arm64
+ - name: Get push tags
+ id: get_push_tags
+ run: |
+ image_name=${{ secrets.DOCKERHUB_OWNER }}/devlake
+ if printf ${{ github.ref_name }} | grep -Pq '^v(\d+).(\d+).(\d+)$'; then
+ echo "TAGS=${image_name}:latest,${image_name}:${{ github.ref_name }}" >> $GITHUB_OUTPUT
+ else
+ echo "TAGS=${image_name}:${{ github.ref_name }}" >> $GITHUB_OUTPUT
+ fi
+ - name: Build and push lake image
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.get_push_tags.outputs.TAGS }}
+ platforms: linux/amd64,linux/arm64
+ cache-from: |
+ ${{ secrets.DOCKERHUB_OWNER }}/devlake:amd64-builder
+ ${{ secrets.DOCKERHUB_OWNER }}/devlake:base
+ type=local,src=/tmp/devlake-build-cache-amd64
+ type=local,src=/tmp/devlake-build-cache-arm64
+ build-args: |
+ TAG=${{ github.ref_name }}
+ SHA=${{ steps.get_short_sha.outputs.SHORT_SHA }}
+ - name: Clear cache
+ uses: actions/github-script@v6
+ if: always()
+ with:
+ script: |
+ for (const arch of ['amd64', 'arm64']) {
+ try {
+ await github.rest.actions.deleteActionsCacheByKey({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ key: `buildx-devlake-build-cache-${context.runId}-${arch}`,
+ })
+ console.log(`Clear cache buildx-devlake-build-cache-${context.runId}-${arch}`)
+ } catch (e) {
+ console.warn(`Error clear cache buildx-devlake-build-cache-${context.runId}-${arch}: ${e}`)
+ }
+ }
- - name: Login to DockerHub
- uses: docker/login-action@v2
- with:
- username: ${{ env.DOCKERHUB_USERNAME }}
- password: ${{ env.DOCKERHUB_TOKEN }}
- - name: Build lake image
- run: |
- docker build --build-arg TAG=${{ github.ref_name }} --build-arg SHA=${{ github.sha }} -t ${{ env.IMAGE_LAKE }}:latest --file ./Dockerfile .
- docker tag ${{ env.IMAGE_LAKE }}:latest ${{ env.IMAGE_LAKE }}:${{ github.ref_name }}
- docker push ${{ env.IMAGE_LAKE }}:${{ github.ref_name }}
- if printf ${{ github.ref_name }} | grep -Pq '^v(\d+).(\d+).(\d+)$'; then
- echo "push latest tag"
- docker push ${{ env.IMAGE_LAKE }}:latest
- fi
- build-configui:
- name: Build and Push config-ui image
- runs-on: ubuntu-20.04
+ build-and-push-other-image:
+ name: Build and Push ${{ matrix.build.name }} image
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ build:
+ - name: config-ui
+ image: devlake-config-ui
+ context: config-ui
+ - name: grafana
+ image: devlake-dashboard
+ context: grafana
steps:
- - uses: actions/checkout@v2
- - name: Cache config-ui
- id: cache-config-ui
- uses: actions/cache@v3
- with:
- path: config-ui
- key: ${{ runner.os }}-config-ui
+ - uses: actions/checkout@v3
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ env.DOCKERHUB_TOKEN }}
- - name: Build config ui image
+ - name: Get push tags
+ id: get_push_tags
run: |
- cd config-ui
- docker build -t ${{ env.IMAGE_CONFIG_UI }}:latest --file ./Dockerfile .
- docker tag ${{ env.IMAGE_CONFIG_UI }}:latest ${{ env.IMAGE_CONFIG_UI }}:${{ github.ref_name }}
- docker push ${{ env.IMAGE_CONFIG_UI }}:${{ github.ref_name }}
- if printf ${{ github.ref_name }} | grep -Pq '^v(\d+).(\d+).(\d+)$'; then
- docker push ${{ env.IMAGE_CONFIG_UI }}:latest
- fi
- build-grafana:
- name: Build and Push grafana image
- runs-on: ubuntu-20.04
- steps:
- - uses: actions/checkout@v2
- - name: Cache grafana
- id: cache-grafana
- uses: actions/cache@v3
+ image_name=${{ secrets.DOCKERHUB_OWNER }}/${{ matrix.build.image }}
+ if printf ${{ github.ref_name }} | grep -Pq '^v(\d+).(\d+).(\d+)$'; then
+ echo "TAGS=${image_name}:latest,${image_name}:${{ github.ref_name }}" >> $GITHUB_OUTPUT
+ else
+ echo "TAGS=${image_name}:${{ github.ref_name }}" >> $GITHUB_OUTPUT
+ fi
+ - name: Build and push ${{ matrix.build.name }} image
+ uses: docker/build-push-action@v3
with:
- path: grafana
- key: ${{ runner.os }}-grafana
- - name: Login to DockerHub
- uses: docker/login-action@v2
- with:
- username: ${{ env.DOCKERHUB_USERNAME }}
- password: ${{ env.DOCKERHUB_TOKEN }}
- - name: Build grafana
- run: |
- cd grafana
- docker build -t ${{ env.IMAGE_GRAFANA }}:latest --file ./Dockerfile .
- docker tag ${{ env.IMAGE_GRAFANA }}:latest ${{ env.IMAGE_GRAFANA }}:${{ github.ref_name }}
- docker push ${{ env.IMAGE_GRAFANA }}:${{ github.ref_name }}
- if printf ${{ github.ref_name }} | grep -Pq '^v(\d+).(\d+).(\d+)$'; then
- docker push ${{ env.IMAGE_GRAFANA }}:latest
- fi
+ context: ${{ matrix.build.context }}
+ push: true
+ tags: ${{ steps.get_push_tags.outputs.TAGS }}
+ platforms: linux/amd64,linux/arm64
+
diff --git a/Dockerfile b/Dockerfile
index bd559c3..5b15b5a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -23,43 +23,140 @@
#While incubation status is not necessarily a reflection of the completeness or stability of the code,
#it does indicate that the project has yet to be fully endorsed by the ASF.
-FROM mericodev/lake-builder:latest as builder
+
+FROM --platform=linux/amd64 debian:bullseye as debian-amd64
+RUN apt-get update
+RUN apt-get install -y libssh2-1-dev libssl-dev zlib1g-dev
+
+FROM --platform=linux/arm64 debian:bullseye as debian-arm64
+RUN apt-get update
+RUN apt-get install -y libssh2-1-dev libssl-dev zlib1g-dev
+
+FROM --platform=$BUILDPLATFORM golang:1.19-bullseye as builder
# docker build --build-arg GOPROXY=https://goproxy.io,direct -t mericodev/lake .
ARG GOPROXY=
# docker build --build-arg HTTPS_PROXY=http://localhost:4780 -t mericodev/lake .
ARG HTTP_PROXY=
ARG HTTPS_PROXY=
-ARG TAG=
-ARG SHA=
+
+RUN apt-get update
+RUN apt-get install -y gcc binutils libfindbin-libs-perl cmake libssh2-1-dev libssl-dev zlib1g-dev
+
+RUN if [ "$(arch)" != "aarch64" ] ; then \
+ apt-get install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu ; \
+ fi
+RUN if [ "$(arch)" != "x86_64" ] ; then \
+ apt-get install -y gcc-x86-64-linux-gnu binutils-x86-64-linux-gnu ; \
+ fi
+
+RUN go install github.com/vektra/mockery/v2@v2.12.3
+RUN go install github.com/swaggo/swag/cmd/swag@v1.8.4
+
+COPY --from=debian-amd64 /usr/include /rootfs-amd64/usr/include
+COPY --from=debian-amd64 /usr/lib/x86_64-linux-gnu /rootfs-amd64/usr/lib/x86_64-linux-gnu
+COPY --from=debian-amd64 /lib/x86_64-linux-gnu /rootfs-amd64/lib/x86_64-linux-gnu
+
+COPY --from=debian-arm64 /usr/include /rootfs-arm64/usr/include
+COPY --from=debian-arm64 /usr/lib/aarch64-linux-gnu /rootfs-arm64/usr/lib/aarch64-linux-gnu
+COPY --from=debian-arm64 /lib/aarch64-linux-gnu /rootfs-arm64/lib/aarch64-linux-gnu
+
+RUN for arch in aarch64 x86_64 ; do \
+ mkdir -p /tmp/build/${arch} && cd /tmp/build/${arch} && \
+ wget https://github.com/libgit2/libgit2/archive/refs/tags/v1.3.2.tar.gz -O - | tar -xz && \
+ cd libgit2-1.3.2 && \
+ mkdir build && cd build && \
+ if [ "$arch" = "aarch64" ] ; then \
+ cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_SYSROOT=/rootfs-arm64 \
+ -DCMAKE_INSTALL_PREFIX=/usr/local/deps/${arch} ; \
+ elif [ "$arch" = "x86_64" ] ; then \
+ cmake .. -DCMAKE_C_COMPILER=x86_64-linux-gnu-gcc \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_SYSROOT=/rootfs-amd64 \
+ -DCMAKE_INSTALL_PREFIX=/usr/local/deps/${arch} ; \
+ fi && \
+ make -j install ; \
+ done
+
+
+FROM builder as build
WORKDIR /app
COPY . /app
ENV GOBIN=/app/bin
-RUN make clean && make all
+ARG TARGETPLATFORM
+ARG TAG=
+ARG SHA=
-FROM --platform=linux/amd64 mericodev/alpine-dbt:0.0.1
+RUN --mount=type=cache,target=/root/.cache/go-build \
+ if [ "$TARGETPLATFORM" = "linux/arm64" ] ; then \
+ ln -s /usr/local/deps/aarch64 /usr/local/deps/target && \
+ export CC=aarch64-linux-gnu-gcc && \
+ export GOARCH=arm64 ; \
+ else \
+ ln -s /usr/local/deps/x86_64 /usr/local/deps/target && \
+ export CC=x86_64-linux-gnu-gcc && \
+ export GOARCH=amd64 ; \
+ fi && \
+ export PKG_CONFIG_PATH=/usr/local/deps/target/lib/pkgconfig && \
+ export CGO_ENABLED=1 && \
+ make all
+
+# remove symlink in lib, we will recreate in final image
+RUN cd /usr/local/deps/target/lib && \
+ for file in *.so* ; do \
+ if [ -L $file ] ; then \
+ unlink $file ; \
+ fi \
+ done
+
+
+FROM debian:bullseye-slim as base
+
+ENV PYTHONUNBUFFERED=1
+
+RUN apt-get update && \
+ apt-get install -y python3-dev python3-pip tar curl libssh2-1 zlib1g && \
+ apt-get clean && \
+ rm -fr /usr/share/doc/* \
+ /usr/share/info/* \
+ /usr/share/linda/* \
+ /usr/share/lintian/overrides/* \
+ /usr/share/locale/* \
+ /usr/share/man/* \
+ /usr/share/doc/kde/HTML/* \
+ /usr/share/gnome/help/* \
+ /usr/share/locale/* \
+ /usr/share/omf/*/*-*.emf \
+ /var/lib/apt/lists/*
EXPOSE 8080
WORKDIR /app
-COPY --from=builder /app/bin /app/bin
-COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
-COPY --from=builder /app/requirements.txt /app/requirements.txt
-COPY --from=builder /app/config/tap /app/config/tap
-
# Setup Python
-RUN python -m venv /app/.venv
-RUN echo "source /app/.venv/bin/activate" >> ~/.profile
-RUN source ~/.profile
-RUN pip install --upgrade pip -r requirements.txt
-RUN apk add --no-cache curl
+COPY requirements.txt /app/requirements.txt
+RUN python3 -m pip install --no-cache --upgrade pip setuptools && \
+ python3 -m pip install --no-cache dbt-mysql dbt-postgres && \
+ python3 -m pip install --no-cache -r requirements.txt && \
+ rm -fr /usr/share/python-wheels/*
+
+
+
+FROM base as devlake-base
+
+# libraries
+ENV LD_LIBRARY_PATH=/app/libs
+RUN mkdir -p /app/libs
+COPY --from=build /usr/local/deps/target/lib/*.so* /app/libs
+RUN ldconfig -vn /app/libs
+
+# apps
+COPY --from=build /app/bin /app/bin
+COPY --from=build /app/config/tap /app/config/tap
ENV PATH="/app/bin:${PATH}"
CMD ["lake"]
-# Notes: Docker for Mac(M1) sets up qemu emulation, you can try to use the amd64 image by adding the --platform=linux/amd64 flag.
-# Such as: FROM --platform=linux/amd64 alpine:3.15
diff --git a/Makefile b/Makefile
index d3a66f7..0b825b2 100644
--- a/Makefile
+++ b/Makefile
@@ -54,7 +54,7 @@
all: build build-worker
build-server-image:
- docker build -t $(IMAGE_REPO)/devlake:$(TAG) --file ./Dockerfile .
+ docker build -t $(IMAGE_REPO)/devlake:$(TAG) --build-arg TAG=$(TAG) --build-arg SHA=$(SHA) --file ./Dockerfile .
build-config-ui-image:
cd config-ui; docker build -t $(IMAGE_REPO)/devlake-config-ui:$(TAG) --file ./Dockerfile .
diff --git a/config-ui/Dockerfile b/config-ui/Dockerfile
index dcb0bf0..35296ef 100644
--- a/config-ui/Dockerfile
+++ b/config-ui/Dockerfile
@@ -23,7 +23,7 @@
#While incubation status is not necessarily a reflection of the completeness or stability of the code,
#it does indicate that the project has yet to be fully endorsed by the ASF.
-FROM node:14 as builder
+FROM --platform=$BUILDPLATFORM node:14 as builder
WORKDIR /home/node/code
COPY package.json /home/node/code
COPY package-lock.json /home/node/code