Reinstalling Istio on GKE Using ArgoCD
A step-by-step guide to migrating an existing Istio installation on GKE to be managed by ArgoCD using Helm charts. Covers backing up resources, reserving the gateway IP, creating ArgoCD Application manifests, and cleanly uninstalling and reinstalling Istio.
Our team started adopting GitOps practices to manage everything running in our GKE clusters, and we chose ArgoCD for the job. One of the tasks that fell on me was migrating the existing Istio installation to be managed by ArgoCD. It sounds daunting at first, but after going through it, it’s surprisingly straightforward.
Backing Up Istio Resources
Before touching anything, export all existing Istio CRD configs across all namespaces. This gives you a safety net to restore from if something goes sideways.
kubectl get gateway -A -o yaml > istio-gateways-backup.yamlkubectl get virtualservice -A -o yaml > istio-virtualservices-backup.yamlkubectl get destinationrule -A -o yaml > istio-destinationrules-backup.yamlkubectl get serviceentry -A -o yaml > istio-serviceentries-backup.yamlkubectl get authorizationpolicy -A -o yaml > istio-authorizationpolicies-backup.yamlkubectl get peerauthentication -A -o yaml > istio-peerauthentications-backup.yamlkubectl get requestauthentication -A -o yaml > istio-requestauthentications-backup.yamlkubectl get envoyfilter -A -o yaml > istio-envoyfilters-backup.yamlTo clean up the exported YAML, you can pipe the commands through kubectl-neat — it strips out the managed fields and other noise that makes diffs unreadable.
Making Sure the IP is Reserved
To keep running applications unaffected, the gateway IP must stay the same after reinstall. Make sure the IP address used by your ingress gateway is reserved as a static IP in Google Cloud Console. You’ll reference it explicitly in the ArgoCD Application manifest later.
Creating ArgoCD Application Manifests
To install Istio via ArgoCD, I use the official Helm charts. There are three charts to install in order: base, istiod, and gateway.
First, add the Istio Helm repo:
helm repo add istio https://istio-release.storage.googleapis.com/chartshelm repo update1. Base
The base chart installs all the CRDs that Istio requires. This must go first because the other charts depend on those CRDs being present.
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: istio-base namespace: argocd finalizers: - resources-finalizer.argocd.argoproj.iospec: project: default source: repoURL: https://istio-release.storage.googleapis.com/charts chart: base targetRevision: 1.25.2 helm: valuesObject: global: istioNamespace: istio-system destination: server: https://kubernetes.default.svc namespace: istio-system syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true ignoreDifferences: - group: "" kind: ValidatingWebhookConfiguration name: istiod-default-validator jsonPointers: - /webhooks/0/failurePolicyYou might also want to move the valuesObject content into a separate values file — I’ve read that’s considered best practice for keeping manifests clean.
2. Istio Discovery (istiod)
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: istio-discovery namespace: argocd finalizers: - resources-finalizer.argocd.argoproj.iospec: project: default source: repoURL: https://istio-release.storage.googleapis.com/charts chart: istiod targetRevision: 1.25.2 helm: valuesObject: autoscaleEnabled: true autoscaleMin: 1 autoscaleMax: 5 rollingMaxSurge: 100% rollingMaxUnavailable: 25% destination: server: https://kubernetes.default.svc namespace: istio-system syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true3. Istio IngressGateway
Notice that we pin the loadBalancerIP to the reserved address from the earlier step, ensuring the same IP is used after reinstall.
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: istio-ingressgateway namespace: argocd finalizers: - resources-finalizer.argocd.argoproj.iospec: project: default source: repoURL: https://istio-release.storage.googleapis.com/charts chart: gateway targetRevision: 1.25.2 helm: valuesObject: service: annotations: networking.gke.io/load-balancer-type: Internal networking.gke.io/internal-load-balancer-allow-global-access: "true" loadBalancerIP: <YOUR_RESERVED_IP> resources: requests: cpu: 100m memory: 128Mi destination: server: https://kubernetes.default.svc namespace: istio-system syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=trueNote: If you’re running Istio in ambient mode with CNI, you’ll also need to install the
cniandztunnelcharts as a second step, beforeistiod.
Uninstalling Istio
With the backups done and the ArgoCD manifests ready, go ahead and uninstall the existing Istio installation:
istioctl uninstall --purgeCommitting Manifests to ArgoCD Repo
Commit the three Application manifests to your GitOps repository. ArgoCD will pick them up and start syncing. Wait for all three apps to reach a healthy state.
After that, expect some errors — fix and debug them as they come. The most common one I ran into was namespaces losing their Istio injection label.
Verifying Istio Injection Labels
Check which namespaces have the injection label:
kubectl get ns -L istio-injectionMake sure the correct namespaces are labeled enabled. If any are missing, re-apply the label:
kubectl label namespace <YOUR_NAMESPACE> istio-injection=enabledThat’s it. Once the injection labels are correct and all ArgoCD apps are synced and healthy, the migration is done.