为什么构建容器需要 Namespace

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 为什么构建容器需要 Namespace

Namespace 概念

Namespace 是 Linux 内核的一个特性,该特性可以实现在同一主机系统中,对进程 ID、主机名、用户 ID、文件名、网络和进程间通信等资源的隔离。Docker 利用 Linux 内核的 Namespace 特性,实现了每个容器的资源相互隔离,从而保证容器内部只能访问到自己 Namespace 的资源。

目前 Linux 内核中提供了 8 种类型的 Namespace:image.png

各种 Namespace 的作用

Mount Namespace

Mount Namespace 是 Linux 内核实现的第一个 Namespace,从内核 2.4.19 版本开始加入。它可以用来隔离不同的进程或者进程组看到的挂载点。通俗地说,就是可以实现在不同的进程中看到不同的挂载目录。使用 Mount Namespace 可以实现容器内只能看到自己的挂载信息,在容器内的挂载操作不会影响主机的挂载目录。

下面我们通过一个实例来演示下 Mount Namespace。在演示之前,我们先来认识一个命令行工具 unshare。unshare 是 util-linux 工具包中的一个工具,CentOS 7 系统默认已经集成了该工具,使用 unshare 命令可以实现创建并访问不同类型的 Namespace。

首先我们使用以下命令创建一个 bash 进程并且新建一个 Mount Namespace:

#新建一个 Mount Namespace
root@docker-demo ~]# unshare --mount --fork /bin/bash

执行完上述命令后,这时我们已经在主机上创建了一个新的 Mount Namespace,并且当前命令行窗口加入了新创建的 Mount Namespace。下面我通过一个例子来验证下,在独立的 Mount Namespace 内创建挂载目录是不影响主机的挂载目录的。

然后在 /tmp 目录下创建一个目录,创建好目录后使用 mount 命令挂载一个 tmpfs 类型的目录:

[root@docker-demo ~]# mkdir /tmp/tmpfs
[root@docker-demo ~]# mount -t tmpfs -o size=20m tmpfs /tmp/tmpfs

然后使用 df 命令查看一下已挂载的目录信息:

[root@docker-demo ~]# df -h
文件系统                 容量  已用  可用 已用% 挂载点
/dev/mapper/centos-root   50G   15G   36G   29% /
devtmpfs                 3.9G     0  3.9G    0% /dev
tmpfs                    3.9G     0  3.9G    0% /dev/shm
tmpfs                    3.9G     0  3.9G    0% /sys/fs/cgroup
tmpfs                    3.9G  8.9M  3.9G    1% /run
tmpfs                    797M  8.0K  797M    1% /run/user/42
tmpfs                    797M     0  797M    0% /run/user/0
/dev/sda1               1014M  222M  793M   22% /boot
/dev/mapper/centos-home  142G   40M  142G    1% /home
tmpfs                     20M     0   20M    0% /tmp/tmpfs  #刚刚挂载的目录

可以看到 /tmp/tmpfs 目录已经被正确挂载。为了验证主机上并没有挂载此目录,我们新打开一个命令行窗口,同样执行 df 命令查看主机的挂载信息:

root@docker-demo ~]# df -hT
文件系统                类型      容量  已用  可用 已用% 挂载点
devtmpfs                devtmpfs  3.9G     0  3.9G    0% /dev
tmpfs                   tmpfs     3.9G     0  3.9G    0% /dev/shm
tmpfs                   tmpfs     3.9G  9.0M  3.9G    1% /run
tmpfs                   tmpfs     3.9G     0  3.9G    0% /sys/fs/cgroup
/dev/mapper/centos-root xfs        50G   15G   36G   29% /
/dev/sda1               xfs      1014M  222M  793M   22% /boot
/dev/mapper/centos-home xfs       142G   40M  142G    1% /home
tmpfs                   tmpfs     797M  8.0K  797M    1% /run/user/42
tmpfs                   tmpfs     797M     0  797M    0% /run/user/0

通过上面输出可以看到主机上并没有挂载 /tmp/tmpfs,可见我们独立的 Mount Namespace 中执行 mount 操作并不会影响主机。

