ถ้าต้องการระบบจัดการ 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/