How to Run NGINX Web Server
Trying to run NGINX web server is common test to deploy application/service into Kubernetes. It seems to be an easy task as NGINX is simple to run and moreover, there is docker image already prepared. However, deployment can fail for several reasons, therefore, we provide some notes, how to run at at first try.
Privileged vs. Unprivileged
This Kubernetes platform is multitenant, i.e., it is shared by many users and therefore, it has some restrictions. One of them is necessity to set security context because privilege escalation is not allowed. Setting the security context to unprivileged mode sometimes requires creating own docker image for the application. Luckily, for NGINX, there is already prepared unprivileged image. Its reference id is: nginxinc/nginx-unprivileged. This image is fully compatible with our platform as is, no changes into image are needed.
Running User ID
The above mentioned security context requires a correct user id and group id to be set (those RunAsUser, RunAsGroup fields). For the nginx-unprivileged image, the correct value for both of them is 101. This value can be simply obtained using docker as follows. See the uid=101 and gid=101:
docker run -it --rm --entrypoint /bin/sh nginxinc/nginx-unprivileged
$ id
uid=101(nginx) gid=101(nginx) groups=101(nginx)
$This example assumes that the image contains /bin/sh and id binaries. Mainly containerized Go Lang applications usualy contain only the Go application and nothing else. In such a case, Dockerfile used for image build or project documentation must be consulted.
Deployment Manifest
The following manifest can be used to run the NGINX instance. The name nginx can be replaced with any other valid string (small letters, numbers, dash). Also resources part should be set accordingly to expected usage (memory and CPU).
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
securityContext:
fsGroupChangePolicy: OnRootMismatch
fsGroup: 101
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginxinc/nginx-unprivileged
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 101
runAsGroup: 101
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 2
memory: 8GiAdding Persistent Storage
The deployment above will work, however, no useful content will be served. There is just hello page. Also, the image does not expect that any content is added later by copying data into web root (/usr/share/nginx/html), it is not writable by default.
Optimal way to change served content is to add PVC volume, see more about PVC.
We can create ad-hoc PVC using the following manifest:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-nginx
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
storageClassName: nfs-csiData on this PVC are preserved until the PVC is deleted.
If the PVC has been created, we need to change the deployment so that PVC is used:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
securityContext:
fsGroupChangePolicy: OnRootMismatch
fsGroup: 101
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginxinc/nginx-unprivileged
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 101
runAsGroup: 101
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 2
memory: 8Gi
volumeMounts:
- name: vol-1
mountPath: /usr/share/nginx/html
volumes:
- name: vol-1
persistentVolumeClaim:
claimName: test-nginxThe PVC name (name: test-nginx) must match the claimName (claimName: test-nginx).
Directory index is not enabled by default for the NGINX. Therefore, if there is no index.html file uploaded to the PVC, the NGINX will return error 403 Forbidden if the URL explained below will be opened in browser.
Exposing the NGINX
To make the running NGINX accessible from internet, see Exposing applications section. Basically, two additional simple manifests are needed:
Service
The service manifest route traffic into the running Pod. It needs correct target port and application label. In our case, we chose app: nginx as application label (see metadata labels section of the deployment manifest), and from nginx-unprivileged documentation we can see, that the NGINX is using 8080 port, so the Service manifest looks as follows:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
app: nginxIngress
The last required manifest is the Ingress manifest. It contains the hostname of the application and it can also provide the Let’s Encrypt certificate.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- "[some-name].dyn.cloud.e-infra.cz"
secretName: [some-name]-dyn-cloud-e-infra-cz
rules:
- host: [some-name].dyn.cloud.e-infra.cz
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: nginx
port:
number: 8080Replace the [some-name] with some real name. Do not use generic names such as test, example, and so on, they can be already taken.
The service name and port number must match values from the Service object.
Last updated on
