Co Kubernetes je, jak to funguje, jak to použít
Celou prezentaci a další materiály najdete na https://github.com/che0/kubernetes-workshop
Tvoří image a pouští kontejnery
docker run -ti --rm ubuntu /bin/bash
docker run -ti --rm debian grep ^deb /etc/apt/sources.list
docker run --name databaze --rm mariadb
docker run -ti --rm -v "$(pwd)/php-site:/var/www/html" -p 8000:80 php:7.2-apache
docker run -ti --rm -p 8000:80 pn2d/hello-web:v1
docker run -ti --rm -p 8000:8080 gcr.io/google-samples/kubernetes-bootcamp:v1
kubectl
(dokud na něj chcete sahat)kubectl get nodes
mi vypíše seznam nodů
kubectl run hello-web --image pn2d/hello-web:v1 --port=80
kubectl get pods
kubectl describe pod ${pod}
kubectl logs ${pod}
kubectl port-forward ${pod} 8000:80
kubectl exec -ti ${pod} /bin/bash
kubectl scale --replicas=3 deploy ${deployment}
potřebuju service která web dostane ven
$ kubectl expose deploy hello-web \
--port 80 --target-port 80 --type=LoadBalancer --name=pub-hello
minikube service pub-hello --url
kubectl get services
, kubectl describe service ${service}
run
, expose
$ kubectl create -f web.yaml
$ kubectl replace -f web.yaml
$ kubectl diff -f my-config
$ kubectl apply -f my-config
Na všechny objekty funguje kubectl get
, describe
a delete
, a jde to i s -f
.
apiVersion: apps/v1 kind: Deployment metadata: name: hello-web spec: replicas: 2 selector: matchLabels: app: hello template: metadata: name: hello-web labels: app: hello spec: containers: - name: flask image: pn2d/hello-web:v1 ports: - name: http containerPort: 80
kubectl apply -f web.yaml
kubectl rollout status -f web.yaml
apiVersion: v1 kind: Service metadata: name: hello-loadbalancer spec: type: LoadBalancer selector: app: hello ports: - protocol: TCP port: 80 targetPort: 80 #loadBalancerIP: "X.X.X.X" # když chcete statickou
Další skvělá komponenta: backend.py
apiVersion: apps/v1 kind: Deployment metadata: name: hello-backend spec: replicas: 2 selector: matchLabels: app: hello what: backend # nutno označit i front-end, jinak blbne loadbalancer template: metadata: name: hello-backend labels: app: hello what: backend spec: containers: - name: backend image: pn2d/hello-backend:v1 ports: - name: http containerPort: 80
ClusterIP – jedna IP adresa v rámci clusteru
kind: Service apiVersion: v1 metadata: name: hello-backend spec: type: ClusterIP selector: app: hello what: backend ports: - protocol: TCP port: 80 targetPort: 80
Adresu předáme v env proměnné
spec: template: spec: containers: - ... image: pn2d/hello-web:v2 env: - name: BACKEND_URL value: "http://hello-backend/backend"
spec: template: spec: containers: - ... livenessProbe: httpGet: path: /alive port: 80 initialDelaySeconds: 5 periodSeconds: 1 failureThreshold: 1 readinessProbe: httpGet: path: /ready port: 80 initialDelaySeconds: 5 periodSeconds: 1 failureThreshold: 1
livenessProbe
= je to naživu (když není, Kubernetes kontejner restartuje)readinessProbe
= reaguje to (když není, Kubernetes na pod nesměruje provoz)httpGet
tam je i exec
a tcpSocket
kind: StatefulSet apiVersion: apps/v1 metadata: name: hello-db spec: serviceName: hello-db replicas: 1 selector: matchLabels: app: hello what: db template: metadata: name: hello-db labels: app: hello what: db spec: containers: - name: mysql image: percona:5 ports: - name: mysql containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD value: "12345" - name: MYSQL_DATABASE value: hellodb - name: MYSQL_USER value: hello_user - name: MYSQL_PASSWORD value: "aaa"
--- kind: Service apiVersion: v1 metadata: name: hello-db spec: selector: app: hello what: db ports: - protocol: TCP port: 3306 targetPort: 3306 clusterIP: None # žádný load balancing, jenom udělá DNS
kind: StatefulSet spec: template: spec: containers: - ... volumeMounts: - name: hello-db-data mountPath: "/var/lib/mysql" volumeClaimTemplates: - metadata: name: hello-db-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi
V Google Cloud je každý persistent volume samostatný ext4fs, který má lost+found
a vlastníka, což MySQL nemá ráda.
kind: StatefulSet spec: template: spec: containers: - ... args: - '--datadir=/var/lib/mysql' - '--ignore-db-dir=lost+found' initContainers: - name: chown-datadir image: busybox command: ['sh', '-c', 'chown 999:999 /mnt/mysql-data'] volumeMounts: - name: hello-db-data mountPath: "/mnt/mysql-data"
spec: template: spec: containers: - image: pn2d/hello-backend:v2 readinessProbe: httpGet: path: /ready port: 80 initialDelaySeconds: 20 periodSeconds: 10 env: - name: MYSQL_HOST value: hello-db - name: MYSQL_DATABASE value: hellodb - name: MYSQL_USER value: hello_user - name: MYSQL_PASSWORD value: "aaa"
Konfigurace se ukládá co ConfigMaps, což jsou vlastně key-value story
ConfigMap se dá vytvářet:
kubectl create configmap foo --from-file=foo.config
nebo --from-file=<key>=<path>
--from-env-file
--from-literal=<key>=<value>
apiVersion: v1 kind: ConfigMap metadata: name: hello-config data: backend.url: "http://hello-backend/backend" db.host: hello-db db.database: hellodb
spec: template: spec: containers: - ... env: - name: BACKEND_URL valueFrom: configMapKeyRef: name: hello-config key: backend.url envFrom: - configMapRef: name: hello-config prefix: FROM_FULL_CONFIGMAP_
spec: template: spec: containers: - ... volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: hello-config
Secrets jsou jako ConfigMapy, ale tajné
apiVersion: v1 kind: Secret metadata: name: hello-db-root-password data: password: MTIzNDU=
apiVersion: v1 kind: Secret metadata: name: hello-db-passwords data: username: aGVsbG9fdXNlcg== password: YWFh
spec: template: spec: containers: - ... env: - name: MYSQL_USER valueFrom: secretKeyRef: name: hello-db-passwords key: username - ...
Analogicky jdou doplnit do envFrom
přes secretRef
a jako volumes typu secret
.
Webserver rozdělíme na nginx proxy a uwsgi
spec: template: spec: containers: - name: web image: pn2d/hello-web:v3-web env: - name: BACKEND_URL valueFrom: ... - name: proxy image: pn2d/hello-web:v3-proxy ports: - name: http containerPort: 80
spec: template: spec: containers: - ... livenessProbe: exec: command: - uwping - uwsgi://localhost:9090/alive initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: exec: command: - uwping - uwsgi://localhost:9090/ready initialDelaySeconds: 20 periodSeconds: 10
Nová verze backendu umí resetovat počítadlo
apiVersion: batch/v1beta1 kind: CronJob metadata: name: reset-counter spec: schedule: "*/3 * * * *" jobTemplate: spec: template: spec: restartPolicy: OnFailure containers: - name: reset-counter image: pn2d/hello-backend:v3 command: ["python3", "/app/reset.py"] env: - name: MYSQL_HOST valueFrom: configMapKeyRef: name: hello-config key: db.host - name: MYSQL_DATABASE valueFrom: configMapKeyRef: name: hello-config key: db.database - name: MYSQL_USER valueFrom: secretKeyRef: name: hello-db-passwords key: username - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: hello-db-passwords key: password
web v4 umí vytáhnout info o posledním resetu
kubectl rollout status deployment ${deployment}
apiVersion: v1 kind: Service metadata: name: hello-nodeport spec: type: NodePort selector: app: hello what: frontend ports: - protocol: TCP port: 80 nodePort: 31000
Ingress spravuje externí přístup do clusteru. Typicky terminuje a load balancuje HTTP/HTTPS.
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: hello-ingress annotations: #kubernetes.io/ingress.global-static-ip-name: my-static-address # ^ anotace vidí a mohou použít další procesy v clusteru spec: rules: - http: paths: - path: /* backend: serviceName: hello-nodeport servicePort: 80
Potřebujet do clusteru nainstalovat rozšíření cert-manager
apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: email: seznam-workshop@example.com server: https://acme-staging-v02.api.letsencrypt.org/directory # production server: https://acme-v01.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-staging-account-key http01: {}
kind: Ingress spec: tls: - secretName: www-example-com-tls --- apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: www-example-com spec: secretName: www-example-com-tls issuerRef: name: letsencrypt-staging kind: ClusterIssuer commonName: www.example.com acme: config: - http01: ingress: hello-ingress domains: - www.example.com
spec: template: spec: containers: - name: forum image: pn2d/phpbb2-k8s env: - name: PHPBB_DB_HOST valueFrom: configMapKeyRef: name: hello-config key: db.host - name: PHPBB_DB_NAME valueFrom: configMapKeyRef: name: hello-config key: db.database - name: PHPBB_DB_USER valueFrom: secretKeyRef: name: hello-db-passwords key: username - name: PHPBB_DB_PASSWORD valueFrom: secretKeyRef: name: hello-db-passwords key: password - name: PHPBB_SERVER_NAME value: forum.example.com - name: PHPBB_SERVER_PORT value: "80" - name: PHPBB_USE_HTTPS value: "0"