Namespace
Linux Namespace 是 Linux 提供的一種內核級別環境隔離的方法。這種隔離機制和 chroot 很相似,chroot 是把某個目錄修改成根目錄,從而沒法訪問外部的內容。Linux Namesapce 在此基礎之上,提供了對 UTS、IPC、Mount、PID、Network、User 等的隔離機制,以下所示。web
分類 | 系統調用參數 | 相關內核版本 |
---|---|---|
Mount Namespaces | CLONE_NEWNS | Linux 2.4.19 |
UTS Namespaces | CLONE_NEWUTS | Linux 2.6.19 |
IPC Namespaces | CLONE_NEWIPC | Linux 2.6.19 |
PID Namespaces | CLONE_NEWPID | Linux 2.6.19 |
Network Namespaces | CLONE_NEWNET | 始於Linux 2.6.24 完成於 Linux 2.6.29 |
User Namespaces | CLONE_NEWUSER | 始於 Linux 2.6.23 完成於 Linux 3.8) |
★Linux Namespace 官方文檔:Namespaces in operationdocker
」
namespace 有三個系統調用可使用:shell
-
clone()
--- 實現線程的系統調用,用來建立一個新的進程,並能夠經過設計上述參數達到隔離。 -
unshare()
--- 使某個進程脫離某個 namespace -
setns(int fd, int nstype)
--- 把某進程加入到某個 namespace
下面使用這幾個系統調用來演示 Namespace 的效果,更加詳細地能夠看 DOCKER基礎技術:LINUX NAMESPACE(上)、 DOCKER基礎技術:LINUX NAMESPACE(下)。ubuntu
UTS Namespace
UTS Namespace 主要是用來隔離主機名的,也就是每一個容器都有本身的主機名。咱們使用以下的代碼來進行演示。注意:假如在容器內部沒有設置主機名的話會使用主機的主機名的;假如在容器內部設置了主機名可是沒有使用 CLONE_NEWUTS 的話那麼改變的實際上是主機的主機名。windows
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container_dawn", 15);
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main() {
printf("Parent [%5d] - start a container!\n", getpid());
int container_id = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | SIGCHLD, NULL);
waitpid(container_id, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}

PID Namespace
每一個容器都有本身的進程環境中,也就是至關於容器內進程的 PID 從 1 開始命名,此時主機上的 PID 其實也仍是從 1 開始命名的,就至關於有兩個進程環境:一個主機上的從 1 開始,另外一個容器裏的從 1 開始。centos
爲啥 PID 從 1 開始就至關於進程環境的隔離了呢?所以在傳統的 UNIX 系統中,PID 爲 1 的進程是 init,地位特殊。它做爲全部進程的父進程,有不少特權。另外,其還會檢查全部進程的狀態,咱們知道若是某個進程脫離了父進程(父進程沒有 wait 它),那麼 init 就會負責回收資源並結束這個子進程。因此要想作到進程的隔離,首先須要建立出 PID 爲 1 的進程。安全
可是,【kubernetes 裏面的話】bash
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container_dawn", 15);
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main() {
printf("Parent [%5d] - start a container!\n", getpid());
int container_id = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL);
waitpid(container_id, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}

