日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
讀讀Pause容器源碼

本文轉載自微信公眾號「董澤潤的技術筆記」,作者董澤潤。轉載本文請聯(lián)系董澤潤的技術筆記公眾號。

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供鐵山網(wǎng)站建設、鐵山做網(wǎng)站、鐵山網(wǎng)站設計、鐵山網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、鐵山企業(yè)網(wǎng)站模板建站服務,十載鐵山做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。

都知道 k8s 的調度最小單位是 POD, 并且每個 POD 都有一個所謂的 Infra 容器 Pause, 負責初始化相關 namespace, 先于 POD 內其它容器起動。那么到底什么是 Pause 容器呢?長什么樣?有什么作用?

分析源碼

廢話不多,直接上源碼,來自官方 pause.c[1]

 
 
 
 
  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7. #include  
  8.  
  9. #define STRINGIFY(x) #x 
  10. #define VERSION_STRING(x) STRINGIFY(x) 
  11.  
  12. #ifndef VERSION 
  13. #define VERSION HEAD 
  14. #endif 
  15.  
  16. static void sigdown(int signo) { 
  17.   psignal(signo, "Shutting down, got signal"); 
  18.   exit(0); 
  19.  
  20. static void sigreap(int signo) { 
  21.   while (waitpid(-1, NULL, WNOHANG) > 0) 
  22.     ; 
  23.  
  24. int main(int argc, char **argv) { 
  25.   int i; 
  26.   for (i = 1; i < argc; ++i) { 
  27.     if (!strcasecmp(argv[i], "-v")) { 
  28.       printf("pause.c %s\n", VERSION_STRING(VERSION)); 
  29.       return 0; 
  30.     } 
  31.   } 
  32.  
  33.   if (getpid() != 1) 
  34.     /* Not an error because pause sees use outside of infra containers. */ 
  35.     fprintf(stderr, "Warning: pause should be the first process\n"); 
  36.  
  37.   if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0) 
  38.     return 1; 
  39.   if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0) 
  40.     return 2; 
  41.   if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap, 
  42.                                              .sa_flags = SA_NOCLDSTOP}, 
  43.                 NULL) < 0) 
  44.     return 3; 
  45.  
  46.   for (;;) 
  47.     pause(); 
  48.   fprintf(stderr, "Error: infinite loop terminated\n"); 
  49.   return 42; 

可以看到 Pause 容器做如下兩件事情:

  1. 注冊各種信號處理函數(shù),主要處理兩類信息:退出信號和 child 信號。收到 SIGINT 或是 SIGTERM 后,直接退出。收到 SIGCHLD 信號,調用 waitpid, 回收退出進程
  2. 主進程 for 循環(huán)調用 pause() 函數(shù),使進程進入休眠狀態(tài),直到被終止或是收到信號

可疑的 waitpid

還是 c 的基礎不夠扎實,一直以為 waitpid 是父進程等待回收退出的子進程,但是真的這樣嘛?

 
 
 
 
  1. zerun.dong$ man waitpid 
  2. WAIT(2)                     BSD System Calls Manual                    WAIT(2) 
  3.  
  4. NAME 
  5.      wait, wait3, wait4, waitpid -- wait for process termination 
  6.  
  7. SYNOPSIS 
  8.      #include  

在 mac 上查看 man 手冊,wait for process termination 也確實這么寫的。登到 ubuntu 18.04 查看一下

 
 
 
 
  1. :~# man waitpid 
  2. WAIT(2)                                                      Linux Programmer's Manual                                                      WAIT(2) 
  3.  
  4. NAME 
  5.        wait, waitpid, waitid - wait for process to change state 

對于 linux man 手冊,就變成了 wait for process to change state 等待進程的狀態(tài)變更!!!

 
 
 
 
  1. All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose 
  2. state has changed.  A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by 
  3. a  signal.   In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a 
  4. wait is not performed, then the terminated child remains in a "zombie" state (see NOTES below). 

