Docker NameSpace 对 进程 ID、主机名、用户 ID、文件名、网络和进程间通信等资源隔离

网友投稿 870 2022-11-30

Docker NameSpace 对 进程 ID、主机名、用户 ID、文件名、网络和进程间通信等资源隔离

Docker NameSpace 对 进程 ID、主机名、用户 ID、文件名、网络和进程间通信等资源隔离

namespace是用来隔离应用进程的,给应用进程一个独立的运行环境,让彼此之间不受到干扰。

初识容器

cat Dockerfile

FROM ubuntuENV MY_SERVICE_PORT=80ADD bin/amd64// //将一个二进制文件加入到这个容器镜像里面ENTRYPOINT / //容器启动要运行哪条命令

将Dockerfile打包成镜像

docker build -t cncamp/.docker push cncamp/run -d cncamp/inspect

进入容器:

docker attachdocker exec

通过 nsenter:(所谓nsenter是要进入到哪种namespace下面,去看其资源)

查看容器对应宿主机上面的pid,容器技术的实质是进程,并没有完整的操作系统,就相当于在主机上面fork了一个子进程,通过docker daemon去fork一个子进程,这个子进程是可以在主机上面看到其pid的。

PID=$(docker inspect --format "{{ .State.Pid }}" )$ nsenter --target $PID --mount --uts --ipc --net --pid#查看容器对应宿主机上面的pid,容器技术的实质是进程,并没有完整的操作系统,就相当于在主机上面fork了一个子进程,通过docker daemon去fork一个子进程,这个子进程是可以在主机上面看到其pid的。[root@docker ~]# docker inspect 37d084d8e21b | grep -i pid "Pid": 18496, "PidMode": "", "PidsLimit": null,[root@docker ~]# ps -ef | grep 18496root 18496 18476 0 15:34 pts/0 00:00:00 sh

-p 是pid namesapce -n是network namespace,在主机上面通过nsenter去敲ip a,ps命令和在容器内部敲的命令返回的结果是一样的。

[root@docker ~]# nsenter -t 18496 -n ip a1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever44: eth0@if45: mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever[root@docker ~]# nsenter -t 18496 -p ps PID TTY TIME CMD18648 pts/1 00:00:00 sh20436 pts/1 00:00:00 bash21368 pts/1 00:00:00 nsenter21369 pts/1 00:00:00 bash21529 pts/1 00:00:00 nsenter21530 pts/1 00:00:00 ps

容器看到的是可以通过在主机上面nsenter看到的。这是日常去调试容器里面进程应用的一个法宝!