为了进一步验证我们的想法,我们继续在当前命令行窗口查看一下当前进程的 Namespace 信息,命令如下:

[root@docker-demo ~]# ls -l /proc/self/ns/
总用量 0
lrwxrwxrwx. 1 root root 0 3月  21 19:58 cgroup -> cgroup:[4026531835]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 mnt -> mnt:[4026532670]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 net -> net:[4026531992]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid_for_children -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time_for_children -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 uts -> uts:[4026531838]

然后新打开一个命令行窗口,使用相同的命令查看一下主机上的 Namespace 信息:

[root@docker-demo ~]#  ls -l /proc/self/ns/
总用量 0
lrwxrwxrwx. 1 root root 0 3月  21 19:58 cgroup -> cgroup:[4026531835]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 net -> net:[4026531992]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid_for_children -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time_for_children -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 uts -> uts:[4026531838]

通过对比两次命令的输出结果,我们可以看到,除了 Mount Namespace 的 ID 值不一样外,其他Namespace 的 ID 值均一致。

通过以上结果我们可以得出结论,使用 unshare 命令可以新建 Mount Namespace,并且在新建的 Mount Namespace 内 mount 是和外部完全隔离的。

PID Namespace

PID Namespace 的作用是用来隔离进程。在不同的 PID Namespace 中,进程可以拥有相同的 PID 号,利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。例如一个进程在主机上 PID 为 122,使用 PID Namespace 可以实现该进程在容器内看到的 PID 为 1。

下面我们通过一个实例来演示下 PID Namespace的作用。首先我们使用以下命令创建一个 bash 进程,并且新建一个 PID Namespace:

[root@docker-demo ~]# unshare --pid --fork --mount-proc /bin/bash
[root@docker-demo ~]#

执行完上述命令后,我们在主机上创建了一个新的 PID Namespace,并且当前命令行窗口加入了新创建的 PID Namespace。在当前的命令行窗口使用 ps aux 命令查看一下进程信息:

[root@docker-demo ~]# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  1.7  0.0 116752  4884 pts/0    S    19:59   0:00 /bin/bash
root        31  7.0  0.0 151124  3872 pts/0    R+   19:59   0:00 ps aux

通过上述命令输出结果可以看到当前 Namespace 下 bash 为 1 号进程,而且我们也看不到主机上的其他进程信息。

UTS Namespace

UTS Namespace 主要是用来隔离主机名的,它允许每个 UTS Namespace 拥有一个独立的主机名。例如我们的主机名称为 docker,使用 UTS Namespace 可以实现在容器内的主机名称为 lagoudocker 或者其他任意自定义主机名。

同样我们通过一个实例来验证下 UTS Namespace 的作用,首先我们使用 unshare 命令来创建一个 UTS Namespace:

[root@docker-demo ~]# unshare --uts --fork /bin/bash
[root@docker-demo ~]#

创建好 UTS Namespace 后,当前命令行窗口已经处于一个独立的 UTS Namespace 中,下面我们使用 hostname 命令(hostname 可以用来查看主机名称)设置一下主机名:

[root@docker-demo ~]# hostname -b lagoudocker
#查看更改后的主机名
[root@docker-demo ~]# hostname
lagoudocker

通过上面命令的输出,我们可以看到当前UTS Namespace 内的主机名已经被修改为 lagoudocker。然后我们新打开一个命令行窗口,使用相同的命令查看一下主机的 hostname:

[root@docker-demo ~]# hostname
docker-demo

可以看到主机的名称仍然为 docker-demo,并没有被修改。由此,可以验证 UTS Namespace 可以用来隔离主机名。

IPC Namespace

IPC Namespace 主要是用来隔离进程间通信的。例如 PID Namespace 和 IPC Namespace 一起使用可以实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信。

同样我们通过一个实例来验证下IPC Namespace的作用,首先我们使用 unshare 命令来创建一个 IPC Namespace:

[root@docker-demo ~]# unshare --ipc --fork /bin/bash
[root@docker-demo ~]#