并且還很貼心的提供了測試代碼

 
 
 
 
  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5.  
  6. int main(int argc, char *argv[]) 
  7.    pid_t cpid, w; 
  8.    int wstatus; 
  9.  
  10.    cpid = fork(); 
  11.    if (cpid == -1) { 
  12.        perror("fork"); 
  13.        exit(EXIT_FAILURE); 
  14.    } 
  15.  
  16.    if (cpid == 0) {            /* Code executed by child */ 
  17.        printf("Child PID is %ld\n", (long) getpid()); 
  18.        if (argc == 1) 
  19.            pause();                    /* Wait for signals */ 
  20.        _exit(atoi(argv[1])); 
  21.  
  22.    } else {                    /* Code executed by parent */ 
  23.        do { 
  24.            w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED); 
  25.            if (w == -1) { 
  26.                perror("waitpid"); 
  27.                exit(EXIT_FAILURE); 
  28.            } 
  29.  
  30.            if (WIFEXITED(wstatus)) { 
  31.                printf("exited, status=%d\n", WEXITSTATUS(wstatus)); 
  32.            } else if (WIFSIGNALED(wstatus)) { 
  33.                printf("killed by signal %d\n", WTERMSIG(wstatus)); 
  34.            } else if (WIFSTOPPED(wstatus)) { 
  35.                printf("stopped by signal %d\n", WSTOPSIG(wstatus)); 
  36.            } else if (WIFCONTINUED(wstatus)) { 
  37.                printf("continued\n"); 
  38.            } 
  39.        } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); 
  40.        exit(EXIT_SUCCESS); 
  41.    } 

子進程一直處于 pause 狀態(tài),而父進程則調用 waitpid 等待子進程狀態(tài)變更。讓我們開啟一個 session 運行代碼,另外一個 session 發(fā)送信號

 
 
 
 
  1. ~$ ./a.out 
  2. Child PID is 70718 
  3. stopped by signal 19 
  4.  
  5. continued 
  6. stopped by signal 19 
  7. continued 
  8. ^C 
  9. ~# ps aux | grep a.out 
  10. zerun.d+   70717  0.0  0.0   4512   744 pts/0    S+   06:48   0:00 ./a.out 
  11. zerun.d+   70718  0.0  0.0   4512    72 pts/0    S+   06:48   0:00 ./a.out 
  12. root       71155  0.0  0.0  16152  1060 pts/1    S+   06:49   0:00 grep --color=auto a.out 
  13. ~# 
  14. ~# kill -STOP 70718 
  15. ~# 
  16. ~# ps aux | grep a.out 
  17. zerun.d+   70717  0.0  0.0   4512   744 pts/0    S+   06:48   0:00 ./a.out 
  18. zerun.d+   70718  0.0  0.0   4512    72 pts/0    T+   06:48   0:00 ./a.out 
  19. root       71173  0.0  0.0  16152  1060 pts/1    S+   06:49   0:00 grep --color=auto a.out 
  20. ~# 
  21. ~# kill -CONT 70718 
  22. ~# 
  23. ~# ps aux | grep a.out 
  24. zerun.d+   70717  0.0  0.0   4512   744 pts/0    S+   06:48   0:00 ./a.out 
  25. zerun.d+   70718  0.0  0.0   4512    72 pts/0    S+   06:48   0:00 ./a.out 
  26. root       71296  0.0  0.0  16152  1056 pts/1    R+   06:49   0:00 grep --color=auto a.out 

通過向子進程發(fā)送信號 STOP CONT 來控制進程。

