Ken Muse

Deploying to Azure from Private Container Registries


Continuing with our discussion around containerization and Bicep, let’s examine another common scenario: deploying from a private container registry. It’s a best practice to use private container registries whenever possible. This allows us to control what images are made available in our environments, and it can enable us to take advantage of additional security offerings. For example, images stored in Azure Container Registry can be scanned using Microsoft Defender to ensure that they do not contain known malware, exploits, or malicious content. Container registries also enable us to protect internal resources by not exposing our images (and the files within them) to the general public.

While we can utilize the public registries and Azure Container Registry (ACR), sometimes we need to utilize a private, protected registry such as GitHub Container Registry. The documentation tends to focus on ACR, which can create the impression that other registries are difficult to use with Azure. In truth, the process is not very difficult.

GitHub Container Registry

To utilize a GitHub Container Registry, the process begins with creating a Personal Access Token (PAT). This is located in your Developer Settings, under Personal Access Tokens ( https://github.com/settings/tokens). Click Generate new token to create a token that will be used to access the repository. Under Select scopes, make sure to select read:packages to grant the token access to download the image.

The PAT acts as the password for accessing the registry. There are two other items you’ll need to complete the credential set: the username and the server. For GitHub, the registry server will be ghcr.io. The user name will be your GitHub handle. In my case, it would be kenmuse.

Azure Container Instance (ACI)

Next, you need to make the credentials available to the service. For Azure Container Instance, you can provide the credentials directly in the Bicep/ARM template or in the Azure Portal. In the Portal, you need to do the following:

  1. Under Image source, select Other registry
  2. Under Image type, select Private
  3. Enter the login credentials:
    • Image registry login server: ghcr.io
    • Image registry user name: Your GitHub handle
    • Image registry password: The PAT token with read:packages permissions

It’s that easy!

App Services

Azure App Services can be configured to utilize a private registry similarly. If you prefer Azure CLI, Microsoft provides guidance on using a private registry. They also have guidance for configuring the private registry in the Azure Portal. If you’re creating an ARM or Bicep template, these configuration settings are provided as App Settings which should be included in the template:

  • DOCKER_CUSTOM_IMAGE_NAME: The image name, in the form ghcr.io/owner-name/image-name:tag
  • DOCKER_REGISTRY_SERVER_URL: ghcr.io
  • DOCKER_REGISTRY_SERVER_USERNAME: Your GitHub handle
  • DOCKER_REGISTRY_SERVER_PASSWORD: The PAT token with read:packages permissions

Azure Container Apps

Azure Container Apps are very similar to ACI. In the Azure Portal, you need to specify the Image source as Docker Hub or other registries and the Image type as Private. This will enable you to specify the login credentials.

The Bicep template works similarly, with the credential details provided to the registries and secrets portions of the containerApp resource. Remember to pass in these credentials securely! The template would look something like this:

 1@secure()
 2param githubHandle string
 3
 4@secure()
 5param githubPat string
 6
 7// Other resources, including the container environment, Log Analytics, etc.
 8// ...
 9// ...
10
11resource containerApp 'Microsoft.Web/containerApps@2021-03-01' = {
12  name: containerAppName
13  location: location
14  properties: {
15    kubeEnvironmentId: containerEnvironment.id
16    configuration: {
17      ingress: {
18        external: true
19        targetPort: 443
20        transport: 'auto'
21        allowInsecure: false
22        traffic: [
23          {
24            latestRevision: true
25            weight: 100
26          }
27        ]
28      }
29      registries: [
30        {
31          server: 'ghcr.io'
32          username: githubHandle
33          passwordSecretRef: 'ghcr-password'
34        }
35      ]
36      secrets: [
37        {
38          name: 'ghcr-password'
39          value: githubPat
40        }
41      ]
42    }
43    template: {
44      containers: [
45        {
46          name: containerAppName
47          image: 'ghcr.io/${githubHandle}/myimage:v1'
48          resources: {
49            cpu: '2.0'
50            memory: '4.0Gi'
51          }         
52        }
53      ]
54      scale: {
55        minReplicas: 1
56        maxReplicas: 1
57      }
58    }
59  }
60}

Note that the image name (line 47) is the full path to the image, including the owner name. For personal accounts, this is the GitHub handle. For organizations, this is the organization name.

Azure Kubernetes Service

This one could be it’s own blog post! In fact, fellow MVP René van Osnabrugge actually covered this topic in depth. You can read more on his blog about configuring AKS to use the GitHub container registry.

The basic process is to create an image pull secret containing your credentials:

kubectl create secret docker-registry ghcr-secret --docker-server=https://ghcr.io/ --docker-username=notneeded --docker-password=<YourPatToken>

Then use the image pull secret ghcr-secret in the deployment YAML:

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: my-pod
 5  namespace: sample
 6spec:
 7  containers:
 8    - name: my-app-container
 9      image: ghcr.io/kenmuse/my-app:v1
10      imagePullPolicy: IfNotPresent
11  imagePullSecrets:
12    - name: ghcr-secret

Containers and Registries

Another long post, but there was a lot to cover. There are a lot of container solutions available in Azure, each with very similar ways to configure a private registry. While all of these examples are focused on GitHub Container Registry, the same approaches apply with other private registries. It’s one of the great things about containers – consistency.

Happy DevOp’ing!