Nu de Kubernetes Cluster ‘up-and-running‘ is kan er een applicatie op geplaatst worden. Als toepassing ga ik een WordPress site maken die uit twee deployments bestaat: WordPress en mySQL. De data voor zowel de WordPress site zelf als de mySQL database plaats ik een een NFS volume. Het plaatje ziet er alsvolgt uit:

Gebruikers die de URL aankiezen komen via een loadbalancer binnen (ik gebruik hier MetalLB voor) en worden naar de wordpress-service gestuurd die benaderbaar is via een networkpolicy die uitsluitend http-verkeer naar de wordpress-deployment toestaat.
De wordpress-deployment maakt gebruik van de mySQL database via een networkpolicy die alleen de wordpress-pods toestaat om via TCP-port 3306 naar de mySQL-deployments te verbinden.
mySQL is toegankelijk met behulp van een mysql-root-password die in een secret opgeslagen is en zodoende kan door de applicatie een wordpress-database aangemaakt worden.
Alle data voor WordPress en mySQL wordt opgslagen in een NFS share die bereikbaar is via twee persistent-volume-claims die gebruik maken van de persistent-volumes die van te voren zijn aangemaakt.
Namespace en secret
Voor de gehele applicatie wordt uiteraard gebruik gemaakt van een eigen namespace genaamd wordpress:
$ kubectl create ns wordpress
Vervolgens maken we hierin de secret voor het mysql-wachtwoord:
$ kubectl -n wordpress create secret generic mysql-pass --from-literal=password=type-een-wachtwoord
Pesistent Volume
De manifest genaamd pv-wordpress.yml voor de WordPress PersistentVolume ziet er alsvolgt uit:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-wordpress
namespace: wordpress
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 192.168.1.123
path: /nfs/wordpress
WordPress Deployment
Als eerste wordt gebuik gemaakt van een PV-claim genaamd pvc-wordpress.yml om het volume te ‘binden‘:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-wordpress
namespace: wordpress
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
Vervolgens dient er een Service genaamd svc-wordpress.yml van het type LoadBalancer gemaakt te worden die http-verkeer toestaat naar de deployment voor de pod(s) die de labels app=wordpress en tier=frontend hebben: (later zou ik daar https van maken)
apiVersion: v1
kind: Service
metadata:
name: wordpress
namespace: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
En tenslotte dan de manifest voor de deployment zelf, genaamd deploy-wordpress.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
namespace: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
replicas: 1
containers:
- image: nokkie/wordpress-rpi:latest
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
securityContext:
runAsUser: 0
runAsGroup: 0
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-wordpress
Wat hier opvalt is het gebruik van de securityContext voor uid:0 en guid:0. Dit is omdat de WordPress app deze rechten nodig heeft om de configuratie bestanden voor de php-site aan te passen.
Mocht het druk worden op de site, dan kan ik replicas toevoegen, vooralsnog houd ik de standaard van 1 replica aan.
Ik ga nu eerst deze deployment starten zodat ik zeker weet dat de PVC de enige PV gebruikt die er is, namelijk de wordpress-pv. Dit is dan de volgorde:
$ kubectl apply -f pv-wordpress.yml $ kubectl apply -f pvc-wordpress.yml $ kubectl apply -f svc-wordpress.yml $ kubectl apply -f deploy-wordpress.yml
Controle
Ter controle ga ik de PV en PVC status bekijken, de service opvragen en zien of de WordPress deployment ‘READY’ is:
$ kubectl -n wordpress get pv nfs-wordpress NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-wordpress 100Gi RWO Recycle Bound wordpress/wp-pv-wordpress 37s $ kubectl -n wordpress get svc wordpress NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE wordpress LoadBalancer 10.104.38.24 192.168.123.101 80:31212/TCP 37s $ kubectl -n wordpress get deploy wordpress NAME READY UP-TO-DATE AVAILABLE AGE wordpress 1/1 1 1 35s
Dat ziet er goed uit, door naar de database!
mySQL Deployment
Als eerste de PersistentVolume genaamd pv-mysql.yml voor het mysql-volume:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-mysql
namespace: wordpress
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 192.168.1.123
path: /nfs/mysql
De PV-Claim pvc-mysql.yml voor mySQL ziet er alsvolgt uit:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-wordpress
namespace: wordpress
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
De service svc-mysql.yml is alsvolgt:
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
namespace: wordpress
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
Hier valt op de we geen clusterIP aanmaken, hierdoor kan de service uitsluitend benaderd worden via svc-naam, oftewel wordpress-mysql zodat dit door de coreDNS wordt afgehandeld. Je ziet dit terug in de wordpress-deployment manifest bij de environment-variable WORDPRESS_DB_HOST.
Verder met de deployment voor mysql, genaamd deploy-mysql.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
namespace: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: nokkie/mariadb-arm:latest
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
securityContext:
runAsUser: 1000
runAsGroup: 1000
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-wordpress
Hier gebruik ik de securityContext uid:1000 en guid:1000 om de mysql-useraccount de rechten te geven om de database aan te maken in het NFS-Volume. Ik da de mysql-deployment ‘uitrollen’:
$ kubectl apply -f pv-mysql.yml $ kubectl apply -f pvc-mysql.yml $ kubectl apply -f svc-mysql.yml $ kubectl apply -f deploy-mysql.yml
Controle
Nu de mysql ook deployed is ga ik controleren of het goed opgestart is, de PV en PVC status, de Service en de Deployment:
$ kubectl -n wordpress get pv nfs-mysql NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-mysql 100Gi RWO Recycle Bound wordpress/mysql-pv-wordpress 37s $ kubectl -n wordpress get svc wordpress-mysql NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE wordpress-mysql ClusterIP None 3306/TCP 37s $ kubectl -n wordpress get deploy wordpress-mysql NAME READY UP-TO-DATE AVAILABLE AGE wordpress-mysql 1/1 1 1 37s
Ziet er ook goed uit!
Security
Ik ga een en ander nu beveiligen met behulp van networkPolicies. Hiervoor dient er wel een CNI te zijn die dat ondersteund en aangezien ik Weave gebruik is dat geen probleem.
De eerste stap is om de gehele wordpress namespace te isoleren met een deny-all policy. Ik maak een manifest genaamd netpol-deny-all.yml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
Vervolgens is het noodzakelijk om http-verkeer toe te staan naar alle pods die het label ‘tier=frontend‘ hebben. Even ter controle:
$ kubectl -n wordpress get po --show-labels NAME READY STATUS RESTARTS AGE LABELS wordpress-7fcf7c6789-jbw6c 1/1 Running 0 36h app=wordpress,pod-template-hash=7fcf7c6789,tier=frontend wordpress-mysql-7996d586c8-qtdnm 1/1 Running 0 37h app=wordpress,pod-template-hash=7996d586c8,tier=mysql
Het manifest netpol-allow-http.yml ziet er dan zo uit:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector:
matchLabels:
tier: frontend
ingress:
- from:
ports:
protocol: TCP
port: 80
policyTypes:
- Ingress
Tenslotte isoleer ik de mysql-deployment zodat uitsluitend de wordpress-deployment hier bij kan via port 3306 vanuit de wordpress-namespace. De manifest netpol-wp-to-mysql.yml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-wp-to-mysql
spec:
podSelector:
matchLabels:
tier: mysql
ingress:
- from:
- namespaceSelector:
matchLabels:
name: wordpress
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 3306
policyTypes:
- Ingress
Uitrollen gaat alsvolgt:
$ kubectl apply -f netpol-deny-all.yml $ kubectl apply -f netpol-allow-http.yml $ kubectl apply -f netpol-wp-to-mysql.yml
The proof is in the pudding!
Gebruik een Internet browser om naar het IP-adres van de loadbalancer te gaan en de WordPress site in te stellen:
http://192.168.123.101

