รู้จักกับ Kubernetes และวิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 1 Master Node)

ถ้าต้องการระบบจัดการ docker container สักตัวต้องทำอย่างไร

            เมื่อกล่าวถึงระบบจัดการ docker container สักตัวหนึ่ง มักจะกล่าวถึง opensource ตัวหนึ่งชื่อ kubernetes ซึ่งเป็นเครื่องมือที่พัฒนาขึ้นมาด้วย Google[1] ซึ่งสามารถรองรับทั้งในส่วนของ Google Container Engine และ CoreOS จริง ๆ ลงได้บนอีกหลาย platform แต่สำหรับ CoreOS ได้ออกแบบมาให้รองรับ Kubernetes ทำให้การติดตั้งง่ายและสมบูรณ์มากขึ้น (อ่านไปเรื่อย ๆ จะสงสัยนี่ง่ายแล้วเหรอ แต่หลังจากเขียนบทความนี้เสร็จก็มีตัวติดตั้งตัวใหม่ชื่อ Tectonic[6] น่าจะเป็นตัวที่ทำให้การติดตั้งง่ายขึ้นครับ)

            Kubernetes เป็นเครื่องมือที่ช่วย build, deploy และ scale ให้เป็นเรื่องง่าย ๆ และสามารถ replicate containner ได้ง่ายมาก การติดตั้ง Kubernetes บน Google Container Engine ง่ายมาก แต่การติดตั้งบน CoreOS จะยากกว่า ซึ่งสามารถติดตั้งแบบ Single Node และ Multi-Node ซึ่งถ้าจะทำ Multi-Node ต้องเชื่อมต่อไปยัง etcd service ที่ติดตั้งบน CoreOS Cluster ด้วยก็จะดีครับ (ไม่งั้นเป็น Cluster ที่ไม่สมบูรณ์) สามารถอ่านเพิ่มเติมได้ที่วิธีการติดตั้ง CoreOS Cluster[2] (ในบทความใช้ก่อนหน้านี้ CoreOS Cluster เขียนในส่วน ETCD Version 2 แต่การใช้งานกับ Kubernetes ต้องใช้ ETCD Version 3 ตอนนี้ยังไม่มีบทความเสริมในส่วนนี้ อาจจะต้องหาข้อมูลเองก่อนครับ จริง ๆ มีวิธีแต่ยังยากในการเขียนบทความ ไว้ค่อยเขียนเพิ่มให้อีกทีครับ) ซึ่งแน่นอนระหว่าง master node และ worker node มีการตรวจสอบเรื่อง certificate ระหว่าง service ด้วย

            ตัวอย่าง Diagram ของระบบทั้งหมด[3]

เรียนรู้การค่าเบื้องต้นก่อนการติดตั้ง[4]

  • MASTER_HOST : คือ ชื่อ host ที่ node ลูกเอาไว้ติดต่อและให้เข้าถึงจาก external client  เช่นเวลาสั่งสร้างเครื่องก็จะสั่งผ่านตัวนี้ ในกรณที่ต้องการทำ HA MASTER_HOST จะถูกใช้เป็น load balance โดยใช้ร่วมกับ DNS name โดยการคุยกันระหว่าง MASTER_HOST และ worker (node ลูก) จะใช้ port 443 และมีการเข้ารหัสและยืนยันด้วย TLS Certificate
  • ETCD_ENDPOINT : คือ บริการของ etcd service ซึ่งมีใน CoreOS Cluster ที่ติดตั้งไว้ ให้ใส่ไปให้หมดว่ามีเครื่องอะไรบ้าง คั่นด้วย , (ในที่นี้ไม่ได้ใช้ fleet สร้างเครื่อง หลังจากลงเสร็จจะใช้ kubectl สร้างแทน)
  • POD_NETWORK : เช่น 10.2.0.0/16 ใช้สำหรับกำหนดวง IP ของ pod (pod คือ ชื่อเรียก container อาจจะแค่ 1 container เหรือเรียกเป็นกลุ่มของหลาย ๆ container ที่สร้างด้วย kubernetes)
  • SERVICE_IP_RANGE : เช่น 10.3.0.0/24 ใช้สำหรับ service cluster VIPs ซึ่งควรจะอยู่คนละวงกับ pod_network ซึ่งเป็น ip ที่ไว้ให้ข้างนอกวงเข้าถึง มักจะเป็น IP สำหรับให้บริการ Service ต่าง ๆ และไว้สำหรับใช้งานคู่กับ kube-proxy
  • K8S_SERVICE_IP : เช่น 10.3.0.1 เป็น IP หนึ่งในวง SERVICE_IP_RANGE เพื่อให้บริการ Kubernetes API Service
  • DNS_SERVICE_IP : เช่น 10.3.0.10 เป็น IP หนึ่งในวง SERVICE_IP_RANGE ใช้สำหรับเป็น VIP ของ DNS Service เพื่อให้บริการ DNS ให้กับ worker แต่ละตัว

