Building Windows and Linux Containers with GitHub Actions
I have been discussing PowerShell modules in containers and building multiplatform containers in my last few posts. I want to follow up those posts with the GitHub Actions that I created for the pstools repository. I created two GH Actions, one called CI
for PRs and commits and one called Publish
that runs when a GitHub release is created. These actions leverage the matrix and strategy to configure agents for building Linux, Windows 2019, and Windows 2022 container images. The publish workflow differs just a little because it also pushes and generates a manifest.
CI Workflow
This workflow is only executed on pushed to main and any pull requests to main. The whole purpose is to ensure that the container images are built properly. The piece to focus on is the strategy
section as it creates the build matrix for each OS, which is required when building Windows containers. Then based on OS, I set some OS specific settigns like which base
to use and which Dockerfile. Then it is matter of executing the build with the different parameters configured.
name: CI Workflow
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-containers:
name: Build Containers
runs-on: ${{ matrix.os }}
strategy:
max-parallel: 3
matrix:
os: [ubuntu-latest, windows-2019, windows-2022]
include:
- os: ubuntu-latest
base: alpine-3.14
file: Dockerfile.linux
- os: windows-2019
base: nanoserver-1809
file: Dockerfile.windows
- os: windows-2022
base: nanoserver-ltsc2022
file: Dockerfile.windows
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker Build
run: |
docker build -f ${{ matrix.file }} --build-arg BASE=${{ matrix.base }} .
Publish Workflow
The biggest difference between this one and the CI workflow is the trigger being set as release with a type of published. You will see almost the same matrix as before with a tag parameter added based on OS. This ensures that the tag for the release is used along with using the standardized tagging for those images. Then along with building the containers, they are pushed to DockerHub along with having a manifest generated.
name: Publish Workflow
on:
release:
types:
- published
jobs:
build-publish-containers:
name: Build and Publish Containers
runs-on: ${{ matrix.os }}
strategy:
max-parallel: 3
matrix:
os: [ubuntu-latest, windows-2019, windows-2022]
include:
- os: ubuntu-latest
base: alpine-3.14
file: Dockerfile.linux
tag: phillipsj/pstools:${{ github.event.release.tag_name }}-linux-amd64
- os: windows-2019
base: nanoserver-1809
file: Dockerfile.windows
tag: phillipsj/pstools:${{ github.event.release.tag_name }}-windows-ltsc2019-amd64
- os: windows-2022
base: nanoserver-ltsc2022
file: Dockerfile.windows
tag: phillipsj/pstools:${{ github.event.release.tag_name }}-windows-ltsc2022-amd64
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Build
run: |
docker build -f ${{ matrix.file }} --build-arg BASE=${{ matrix.base }} -t ${{ matrix.tag }} .
- name: Docker Push
run: |
docker push ${{ matrix.tag }}
publish-manfiest:
name: Publish Manifest
runs-on: ubuntu-latest
needs: build-publish-containers
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Manifest
run: |
docker manifest create phillipsj/pstools:${{ github.event.release.tag_name }} \
--amend phillipsj/pstools:${{ github.event.release.tag_name }}-linux-amd64 \
--amend phillipsj/pstools:${{ github.event.release.tag_name }}-windows-ltsc2019-amd64 \
--amend phillipsj/pstools:${{ github.event.release.tag_name }}-windows-ltsc2022-amd64
- name: Docker Annotate
run: |
docker manifest annotate --os windows --arch amd64 \
--os-version "10.0.17763.1817" \
phillipsj/pstools:${{ github.event.release.tag_name }} phillipsj/pstools:${{ github.event.release.tag_name }}-windows-ltsc2019-amd64
docker manifest annotate --os windows --arch amd64 \
--os-version "10.0.20348.169"\
phillipsj/pstools:${{ github.event.release.tag_name }} phillipsj/pstools:${{ github.event.release.tag_name }}-windows-ltsc2022-amd64
- name: Docker Push
run: |
docker manifest push phillipsj/pstools:${{ github.event.release.tag_name }}
Wrapping Up
There could be a better way to do some of these, this is just how I did it. I chose not to use the Docker build-push-action due to the lack of Windows support offered so I favored consistency to keep it the same across all of them. Hopefully someone finds what I did here useful.
Thanks for reading,
Jamie
If you enjoy the content, then consider buying me a coffee.