Deploy a NGINX Webserver to an Istio Service Mesh
Introduction
Istio is an open platform for providing a uniform way to integrate microservices, manage traffic flow across microservices, enforce policies and aggregate telemetry data. Istio’s control plane provides an abstraction layer over the underlying cluster management platform, such as Kubernetes.
Istio is composed of these components:
-
Envoy - Sidecar proxies per microservice to handle ingress/egress traffic between services in the cluster and from a service to external services. The proxies form a secure microservice mesh providing a rich set of functions like discovery, rich layer-7 routing, circuit breakers, policy enforcement and telemetry recording/reporting functions.
Note: The service mesh is not an overlay network. It simplifies and enhances how microservices in an application talk to each other over the network provided by the underlying platform.
-
Istiod - The Istio control plane. It provides service discovery, configuration and certificate management. It consists of the following sub-components:
-
Pilot - Responsible for configuring the proxies at runtime.
-
Citadel - Responsible for certificate issuance and rotation.
-
Galley - Responsible for validating, ingesting, aggregating, transforming and distributing config within Istio.
-
-
Operator - The component provides user-friendly options to operate the Istio service mesh.
Implementation Considerations
This is going to be a Secure Istio Gateway implementation using Let’s Encrypt to provide production-quality certificates for the Istio IngressGateway into the mesh, as well as for the webserver providing the content.
The installation of cert-manager is not covered in this section, but it is clear from the below YAML file that this is a clusterIssuer implementation with a “DNS challenge”. More details on this approach can be seen here.
Let’s look at the TLS configuration for the docs pod using an istioctl authz check:
Davids-MacBook-Pro:xtremecloud-sso-minikube davidjbrewer$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
docs-558df446b5-rwd2s 2/2 Running 17 46h 172.17.0.16 xtremecloud-istio <none> <none>
Davids-MacBook-Pro:xtremecloud-sso-minikube davidjbrewer$ istioctl x authz check docs-558df446b5-rwd2s
Checked 8/24 listeners with node IP 172.17.0.16.
LISTENER[FilterChain] CERTIFICATE mTLS (MODE) AuthZ (RULES)
0.0.0.0_80[0] none no (none) no (none)
0.0.0.0_80[1] none no (none) no (none)
0.0.0.0_8080[0] none no (none) no (none)
0.0.0.0_8080[1] none no (none) no (none)
0.0.0.0_9090[0] none no (none) no (none)
0.0.0.0_9090[1] none no (none) no (none)
virtualOutbound none no (none) no (none)
virtualInbound[0] noneSDS: default yes (none) no (none)
virtualInbound[1] none no (none) no (none)
virtualInbound[2] noneSDS: default yes (none) no (none)
virtualInbound[3] none no (none) no (none)
virtualInbound[4] none no (none) no (none)
virtualInbound[5] noneSDS: default yes (PERMISSIVE) no (none)
virtualInbound[6] none no (PERMISSIVE) no (none)
0.0.0.0_15010[0] none no (none) no (none)
0.0.0.0_15010[1] none no (none) no (none)
0.0.0.0_15014[0] none no (none) no (none)
0.0.0.0_15014[1] none no (none) no (none)
0.0.0.0_20001[0] none no (none) no (none)
0.0.0.0_20001[1] none no (none) no (none)
Since the webserver is not actually serving sensitive content, the mesh is in permissive mode. So, end-to-end encryption (mTLS) is not absolutely necessary.
# docs-certificate.yaml
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: docs-eupraxialabs-com
namespace: istio-system
spec:
secretName: docs-eupraxialabs-com
dnsNames:
- docs.eupraxialabs.com
acme:
config:
- dns01:
provider: cloudflare
domains:
- docs.eupraxialabs.com
issuerRef:
name: letsencrypt-prod
# We can reference ClusterIssuers by changing the kind here.
# The default value is Issuer (i.e. a locally namespaced Issuer)
kind: ClusterIssuer
We’re going to use an “all-in-one” manifest for Kubernetes and Istio resources. It’s applied like this:
Davids-MacBook-Pro:xtremecloud-sso-minikube davidjbrewer$ kubectl apply -f nginx-docs-all.yaml
# nginx-docs-all.yaml
apiVersion: v1
kind: Namespace
metadata:
name: docs
labels:
istio-injection: enabled
---
apiVersion: v1
kind: Service
metadata:
name: docs-service
namespace: docs
labels:
app: docs
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: http
protocol: TCP
name: http
selector:
app: docs
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: docs
namespace: docs
spec:
selector:
matchLabels:
app: docs
replicas: 1
template:
metadata:
labels:
app: docs
annotations:
sidecar.istio.io/rewriteAppHTTPProbers: "true"
spec:
containers:
- name: nginx-docs
image: quay.io/eupraxialabs/docs-jekyll:1.1
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: docs-ingress
namespace: docs
spec:
rules:
- host: docs.eupraxialabs.com
http:
paths:
- path: /
backend:
serviceName: docs-service
servicePort: http
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: docs-gateway
namespace: docs
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https-docs
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: docs-eupraxialabs-com
hosts:
- docs.eupraxialabs.com
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: docs-virtualservice
namespace: docs
spec:
hosts:
- docs.eupraxialabs.com
gateways:
- docs-gateway
http:
- match:
- uri:
exact: /
- uri:
prefix: /docs
- uri:
regex: ^.*\.(ico|png|jpg|css|js|img|jpeg|map)$
route:
- destination:
host: docs-service
port:
number: 8080
Let’s take at the certificate used as a credential in the Istio Gateway: