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

