Implementing Istio From Start To Finish

Implementing Istio From Start To Finish

If you’re implementing a Service Mesh in your Kubernetes environment, chances are it’s for one of the following:

  1. Encryption for service-to-service communication.
  2. The ability to see network traffic and performance.
  3. Network resilience and scale (blue/green, circuit breakers, fault injection, retires, AB testing)

The service Mesh you see in an environment will differ based on organization, but there’s a high likelihood that you’ll see Istio implemented. Even if Istio is not implemented in the environment you’re working in or going to work in, once you know one Service Mesh, you can confidently figure out the rest.

In this blog post, you’ll learn how to not only install Istio, but configure mTLS and ensure that the Istio Sidecar is injected into Pods.

Installation

The first step to getting started with a Service Mesh is the installation. There are two primary ways to install Istio:

  • Helm
  • The Istio CTL
💡
From a production perspective, you’ll most likely see Istio installed with Helm as it allows you to manage installation, upgrades, and removal like any other package.
💡
Installing Istio does NOT mean Istio will work throughout your environment within each Pod. For Istio to send policies via Envoy to Pods (within the Data Plane) of your choosing, you need to enable the sidecar container or Ambient Mesh. You’ll see a few different ways to do that in the sections after this section.
  1. First, add the Istio repository.
helm repo add istio https://istio-release.storage.googleapis.com/charts

  1. Next, install the Istio base configurations. The base configuration is a set of CRDs and are separate from the Istio installation itself.
helm install istio-base istio/base -n istio-system --create-namespace

  1. Lastly, install the Istio workloads. You’ll see the Pod and Service in the istio-system Namespace once complete.
helm install istiod istio/istiod -n istio-system
Image description

Now that Istio is installed, let’s see a few different ways to ensure that the Istio Sidecar container is implemented in the Pods that need Istio.

💡
Remember - Istio operates at the L7 layer. Networking for IP addressing like Cilium and Calico (popular CNIs) operate at the L3/L4 layer. If you're not familiar with the layers of the OSI model, here's a great write-up on it: https://www.cloudflare.com/learning/ddos/glossary/open-systems-interconnection-model-osi/

Istio Injection (Option 1: Namespace)

When you install Istio like you did in the previous section, you're installing what many call the "Control Plane" or "Management Server" (not to be confused with the Kubernetes Control Plane). The "Control Plane" in the sense of Istio is like the brain of the operation. When you write Manifests that contain things like routing rules via Istio, you're applying those Manifests via the cluster running the Istio Control Plane, and that Control Plane is sending the instructions down to the Data Plane. The Data Plane is the Worker Nodes where the Pods are running that need said instructions from Istio.

The two ways to properly ensure that the Data Plane (Pods running that need the Istio instruction) get the instruction are by using either:

  1. Ambient Mesh
  2. Sidecar Injection

Let's go the Sidecar Injection route for the purposes of this blog post.

The first method of injecting the Istio Sidecar container is via a Namespace label. With the istio-injection Namespace Label, all Pods that are deployed after the Label is set will retrieve the Sidecar container.

Remember - Pods that are already deployed will not receive the Sidecar container. For example, if I have two Pods already running and then create the Istio Injection Label, the Pods won’t get the Istio Sidecar. You’ll have to remove/restart the Pods and have them redeploy via self-healing (the ReplicaSet Controller). Once the Pods redeploy, they’ll obtain the Istio Sidecar.

As an example, you can add the Injection label on the default Namespace.

kubectl label namespace default istio-injection=enabled --overwrite

You can then check and confirm that the Label was added successfully.

kubectl get namespace -L istio-injection

If you want to remove the Label, run the following:

kubectl label namespaces default istio-injection-

Ensure that you restart the Pods; otherwise, you will not see the sidecar container running.

Option 2: Deployment YAML Injection

A good way to ensure that the Istio Sidecar is installed as soon as a Pod is deployed is by adding the inject label into the Kubernetes Manifest.

For example, the below Manifest has a label that is set to true for injecting the Istio sidecar.

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
        sidecar.istio.io/inject: "true"
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80
EOF

Option 3: Istioctl Injection

The third option you can use to inject Istio is via the Istio CLI, which is Istio's Command-Line Interface/Tool.

  1. First, install the Istio CLI.
curl -L https://istio.io/downloadIstio | sh -
  1. Next, use the following command to inject the Sidecar into a Deployment. For example, the Deployment that Istio is injecting the Sidecar into is from the nginx.yaml Deployment Manifest. You’ll need to change that name to whatever name your Kubernetes Manifest is.
istioctl kube-inject -f nginx.yaml | kubectl apply -f -

If you take a look at the Pods running within that Deployment, you’ll notice that the kube-inject command deletes the Pods that are currently running and new Pods come up with the Sidecar Container.

Enabling mTLS

As you learned about in the opening of this blog post, there are three primary reasons engineers choose to go with a Service Mesh option. One of the most popular is for Mutual TLS, which is to encrypt traffic between Kubernetes Services.

💡
By default, the k8s network is very flat. That means whether Pods/Services are running within the same Namespace or a completely different Namespace, they can communicate with each other over the network via plain-text. The goal of mTLS from a k8s Service perspective is to ensure that traffic is encrypted, assuming that your application stack support mTLS.

Istio, by default, enables mTLS, but it works in permissive mode. The three modes are:

  1. Permissive: Accepts both mTLS and plain-text traffic.
  2. Strict: mTLS traffic only.
  3. Disabled: mTLS is turned off

The way that you enable strict mode is by using the PeerAuthentication object/kind in a Kubernetes Manifest with the Istio CRDs.

💡
Permissive by default is great for organizations who are adopting a Service Mesh solution for the first time. They can dip their toes into the water to see if everything works as expected instead of being forced to go "all or nothing".

As an example, the configuration below adds mTLS to the default Namespace, as it’s not specifying a Namespace.

kubectl apply -n default -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT
EOF

Closing Thoughts

Understanding traffic within your cluster is the make or break between understanding the performance of your environment and getting an angry call from users. It also helps to get to the bottom of errors/warnings, potential malicious attacks, and misconfigurations.