Jak spustit aplikaci v Kubernetu

Jak spustit aplikaci v K8s

Co Kubernetes je, jak to funguje, jak to použít

Celou prezentaci a další materiály najdete na https://github.com/che0/kubernetes-workshop

Docker

Docker

Tvoří image a pouští kontejnery

Docker Hub

Kubernetes

Kubernetes

Kde takový cluster vzít?

Jak poznám že mám cluster?

kubectl get nodes mi vypíše seznam nodů

Web

Jak na web pustit lidi

potřebuju service která web dostane ven

kubectl get services, kubectl describe service ${service}

Jak se dá sahat na věci v Kubernetu

  1. Imperativní příkazy: dělá věci co dostane v argumentech
    • run, expose
  2. Imperativní konfigurace: vytvoří/přepíše věci podle souboru
    • $ kubectl create -f web.yaml
    • $ kubectl replace -f web.yaml
  3. Deklarativní konfigurace: zjistí stav objektů, porovná je s argumentem a aplikuje změny
    • asi nejlepší způsob, dá se spouštět na celý adresář plný 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.

Jednoduchý web (znovu)

web.yaml

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
    

Jednoduchý web (znovu)

loadbalancer.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
    

Co když potřebuju backend?

Další skvělá komponenta: backend.py

backend.yaml

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
        

Jak web backend najde?

ClusterIP – jedna IP adresa v rámci clusteru

clusterip.yaml

kind: Service
apiVersion: v1
metadata:
  name: hello-backend
spec:
  type: ClusterIP
  selector:
    app: hello
    what: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    

Jak web backend najde?

Adresu předáme v env proměnné

web.yaml (kousky)

spec:
  template:
    spec:
      containers:
      - ...
        image: pn2d/hello-web:v2
        env:
          - name: BACKEND_URL
            value: "http://hello-backend/backend"
    

Co když mi tam něco umře?

web.yaml (kousky)

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
    

Databáze v Kubernetu

Méně tupý backend

mysql.yaml

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"
    

Jak na databázi směrovat provoz

mysql.yaml (navíc)

---
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
    

Perzistence

mysql.yaml (ještě pořád)

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
    

MySQL workaround pro Google

V Google Cloud je každý persistent volume samostatný ext4fs, který má lost+found a vlastníka, což MySQL nemá ráda.

mysql.yaml (ještě pořád)

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"
    

Napojení backendu

backend.yaml (kus)

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

Konfigurace

Konfigurace se ukládá co ConfigMaps, což jsou vlastně key-value story

ConfigMap se dá vytvářet:

0config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-config
data:
  backend.url: "http://hello-backend/backend"
  db.host: hello-db
  db.database: hellodb
    

ConfigMap se dá používat

web.yaml (kus)

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_
    

ConfigMap ve volume

web.yaml (kus)

spec:
  template:
    spec:
      containers:
      - ...
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
      - name: config-volume
        configMap:
          name: hello-config
    

Secrets

Secrets jsou jako ConfigMapy, ale tajné

0db-root-password.yaml

apiVersion: v1
kind: Secret
metadata:
    name: hello-db-root-password
data:
    password: MTIzNDU=
    

0db-passwords.yaml

apiVersion: v1
kind: Secret
metadata:
    name: hello-db-passwords
data:
    username: aGVsbG9fdXNlcg==
    password: YWFh
    

Secrets

backend.yaml

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.

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.

Produkční webserver

Webserver rozdělíme na nginx proxy a uwsgi

web.yaml (dva kontejnery)

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
    

Jak ověřit že uwsgi funguje

web.yaml (kus kontejneru)

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
    

Cron joby

restart-job.yaml

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
    

Informace o resetu

web v4 umí vytáhnout info o posledním resetu

Rozličné informace k updatům

Jak do clusteru dostat provoz

nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: hello-nodeport
spec:
  type: NodePort
  selector:
    app: hello
    what: frontend
  ports:
  - protocol: TCP
    port: 80
    nodePort: 31000
    

Ingress

Ingress spravuje externí přístup do clusteru. Typicky terminuje a load balancuje HTTP/HTTPS.

ingress.yaml

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
    

TLS s Let's Encrypt v GKE

Potřebujet do clusteru nainstalovat rozšíření cert-manager

0clusterissuer.yaml

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: {}
    

TLS pro Ingress v GKE

inguress.yaml (kus)

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
    

Jak převést starou aplikaci

phpBB 2.0

phpBB 2.0 v Kubernetu

forum.yaml

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"    
    

Konec