下面我们需要借助两个命令来实现对 IPC Namespace 的验证。

  • ipcs -q 命令:用来查看系统间通信队列列表。
  • ipcmk -Q 命令:用来创建系统间通信队列。

我们首先使用 ipcs -q 命令查看一下当前 IPC Namespace 下的系统通信队列列表:

[root@docker-demo ~]# ipcs -q
--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息

由上可以看到当前无任何系统通信队列,然后我们使用 ipcmk -Q 命令创建一个系统通信队列:

[root@docker-demo ~]# ipcmk -Q
消息队列 id:0

再次使用 ipcs -q 命令查看当前 IPC Namespace 下的系统通信队列列表:

[root@docker-demo ~]# ipcs -q
--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息
0x91ce1a3d 0          root       644        0            0

可以看到我们已经成功创建了一个系统通信队列。然后我们新打开一个命令行窗口,使用ipcs -q 命令查看一下主机的系统通信队列:

[root@docker-demo ~]# ipcs -q
--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息

通过上面的实验,可以发现,在单独的 IPC Namespace 内创建的系统通信队列在主机上无法看到。即 IPC Namespace 实现了系统通信队列的隔离。

User Namespace

User Namespace 主要是用来隔离用户和用户组的。一个比较典型的应用场景就是在主机上以非 root 用户运行的进程可以在一个单独的 User Namespace 中映射成 root 用户。使用 User Namespace 可以实现进程在容器内拥有 root 权限,而在主机上却只是普通用户。User Namesapce 的创建是可以不使用 root 权限的,下面我们以普通用户的身份创建一个 User Namespace,命令如下:

# 创建并切换到普通用户
[root@docker-demo ~]# su - chengzw
[chengzw@docker-demo ~]$ id
uid=1002(chengzw) gid=1004(chengzw) groups=1004(chengzw) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# 创建 User Namespace
[chengzw@docker-demo ~]$ unshare --user -r /bin/bash

然后执行 id 命令查看一下当前的用户信息:

[root@docker-demo ~]# id
uid=0(root) gid=0(root) 组=0(root) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

通过上面的输出可以看到我们在新的 User Namespace 内已经是 root 用户了。下面我们使用只有主机 root 用户才可以执行的 reboot 命令来验证一下,在当前命令行窗口执行 reboot 命令:

[root@docker-demo ~]# reboot
Failed to open /dev/initctl: Permission denied
Failed to talk to init daemon.

查看当前 User Namespace 所在的进程:

[root@docker-demo ~]# echo $$
2157

在宿主机上查看该进程对应的用户,可以看到实际上在宿主机上是 chengzw 用户:

[root@docker-demo ~]# ps -ef  | grep 2157
chengzw   2157  2092  0 20:41 pts/0    00:00:00 /bin/bash

可以看到,我们在新创建的 User Namespace 内虽然是 root 用户,但是并没有权限执行 reboot 命令。这说明在隔离的 User Namespace 中,并不能获取到主机的 root 权限,也就是说 User Namespace 实现了用户和用户组的隔离。

Net Namespace

Net Namespace 是用来隔离网络设备、IP 地址和端口等信息的。Net Namespace 可以让每个进程拥有自己独立的 IP 地址,端口和网卡信息。例如主机 IP 地址为 192.168.1.41 ,容器内可以设置独立的 IP 地址为 172.16.1.2。

同样用实例验证,我们首先使用 ip addr 命令查看一下主机上的网络信息:

[root@docker-demo ~]$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> 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 forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:50:56:9b:2d:90 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.41/24 brd 192.168.1.255 scope global ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::2556:f369:b4e7:fb64/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
    inet6 fe80::6b8d:29f7:a5fe:dbee/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
    inet6 fe80::f387:57a3:4975:d8f2/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

然后我们使用以下命令创建一个 Net Namespace:

[root@docker-demo ~]# unshare --net --fork /bin/bash

同样的我们使用 ip addr 命令查看一下网络信息:

[root@docker-demo ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

可以看到,宿主机上有 lo、ens192 等网络设备,而我们新建的 Net Namespace 内则与主机上的网络设备不同。我们可以在 Net Namespace 中新增网卡并配置 IP:

[root@docker-demo ~]# ip link add eth0 type dummy
[root@docker-demo ~]# ip addr add 172.16.1.2/24 dev 
[root@docker-demo ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
    link/ether e6:b7:92:d4:cc:ae brd ff:ff:ff:ff:ff:ff
    inet 172.16.1.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::e4b7:92ff:fed4:ccae/64 scope link
       valid_lft forever preferred_lft forever

Time Namespace

首先查看宿主机的时间,目前是3月21号:

[root@docker-demo ~]# date
2021年 03月 21日 星期日 21:02:38 CST

启动一个 centos 容器,查看时间和宿主机一致(UTC + 8 = CST):

[root@docker-demo ~]# docker exec -it centos bash
[root@a695de10917d /]# date
Sun Mar 21 13:02:46 UTC 2021

尝试在容器中修改时间,提示权限不够:

[root@a695de10917d /]# date -s 05/28
date: cannot set date: Operation not permitted

在启动容器的时候,加上--cap-add SYS_TIME参数,在容器内将时间修改成3月20号:

root@docker-demo ~]# docker run -it --cap-add SYS_TIME --name centos centos /bin/bash
[root@e7b61aba6222 /]# date -s 03/20
Sat Mar 20 00:00:00 UTC 2021
[root@e7b61aba6222 /]# date
Sat Mar 20 00:00:01 UTC 2021
# 退出容器
[root@e7b61aba6222 /]# exit
exit

在宿主机上查看时间发现也改成3月20号了:

[root@docker-demo ~]# date
2021年 03月 20日 星期六 08:00:07 CST

--cap-add SYS_TIME是为了将宿主机的内核时间挂载进来与容器共享,因此容器时间更改了,宿主机时间也会跟着更改。

为什么 Docker 需要 Namespace?

Linux 内核从 2002 年 2.4.19 版本开始加入了 Mount Namespace,而直到内核 3.8 版本加入了 User Namespace 才为容器提供了足够的支持功能。

当 Docker 新建一个容器时, 它会创建这六种 Namespace,然后将容器中的进程加入这些 Namespace 之中,使得 Docker 容器中的进程只能看到当前 Namespace 中的系统资源。

正是由于 Docker 使用了 Linux 的这些 Namespace 技术,才实现了 Docker 容器的隔离,可以说没有 Namespace,就没有 Docker 容器。

Docker exec 原理

在了解了 Linux Namespace 的隔离机制后,你应该会很自然地想到一个问题:docker exec 是怎么做到进入容器里的呢?

实际上,Linux Namespace 创建的隔离空间虽然看不见摸不着,但一个进程的 Namespace 信息在宿主机上是确确实实存在的,并且是以一个文件的方式存在。

首先创建一个容器:

$ docker run -p 4000:80 helloworld python app.py

通过如下指令,你可以看到当前正在运行的 Docker 容器的进程号(PID)是 25686:

$ docker inspect --format '{{ .State.Pid }}' 4ddf4638572d25686

这时,你可以通过查看宿主机的 proc 文件,看到这个 25686 进程的所有 Namespace 对应的文件:

$ ls -l  /proc/25686/ns
total 0
lrwxrwxrwx 1 root root 0 Aug 13 14:05 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid_for_children -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 uts -> uts:[4026532277]

可以看到,一个进程的每种 Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上。

有了这样一个可以“hold 住”所有 Linux Namespace 的文件,我们就可以对 Namespace 做一些很有意义事情了,比如:加入到一个已经存在的 Namespace 当中。

这也就意味着:一个进程,可以选择加入到某个进程已有的 Namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理。

而这个操作所依赖的,乃是一个名叫 setns() 的 Linux 系统调用。它的调用方法,我可以用如下一段小程序为你说明:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE);} while (0)
int main(int argc, char *argv[]) {
    int fd;
    fd = open(argv[1], O_RDONLY);
    if (setns(fd, 0) == -1) {
        errExit("setns");
    }
    execvp(argv[2], &argv[2]); 
    errExit("execvp");
}

