不背鍋運(yùn)維:耗時(shí)1周整理:分享K8S Pod知識(shí)點(diǎn),帶你一文打盡

1. Pod概念熱身

Pod是一個(gè)邏輯抽象概念,K8s創(chuàng)建和管理的最小單元,一個(gè)Pod由一個(gè)容器或多個(gè)容器組成。 特點(diǎn):

  • 一個(gè)Pod可以理解為是一個(gè)應(yīng)用實(shí)例
  • Pod中容器始終部署在一個(gè)Node上
  • Pod中容器共享網(wǎng)絡(luò)、存儲(chǔ)資源

Pod主要用法:

  • 運(yùn)行單個(gè)容器:最常見(jiàn)的用法,在這種情況下,可以將Pod看作是單個(gè)容器的抽象封裝
  • 運(yùn)行多個(gè)容器:邊車模式(Sidecar),通過(guò)在Pod中定義專門容器,來(lái)執(zhí)行主業(yè)務(wù)容器需要的輔助工作,這樣好處是將輔助功能同主業(yè)務(wù)容器解耦,實(shí)現(xiàn)獨(dú)立發(fā)布和能力重用。 例如:
    • 日志收集
    • 應(yīng)用監(jiān)控

擴(kuò)展:READY字段的意義:

tantianran@test-b-k8s-master:~$ kubectl get pods -n test-a
NAME                         READY   STATUS    RESTARTS        AGE
goweb-demo-b98869456-25sj9   1/1     Running   1 (3m49s ago)   5d10h

在READY字段中,1/1的意義為在這個(gè)pod里,已準(zhǔn)備的容器/一共有多少個(gè)容器。

在pod中,它可以有3種類型的容器,分別是:

  • 基礎(chǔ)容器(pause container)
  • 初始化容器(init container)
  • 普通容器(業(yè)務(wù)容器/應(yīng)用容器)

2. POD內(nèi)容器間資源共享實(shí)現(xiàn)機(jī)制

2.1 共享數(shù)據(jù)的機(jī)制

  • emptyDir:會(huì)在 Pod 被刪除的同時(shí)也會(huì)被刪除,當(dāng) Pod 分派到某個(gè)節(jié)點(diǎn)上時(shí),emptyDir 卷會(huì)被創(chuàng)建,并且在 Pod 在該節(jié)點(diǎn)上運(yùn)行期間,卷一直存在。 就像其名稱表示的那樣,卷最初是空的。 盡管 Pod 中的容器掛載 emptyDir 卷的路徑可能相同也可能不同,這些容器都可以讀寫 emptyDir 卷中相同的文件。 當(dāng) Pod 因?yàn)槟承┰虮粡墓?jié)點(diǎn)上刪除時(shí),emptyDir 卷中的數(shù)據(jù)也會(huì)被永久刪除。
    • 例子

      apiVersion: v1
      kind: Pod
      metadata:
      name: test-pod1
      spec:
      containers:
      - image: nginx
          name: nginx1
          volumeMounts:
          - mountPath: /cache
          name: cache-volume
      - image: busybox
          name: bs1
          command: ["/bin/sh", "-c", "sleep 12h"]
          volumeMounts:
          - mountPath: /cache
          name: cache-volume
      volumes:
      - name: cache-volume
          emptyDir:
          sizeLimit: 500Mi
      
  • cephfs:cephfs 卷允許你將現(xiàn)存的 CephFS 卷掛載到 Pod 中,cephfs 卷的內(nèi)容在 Pod 被刪除時(shí)會(huì)被保留,只是卷被卸載了。 這意味著 cephfs 卷可以被預(yù)先填充數(shù)據(jù),且這些數(shù)據(jù)可以在 Pod 之間共享。同一 cephfs 卷可同時(shí)被多個(gè)寫者掛載。

2.2 共享網(wǎng)絡(luò)的機(jī)制

共享網(wǎng)絡(luò)的機(jī)制是由Pause容器實(shí)現(xiàn),下面慢慢分析一下,啥是pause,了解一下它的作用等等。

  1. 先準(zhǔn)備一個(gè)yaml文件(pod1.yaml ),創(chuàng)建一個(gè)pod,pod里包含兩個(gè)容器,一個(gè)是名為nginx1的容器,還有一個(gè)是名為bs1的容器
apiVersion: v1
kind: Pod
metadata:
  name: test-pod1