วิธีการตั้งค่า TLS สำหรับ Kubernetes[4]

  • ทำการสร้าง Cluster Root CA  (ทุกไฟล์ต่อจากนี้ทำเก็บไว้ที่ CoreOS สักเครื่องหนึ่ง ถึงเวลาติดตั้ง Master, Worker ค่อยมาโหลดมาใช้ครับ)
    sudo openssl genrsa -out ca-key.pem 2048
    sudo openssl req -x509 -new -nodes -key ca-key.pem -days 10000 -out ca.pem -subj "/CN=kube-ca"

  • ตั้งค่า openssl config สำหรับ api-server โดยการสร้างไฟล์ดังนี้
    vim openssl.cnf

    โดยมีเนื้อหาในไฟล์ดังนี้ (ให้ระบุ IP Service และ Master Host ลงไปด้วย)
    *หมายเหตุ : แทนที่เลข IP ใน <….>

    [req]
    req_extensions = v3_req
    distinguished_name = req_distinguished_name
    [req_distinguished_name]
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster.local
    IP.1 = <K8S_SERVICE_IP>
    IP.2 = <MASTER_HOST>
  • จากนั้นทำการสร้าง API Server Keypair ดังนี้
    sudo openssl genrsa -out apiserver-key.pem 2048
    sudo openssl req -new -key apiserver-key.pem -out apiserver.csr -subj "/CN=kube-apiserver" -config openssl.cnf
    sudo openssl x509 -req -in apiserver.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out apiserver.pem -days 365 -extensions v3_req -extfile openssl.cnf

  • ต่อไปเป็นส่วนของสร้าง Cer ในเครื่องลูกทุกเครื่อง (สร้างทีละเครื่องนะครับ) ทำการสร้างไฟล์ดังนี้
    vim worker-openssl.cnf

    เนื้อหาในไฟล์ดังนี้ แทนที่ WORKER_IP ด้วยเครื่องที่ต้องการ

    [req]
    req_extensions = v3_req
    distinguished_name = req_distinguished_name
    [req_distinguished_name]
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName = @alt_names
    [alt_names]
    IP.1 = <WORKER_IP>
  • จากนั้นทำการสร้าง Keypair ในแต่ละ Worker ดังนี้ (WORKER_FQDN คือชื่อที่จด domain ไว้ ไม่มีก็ใส่ /etc/hosts เอาก็ได้ครับ แต่ต้องใส่เองทุกเครื่อง)
    sudo openssl genrsa -out ${WORKER_FQDN}-worker-key.pem 2048
    sudo WORKER_IP=${WORKER_IP} openssl req -new -key ${WORKER_FQDN}-worker-key.pem -out ${WORKER_FQDN}-worker.csr -subj "/CN=${WORKER_FQDN}" -config worker-openssl.cnf
    sudo WORKER_IP=${WORKER_IP} openssl x509 -req -in ${WORKER_FQDN}-worker.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out ${WORKER_FQDN}-worker.pem -days 365 -extensions v3_req -extfile worker-openssl.cnf
  • สุดท้ายคือการสร้าง Keypair ของ Cluster Admin
     sudo openssl genrsa -out admin-key.pem 2048
     sudo openssl req -new -key admin-key.pem -out admin.csr -subj "/CN=kube-admin"
     sudo openssl x509 -req -in admin.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out admin.pem -days 365
  • ทำได้ครบแล้วก็จะได้ประมาณดังรูป (ยกตัวอย่างมี  4 worker ใช้วิธีเขียน gen-worker-keypair.sh เพื่อรันหลาย ๆ รอบ ลองหาวิธีเขียนดูเองนะครับ)

วิธีการตั้งค่า Kubernetes บน CoreOS Cluster[5]

เพื่อให้การติดตั้งง่ายขึ้น อย่าลืมกำหนดค่าต่าง ๆ จดไว้ก่อน ไม่งั้นจะงงได้ ประมาณนี้ครับ

