腾讯面试:如何调试Docker容器
Editor's Note
哈喽哈喽,大家好,最近面试腾讯,聊到Docker的调试,大多时候,为了docker轻量化,容器内部都是没有相关调试命令的,所以Docker调试是云原生时代,运维必须要掌握的,正好薛总之前整理过相关的文章,拿来分享下
The following article is from 云原生生态圈 Author Marionxue
Linux Network Namespace
Linux的Namespace[1]机制提供了一种资源隔离的解决方案,而目前Linux内核里面实现且支持的Namespace有7种,如下表:
名称 | 定义 | 说明 |
---|---|---|
Cgroup | CLONE_NEWCGROUP | Cgroup root directory (since Linux 4.6) |
IPC | CLONE_NEWIPC | System V IPC, POSIX message queues (since Linux 2.6.19) |
Network | CLONE_NEWNET | Network devices, stacks, ports, etc. (since Linux 2.6.24) |
Mount | CLONE_NEWNS | Mount points (since Linux 2.4.19) |
PID | CLONE_NEWPID | Process IDs (since Linux 2.6.24) |
User | CLONE_NEWUSER | User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8) |
UTS | CLONE_NEWUTS | Hostname and NIS domain name (since Linux 2.6.19) |
但是今天只分析一下Network NameSpace
以及管理,Network NameSpace
是实现网络空间隔离或者说网络虚拟化的基础, 不管是Docker还是虚拟机技术上的程序也都是由Linux内核实现的Network NameSpace
隔离开的命令空间内运行。ip netns
就是管理命令空间的命令,在学习之前,先了解几个命令unshare
、readlink
、nsenter
unshare
运行一些与父级不共享的某些名称空间的程序。
root@node3:~# unshare --help
Usage:
unshare [options] <program> [<argument>...]
Run a program with some namespaces unshared from the parent.
Options:
-h,--help
显示帮助文本并退出。
-i,-- ipc 取消共享IPC名称空间。
-m,-- mount 取消共享安装名称空间。
-n,-- net 取消共享网络名称空间。
-p,-- pid 取消共享pid名称空间。另请参见--fork和--mount-proc选项。
-u,-- uts 取消共享UTS名称空间。
-U,--user 取消共享用户名称空间。
-f,-将指定程序fork为取消共享的子进程,而不是直接运行它。这在创建新的pid名称空间时很有用。
--mount-proc [=mountpoint]在运行程序之前,将proc文件系统挂载到mountpoint (默认为/ proc)。这在创建新的pid名称空间时很有用。这也意味着创建一个新的挂载名称空间,因为/ proc挂载否则会破坏系统上的现有程序。新的proc文件系统显式安装为私有文件(由MS_PRIVATE | MS_REC)。
-r,-- map-root-user 仅在当前有效的用户和组ID已映射到新创建的用户名称空间中的超级用户UID和GID之后,才运行该程序。这样即使在没有特权的情况下运行,也可以方便地获得管理新创建的名称空间各个方面所需的功能(例如,在网络名称空间中配置接口或在安装名称空间中安装文件系统)。仅作为一项便利功能,它不支持更复杂的用例,例如映射多个范围的UID和GID。
For more details see unshare(1).
readLink
readlink是Linux系统中一个常用工具,主要用来找出符号链接所指向的位置
root@node3:/etc# readlink /etc/resolv.conf
../run/resolvconf/resolv.conf
nsenter
一个可以在指定进程的命令空间下运行指定程序的命令。这个命令大家在容器网络调试下可能常用,比如在一些没有网络调试工具(ip address
,ping
,telnet
,ss
,tcpdump
)的容器内利用宿主机上的命令进行容器内网络连通性的调试等等
root@node3:/etc# nsenter --help
...略
options:
-t, --target pid:指定被进入命名空间的目标进程的pid
-m, --mount[=file]:进入mount命令空间。如果指定了file,则进入file的命令空间
-u, --uts[=file]:进入uts命令空间。如果指定了file,则进入file的命令空间
-i, --ipc[=file]:进入ipc命令空间。如果指定了file,则进入file的命令空间
-n, --net[=file]:进入net命令空间。如果指定了file,则进入file的命令空间
-p, --pid[=file]:进入pid命令空间。如果指定了file,则进入file的命令空间
-U, --user[=file]:进入user命令空间。如果指定了file,则进入file的命令空间
-G, --setgid gid:设置运行程序的gid
-S, --setuid uid:设置运行程序的uid
-r, --root[=directory]:设置根目录
-w, --wd[=directory]:设置工作目录
运行一个Demo演示一下
root@node3:/etc# docker run -d --name test nginx
31095893cbddd999a9f2d9e78cd4c057d4f5b3763fe1b5ecd5d68df4e9ce5943
root@node3:/etc# docker inspect -f {{.State.Pid}} test
26308
root@node3:/etc# nsenter -t 26308 -n
root@node3:/etc# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
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@node3:/etc# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
root@node3:/etc# netstat -unlpt
Active 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 26308/nginx: master
tcp6 0 0 :::80 :::* LISTEN 26308/nginx: master
root@node3:/etc# exit
logout
这样看起来是不是很清楚,以后排查容器网络感觉又方便不少了
前期准备工作做了不少,下面好好说说Network NameSpace
如何通过ip netns
进行管理
Network NameSpace
管理
ip netns
命令基本很少使用到,所以先来熟悉一番
root@node3:~# ip netns help
Usage: ip netns list
ip netns add NAME
ip netns set NAME NETNSID
ip [-all] netns delete [NAME]
ip netns identify [PID]
ip netns pids NAME
ip [-all] netns exec [NAME] cmd ...
ip netns monitor
ip netns list-id
ip netns list
Linux系统下默认是没有network namespace,我们可以通过ls
或者ip netns
查看
root@node3:~# ip netns list
root@node3:~# ls -al /var/run/netns/
total 0
drwxr-xr-x 2 root root 40 Oct 6 05:07 .
drwxr-xr-x 26 root root 1020 Oct 6 04:57 ..
ip netns add
root@node3:~# ip netns add nsdemo1
root@node3:~# ip netns add nsdemo1 # 重复创建会报错
Cannot create namespace file "/var/run/netns/nsdemo1": File exists
root@node3:~# ip netns add nsdemo2
root@node3:~# ip netns list
nsdemo2
nsdemo1
root@node3:~# ls -al /var/run/netns/
total 0
drwxr-xr-x 2 root root 80 Oct 6 05:08 .
drwxr-xr-x 26 root root 1020 Oct 6 04:57 ..
-r--r--r-- 1 root root 0 Oct 6 05:08 nsdemo1
-r--r--r-- 1 root root 0 Oct 6 05:08 nsdemo2
由ip netns add
创建的network namespace会在/var/run/netns
目录下创建一个同名的文件,
ip netns del
root@node3:~# ip netns add nsdemo1
root@node3:~# ip netns add nsdemo2
root@node3:~# ip netns list
nsdemo2
nsdemo1
root@node3:~# ip -all netns del
root@node3:~# ip netns list
root@node3:~# ip netns add nsdemo3
root@node3:~# ip netns list
nsdemo3
root@node3:~# ip -all netns delete # 可以使用del,也可以使用delete
root@node3:~# ip netns list
root@node3:~#
ip netns exec
在创建了一个新的network namespace之后,很好奇里面究竟包含哪些东西,此时就可以通过exec进去查看,除了网络信息之外与宿主机是没有区别的
root@node3:~# ip netns add nsdemo3
root@node3:~# ip netns exec nsdemo3 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
root@node3:~# ip netns exec nsdemo3 bash
root@node3:~# ip addr # 每个network namespace都存在一个没有启用的lo本地回环网卡
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
root@node3:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
# 在进入nsdemo3这个network namespace之前,给nsdemo3的bash指定配置文件,用来修改bash前缀区分shell
root@node3:~# ip netns exec nsdemo3 /bin/bash --rcfile <(echo "PS1=\"namespace nsdemo3> \"")
namespace nsdemo3> ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
namespace nsdemo3> exit
exit
启用network namespace中的lo
网卡
root@node3:~# ip netns del nsdemo3
root@node3:~# ip netns list
root@node3:~# ip netns add nsdemo4
root@node3:~# ip netns list
nsdemo4
root@node3:~# ip netns exec nsdemo4 /bin/bash --rcfile <(echo "PS1=\"namespace nsdemo4> \"")
namespace nsdemo4> ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
namespace nsdemo4> ip link set lo up # 启用网卡lo
namespace nsdemo4> ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
namespace nsdemo4> ping 192.168.99.128 # 查看与主机之间的网络状态,发现网络不同
connect: Network is unreachable
namespace nsdemo4> exit
exit
root@node3:~# ip netns exec nsdemo4 /bin/bash --rcfile <(echo "PS1=\"namespace nsdemo4> \"")
namespace nsdemo4> ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
默认情况下,隔离的网络命名空间是无法进行通信的,同样可以利用ip
的子命令link
创建veth
和bridge
类型的网卡实现两个或者多个隔离的Network NameSpace之间相互通信。这样更有助于理解容器网络之间的隔离与通信。
运维技术交流群
「运维研习社」建立了运维技术交流群,大家可以添加小编微信进行加群。欢迎有想法、乐于分享的朋友们一起进群交流学习。
扫描添加好友邀您进运维交流群
免费知识星球
「运维研习社」同时建立了免费知识星球做运维知识沉淀,大家可以直接扫码进星球。欢迎进星球提问或发表看法。
运维技术交流群
「运维研习社」建立了运维技术交流群,大家可以添加小编微信进行加群。欢迎有想法、乐于分享的朋友们一起进群交流学习。
扫描添加好友邀您进运维交流群
免费知识星球
「运维研习社」同时建立了免费知识星球做运维知识沉淀,大家可以直接扫码进星球。欢迎进星球提问或发表看法。