Skip to content

Commit

Permalink
Add multiplatform images with amd64 and arm64
Browse files Browse the repository at this point in the history
This uses github's beta arm64 runners, which until they're released as
generally-available, have very limited software availability. There's
a step in the GH action to install build-essential and docker that we
can remove once the builder image has been updated.

The software available to arm images is somewhat limited compared to
amd64 images, and the version.yaml file parsing has been updated to
include the ability to specify the architecture for particular versions
of extensions.

Arm64 and amd64 images are built independently, and then at the last
step, a new manifest that points to both images is pushed to docker hub,
allowing at least linux/amd64 and linux/arm64 platforms to automatically
pick the specific image they want.
  • Loading branch information
graveland committed Apr 17, 2024
1 parent d081c9e commit 1aac8fd
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 26 deletions.
56 changes: 54 additions & 2 deletions .github/workflows/build_branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,48 @@ concurrency:
env:
DOCKER_REPOSITORY: timescale/timescaledb-ha
DOCKER_REGISTRY: docker.io
PLATFORM: amd64
PG_MAJOR: 16
ALL_VERSIONS: "true"
OSS_ONLY: "false"

jobs:
build-branch:
name: Build and push branch
runs-on: ubuntu-latest
runs-on: ${{ matrix.runs_on }}

strategy:
fail-fast: false
matrix:
platform: [ amd64, arm64 ]
include:
- platform: amd64
runs_on: ubuntu-22.04
- platform: arm64
runs_on: cloud-image-runner-arm64

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install docker (arm64 beta)
if: matrix.platform == 'arm64'
run: |
sudo apt-get update
sudo apt-get install -y ca-certificates curl acl build-essential
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
sudo setfacl --modify user:$USER:rw /var/run/docker.sock
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
Expand All @@ -39,10 +67,34 @@ jobs:
uses: docker/setup-buildx-action@v3

- name: Build
env:
PLATFORM: ${{ matrix.platform }}
run: make build-sha

- name: Check
env:
PLATFORM: ${{ matrix.platform }}
run: make check-sha

- name: Publish
env:
PLATFORM: ${{ matrix.platform }}
run: make publish-sha

publish-combined-manifest:
name: Publish branch manifest
needs: [ "build-branch" ]
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.ORG_DOCKER_HUB_USERNAME }}
password: ${{ secrets.ORG_DOCKER_HUB_ACCESS_TOKEN }}

- name: Publish combined manifest for branch
run: make publish-combined-sha
42 changes: 38 additions & 4 deletions .github/workflows/publish_images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
push:
branches:
- master
- main
paths-ignore:
- "*.md"

Expand All @@ -22,12 +23,12 @@ env:

jobs:
publish:
name: Publish pg${{ matrix.pg_major }}${{ matrix.all }}${{ matrix.oss }}
name: Publish pg${{ matrix.pg_major }}${{ matrix.all }}${{ matrix.oss }} ${{ matrix.platform }}

strategy:
fail-fast: false
matrix:
platform: [ "amd64" ]
platform: [ "amd64", "arm64" ]
pg_major: [ "16", "15", "14", "13", "12" ]
all_versions: [ "false", "true" ]
oss_only: [ "false", "true" ]
Expand All @@ -37,13 +38,36 @@ jobs:
oss: "-oss"
- all_versions: "true"
all: "-all"
- platform: amd64
runs_on: ubuntu-22.04
- platform: arm64
runs_on: cloud-image-runner-arm64

runs-on: ubuntu-latest
runs-on: "${{ matrix.runs_on }}"

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install docker (arm64 beta)
if: matrix.platform == 'arm64'
run: |
sudo apt-get update
sudo apt-get install -y ca-certificates curl acl build-essential
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
sudo setfacl --modify user:$USER:rw /var/run/docker.sock
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
Expand All @@ -58,7 +82,7 @@ jobs:
with:
endpoint: ha-builder

