Building and Publishing Docker Images for AWS, Azure, and GCP

In this tutorial, we will set up a GitHub Actions pipeline to automatically build and publish Docker images for AWS CLI, Azure CLI, and Google Cloud Platform (GCP) CLI to Docker Hub repositories.

Prerequisites

Before we begin, ensure you have the following:

  • A GitHub repository containing Dockerfiles for AWS, Azure, and GCP CLI. You can find the repository for AWS Dockerfile here.
  • Docker Hub account and authentication token for pushing Docker images.
  • GitHub repository secrets configured for Docker Hub credentials.

AWS Dockerfile

# Use the latest Alpine Linux base image
FROM alpine:3.19.1

ENV TERRAFORM_VERSION=1.7.4

# Install necessary packages: Python 3, pip, awscli, jq, git
RUN apk --update --no-cache add \
        python3 \
        py3-pip \
        py3-yaml \
        aws-cli \
        jq \
        git \
        openssl \
        unzip \
        curl \
        py3-cryptography \
    # Install yq (YAML processor)
    && wget -q -O /usr/bin/yq $(wget -q -O - https://api.github.com/repos/mikefarah/yq/releases/latest | jq -r '.assets[] | select(.name == "yq_linux_amd64") | .browser_download_url') \
    && chmod +x /usr/bin/yq \
    && echo "Intalling Terraform ...." \
    && wget -q https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
    && unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/bin \
    && rm -rf /var/cache/apk/*

# Add a non-root user named dockuser
RUN addgroup -g 1000 dockuser \
    && adduser -D -G dockuser -u 1000 dockuser

# Switch to the dockuser
USER dockuser

# Set the working directory
WORKDIR /home/dockuser

Azure Dockerfile

# Use the latest Alpine Linux base image
FROM alpine:3.19.1

ENV TERRAFORM_VERSION=1.7.4
ENV AZURE_CLI_VERSION=2.0.81

# Install necessary packages: Python 3, pip, jq, git
RUN apk --update --no-cache add \
        alpine-sdk \
        python3 \
        python3-dev \
        py3-pip \
        py3-yaml \
        jq \
        git \
        openssl \
        unzip \
        curl \
        py3-cryptography \
        gcc \
        libc-dev \
        libffi-dev \
    # Install yq (YAML processor)
    && wget -q -O /usr/bin/yq $(wget -q -O - https://api.github.com/repos/mikefarah/yq/releases/latest | jq -r '.assets[] | select(.name == "yq_linux_amd64") | .browser_download_url') \
    && chmod +x /usr/bin/yq \
    && echo "Intalling Terraform ...." \
    && wget -q https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
    && unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/bin \
    && rm -rf /var/cache/apk/*

RUN pip install --upgrade pip --break-system-packages \
    && pip install azure-cli --break-system-packages

# Add a non-root user named dockuser
RUN addgroup -g 1000 dockuser \
    && adduser -D -G dockuser -u 1000 dockuser

# Switch to the dockuser
USER dockuser

# Set the working directory
WORKDIR /home/dockuser

GCP Dockerfile

# Use the latest Alpine Linux base image
FROM alpine:3.19.1

ENV TERRAFORM_VERSION=1.7.4
ENV CLOUD_SDK_VERSION=467.0.0
ENV PATH /google-cloud-sdk/bin:$PATH

# Add a non-root user named dockuser
RUN addgroup -g 1000 dockuser \
    && adduser -D -G dockuser -u 1000 dockuser

RUN if [ `uname -m` = 'x86_64' ]; then echo -n "x86_64" > /tmp/arch; else echo -n "arm" > /tmp/arch; fi;

# Install necessary packages: Python 3, pip, awscli, jq, git
RUN ARCH=`cat /tmp/arch` && apk --update --no-cache add \
        python3 \
        py3-pip \
        py3-yaml \
        jq \
        git \
        openssl \
        unzip \
        curl \
        py3-cryptography \
        bash \
        gnupg \
    # Install yq (YAML processor)
    && wget -q -O /usr/bin/yq $(wget -q -O - https://api.github.com/repos/mikefarah/yq/releases/latest | jq -r '.assets[] | select(.name == "yq_linux_amd64") | .browser_download_url') \
    && chmod +x /usr/bin/yq \
    && echo "Intalling Terraform ...." \
    && wget -q https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
    && unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/bin \
    && curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-${CLOUD_SDK_VERSION}-linux-${ARCH}.tar.gz \
    && tar xzf google-cloud-cli-${CLOUD_SDK_VERSION}-linux-${ARCH}.tar.gz \
    && rm google-cloud-cli-${CLOUD_SDK_VERSION}-linux-${ARCH}.tar.gz \
    && gcloud components install kubectl \
    && rm -rf /var/cache/apk/*

# Switch to the dockuser
USER dockuser

# Set the working directory
WORKDIR /home/dockuser

AWS Docker Image YAML (docker_aws_image.yml)

name: AWS Build and publish a Docker image
on:
  push:
    branches:
      - '*'

env:
    REGISTRY: devtoolhub
    IMAGE_NAME: aws_cli
    COMPARE_TAG: latest
    SHA: ${{ github.event.after }}
jobs:
  build:
    name: AWS Build & push docker image
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
            ref: ${{ env.SHA }}

      - name: Debug
        run: |
          echo "github.ref -> {{ github.ref }}"

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
            driver-opts: |
              image=moby/buildkit:v0.13.0

      - name: lint Action for AWS
        uses: hadolint/[email protected]
        with:
            dockerfile: aws/Dockerfile
            ignore: DL3018,SC2046,DL4006
            failure-threshold: warning
            no-color : false
            no-fail: true

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
                 
      - name: AWS Docker metadata
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          labels: |
            org.opencontainers.image.revision=${{ env.SHA }}
          tags: |
            type=edge,branch=$repo.default_branch
            type=semver,pattern=v{{version}}
            type=sha,prefix=,suffix=,format=short

      - name: AWS Build & push Docker image
        uses: docker/build-push-action@v5
        with:
          context: aws/
          push: true
          tags: ${{ steps.metadata.outputs.tags }}
          labels: ${{ steps.metadata.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: AWS Docker Scout
        id: docker-scout
        uses: docker/scout-action@v1
        with:
            image: ${{ steps.metadata.outputs.tags }}
            command: cves
            ignore-unchanged: true
            only-severities: critical,high,medium,low,unspecified
            only-fixed: true

Azure Docker Image YAML (docker_azure_image.yml)

name: Azure Build and publish a Docker image
on:
  push:
    branches:
      - '*'

env:
    REGISTRY: devtoolhub
    IMAGE_NAME: azure_cli
    COMPARE_TAG: latest
    SHA: ${{ github.event.after }}
jobs:
  build:
    name: Azure Build & push docker image
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
            ref: ${{ env.SHA }}

      - name: Debug
        run: |
          echo "github.ref -> {{ github.ref }}"

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
            driver-opts: |
              image=moby/buildkit:v0.13.0

      - name: lint Action for Azure
        uses: hadolint/[email protected]
        with:
            dockerfile: azure/Dockerfile
            ignore: DL3018,SC2046,DL4006
            failure-threshold: warning
            no-color : false
            no-fail: true

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
                 
      - name: Azure Docker metadata
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          labels: |
            org.opencontainers.image.revision=${{ env.SHA }}
          tags: |
            type=edge,branch=$repo.default_branch
            type=semver,pattern=v{{version}}
            type=sha,prefix=,suffix=,format=short

      - name: Azure Build & Push Docker image
        uses: docker/build-push-action@v5
        with:
          context: azure/
          push: true
          tags: ${{ steps.metadata.outputs.tags }}
          labels: ${{ steps.metadata.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Azure Docker Scout
        id: docker-scout
        uses: docker/scout-action@v1
        with:
            image: ${{ steps.metadata.outputs.tags }}
            command: cves
            ignore-unchanged: true
            only-severities: critical,high,medium,low,unspecified
            only-fixed: true

GCP Docker Image YAML (docker_gcp_image.yml)

name: GCP Build and publish a Docker image
on:
  push:
    branches:
      - '*'

env:
    REGISTRY: devtoolhub
    IMAGE_NAME: gcp_cli
    COMPARE_TAG: latest
    SHA: ${{ github.event.after }}
jobs:
  build:
    name: GCP Build & push docker image
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
            ref: ${{ env.SHA }}

      - name: Debug
        run: |
          echo "github.ref -> {{ github.ref }}"

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
            driver-opts: |
              image=moby/buildkit:v0.13.0

      - name: lint Action for GCP
        uses: hadolint/[email protected]
        with:
            dockerfile: gcp/Dockerfile
            ignore: DL3018,SC2046,DL4006
            failure-threshold: warning
            no-color : false
            no-fail: true

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
                 
      - name: GCP Docker metadata
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          labels: |
            org.opencontainers.image.revision=${{ env.SHA }}
          tags: |
            type=edge,branch=$repo.default_branch
            type=semver,pattern=v{{version}}
            type=sha,prefix=,suffix=,format=short

      - name: GCP Build & Push Docker image
        uses: docker/build-push-action@v5
        with:
          context: gcp/
          push: true
          tags: ${{ steps.metadata.outputs.tags }}
          labels: ${{ steps.metadata.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Docker Scout
        id: docker-scout
        uses: docker/scout-action@v1
        with:
            image: ${{ steps.metadata.outputs.tags }}
            command: cves
            ignore-unchanged: true
            only-severities: critical,high,medium,low,unspecified
            only-fixed: true

Explanation of YAML File Steps

  1. Checkout: This step checks out the codebase from the repository.
  2. Debug: Displays the GitHub reference being used for the workflow.
  3. Set up QEMU: Sets up QEMU for cross-platform builds.
  4. Set up Docker Buildx: Sets up Docker Buildx for multi-platform builds.
  5. Lint Action for AWS/Azure/GCP: Uses Hadolint to lint Dockerfiles, ignoring certain rules.
  6. Log in to Docker Hub: Authenticates with Docker Hub using provided credentials.
  7. Docker Metadata: Generates metadata for Docker image including tags and labels.
  8. Build & Push Docker image: Builds Docker image and pushes it to Docker Hub.
  9. Docker Scout: Scans the Docker image for vulnerabilities using Docker Scout.

Docker Hub URLs

Conclusion

In this tutorial, we have set up a GitHub Actions pipeline to automate the building and publishing of Docker images for AWS CLI, Azure CLI, and GCP CLI. By leveraging GitHub Actions, you can streamline your CI/CD processes and ensure the consistency and reliability of your Docker images.