With argocd-image-updater
we can automate deployments in our Kubernetes cluster, after our continous integration (CI) pipelines have built and pushed the docker image into Artifact Registry or our preferred artifact repository. Once correctly configured, argocd-image-updater
will continuously check our Artifact Registry and check if any newer images exist, if they exist it can create a git commit to update the image in a kubernetes Deployment
, Pod
, CronJob
, etc. This way we achieve automated deployments in ArgoCD
.
In this post we will check how to Authenticate to Artifact Registry with argocd-image-updater
.
Imagine we have a highly available Artifact Registry docker repository in multiregion us
. Where we store a docker image called my-image
which has 3 version tags: 0.0.1
, 0.0.2
and 0.0.3
. The full path of the image is:
us-docker.pkg.dev/my-gcp-project/my-artifact-repository/my-image
Install the helm chart for argocd-image-updater
, I like pulling and untaring it locally for testing and getting familiar with it's templates:
helm pull argo/argocd-image-updater --untar
Proceed to customize the values.yaml
file of the argocd-image-updater
with the following values:
config:
registries:
- name: GCP Artifact Registry
api_url: https://us-docker.pkg.dev
prefix: us-docker.pkg.dev
credentials: ext:/scripts/gcp-auth.sh # defined in authScripts below
credsexpire: 30m
default: true
authScripts:
enabled: true
scripts:
gcp-auth.sh: |
#!/bin/sh
ACCESS_TOKEN=$(wget --header 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token -q -O - | grep -Eo '"access_token":.*?[^\\]",' | cut -d '"' -f 4)
echo "oauth2accesstoken:$ACCESS_TOKEN"
serviceAccount:
create: true
annotations: {
# the GCP service account to bind the present k8s service account
iam.gke.io/gcp-service-account: sa-argocd-image-updater@my-gcp-project.iam.gserviceaccount.com
}
Install the helm chart of argocd-image-updater
with the custom values defined above, using the following helm command:
helm install argocd-image-updater \
charts/argocd-image-updater \
-n argocd \
-f ./path/to/values.yaml
Now you need to create a GCP service account assign the role roles/artifactregistry.admin
to it and finally bind it to the argocd-image-updater
service account inside your kubernetes cluster. I created a helper function in Pulumi
to achieve (you can do it via gcloud
commands or terraform
if you prefer):
type BinderOptions = {
tag: string; // tag for easily identifying pulumi urns
env: Environments;
role: string; // Example: "roles/dns.admin"
gcpServiceAccountName: string;
gcpServiceAccountProject?: string; // GCP project where the service account is created
// Note: k8s service account and namespace, is usually created by helm or custom resource in argocd
k8sServiceAccountName: string;
k8sNamespace: string; // k8s namespace where service account is located
};
const gcpServiceAccountToKubernetesServiceAccountBinder = async ({
tag,
gcpServiceAccountName,
k8sServiceAccountName,
k8sNamespace,
env,
role,
gcpServiceAccountProject,
}: BinderOptions) => {
// Create GCP service account
const gcpServiceAccount = new gcp.serviceaccount.Account(
`${env}-gcp-sa-${tag}`,
{
accountId: gcpServiceAccountName,
project: gcpServiceAccountProject || gcpProject,
}
);
// bind the GCP sa to a role
const iamPolicyBinding = new gcp.projects.IAMBinding(
`${env}-gcp-sa-policy-${tag}-binding`,
{
role,
members: [
pulumi.interpolate`serviceAccount:${gcpServiceAccount.email}`,
],
project: gcpServiceAccountProject || gcpProject,
}
);
// Allow k8s serv account to use roles from the GCP service account
const workloadIdentityBinding = new gcp.serviceaccount.IAMBinding(
`${env}-gcp-sa-${tag}-workload-identity-binding`,
{
serviceAccountId: gcpServiceAccount.name,
role: "roles/iam.workloadIdentityUser",
members: [
`serviceAccount:${gcpProject}.svc.id.goog[${k8sNamespace}/${k8sServiceAccountName}]`,
],
}
);
You can call the function in this way:
const argoCdImageUpdaterAppSaBinderOptions: BinderOptions = {
tag: "argocd-image-updater",
gcpServiceAccountName: "sa-argocd-image-updater",
k8sServiceAccountName: "argocd-image-updater",
k8sNamespace: "argocd",
env,
role: "roles/artifactregistry.admin",
};
gcpServiceAccountToKubernetesServiceAccountBinder(
argoCdImageUpdaterAppSaBinderOptions
);
Deploy above changes in your infrastructure.
For debugging you can ssh
or attach a debug container into your running argocd-image-updater
pod and run this commands to check everything is as expected:
cat /app/config/registries.conf
# Output
registries:
- api_url: https://us-docker.pkg.dev
credentials: ext:/scripts/gcp-auth.sh
credsexpire: 30m
default: true
name: GCP Artifact Registry
prefix: us-docker.pkg.dev
Let's also check the gcp-auth script works as expected:
./scripts/gcp-auth.sh
# Output
oauth2accesstoken:my-secret-token
You can use argocd-image-updater test
to test the updater is configured correctly:
argocd-image-updater test us-docker.pkg.dev/my-gcp-project/my-artifact-repository/my-image \
--credentials ext:/scripts/gcp-auth.sh \
--registries-conf-path="/app/config/registries.conf" \
--semver-constraint 0.0.*
--update-strategy semver
The above command will give you at the end of the output the following:
...
INFO[0000] Found 3 tags in registry application=test image_alias= image_name=us-docker.pkg.dev/my-gcp-project/my-artifact-repository/my-image registry_url=us-docker.pkg.dev
DEBU[0000] found 3 from 3 tags eligible for consideration image=us-docker.pkg.dev/my-gcp-project/my-artifact-repository/my-image
INFO[0000] latest image according to constraint is us-docker.pkg.dev/my-gcp-project/my-artifact-repository/my-image:0.0.3
...