ETCD_ENDPOINT=http://a.b.c.d:2379,http://a.b.c.e:2379,http://a.b.c.f:2379
SERVER_IP_NETWORK=?
K8S_SERVICE_IP=?
MASTER_HOST=?
DNS_SERVICE_IP=?
POD_NETWORK=?

ทำการติดตั้ง Master Node

  • ทำการติดตั้ง CoreOS บน Master Node
  • สร้าง directory ดังนี้
    sudo mkdir -p /etc/kubernetes/ssl
  • ทำการ copy ที่สร้างไว้ก่อนหน้านี้มาไว้ใน folder ดังนี้
    File: /etc/kubernetes/ssl/ca.pem
    File: /etc/kubernetes/ssl/apiserver.pem
    File: /etc/kubernetes/ssl/apiserver-key.pem
  • เปลี่ยน permission และ owner file เฉพาะไฟล์ -key* ดังนี้
    sudo chmod 600 /etc/kubernetes/ssl/*-key.pem
    sudo chown root:root /etc/kubernetes/ssl/*-key.pem
  • จากนั้นตั้งค่า flannel network[3] เป็น network ที่เอาไว้เชื่อมแต่ละ pod ให้สามารถคุยกันได้ข้ามเครื่อง (ต้องทำทุก node รวม worker ด้วย) เพื่อสร้างวง virtual ip ให้กับ pod ดังนี้
    sudo mkdir /etc/flannel

    ทำการสร้างไฟล์ option.env

    sudo vim /etc/flannel/options.env

    เนื้อหาในไฟล์ดังนี้

    FLANNELD_IFACE=<Master IP>
    FLANNELD_ETCD_ENDPOINTS=http://<Master IP>:2379,http://<Node1 IP>:2379,http://<Node2 IP>:2379
  • จากนั้นทำการสร้าง flannel service ดังนี้
    sudo mkdir -p /etc/systemd/system/flanneld.service.d/
    sudo vim /etc/systemd/system/flanneld.service.d/40-ExecStartPre-symlink.conf

    เนื้อหาในไฟล์ดังนี้

    [Service]
    ExecStartPre=/usr/bin/ln -sf /etc/flannel/options.env /run/flannel/options.env
  • จากนั้นทำการตั้งค่า docker เพื่อกำหนดให้ใช้งานผ่าน flannel โดยสร้างไฟล์ config ดังนี้
    sudo mkdir -p /etc/systemd/system/docker.service.d
    sudo vim /etc/systemd/system/docker.service.d/40-flannel.conf

    เนื้อหาในไฟล์ดังนี้

    [Unit]
    Requires=flanneld.service
    After=flanneld.service
    [Service]
    EnvironmentFile=/etc/kubernetes/cni/docker_opts_cni.env

    โดยที่ทำสร้างไฟล์ docker_opts_cni.env ขึ้นมาประกอบดังนี้

    sudo mkdir -p /etc/kubernetes/cni
    sudo vim /etc/kubernetes/cni/docker_opts_cni.env

    เนื้อหาในไฟล์ดังนี้

    DOCKER_OPT_BIP=""
    DOCKER_OPT_IPMASQ=""
  • จากนั้นทำการ restart docker service สักรอบดังนี้
    sudo systemctl restart docker
  • หนังจากนั้นทำการสร้าง config ไฟล์สุดท้ายสำหรับ flannel ดังนี้
    sudo mkdir -p /etc/kubernetes/cni/net.d
    sudo vim /etc/kubernetes/cni/net.d/10-flannel.conf

    เนื้อหาในไฟล์ดังนี้

    {
     "name": "podnet",
     "type": "flannel",
     "delegate": {
     "isDefaultGateway": true
     }
    }
  • ทำการ start flannel service และตรวจสถานะการทำงานดู
    sudo systemctl start flanneld
    sudo systemctl enable flanneld
    sudo systemctl status flanneld
  • จากนั้นทำการสั่งให้ทำ vxlan ผ่าน etcdctl ให้กับทุก node ดังนี้ (เปลี่ยน IP เป็นวงที่ต้องการ)
    sudo etcdctl set /coreos.com/network/config "{\"Network\":\"10.2.0.0/16\",\"Backend\":{\"Type\":\"vxlan\"}}"
  • จากนั้นทำการสร้าง kubelet service ดังนี้
    sudo vim /etc/systemd/system/kubelet.service

    เนื้อหาในไฟล์ดังนี้ (KUBELET_IMAGE_TAG version สามารถตรวจสอบล่าสุดได้ที่ https://quay.io/repository/coreos/hyperkube?tab=tags)

    [Service]
    Environment=KUBELET_IMAGE_TAG=v1.6.2_coreos.0
    Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
      --volume var-log,kind=host,source=/var/log \
      --mount volume=var-log,target=/var/log \
      --volume dns,kind=host,source=/etc/resolv.conf \
      --mount volume=dns,target=/etc/resolv.conf"
    ExecStartPre=/usr/bin/mkdir -p /etc/kubernetes/manifests
    ExecStartPre=/usr/bin/mkdir -p /var/log/containers
    ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
    ExecStart=/usr/lib/coreos/kubelet-wrapper \
      --api-servers=http://127.0.0.1:8080 \
      --container-runtime=docker \
      --allow-privileged=true \
      --pod-manifest-path=/etc/kubernetes/manifests \
      --cluster_dns=<DNS_SERVICE_IP> \
      --cluster_domain=<CLUSTER_DNS_NAME>
    ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
  • ทำการ start kubelet service และตรวจสถานะการทำงานดู (ต้องรอสักพักเพราะ service จะต้องโหลด image มาติดตั้งให้ในการ start ครั้งแรก สามารถใช้คำสั่ง journalctl -xe เพื่อตรวจสอบสถานะการทำงาน) ซึ่งจะพบว่ายัง error เพราะยังไม่ได้ลง kube-apiserver (แนะนำลงตัวอื่นให้เสร็จให้หมดแล้วค่อยมา start kubelet service ก็ได้)
    sudo systemctl start kubelet
    sudo systemctl enable kubelet
    sudo systemctl status kubelet
  • ขั้นตอนต่อไปเป็นการสร้าง Rest API สำหรับจัดการ kubernetes (รวมถึงสามารถติดตั้งหน้าจอ GUI เพื่อควบคุมผ่าน Web ได้) โดยสร้างไฟล์ดังนี้
    sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml

    เนื้อหาในไฟล์มีดังนี้ (ระวังเรื่องวรรคด้วยนะครับ ถ้าจะพิมพ์เองไม่แนะนำ copy ไปแก้ดีกว่า)

    apiVersion: v1
    kind: Pod
    metadata:
      name: kube-apiserver
      namespace: kube-system
    spec:
      hostNetwork: true
      containers:
      - name: kube-apiserver
        image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
        command:
        - /hyperkube
        - apiserver
        - --bind-address=0.0.0.0
        - --etcd-servers=${ETCD_ENDPOINTS1},${ETCD_ENDPOINTS2},${ETCD_ENDPOINTS3}
        - --allow-privileged=true
        - --service-cluster-ip-range=${SERVICE_IP_RANGE}
        - --secure-port=443
        - --advertise-address=${ADVERTISE_IP}
        - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota
        - --tls-cert-file=/etc/kubernetes/ssl/apiserver.pem
        - --tls-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem
        - --client-ca-file=/etc/kubernetes/ssl/ca.pem
        - --service-account-key-file=/etc/kubernetes/ssl/apiserver-key.pem
        - --runtime-config=extensions/v1beta1/networkpolicies=true
        - --anonymous-auth=false
        livenessProbe:
          httpGet:
            host: 127.0.0.1
            port: 8080
            path: /healthz
          initialDelaySeconds: 15
          timeoutSeconds: 15
        ports:
        - containerPort: 443
          hostPort: 443
          name: https
        - containerPort: 8080
          hostPort: 8080
          name: local
        volumeMounts:
        - mountPath: /etc/kubernetes/ssl
          name: ssl-certs-kubernetes
          readOnly: true
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/ssl
        name: ssl-certs-kubernetes
      - hostPath:
          path: /usr/share/ca-certificates
        name: ssl-certs-host
    
  • เพื่อให้สมบูรณ์ไปในคราวเดียว ต่อด้วยติดตั้ง kube-proxy ดังนี้
    sudo vim /etc/kubernetes/manifests/kube-proxy.yaml

    มีเนื้อหาในไฟล์ดังนี้

    apiVersion: v1
    kind: Pod
    metadata:
      name: kube-proxy
      namespace: kube-system
    spec:
      hostNetwork: true
      containers:
      - name: kube-proxy
        image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
        command:
        - /hyperkube
        - proxy
        - --master=http://127.0.0.1:8080
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
      volumes:
      - hostPath:
          path: /usr/share/ca-certificates
        name: ssl-certs-host
    
  • ยังไม่หมดต่อด้วย kube-controller-manager ใช้ในการทำ scale, replica โดยเป็นตัวควบคุม API อีกชั้นหนึ่ง ทำการสร้างไฟล์ดังนี้
    sudo vim /etc/kubernetes/manifests/kube-controller-manager.yaml

    มีเนื้อหาในไฟล์ดังนี้

    apiVersion: v1
    kind: Pod
    metadata:
      name: kube-controller-manager
      namespace: kube-system
    spec:
      hostNetwork: true
      containers:
      - name: kube-controller-manager
        image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
        command:
        - /hyperkube
        - controller-manager
        - --master=http://127.0.0.1:8080
        - --leader-elect=true
        - --service-account-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem
        - --root-ca-file=/etc/kubernetes/ssl/ca.pem
        resources:
          requests:
            cpu: 200m
        livenessProbe:
          httpGet:
            host: 127.0.0.1
            path: /healthz
            port: 10252
          initialDelaySeconds: 15
          timeoutSeconds: 15
        volumeMounts:
        - mountPath: /etc/kubernetes/ssl
          name: ssl-certs-kubernetes
          readOnly: true
        - mountPath: /etc/ssl/certs
          name: ssl-certs-host
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/ssl
        name: ssl-certs-kubernetes
      - hostPath:
          path: /usr/share/ca-certificates
        name: ssl-certs-host
  • สำหรับอันสุดท้ายก็จะเป็น kube-scheduler (สำหรับการใช้งานเนื่องจากยังไม่ได้ลองใช้งาน ยังไม่เข้าใจว่าทำงานอย่างไร ถ้าว่างจะมาแก้อีกทีครับ) ทำการสร้างไฟล์ดังนี้
    sudo vim /etc/kubernetes/manifests/kube-scheduler.yaml

    มีเนื้อหาในไฟล์ดังนี้

    apiVersion: v1
    kind: Pod
    metadata:
      name: kube-scheduler
      namespace: kube-system
    spec:
      hostNetwork: true
      containers:
      - name: kube-scheduler
        image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
        command:
        - /hyperkube
        - scheduler
        - --master=http://127.0.0.1:8080
        - --leader-elect=true
        resources:
          requests:
            cpu: 100m
        livenessProbe:
          httpGet:
            host: 127.0.0.1
            path: /healthz
            port: 10251
          initialDelaySeconds: 15
          timeoutSeconds: 15
    
  • ในส่วนของ Calico จะใช้สำหรับทำ Network Policy เนื่องจากเป็น Optional จึงขอยังไม่ตั้งค่าสำหรับบริการนี้
  • ในกรณีที่ต้องการแจ้ง systemd ว่าเรามีการเปลี่ยนข้อมูลใน config และต้องการ rescan ทุกอย่างอีกครั้งให้สั่งดังนี้
    sudo systemctl daemon-reload
  • จากนั้นทดสอบ restart kubelet service ใหม่อีกครั้งและตรวจสอบ status หรือ journalctl -xe เพื่อดูว่าการทำงานปกติหรือไม่ ถ้าไม่แก้ให้ถูกต้องแล้ว restart ใหม่อีกครั้งไปเรื่อย ๆ จนกว่าจะปกติ
    sudo systemctl restart kubelet

    จบตอนแรกไปก่อนนะครับ ตอนต่อไปจะต่อด้วย worker node และ addon อื่น ๆ

==================================

Reference :

[1] [Kubernetes] Deploy Docker Container บน Google Container Engine : https://www.nomkhonwaan.com/2016/04/12/deploy-docker-container-on-google-container-engine

[2] วิธีสร้าง CoreOS Cluster : https://sysadmin.psu.ac.th/2017/05/04/setup-coreos-cluster/

[3] How to Deploy Kubernetes on CoreOS Cluster : https://www.upcloud.com/support/deploy-kubernetes-coreos/

[4] CoreOS -> Cluster TLS using OpenSSL : https://coreos.com/kubernetes/docs/latest/openssl.html

[5] CoreOS -> CoreOS + Kubernetes Step By Step : https://coreos.com/kubernetes/docs/latest/getting-started.html

[6] CoreOS -> Tectonic, Kubernetes cluster orchestrator with enterprise integration  : https://coreos.com/tectonic/docs/latest/