Declarative Meets Mesh: Deploying Istio Ambient With ArgoCD

Declarative Meets Mesh: Deploying Istio Ambient With ArgoCD

Workloads are meant to be managed in a declarative way within Kubernetes. When it comes to thinking about what declarative means in this case, it's "tell me what to do, not how to do it". For example, you tell Kubernetes what container image you want to use, but you don't tell it how to go and get it.

With a production-grade Istio configuration, the same rules should apply.

In this blog post, you'll learn how to deploy Istio Ambient Mesh via ArgoCD.

Istio Ambient Mesh Recap

If you're new to Istio Ambient Mesh, as in, have only deployed Istio with sidecars or haven't used a Service Mesh at all, below is a breakdown of the components that make up Ambient Mesh and why they're important

Istiod is, both in Ambient Mesh and standard Istio with sidecars, the Control Plane. It's the brains of the operation. If you're sending destination rules or circuit breaking rules to the mesh, it's going through Istiod. Istiod then sends those configurations to the data plane.

Istio CRDs (you'll see it called istio-base most likely) is how the Kubernetes API is extended so engineers can use Istio objects within a Manifest (much like any other CRD).

Ztunnel, in the case of Ambient Mesh, is the proxy. Instead of using Envoy, you can use a more performant, memory safe (it's written in Rust), and lightweight proxy for apps that are enrolled into the mesh. It still uses xDS as the dynamic configuration protocol, but it's a lighter version in comparison to Envoy. Ztunnel is deployed as a Daemonset, so instead of having sidecar containers for each Pod that's enrolled into the mesh, you have one Pod per Worker Node. Ztunnel hands L4 traffic.

💡
A big downside of sidecars is that they handle L4 and L7 traffic. If an engineer doesn't need to, for example, worry about L7 traffic, they're still forced to deal with the resource constraints of implementing L7 capibilites even if they don't need them.

Istio CNI (despite it's name "CNI") directs traffic from what's incoming to Ztunnel. Ztunnel then makes the decision of where the traffic should go based on policies that are configured and the chosen destination of the traffic (e.g - This traffic is intended for backend A).

Waypoints are used to manage all of the L7 traffic, like HTTP routing, circuit breaking, timeouts, retries, and everything else that happens at the Application level.

ArgoCD Installation

Now that you know a bit about Istio Ambient Mesh and how each component is split, let's dive into the installation and configuration of ArgoCD so you can declaratively manage Istio Ambient Mesh.

  1. Install the ArgoCD CLI, which will be used to authenticate to your instance of ArgoCD locally to deploy application stacks. For example, if you're on a Mac, you can use the following:
brew install argocd
  1. Add the ArgoCD Helm repo.
helm repo add argo https://argoproj.github.io/argo-helm
  1. Install ArgoCD in a High Availability (HA) configuration.
💡
HA for ArgoCD means there's more than one replica running ArgoCD and Redis, which is what ArgoCD uses for caching. If you're planning on using ArgoCD for your entire environment, you want to make sure that what's expected to deploy will deploy and that won't occur if ArgoCD is down.
helm install argocd -n argocd argo/argo-cd \
--set redis-ha.enabled=true \
--set controller.replicas=1 \
--set server.autoscaling.enabled=true \
--set server.autoscaling.minReplicas=2 \
--set repoServer.autoscaling.enabled=true \
--set repoServer.autoscaling.minReplicas=2 \
--set applicationSet.replicaCount=2 \
--set server.service.type=LoadBalancer \
--create-namespace
  1. Get the initial admin password for ArgoCD, which is stored in a Kubernetes Manifest.
kubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
  1. Get the load balancer IP of the ArgoCD UI.
kubectl get svc -n argocd

You'll see a service called argocd-server

  1. Log into ArgoCD with the following command:

argocd login LOAD_BALANCER_IP_ADDRESS

Username: admin
Password: 
'admin:login' logged in successfully
Context '51.8.48.167' updated

You can also use the same IP address, username, and password to log into the UI.

In the next section, you'll learn how to use the ArgoCD Application object to deploy resources.

Using ArgoCDs Application Object

The Application object/kind that you'll see in the upcoming configurations is the declarative method of deploying application stacks via ArgoCD. You could also use the argocd CLI to deploy applications, but it's far more imperative.

  1. Set a specific Istio version that you want to use for the Helm Charts that are being deployed via the Application object.
export ISTIO_VERSION=1.26.4
  1. Install the Istio CRDs.

Notice how it's calling out to the Istio CRD Helm Chart. You can use the Application object to install a Helm Chart, but you can also point it directly to a Kubernetes Manifest.

kubectl apply -f- <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: istio-base
  namespace: argocd
spec:
  destination:
    server: https://kubernetes.default.svc
    namespace: istio-system
  project: default
  source:
    chart: base
    repoURL: https://istio-release.storage.googleapis.com/charts
    targetRevision: "${ISTIO_VERSION}"
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
EOF
  1. Install Istiod.
kubectl apply -f- <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: istiod
  namespace: argocd
spec:
  destination:
    server: https://kubernetes.default.svc
    namespace: istio-system
  project: default
  source:
    chart: istiod
    repoURL: https://istio-release.storage.googleapis.com/charts
    targetRevision: ${ISTIO_VERSION}
    helm:
      values: |
        profile: ambient
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
  ignoreDifferences:
  - group: admissionregistration.k8s.io
    kind: ValidatingWebhookConfiguration
EOF
  1. Install the Istio CNI.
kubectl apply -f- <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: istio-cni
  namespace: argocd
spec:
  destination:
    server: https://kubernetes.default.svc
    namespace: istio-system
  project: default
  source:
    chart: cni
    repoURL: https://istio-release.storage.googleapis.com/charts
    targetRevision: ${ISTIO_VERSION}
    helm:
      values: |
        profile: ambient
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
EOF
  1. Install Ztunnel.
kubectl apply -f- <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: istio-ztunnel
  namespace: argocd
spec:
  destination:
    server: https://kubernetes.default.svc
    namespace: istio-system
  project: default
  source:
    chart: ztunnel
    repoURL: https://istio-release.storage.googleapis.com/charts
    targetRevision: ${ISTIO_VERSION}
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
EOF

You should now see that Istio Ambient Mesh is up and running.

Deploying An Application

In the previous section, you learned how to deploy Istio Ambient Mesh with the ArgoCD Application object. You can use the same object to deploy your application stack, which you'll see in this section.

  1. Use the Application object to deploy an application.

Notice how the application that's being used is from a public GitHub repo. It's not calling out to a Helm Chart like the Istio Ambient Mesh installation, but instead, calling directly to a Kubernetes Manifest YAML configuration that contains the entire application stack.

kubectl apply -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: microapp
  namespace: argocd # Or your designated Argo CD namespace
spec:
  project: default
  source:
    repoURL: https://github.com/GoogleCloudPlatform/microservices-demo.git
    targetRevision: main
    path: release/
    directory:
      include: 'kubernetes-manifests.yaml'
  destination:
    server: https://kubernetes.default.svc # Or the API server URL of your target cluster
    namespace: microapp
  syncPolicy:
    automated:
      selfHeal: true # Automatically sync differences between Git and cluster state
    syncOptions:
      - CreateNamespace=true
EOF
  1. Label your application stacks Namespace so it can be enrolled within Ambient Mesh.
kubectl label namespace microapp istio.io/dataplane-mode=ambient

You can now see your application syncing and readily available within ArgoCD.

Conclusion

Kubernetes, by default, is set up to be declarative. Because of that, it only makes sense to manage your Istio Ambient Mesh in a declarative fashion as well as your application stack itself. That way, you're following all of the "best practices" when it comes to how someone should manage workloads in Kubernetes.