, , , ,

Google ACM: ConfigSync with Helm


Helm is to Kubernetes as yum and apt are to Linux. It’s the most mature and often de facto tool for Kubernetes package management. It makes installation of k8s software as simple as:

helm install <PACKAGENAME>

Anthos Config Management (ACM) is one of Google’s services that does continuous deployment using GitOps methodologies. If you’re familiar with other GitOps tools, I would say that it most closely compares to ArgoCD or FluxCD. 

Because I started my career in infrastructure and operations, one of the qualities that I appreciate most about ACM is that it was originally designed for platform operators. ACM can be used to bootstrap clusters and ensure they have desired security safeguards, compliance requirements, business rules, and business logic in a known good state. 

For reference, Google Config Sync was open sourced in 2022 and its source is available here.

https://github.com/GoogleContainerTools/kpt-config-sync

Config Sync has supported public helm charts for some time via kustomize. Here’s a sample kustomize file that calls a public helm chart.

# ./base/kustomization.yaml
helmCharts:
- name: cert-manager
  repo: https://charts.jetstack.io
  version: v1.5.3
  releaseName: my-cert-manager
  namespace: cert-manager

Unfortunately as of Jan 2023, kustomize does not yet support OCI compliant chart registries which until now, made it challenging to deploy helm charts from OCI compliant repositories such as Google Artifact Repository, JFrog Artifactory, etc. 

To better support this use case, the ACM engineering team has recently introduced direct helm support. 

ACM Config Sync Helm Deployer

The new helm deployer can be referenced within a RootSync or RepoSync as shown here.

  1. Create a RootSync or RepoSync object. Note that helm values can be overridden beneath `spec.helm.values`.
cat <<EOF>> root-sync-example.yamlapiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
  name: root-sync-example
  namespace: config-management-system
spec:
  sourceFormat: unstructured
  sourceType: helm
  helm:
    repo: oci://${AR_REGION}.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}
    chart: mysql
    version: 9.3.1
    releaseName: my-mysql
    namespace: test
    auth: gcpserviceaccount
    gcpServiceAccountEmail: ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
    # From Anthos Config Management version 1.13.1 and later, you can use
    # the optional field spec.helm.values to override default values.
    # You can use the same format as the default values file to override
    # default values.
    values:
      image:
        pullPolicy: Always
      primary:
        resources:
          limits:
            cpu: 250m
            memory: 256Mi
          requests:
            cpu: 250m
            memory: 256Mi
EOF
  1. Apply the RootSync object
kubectl apply -f root-sync.yaml
  1. Verify the status of the root repository
nomos status --contexts=$(kubectl config current-context)

Connecting to clusters...

*gke_stolos-dev_us-central1-xxxxxx
  --------------------
  <root>:root-sync   https://kubernetes.github.io/ingress-nginx/ingress-nginx:4.0.5                 
  SYNCED             ingress-nginx:4.0.5   
  Managed resources:
     NAMESPACE       NAME                                                                                     STATUS    SOURCEHASH
                     clusterrole.rbac.authorization.k8s.io/my-ingress-nginx                                   Current   ingress
                     clusterrole.rbac.authorization.k8s.io/my-ingress-nginx-admission                         Current   ingress
                     clusterrolebinding.rbac.authorization.k8s.io/my-ingress-nginx                            Current   ingress