spec:
  containers:
  - image: nginx
    name: nginx1
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  - image: busybox
    name: bs1
    command: ["/bin/sh", "-c", "sleep 12h"]
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi
  1. 開(kāi)始創(chuàng)建
tantianran@test-b-k8s-master:~$ kubectl create -f pod1.yaml 
pod/test-pod1 created
  1. 創(chuàng)建完后看看在哪個(gè)節(jié)點(diǎn)
tantianran@test-b-k8s-master:~$ kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE     IP              NODE                NOMINATED NODE   READINESS GATES
test-pod1   2/2     Running   0          9m21s   10.244.222.44   test-b-k8s-node02   <none>           <none>
  1. 去到對(duì)應(yīng)的節(jié)點(diǎn)查看容器
tantianran@test-b-k8s-node02:~$ sudo docker ps | grep test-pod1
0db01653bdac   busybox                                                "/bin/sh -c 'sleep 1…"   9 minutes ago    Up 9 minutes              k8s_bs1_test-pod1_default_c3a15f70-3ae2-4a73-8a84-d630c047d827_0
296972c29efe   nginx                                                  "/docker-entrypoint.…"   9 minutes ago    Up 9 minutes              k8s_nginx1_test-pod1_default_c3a15f70-3ae2-4a73-8a84-d630c047d827_0
a5331fba7f11   registry.aliyuncs.com/google_containers/pause:latest   "/pause"                 10 minutes ago   Up 10 minutes             k8s_POD_test-pod1_default_c3a15f70-3ae2-4a73-8a84-d630c047d827_0
tantianran@test-b-k8s-node02:~$ 

通過(guò)查看容器,名為test-pod1的pod里,除了兩個(gè)業(yè)務(wù)容器外(k8s_bs1_test-pod1、nginx1_test-pod1),還有一個(gè)pause容器。這個(gè)到底是什么鬼呢?

「對(duì)pause容器的理解」

  • pause容器又叫Infra container,就是基礎(chǔ)設(shè)施容器的意思,Infra container只是pause容器的一個(gè)叫法而已

  • 上面看到paus容器,是從registry.aliyuncs.com/google_containers/pause:latest這個(gè)鏡像拉起的

  • 在其中一臺(tái)node節(jié)點(diǎn)上查看docker鏡像,可看到該鏡像的大小是240KB

    registry.aliyuncs.com/google_containers/pause        latest       350b164e7ae1   8 years ago     240kB
    
  • 根據(jù)理解,畫了圖:

nginx1容器里的nginx組件默認(rèn)監(jiān)聽(tīng)的端口是80,在bs1容器里去curl http://127.0.0.1 就可以放到nginx1容器的80端口。

# step1:查看test-pod1里的容器
kubectl get pods test-pod1 -o jsonpath={.spec.containers[*].name}

# step2:進(jìn)入指定容器
kubectl exec -it test-pod1 -c bs1 -- sh

# 訪問(wèn)nginx(因沒(méi)有curl命令,此處用wget)
/ # wget http://127.0.0.1
Connecting to 127.0.0.1 (127.0.0.1:80)
saving to 'index.html'
index.html           100% |*************************************************************************************************************|   615  0:00:00 ETA
'index.html' saved
/ # 

上面看到,使用wget命令下載到了index.html文件,說(shuō)明訪問(wèn)成功。

3. Pod常用管理命令

  • 查看pod里所有容器的名稱
kubectl get pods test-pod1 -o jsonpath={.spec.containers[*].name}
  • 進(jìn)入pod里的指定容器的終端,如下進(jìn)入pod為test-pod1里的容器nginx1和bs1
kubectl exec -it test-pod1 -c nginx1 -- bash
kubectl exec -it test-pod1 -c bs1 -- sh
  • 查看pod里指定容器的log
kubectl logs test-pod1 -c nginx1 

4. Pod的重啟策略+應(yīng)用健康檢查(應(yīng)用自修復(fù))

「重啟策略」

  • Always:當(dāng)容器終止退出,總是重啟容器,默認(rèn)策略
  • OnFailure:當(dāng)容器異常退出(退出狀態(tài)碼非0)時(shí),才重啟容器
  • Never:當(dāng)容器終止退出,從不重啟容器

查看pod的重啟策略

# 查看pod,以yaml格式輸出
kubectl get pods test-pod1 -o yaml

# 找到restartPolicy字段,就是重啟策略
restartPolicy: Always

