Kubernetes(k8s)中文文件 管理應用: 在生產環境中使用Pods和容器_Kubernetes中文社群
譯者:張鑫
永久性儲存
容器中的檔案系統會隨著容器的停止而消失;如果容器重啟或出錯停止,則會使得容器檔案中的資料丟失,而且容器即使重新執行也會忘記之前的資料和狀態。如果需要比容器的生命更為長久的儲存方案,我們需要使用資料卷。這個需求對於有狀態服務更為重要,例如 資料庫,健-值儲存等。
舉例來說,Redis是一個健-值儲存快取(我們在Guestbook和一些其他的例子中使用過)。我們可以用如下方法為它加一個數據卷:
apiVersion: v1 kind: ReplicationController metadata: name: redis spec: template: metadata: labels: app: redis tier: backend spec: # 為Pod提供一個數據卷 volumes: - name: data emptyDir: {} containers: - name: redis image: kubernetes/redis:v1 ports: - containerPort: 6379 # 將資料卷載入到Pod中 volumeMounts: - mountPath: /redis-master-data name: data # 必須和上面定義的資料卷名字匹配
emptyDir 資料卷的生命週期與Pod的生命週期一致,因此會比任何一個容器更長久;如果某個容器失效或重啟了,我們的資料還會繼續存在。
除了本地磁碟所支援的 emptyDir ,Kubernetes還支援多種網路儲存方案,包括GCE的PD,EC2的EBS。這些儲存方案更適合用來儲存關鍵資料,同時還能處理在節點上動態載入,解除安裝資料卷。
分發認證資訊
很多應用需要認證資訊,例如密碼,OAuth令牌,和TLS的祕鑰等,用來和其他的應用、資料庫、服務等進行認證。在容器映象中或是通過環境變數來儲存這些私密資訊並不理想,因為只要能夠訪問這些映象,或是Pod/容器配置,或是宿主機的檔案系統,或是DockerDaemon,一個人就可以獲取這些私密資訊。
Kubernetes提供一個叫做祕密的機制用來幫助分發敏感的認證資訊。一個祕密是一種含有map資料的資源。舉例來說,一個含有使用者名稱和密碼的祕密可以定義如下:
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: password: dmFsdWUtMg0K username: dmFsdWUtMQ0K
如同其它資源一樣,這個祕密可以通過 create 命令來生成,同時可以通過 get 命令來檢視:
$ kubectl create -f ./secret.yaml secrets/mysecret $ kubectl get secrets NAME TYPE DATA default-token-v9pyz kubernetes.io/service-account-token 2 mysecret Opaque 2
要使用祕密,我們需要在一個pod的模版中引用它。 secret 資料卷可以讓我們像記憶體目錄一樣來把祕密載入到容器裡。
apiVersion: v1 kind: ReplicationController metadata: name: redis spec: template: metadata: labels: app: redis tier: backend spec: volumes: - name: data emptyDir: {} - name: supersecret secret: secretName: mysecret containers: - name: redis image: kubernetes/redis:v1 ports: - containerPort: 6379 # Mount the volume into the pod volumeMounts: - mountPath: /redis-master-data name: data # must match the name of the volume, above - mountPath: /var/run/secrets/super name: supersecret
與私有映象倉庫進行認證我們還可以利用祕密來傳遞私有映象倉庫的認證資訊。
第一步,我們生成一個 .dockercfg 檔案(例如可以通過執行 docker login<registry.domain> 命令)。然後,我們將生成的 .dockercfg 檔案放到一個祕密資源裡,例如:
$ docker login Username: janedoe Password: ●●●●●●●●●●● Email: [email protected] WARNING: login credentials saved in /Users/jdoe/.dockercfg. Login Succeeded $ echo $(cat ~/.dockercfg) { "https://index.docker.io/v1/": { "auth": "ZmFrZXBhc3N3b3JkMTIK", "email": "[email protected]" } } $ cat ~/.dockercfg | base64 eyAiaHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vdjEvIjogeyAiYXV0aCI6ICJabUZyWlhCaGMzTjNiM0prTVRJSyIsICJlbWFpbCI6ICJqZG9lQGV4YW1wbGUuY29tIiB$ cat > /tmp/image-pull-secret.yaml <<EOF apiVersion: v1 kind: Secret metadata: name: myregistrykey data: .dockercfg: eyAiaHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vdjEvIjogeyAiYXV0aCI6ICJabUZyWlhCaGMzTjNiM0prTVRJSyIsICJlbWFpbCI6ICJqZG9lQGV4YW1wbGUuY29tIiBtype: kubernetes.io/dockercfg EOF $ kubectl create -f ./image-pull-secret.yaml secrets/myregistrykey
現在你就可以生成一個pod,並通過新增 imagePullSecrets 部分來引用上述生成的祕密:
apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - name: foo image: janedoe/awesomeapp:v1 imagePullSecrets: - name: myregistrykey
協同容器
Pods支援將多個容器一起排程,保證它們執行在同一個宿主機上。他們可以被用來支援一整個應用棧,但主要應用場景是用來執行輔助程式。經典的例子包括資料釋出、獲取程式,代理等等。
這些容器往往需要通過檔案系統相互通訊,而這個通訊過程可以通過在兩個容器中都加入同樣的資料捲來實現。這個模式的一個例子就是網路伺服器通過git儲存庫來等待程式最近更新:
apiVersion: v1 kind: ReplicationController metadata: name: my-nginx spec: template: metadata: labels: app: nginx spec: volumes: - name: www-data emptyDir: {} containers: - name: nginx image: nginx # This container reads from the www-data volume volumeMounts: - mountPath: /srv/www name: www-data readOnly: true - name: git-monitor image: myrepo/git-monitor env: - name: GIT_REPO value: http://github.com/some/repo.git # This container writes to the www-data volume volumeMounts: - mountPath: /data name: www-data
資源管理
只有在有足夠的CPU和儲存的地方Kubernetes的排程器才會放置應用程式,但在知道應用程式需要多少資源的時候也只能這麼做。指定的CPU太小的後果就是,如果太多其他容器被安排到同一個節點的話,CPU資源會被耗盡。同樣地,容器也可能會由於在耗盡儲存要不到其他記憶體的情況下而不可預見的終止執行,對於大記憶體的應用程式尤其可能出現這種情況。
如果沒有指定資源需求,那麼資源數量在名義上是被假定的(這個預設值是通過Limitrange給預設值的名稱空間設定的)。它也可以通過`kubectl desribe limitrange limits`被檢視。)我們可以明確地把要求的資源數量列舉如下:
apiVersion: v1 kind: ReplicationController metadata: name: my-nginx spec: replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 resources: limits: # cpu units are cores cpu: 500m # memory units are bytes memory: 64Mi
容器的執行超過指定限度的時候會因為記憶體耗盡而終止程式,所以制定一個略高於預期的值提高了總體的可信度。
如果不確定該請求多少資源,那麼我們可以先不指定啟動應用程式資源,然後根據資源使用情況量監控來確定適當的值。
健康檢查
許多應用程式經過長時間的執行,最終過渡到了無法執行的狀態,除了重啟,無法恢復。Kubernetes提供活性探針來檢測並且對相應狀況進行相應的補救措施。通常來檢測一個應用程式的方法是用HTTP,詳細說明如下:
apiVersion: v1 kind: ReplicationController metadata: name: my-nginx spec: replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 livenessProbe: httpGet: # Path to probe; should be cheap, but representative of typical behavior path: /index.html port: 80 initialDelaySeconds: 30 timeoutSeconds: 1
很多時候,應用程式只是暫時無法服務,之後會自己恢復。通常在這樣的情況下,我們不願意關閉應用程式,但是也不想傳送請求,因為應用層程式不正確迴應或者根本不迴應。常見的這樣的場景是載入大資料或者在應用程式啟動期間配置檔案。Kubernetes提供探針來檢測和緩解這種情況。探針的配置只是用到 readinessProbe 欄位。由容器組成的pod報告說他們還沒準備好通過Kubernetes服務接收通訊量。
生命週期外掛介面和終止通知
當然,節點和應用程式在任何時候都可能會執行失敗,但是當應用程式的終止被人為下達的時候,很多應用程式受益於正常關機來完成正在處理的請求。為了支援這樣的情況,Kubernetes支援兩種資訊通知:Kubernetes將傳送終止訊號給應用程式,這種訊號可以被接收到來平滑的終止程式。如果應用程式沒有儘早終止,無條件終止命令將會在10秒後傳送。Kubernetes支援(可選擇的)預停止生命週期外掛介面,這個動作執行在傳送終止訊號之前。
預停止外掛介面的細節跟調查細節相似,但是沒有時限相關引數。例如:
apiVersion: v1 kind: ReplicationController metadata: name: my-nginx spec: replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 lifecycle: preStop: exec: # SIGTERM triggers a quick exit; gracefully terminate instead command: ["/usr/sbin/nginx","-s","quit"]
終止資訊
為了達到一個相當高水平的實用性,特別是為了積極開發應用,快速除錯失敗是很重要的。除了一般的日誌採集,Kubernetes還能通過查出重大錯誤原因來加速除錯,並在某種程度上通過kubectl或者UI陳列出來。可以指定一個’terminationMessagePath’來讓容器寫下它的“death rattle“,比如宣告失敗訊息,堆疊跟蹤,免責條款等等。預設途徑是‘/dev/termination-log’。
這是一個小例子:
apiVersion: v1 kind: Pod metadata: name: pod-w-message spec: containers: - name: messager image: "ubuntu:14.04" command: ["/bin/sh","-c"] args: ["sleep 60 && /bin/echo Sleep expired > /dev/termination-log"]
訊息和上一個(也就是:最近的)終止的其他狀態是一起被記錄的。
$ kubectl create -f ./pod.yaml pods/pod-w-message $ sleep 70 $ kubectl get pods/pod-w-message -o template -t "{{range .status.containerStatuses}}{{.lastState.terminated.Sleep expired $ kubectl get pods/pod-w-message -o template -t "{{range .status.containerStatuses}}{{.lastState.terminated.0