大家在使用Docker容器或者Kubernetes時,遇到過這個容器麼?gcr.io/google_containers/pause-amd64
docker ps的指令傳回的結果:
[root@k8s-minion1 kubernetes]# docker ps |grep pause
c3026adee957 gcr.io/google_containers/pause-amd64:3.0 "/pause" 22 minutes ago Up 22 minutes k8s_POD.d8dbe16c_redis-master-343230949-04glm_default_ce3f60a9-095d-11e7-914b-0a77ecd65f3e_66c108d5
202df18d636e gcr.io/google_containers/pause-amd64:3.0 "/pause" 24 hours ago Up 24 hours k8s_POD.d8dbe16c_kube-proxy-js0z0_kube-system_2866cfc2-0891-11e7-914b-0a77ecd65f3e_c8e1a667
072d3414d33a gcr.io/google_containers/pause-amd64:3.0 "/pause" 24 hours ago Up 24 hours k8s_POD.d8dbe16c_kube-flannel-ds-tsps5_default_2866e3fb-0891-11e7-914b-0a77ecd65f3e_be4b719e
[root@k8s-minion1 kubernetes]#
Kubernetes的官網解釋:
it's part of the infrastructure. This container is started first in all Pods to setup the network for the Pod.
意思是:pause-amd64是Kubernetes基礎設施的一部分,Kubernetes管理的所有pod裡,pause-amd64容器是第一個啟動的,用于實作Kubernetes叢集裡pod之間的網絡通訊。
對這個特殊容器感興趣的朋友,可以閱讀其源代碼:
https://github.com/kubernetes/kubernetes/tree/master/build/pause我們檢視這個pause-amd64鏡像的dockerfile,發現實作很簡單,基于一個空白鏡像開始:
FROM scratch
ARG ARCH
ADD bin/pause-${ARCH} /pause
ENTRYPOINT ["/pause"]
ARG指令用于指定在執行docker build指令時傳遞進去的參數。
這個pause container是用C語言寫的:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwQjMx8CX39CXy8CXycXZpZVZnFWbpN0NlAXayR3cvwFduVWay9WLvRXdh9CXyI3Zv1UZnFWbp9zZuBnLjdzMxMzYhhzMiVjYmVWZj1SM5cTN4AjMvw1cldWYtl2XkF2bsBXdvw1bp5SdoNnbhlmauMXZnFWbp1CZh9GbwV3Lc9CX6MHc0RHaiojIsJye.png)
在運作的Kubernetes node上運作docker ps,能發現這些pause container:
pause container作為pod裡其他所有container的parent container,主要有兩個職責:
- 是pod裡其他容器共享Linux namespace的基礎
- 扮演PID 1的角色,負責處理僵屍程序
這兩點我會逐一細說。在Linux裡,當父程序fork一個新程序時,子程序會從父程序繼承namespace。目前Linux實作了六種類型的namespace,每一個namespace是包裝了一些全局系統資源的抽象集合,這一抽象集合使得在程序的命名空間中可以看到全局系統資源。命名空間的一個總體目标是支援輕量級虛拟化工具container的實作,container機制本身對外提供一組程序,這組程序自己會認為它們就是系統唯一存在的程序。
在Linux裡,父程序fork的子程序會繼承父程序的命名空間。與這種行為相反的一個系統指令就是unshare:
再來聊聊pause容器如何處理僵屍程序的。
Pause容器内其實就運作了一個非常簡單的程序,其邏輯可以從前面提到的Pause github倉庫上找到:
static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal");
exit(0);
}
static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main() {
if (getpid() != 1)
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, "Warning: pause should be the first process\n");
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
.sa_flags = SA_NOCLDSTOP},
NULL) < 0)
return 3;
for (;;)
pause();
fprintf(stderr, "Error: infinite loop terminated\n");
return 42;
}
這個c語言實作的程序,核心代碼就28行:
其中第24行裡一個無限循環for(;;), 至此大家能看出來pause容器名稱的由來了吧?
這個無限循環裡執行的是一個系統調用pause,
是以pause容器大部分時間都在沉睡,等待有信号将其喚醒。
接收什麼信号呢?
一旦收到SIGCHLD信号,pause程序就執行注冊的sigreap函數。
看下SIGCHLD信号的幫助:
SIGCHLD,在一個程序正常終止或者停止時,将SIGCHLD信号發送給其父程序,按系統預設将忽略此信号,如果父程序希望被告知其子系統的這種狀态,則應捕捉此信号。
pause程序注冊的信号處理函數sigreap裡,調用另一個系統調用waitpid來獲得子程序終止的原因。
希望這篇文章對大家了解Kubernetes裡的pause容器有所幫助。感謝閱讀。
本文來自雲栖社群合作夥伴“汪子熙”,了解相關資訊可以關注微信公衆号"汪子熙"。