「健康檢查有以下3種類型:」

健康檢查是檢查容器里面的服務(wù)是否正常

  • livenessProbe(存活探測(cè)):如果檢查失敗,將殺死容器,根據(jù)pod的restartPolicy來(lái)操作。
  • readinessProbe(就緒探測(cè)):如果檢查失敗,k8s會(huì)把Pod從service endpoints中剔除
  • startupProbe(啟動(dòng)探測(cè)):檢查成功才由存活檢查接手,用于保護(hù)慢啟動(dòng)容器

「支持以下三種檢查方法:」

  • httpGet:發(fā)起HTTP請(qǐng)求,返回200-400范圍狀態(tài)碼為成功。
  • exec:執(zhí)行Shell命令返回狀態(tài)碼是0為成功。
  • tcpSocket:發(fā)起TCP Socket建立成功。

「案例實(shí)戰(zhàn)」

  1. livenessProbe(存活探針):使用exec的方式(執(zhí)行Shell命令返回狀態(tài)碼是0則為成功)
apiVersion: v1
kind: Namespace
metadata:
  name: test-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  replicas: 10
  selector:
    matchLabels:
      app: goweb-demo
  template:
    metadata:
      labels:
        app: goweb-demo
    spec:
      containers:
      - name: goweb-demo
        image: 192.168.11.247/web-demo/goweb-demo:20221229v3
        livenessProbe:
          exec:
            command:
            - ls
            - /opt/goweb-demo/runserver
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8090
  selector:
    app: goweb-demo
  type: NodePort

periodSeconds 字段指定了 kubelet 應(yīng)該每 5 秒執(zhí)行一次存活探測(cè),initialDelaySeconds 字段告訴 kubelet 在執(zhí)行第一次探測(cè)前應(yīng)該等待 5 秒,kubelet 在容器內(nèi)執(zhí)行命令 ls /opt/goweb-demo/runserver 來(lái)進(jìn)行探測(cè)。 如果命令執(zhí)行成功并且返回值為 0,kubelet 就會(huì)認(rèn)為這個(gè)容器是健康存活的。 如果這個(gè)命令返回非 0 值,kubelet 會(huì)殺死這個(gè)容器并重新啟動(dòng)它。

驗(yàn)證存活檢查的效果

# 查看某個(gè)pod的里的容器,
kubectl get pods goweb-demo-686967fd56-556m9 -n test-a -o jsonpath={.spec.containers[*].name}

# 進(jìn)入某個(gè)pod里的容器
kubectl exec -it goweb-demo-686967fd56-556m9 -c goweb-demo -n test-a -- bash

# 進(jìn)入容器后,手動(dòng)刪除掉runserver可執(zhí)行文件,模擬故障
rm -rf /opt/goweb-demo/runserver

# 查看Pod詳情(在輸出結(jié)果的最下面,有信息顯示存活探針失敗了,這個(gè)失敗的容器被殺死并且被重建了。)
kubectl describe pod goweb-demo-686967fd56-556m9 -n test-a

Events:
  Type     Reason     Age                   From     Message
  ----     ------     ----                  ----     -------
  Warning  Unhealthy  177m (x6 over 3h59m)  kubelet  Liveness probe failed: ls: cannot access '/opt/goweb-demo/runserver': No such file or directory

# 一旦失敗的容器恢復(fù)為運(yùn)行狀態(tài),RESTARTS 計(jì)數(shù)器就會(huì)增加 1
tantianran@test-b-k8s-master:~$ kubectl get pods -n test-a
NAME                          READY   STATUS    RESTARTS      AGE
goweb-demo-686967fd56-556m9   1/1     Running   1 (22s ago)   13m # RESTARTS字段加1,
goweb-demo-686967fd56-8hzjb   1/1     Running   0             13m
...
  1. livenessProbe(存活探針):使用 httpGet 請(qǐng)求的方式檢查uri path是否正常
apiVersion: v1
kind: Namespace
metadata:
  name: test-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  replicas: 10
  selector:
    matchLabels:
      app: goweb-demo
  template:
    metadata:
      labels:
        app: goweb-demo
    spec:
      containers:
      - name: goweb-demo
        image: 192.168.11.247/web-demo/goweb-demo:20221229v3
        livenessProbe:
          httpGet:
            path: /login
            port: 8090
            httpHeaders:
            - name: Custom-Header
              value: Awesome
          initialDelaySeconds: 3
          periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8090
  selector:
    app: goweb-demo
  type: NodePort