这段代码功能非常简单:它一共接收两个参数,第一个参数是 argv[1],即当前进程要加入的 Namespace 文件的路径,比如 /proc/25686/ns/net;而第二个参数,则是你要在这个 Namespace 里运行的进程,比如 /bin/bash。

这段代码的核心操作,则是通过 open() 系统调用打开了指定的 Namespace 文件,并把这个文件的描述符 fd 交给 setns() 使用。在 setns() 执行后,当前进程就加入了这个文件对应的 Linux Namespace 当中了。

现在,你可以编译执行一下这个程序,加入到容器进程(PID=25686)的 Network Namespace 中:

$ gcc -o set_ns set_ns.c 
$ ./set_ns /proc/25686/ns/net /bin/bash 
$ ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
     collisions:0 txqueuelen:0 
          RX bytes:976 (976.0 B)  TX bytes:796 (796.0 B)
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

正如上所示,当我们执行 ifconfig 命令查看网络设备时,我会发现能看到的网卡“变少”了:只有两个。而我的宿主机则至少有四个网卡。这是怎么回事呢?

实际上,在 setns() 之后我看到的这两个网卡,正是我在前面启动的 Docker 容器里的网卡。也就是说,我新创建的这个 /bin/bash 进程,由于加入了该容器进程(PID=25686)的 Network Namepace,它看到的网络设备与这个容器里是一样的,即:/bin/bash 进程的网络设备视图,也被修改了。

而一旦一个进程加入到了另一个 Namespace 当中,在宿主机的 Namespace 文件上,也会有所体现。

在宿主机上,你可以用 ps 指令找到这个 set_ns 程序执行的 /bin/bash 进程,其真实的 PID 是 28499:

# 在宿主机上
ps aux | grep /bin/bash
root     28499  0.0  0.0 19944  3612 pts/0    S    14:15   0:00 /bin/bash

这时,如果查看一下这个 PID=28499 的进程的 Namespace,你就会发现这样一个事实:

$ ls -l /proc/28499/ns/net
lrwxrwxrwx 1 root root 0 Aug 13 14:18 /proc/28499/ns/net -> net:[4026532281]
$ ls -l  /proc/25686/ns/net
lrwxrwxrwx 1 root root 0 Aug 13 14:05 /proc/25686/ns/net -> net:[4026532281]

在 /proc/[PID]/ns/net 目录下,这个 PID=28499 进程,与我们前面的 Docker 容器进程(PID=25686)指向的 Network Namespace 文件完全一样。这说明这两个进程,共享了这个名叫 net:[4026532281]的 Network Namespace。

此外,Docker 还专门提供了一个参数,可以让你启动一个容器并“加入”到另一个容器的 Network Namespace 里,这个参数就是 -net,比如:

$ docker run -it --net container:4ddf4638572d busybox ifconfig

这样,我们新启动的这个容器,就会直接加入到 ID=4ddf4638572d 的容器,也就是我们前面的创建的应用容器(PID=25686)的 Network Namespace 中。所以,这里 ifconfig 返回的网卡信息,跟我前面那个小程序返回的结果一模一样,你也可以尝试一下。

而如果我指定–net=host,就意味着这个容器不会为进程启用 Network Namespace。这就意味着,这个容器拆除了 Network Namespace 的“隔离墙”,所以,它会和宿主机上的其他普通进程一样,直接共享宿主机的网络栈。这就为容器直接操作和使用宿主机网络提供了一个渠道。