看來不同操作系統(tǒng),同名 c 函數(shù)行為是不太一樣的。大驚小怪,就是菜:(

共享哪些 NS

一般提起 POD 就知道,同一個 POD 內的容器如果互相訪問,只需調用 localhost 即可。如果把 k8s 集群想象成分布式操作系統(tǒng),那么 POD 就是進程組的概念,一定要共享某些東西的,那么默認共享哪些 namespace 呢?

使用 minikube 搭建環(huán)境,先看一下 POD 定義文件

 
 
 
 
  1. apiVersion: v1 
  2. kind: Pod 
  3. metadata: 
  4.   name: nginx 
  5. spec: 
  6.   shareProcessNamespace: true 
  7.   containers: 
  8.   - name: nginx 
  9.     image: nginx 
  10.   - name: shell 
  11.     image: busybox 
  12.     securityContext: 
  13.       capabilities: 
  14.         add: 
  15.         - SYS_PTRACE 
  16.     stdin: true 
  17.     tty: true 

從 1.17 開始有參數(shù) shareProcessNamespace 用來控制是否 POD 內共享 PID namespace, 1.18 之后默認是 false 的,如果有需求需要填寫該字段。

 
 
 
 
  1. ~$ kubectl attach -it nginx -c shell 
  2. If you don't see a command prompt, try pressing enter. 
  3. / # ps aux 
  4. PID   USER     TIME  COMMAND 
  5.     1 root      0:00 /pause 
  6.     8 root      0:00 nginx: master process nginx -g daemon off; 
  7.    41 101       0:00 nginx: worker process 
  8.    42 root      0:00 sh 
  9.    49 root      0:00 ps aux 

attach 到 shell 容器中,可以看到該 POD 內所有進程,并且只有 pause 容器是 init 1 進程。

 
 
 
 
  1. / # kill -HUP 8 
  2. / # ps aux 
  3. PID   USER     TIME  COMMAND 
  4.     1 root      0:00 /pause 
  5.     8 root      0:00 nginx: master process nginx -g daemon off; 
  6.    42 root      0:00 sh 
  7.    50 101       0:00 nginx: worker process 
  8.    51 root      0:00 ps aux 

測試給 nginx master 發(fā)送 HUP 信號,子進程重啟。

如果不共享 PID ns, 那么每個容器內的進程 pid 都是 init 1 進程。共享 PID ns 有什么影響呢?參考這篇文章[2]

容器進程不再具有 PID 1。在沒有 PID 1 的情況下,一些容器鏡像拒絕啟動(例如,使用 systemd 的容器),或者拒絕執(zhí)行 kill -HUP 1 之類的命令來通知容器進程。在具有共享進程命名空間的 pod 中,kill -HUP 1 將通知 pod 沙箱(在上面的例子中是 /pause)。

進程對 pod 中的其他容器可見。這包括 /proc 中可見的所有信息,例如作為參數(shù)或環(huán)境變量傳遞的密碼。這些僅受常規(guī) Unix 權限的保護。

容器文件系統(tǒng)通過 /proc/$pid/root 鏈接對 pod 中的其他容器可見。這使調試更加容易,但也意味著文件系統(tǒng)安全性只受文件系統(tǒng)權限的保護。

在宿主機查看 nginx, sh 的進程 id, 通過 /proc/pid/ns 查看 namespace id

 
 
 
 
  1. ~# ls -l /proc/140756/ns 
  2. total 0 
  3. lrwxrwxrwx 1 root root 0 May  6 09:08 cgroup -> 'cgroup:[4026531835]' 
  4. lrwxrwxrwx 1 root root 0 May  6 09:08 ipc -> 'ipc:[4026532497]' 
  5. lrwxrwxrwx 1 root root 0 May  6 09:08 mnt -> 'mnt:[4026532561]' 
  6. lrwxrwxrwx 1 root root 0 May  6 09:08 net -> 'net:[4026532500]' 
  7. lrwxrwxrwx 1 root root 0 May  6 09:08 pid -> 'pid:[4026532498]' 
  8. lrwxrwxrwx 1 root root 0 May  6 09:08 pid_for_children -> 'pid:[4026532498]' 
  9. lrwxrwxrwx 1 root root 0 May  6 09:08 user -> 'user:[4026531837]' 
  10. lrwxrwxrwx 1 root root 0 May  6 09:08 uts -> 'uts:[4026532562]' 
  11. ~# ls -l /proc/140879/ns 
  12. total 0 
  13. lrwxrwxrwx 1 root root 0 May  6 09:08 cgroup -> 'cgroup:[4026531835]' 
  14. lrwxrwxrwx 1 root root 0 May  6 09:08 ipc -> 'ipc:[4026532497]' 
  15. lrwxrwxrwx 1 root root 0 May  6 09:08 mnt -> 'mnt:[4026532563]' 
  16. lrwxrwxrwx 1 root root 0 May  6 09:08 net -> 'net:[4026532500]' 
  17. lrwxrwxrwx 1 root root 0 May  6 09:08 pid -> 'pid:[4026532498]' 
  18. lrwxrwxrwx 1 root root 0 May  6 09:08 pid_for_children -> 'pid:[4026532498]' 
  19. lrwxrwxrwx 1 root root 0 May  6 09:08 user -> 'user:[4026531837]' 
  20. lrwxrwxrwx 1 root root 0 May  6 09:08 uts -> 'uts:[4026532564]' 

可以看到這里共享了 cgroup, ipc, net, pid, user. 這里僅限于測試案例。

殺掉 Pause 容器

測試一下殺掉 Pause 容器的話,k8s 是如何處理 POD. 使用 minikube 搭建環(huán)境,先看一下 POD 定義文件

 
 
 
 
  1. apiVersion: v1 
  2. kind: Pod 
  3. metadata: 
  4.   name: nginx 
  5. spec: 
  6.   shareProcessNamespace: false 
  7.   containers: 
  8.   - name: nginx 
  9.     image: nginx 
  10.   - name: shell 
  11.     image: busybox 
  12.     securityContext: 
  13.       capabilities: 
  14.         add: 
  15.         - SYS_PTRACE 
  16.     stdin: true 
  17.     tty: true 

啟動后,查看 pause 進程 id, 然后殺掉

 
 
 
 
  1. ~$ kubectl describe pod nginx 
  2. ...... 
  3. Events: 
  4.   Type    Reason          Age                   From     Message 
  5.   ----    ------          ----                  ----     ------- 
  6.   Normal  SandboxChanged  3m1s (x2 over 155m)   kubelet  Pod sandbox changed, it will be killed and re-created. 
  7.   Normal  Killing         3m1s (x2 over 155m)   kubelet  Stopping container nginx 
  8.   Normal  Killing         3m1s (x2 over 155m)   kubelet  Stopping container shell 
  9.   Normal  Pulling         2m31s (x3 over 156m)  kubelet  Pulling image "nginx" 
  10.   Normal  Pulling         2m28s (x3 over 156m)  kubelet  Pulling image "busybox" 
  11.   Normal  Created         2m28s (x3 over 156m)  kubelet  Created container nginx 
  12.   Normal  Started         2m28s (x3 over 156m)  kubelet  Started container nginx 
  13.   Normal  Pulled          2m28s                 kubelet  Successfully pulled image "nginx" in 2.796081224s 
  14.   Normal  Created         2m25s (x3 over 156m)  kubelet  Created container shell 
  15.   Normal  Started         2m25s (x3 over 156m)  kubelet  Started container shell 
  16.   Normal  Pulled          2m25s                 kubelet  Successfully pulled image "busybox" in 2.856292466s 

k8s 檢測到 pause 容器狀態(tài)異常,就會重啟該 POD, 其實也不難理解,無論是否共享 PID namespace, infra 容器退出了,POD 必然要重啟,畢竟生命周期是與 infra 容器一致的。

參考資料

[1]pause.c: https://github.com/kubernetes/kubernetes/blob/master/build/pause/linux/pause.c,

[2]share proceess namespace: https://kubernetes.io/zh/docs/tasks/configure-pod-container/share-process-namespace/,


文章題目:讀讀Pause容器源碼
本文URL:http://www.dlmjj.cn/article/cdpihpg.html