ArgoCD - what need be done after build a new image

Viewed 180

I have deployed the application via ArgoCD successfully, and I can access it via its ingress url

The applicaiton uses the image name with latest tag, such as

image: <private_registry>/app_1_service:latest

I also manage other tags on same of tag latest, such as image:<commit_id> or image:<1.0.xxx>

Now, developers will update the codes, after commit changes, a gitlab pipeline autoamtically runs and build a new image and override to tag latest with other tags and push to private docker registry

So what's the next step in ArgoCD?

How argocd know the application is changed, and need be redeployed, and the image:latest need be pull again?

3 Answers

ArgoCD supports 2 types of application syncing policies:

  1. manual: a user will login into the dashboard and update the image/chart version
  2. automatic: ArgoCD will poll the container registry at fixed interval (e.g 3 min) and check if a new image's/chart's version is available based on a pattern. You can use regex to specify the image pattern. Argo will compare the current version with the new one.

Auto sync option in ArgoCD dashboard

Here's an example of deploying helm from Jfrog artifact registry automatically:

project: default
source:
  repoURL: 'https://abc.jfrog.io/artifactory/helm'
  targetRevision: '*.*.*' # set your regex pattern here
  helm:
    parameters:
      - name: image.tag
        env: dev
  chart: frontend-chart
destination:
  server: 'https://kubernetes.default.svc'
  namespace: default
syncPolicy:
  automated: {} # enables auto syncing

You can use ArgoCD image-updater

But before using the the image-updater, you need to install and set appropriate permission

helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd-image-updater argo/argocd-image-updater

Once image-updator is up and running, then you need to set a few annotations in the Argocd application, as the update workers on different strategies

semver - Update to the latest version of an image considering semantic versioning constraints latest - Update to the most recently built image found in a registry
digest - Update to the latest version of a given version (tag), using the tag's SHA digest
name - Sorts tags alphabetically and update to the one with the highest cardinality

latest strategies working awesome with tagging under some regex and digest more suited for testing environment.

update-strategies

You can also pull the private image from gitlab as well.

Here is the working example with helm-release

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-alias.allow-tags: 'regexp:^1.3.0-SNAPSHOT.[0-9]+$'
    argocd-image-updater.argoproj.io/image-alias.force-update: 'true'
    argocd-image-updater.argoproj.io/image-alias.pull-secret: 'pullsecret:develop-namespace/develop-app-gitlab-secrets'
    argocd-image-updater.argoproj.io/image-alias.update-strategy: latest
    argocd-image-updater.argoproj.io/image-list: >-
      image-alias=registry.gitlab.com/myorg/my-test-image
  finalizers:
    - resources-finalizer.argocd.argoproj.io
  labels:
    app.kubernetes.io/instance: develop-platform
  name: develop-app
  namespace: argocd
spec:
  destination:
    namespace: develop-app
    server: 'https://kubernetes.default.svc'
  project: develop-app-west6-b
  source:
    helm:
      releaseName: develop-app
      valueFiles:
        - develop-platform/values.yaml
    path: helm-chart/helm-chart
    repoURL: 'https://gitlab.com/my-org/develop-app.git'
    targetRevision: staging
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

If you need digest or just a latest then remove this

  argocd-image-updater.argoproj.io/image-alias.allow-tags: 'regexp:^1.3.0-SNAPSHOT.[0-9]+$'

this is working base on regex. so in your case <1.0.xxx>

it can be 'regexp:^1.0.[0-9]+$'

If everything configured properly and image updater working fine then you should be able to see logs image updater logs like this

time="2022-04-27T15:18:36Z" level=info msg="Successfully updated image 'registry.gitlab.com/test-image:0.3.0-SNAPSHOT.115' to 'registry.gitlab.com/test-image:0.3.0-SNAPSHOT.118'

If you are using latest tag, the most simple way is this

  • set your k8s yaml imagePullPolicy to Always
  • add below step in gitlab-ci.yml to restart application by calling argocd api