- name: Build and publish (pg${{ matrix.pg_major }}${{ matrix.all }}${{ matrix.oss }})
- name: Build and publish (pg${{ matrix.pg_major }}${{ matrix.all }}${{ matrix.oss }} ${{ matrix.platform }})
env:
PLATFORM: ${{ matrix.platform }}
PG_MAJOR: ${{ matrix.pg_major }}
Expand All @@ -80,11 +104,17 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.ORG_DOCKER_HUB_USERNAME }}
password: ${{ secrets.ORG_DOCKER_HUB_ACCESS_TOKEN }}

# QEMU for multiplatform, which should be quick enough for pulling version information out of the images
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Publish combined manifest for pg${{ matrix.pg_major }}${{ matrix.docker_tag_postfix }}
env:
PG_MAJOR: ${{ matrix.pg_major }}
Expand Down Expand Up @@ -112,6 +142,10 @@ jobs:
username: ${{ secrets.ORG_DOCKER_HUB_USERNAME }}
password: ${{ secrets.ORG_DOCKER_HUB_ACCESS_TOKEN }}

# QEMU for multiplatform, which should be quick enough for just the checks
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Check pg${{ matrix.pg_major }}${{ matrix.docker_tag_postfix }}
env:
PG_MAJOR: ${{ matrix.pg_major }}
Expand Down
48 changes: 31 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ DOCKER_BUILDER_URL=$(DOCKER_PUBLISH_URL):pg$(PG_MAJOR)$(DOCKER_TAG_POSTFIX)-buil
DOCKER_BUILDER_ARCH_URL=$(DOCKER_PUBLISH_URL):pg$(PG_MAJOR)$(DOCKER_TAG_POSTFIX)-builder-$(PLATFORM)
DOCKER_RELEASE_URL=$(DOCKER_PUBLISH_URL):pg$(PG_MAJOR)$(DOCKER_TAG_POSTFIX)
DOCKER_RELEASE_ARCH_URL=$(DOCKER_PUBLISH_URL):pg$(PG_MAJOR)$(DOCKER_TAG_POSTFIX)-$(PLATFORM)
CICD_URL=$(DOCKER_PUBLISH_URL):cicd-$(shell printf "%.7s" "$(GITHUB_SHA)")-$(PLATFORM)
CICD_URL=$(DOCKER_PUBLISH_URL):cicd-$(shell printf "%.7s" "$(GITHUB_SHA)")
CICD_ARCH_URL=$(CICD_URL)-$(PLATFORM)

GITHUB_STEP_SUMMARY?=/dev/null
GITHUB_OUTPUT?=/dev/null
Expand Down Expand Up @@ -199,12 +200,12 @@ build-sha: is_ci
ifeq ($(strip $(GITHUB_SHA)),)
$(error GITHUB_SHA is empty, is this running in github actions?)
endif
$(DOCKER_BUILD_COMMAND) --tag "$(CICD_URL)"
$(DOCKER_BUILD_COMMAND) --tag "$(CICD_ARCH_URL)"

.PHONY: publish-sha
publish-sha: # push the specific git commit image
publish-sha: is_ci
docker push "$(CICD_URL)"
docker push "$(CICD_ARCH_URL)"

.PHONY: build-tag
build-tag: DOCKER_TAG_POSTFIX?=$(GITHUB_TAG)
Expand Down Expand Up @@ -254,40 +255,53 @@ build:

