Skip to content

Deploy nginx container with self-signed certificate on RKE2 Kubernetes distribution

Introduction

Following RKE2 Kubernetes distribution installation instructions in the last post, here I will show how to run simple nginx container.

OS used: Debian 12
Software used: RKE2 1.30.2, Helm 3.16.2, cert-manager 1.16.1

Source

Running simple container

To run nginx container few Kubernetes files need to be created. Order in which Kubernetes elements are created is important (for example Persistent Volume Claim must be created before Deployment). Following files will be created with Kubernetes objects listed in each file:

  • 01_nginx-test-namespace.yml
    • Namespace
  • 02_self-signed-cert.yml
    • Issuer for self-signed certificate
  • 03_nginx-test-volume.yml
    • Persistent Volume
    • Persistent Volume Claim
  • 04_nginx-test-deployment.yml
    • Deployment
    • Service
    • Ingress

Creating namespace

First create folder in which you will put your Kubernetes files:

$ mkdir -m 700 ~/test-stack

Next prepare file with new namespace which will be used for grouping Kubernetes elements:

$ vim ~/test-stack/01_nginx-test-namespace.yml
~/test-stack/01_nginx-test-namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: test-space

Installing Helm and cert-manager

For accessing webpage using HTTPS you need SSL certificate. This example will use self-signed certificate and to automate this process cert-manager will be used.

Install cert-mananger using Helm package manager for Kubernetes with following commands (commands taken from Official Helm documentation and Official cert-manager documentation):

  • Install Helm:
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && \
  chmod 700 get_helm.sh && \
  ./get_helm.sh && \
  rm ./get_helm.sh
  • Install cert-manager:
$ helm repo add jetstack https://charts.jetstack.io --force-update
$ helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.16.1 \
  --set crds.enabled=true

Enable autocompletion for helm

To enable helm bash autocompletion for user (when using Tab key) run command to add entry to your .bashrc:

$ echo -e "\n# helm completion bash" >> ~/.bashrc && \
  echo 'source <(helm completion bash)' >>~/.bashrc

echo -e - enable interpretation of backslash escapes

Login again or run following command to enable bash autocompletion in current session:

$ source ~/.bashrc

Creating Issuer for self-signed certificate

Create file to issue self-signed certificate.

$ vim ~/test-stack/02_self-signed-cert.yml
~/test-stack/02_self-signed-cert.yml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: selfsigned-issuer
  namespace: test-space
spec:
  selfSigned: {}

Note

cert-manager Issuer can be type Issuer or ClusterIssuer. Here is description from the official documentation:

An Issuer is a namespaced resource, and it is not possible to issue certificates from an Issuer in a different namespace. This means you will need to create an Issuer in each namespace you wish to obtain Certificates in.

If you want to create a single Issuer that can be consumed in multiple namespaces, you should consider creating a ClusterIssuer resource. This is almost identical to the Issuer resource, however is non-namespaced so it can be used to issue Certificates across all namespaces.

Creating Kubernetes Volume

Create folder for data available to container:

  • html - folder with web page
$ sudo mkdir -p /mnt/nginx-test/html

Create volume configuration file:

$ vim ~/test-stack/03_nginx-test-volume.yml
~/test-stack/03_nginx-test-volume.yml
apiVersion: storage.k8s.io/v1
kind: storageClass
metadata:
  name: nginx-test-storage
  namespace: test-space
provisioner: kubernetes.io/no-provisioner  # storageClass setting for local storage
volumeBindingMode: WaitForFirstConsumer  # storageClass setting for local storage
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-test-html-pv
  namespace: test-space
spec:
  capacity:
    storage: 1Gi # for local storage it's just a label and don't cap space available for container
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: nginx-test-storage
  local:
    path: /mnt/nginx-test/html # path on your server
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - yourhostname # host name of your server
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-test-html-pvc
  namespace: test-space
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: nginx-test-storage
  resources:
    requests:
      storage: 1Gi # for local storage it's just a label and don't cap space available for container

Creating Kubernetes Deployment

$ vim ~/test-stack/04_nginx-test-deployment.yml
~/test-stack/04_nginx-test-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test-deployment
  namespace: test-space
  labels:
    app: nginx-test-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-test-deployment
  template:
    metadata:
      labels:
        app: nginx-test-deployment
    spec:
      containers:
        - name: nginx
          image: nginx:1.27.2 # image you want to use
          ports:
            - containerPort: 80
          volumeMounts: # VolumeMounts are the paths that your application uses inside container
            - name: nginx-test-html-volume
              mountPath: /usr/share/nginx/html # the path that your application uses inside container
      volumes:
        - name: nginx-test-html-volume
          persistentVolumeClaim:
            claimName: nginx-test-html-pvc # should match name of the PersistentVolumeClaim
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-test-service
  namespace: test-space
spec:
  selector:
    app: nginx-test-deployment
  ports:
    - name: http
      port: 80
      targetPort: 80 # should match containerPort in Deployment
      protocol: TCP # protocol that will be used
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-test-ingress
  namespace: test-space
  annotations:
    cert-manager.io/cluster-issuer: "issuer" # should match name of issuer
spec:
  tls:
  - hosts:
    - myservername.example.com # replace with your domain
    secretName: nginx-test-self-signed-tls # this is the name of certificate that will be created
  rules:
    - host: myservername.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-test-service # reference to created Service
                port:
                  number: 80 # Service port number

Info

  • If using an Issuer update the Ingress annotation to cert-manager.io/issuer.
  • If using a ClusterIssuer update the Ingress annotation to cert-manager.io/cluster-issuer.

Adding address to local DNS

Add address that you put in Ingress to your local DNS server, for example:

DNS A record: myservername.example.com | IPv4 address: 192.168.1.46.

Running Kubernetes Deployment

It's important to create Kubernetes elements in a specific order, that is why files are numbered and should be run in sequence:

$ kubectl apply -f ~/test-stack/01_nginx-test-namespace.yml && \
  kubectl apply -f ~/test-stack/02_self-signed-cert.yml && \
  kubectl apply -f ~/test-stack/03_nginx-test-volume.yml && \
  kubectl apply -f ~/test-stack/04_nginx-test-deployment.yml

Serving your own webpage with nginx

Create your simple webpage:

$ sudo vim /mnt/nginx-test/html/index.html
/mnt/nginx-test/html/index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Page Title</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>

  <h1>My Simple Website</h1>
  <p>Simple website.</p>

</body>

</html>

Enter the address of your service into web browser: https://myservername.example.com. You should see a warning message about HTTPS because you are using self-signed certificate and after you accept it your webpage should be shown.

Appendix

Upgrade helm

To upgrade helm to new version just perform install operation. Helm is a single binary file and this will replace it to new version:

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && \
  chmod 700 get_helm.sh && \
  ./get_helm.sh && \
  rm ./get_helm.sh

Upgrade cert-manager

Use following command to upgrade cert-manager:

$ helm repo update && \
  helm upgrade --reset-then-reuse-values --namespace cert-manager --version <version> cert-manager jetstack/cert-manager

<version> - enter new version of cert-manager