目录
相关文章
|
7天前
|
运维 监控 Devops
构建高效自动化运维体系:DevOps与容器化实践
【7月更文挑战第44天】在现代IT基础设施管理中,自动化运维已成为提升效率、降低成本、确保系统稳定性的关键。本文将探讨如何通过结合DevOps理念和容器化技术来构建一个高效的自动化运维体系。我们将分析这一体系对提高软件交付速度、优化资源利用率以及增强系统可靠性的积极影响,并提供一系列实施策略和最佳实践,帮助企业实现运维自动化转型。
|
14天前
|
存储 Kubernetes 调度
通过重新构建Kubernetes来实现更具弹性的容器编排系统
通过重新构建Kubernetes来实现更具弹性的容器编排系统
33 8
|
22天前
|
运维 Kubernetes 开发者
构建高效后端服务:微服务架构与容器化部署的实践
【7月更文挑战第29天】 在现代软件开发中,后端服务的构建不再仅仅是代码的堆砌,而是需要考虑到可扩展性、可靠性和快速迭代等多重因素。本文将探讨如何通过微服务架构和容器化技术来构建一个高效的后端服务系统。我们将从微服务的概念出发,分析其在后端开发中的应用优势,并结合容器化技术,特别是Docker和Kubernetes的使用,来展示如何在保证服务高可用性和伸缩性的同时,简化开发和部署流程。文章还将涵盖如何应对微服务架构下的数据一致性挑战,以及如何利用现代云平台资源来优化后端服务的性能和成本效益。
|
1月前
|
Ubuntu 网络协议 Android开发
使用ruri快速构建跨架构chroot容器
【7月更文挑战第3天】使用ruri在x86_64系统上构建arm64 Ubuntu容器的简要步骤: 1. 确认binfmt_misc支持,检查/proc/filesystems。 2. 安装qemu-user-static以模拟arm64指令。 3. 通过rootfstool脚本获取arm64的Ubuntu rootfs。 4. 下载最新版的ruri二进制文件。 5. 解压rootfs,将qemu-aarch64-static复制进去,用ruri启动容器。 6. 在容器内配置DNS(如设置resolv.conf)。 注意:需以root权限运行ruri,并根据系统环境调整步骤。
56 20
|
24天前
|
运维 Kubernetes 负载均衡
构建高效后端:微服务架构与容器化实践
【7月更文挑战第27天】在数字化浪潮的推动下,后端技术不断演进以满足日益增长的性能和可扩展性需求。本文深入探讨了微服务架构的设计原则、优势及其挑战,并结合容器化技术,分析了如何在现代软件开发中实现高效、灵活的后端系统。通过实际案例,我们揭示了微服务与容器化相结合的最佳实践,旨在为开发者提供一条清晰的技术路线图。
|
27天前
|
Ubuntu 网络协议 Android开发
使用ruri快速构建跨架构chroot容器
【7月更文挑战第10天】使用ruri快速构建跨架构chroot容器:先确认binfmt_misc支持;安装qemu-user-static;用rootfstool脚本获取rootfs;下载最新ruri二进制;解压rootfs并启动容器;配置DNS。完成这些步骤后,可在x86_64上运行arm64的Ubuntu容器。注意,ruri处于实验阶段,使用前需评估风险,并根据需求调整与优化。此流程提供基础参考,具体操作可能需微调。
|
24天前
|
存储 运维 Kubernetes
容器镜像的构建与管理实践
在云原生时代,容器技术已成为现代软件开发和运维不可或缺的一部分。本文将深入探讨容器镜像的构建流程、管理策略以及安全性考量,旨在为读者提供一套系统化的容器镜像管理方案。我们将从实际案例出发,分析容器镜像构建的最佳实践,同时指出常见的陷阱与误区。此外,文章还将介绍如何有效利用现有的工具和平台来提升容器镜像的安全性和管理效率,确保在快速迭代的开发周期中,能够维护镜像的一致性和可靠性。
43 0
|
8天前
|
Docker 容器
Docker cp 将宿主机上的文件复制到容器中
Docker cp 将宿主机上的文件复制到容器中
10 0
|
4天前
|
JSON JavaScript 开发者
Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程
【8月更文挑战第7天】Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程
Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程
|
6天前
|
监控 Ubuntu Docker
如何在Docker容器启动时自动运行脚本
【8月更文挑战第13天】在Docker容器启动时自动运行脚本可通过以下方式实现:1) 使用`ENTRYPOINT`或`CMD`指令在Dockerfile中直接指定启动脚本,如`ENTRYPOINT [&quot;/startup.sh&quot;]`;2) 启动容器时通过`--entrypoint`参数指定脚本路径;3) 利用supervisor等进程管理工具自动启动与监控脚本,确保其稳定运行。确保脚本具有执行权限并正确设置依赖资源路径。