linux namespace
namespace简介
- linux操作系统的namespace用于资源的隔离,是容器功能实现的底层支持
- 不同namespace内看到的东西不一样。namespace的类型有pid,uts,cgroup,ipc,network,user,mount,分别实现对不同资源的隔离
- 这篇文章介绍namespace相关的系统调用和命令行工具nsenter。
namespaces相关的系统调用:
- clone创建一个新的namespace
- setns加入一个namespace
- unshare将进程迁移到一个新的namespace
clone创建新的namespace
[root@k8snode1 namespace]# cat cloneuts.c
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.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
};
//容器进程设置hostname为utstest,启动一个/bin/bash
int container_main(void *arg){
printf("container - inside the container\n");
sethostname("utstest",7);
execv(container_args[0], container_args);
printf("Something wrong\n");
return 1;
}
int main(){
printf("parent - start a container\n");
//创建一个子进程,子进程使用新的UTS namespace
int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD|CLONE_NEWUTS, NULL);
waitpid(container_pid, NULL, 0);
printf("parent - container stopped\n");
return 0;
}
[root@k8snode1 namespace]# gcc cloneuts.c -o uts
[root@k8snode1 utsns]# ./uts
parent - start a container
container - inside the container
#容器内打印hostname
[root@utstest utsns]# hostname
utstest
[root@utstest utsns]# exit
exit
parent - container stopped
#容器外打印hostname
[root@k8snode1 utsns]# hostname
k8snode1
- 因为给clone传递了参数CLONE_NEWUTS,所以clone创建的进程会被放入一个新建的uts namespace。
- 不同的uts namespace中的进程执行sethostname不会互相影响。
setns加入ns
[root@k8snode1 namespace]# cat setnsuts.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
//使用系统调用setns进入指定的namespace
int main(int argc, char *argv[]){
int k,fd;
if(argc!=2) {
printf("usage: command namespacefile\n");
return -1;
}
fd = open(argv[1],O_RDONLY);
if(fd == -1){
printf("open namespace file failed\n");
return -1;
}
//加入指定的namespace
if(setns(fd,0)==-1){
printf("setns failed\n");
return -1;
}
char buff[1024];
k=gethostname(buff,1023);
printf("hostname:%s\n",buff);
return 1;
}
[root@k8snode1 namespace]# gcc setnsuts.c -o setuts
#加入指定进程(上面的uts命令启动的进程)的uts并打印hostname
[root@k8snode1 namespace]# ./setuts /proc/3893548/ns/uts
hostname:utstest
- 可以看到setuts打印的hostname与uts进程的相同,说明setns成功了。
- 文件/proc/3893548/ns/uts代表了进程3893548的uts namspace,这里验证了linux上万物皆文件的概念。
命令行工具nsenter
#nsenter可以进入某个进程的namespaces
#进入指定进程的namespace
[root@testdsq namespace]# nsenter -a -t 3893548
#根据namespace file进入到某个namespace
[root@testdsq namespace]# nsenter --uts=/proc/3893548/ns/uts
排查容器网络
[root@testdsq ~]# docker ps -f name=nginx
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42e8e1e7cf6c goharbor/nginx-photon:v2.8.2 "nginx -g 'daemon of…" 2 months ago Up 2 months (healthy) 0.0.0.0:8898->8080/tcp, :::8898->8080/tcp nginx
#获取容器进程pid
[root@testdsq ~]# docker inspect -f {{.State.Pid}} nginx
1015456
#进入容器net namespace
[root@testdsq ~]# nsenter -n -t 1015456
#查看容器网络信息
[root@testdsq ~]# ifconfig -a
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.9 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ac:12:00:09 txqueuelen 0 (Ethernet)
RX packets 1621358 bytes 1376204886 (1.2 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1645975 bytes 1178811498 (1.0 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 2203964 bytes 386354434 (368.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2203964 bytes 386354434 (368.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@testdsq ~]# netstat -ntl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.11:35319 0.0.0.0:* LISTEN
#抓取容器网络包
[root@testdsq ~]# tcpdump -i eth0 tcp port 8080
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:43:04.572612 IP testdsq.43842 > testdsq.webcache: Flags [S], seq 2355546266, win 43690, options [mss 65495,sackOK,TS val 3576319139 ecr 0,nop,wscale 7], length 0
15:43:04.572650 IP testdsq.webcache > testdsq.43842: Flags [S.], seq 2468828826, ack 2355546267, win 28960, options [mss 1460,sackOK,TS val 2491864504 ecr 3576319139,nop,wscale 7], length 0
15:43:04.572669 IP testdsq.43842 > testdsq.webcache: Flags [.], ack 1, win 342, options [nop,nop,TS val 3576319139 ecr 2491864504], length 0
#容器外通过curl访问容器服务
[root@testdsq ~]# curl http://88.88.88.22:8898
基本思路就是用nsenter进入到容器的网络命名空间,然后就可以像常规一样使用tcpdump抓包分析了。
参考
- man namespaces

浙公网安备 33010602011771号