在這個(gè)配置文件中,你可以看到 Pod 也只有一個(gè)容器。 periodSeconds 字段指定了 kubelet 每隔 3 秒執(zhí)行一次存活探測(cè)。 initialDelaySeconds 字段告訴 kubelet 在執(zhí)行第一次探測(cè)前應(yīng)該等待 3 秒。 kubelet 會(huì)向容器內(nèi)運(yùn)行的服務(wù)(服務(wù)在監(jiān)聽(tīng) 8090 端口)發(fā)送一個(gè) HTTP GET 請(qǐng)求來(lái)執(zhí)行探測(cè)。 如果服務(wù)器上 /login 路徑下的處理程序返回成功代碼,則 kubelet 認(rèn)為容器是健康存活的。 如果處理程序返回失敗代碼,則 kubelet 會(huì)殺死這個(gè)容器并將其重啟。返回大于或等于 200 并且小于 400 的任何代碼都表示成功,其它返回代碼都表示失敗。

驗(yàn)證效果

# 進(jìn)入容器刪除靜態(tài)文件,模擬故障
kubectl exec -it goweb-demo-586ff85ddb-4646k -c goweb-demo -n test-a -- bash
rm -rf login.html 

# 查看pod的log
kubectl logs goweb-demo-586ff85ddb-4646k -n test-a