clusterrolebinding.rbac.authorization.k8s.io/my-ingress-nginx-admission                  Current   ingress
                     ingressclass.networking.k8s.io/nginx                                                     Current   ingress
                     namespace/ingress-nginx                                                                  Current   ingress
                     validatingwebhookconfiguration.admissionregistration.k8s.io/my-ingress-nginx-admission   Current   ingress
     ingress-nginx   configmap/my-ingress-nginx-controller                                                    Current   ingress
     ingress-nginx   deployment.apps/my-ingress-nginx-controller                                              Current   ingress
     ingress-nginx   job.batch/my-ingress-nginx-admission-create                                              Current   ingress
     ingress-nginx   job.batch/my-ingress-nginx-admission-patch                                               Current   ingress
     ingress-nginx   role.rbac.authorization.k8s.io/my-ingress-nginx                                          Current   ingress
     ingress-nginx   role.rbac.authorization.k8s.io/my-ingress-nginx-admission                                Current   ingress
     ingress-nginx   rolebinding.rbac.authorization.k8s.io/my-ingress-nginx                                   Current   ingress
     ingress-nginx   rolebinding.rbac.authorization.k8s.io/my-ingress-nginx-admission                         Current   ingress
     ingress-nginx   service/my-ingress-nginx-controller                                                      Current   ingress
     ingress-nginx   service/my-ingress-nginx-controller-admission                                            Current   ingress
     ingress-nginx   serviceaccount/my-ingress-nginx                                                          Current   ingress
     ingress-nginx   serviceaccount/my-ingress-nginx-admission                                                Current   ingress

Private Repository

For a private repository, we may need to pass a token so that ConfigSync can access the repository. The additional steps to pass these credentials are detailed below.

  1. Create a secret with helm repo username and password
kubectl create secret generic foo \
--namespace=config-management-system \
--from-literal=username=helm-user \
--from-literal=password=helm-password
  1. Create the RootSync and note the last few lines where we pass the auth/token information with the secret name.
apiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
  name: root-sync
  namespace: config-management-system
spec:
  sourceFormat: unstructured
  sourceType: helm
  helm:
    #repo is the helm repository URL
    repo: ${HELM_REPO_URL}
    #chart is the helm chart name
    chart: ${HELM_CHART_NAME}
    version: ${HELM_CHART_VERSION}
    releaseName: ${HELM_RELEASE_NAME}
    namespace: ${HELM_RELEASE_NAMESPACE}
    values:
      resources:
        requests:
          cpu: 200m
          memory: 250Mi
      extraEnvs:
      - name: NAME_1
  value: "val1"
      - name: NAME_2
  value: "val2"    
    auth: token
    secretRef:                   #<-- These lines
      name: foo                  #<-- here
  1. Apply the RootSync object
kubectl apply -f root-sync.yaml
  1. Verify the status of the root repository
nomos status --contexts=$(kubectl config current-context)

Google Workload Identity (with Google Cloud Source Repos)

The last scenario that we’ll show is synchronizing with Google Artifact Registry using a GCP Service Account.

  1. Ensure workload identity is enabled
  2. Assign the  ‘artifactregistry.reader’ role to your Google Service Account
export PROJECT_ID=YOUR_PROJECT_ID
export GSA_NAME=YOUR_GSA_EMAIL_NAME
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com --role=roles/artifactregistry.reader
  1. Then the ‘iam.workloadIdentityUser’ role to your Google Service Account. 
gcloud iam service-accounts add-iam-policy-binding --project=${PROJECT_ID} \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:${PROJECT_ID}.svc.id.goog[config-management-system/root-reconciler]" \
    ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
  1. Configure the RootSync object and apply
apiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
  name: root-sync
  namespace: config-management-system
spec:
  sourceFormat: unstructured
  sourceType: helm
  helm:
    #repo is the helm repository URL
    repo: ${HELM_REPO_URL}
    #chart is the helm chart name
    chart: ${HELM_CHART_NAME}
    version: ${HELM_CHART_VERSION}
    releaseName: ${HELM_RELEASE_NAME}
    namespace: ${HELM_RELEASE_NAMESPACE}  
    auth: gcpserviceaccount        #<-- Service account type
    gcpServiceAccountEmail: ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com  #<-- Service Acct
  1. Apply the RootSync Object
kubectl apply -f root-sync.yaml
  1. Verify the sync status of the root repository
nomos status --contexts=$(kubectl config current-context)

Additional References for Further Exploration

ConfigSync w/ Helm via Kustomize:

https://cloud.google.com/anthos-config-management/docs/tutorials/config-sync-helm

ConfigSync w/ New Helm Deployer

https://cloud.google.com/anthos-config-management/docs/how-to/sync-helm-charts-from-artifact-registry

Categories

Archives