[root@docker ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES4461cb8f8be5 nginx "/docker-entrypoint.?? 59 minutes ago Up About a minute 80/tcp pedantic_ganguly[root@docker ~]# docker inspect 4461cb8f8be5 | grep -i pid "Pid": 21903, "PidMode": "", "PidsLimit": null,[root@docker ~]# nsenter -t 21903 -n netstat -tplnActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 21903/nginx: master tcp6 0 0 :::80 :::* LISTEN 21903/nginx: master

拷贝文件至容器内:

docker cp file1 :/file-to-path

容器主要特性

虚拟机提供了哪些功能,这其实是docker需要去实现的一个目标,是用一种轻量级的手段来实现类似于虚拟机的业务目标。

隔离性:应用程序要跑,那么要有应用程序运行的沙箱,也就是整个运行环境。这个运行环境要和其他人隔离,需要一个封闭的运行环境,这样一个封闭的运行环境和其他人隔离有什么好处呢?

应用之间不会互相影响,其次是一个封闭的系统,所有的依赖都在封闭的环境里面存在,外部的依赖是使用不了的,你所需要的依赖必须在你的隔离环境里面。这样在不同的环境里面运行其实就是将容器镜像的整个环境还原出来。

Namespace

• Linux Namespace 是一种 Linux Kernel 提供的资源隔离方案:

• 系统可以为进程分配不同的 Namespace(主机下面任何应用进程都有自己的namespace的)

• 并保证不同的 Namespace 资源独立分配、进程彼此隔离, 即不同的 Namespace 下的进程互不干扰 。

cgroup + namespace + unionfs  这些东西是保证容器技术可以承担和虚拟机一样的职责,但是更加轻量。

在当前命令行窗口查看一下当前进程的 Namespace 信息

[root@docker ~]# ls /proc/self/nscgroup ipc mnt net pid pid_for_children user uts[root@docker ~]# ls -l /proc/self/nstotal 0lrwxrwxrwx 1 root root 0 Feb 22 14:20 cgroup -> cgroup:[4026531835]lrwxrwxrwx 1 root root 0 Feb 22 14:20 ipc -> ipc:[4026531839]lrwxrwxrwx 1 root root 0 Feb 22 14:20 mnt -> mnt:[4026531840]lrwxrwxrwx 1 root root 0 Feb 22 14:20 net -> net:[4026531992]lrwxrwxrwx 1 root root 0 Feb 22 14:20 pid -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Feb 22 14:20 pid_for_children -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Feb 22 14:20 user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Feb 22 14:20 uts -> uts:[4026531838]root@e70a5541fa99:/# ls -l /proc/self/nstotal 0lrwxrwxrwx 1 root root 0 Feb 22 06:20 cgroup -> 'cgroup:[4026531835]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 ipc -> 'ipc:[4026532261]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 mnt -> 'mnt:[4026532259]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 net -> 'net:[4026532264]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 pid -> 'pid:[4026532262]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 pid_for_children -> 'pid:[4026532262]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 user -> 'user:[4026531837]'lrwxrwxrwx 1 root root 0 Feb 22 06:20 uts -> 'uts:[4026532260]'

Linux 内核代码中 Namespace 的实现

Linux 内核里面对任何进程的数据结构描述叫做task_struct。这里面包含很多信息,如pid的信息,内存的信息,打开文件的信息。

这里面的nsproxy这个属性就是定义这个进程所在的namespace信息的。

再去看看nsproxy数据结构里面细节,可以看到这里面就有好几类。

一个经常在初创的时候,它就是有天然的namespace的,linux操作系统里面pid为1的进程是systemd,systemd这个进程本身有自己的namespace,那就是默认的namespace,也就是主机namespace,当其他的进程被init systemd拉起来之后,也就是fork其他进程的时候,它将自己的这些namespace复制到新的进程上面去了,默认情况下面,新的子进程会和主机公用一个namspace,所以默认情况下我们感受不到公用同一个pid的namspace,以及其他的namspace。

Linux 对 Namespace操作方法

任何进程都有namespace,即使是在host namespace这些进程。systemd要有自己的namespace。在Linux里面其他进程都是fork出来的,当默认情况下父进程fork子进程,会将自己的namespace赋予给子进程。但是Linux还支持下面这些操作。

clone

在创建新进程的系统调用时,可以通过 flags 参数指定需要新建的 Namespace 类型:// CLONE_NEWCGROUP / CLONE_NEWIPC / CLONE_NEWNET / CLONE_NEWNS / CLONE_NEWPID / CLONE_NEWUSER / CLONE_NEWUTSint clone(int (*fn)(void *), void *child_stack, int flags, void *arg)

setns 先将进程fork出来,通过setns将其namespace改掉

该系统调用可以让调用进程加入某个已经存在的 Namespace 中:

Int setns(int fd, int nstype)

unshare   要去fork一个子进程,fork子进程的时候告诉操作系统移动到新的namespace,不和父进程共享

该系统调用可以将调用进程移动到新的 Namespace 下:

int unshare(int flags)

上面这些就可以让父进程和子进程处于不同的namespace下面,这样就实现了隔离。

隔离性 – Linux Namespace

namespace不同类型如下: 可以看到不同的namespace在不同的内核版本的支持,所以现在的这些都能够支持。

上面可以看到每个namespace下面,进程都运行在自己的封闭的空间,有独立的网络,独立的pid,独立的文件系统,独立的用户,独立的主机名,它就是完全孤立的一个环境,和主机完全隔离开来的一个环境。

为什么之前讲微服务,微服务最终的目的是什么?就是要让进程跑到一个独立的环境里面,有一个独立的网路身份,并且控制资源。

支持的namespace主要有上面几种。

NameSpace PID类型

可以看到每个进程还有pid,

[root@master ~]# ps -ef | head -n 10UID PID PPID C STIME TTY TIME CMDroot 1 0 0 13:23 ? 00:00:07 /usr/lib/systemd/systemd --switched-root --system --deserialize 21root 2 0 0 13:23 ? 00:00:00 [kthreadd]root 3 2 0 13:23 ? 00:00:04 [ksoftirqd/0]root 5 2 0 13:23 ? 00:00:00 [kworker/0:0H]

现在运行一个容器,可以看到和外面的pid完全不一样了。容器在启动时候,启动容器进程的时候,启动了一个新的pid namespace,所以里面的进程看到的和外面看到的是不一样的。

[root@docker ~]# docker run -itd busybox[root@docker ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES37d084d8e21b busybox "sh" 6 seconds ago Up 5 seconds hopeful_tereshkova[root@docker ~]# docker exec -it 37d084d8e21b sh/ # ps -efPID USER TIME COMMAND 1 root 0:00 sh 15 root 0:00 sh 22 root 0:00 ps -ef

bash1的ns信息[root@docker ~]# ls -l /proc/self/nstotal 0lrwxrwxrwx 1 root root 0 Feb 23 15:37 cgroup -> cgroup:[4026531835]lrwxrwxrwx 1 root root 0 Feb 23 15:37 ipc -> ipc:[4026531839]lrwxrwxrwx 1 root root 0 Feb 23 15:37 mnt -> mnt:[4026531840]lrwxrwxrwx 1 root root 0 Feb 23 15:37 net -> net:[4026531992]lrwxrwxrwx 1 root root 0 Feb 23 15:37 pid -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Feb 23 15:37 pid_for_children -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Feb 23 15:37 user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Feb 23 15:37 uts -> uts:[4026531838]可以看到在bash2和bash1处于同一个ns下面[root@docker ~]# ls -l /proc/self/ns/total 0lrwxrwxrwx 1 root root 0 Feb 23 15:37 cgroup -> cgroup:[4026531835]lrwxrwxrwx 1 root root 0 Feb 23 15:37 ipc -> ipc:[4026531839]lrwxrwxrwx 1 root root 0 Feb 23 15:37 mnt -> mnt:[4026531840]lrwxrwxrwx 1 root root 0 Feb 23 15:37 net -> net:[4026531992]lrwxrwxrwx 1 root root 0 Feb 23 15:37 pid -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Feb 23 15:37 pid_for_children -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Feb 23 15:37 user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Feb 23 15:37 uts -> uts:[4026531838]//容器当中pid:[4026532265]和bash pid:[4026531836]是不一样的/ # ls -l /proc/self/ns/total 0lrwxrwxrwx 1 root root 0 Feb 23 07:38 cgroup -> cgroup:[4026531835]lrwxrwxrwx 1 root root 0 Feb 23 07:38 ipc -> ipc:[4026532264]lrwxrwxrwx 1 root root 0 Feb 23 07:38 mnt -> mnt:[4026532262]lrwxrwxrwx 1 root root 0 Feb 23 07:38 net -> net:[4026532267]lrwxrwxrwx 1 root root 0 Feb 23 07:38 pid -> pid:[4026532265]lrwxrwxrwx 1 root root 0 Feb 23 07:38 pid_for_children -> pid:[4026532265]lrwxrwxrwx 1 root root 0 Feb 23 07:38 user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Feb 23 07:38 uts -> uts:[4026532263]

NameSpace NetWork类型:

容器里面的网络配置和外面也是不一致的。

/ # ip a1: lo: mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever44: eth0@if45: mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever

这就是namespace,当系统启动了新进程的时候,它开辟了一些新的namespace,这些新的namespace可以将用户的进程隔离,让容器里面的进程看不到外面的进程,让他有一个独立的网络IP地址,让其有独立的文件系统,让其有独立的用户,让其有独立的主机名。

这样就相当于在本机启动了一个进程,但是模拟成为了一个操作系统。

所以namespace是一种隔离的技术,就是将进程放到了不同的namespace下面。这些新的namespace里面可以创建新的网络,有新的进程管理,有新的文件系统,有新的用户等....

这就达到了作为微服务这样的一个平台,这个运行在容器里面的微服务就有独立的主机域名,ip地址,端口号,将进程塞到这个namespace下面就达到这个目的了。

隔离性-linux namespace

​​​

关于 namespace 的常用操作

查看当前系统的 namespace:

lsns –t

罗列当前主机上所有的namespace,可以指定类型-t指定要看哪一类的namespace,因为我只跑了一个容器busybox,所以只能看到两个namespace。

[root@docker ~]# lsns -t pid NS TYPE NPROCS PID USER COMMAND4026531836 pid 100 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 214026532265 pid 2 18496 root sh[root@docker ~]# lsns -t net NS TYPE NPROCS PID USER COMMAND4026531992 net 100 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 214026532267 net 2 18496 root sh

查看某进程的 namespace:

ls -la /proc//ns/

进入某 namespace 运行命令:(这是日常去调试容器里面进程应用的一个法宝!)

nsenter -t -n ip addr

查看容器对应宿主机上面的pid,容器技术的实质是进程,并没有完整的操作系统,就相当于在主机上面fork了一个子进程,通过docker daemon去fork一个子进程,这个子进程是可以在主机上面看到其pid的。

PID=$(docker inspect --format "{{ .State.Pid }}" )$ nsenter --target $PID --mount --uts --ipc --net --pid#查看容器对应宿主机上面的pid,容器技术的实质是进程,并没有完整的操作系统,就相当于在主机上面fork了一个子进程,通过docker daemon去fork一个子进程,这个子进程是可以在主机上面看到其pid的。[root@docker ~]# docker inspect 37d084d8e21b | grep -i pid "Pid": 18496, "PidMode": "", "PidsLimit": null,[root@docker ~]# ps -ef | grep 18496root 18496 18476 0 15:34 pts/0 00:00:00 sh

-p 是pid namesapce -n是network namespace,在主机上面通过nsenter去敲ip a,ps命令和在容器内部敲的命令返回的结果是一样的。

[root@docker ~]# nsenter -t 18496 -n ip a1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever44: eth0@if45: mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever[root@docker ~]# nsenter -t 18496 -p ps PID TTY TIME CMD18648 pts/1 00:00:00 sh20436 pts/1 00:00:00 bash21368 pts/1 00:00:00 nsenter21369 pts/1 00:00:00 bash21529 pts/1 00:00:00 nsenter21530 pts/1 00:00:00 ps

容器看到的是可以通过在主机上面nsenter看到的。这是日常去调试容器里面进程应用的一个法宝!,有些时候容器里面要做一些debug,缺一些工具,可以登入到主机上面进入到容器的namespace里面去看其网络配置,看其端口监听。这样就能够知道网络连接的状态是怎么样的。

Namespace 练习

在新 network namespace 执行 sleep 指令:unshare这个命令是说要fork一个进程,fork这个进程可以指定说不和父进程共用一个namespace。

这是告诉操作系统说要创建一个新的进程,这个新的子进程用新的namespace。下面就是unshare启动一个新的进程,并且加入了新的网络namespace。

其实docker就是这种技术,相当于启动了一个新的进程,然后set到了新的namespace里面,在新的namesapce里面可以配置自己的网络。

容器里面的网络设备是docker的网络驱动去配置的,kubernetes也是类似的,它会有自己的网络插件去配置网络。

unshare -fn sleep 60 -n, --net unshare network namespace -f, --fork fork before launching [root@docker ~]# unshare -fn sleep 60[root@docker ~]# lsns -t net NS TYPE NPROCS PID USER COMMAND4026532319 net 2 1817 root unshare -fn sleep 60

查看进程信息

ps -ef|grep sleeproot 32882 4935 0 10:00 pts/0 00:00:00 unshare -fn sleep 60root 32883 32882 0 10:00 pts/0 00:00:00 sleep 60

查看网络 Namespace

lsns -t net4026532508 net 2 32882 root unassigned unshare

进入改进程所在 Namespace 查看网络配置,与主机不一致

nsenter -t 32882 -n ip a1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

容器分为两个部分,一个叫runtime service,一个叫image service。

runtime service其实就是容器启动时候需要做哪些事情,比如namespace,cgroup这些都是和runtime service紧密相关的,还有一个image service,这个是镜像,镜像更多是overylay fs。

总结

namespace其实就是将进程放到一个隔离的环境,这个隔离的环境和其他的namespace也就是主机是分离的。这样可以又额外的网络配置,域名,文件系统。

实现的主要目的是,有一个进程,这个进程里面跑着一个服务。这个服务希望给它封闭的运行环境,并且有独立的网络标识,这样别人可以来访问。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:PromQL 初识 过滤时间序列/瞬时查询 范围查询
下一篇:Docker 为什么出现 解决哪些问题 VS 虚拟机
相关文章

 发表评论

暂时没有评论,来抢沙发吧~