2023/01/12 06:45:19 [Recovery] 2023/01/12 - 06:45:19 panic recovered:
GET /login HTTP/1.1
Host: 10.244.222.5:8090
Connection: close
Accept: */*
Connection: close
Custom-Header: Awesome
User-Agent: kube-probe/1.25


html/template: "login.html" is undefined
/root/my-work-space/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:911 (0x8836d1)
/root/my-work-space/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:920 (0x88378c)
/root/my-work-space/src/goweb-demo/main.go:10 (0x89584e)

# 查看pod詳情
kubectl describe pod goweb-demo-586ff85ddb-4646k -n test-a

Warning  Unhealthy  34s (x3 over 40s)   kubelet            Liveness probe failed: HTTP probe failed with statuscode: 500 # 狀態(tài)碼為500

# 恢復(fù)后查看Pod,RESTARTS計(jì)數(shù)器已經(jīng)增1
kubectl get pod goweb-demo-586ff85ddb-4646k -n test-a

NAME                          READY   STATUS    RESTARTS      AGE
goweb-demo-586ff85ddb-4646k   1/1     Running   1 (80s ago)   5m39s
  1. readinessProbe(就緒探針)結(jié)合livenessProbe(存活探針)探測(cè)tcp端口:

第三種類型的存活探測(cè)是使用 TCP 套接字。 使用這種配置時(shí),kubelet 會(huì)嘗試在指定端口和容器建立套接字鏈接。 如果能建立連接,這個(gè)容器就被看作是健康的,如果不能則這個(gè)容器就被看作是有問(wèn)題的。

apiVersion: v1
kind: Namespace
metadata:
  name: test-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  replicas: 10
  selector:
    matchLabels:
      app: goweb-demo
  template:
    metadata:
      labels:
        app: goweb-demo
    spec:
      containers:
      - name: goweb-demo
        image: 192.168.11.247/web-demo/goweb-demo:20221229v3
        readinessProbe:
          tcpSocket:
            port: 8090
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          tcpSocket:
            port: 8090
          initialDelaySeconds: 15
          periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8090
  selector:
    app: goweb-demo
  type: NodePort

TCP 檢測(cè)的配置和 HTTP 檢測(cè)非常相似。 這個(gè)例子同時(shí)使用就緒和存活探針。kubelet 會(huì)在容器啟動(dòng) 5 秒后發(fā)送第一個(gè)就緒探針。 探針會(huì)嘗試連接 goweb-demo 容器的 8090 端口。 如果探測(cè)成功,這個(gè) Pod 會(huì)被標(biāo)記為就緒狀態(tài),kubelet 將繼續(xù)每隔 10 秒運(yùn)行一次探測(cè)。除了就緒探針,這個(gè)配置包括了一個(gè)存活探針。 kubelet 會(huì)在容器啟動(dòng) 15 秒后進(jìn)行第一次存活探測(cè)。 與就緒探針類似,存活探針會(huì)嘗試連接 goweb-demo 容器的 8090 端口。 如果存活探測(cè)失敗,容器會(huì)被重新啟動(dòng)。

驗(yàn)證效果

# 進(jìn)入容器后,殺掉goweb-demo的進(jìn)程
kubectl exec -it goweb-demo-5d7d55f846-vm2kc -c goweb-demo -n test-a -- bash

root@goweb-demo-5d7d55f846-vm2kc:/opt/goweb-demo# ps -aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0   2476   576 ?        Ss   07:23   0:00 /bin/sh -c /opt/goweb-demo/runserver
root@goweb-demo-5d7d55f846-vm2kc:/opt/goweb-demo# kill -9 1

# 查看pod詳情,已經(jīng)發(fā)出警告
kubectl describe pod goweb-demo-5d7d55f846-vm2kc -n test-a

  Warning  Unhealthy  16s                 kubelet            Readiness probe failed: dial tcp 10.244.240.48:8090: connect: connection refused
  Warning  BackOff    16s                 kubelet            Back-off restarting failed container

# 查看pod,RESTARTS計(jì)數(shù)器已經(jīng)增加為2,因?yàn)橛袃蓚€(gè)探針
tantianran@test-b-k8s-master:~$ kubectl get pod -n test-a

NAME                          READY   STATUS    RESTARTS        AGE
goweb-demo-5d7d55f846-vm2kc   1/1     Running   2 (2m55s ago)   12m
  1. 使用startupProbe(啟動(dòng)探針)保護(hù)慢啟動(dòng)容器

有一種情景是這樣的,某些應(yīng)用在啟動(dòng)時(shí)需要較長(zhǎng)的初始化時(shí)間。要這種情況下,若要不影響對(duì)死鎖作出快速響應(yīng)的探測(cè),設(shè)置存活探測(cè)參數(shù)是要技巧的。 技巧就是使用相同的命令來(lái)設(shè)置啟動(dòng)探測(cè),針對(duì) HTTP 或 TCP 檢測(cè),可以通過(guò)將 failureThreshold * periodSeconds 參數(shù)設(shè)置為足夠長(zhǎng)的時(shí)間來(lái)應(yīng)對(duì)糟糕情況下的啟動(dòng)時(shí)間。

apiVersion: v1
kind: Namespace
metadata:
  name: test-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  replicas: 10
  selector:
    matchLabels:
      app: goweb-demo
  template:
    metadata:
      labels:
        app: goweb-demo
    spec:
      containers:
      - name: goweb-demo
        image: 192.168.11.247/web-demo/goweb-demo:20221229v3
        livenessProbe:
          httpGet:
            path: /login
            port: 8090
          failureThreshold: 1
          periodSeconds: 10
        startupProbe:
          httpGet:
            path: /login
            port: 8090
          failureThreshold: 30
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8090
  selector:
    app: goweb-demo
  type: NodePort

上面的例子,應(yīng)用程序?qū)?huì)有最多 5 分鐘(30 * 10 = 300s)的時(shí)間來(lái)完成其啟動(dòng)過(guò)程。 一旦啟動(dòng)探測(cè)成功一次,存活探測(cè)任務(wù)就會(huì)接管對(duì)容器的探測(cè),對(duì)容器死鎖作出快速響應(yīng)。 如果啟動(dòng)探測(cè)一直沒(méi)有成功,容器會(huì)在 300 秒后被殺死,并且根據(jù) restartPolicy 來(lái)執(zhí)行進(jìn)一步處置。

5. 環(huán)境變量

創(chuàng)建 Pod 時(shí),可以為其下的容器設(shè)置環(huán)境變量。通過(guò)配置文件的 env 或者 envFrom 字段來(lái)設(shè)置環(huán)境變量。

應(yīng)用場(chǎng)景:

  • 容器內(nèi)應(yīng)用程序獲取pod信息
  • 容器內(nèi)應(yīng)用程序通過(guò)用戶定義的變量改變默認(rèn)行為

變量值定義的方式:

  • 自定義變量值
  • 變量值從Pod屬性獲取
  • 變量值從Secret、ConfigMap獲取

下面來(lái)個(gè)小例子,設(shè)置自定義變量,使用env給pod里的容器設(shè)置環(huán)境變量,本例子中,設(shè)置了環(huán)境變量有SAVE_TIME、MAX_CONN、DNS_ADDR。

apiVersion: v1
kind: Pod
metadata:
  name: test-env-demo
spec:
  containers:
  - name: test-env-demo-container
    image: 192.168.11.247/web-demo/goweb-demo:20221229v3
    env:
    - name: SAVE_TIME
      value: "60"
    - name: MAX_CONN
      value: "1024"
    - name: DNS_ADDR
      value: "8.8.8.8"

開(kāi)始創(chuàng)建pod

tantianran@test-b-k8s-master:~$ kubectl create -f test-env.yaml 
pod/test-env-demo created
tantianran@test-b-k8s-master:~$ 

創(chuàng)建后,驗(yàn)證環(huán)境變量是否能獲取到

# 使用printenv打印環(huán)境變量
tantianran@test-b-k8s-master:~/goweb-demo$ kubectl exec test-env-demo -- printenv
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=test-env-demo
SAVE_TIME=60 # 這個(gè)是
MAX_CONN=1024 # 這個(gè)是
DNS_ADDR=8.8.8.8 # 這個(gè)是
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
GOLANG_VERSION=1.19.4
GOPATH=/go
HOME=/root
tantianran@test-b-k8s-master:~/goweb-demo$ 

# 進(jìn)入容器打印環(huán)境變量
tantianran@test-b-k8s-master:~/goweb-demo$ kubectl exec -it test-env-demo -c test-env-demo-container -- bash
root@test-env-demo:/opt/goweb-demo# echo $SAVE_TIME # 單獨(dú)打印一個(gè)
60
root@test-env-demo:/opt/goweb-demo# env # 執(zhí)行env命令查看
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=test-env-demo
PWD=/opt/goweb-demo
DNS_ADDR=8.8.8.8
HOME=/root
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
MAX_CONN=1024
GOLANG_VERSION=1.19.4
TERM=xterm
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
SAVE_TIME=60
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
GOPATH=/go
_=/usr/bin/env
root@test-env-demo:/opt/goweb-demo# 

再來(lái)個(gè)小例子,直接取pod的屬性作為變量的值,下面的例子是拿到pod所處的節(jié)點(diǎn)名稱

apiVersion: v1
kind: Pod
metadata:
  name: test-env-demo
spec:
  containers:
  - name: test-env-demo-container
    image: 192.168.11.247/web-demo/goweb-demo:20221229v3
    env:
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName

打印變量

tantianran@test-b-k8s-master:~$ kubectl exec test-env-demo -- printenv
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=test-env-demo
NODE_NAME=test-b-k8s-node02 # NODE_NAME變量,值為test-b-k8s-node02
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
GOLANG_VERSION=1.19.4
GOPATH=/go
HOME=/root

再來(lái)最后一個(gè)例子,使用容器字段作為環(huán)境變量的值,這個(gè)例子設(shè)置了資源限制的字段requests和limits,在設(shè)置環(huán)境變量中,使用資源限制的值作為了變量的值。

apiVersion: v1
kind: Pod
metadata:
  name: test-env-demo
spec:
  containers:
  - name: test-env-demo-container
    image: 192.168.11.247/web-demo/goweb-demo:20221229v3
    resources:
      requests:
        memory: "32Mi"
        cpu: "125m"
      limits:
        memory: "64Mi"
        cpu: "250m"
    env:
      - name: CPU_REQUEST
        valueFrom:
          resourceFieldRef:
            containerName: test-env-demo-container
            resource: requests.cpu
      - name: CPU_LIMIT
        valueFrom:
          resourceFieldRef:
            containerName: test-env-demo-container
            resource: limits.cpu
      - name: MEM_REQUEST
        valueFrom:
          resourceFieldRef:
            containerName: test-env-demo-container
            resource: requests.memory
      - name: MEM_LIMIT
        valueFrom:
          resourceFieldRef:
            containerName: test-env-demo-container
            resource: limits.memory

打印變量

tantianran@test-b-k8s-master:~$ kubectl exec test-env-demo -- printenv
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=test-env-demo
MEM_REQUEST=33554432
MEM_LIMIT=67108864
CPU_REQUEST=1
CPU_LIMIT=1
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
GOLANG_VERSION=1.19.4
GOPATH=/go
HOME=/root

6. init container(初始化容器)

初始化容器的特點(diǎn):

  • Init容器是一種特殊容器,在Pod內(nèi),會(huì)在應(yīng)用容器啟動(dòng)之前運(yùn)行。
  • 如果Pod的Init容器失敗,kubelet會(huì)不斷地重啟該Init容器,直到該容器成功為止。
  • 如果 Pod 對(duì)應(yīng)的 restartPolicy 值為 "Never",并且 Pod 的 Init 容器失敗, 則 Kubernetes 會(huì)將整個(gè) Pod 狀態(tài)設(shè)置為失敗。
  • 如果為一個(gè) Pod 指定了多個(gè) Init 容器,這些容器會(huì)按順序逐個(gè)運(yùn)行。 每個(gè) Init 容器必須運(yùn)行成功,下一個(gè)才能夠運(yùn)行。
  • Init 容器不支持探針,包括 lifecycle、livenessProbe、readinessProbe 和 startupProbe

在實(shí)際工作中,可利用它的這種工作機(jī)制應(yīng)對(duì)某些場(chǎng)景,比如在應(yīng)用容器啟動(dòng)之前,需要做一些初始化相關(guān)的工作,比如初始化配置文件,測(cè)試IP或端口的連通性等等場(chǎng)景。

下面來(lái)個(gè)小例子,場(chǎng)景是這樣的,我的應(yīng)用容器是goweb-demo,初始化容器是init-check-mysql-ip,假設(shè)應(yīng)用容器是依賴數(shù)據(jù)庫(kù)的,如果數(shù)據(jù)庫(kù)沒(méi)起來(lái),那么應(yīng)用容器就算起來(lái)了也是服務(wù)不可用。所以,現(xiàn)在的主要目的是想在應(yīng)用容器啟動(dòng)之前檢查mysql服務(wù)器的IP地址是否可ping通,如果是通的才啟動(dòng)應(yīng)用容器。這個(gè)例子應(yīng)該是比較貼近實(shí)際場(chǎng)景了。下面,看我寫好的yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: test-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  replicas: 3
  selector:
    matchLabels:
      app: goweb-demo
  template:
    metadata:
      labels:
        app: goweb-demo
    spec:
      containers:
      - name: goweb-demo
        image: 192.168.11.247/web-demo/goweb-demo:20221229v3
      initContainers:
      - name: init-check-mysql-ip
        image: 192.168.11.247/os/busybox:latest
        command: ['sh', '-c', "ping 192.168.11.248 -c 5"]
---
apiVersion: v1
kind: Service
metadata:
  name: goweb-demo
  namespace: test-a
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8090
  selector:
    app: goweb-demo
  type: NodePort

mysql服務(wù)器故意沒(méi)拉起,看看效果:

tantianran@test-b-k8s-master:~/goweb-demo$ kubectl get pods -n test-a
NAME                          READY   STATUS     RESTARTS      AGE
goweb-demo-859cc77bd5-jpcfs   0/1     Init:0/1   2 (18s ago)   48s
goweb-demo-859cc77bd5-n8hqd   0/1     Init:0/1   2 (17s ago)   48s
goweb-demo-859cc77bd5-sns67   0/1     Init:0/1   2 (17s ago)   48s
tantianran@test-b-k8s-master:~/goweb-demo$ 

tantianran@test-b-k8s-master:~/goweb-demo$ kubectl get pods -n test-a
NAME                          READY   STATUS       RESTARTS      AGE
goweb-demo-859cc77bd5-jpcfs   0/1     Init:Error   4 (67s ago)   2m44s
goweb-demo-859cc77bd5-n8hqd   0/1     Init:Error   4 (66s ago)   2m44s
goweb-demo-859cc77bd5-sns67   0/1     Init:Error   4 (67s ago)   2m44s
tantianran@test-b-k8s-master:~/goweb-demo$ 

tantianran@test-b-k8s-master:~/goweb-demo$ kubectl get pods -n test-a
NAME                          READY   STATUS                  RESTARTS      AGE
goweb-demo-859cc77bd5-jpcfs   0/1     Init:CrashLoopBackOff   3 (34s ago)   2m11s
goweb-demo-859cc77bd5-n8hqd   0/1     Init:CrashLoopBackOff   3 (33s ago)   2m11s
goweb-demo-859cc77bd5-sns67   0/1     Init:CrashLoopBackOff   3 (34s ago)   2m11s
tantianran@test-b-k8s-master:~/goweb-demo$ 

觀察STATUS字段發(fā)現(xiàn),它經(jīng)歷了3個(gè)階段,第一階段是正常的運(yùn)行,也就是執(zhí)行ping檢查的操作,因?yàn)樗阑頟ing不同,所以進(jìn)入了第二階段,狀態(tài)為Error。緊接著是第三階段,狀態(tài)變成了CrashLoopBackOff,對(duì)于這個(gè)狀態(tài),我的理解是,初始化容器運(yùn)行失敗了,準(zhǔn)備再次運(yùn)行??偠灾?,如果Mysql服務(wù)器的IP死活ping不通,它就會(huì)的狀態(tài)就會(huì)一直這樣:運(yùn)行->Error->CrashLoopBackOff。當(dāng)然這種情況是當(dāng)Pod對(duì)應(yīng)的restartPolicy為"Always"(這是默認(rèn)策略)才會(huì)這樣不斷的循環(huán)檢查,如果Pod對(duì)應(yīng)的restartPolicy值為"Never",并且Pod的 Init容器失敗,則Kubernetes會(huì)將整個(gè)Pod狀態(tài)設(shè)置為失敗。

當(dāng)我把mysql服務(wù)器啟動(dòng)后,初始化容器執(zhí)行成功,那么應(yīng)用容器也就成功起來(lái)了:

tantianran@test-b-k8s-master:~/goweb-demo$ kubectl get pods -n test-a
NAME                          READY   STATUS    RESTARTS   AGE
goweb-demo-859cc77bd5-jpcfs   1/1     Running   0          30m
goweb-demo-859cc77bd5-n8hqd   1/1     Running   0          30m
goweb-demo-859cc77bd5-sns67   1/1     Running   0          30m
tantianran@test-b-k8s-master:~/goweb-demo$ 

7. 靜態(tài)pod

在實(shí)際工作中,靜態(tài)Pod的應(yīng)用場(chǎng)景是畢竟少的,幾乎沒(méi)有。不過(guò)也還是得對(duì)它做一個(gè)簡(jiǎn)單的了解。靜態(tài)Pod在指定的節(jié)點(diǎn)上由 kubelet 守護(hù)進(jìn)程直接管理,不需要 API 服務(wù)器監(jiān)管。 與由控制面管理的 Pod(例如,Deployment) 不同;kubelet 監(jiān)視每個(gè)靜態(tài) Pod(在它失敗之后重新啟動(dòng)),靜態(tài) Pod 始終都會(huì)綁定到特定節(jié)點(diǎn)的 Kubelet上。

在每個(gè)node節(jié)點(diǎn)上,kubelet的守護(hù)進(jìn)程會(huì)自動(dòng)在/etc/kubernetes/manifests/路徑下發(fā)現(xiàn)yaml,因此,如果想要?jiǎng)?chuàng)建靜態(tài)Pod,就得把yaml放到該目錄,下面我們直接實(shí)戰(zhàn)一下。

隨便登錄到某臺(tái)node節(jié)點(diǎn),然后創(chuàng)建/etc/kubernetes/manifests/static_pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: test-static-pod
spec:
  containers:
  - name: test-container
    image: 192.168.11.247/web-demo/goweb-demo:20221229v3

創(chuàng)建后,回到master節(jié)點(diǎn)上查看pod

tantianran@test-b-k8s-master:~$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
test-static-pod-test-b-k8s-node01   1/1     Running   0          11s

通過(guò)上面的輸出結(jié)果可以看到,該靜態(tài)pod已經(jīng)在節(jié)點(diǎn)test-b-k8s-node01上面正常運(yùn)行了,說(shuō)明kubelet守護(hù)進(jìn)程已經(jīng)自動(dòng)發(fā)現(xiàn)并創(chuàng)建了它。你可能會(huì)問(wèn),它不是不需要API服務(wù)器監(jiān)管嗎?為啥在master節(jié)點(diǎn)能看到它?因?yàn)閗ubelet 會(huì)嘗試通過(guò) Kubernetes API服務(wù)器為每個(gè)靜態(tài)Pod自動(dòng)創(chuàng)建一個(gè)鏡像Pod,這意味著節(jié)點(diǎn)上運(yùn)行的靜態(tài)Pod對(duì)API服務(wù)來(lái)說(shuō)是可見(jiàn)的,但是不能通過(guò)API服務(wù)器來(lái)控制。 且Pod名稱將把以連字符開(kāi)頭的節(jié)點(diǎn)主機(jī)名作為后綴。

本文轉(zhuǎn)載于(喜歡的盆友關(guān)注我們):https://mp.weixin.qq.com/s/5Kv7DU34_Ae5y17MAQVO-Q

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容