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

Docker multi-arch build and push to registries #29

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Do not look for these files when docker builds
# https://www.geeksforgeeks.org/how-to-use-a-dockerignore-file/
# hadolint ignore
*.md
Dockerfile.dockerignore
.git
.vscode
.gitignore
Makefile
114 changes: 114 additions & 0 deletions .github/workflows/workflow.docker.buildx.gha.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# vim: se ts=2 sw=2 et:
# https://dev.to/cloudx/multi-arch-docker-images-the-easy-way-with-github-actions-4k54
# https://docs.docker.com/ci-cd/github-actions/
# assumes the image to build is the owner/current repo

name: Build docker buildx image

# yamllint disable-line rule:truthy
on:
push:
branches:
- main
- 'rich-*'
tags:
- 'v*'

pull_request:
branches:
- main

env:
# normal location
# DOCKERFILE: Dockerfile
DOCKERFILE: ./docker/Dockerfile
TEST_TAG: ${{github.repository}}:test
# https://github.com/docker/build-push-action/issues/557
# only 7GB of memory and two cores so only one job at a time
BUILD_ARGS: NR_JOBS=1

# need permissions to write to github repo artifacts
permissions:
packages: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
- name: Setup buildx
id: buildx
uses: docker/setup-buildx-action@v1

# https://docs.github.com/en/actions/security-guides/encrypted-secrets
- name: Login to GitHub registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

# https://docs.docker.com/docker-hub/access-tokens/
# you need to add the docker Personal Access Token to the secrets
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PERSONAL_ACCESS_TOKEN }}

# type=semvar,pattern={{major}}
# type=semvar,pattern={{major}.{{minor}}
# type=semvar,pattern={{major}}.{{minor}}.{{patch}}
# type=semvar,pattern={{version}}
# https://github.com/docker/build-push-action/blob/master/TROUBLESHOOTING.md
- name: Docker meta tags
id: meta
uses: docker/metadata-action@v4
with:
# use different registry names here to push to different registries
images: |
${{github.repository}}
ghcr.io/${{github.repository}}
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=sha
latest

# https://github.com/docker/build-push-action/blob/master/docs/advanced/test-before-push.md
- name: Build image for test
uses: docker/build-push-action@v3
with:
context: .
file: ${{ env.DOCKERFILE }}
build-args: ${{ env.BUILD_ARGS }}
# cannot do multi-platform because runs out of memory
# platforms: linux/amd64,linux/arm64
load: true
tags: ${{ env.TEST_TAG }}

- name: Test docker image runs
run: |
docker run --rm ${{ env.TEST_TAG }}

- name: Push image to registries (using cached build)
uses: docker/build-push-action@v3
with:
context: .
# for covins how parallel is the build too much and it fails
build-args: ${{ env.BUILD_ARGS }}
file: ${{ env.DOCKERFILE }}
# this currently fails probably too much memory used as of Aug 2022
# see https://github.com/docker/build-push-action/issues/621
# platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
# this is the step that creates the correct image names from above
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# instead of manual tgs use the metadata-action to generate them
#tags: ${{GITHUB_REPOSITORY}}:${{GITHUB_REF_NAME}}, ${{GITHUB_REPOSITORY}}:${{GITHUB_SHA}}
80 changes: 45 additions & 35 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,52 +1,61 @@
# This is an auto generated Dockerfile for ros:desktop-full
# generated from docker_images/create_ros_image.Dockerfile.em
FROM osrf/ros:melodic-desktop-bionic

ENV CATKIN_WS=/root/covins_ws

# fix keys see (https://github.com/googlecartographer/cartographer_ros/issues/1323)
RUN apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
RUN apt-key del 421C365BD9FF1F717815A3895523BAEEB01FA116
RUN apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' \
--recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 && \
apt-key del 421C365BD9FF1F717815A3895523BAEEB01FA116