.PHONY: publish-combined-builder-manifest
publish-combined-builder-manifest: # publish a combined builder image manifest
@echo "Creating manifest $(DOCKER_BUILDER_URL) that includes $(DOCKER_BUILDER_URL)-amd64"
@echo "Creating manifest $(DOCKER_BUILDER_URL) that includes $(DOCKER_BUILDER_URL)-amd64 and $(DOCKER_BUILDER_URL)-arm64"
amddigest_image="$$(./fetch_tag_digest $(DOCKER_BUILDER_URL)-amd64)"
echo "AMD: $$amddigest_image"
armdigest_image="$$(./fetch_tag_digest $(DOCKER_BUILDER_URL)-arm64)"
echo "AMD: $$amddigest_image ARM: $$armdigest_image"
docker manifest rm "$(DOCKER_BUILDER_URL)" >& /dev/null || true
docker manifest create "$(DOCKER_BUILDER_URL)" --amend "$$amddigest_image"
docker manifest create "$(DOCKER_BUILDER_URL)" --amend "$$amddigest_image" --amend "$$armdigest_image"
docker manifest push "$(DOCKER_BUILDER_URL)"
echo "pushed $(DOCKER_BUILDER_URL)"
echo "Pushed $(DOCKER_BUILDER_URL) (amd:$$amddigest_image)" >> "$(GITHUB_STEP_SUMMARY)"
echo "Pushed $(DOCKER_BUILDER_URL) (amd:$$amddigest_image, arm:$$armdigest_image)" >> "$(GITHUB_STEP_SUMMARY)"

# since we're using immutable tags, we don't need to pull/find the child image SHAs, we can just use the tags
.PHONY: publish-combined-manifest
publish-combined-manifest: # publish the main combined manifest that includes amd64 and arm64 images
publish-combined-manifest: $(VAR_VERSION_INFO)
@echo "Creating manifest $(DOCKER_RELEASE_URL) that includes $(DOCKER_RELEASE_URL)-amd64"
@echo "Creating manifest $(DOCKER_RELEASE_URL) that includes $(DOCKER_RELEASE_URL)-amd64 and $(DOCKER_RELEASE_URL)-arm64"
amddigest_image="$$(./fetch_tag_digest $(DOCKER_RELEASE_URL)-amd64)"
echo "AMD: $$amddigest_image"
armdigest_image="$$(./fetch_tag_digest $(DOCKER_RELEASE_URL)-arm64)"
echo "AMD: $$amddigest_image ARM: $$armdigest_image"
for tag in pg$(PG_MAJOR) pg$(PG_MAJOR)-ts$(VAR_TSMAJOR) pg$(VAR_PGMAJOR)-ts$(VAR_TSVERSION); do
url="$(DOCKER_PUBLISH_URL):$$tag$(DOCKER_TAG_POSTFIX)"
docker manifest rm "$$url" >&/dev/null || true
docker manifest create "$$url" --amend "$$amddigest_image"
docker manifest create "$$url" --amend "$$amddigest_image" --amend "$$armdigest_image"
docker manifest push "$$url"
echo "pushed $$url"
echo "Pushed $$url (amd:$$amddigest_image)" >> "$(GITHUB_STEP_SUMMARY)"
echo "Pushed $$url (amd:$$amddigest_image, arm:$$armdigest_image)" >> "$(GITHUB_STEP_SUMMARY)"
done

.PHONY: publish-manifests
publish-manifests: # publish the combined manifests for the builder and the release images
publish-manifests: publish-combined-builder-manifest publish-combined-manifest

.PHONY: publish-combined-sha
publish-combined-sha: is_ci # publish a combined image manifest for a CICD branch build
@echo "Creating manifest $(CICD_URL) that includes $(CICD_URL)-amd64 and $(CICD_URL)-arm64"
amddigest_image="$$(./fetch_tag_digest $(CICD_URL)-amd64)"
armdigest_image="$$(./fetch_tag_digest $(CICD_URL)-arm64)"
echo "AMD: $$amddigest_image ARM: $$armdigest_image"
docker manifest rm "$(CICD_URL)" >& /dev/null || true
docker manifest create "$(CICD_URL)" --amend "$$amddigest_image" --amend "$$armdigest_image"
docker manifest push "$(CICD_URL)"
echo "pushed $(CICD_URL)"
echo "Pushed $(CICD_URL) (amd:$$amddigest_image, arm:$$armdigest_image)" >> "$(GITHUB_STEP_SUMMARY)"

