Search code examples
kubernetesazure-aks

How to avoid having to enter image version in deployment yaml and use most recent image from azure container registry


I'm trying to deploy an app to AKS cluster. Everytime I push changes to my branch, I want AKS to redeploy pods and make use of the most recent tag (which I have versioned with $(Build.BuildId))

The problem is right now I have to manually retrieve this build version and enter it into deployment.yaml and then run kubectl apply -f deployment.yaml for the change to go ahead. For example, the most recent tag is 58642, so I would have to log into my Azure Container Registry, retrieve the version number, update the deployment.yaml, and then apply for changes to take effect.

How can I change my setup here so that the most recently built and tagged container is deployed to the AKS cluster as part of my CICD?

Here is my deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mission-model-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mission-model-api
  template:
    metadata:
      labels:
        app: mission-model-api
    spec:
      containers:
      - name: mission-model-api
        image: my_registry.azurecr.io/serving/mission_model_api:58642
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 250m
            memory: 256Mi
        ports:
        - containerPort: 80
        imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: mission-model-api
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    app: mission-model-api

And here is my CI/CD azure-pipelines.yaml

variables:
  tag: '$(Build.BuildId)'
  vmImageName: 'ubuntu-latest'
  envName: 'poc-releases'
  docker_image_name: API
  imagePullSecret: 'AUTH'
  dockerRegistryServiceConnection: 'XX'

trigger:
  batch: true
  branches:
    include:
      - feature/*

stages:
- stage: Build
  displayName: Build stage
  pool:
    vmImage: $(vmImageName)
  jobs:
    - job: Build
      displayName: Build job
      variables:
        PROJECT_DIR: $(Build.SourcesDirectory)/apps/$(docker_image_name)
        IMAGE_AND_TAG: "$(docker_image_name):$(tag)"
      steps:
        - script: |
            az acr login --name my_registry.azurecr.io --username user --password $(acr_password)
          displayName: ACR Login
        - bash: >
            docker build -f ./Dockerfile -t "$(IMAGE_AND_TAG)" .
          displayName: Build docker image
          workingDirectory: $(PROJECT_DIR)
        - script: |
            REGISTRY_PATH=my_registry.azurecr.io/serving
            docker tag "$(IMAGE_AND_TAG)" "$REGISTRY_PATH/$(IMAGE_AND_TAG)"
            docker push "$REGISTRY_PATH/$(IMAGE_AND_TAG)"
          displayName: Tag and Push to ACR
        - task: PublishPipelineArtifact@0
          inputs:
            artifact: 'manifests'
            artifactName: 'manifests'
            targetPath: '$(PROJECT_DIR)/manifests'

- stage: Deploy_BVT
  displayName: Deploy BVT
  dependsOn: Build
  jobs:
  - deployment: Deploy_BVT
    pool:
      vmImage: $(vmImageName)
    environment: '$(envName).ingress-basic'
    strategy:
      runOnce:
        deploy:
          steps:
          - task: DownloadPipelineArtifact@1
            inputs:
              artifactName: 'manifests'
              downloadPath: '$(System.ArtifactsDirectory)/manifests'
          - task: KubernetesManifest@0
            displayName: Create imagePullSecret
            inputs:
              action: createSecret
              secretName: $(imagePullSecret)
              namespace: ingress-basic
              dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
          - task: KubernetesManifest@0
            displayName: Deploy to Kubernetes cluster
            inputs:
              action: deploy
              namespace: "ingress-basic"
              manifests: |
                $(System.ArtifactsDirectory)/manifests/cluster-isseur.yaml
                $(System.ArtifactsDirectory)/manifests/deployment.yaml
                $(System.ArtifactsDirectory)/manifests/ingress.yaml
              imagePullSecrets: |
                $(imagePullSecret)
              containers: |
                "$REGISTRY_PATH/$(docker_image_name):$(tag)"

Solution

  • Replace tokens task can solve your problem. I use it most of the time.

    1. For the deployment yaml, change the image like this.
    image: my_registry.azurecr.io/serving/mission_model_api:#{Build.BuildId}#
    
    1. Before the task: PublishPipelineArtifact@0 task in Build stage, put a Replace Tokens task. You should add it as an extension to Azure DevOps
    - task: replacetokens@4
      inputs:
        targetFiles: '**/deployment.yml'
        encoding: 'auto'
        tokenPattern: 'default'
        writeBOM: true
        actionOnMissing: 'warn'
        keepToken: false
        actionOnNoFiles: 'continue'
        enableTransforms: false
        useLegacyPattern: false
        enableTelemetry: true
    

    Then it should work as you expected.