Deploy single page webapp shared volume
Single page webapp served from shared volume and exposed with ingress
This is a simple example singlepage webapp that starts with a PersistentVolumeClaim and a onetime Job that
copies the index.html from a git gist to the shared volume. The Deployment spawns 3 Pods with an init
container that waits until an index.html file is present.
After that an unpriviledged nginx container starts serving it. A service is created and exposed through an Ingress,
in case of Openshift it also creates a coupled Route. A CronJob is added to periodically copy again the
index.html for automatic updating purposes.
Let's begin!
Preparation
Before applying the manifests be sure to log in to the Kubernetes Openshift Cluster and create
a docker secret.
ie.
kubectl create secret docker-registry docker-cred --docker-server="docker.io" --docker-username=<your-uname> --docker-password=<your-pword> --docker-email=<your-uuemail>
The shared volume, the job, the deployment
In the Openshift web UI, press + on the upper right and import YAML (copy-paste). Alternatively,
kubectl apply -f <filename/directory> can be used.
First we create the PersistentVolumeClaim, then we create a onetime Job that mounts it under the directory
/tmp/gist/ and downloads index.htmlinto it.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-phoenix
labels:
app: phoenix
spec:
storageClassName: netapp
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
---
apiVersion: batch/v1
kind: Job
metadata:
name: copiecurl-phoenix
labels:
app: phoenix
spec:
template:
spec:
volumes:
- name: storage-phoenix
persistentVolumeClaim:
claimName: pvc-phoenix
imagePullSecrets:
- name: docker-cred
containers:
- name: curl-phoenix
image: docker.io/curlimages/curl
volumeMounts:
- name: storage-phoenix
mountPath: /tmp/gist/
command: ["/bin/sh"]
args: ["-c", "curl https://gist.githubusercontent.com/gdamaskos/f1a8ee5cffa83f51fed45680c310c9ad/raw/index.html -o /tmp/gist/index.html;"]
restartPolicy: Never
backoffLimit: 4
In the meantime, the 3 Pods that are create by the Deployment are running their init containers which wait until
the index.html file is present. Then an unpriviledged nginx container starts serving it.
Note that the containers' volumeMount as readOnly: true. Since there is no need to write to it simultaneously,
there is no need for a StatefulSet.
apiVersion: apps/v1
kind: Deployment
metadata:
name: server-phoenix
labels:
app: phoenix
spec:
replicas: 3
selector:
matchLabels:
app: phoenix
progressDeadlineSeconds: 300
minReadySeconds: 10
template:
metadata:
labels:
app: phoenix
spec:
volumes:
- name: storage-phoenix
persistentVolumeClaim:
claimName: pvc-phoenix
imagePullSecrets:
- name: docker-cred
initContainers:
- name: init-wait4index
volumeMounts:
- name: storage-phoenix
mountPath: /tmp
readOnly: true
image: busybox
command: [ 'sh', '-c', "until [ -e /tmp/index.html ]; do sleep 1; done; echo index.html exists" ]
containers:
- name: nginx-unprivileged
image: docker.io/nginxinc/nginx-unprivileged
ports:
- containerPort: 8080
volumeMounts:
- name: storage-phoenix
mountPath: /usr/share/nginx/html/
readOnly: true
The service, the ingress, the cronjob
A service is created and exposed through an Ingress, in case of Openshift it also creates a coupled Route. A *
CronJob* is added to periodically copy again the index.html for automatic updating purposes.
apiVersion: v1
kind: Service
metadata:
name: service-phoenix
labels:
app: phoenix
spec:
type: ClusterIP
selector:
app: phoenix
ports:
- port: 8080
protocol: TCP
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-phoenix
labels:
app: phoenix
annotations:
INGRESS.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: openshift-default
rules:
- host: <your-domain>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-phoenix
port:
number: 8080
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: croncopiecurl-phoenix
labels:
app: phoenix
spec:
schedule: "0 0 * * *" # run once a day at midnight
jobTemplate:
spec:
template:
spec:
volumes:
- name: storage-phoenix
persistentVolumeClaim:
claimName: pvc-phoenix
imagePullSecrets:
- name: docker-cred
containers:
- name: curl-phoenix
image: docker.io/curlimages/curl
command: [ "/bin/sh" ]
args: [ "-c", "curl https://gist.githubusercontent.com/gdamaskos/f1a8ee5cffa83f51fed45680c310c9ad/raw/index.html -o /tmp/gist/index.html;" ]
volumeMounts:
- name: storage-phoenix
mountPath: /tmp/gist
restartPolicy: Never
backoffLimit: 4
kubectl get ingress and then curl the returned URL i.e. curl phoenix.k8s.im.hum.uu.nl.
Cleanup
The resources can be deleted manually from the web UI or if there are YAML manifests:
kubectl delete -f <filename/directory> --now
If necessary the secret can be removed with a separate command.