In this lab, you will learn how to install and work with Istio, a popular service mesh, using Helm, a package manager for Kubernetes.
Installing Istio #
Installing Istio is a multi-install process. We will first install the base Istio system.
To begin, create a dedicated directory for this lab and switch into it:
cd ~
mkdir istio && cd istio
Use the following ArgoCD Application manifest to deploy the Istio base. Copy the following manifest and apply it to your cluster:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: istio-base
namespace: argocd
spec:
destination:
namespace: istio-system
server: https://kubernetes.default.svc
source:
repoURL: https://istio-release.storage.googleapis.com/charts
targetRevision: 1.18.0
chart: base
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
ignoreDifferences:
- group: admissionregistration.k8s.io
kind: ValidatingWebhookConfiguration
jqPathExpressions:
- .webhooks[].failurePolicy
Once deployed, go to your ArgoCD UI and wait for the application to become Healthy.
Next, we will actually deploy Istio with the following ArgoCD Application manifest. Again, copy the below manifest and apply it to your cluster:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: istiod
namespace: argocd
spec:
destination:
namespace: istio-system
server: https://kubernetes.default.svc
source:
repoURL: https://istio-release.storage.googleapis.com/charts
targetRevision: 1.18.0
chart: istiod
helm:
values: |-
meshConfig:
outboundTrafficPolicy:
mode: ALLOW_ANY
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Once deployed, go to your ArgoCD UI and wait for the application to become Healthy.
Finally, let’s install the Istio’s Gateway, specifically, the Ingress Gateway. This Gateway behaves similar to the NGINX Ingress Gateway you deployed earlier in the labs.
In the Understanding Kubernetes Networking and Ingress lab, you provisioned the NGINX Ingress Gateway with a static IP address that was already reserved named wildcard. We will do the same here but use the other static IP address provisioned named istio-wildcard. You can get that IP by running the following gcloud command:
gcloud compute addresses list
Copy the below ArgoCD Application manifest to deploy the Istio Gateway:
Note: Ensure you update the manifest with your Istio Wildcard IP.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: istio-gateway
namespace: argocd
spec:
destination:
namespace: istio-gateway
server: https://kubernetes.default.svc
source:
repoURL: https://istio-release.storage.googleapis.com/charts
targetRevision: 1.18.0
chart: gateway
helm:
values: |-
service:
loadBalancerIP: <YOUR_ISTIO_WILDCARD_IP>
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Once deployed, go back to your ArgoCD UI and wait for the application to become Healthy.
Also validate that your Istio Ingress Gateway Service took the appropriate IP:
kubectl get service --namespace istio-gateway
The “EXTERNAL-IP” should match your Istio Wildcard IP.
Deploying an Application to the Istio Service Mesh #
Let’s now look at deploying an application that will be part of the Istio Service Mesh and accessible via our Istio Ingress Gateway.
Create a new namespace called istio-app and label it with lab=istio-app.
Copy the following Deployment manifest and apply it to your cluster. Be sure to update the image spec to point to your Artifact Registry.
apiVersion: apps/v1
kind: Deployment
metadata:
name: random-facts-app
namespace: istio-app
labels:
lab: istio-app
spec:
replicas: 1
selector:
matchLabels:
lab: istio-app
template:
metadata:
labels:
lab: istio-app
spec:
containers:
- name: random-facts-app
image: us-central1-docker.pkg.dev/<YOUR_PROJECT_ID>/<YOUR_REGISTRY_NAME>/random-facts-app:1.0
ports:
- containerPort: 5000
Once deployed, validate that the Pod is running. Notice that the application still has a single container running in the Pod. We want to onboard our application to the Istio Service Mesh. To do that, update your Namespace and add the following label istio-injection=enabled. Once updated, delete the Pods in the namespace with the following command:
kubectl delete pods --all --namespace istio-app
Check the Pods again and you will now see an output similar to below:
NAME READY STATUS RESTARTS AGE
random-facts-app-7f4fcdb99c-z67vk 2/2 Running 0 1m
We can now see a second container has been added. You can check that the second container is an Istio container by running kubectl describe pod --namespace istio-app.
Let’s create a Service for our application. This Service is similar to other Services you have deployed in previous labs. Apply the following manifest to your cluster:
apiVersion: v1
kind: Service
metadata:
name: random-facts-app-service
namespace: istio-app
labels:
lab: istio-app
spec:
selector:
lab: istio-app
ports:
- name: http
port: 5000
protocol: TCP
targetPort: 5000
type: ClusterIP
Exposing our Application through the Istio Ingress Gateway #
In previous labs, you exposed your application with an Ingress object. With Istio, it is broken up into two parts, the Gateway and the VirtualService.
Let’s deploy the Gateway configuration first. Copy the following Gateway manifest and apply it to your cluster:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: main-gateway
namespace: istio-gateway
spec:
selector:
istio: gateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*.istio.<YOUR_STUDENT_ID>.<RANDOM_ID>.workshops.acceleratorlabs.ca"
Once applied, you can create the VirtualService. The VirtualService is the link between your Kubernetes Service and the Gateway.
Copy the following VirtualService manifest and apply it to your cluster:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: random-facts-app-vs
namespace: istio-app
spec:
hosts:
- "random-facts-app.istio.<YOUR_STUDENT_ID>.<RANDOM_ID>.workshops.acceleratorlabs.ca"
gateways:
- istio-gateway/main-gateway
http:
- route:
- destination:
port:
number: 5000
host: random-facts-app-service
You can now visit your application at http://random-facts-app.istio.<YOUR_STUDENT_ID>.<RANDOM_ID>.workshops.acceleratorlabs.ca
Protecting Your Service Mesh #
Although we have on-boarded our application to the Istio Service Mesh, we still are allowing traffic from applications that are not part of the mesh to communicate with applications that are.
We can now lock down our entire mesh with an Istio PeerAuthentication policy. Copy the following manifest and apply it to your cluster:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
Let’s now deploy a Pod outside of the mesh to test connectivity to our application inside the mesh. Run the following command to start that Pod:
kubectl run --rm -it toolbox --image=jacobmammoliti/toolbox -- sh
Once a prompt comes up, run the following cURL command against your application in the istio-app namespace with the following command:
curl random-facts-app-service.istio-app:5000
You will get back the following:
curl: (56) Recv failure: Connection reset by peer
We have effectively blocked all traffic from outside of our mesh to our applications.
To allow connectivity, let’s deploy our application in a new namespace where we will enable Istio Injection.
Create a new namespace called istio-allowed and add the following labels: lab=istio-app and istio-injection=enabled.
Once created, run the following command to create the test Pod in the new istio-allowed namespace:
kubectl run --rm -it toolbox --image=jacobmammoliti/toolbox --namespace istio-allowed -- sh
Once a prompt comes up, run the following cURL command against your application in the istio-app namespace with the following command:
# show document info only
curl -I random-facts-app-service.istio-app:5000
You will now see an output similar to below:
HTTP/1.1 200 OK
server: istio-envoy
...
If you remember this exercise from the Applying Kubernetes NetworkPolicy lab, you’ll notice a difference in the server. Previously, we got back Werkzeug/2.3.4 Python/3.12.0b1 and now because our application is in the mesh, we are now hitting the Istio Envoy proxy first.
We have successfully ensured that all of our workloads in our mesh communicate using mutual TLS.
Restricting Outbound Connections #
Previously, we restricted inbound connections to our applications. What about outbound connections? If you look at your istiod Helm values from earlier, you will notice this following snippet:
meshConfig:
outboundTrafficPolicy:
mode: ALLOW_ANY
This tells Istio, specifically the envoy proxies, to allow all traffic out of the Pod. It some cases, we may want to restrict the outbound connections of our applications. To do this, update your manifest to change the mode to REGISTRY_ONLY and re-apply your ArgoCD Application CRD to your cluster. This will update Istio’s settings to only allow connections to services inside the mesh or specific external services.
After updating the Istio config, run the following command to create the test Pod in the istio-allowed namespace:
kubectl run --rm -it toolbox --image=jacobmammoliti/toolbox --namespace istio-allowed -- sh
Once a prompt comes up, run the following cURL command against google.ca:
curl https://www.google.ca -I
You will now see an output similar to below:
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to google.ca:443
We are getting an error because google.ca is not part of our mesh or registered within our mesh. We can register an external service via an Istio ServiceEntry.
Copy the following ServiceEntry manifest and apply it to your cluster:
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: external-svc-https
spec:
hosts:
- www.google.ca
location: MESH_EXTERNAL
ports:
- number: 443
name: https
protocol: TLS
resolution: DNS
Run the following command again to create the test Pod in the istio-allowed namespace:
kubectl run --rm -it toolbox --image=jacobmammoliti/toolbox --namespace istio-allowed -- sh
Once a prompt comes up, run the following cURL command against google.ca:
curl https://www.google.ca -I
You will now see an output similar to below:
HTTP/2 200
content-type: text/html; charset=ISO-8859-1
...
We see now we are getting a 200 response!
Troubleshooting #
If you try and access your application again at http://random-facts-app.istio.<YOUR_STUDENT_ID>.<RANDOM_ID>.workshops.acceleratorlabs.ca, you will notice that you are getting a 500 Internal Server Error. Try and figure out why this is happening and fix it.
Hint: Looking back at your source code from the Creating Your First Dockerfile lab may help you.