Skip to content

Lab 2b: Advanced Kubernetes

Ingress

Kubernetes Ingress manages external access to services in a cluster, typically over HTTP and HTTPS. It provides features such as load balancing, SSL termination and name-based routing.

Install Ingress Controller

An Ingress resource requires an Ingress Controller deployed in your cluster to function. One very common controller is the NGINX ingress controller.

Warning

The following instruction installs the NGINX ingress controller only in minikube.

minikube addons enable ingress

Create an Ingress Resource

Start by creating and applying the following Ingress resource:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wikijs-ingress
spec:
  rules:
  - host: wikijs.cluster.org
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wiki-svc
            port:
              number: 3000

The configuration above routes traffic from "wikijs.cluster.org" to the wiki-svc service, which should be running on port 3000. To see if the ingress has been correctly applied, run:

kubectl get ingress

Warning

The following instruction is needed only in minikube to expose the ingress to your host.

minikube tunnel

Now navigate to http://127.0.0.1/

Questions 5-6

Question 5

Why you are not seeing the WikiJS page? What can you do to Fix the issue?

Question 6

Your cluster now exposes a NodePort and an Ingress for the same service. Can you think about some reasons why this is considered a bad practice? Which other service type would you use in this case?

Use NetworkPolicy resources

Network policies in Kubernetes are managed by the CNI. The basic resource (compatible with all CNIs) is the NetworkPolicy.

By default, everything is allowed in Kubernetes. A default-deny policy looks like:

# policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Apply the policy and check Internet connectivity from test container e.g.,:

ping google.com

You should not be allowed to ping the google website.

Question 7

Create a new namespace called test and install the ubuntu test container in this namespace. Can the container ping google.com ?

To remove the policy run:

kubectl delete -f policy.yaml

Question 8

Create a NetworkPolicy that allows WikiJS to receive connections from every Pod but prevents it to initiate connections. Also WikiJS should not be able to connect to the Internet. Provide the YAML content of the policy.

The Sidecar pattern in Kubernetes

The following is a Kubernetes application that deploys a Wordpress website and a database for storing its content.

First, we need to delete all the resources from the previous step. This can be done either by deleting each resource by name: e.g., kubectl delete service/wiki-svc or by destroying and re-creating the cluster. See Delete Minikube.

# wordpress.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:lts
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "password"
            - name: MYSQL_DATABASE
              value: "wordpress"
          volumeMounts:
            - name: mysql-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-storage
          persistentVolumeClaim:
            claimName: mysql-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: mysql
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - name: wordpress
          image: wordpress:latest
          ports:
            - containerPort: 80
          env:
            - name: WORDPRESS_DB_HOST
              value: "mysql-service"
            - name: WORDPRESS_DB_USER
              value: "root"
            - name: WORDPRESS_DB_PASSWORD
              value: "password"
            - name: WORDPRESS_DB_NAME
              value: "wordpress"
          volumeMounts:
            - name: wordpress-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-storage
          persistentVolumeClaim:
            claimName: wordpress-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-service
spec:
  selector:
    app: wordpress
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30080

Exercise 9

We want to add a sidecar proxy container (nginx) named logs-proxy that listens to port 8080. This sidecar forwards each request to the Wordpress container and prints each request to the STDOUT in JSON format. In this exercise you will provide the updated wordpress.yaml file.

    graph LR
    A[User] --> B[Wordpress Service]
    B --> |8080| C[logs-proxy]
    C -->|80| D[Wordpress]
    D -->A

Warning

Do not provide screenshots of the file. Only text will be considered for the submission of this exercise.

Tip

You can use the ConfigMap resource to add configuration files to a Pod. See the Kubernetes documentation.

Service Mesh in Kubernetes

We will use Istio as a service mesh in this lab.

1. Install Istio

First install and download Istio using the official instructions.

Warning

You do not have to follow the entire tutorial from the istio website, just install Istio.

2. Run the tutorial

Run the bookinfo tutorial on the Istio website and answer the following questions:

Questions

Question 10

Does Istio - in the current configuration - allow pod-to-pod communication and is the communication secure? Explain your reasoning and commands you use to verify that.

Tip

You can use the kubectl exec -it <podname> -- bash command in some of the containers to get a shell.

Question 11

Look at the pods and containers installed by Istio. Where are the sidecar containers installed? Which commands are executed?

Tip

You can inspect the content of a Pod using the kubectl describe <podname> command.

3. Deep dive in Istio and service mesh concepts.

It is possibe to enable mTLS in Istio to encrypt and secure traffic between applications. Specifically it allows both PERMISSIVE and STRICT mode.

Question 12

Enable mTLS first in PERMISSIVE and then in STRICT mode. Does Istio - in each of the configuration - allow pod-to-pod communication and is the communication secure? Explain your reasoning and commands you use to verify. Then, draw a conclusion about the security of Istio depending on the settings that are used.

Introduction to Helm

Helm is a package manager and template engine for Kubernetes that allows to easily manage applications. Helm applications are called charts and they are collection of files that describe Kubernetes resources.

Why Helm ?

Some of the reasons that make Helm a popular solution are:

  • Easy Deployment: A single command can install complex applications
  • Version Control: Helm charts can be versioned, allowing you to roll back to previous versions of your application if needed.
  • Reusability: Helm charts can be reused across different environments, ensuring consistency in deployments.

Helm Tutorials

We suggest you to follow the official quickstart guide for Helm.

Helm chart for WikiJS

A Helm chart lets you package the WikiJS Deployment, Service, and (optional) Ingress into a single, reusable artifact. Users can install the chart with one command and tweak parameters via values.yaml (to e.g., edit the exposed ports or application configuration).

Chart layout (Create the layout under charts/wiki-js/)

Creat the chart's scheleton by running: helm create wiki-js. The tool should generate the following set of files:

wiki-js
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

Here are some examples of files you can edit to effectively install the app on Kubernetes:

Chart.yaml

apiVersion: v2
name: wiki-js
description: A Helm chart for deploying WikiJS on Kubernetes
type: application
version: 0.1.0
appVersion: "2.5"

values.yaml

replicaCount: 3

image:
  repository: lscr.io/linuxserver/wikijs
  tag: latest
  pullPolicy: IfNotPresent

service:
  type: NodePort
  port: 3000
  nodePort: 30080

ingress:
  enabled: true
  host: wikijs.local
  tls: false

templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "wiki-js.fullname" . }}
  labels:
    {{- include "wiki-js.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "wiki-js.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "wiki-js.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: wikijs
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.port }}

templates/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ include "wiki-js.fullname" . }}
spec:
  type: {{ .Values.service.type }}
  selector:
    {{- include "wiki-js.selectorLabels" . | nindent 4 }}
  ports:
    - protocol: TCP
      port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      {{- if eq .Values.service.type "NodePort" }}
      nodePort: {{ .Values.service.nodePort }}
      {{- end }}

templates/ingress.yaml (enabled when ingress.enabled is true)

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "wiki-js.fullname" . }}
spec:
  rules:
    - host: {{ .Values.ingress.host }}
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ include "wiki-js.fullname" . }}
                port:
                  number: {{ .Values.service.port }}
{{- end }}

Render the chart

Rendering the chart means interpolating the YAML templates with the values provided in values.yaml to produce Kubernetes mannifests.

Render the chart to see the generated manifests (optional):

helm template wiki-js .wiki-js

Install the chart with default values:

helm install wiki-js ./wiki-js

Verify the deployment:

kubectl get pods
kubectl get svc
kubectl get ingress   # only if ingress.enabled is true