若是此時你在子進程的 shell 中輸入 ps、top 等命令,咱們仍是能夠看到全部進程。這是由於,ps、top 這些命令是去讀 /proc 文件系統,因爲此時文件系統並無隔離,因此父進程和子進程經過命令看到的狀況都是同樣的。微信
IPC Namespace
常見的 IPC 有共享內存、信號量、消息隊列等。當使用 IPC Namespace 把 IPC 隔離起來以後,只有同一個 Namespace 下的進程才能相互通訊,由於主機的 IPC 和其餘 Namespace 中的 IPC 都是看不到了的。而這個的隔離主要是由於建立出來的 IPC 都會有一個惟一的 ID,那麼主要對這個 ID 進行隔離就行了。網絡
想要啓動 IPC 隔離,只須要在調用 clone 的時候加上 CLONE_NEWIPC 參數就能夠了。
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container_dawn", 15);
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main() {
printf("Parent [%5d] - start a container!\n", getpid());
int container_id = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWIPC | SIGCHLD, NULL);
waitpid(container_id, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}
Mount Namespace
Mount Namespace 可讓容器有本身的 root 文件系統。須要注意的是,在經過 CLONE_NEWNS 建立 mount namespace 以後,父進程會把本身的文件結構複製給子進程中。因此當子進程中不從新 mount 的話,子進程和父進程的文件系統視圖是同樣的,假如想要改變容器進程的視圖,必定須要從新 mount(這個是 mount namespace 和其餘 namespace 不一樣的地方)。
另外,子進程中新的 namespace 中的全部 mount 操做都隻影響自身的文件系統(注意這邊是 mount 操做,而建立文件等操做都是會有所影響的),而不對外界產生任何影響,這樣能夠作到比較嚴格地隔離(固然這邊是除 share mount 以外的)。
下面咱們從新掛載子進程的 /proc
目錄,從而可使用 ps 來查看容器內部的狀況。
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container_dawn", 15);
if (mount("proc", "/proc", "proc", 0, NULL) !=0 ) {
perror("proc");
}
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main() {
printf("Parent [%5d] - start a container!\n", getpid());
int container_id = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
waitpid(container_id, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}

★這裏會有個問題就是在退出子進程以後,當再次使用
ps -elf
的時候會報錯,以下所示![]()
這是由於 /proc 是 share mount,對它的操做會影響全部的 mount namespace,能夠看這裏:http://unix.stackexchange.com/questions/281844/why-does-child-with-mount-namespace-affect-parent-mounts
」
上面僅僅從新 mount 了 /proc
這個目錄,其餘的目錄仍是跟父進程同樣視圖的。通常來講,容器建立以後,容器進程須要看到的是一個獨立的隔離環境,而不是繼承宿主機的文件系統。接下來演示一個山寨鏡像,來模仿 Docker 的 Mount Namespace。也就是給子進程實現一個較爲完整的獨立的 root 文件系統,讓這個進程只能訪問本身構成的文件系統中的內容(想一想咱們日常使用 Docker 容器的樣子)。
-
首先咱們使用
docker export
將 busybox 鏡像導出成一個 rootfs 目錄,這個 rootfs 目錄的狀況如圖所示,已經包含了/proc
、/sys
等特殊的目錄。 -
以後咱們在代碼中將一些特殊目錄從新掛載,並使用
chroot()
系統調用將進程的根目錄改爲上文的rootfs
目錄。char* const container_args[] = {
"/bin/sh",
NULL
};
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
sethostname("container_dawn", 15);
if (mount("proc", "rootfs/proc", "proc", 0, NULL) != 0) {
perror("proc");
}
if (mount("sysfs", "rootfs/sys", "sysfs", 0, NULL)!=0) {
perror("sys");
}
if (mount("none", "rootfs/tmp", "tmpfs", 0, NULL)!=0) {
perror("tmp");
}
if (mount("udev", "rootfs/dev", "devtmpfs", 0, NULL)!=0) {
perror("dev");
}
if (mount("devpts", "rootfs/dev/pts", "devpts", 0, NULL)!=0) {
perror("dev/pts");
}
if (mount("shm", "rootfs/dev/shm", "tmpfs", 0, NULL)!=0) {
perror("dev/shm");
}
if (mount("tmpfs", "rootfs/run", "tmpfs", 0, NULL)!=0) {
perror("run");
}
if ( chdir("./rootfs") || chroot("./") != 0 ){
perror("chdir/chroot");
}
// 改變根目錄以後,那麼 /bin/bash 是從改變以後的根目錄中搜索了
execv(container_args[0], container_args);
perror("exec");
printf("Something's wrong!\n");
return 1;
}
int main() {
printf("Parent [%5d] - start a container!\n", getpid());
int container_id = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
waitpid(container_id, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
} -
最後,查看實現效果以下圖所示。
實際上,Mount Namespace 是基於 chroot 的不斷改良才被髮明出來的,chroot 能夠算是 Linux 中第一個 Namespace。那麼上面被掛載在容器根目錄上、用來爲容器鏡像提供隔離後執行環境的文件系統,就是所謂的容器鏡像,也被叫作 rootfs(根文件系統)。須要明確的是,rootfs 只是一個操做系統所包含的文件、配置和目錄,並不包括操做系統內核。
User Namespace
容器內部看到的 UID 和 GID 和外部是不一樣的了,好比容器內部針對 dawn 這個用戶顯示的是 0,可是實際上這個用戶在主機上應該是 1000。要實現這樣的效果,須要把容器內部的 UID 和主機的 UID 進行映射,須要修改的文件是 /proc/<pid>/uid_map
和 /proc/<pid>/gid_map
,這兩個文件的格式是
ID-INSIDE-NS ID-OUTSIDE-NS LENGTH
-
ID-INSIDE-NS :表示在容器內部顯示的 UID 或 GID -
ID-OUTSIDE-NS:表示容器外映射的真實的 UID 和 GID -
LENGTH:表示映射的範圍,通常爲 1,表示一一對應
好比,下面就是將真實的 uid=1000 的映射爲容器內的 uid =0:
$ cat /proc/8353/uid_map
0 1000 1
再好比,下面則表示把 namesapce 內部從 0 開始的 uid 映射到外部從 0 開始的 uid,其最大範圍是無符號 32 位整型(下面這條命令是在主機環境中輸入的)。
$ cat /proc/$$/uid_map
0 0 4294967295
默認狀況,設置了 CLONE_NEWUSER 參數可是沒有修改上述兩個文件的話,容器中默認狀況下顯示爲 65534,這是由於容器找不到真正的 UID,因此就設置了最大的 UID。以下面的代碼所示:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/capability.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
printf("Container: eUID = %ld; eGID = %ld, UID=%ld, GID=%ld\n",
(long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());
printf("Container [%5d] - setup hostname!\n", getpid());
//set hostname
sethostname("container",10);
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main() {
const int gid=getgid(), uid=getuid();
printf("Parent: eUID = %ld; eGID = %ld, UID=%ld, GID=%ld\n",
(long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());
printf("Parent [%5d] - start a container!\n", getpid());
int container_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWUSER | SIGCHLD, NULL);
printf("Parent [%5d] - Container [%5d]!\n", getpid(), container_pid);
printf("Parent [%5d] - user/group mapping done!\n", getpid());
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}
當我以 dawn 這個用戶執行的該程序的時候,那麼會顯示以下圖所示的效果。使用 root 用戶的時候是一樣的:


接下去,咱們要開始來實現映射的效果了,也就是讓 dawn 這個用戶在容器中顯示爲 0。代碼是幾乎徹底拿耗子叔的博客上的,連接可見文末:
int pipefd[2];
void set_map(char* file, int inside_id, int outside_id, int len) {
FILE* mapfd = fopen(file, "w");
if (NULL == mapfd) {
perror("open file error");
return;
}
fprintf(mapfd, "%d %d %d", inside_id, outside_id, len);
fclose(mapfd);
}
void set_uid_map(pid_t pid, int inside_id, int outside_id, int len) {
char file[256];
sprintf(file, "/proc/%d/uid_map", pid);
set_map(file, inside_id, outside_id, len);
}
int container_main(void* arg) {
printf("Container [%5d] - inside the container!\n", getpid());
printf("Container: eUID = %ld; eGID = %ld, UID=%ld, GID=%ld\n",
(long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());
/* 等待父進程通知後再往下執行(進程間的同步) */
char ch;
close(pipefd[1]);
read(pipefd[0], &ch, 1);
printf("Container [%5d] - setup hostname!\n", getpid());
//set hostname
sethostname("container",10);
//remount "/proc" to make sure the "top" and "ps" show container's information
mount("proc", "/proc", "proc", 0, NULL);
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main() {
const int gid=getgid(), uid=getuid();
printf("Parent: eUID = %ld; eGID = %ld, UID=%ld, GID=%ld\n",
(long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());
pipe(pipefd);
printf("Parent [%5d] - start a container!\n", getpid());
int container_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL);
printf("Parent [%5d] - Container [%5d]!\n", getpid(), container_pid);
//To map the uid/gid,
// we need edit the /proc/PID/uid_map (or /proc/PID/gid_map) in parent
set_uid_map(container_pid, 0, uid, 1);
printf("Parent [%5d] - user/group mapping done!\n", getpid());
/* 通知子進程 */
close(pipefd[1]);
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}
實現的最終效果如圖所示,能夠看到在容器內部將 dawn 這個用戶 UID 顯示爲了 0(root),但其實這個容器中的 /bin/bash 進程仍是以一個普通用戶,也就是 dawn 來運行的,只是顯示出來的 UID 是 0,因此當查看 /root 目錄的時候仍是沒有權限。

User Namespace 是以普通用戶運行的,可是別的 Namespace 須要 root 權限,那麼當使用多個 Namespace 該怎麼辦呢?咱們能夠先用通常用戶建立 User Namespace,而後把這個通常用戶映射成 root,那麼在容器內用 root 來建立其餘的 Namespace。
Network Namespace
隔離容器中的網絡,每一個容器都有本身的虛擬網絡接口和 IP 地址。在 Linux 中,可使用 ip 命令建立 Network Namespace(Docker 的源碼中,它沒有使用 ip 命令,而是本身實現了 ip 命令內的一些功能)。
下面就使用 ip 命令來說解一下 Network Namespace 的構建,以 bridge 網絡爲例。bridge 網絡的拓撲圖通常以下圖所示,其中 br0 是 Linux 網橋。

在使用 Docker 的時候,若是啓動一個 Docker 容器,並使用 ip link show 查看當前宿主機上的網絡狀況,那麼你會看到有一個 docker0 還有一個 veth****
的虛擬網卡,這個 veth 的虛擬網卡就是上圖中 veth,而 docker0 就至關於上圖中的 br0。
那麼,咱們可使用下面這些命令便可建立跟 docker 相似的效果(參考自耗子叔的博客,連接見文末參考,結合上圖加了一些文字)。
## 1. 首先,咱們先增長一個網橋 lxcbr0,模仿 docker0
brctl addbr lxcbr0
brctl stp lxcbr0 off
ifconfig lxcbr0 192.168.10.1/24 up #爲網橋設置IP地址
## 2. 接下來,咱們要建立一個 network namespace ,命名爲 ns1
# 增長一個 namesapce 命令爲 ns1 (使用 ip netns add 命令)
ip netns add ns1
# 激活 namespace 中的 loopback,即127.0.0.1(使用 ip netns exec ns1 至關於進入了 ns1 這個 namespace,那麼 ip link set dev lo up 至關於在 ns1 中執行的)
ip netns exec ns1 ip link set dev lo up
## 3. 而後,咱們須要增長一對虛擬網卡
# 增長一對虛擬網卡,注意其中的 veth 類型。這裏有兩個虛擬網卡:veth-ns1 和 lxcbr0.1,veth-ns1 網卡是要被安到容器中的,而 lxcbr0.1 則是要被安到網橋 lxcbr0 中的,也就是上圖中的 veth。
ip link add veth-ns1 type veth peer name lxcbr0.1
# 把 veth-ns1 按到 namespace ns1 中,這樣容器中就會有一個新的網卡了
ip link set veth-ns1 netns ns1
# 把容器裏的 veth-ns1 更名爲 eth0 (容器外會衝突,容器內就不會了)
ip netns exec ns1 ip link set dev veth-ns1 name eth0
# 爲容器中的網卡分配一個 IP 地址,並激活它
ip netns exec ns1 ifconfig eth0 192.168.10.11/24 up
# 上面咱們把 veth-ns1 這個網卡按到了容器中,而後咱們要把 lxcbr0.1 添加上網橋上
brctl addif lxcbr0 lxcbr0.1
# 爲容器增長一個路由規則,讓容器能夠訪問外面的網絡
ip netns exec ns1 ip route add default via 192.168.10.1
## 4. 爲這個 namespace 設置 resolv.conf,這樣,容器內就能夠訪問域名了
echo "nameserver 8.8.8.8" > conf/resolv.conf
上面基本上就至關於 docker 網絡的原理,只不過:
-
Docker 不使用 ip 命令而是,本身實現了 ip 命令內的一些功能。 -
Docker 的 resolv.conf 沒有使用這樣的方式,而是將其寫到指定的 resolv.conf 中,以後在啓動容器的時候將其和 hostname、host 一塊兒以只讀的方式加載到容器的文件系統中。 -
docker 使用進程的 PID 來作 network namespace 的名稱。
同理,咱們還可使用以下的方式爲正在運行的 docker 容器增長一個新的網卡
ip link add peerA type veth peer name peerB
brctl addif docker0 peerA
ip link set peerA up
ip link set peerB netns ${container-pid}
ip netns exec ${container-pid} ip link set dev peerB name eth1
ip netns exec ${container-pid} ip link set eth1 up
ip netns exec ${container-pid} ip addr add ${ROUTEABLE_IP} dev eth1
Namespace 狀況查看
Cgroup 的操做接口是文件系統,位於 /sys/fs/cgroup
中。假如想查看 namespace 的狀況一樣能夠查看文件系統,namespace 主要查看 /proc/<pid>/ns
目錄。
咱們以上面的 [PID Namespace 程序](#PID Namespace) 爲例,當這個程序運行起來以後,咱們能夠看到其 PID 爲 11702。

以後,咱們保持這個子進程運行,而後打開另外一個 shell,查看這個程序建立的子進程的 PID,也就是容器中運行的進程在主機中的 PID。
最後,咱們分別查看 /proc/11702/ns
和 /proc/11703/ns
這兩個目錄的狀況,也就是查看這兩個進程的 namespace 狀況。能夠看到其中 cgroup、ipc、mnt、net、user 都是同一個 ID,而 pid、uts 是不一樣的 ID。若是兩個進程的 namespace 編號相同,那麼表示這兩個進程位於同一個 namespace 中,不然位於不一樣 namespace 中。

若是能夠查看 ns 的狀況以外,這些文件一旦被打開,只要 fd 被佔用着,即便 namespace 中全部進程都已經結束了,那麼建立的 namespace 也會一直存在。好比可使用 mount --bind /proc/11703/ns/uts ~/uts
,讓 11703 這個進程的 UTS Namespace 一直存在。
總結
Namespace 技術實際上修改了應用進程看待整個計算機「視圖」,即它的」視圖「已經被操做系統作了限制,只能」看到「某些指定的內容,這僅僅對應用進程產生了影響。可是對宿主機來講,這些被隔離了的進程,其實仍是進程,跟宿主機上其餘進程並沒有太大區別,都由宿主機統一管理。只不過這些被隔離的進程擁有額外設置過的 Namespace 參數。那麼 Docker 項目在這裏扮演的,更可能是旁路式的輔助和管理工做。以下左圖所示

所以,相比虛擬機的方式,容器會更受歡迎。這是假如使用虛擬機的方式做爲應用沙盒,那麼必需要由 Hypervisor 來負責建立虛擬機,這個虛擬機是真實存在的,而且裏面必需要運行一個完整的 Guest OS 才能執行用戶的應用進程。這樣就致使了採用虛擬機的方式以後,不可避免地帶來額外的資源消耗和佔用。根據實驗,一個運行着 CentOS 的 KVM 虛擬機啓動後,在不作優化的狀況下,虛擬機就須要佔用 100-200 MB 內存。此外,用戶應用運行在虛擬機中,它對宿主機操做系統的調用就不可避免地要通過虛擬機軟件的攔截和處理,這自己就是一層消耗,尤爲對資源、網絡和磁盤 IO 的損耗很是大。
而假如使用容器的方式,容器化以後應用本質仍是宿主機上的一個進程,這也就意味着由於虛擬機化帶來的性能損耗是不存在的;而另外一方面使用 Namespace 做爲隔離手段的容器並不須要單獨的 Guest OS,這就使得容器額外的資源佔用幾乎能夠忽略不計。
總得來講,「敏捷」和「高性能」是容器相對於虛擬機最大的優點,也就是容器能在 PaaS 這種更加細粒度的資源管理平臺上大行其道的重要緣由。
可是!基於 Linux Namespace 的隔離機制相比於虛擬化技術也有不少不足之處,其中最主要的問題就是隔離不完全。
-
首先,容器只是運行在宿主機上的一種特殊進程,那麼容器之間使用的仍是同一個宿主機上的操做系統。儘管能夠在容器裏面經過 mount namesapce 單獨掛載其餘不一樣版本的操做系統文件,好比 centos、ubuntu,可是這並不能改變共享宿主機內核的事實。這就意味着你要在 windows 上運行 Linux 容器,或者在低版本的 Linux 宿主機上運行高版本的 Linux 容器都是行不通的。
而擁有虛擬機技術和獨立 Guest OS 的虛擬機就要方便多了。
-
其次,在 Linux 內核中,有不少資源和對象都是不能被 namespace 化的,好比時間。假如你的容器中的程序使用
settimeofday(2)
系統調用修改了時間,整個宿主機的時間都會被隨之修改。相比虛擬機裏面能夠隨意折騰的自由度,在容器裏部署應用的時候,「什麼能作,什麼不能作」 是用戶必須考慮的一個問題。以外,容器給應用暴露出來的攻擊面是至關大的,應用「越獄」的難度也比虛擬機低不少。雖然,實踐中可使用 Seccomp 等技術對容器內部發起的全部系統調用進行過濾和甄別來進行安全加固,但這種方式由於多了一層對系統調用的過濾,也會對容器的性能產生影響。所以,在生產環境中沒有人敢把運行在物理機上的 Linux 容器直接暴露到公網上。
另外,容器是一個「單進程」模型。容器的本質是一個進程,用戶的應用進程實際上就是容器裏 PID=1 的進程,而這個進程也是後續建立的全部進程的父進程。這也就意味着,在一個容器中,你沒辦法同時運行兩個不一樣的應用,除非能事先找到一個公共的 PID=1 的程序來充當二者的父進程,好比使用 systemd 或者 supervisord。容器的設計更可能是但願容器和應用同生命週期的,而不是容器還在運行,而裏面的應用早已經掛了。
★上面這段話我的的理解是:由於建立出子進程以後,子進程須要運行的,而此時父進程須要等待子進程運行結束,至關於只有子進程在運行。好比容器中的第一個進程每每就是業務須要的進程,也就是 entrypoint 指定的程序運行起來的進程。而建立出子進程會致使這個進程被暫停。即便將子進程改成後臺執行,可是因爲容器中 PID=1 的進程壓根沒有管理後臺進程的能力,因此仍是會有進程沒法管理。
」
巨人的肩膀
極客時間---《深刻剖析 Kubernetes》---張磊老師
DOCKER基礎技術:LINUX NAMESPACE(上)
DOCKER基礎技術:LINUX NAMESPACE(下)。
本文分享自微信公衆號 - 多選參數(zhouxintalk)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。