# install ros packages
RUN apt-get update && apt-get install -y \
wget \
ros-melodic-desktop-full \
ros-melodic-serial \
ros-melodic-pcl-ros\
software-properties-common\
python3-catkin-tools \
python3-wstool \
vim \
sudo \
net-tools\
autoconf \
mesa-utils\
gedit \
gdb \
cgdb \
# install ros packages determine version pinning with
# apt-cache show _packages_ | grep Version
RUN apt-get update && apt-get install -y --no-install-recommends \
wget="1.19.*" \
ros-melodic-desktop-full="1.4.*" \
ros-melodic-serial="1.2.*" \
ros-melodic-pcl-ros="1.7.*" \
software-properties-common="0.96.*" \
python3-catkin-tools="0.9.*" \
python3-wstool="0.1.*" \
vim="2:8.0.*" \
sudo="1.8.*" \
net-tools="1.60*" \
autoconf="2.69*" \
mesa-utils="8.4.*" \
gedit="3.28.*" \
gdb="8.1.*" \
cgdb="0.6.*" \
&& rm -rf /var/lib/apt/lists/*

RUN apt-get update && apt-get upgrade -y
# not sure why this is here
RUN apt-get update && apt-get upgrade -y && apt-get clean && rm -rf /var/lib/apt/lists/*

# -------------------------------------------
# Libraries for building COVINS
# -------------------------------------------
RUN apt-get update && apt-get install -y \
cmake \
doxygen \
libgoogle-glog-dev \
libatlas-base-dev \
libsuitesparse-dev \
libyaml-cpp-dev \
libvtk6-dev \
libv4l-dev \
libomp-dev \
libglew-dev

RUN apt-get update && apt-get install -y --no-install-recommends \
cmake="3.10.*" \
doxygen="1.8.*" \
libgoogle-glog-dev="0.3.*" \
libatlas-base-dev="3.10.*" \
libsuitesparse-dev="1:5.1.*" \
libyaml-cpp-dev="0.5.*" \
libvtk6-dev="6.3.*" \
libv4l-dev="1.14.*" \
libomp-dev="5.0.*" \
unzip="6.0*" \
gcc="4:7.4.*" \
libglew-dev="2.0.*" && \
rm -rf /var/lib/apt/lists/*


#--------------------------
# BUILD COVINS
#--------------------------
# note this cache busts because of the copy if any files have changed at all
# including READMEs etc
COPY ./ $CATKIN_WS/src/covins
ARG NR_JOBS=
WORKDIR $CATKIN_WS
Expand All @@ -57,12 +66,13 @@ RUN catkin init && \
catkin config \
--extend /opt/ros/melodic \
--merge-devel \
--cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo && \
chmod +x src/covins/install_file.sh && \
--cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo

# shellcheck disable=SC2016
RUN chmod +x src/covins/install_file.sh && \
./src/covins/install_file.sh $NR_JOBS && \
sed -i '/exec "$@"/i \
source "${CATKIN_WS}/devel/setup.bash"' /ros_entrypoint.sh

#--------------------------
# BUILD COVINS ROS NODE
#--------------------------
Expand Down
9 changes: 9 additions & 0 deletions docker/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Do not look for these files when docker builds
# https://www.geeksforgeeks.org/how-to-use-a-dockerignore-file/
# hadolint ignore
*.md
Dockerfile.dockerignore
.git
.vscode
.gitignore
Makefile
130 changes: 118 additions & 12 deletions docker/Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,122 @@
all: help
## Dockerized Make for Covins
SHELL := /usr/bin/env bash
NR_JOBS ?= $$(($$(nproc)/2))
# NR_JOBS ?= $(shell nproc)

help:
@echo ""
@echo "-- Help Menu"
@echo ""
@echo " 1. make build - build docker image"
@echo " 1. make clean - remove all images"
@echo ""
# registry can be docker.io, ghcr.io, gcr.io for exmaple
REGISTRY ?= docker.io
ORG ?= vis4rob
IMAGE ?= covins
DOCKERFILE ?= Dockerfile
# we are building the parent
BUILD_PATH ?= ..
# use Dockerfile.dockerignore for files like README that should not break the
# cache
# https://github.com/docker/buildx/issues/166
# the --load not compatible with multiarch images so do not
# use with --platform=linux/arm64,linux/amd64 for example
OUTPUT ?= $(if $(PUSH),--push,--load)

build:
@echo "Use -j$(NR_JOBS) for build commands"
@docker build --tag covins -f ./Dockerfile --build-arg NR_JOBS=$(NR_JOBS) ..
# default tag is just the sha of the current git commit
# overwrite if you want with the specific tag in
# for instance make build TAG=v1.0.1
TAG ?= $(shell git rev-parse HEAD)
# Cannot run multiple arch if memory <= 7GB
PLATFORM ?= linux/amd64,linux/arm64
#PLATFORM ?= linux/amd64
#PLATFORM ?= linux/arm64

MEMORY != docker info | grep "Total Memory" | sed 's/[^0-9\.]*//g'