argocd-restart:
    image: argoproj/argocd
    stage: deploy
    variables:
      GIT_STRATEGY: none
      ARGOCD_SERVER: "192.111.111.111:30000"
      # gitlab admin panel variable
      # ARGOCD_USERNAME: "admin"
      # ARGOCD_PASSWORD: "XXXXXX"
    before_script:
      - echo "ARGOCD_SERVER:$ARGOCD_SERVER"
      - echo "ARGOCD_APP_NAME:$ARGOCD_APP_NAME"
      - echo "ARGOCD_USERNAME:$ARGOCD_USERNAME"
    script:
      - argocd login "${ARGOCD_SERVER}" --insecure --username "${ARGOCD_USERNAME}" --password "${ARGOCD_PASSWORD}"
      - argocd app actions run "$ARGOCD_APP_NAME" restart --kind Deployment |& tee response.txt
      - cat response.txt
      # if response.txt have content, exit with error, empty response means success
      - if [ -s response.txt ]; then exit 1; fi
    only:
      - master
      - dev

argocd cli doc: https://argo-cd.readthedocs.io/en/stable/user-guide/commands/argocd_app_actions_run/

Above solution is not suit for production, because

  • imagePullPolicy: Always means k8s always need to connect registry
  • download image everytime when restart is waste of resource
  • don't have version control

so for stg and prod, i do this. Create a helm value file only for image tag such as stg-image-tag.yaml.

image:
  tag: "stg-v0.0.1"

Add this file to argocd app config enter image description here

Modify gitlab-ci.yml, build image with new image tag, and commit the image tag to stg-image-tag.yaml

  • i use $CI_PIPELINE_IID as version number in image tag.
  • i have branch name for each env
docker-build:
  image: docker
  stage: build
  variables:
    # REGISTRY_SERVER: 192.168.111.111
    # REGISTRY_USER: xxx
    # REGISTRY_PASSWORD: xxx
  before_script:
    - IMAGE_TAG="$CI_COMMIT_REF_SLUG-v0.0.$CI_PIPELINE_IID"
  script:
    - docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASSWORD" $REGISTRY_SERVER
    - docker build .
      -t "$NEW_IMAGE_REPO:latest"
      -t "$NEW_IMAGE_REPO:$IMAGE_TAG"
    - docker push "$NEW_IMAGE_REPO" --all-tags
    - echo IMAGE_TAG=$IMAGE_TAG >> IMAGE_TAG.env
    - cat IMAGE_TAG.env
  artifacts:
    reports:
      # add IMAGE_TAG to other jobs as env var
      dotenv: IMAGE_TAG.env
    expire_in: "86400" # 1 day

commit-image-tag:
  image: curlimages/curl
  stage: deploy
  needs:
    - job: docker-build
      artifacts: true
  variables:
    GIT_STRATEGY: none
    GITLAB_PROJECT_ID: "111"
    GITLAB_PROJECT_TOKEN: "xxxxxxxxxxx"
  before_script:
    - echo "IMAGE_TAG:$IMAGE_TAG"
  script:
    - |
      cat <<EOF > body.txt
      {
        "branch":"master",
        "commit_message":"update image tag ${IMAGE_TAG}",
        "actions":[
          {
            "action":"update",
            "file_path":"helm-charts/${CI_COMMIT_REF_SLUG}-image-tag.yaml",
            "content":"image:\n  tag: $IMAGE_TAG"
          }
        ]
      }
      EOF
    - |
      cat <<EOF > header.txt
      Authorization: Bearer ${GITLAB_PROJECT_TOKEN}
      Content-Type: application/json
      EOF
    - curl --insecure "${CI_SERVER_URL}/api/v4/projects/${GITLAB_PROJECT_ID}/repository/commits" 
      -i --output response.txt
      --header @header.txt --data @body.txt
      --silent --write-out "%{response_code}" > response_code.txt
    - cat response.txt
    # error if response code is not 201
    - if [ "$(cat response_code.txt)" != "201" ]; then exit 1; fi
  only:
    - stg
    - prod

Then you setup a webhook so argocd will refresh image tag immediately. otherwise argocd will refresh every 3 min. enter image description here

Related