自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

讀讀 Pause 容器源碼

開發(fā) 開發(fā)工具
都知道 k8s 的調(diào)度最小單位是 POD, 并且每個 POD 都有一個所謂的 Infra 容器 Pause, 負(fù)責(zé)初始化相關(guān) namespace, 先于 POD 內(nèi)其它容器起動。那么到底什么是 Pause 容器呢?長什么樣?有什么作用?

[[397735]]

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

都知道 k8s 的調(diào)度最小單位是 POD, 并且每個 POD 都有一個所謂的 Infra 容器 Pause, 負(fù)責(zé)初始化相關(guān) namespace, 先于 POD 內(nèi)其它容器起動。那么到底什么是 Pause 容器呢?長什么樣?有什么作用?

分析源碼

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

  1. #include <signal.h> 
  2. #include <stdio.h> 
  3. #include <stdlib.h> 
  4. #include <string.h> 
  5. #include <sys/types.h> 
  6. #include <sys/wait.h> 
  7. #include <unistd.h> 
  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 信號,調(diào)用 waitpid, 回收退出進(jìn)程
  2. 主進(jìn)程 for 循環(huán)調(diào)用 pause() 函數(shù),使進(jìn)程進(jìn)入休眠狀態(tài),直到被終止或是收到信號

可疑的 waitpid

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

  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 <sys/wait.h> 

在 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 等待進(jìn)程的狀態(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 <sys/wait.h> 
  2. #include <stdlib.h> 
  3. #include <unistd.h> 
  4. #include <stdio.h> 
  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.    } 

子進(jìn)程一直處于 pause 狀態(tài),而父進(jìn)程則調(diào)用 waitpid 等待子進(jìn)程狀態(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 

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

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

共享哪些 NS

一般提起 POD 就知道,同一個 POD 內(nèi)的容器如果互相訪問,只需調(diào)用 localhost 即可。如果把 k8s 集群想象成分布式操作系統(tǒng),那么 POD 就是進(jìn)程組的概念,一定要共享某些東西的,那么默認(rèn)共享哪些 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 內(nèi)共享 PID namespace, 1.18 之后默認(rèn)是 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 內(nèi)所有進(jìn)程,并且只有 pause 容器是 init 1 進(jìn)程。

  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 信號,子進(jìn)程重啟。

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

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

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

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

在宿主機查看 nginx, sh 的進(jìn)程 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 進(jìn)程 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/,

 

責(zé)任編輯:武曉燕 來源: 董澤潤的技術(shù)筆記
相關(guān)推薦

2023-09-21 07:24:52

2023-06-14 08:49:22

PodKubernetes

2020-02-27 21:24:31

JavaAIOBIO

2017-03-06 17:02:21

戴爾

2017-01-16 11:56:21

戴爾

2023-09-22 08:39:00

sleep函數(shù)語言

2025-03-14 10:37:24

SpringSpring IOC容器

2020-05-09 14:58:35

Intel PAUSMySQL性能

2011-07-13 15:07:48

STLC++

2022-09-06 10:18:39

微型容器鏡像微服務(wù)

2011-07-13 14:49:31

STLC++

2024-03-20 10:46:00

云原生容器

2015-03-31 18:26:43

陌陌社交

2022-07-18 11:13:07

容器安全Docker

2015-05-05 14:16:22

容器技術(shù)CoreOSGoogle

2024-10-15 09:10:41

Docker容器iOS

2022-07-25 14:24:53

Docker容器安全

2022-09-13 09:09:37

容器容器云容器化

2020-12-21 09:44:06

Google Transforme技術(shù)

2017-11-28 09:21:53

LinuxWindowsDocker
點贊
收藏

51CTO技術(shù)棧公眾號