CHECK_NAME=ha-check
.PHONY: check
check: # check images to see if they have all the requested content
@set -x
for arch in amd64; do \
for arch in amd64 arm64; do \
key="$$(mktemp -u XXXXXX)"
check_name="$(CHECK_NAME)-$$key"
echo "### Checking $$arch $(DOCKER_RELEASE_URL)" >> $(GITHUB_STEP_SUMMARY); \
Expand All @@ -311,11 +325,11 @@ check: # check images to see if they have all the requested content

.PHONY: check-sha
check-sha: # check a specific git commit-based image
@echo "### Checking $(CICD_URL)" >> $(GITHUB_STEP_SUMMARY)
case "$(CICD_URL)" in
@echo "### Checking $(CICD_ARCH_URL)" >> $(GITHUB_STEP_SUMMARY)
case "$(CICD_ARCH_URL)" in
*-amd64) arch=amd64;;
*-arm64) arch=arm64;;
*) echo "unknown architecture for $(CICD_URL)" >&2; exit 1;;
*) echo "unknown architecture for $(CICD_ARCH_URL)" >&2; exit 1;;
esac
key="$$(mktemp -u XXXXXX)"
check_name="$(CHECK_NAME)-$$key"
Expand All @@ -326,7 +340,7 @@ check-sha: # check a specific git commit-based image
--name "$$check_name" \
-e PGDATA=/tmp/pgdata \
--user=postgres \
"$(CICD_URL)" sleep 300
"$(CICD_ARCH_URL)" sleep 300
docker exec -u root "$$check_name" mkdir -p /cicd/scripts
docker exec -u root "$$check_name" chown -R postgres: /cicd
tar -cf - -C ./cicd . | docker exec -i "$$check_name" tar -C /cicd -x
Expand Down
12 changes: 9 additions & 3 deletions build_scripts/shared_versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ else
fi

DEFAULT_PG_MIN="$(yq .default-pg-min <<< "$VERSION_DATA")"
[ -z "$DEFAULT_PG_MIN" ] && { error "default-pg-min required in versions.yaml"; exit 1; }
[ -z "$DEFAULT_PG_MIN" ] && { error "default-pg-min is required in versions.yaml"; exit 1; }
DEFAULT_PG_MAX="$(yq .default-pg-max <<< "$VERSION_DATA")"
[ -z "$DEFAULT_PG_MAX" ] && { error "default-pg-max required in versions.yaml"; exit 1; }
[ -z "$DEFAULT_PG_MAX" ] && { error "default-pg-max is required in versions.yaml"; exit 1; }

pkg_versions() {
local pkg="$1"
Expand Down Expand Up @@ -112,7 +112,7 @@ install_rust_extensions() {
}

version_is_supported() {
local pkg="$1" pg="$2" ver="$3" pdata pgmin pgmax
local pkg="$1" pg="$2" ver="$3" pdata pgmin pgmax arch
local -a pgversions

pdata="$(yq ".$pkg | pick([\"$ver\"]) | .[]" <<<"$VERSION_DATA")"
Expand All @@ -121,6 +121,10 @@ version_is_supported() {
return
fi

arch="$(yq .arch <<<"$pdata")"
if [ "$arch" = null ]; then arch="both"; fi
if [[ "$arch" != "both" && "$arch" != "$ARCH" ]]; then echo "unsupported arch $ARCH"; return; fi

pgmin="$(yq .pg-min <<<"$pdata")"
if [ "$pgmin" = null ]; then pgmin="$DEFAULT_PG_MIN"; fi
if [ "$pg" -lt "$pgmin" ]; then echo "pg$pg is too old"; return; fi
Expand Down Expand Up @@ -165,6 +169,8 @@ supported_toolkit() {
supported_promscale() {
local pg="$1" ver="$2"

if [ "$ARCH" != amd64 ]; then echo "unsupported arch $ARCH"; return; fi

# just attempt the build for main/master/or other branch build
if [[ "$ver" = main || "$ver" = master || "$ver" =~ [a-z_-]*/[A-Za-z0-9_-]* ]]; then
return
Expand Down
Loading

0 comments on commit 1aac8fd

Please sign in to comment.