第三堂課介紹 Kubernetes。
Conway’s Law
Organizations which design systems … are constrained to produce designs which are copies of the communication structures of these organizations
What is Kubernetes?
將程式包成 container 只解決了 5% 的問題。真實的問題是
- App Configuration
- Service Discovery
- Managing Updates
- Monitoring
Kubernetes 提供了新一層 abstractions 來包覆 deployment,讓使用者可以專注於大局。
Kubernetes 可以讓我們將每台機器抽象起來,讓操作 cluster 起來就像邏輯上操作一台機器一樣。
在 Kubernetes 裡,描述一串 applications ,設定如何互動,並使之發生。
Setting up Kubernetes for this course
課程上是使用 Google Cloud Platform(GCP),而我是準備一台 Ubuntu server ,上面安裝好 Kubernetes 來進行這個課程。
Course Repo
Get started
Launch a single instance
1
| $ kubectl run nginx --image=nginx:1.10.0
|
將 nginx container 透過 kubectl 指令啟動
Get pods
在 Kubernetes 裡,所有的 Containers 運行在 pod ,使用上述指令列出正在運行的 pods
Expose nginx
1
| $ kubectl expose deployment nginx --port 80 --type LoadBalancer
|
上面指令會建立出 load balancer 且派發一個 public IP 在上面,並將 80 port 暴露給外面。任何一個 Client 透過派發的 IP 連線將會經過 load balancer 傳送到內部 container,在這邊的例子是 nginx 。
List services
列出 services 資訊,像是 Public IP, expose port 。
Pods
Kubernetes 的核心是 Pods ,Pods 代表的是 Logical Application 。
- One or more containers - 將具有依賴關係的 containers 包在同一個 pod 裡。
- Volumes - containers 放置資料的地方,可以被所有在 pod 裡的 containers 使用
- Shared namespace - Shared namespace 表示在相同 pod 裡的 containers 互相溝通,並分享相同的 Volumes
- One IP per pod - 在同一個 pod 裡的 containers 共享相同的 public IP 。
Pods Configuration
在 Kubernetes 裡,建立 pods 是透過設定 yaml 格式的設定檔。如下 monolith.yaml
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| $ cat pods/monolith.yaml apiVersion: v1 kind: Pod metadata: name: monolith labels: app: monolith spec: containers: - name: monolith image: udacity/example-monolith:1.0.0 args: - "-http=0.0.0.0:80" - "-health=0.0.0.0:81" - "-secret=secret" ports: - name: http containerPort: 80 - name: health containerPort: 81 resources: limits: cpu: 0.2 memory: "10Mi"
|
在 contains:
底下標示 container 的屬性。包含
name:
: container 名稱
image:
: 要使用的 container image
args:
Application 執行時所需的參數
port:
: 需要暴露給外面的 ports
resources:
: 限制 cpu 和記憶體使用量。
Create the pod
將設定好的 monolith.yaml
透過底下指令將 monolith pod 建立出來。
1 2
| $ kubectl create -f pods/monolith.yaml pod "monolith" created
|
Examine pods
在 pod 剛建立出來時需要準備一段時間讓 container 啟動。透過以下指令觀察是否建立出來以及正在運行。
1 2 3
| $ kubectl get pods NAME READY STATUS RESTARTS AGE monolith 1/1 Running 0 14s
|
Describe pods
需要檢查詳細的資訊可以透過以下指令。
1
| $ kubectl describe pods monolith
|
會列出 container 被指派的 IP,暴露的 port ,以及其他許多的 metadata 和發生的 events log。
Port forward
設定一個或多個 local port 轉送到 pod 裡的 port。
1 2 3
| $ kubectl port-forward monolith 10080:80 Forwarding from 127.0.0.1:10080 -> 80 Forwarding from [::1]:10080 -> 80
|
這道指令會處於 listen 狀態,開啟另一個 terminal 測試 monolith 連線。
1 2 3 4
| $ curl http://127.0.0.1:10080 {"message":"Hello"} $ curl http://127.0.0.1:10080/secure authorization failed
|
測試 login。
1 2 3 4 5
| $ curl -u user http://127.0.0.1:10080/login Enter host password for user 'user': password {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJleHAiOjE0NzcxNTA3ODQsImlhdCI6MTQ3Njg5MTU4NCwiaXNzIjoiYXV0aC5zZXJ2aWNlIiwic3ViIjoidXNlciJ9.Rgm5BBG8VSiLH-u26Am1QAaXtz05nzvfWWZhHjOcaus"} $ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJleHAiOjE0NzcxNTA3ODQsImlhdCI6MTQ3Njg5MTU4NCwiaXNzIjoiYXV0aC5zZXJ2aWNlIiwic3ViIjoidXNlciJ9.Rgm5BBG8VSiLH-u26Am1QAaXtz05nzvfWWZhHjOcaus" http://127.0.0.1:10080/secure {"message":"Hello"}
|
View logs
列出 pod 裡的 container log。
1 2 3 4 5 6 7 8 9 10 11
| $ kubectl logs monolith 2016/10/19 15:04:10 Starting server... 2016/10/19 15:04:10 Health service listening on 0.0.0.0:81 2016/10/19 15:04:10 HTTP service listening on 0.0.0.0:80 127.0.0.1:59954 - - [Wed, 19 Oct 2016 15:22:34 UTC] "GET / HTTP/1.1" curl/7.47.0 127.0.0.1:59986 - - [Wed, 19 Oct 2016 15:22:48 UTC] "GET /secure HTTP/1.1" curl/7.47.0 127.0.0.1:33138 - - [Wed, 19 Oct 2016 15:38:29 UTC] "GET /login HTTP/1.1" curl/7.47.0 127.0.0.1:33226 - - [Wed, 19 Oct 2016 15:39:26 UTC] "GET /login HTTP/1.1" curl/7.47.0 127.0.0.1:33252 - - [Wed, 19 Oct 2016 15:39:43 UTC] "GET /login HTTP/1.1" curl/7.47.0 127.0.0.1:33430 - - [Wed, 19 Oct 2016 15:41:41 UTC] "GET /secure HTTP/1.1" curl/7.47.0 127.0.0.1:33492 - - [Wed, 19 Oct 2016 15:42:20 UTC] "GET /secure HTTP/1.1" curl/7.47.0
|
加 -f
進入 stream 模式,監控即時 log 。
Enter container
需要像 docker exec
進入 container 執行指令的話,透過底下指令。
1 2
| $ kubectl exec monolith --stdin --tty -c monolith /bin/sh /
|
Monitor and Health Checks
Kubernetes 提供了 Health Checks 機制,讓使用者自訂檢查 applications 健康狀態和 readiness check。
Readiness check 用來確認 pod 是否已經準備好可以提供服務,當 check failed 時,container 會被標記成 not ready 並從 load balancer 中移除。
Liveness probe 檢查 container 是否活著,如果檢查多次失敗, container 將會重啟。
問題練習
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| $ cat pods/monolith.yaml ... livenessProbe: httpGet: path: /healthz port: 81 scheme: HTTP initialDelaySeconds: 5 periodSeconds: 15 timeoutSeconds: 5 readinessProbe: httpGet: path: /readiness port: 81 scheme: HTTP initialDelaySeconds: 5 timeoutSeconds: 1 $ kubectl create -f pods/monolith.yaml pod "healthy-monolith" created $ kubectl describe pods healthy-monolith ... State: Running Started: Fri, 21 Oct 2016 22:37:12 +0800 Ready: True Restart Count: 0 Liveness: http-get http://:81/healthz delay=5s timeout=5s period=15s Readiness: http-get http://:81/readiness delay=5s timeout=1s period=10s ...
|
使用 healthy-monolith pod,回答下面三個問題。
Secret and ConfigMap
ConfigMap 和 Secret 相似,但是 ConfigMap 用來存放較不敏感的資料,而像密碼或金鑰之類的需要安全性的資料則使用 Secret。 Secret 會在 pod 建立時, mount 到 container 裡當作 volume,讓 container 擁有和使用。
ConfigMap
Secrets
1 2 3 4
| $ kubectl create secret generic tls-certs --from-file=tls/ $ kubectl create -f pods/secure-monolith.yaml
|
以下使用 nginx reverse proxy 為例子,將 https(443 port) 連線轉送到 http(80 port)。
Create secret
https 連線需要 Certificate ,使用 secret 將 Certificate 封裝起來。
1
| $ kubectl create secret generic tls-certs --from-file=tls/
|
Describe secret
kubectl will create a key for each file in the tls directory under the tls-certs secret bucket. Use the kubectl describe
command to verify that:
1
| $ kubectl describe secrets tls-certs
|
Create ConfigMap
將 nginx reverse proxy 的設定透過 ConfigMap 封裝起來。
1
| $ kubectl create configmap nginx-proxy-conf --from-file=nginx/proxy.conf
|
Describe ConfigMap
透過 kubectl describe configmap
獲得詳細的資訊。
1
| $ kubectl describe configmap nginx-proxy-conf
|
操作練習
使用 secure-monolith pod 作為這次練習。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| $ cat pods/secure-monolith.yaml apiVersion: v1 kind: Pod metadata: name: "secure-monolith" labels: app: monolith spec: containers: - name: nginx image: "nginx:1.9.14" lifecycle: preStop: exec: command: ["/usr/sbin/nginx","-s","quit"] volumeMounts: - name: "nginx-proxy-conf" mountPath: "/etc/nginx/conf.d" - name: "tls-certs" mountPath: "/etc/tls" - name: monolith image: "udacity/example-monolith:1.0.0" ports: - name: http containerPort: 80 - name: health containerPort: 81 resources: limits: cpu: 0.2 memory: "10Mi" livenessProbe: httpGet: path: /healthz port: 81 scheme: HTTP initialDelaySeconds: 5 periodSeconds: 15 timeoutSeconds: 5 readinessProbe: httpGet: path: /readiness port: 81 scheme: HTTP initialDelaySeconds: 5 timeoutSeconds: 1 volumes: - name: "tls-certs" secret: secretName: "tls-certs" - name: "nginx-proxy-conf" configMap: name: "nginx-proxy-conf" items: - key: "proxy.conf" path: "proxy.conf"
|
在 secure-monolith pod 設定裡,可以看到使用兩個 container ,分別為 nginx 和 monolith , nginx 會作為 reverse proxy 將連線導入 monolith 。
在 nginx 設定裡,有設定 lifecycle
當 nginx container 要關閉時,先執行所設定的 command ,清理剩餘的工作。在 volumeMounts
裡,設定先前建立的 secret 和 ConfigMap ,將之 mount。
Create secure-monolith pod
1 2 3 4
| Create the secure-monolith Pod using kubectl. kubectl create -f pods/secure-monolith.yaml kubectl get pods secure-monolith
|
Check connection
使用先前學過的 port-forward 指令,將 local 10443 port 開通到 nginx 443 port ,測試 TLS 連線能不能相通。
1 2 3 4 5
| kubectl port-forward secure-monolith 10443:443 curl --cacert tls/ca.pem https://127.0.0.1:10443 kubectl logs -c nginx secure-monolith
|
Services
不管是使用 pod 還是 docker container ,都會遇到一個問題是,container 會因各種原因需要進行重啟,而重啟時 IP 會重新指派,因此無法仰賴固定的 IP 。因此 Kubernetes 引入 Services 當作 pod 的 abstraction ,由 Services 當作是 pods 前端,提供穩定的連接,因此後端的 pods 的更動,就不會影響整體。
Persistent Endpoint for Pods
- Use labels to select pods
- Internal or External IP
Create Services
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $ cat services/monolith.yaml kind: Service apiVersion: v1 metadata: name: "monolith" spec: selector: app: "monolith" secure: "enabled" ports: - protocol: "TCP" port: 443 targetPort: 443 nodePort: 31000 type: NodePort
|
Services 設定裡透過 selector
將有符合 label 的 pods 選出來聚在一起。並把所有 pods 443 port 暴露出來,讓 local 31000 port 轉到 pods 443 port。
接著建立 services 。
1
| $ kubectl create -f services/monolith.yaml
|
這時 services 沒有選擇到任何東西 ,因為沒有一個 pod 的 label 符合,可以透過以下指令檢查。
1
| $ kubectl get pods -l "app=monolith,secure=enabled"
|
將 secure-monolith pod 加入 label。
1 2 3 4
| $ kubectl label pods secure-monolith "secure=enabled" $ kubectl get pods -l "app=monolith,secure=enabled" NAME READY STATUS RESTARTS AGE secure-monolith 2/2 Running 0 5h
|
此時 services 就可以接管 secure-monolith pod 。