title | description | services | author | ms.topic | ms.date | ms.author | ms.custom |
---|---|---|---|---|---|---|---|
Build, test, and deploy containers to Azure Kubernetes Service using GitHub Actions |
Learn how to use GitHub Actions to deploy your container to Kubernetes |
container-service |
azooinmyluggage |
article |
05/16/2022 |
atulmal |
github-actions-azure |
GitHub Actions gives you the flexibility to build an automated software development lifecycle workflow. You can use multiple Kubernetes actions to deploy to containers from Azure Container Registry to Azure Kubernetes Service with GitHub Actions.
- An Azure account with an active subscription. Create an account for free.
- A GitHub account. If you don't have one, sign up for free.
- A working Kubernetes cluster
A workflow is defined by a YAML (.yml) file in the /.github/workflows/
path in your repository. This definition contains the various steps and parameters that make up the workflow.
For a workflow targeting AKS, the file has three sections:
Section | Tasks |
---|---|
Authentication | Generate deployment credentials. |
Build | Build & push the container image |
Deploy | 1. Set the target AKS cluster |
2. Create a generic/docker-registry secret in Kubernetes cluster | |
3. Deploy to the Kubernetes cluster |
You can create a service principal by using the az ad sp create-for-rbac command in the Azure CLI. You can run this command using Azure Cloud Shell in the Azure portal or by selecting the Try it button.
az ad sp create-for-rbac --name "myApp" --role contributor --scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP> --sdk-auth
In the above command, replace the placeholders with your subscription ID, and resource group. The output is the role assignment credentials that provide access to your resource. The command should output a JSON object similar to this.
{
"clientId": "<GUID>",
"clientSecret": "<GUID>",
"subscriptionId": "<GUID>",
"tenantId": "<GUID>",
(...)
}
Copy this JSON object, which you can use to authenticate from GitHub.
Open ID Connect is an authentication method that uses short-lived tokens. Setting up Open ID Connect with GitHub Actions is more complex process that offers hardened security.
-
If you do not have an existing application, register a new Active Directory application and service principal that can access resources. Create the Active Directory application.
az ad app create --display-name myApp
This command will output JSON with an
appId
that is yourclient-id
. Save the value to use as theAZURE_CLIENT_ID
GitHub secret later.You'll use the
objectId
value when creating federated credentials with Graph API and reference it as theAPPLICATION-OBJECT-ID
. -
Create a service principal. Replace the
$appID
with the appId from your JSON output.This command generates JSON output with a different
objectId
and will be used in the next step. The newobjectId
is theassignee-object-id
.Copy the
appOwnerTenantId
to use as a GitHub secret forAZURE_TENANT_ID
later.az ad sp create --id $appId
-
Create a new role assignment by subscription and object. By default, the role assignment will be tied to your default subscription. Replace
$subscriptionId
with your subscription ID,$resourceGroupName
with your resource group name, and$assigneeObjectId
with the generatedassignee-object-id
. Learn how to manage Azure subscriptions with the Azure CLI.az role assignment create --role contributor --subscription $subscriptionId --assignee-object-id $assigneeObjectId --scopes /subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Web/sites/--assignee-principal-type ServicePrincipal
-
Run the following command to create a new federated identity credential for your active directory application.
- Replace
APPLICATION-OBJECT-ID
with the objectId (generated while creating app) for your Active Directory application. - Set a value for
CREDENTIAL-NAME
to reference later. - Set the
subject
. The value of this is defined by GitHub depending on your workflow:- Jobs in your GitHub Actions environment:
repo:< Organization/Repository >:environment:< Name >
- For Jobs not tied to an environment, include the ref path for branch/tag based on the ref path used for triggering the workflow:
repo:< Organization/Repository >:ref:< ref path>
. For example,repo:n-username/ node_express:ref:refs/heads/my-branch
orrepo:n-username/ node_express:ref:refs/tags/my-tag
. - For workflows triggered by a pull request event:
repo:< Organization/Repository >:pull_request
.
- Jobs in your GitHub Actions environment:
az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/<APPLICATION-OBJECT-ID>/federatedIdentityCredentials' --body '{"name":"<CREDENTIAL-NAME>","issuer":"https://token.actions.githubusercontent.com","subject":"repo:organization/repository:ref:refs/heads/main","description":"Testing","audiences":["api://AzureADTokenExchange"]}'
- Replace
To learn how to create a Create an active directory application, service principal, and federated credentials in Azure portal, see Connect GitHub and Azure.
Follow the steps to configure the secrets:
-
In GitHub, browse to your repository, select Settings > Secrets > New repository secret.
:::image type="content" source="media/kubernetes-action/secrets.png" alt-text="Screenshot shows the Add a new secret link for a repository.":::
-
Paste the contents of the above
az cli
command as the value of secret variable. For example,AZURE_CREDENTIALS
. -
Similarly, define the following additional secrets for the container registry credentials and set them in Docker login action.
- REGISTRY_USERNAME
- REGISTRY_PASSWORD
-
You will see the secrets as shown below once defined.
:::image type="content" source="media/kubernetes-action/kubernetes-secrets.png" alt-text="Screenshot shows existing secrets for a repository.":::
You need to provide your application's Client ID, Tenant ID, and Subscription ID to the login action. These values can either be provided directly in the workflow or can be stored in GitHub secrets and referenced in your workflow. Saving the values as GitHub secrets is the more secure option.
-
Open your GitHub repository and go to Settings.
-
Select Settings > Secrets > New secret.
-
Create secrets for
AZURE_CLIENT_ID
,AZURE_TENANT_ID
, andAZURE_SUBSCRIPTION_ID
. Use these values from your Active Directory application for your GitHub secrets:GitHub Secret Active Directory Application AZURE_CLIENT_ID Application (client) ID AZURE_TENANT_ID Directory (tenant) ID AZURE_SUBSCRIPTION_ID Subscription ID -
Similarly, define the following additional secrets for the container registry credentials and set them in Docker login action.
- REGISTRY_USERNAME
- REGISTRY_PASSWORD
The build and push of the container images is done using azure/docker-login@v1
action.
env:
REGISTRY_NAME: {registry-name}
CLUSTER_NAME: {cluster-name}
CLUSTER_RESOURCE_GROUP: {resource-group-name}
NAMESPACE: {namespace-name}
APP_NAME: {app-name}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
# Connect to Azure Container Registry (ACR)
- uses: azure/docker-login@v1
with:
login-server: ${{ env.REGISTRY_NAME }}.azurecr.io
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
# Container build and push to a Azure Container Registry (ACR)
- run: |
docker build . -t ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
working-directory: ./<path-to-Dockerfile-directory>
To deploy a container image to AKS, you will need to use the azure/k8s-deploy@v1
action. This action has five parameters:
Parameter | Explanation |
---|---|
namespace | (Optional) Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace |
manifests | (Required) Path to the manifest files, that will be used for deployment |
images | (Optional) Fully qualified resource URL of the image(s) to be used for substitutions on the manifest files |
imagepullsecrets | (Optional) Name of a docker-registry secret that has already been set up within the cluster. Each of these secret names is added under imagePullSecrets field for the workloads found in the input manifest files |
kubectl-version | (Optional) Installs a specific version of kubectl binary |
Note
The manifest files should be created manually by you. Currently there are no tools that will generate such files in an automated way, for more information see this sample repository with example manifest files.
Before you can deploy to AKS, you'll need to set target Kubernetes namespace and create an image pull secret. See Pull images from an Azure container registry to a Kubernetes cluster, to learn more about how pulling images works.
# Create namespace if doesn't exist
- run: |
kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o json | kubectl apply -f -
# Create image pull secret for ACR
- uses: azure/k8s-create-secret@v1
with:
container-registry-url: ${{ env.REGISTRY_NAME }}.azurecr.io
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
secret-name: ${{ env.SECRET }}
namespace: ${{ env.NAMESPACE }}
arguments: --force true
Complete your deployment with the azure/k8s-deploy@v1
action. Replace the environment variables with values for your application.
on: [push]
# Environment variables available to all jobs and steps in this workflow
env:
REGISTRY_NAME: {registry-name}
CLUSTER_NAME: {cluster-name}
CLUSTER_RESOURCE_GROUP: {resource-group-name}
NAMESPACE: {namespace-name}
SECRET: {secret-name}
APP_NAME: {app-name}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
# Connect to Azure Container Registry (ACR)
- uses: azure/docker-login@v1
with:
login-server: ${{ env.REGISTRY_NAME }}.azurecr.io
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
# Container build and push to a Azure Container Registry (ACR)
- run: |
docker build . -t ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
working-directory: ./<path-to-Dockerfile-directory>
# Set the target Azure Kubernetes Service (AKS) cluster.
- uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: ${{ env.CLUSTER_NAME }}
resource-group: ${{ env.CLUSTER_RESOURCE_GROUP }}
# Create namespace if doesn't exist
- run: |
kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o json | kubectl apply -f -
# Create image pull secret for ACR
- uses: azure/k8s-create-secret@v1
with:
container-registry-url: ${{ env.REGISTRY_NAME }}.azurecr.io
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
secret-name: ${{ env.SECRET }}
namespace: ${{ env.NAMESPACE }}
arguments: --force true
# Deploy app to AKS
- uses: azure/k8s-deploy@v1
with:
manifests: |
${{ github.workspace }}/manifests/deployment.yaml
${{ github.workspace }}/manifests/service.yaml
images: |
${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
imagepullsecrets: |
${{ env.SECRET }}
namespace: ${{ env.NAMESPACE }}
The Azure Kubernetes Service set context action (azure/aks-set-context) can be used to set cluster context before other actions like k8s-deploy. For Open ID Connect, you'll use the Azure Login action before set context.
on: [push]
# Environment variables available to all jobs and steps in this workflow
env:
REGISTRY_NAME: {registry-name}
CLUSTER_NAME: {cluster-name}
CLUSTER_RESOURCE_GROUP: {resource-group-name}
NAMESPACE: {namespace-name}
SECRET: {secret-name}
APP_NAME: {app-name}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
# Connect to Azure Container Registry (ACR)
- uses: azure/docker-login@v1
with:
login-server: ${{ env.REGISTRY_NAME }}.azurecr.io
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
# Container build and push to a Azure Container Registry (ACR)
- run: |
docker build . -t ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
working-directory: ./<path-to-Dockerfile-directory>
- uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Set the target Azure Kubernetes Service (AKS) cluster.
- uses: azure/aks-set-context@v2.0
with:
cluster-name: ${{ env.CLUSTER_NAME }}
resource-group: ${{ env.CLUSTER_RESOURCE_GROUP }}
# Create namespace if doesn't exist
- run: |
kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o json | kubectl apply -f -
# Create image pull secret for ACR
- uses: azure/k8s-create-secret@v1
with:
container-registry-url: ${{ env.REGISTRY_NAME }}.azurecr.io
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
secret-name: ${{ env.SECRET }}
namespace: ${{ env.NAMESPACE }}
arguments: --force true
# Deploy app to AKS
- uses: azure/k8s-deploy@v1
with:
manifests: |
${{ github.workspace }}/manifests/deployment.yaml
${{ github.workspace }}/manifests/service.yaml
images: |
${{ env.REGISTRY_NAME }}.azurecr.io/${{ env.APP_NAME }}:${{ github.sha }}
imagepullsecrets: |
${{ env.SECRET }}
namespace: ${{ env.NAMESPACE }}
When your Kubernetes cluster, container registry, and repository are no longer needed, clean up the resources you deployed by deleting the resource group and your GitHub repository.
[!div class="nextstepaction"] Learn about Azure Kubernetes Service
[!div class="nextstepaction"] Learn how to create multiple pipelines on GitHub Actions with AKS
- Kubectl tool installer (
azure/setup-kubectl
): Installs a specific version of kubectl on the runner. - Kubernetes set context (
azure/k8s-set-context
): Set the target Kubernetes cluster context which will be used by other actions or run any kubectl commands. - AKS set context (
azure/aks-set-context
): Set the target Azure Kubernetes Service cluster context. - Kubernetes create secret (
azure/k8s-create-secret
): Create a generic secret or docker-registry secret in the Kubernetes cluster. - Kubernetes deploy (
azure/k8s-deploy
): Bake and deploy manifests to Kubernetes clusters. - Setup Helm (
azure/setup-helm
): Install a specific version of Helm binary on the runner. - Kubernetes bake (
azure/k8s-bake
): Bake manifest file to be used for deployments using helm2, kustomize or kompose. - Kubernetes lint (
azure/k8s-lint
): Validate/lint your manifest files.