- 命名空间
- 网络命名空间
- 文件相关
- 容器网络
- cgroup 资源控制
1 命名空间
docker ps
docker inspect 〈ID or NAME〉
docker inspect ivlan_test43
"Pid": 6338,
root@vm-133:~# ls -l /proc/6338/ns
total 0
lrwxrwxrwx 1 root root 0 Oct 13 15:49 cgroup -> 'cgroup:[4026532870]'
lrwxrwxrwx 1 root root 0 Oct 13 15:49 ipc -> 'ipc:[4026532810]'
lrwxrwxrwx 1 root root 0 Oct 13 15:49 mnt -> 'mnt:[4026532808]'
lrwxrwxrwx 1 root root 0 Oct 13 15:32 net -> 'net:[4026532813]'
lrwxrwxrwx 1 root root 0 Oct 13 15:49 pid -> 'pid:[4026532811]'
lrwxrwxrwx 1 root root 0 Oct 13 16:04 pid_for_children -> 'pid:[4026532811]'
lrwxrwxrwx 1 root root 0 Oct 13 15:49 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Oct 13 16:04 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Oct 13 15:49 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Oct 13 15:49 uts -> 'uts:[4026532809]'
2 网络命名空间
root@vm-133:~# docker inspect 8e70627e2871 | jq -r '.[] | {State: .State.Pid, NetworkSettings: {SandboxID: .NetworkSettings.SandboxID, SandboxKey: .NetworkSettings.SandboxKey, EndpointID: (.NetworkSettings.Networks | to_entries[0].value.EndpointID)}}'
{
"State": 6338,
"NetworkSettings": {
"SandboxID": "fdfa3bb6c99124f08b6b49d9bf71ad32e50872604daa0e61f6edc335ef46bd23",
"SandboxKey": "/var/run/docker/netns/fdfa3bb6c991",
"EndpointID": "13c6db40e824e45fa568eea2b9fc05cae7bf840f7c24f6b7a52ede02a70de921"
}
}
3 文件
在 Docker 中,/var/lib/docker/containers
和 /var/lib/docker/overlay2
,它们分别用来存储 Docker 容器的数据和 Docker 镜像的数据。
/var/lib/docker/containers
:这个目录包含了所有的 Docker 容器的数据。每个容器都有一个对应的目录,目录的名称是容器的 ID。在每个容器的目录中,你可以找到一些文件和目录,包括容器的配置文件(config.v2.json
)、容器的日志文件(<container-id>-json.log
)、容器的运行时状态等。/var/lib/docker/overlay2
:这个目录则用来存储 Docker 镜像的数据。Docker 使用了 Union File System(联合文件系统)来存储镜像,其中的每一层都存储在一个单独的目录中。这些目录的名称是长的哈希值,这些哈希值对应于 Docker 镜像的每一层。在每个目录中,你可以找到diff
目录,该目录包含了这一层中文件的实际内容;link
文件,该文件包含了这一层的短 ID;以及lower
文件,该文件包含了这一层下面所有层的短 ID。
root@vm-133:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
36ff97d8b002 alpine "/bin/sh" 48 minutes ago Up 48 minutes ivlan_test44
8e70627e2871 alpine "/bin/sh" 2 hours ago Up 2 hours ivlan_test43
ce8444aaf7bb alpine "/bin/sh" 6 hours ago Up 6 hours ivlan_test5
556527deebaf alpine "/bin/sh" 6 hours ago Up 6 hours ivlan_test4
f53bc31a686c alpine "/bin/sh" 6 hours ago Up 6 hours ivlan_test3
root@vm-133:~# docker exec ivlan_test43 cat /etc/hostname
8e70627e2871
root@vm-133:~# docker exec 8e70627e2871 cat /etc/hostname
8e70627e2871
root@vm-133:~# cat /var/lib/docker/containers/8e70627e287189b505902d9a905df90b5e720acac0d2fb2277775fec8e9a0088/hostname
8e70627e2871
root@vm-133:~# tree /var/lib/docker/containers/8e70627e287189b505902d9a905df90b5e720acac0d2fb2277775fec8e9a0088
/var/lib/docker/containers/8e70627e287189b505902d9a905df90b5e720acac0d2fb2277775fec8e9a0088
├── 8e70627e287189b505902d9a905df90b5e720acac0d2fb2277775fec8e9a0088-json.log
├── checkpoints
├── config.v2.json
├── hostconfig.json
├── hostname
├── hosts
├── mounts
├── resolv.conf
└── resolv.conf.hash
2 directories, 7 files
4 网络
在 Linux 系统中,/sys/class/net/<interface>
目录包含了关于网络接口的一些信息,其中 ifindex
和 iflink
是两个很重要的文件。
/sys/class/net/<interface>/ifindex
:这个文件包含了网络接口的索引。这是一个在系统中唯一的整数,用来唯一标识一个网络接口。这个值是由内核分配的,每当创建一个新的网络接口时,内核都会分配一个新的ifindex
值。/sys/class/net/<interface>/iflink
:这个文件包含了网络接口的链接索引。对于大多数网络接口来说,这个值和ifindex
是相同的。但是对于一些特殊的网络接口,比如 veth 接口或 macvlan 接口,这个值表示的是对端接口的ifindex
值
ip add show
20: veth1c15c3a@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ce:88:c5:fa:0d:56 brd ff:ff:ff:ff:ff:ff link-netnsid 7
inet6 fe80::cc88:c5ff:fefa:d56/64 scope link
valid_lft forever preferred_lft forever
root@vm-133:~# cat /sys/class/net/veth1c15c3a/ifindex
20
root@vm-133:~# cat /sys/class/net/veth1c15c3a/iflink
19
root@vm-133:~# docker exec -it ivlan_test45 sh
/ # cat /sys/class/net/eth0/ifindex
19
/ # cat /sys/class/net/eth0/iflink
20
也就是说 veth1c15c3a@if19 与容器中ifindex为20的是一对,bridge网络模式下每个容器的iflink号都是不一样的
其他查看方法
root@vm-133:~# ip link show veth1c15c3a
20: veth1c15c3a@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether ce:88:c5:fa:0d:56 brd ff:ff:ff:ff:ff:ff link-netnsid 7
root@vm-133:~# ethtool -S veth1c15c3a
NIC statistics:
peer_ifindex: 19
rx_queue_0_xdp_packets: 0
rx_queue_0_xdp_bytes: 0
rx_queue_0_drops: 0
rx_queue_0_xdp_redirect: 0
rx_queue_0_xdp_drops: 0
rx_queue_0_xdp_tx: 0
rx_queue_0_xdp_tx_errors: 0
tx_queue_0_xdp_xmit: 0
tx_queue_0_xdp_xmit_errors: 0
root@vm-133:~# docker exec ivlan_test45 ip lin sh
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
19: eth0@if20: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
root@vm-133:~# docker exec ivlan_test45 ethtool -S eth0
OCI runtime exec failed: exec failed: unable to start container process: exec: "ethtool": executable file not found in $PATH: unknown
而IPVlan模式下是一样的,因为工用一个mac地址
root@vm-133:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
b3a3736da342 bridge bridge local
a0d241cafaa3 host host local
bc6a5d21f2a5 ipvlan30 ipvlan local
76bbb910822f ipvlan110 ipvlan local
a7f59c059434 none null local
root@vm-133:~# docker inspect ipvlan30 | jq -r '.[] | .Containers | to_entries[] | {Name: .value.Name, MacAddress: .value.MacAddress, IPv4Address: .value.IPv4Address, Options: .value.Options}'
{
"Name": "ivlan_test4",
"MacAddress": "",
"IPv4Address": "192.168.30.3/24",
"Options": null
}
{
"Name": "ivlan_test3",
"MacAddress": "",
"IPv4Address": "192.168.30.2/24",
"Options": null
}
root@vm-133:~# docker inspect ipvlan110 | jq -r '.[] | .Containers | to_entries[] | {Name: .value.Name, MacAddress: .value.MacAddress, IPv4Address: .value.IPv4Address, Options: .value.Options}'
{
"Name": "ivlan_test5",
"MacAddress": "",
"IPv4Address": "192.168.110.2/24",
"Options": null
}
root@vm-133:~# docker inspect bridge | jq -r '.[] | .Containers | to_entries[] | {Name: .value.Name, MacAddress: .value.MacAddress, IPv4Address: .value.IPv4Address, Options: .value.Options}'
{
"Name": "ivlan_test44",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"Options": null
}
{
"Name": "ivlan_test45",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"Options": null
}
root@vm-133:~# docker ps -qa |xargs -i docker exec {} cat /sys/class/net/eth0/iflink
20
18
2
2
2
root@vm-133:~# docker ps -qa |xargs -i docker exec {} cat /sys/class/net/eth0/ifindex
19
17
14
13
12
cgroup
控制组(cgroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计 等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时的对系统资源的竞争。控制组技术最早是由 Google 的程序员 2006 年起提出,Linux 内核自 2.6.24 开始支持。控制组可以提供对容器的内存、CPU、磁盘 IO 等资源的限制和审计管理。
- subsystem: 一个 subsystem 就是一个内核模块,它被关联到一颗 cgroup 树之后,就会在树节点进行具体的操作。subsystem 经常被称作 "resource controller",因为它主要被用来调度或者限制每个进程组的资源,但是这个说法不完全准确,因为有时我们将进程分组只是为了做一些监控,观察一下他们的状态,比如 perf_event subsystem。
- hierarchy:一个 hierarchy 可以理解为一棵 cgroup 树,树的每个节点就是一个进程组,每棵树都会与多个 subsystem 关联。在一颗树里面,会包含 Linux 系统中的所有进程,但每个进程只能属于一个节点(进程组)。系统中可以有很多颗 cgroup 树,每棵树都和不同的 subsystem 关联,一个进程可以属于多颗树,即一个进程可以属于多个进程组,这些进程组和不同的 subsystem 关联。
可以通过查看 /proc/cgroup 目录查看当前系统支持哪些 subsystem 关联
root@Copy-of-VM-U2204:~# cat /proc/cgroups | column -t
#subsys_name hierarchy num_cgroups enabled
cpuset 0 144 1
cpu 0 144 1
cpuacct 0 144 1
blkio 0 144 1
memory 0 144 1
devices 0 144 1
freezer 0 144 1
net_cls 0 144 1
perf_event 0 144 1
net_prio 0 144 1
hugetlb 0 144 1
pids 0 144 1
rdma 0 144 1
misc 0 144 1
docker run -d --name=test11 --cpus=1 --memory=512m \
--device-read-bps /dev/sda:1mb \
--device-write-bps /dev/sda:1mb \
--device-read-iops /dev/sda:1000 \
--device-write-iops /dev/sda:1000 \
ubuntu:18.04 sleep infinity
docker inspect test11
"CpuShares": 0,
"Memory": 536870912,
"NanoCpus": 1000000000,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": [
{
"Path": "/dev/sda",
"Rate": 1048576
}
],
"BlkioDeviceWriteBps": [
{
"Path": "/dev/sda",
"Rate": 1048576
}
],
"BlkioDeviceReadIOps": [
{
"Path": "/dev/sda",
"Rate": 1000
}
],
"BlkioDeviceWriteIOps": [
{
"Path": "/dev/sda",
"Rate": 1000
}
],
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
root@Copy-of-VM-U2204:~# docker inspect -f '{{.Id}}' test11
9e1d8ee845065d10e9eaa13443432c8407d93e0945dd905a05fc5dcb0ed751fa
root@Copy-of-VM-U2204:/sys/fs/cgroup/system.slice/docker-9e1d8ee845065d10e9eaa13443432c8407d93e0945dd905a05fc5dcb0ed751fa.scope#
cat io.max
8:0 rbps=1048576 wbps=1048576 riops=1000 wiops=1000
cat cpu.max
100000 100000
cat memory.max
536870912
cat cpu.weight
100
cpu.max
: 这个文件包含了容器的 CPU 配额和周期。值为两个整数,分别代表配额(quota)和周期(period)。一个负的配额表示没有限制。cpu.weight
: 这个文件包含了容器的 CPU 权重,值的范围在 1 到 10000 之间,它决定了容器的 CPU 时间相对于其他容器的分配。io.max
: 这个文件包含了容器对各设备的 IO 限制,包括读速率、写速率、读写速率和设备的总 IO 限制。memory.high
: 这个文件表示了容器的内存高水位线,如果容器的内存使用超过这个值,内核将开始回收容器的内存。memory.low
: 这个文件表示了容器的内存低水位线,内核在进行内存回收时将尽可能保护容器不低于这个值的内存使用。