How to migrate from GitHub Container Registry (GHCR) to Google Artifact Registry (GAR)¶
Migrate¶
You can switch from GHCR to GAR by following these steps to update your current GitHub workflow. Let's simplify this by taking an example of a workflow using GHCR and show how you can to change it to GAR.
The docker-build-action is employed for building and publishing the Docker image to GAR. Subsequently, the deploy-action facilitates the deployment of the application to a cluster.
Example workflow¶
name: Build, push, and deploy
on: [ push ]
env:
IMAGE: ghcr.io/navikt/my-team/my-app:${{ github.sha }}
jobs:
build:
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: actions/checkout@v4
- name: Build and test my-app
run: some build steps..
- name: Build and publish Docker image
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo ${GITHUB_TOKEN} | docker login docker.pkg.github.com -u ${GITHUB_REPOSITORY} --password-stdin
echo ${GITHUB_TOKEN} | docker login ghcr.io -u ${{github.actor}} --password-stdin
docker build -t ${IMAGE} .
docker push ${IMAGE}
deploy:
name: Deploy to NAIS
runs-on: ubuntu-latest
needs: [ build ]
steps:
- uses: actions/checkout@v4
- uses: nais/deploy/actions/deploy@v2
env:
CLUSTER: target-cluster
RESOURCE: nais.yaml
VAR: image=${{ env.IMAGE }}
Modify workflow¶
Initially, eliminate the environment variable: IMAGE: ghcr.io/navikt/my-team/my-app:${{ github.sha }}
, as
the nais/docker-build-action
, automatically generates this value and outputs it as the image
variable.
The resulting image reference is composed of the following:
- Registry:
europe-north1-docker.pkg.dev
- Google Cloud Project:
nais-management-233d
- Team:
my-team
- Application:
my-app
Build and publish Docker image¶
The tag is automatically generated by the nais/docker-build-action
or can be specified as input to
the action.
- name: Build and publish Docker image
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo ${GITHUB_TOKEN} | docker login docker.pkg.github.com -u ${GITHUB_REPOSITORY} --password-stdin
echo ${GITHUB_TOKEN} | docker login ghcr.io -u ${{github.actor}} --password-stdin
docker build -t ${IMAGE} .
docker push ${IMAGE}
- name: Push docker image to GAR and sign image
uses: nais/docker-build-push@v0
id: docker-build-push
with:
team: my-team
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }} # Provided as Organization Secret
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }} # Provided as Organization Variable
-
The id
docker-build-push
is a named id of the job step. -
identity_provider
andproject_id
are provided as Organization Secrets and Variables, respectively. Please refer to GitHub Secrets for more information. -
team
is the name of your team, and must be provided as input to the action.
Image reference¶
The build
job outputs the image reference as image
, and the deploy
job can reference this output
as needs.build.outputs.image
env:
CLUSTER: target-cluster # Replace
RESOURCE: nais.yaml
VAR: image=${{ needs.build.outputs.image }}
TELEMETRY: ${{ needs.build.outputs.telemetry }}
Also add the following after the build job, to allow the deploy job to access the output:
outputs:
image: ${{ steps.docker-build-push.outputs.image }}
image: ${{ steps.docker-build-push.outputs.telemetry }}
NAIS Salsa (SLSA - Supply Chain Levels for Software Artifacts)¶
SLSA is short for Supply chain Levels for Software Artifacts pronounced salsa. It is a security framework, essentially a checklist comprising standards and controls aimed at preventing tampering, enhancing integrity, and securing both packages and infrastructure within our projects.
The nais/docker-build-push
action automatically signs your image with cosign,
and uploads the attestation, the result can be reviewed in your team tab
in NAIS Console.
For more information about Salsa, please refer to NAIS Salsa.
Workflow permissions¶
Permissions for the workflow should be scoped to ensure that the workflow you are constructing has sufficient access to perform its tasks, please modify the following:
The reason why you only should set explicit permissions for a workflow job
, is because it
brings us into a more 'fine-grained' access model, and makes us a bit less vulnerable to attacks through things like
malicious actions and NPM packages or other elements that can execute code in the workflows.
The nais/docker-build-push
action requires the id-token:write
permission to be able to
authenticate with the Google Cloud Platform. The contents:read
permission is required to be able to clone and build.
For more information about permissions, please refer to The GitHub Blog Post
Authorize your workflow¶
The use of secrets.NAIS_DEPLOY_APIKEY
is deprecated, and will be removed in the future.
For more information on how to authorize your workflow, please refer to Authorize your workflow.
Finalized workflow¶
name: Build, push, and deploy
on: [ push ]
jobs:
build:
name: Build and push Docker container
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.docker-build-push.outputs.image }}
telemetry: ${{ steps.docker-build-push.outputs.telemetry }}
steps:
- uses: actions/checkout@v4
- name: Build and test my-app
run: some build steps..
- uses: actions/checkout@v4
- name: Push docker image to GAR
uses: nais/docker-build-push@v0
id: docker-build-push
with:
team: myteam # Replace
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }} # Provided as Organization Secret
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }} # Provided as Organization Variable
outputs:
image: ${{ steps.docker-build-push.outputs.image }}
image: ${{ steps.docker-build-push.outputs.telemetry }}
deploy:
name: Deploy to NAIS
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: nais/deploy/actions/deploy@v2
env:
CLUSTER: target-cluster # Replace
RESOURCE: nais.yaml
VAR: image=${{ needs.build.outputs.image }}
TELEMETRY: ${{ needs.build.outputs.telemetry }}
The id docker-build-push
is the id of the previous job step, there is where your new image will be outputted. In this
example we divided our workflow into two jobs, build
and deploy
. The deploy
job depends on the build
job, and
will not run unless the build
job is successful. That is why we can reference the output of the build
job as
needs.build.outputs.image
.