.DEFAULT_GOAL := help
.PHONY: help
# https://swcarpentry.github.io/make-novice/08-self-doc/ is simpler just need
help: $(MAKEFILE_LIST)
@sed -n 's/^##//p' $(MAKEFILE_LIST)


## buildx: Use BuildKit if you want multiarch images
# create a builder with large log file
# latest is automatically applied so do not need to tag
.PHONY: buildx
buildx: warning
@time ( \
docker buildx build -t $(REGISTRY)/$(ORG)/$(IMAGE):$(TAG) \
--platform $(PLATFORM) \
--build-arg NR_JOBS=$(NR_JOBS) \
-f $(DOCKERFILE) \
$(OUTPUT) \
$(BUILD_PATH) && \
docker tag $(REGISTRY)/$(ORG)/$(IMAGE):$(TAG) \
$(REGISTRY)/$(ORG)/$(IMAGE):latest \
)

## push: push local images
.PHONY: push
push:
docker push --all-tags $(REGISTRY)/$(ORG)/$(IMAGE)

## build: build one architecture at time to lower memory used
.PHONY: build
build: warning
@time ( \
docker build -t $(REGISTRY)/$(ORG)/$(IMAGE):$(TAG) \
--build-arg NR_JOBS=$(NR_JOBS) \
-f $(DOCKERFILE) \
$(BUILD_PATH) && \
docker tag $(REGISTRY)/$(ORG)/$(IMAGE):$(TAG) \
$(REGISTRY)/$(ORG)/$(IMAGE):latest \
)

## debug: dump all docker buildx messages out
# create a temporary docker context with larger log size
# https://stackoverflow.com/questions/65819424/is-there-a-way-to-increase-the-log-size-in-docker-when-building-a-container
.PHONY: debug
debug:
@time ( \
if ! docker buildx ls | grep -q build-$(IMAGE); then \
docker buildx create --name build-$(IMAGE) --use \
--driver-opt env.BUILDKIT_STEP_LOG_MAX_SIZE=100000000 \
; fi && \
docker buildx use big && \
docker buildx build -t $(REGISTRY)/$(ORG)/$(IMAGE):$(TAG) \
$(OUTPUT) \
--progress plain \
--platform $(PLATFORM) \
--build-arg NR_JOBS=$(NR_JOBS) \
-f $(DOCKERFILE) \
$(BUILD_PATH) && \
docker tag $(REGISTRY)/$(ORG)/$(IMAGE):$(TAG) \
$(REGISTRY)/$(ORG)/$(IMAGE):latest && \
docker buildx rm build-$(IMAGE) \
)

## tag: Add a github tag you can use with make build TAG=_your tag_
.PHONY: tag
tag:
@git tag -a $(TAG) -m "tagged with $(TAG)" && \
git push --tags

## warning: RAM and processor requirements
# note need to use bc because bash does not support floating comparison
# https://stackoverflow.com/questions/33111191/bash-bc-curl-comparing-variables
# Note that the values in a GiB so slightly more than a GB
.PHONY: warning
warning:
@echo "$(MEMORY) RAM available need at least 7GB and 32GB for two architectures" && \
if [[ $(PLATFORM) =~ , ]] && (( $$(bc <<<"$(MEMORY) <= 31") )); then \
echo "WARNING: Multiple build require at least 32GB of RAM"; \
fi && \
if (( $$(bc <<<"$(MEMORY) <= 6.8") )); then \
echo "WARNING: build requires more than 7GB of RAM"; \
fi

## stats: memory and other stats of containers
.PHONY: stats
stats:
@docker stats

## clean: remove docker images
.PHONY: clean
clean:
@docker rmi -f covins
@docker rmi -f $(REGIStRY)/$(ORG)/$(IMAGE)
Loading