nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。

用途

一个最典型的用途就是进入容器的网络命令空间。相当多的容器为了轻量级,是不包含较为基础的命令的,比如说ip addresspingtelnetsstcpdump等等命令,这就给调试容器网络带来相当大的困扰:只能通过docker inspect ContainerID命令获取到容器IP,以及无法测试和其他网络的连通性。这时就可以使用nsenter命令仅进入该容器的网络命名空间,使用宿主机的命令调试容器网络。

此外,nsenter也可以进入mnt, uts, ipc, pid, user命令空间,以及指定根目录和工作目录。

使用

首先看下nsenter命令的语法:

nsenter [options] [program [arguments]]

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]:设置工作目录 如果没有给出program,则默认执行$SHELL。

示例:

运行一个nginx容器,查看该容器的pid:

[root@staight ~]# docker inspect -f {{.State.Pid}} nginx
5645

然后,使用nsenter命令进入该容器的网络命令空间:

[root@staight ~]# nsenter -n -t5645
[root@staight ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
18: eth0@if19: <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

进入成功~

在Kubernetes中,在得到容器pid之前还需获取容器的ID,可以使用如下命令获取:

[root@node1 test]# kubectl get pod test -oyaml|grep containerID
- containerID: docker://cf0873782d587dbca6aa32f49605229da3748600a9926e85b36916141597ec85

或者更为精确地获取containerID:

[root@node1 test]# kubectl get pod test -o template --template='{{range .status.containerStatuses}}{{.containerID}}{{end}}'
docker://cf0873782d587dbca6aa32f49605229da3748600a9926e85b36916141597ec85

原理

namespace

namespace是Linux中一些进程的属性的作用域,使用命名空间,可以隔离不同的进程。

Linux在不断的添加命名空间,目前有:

  • mount:挂载命名空间,使进程有一个独立的挂载文件系统,始于Linux 2.4.19
  • ipc:ipc命名空间,使进程有一个独立的ipc,包括消息队列,共享内存和信号量,始于Linux 2.6.19
  • uts:uts命名空间,使进程有一个独立的hostname和domainname,始于Linux 2.6.19
  • net:network命令空间,使进程有一个独立的网络栈,始于Linux 2.6.24
  • pid:pid命名空间,使进程有一个独立的pid空间,始于Linux 2.6.24
  • user:user命名空间,是进程有一个独立的user空间,始于Linux 2.6.23,结束于Linux 3.8
  • cgroup:cgroup命名空间,使进程有一个独立的cgroup控制组,始于Linux 4.6

Linux的每个进程都具有命名空间,可以在/proc/PID/ns目录中看到命名空间的文件描述符。

[root@staight ns]# pwd
/proc/1/ns
[root@staight ns]# ll
total 0
lrwxrwxrwx 1 root root 0 Sep 23 19:53 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Sep 23 19:53 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Sep 23 19:53 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Sep 23 19:53 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Sep 23 19:53 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Sep 23 19:53 uts -> uts:[4026531838]

clone

clone是Linux的系统调用函数,用于创建一个新的进程。

clone和fork比较类似,但更为精细化,比如说使用clone创建出的子进程可以共享父进程的虚拟地址空间,文件描述符表,信号处理表等等。不过这里要强调的是,clone函数还能为新进程指定命名空间。

clone的语法:

 #define _GNU_SOURCE
#include <sched.h> int clone(int (*fn)(void *), void *child_stack,
int flags, void *arg, ...
/* pid_t *ptid, void *newtls, pid_t *ctid */ );

其中flags即可指定命名空间,包括:

  • CLONE_NEWCGROUP:cgroup
  • CLONE_NEWIPC:ipc
  • CLONE_NEWNET:net
  • CLONE_NEWNS:mount
  • CLONE_NEWPID:pid
  • CLONE_NEWUSER:user
  • CLONE_NEWUTS:uts

使用示例:

pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]);

setns

clone用于创建新的命令空间,而setns则用来让当前线程(单线程即进程)加入一个命名空间。

语法:

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sched.h> int setns(int fd, int nstype); fd参数是一个指向一个命名空间的文件描述符,位于/proc/PID/ns/目录。 nstype指定了允许进入的命名空间,一般可设置为0,表示允许进入所有命名空间。

因此,往往该函数的用法为:

  1. 调用setns函数:指定该线程的命名空间。
  2. 调用execvp函数:执行指定路径的程序,创建子进程并替换父进程。

这样,就可以指定命名空间运行新的程序了。

代码示例:

#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; if (argc < 3) {
fprintf(stderr, "%s /proc/PID/ns/FILE cmd args...\n", argv[0]);
exit(EXIT_FAILURE);
} fd = open(argv[1], O_RDONLY); /* Get file descriptor for namespace */
if (fd == -1)
errExit("open"); if (setns(fd, 0) == -1) /* Join that namespace */
errExit("setns"); execvp(argv[2], &argv[2]); /* Execute a command in namespace */
errExit("execvp");
}

使用示例:

./ns_exec /proc/3550/ns/uts /bin/bash

nsenter

那么,最后就是nsenter了,nsenter相当于在setns的示例程序之上做了一层封装,使我们无需指定命名空间的文件描述符,而是指定进程号即可。

指定进程号PID以及需要进入的命名空间后,nsenter会帮我们找到对应的命名空间文件描述符/proc/PID/ns/FD,然后使用该命名空间运行新的程序。

参考文档

容器内抓包定位网络问题:https://tencentcloudcontainerteam.github.io/tke-handbook/skill/capture-packets-in-container.html

man-page:nsenter:http://www.man7.org/linux/man-pages/man1/nsenter.1.html#top_of_page

man-page:clone:http://www.man7.org/linux/man-pages/man2/clone.2.html

man-page:setns:http://www.man7.org/linux/man-pages/man2/setns.2.html

nsenter命令简介的更多相关文章

  1. mac终端命令简介

    mac终端命令简介(适合刚刚入手mac的新人们) 1.取得root权限 意义相当与windows中的超级管理员权限,甚至还要超出.root权限可以修改系统中的任何文件,不过对普通用户的意义不大,了解即 ...

  2. linux中的strings命令简介2

    摘自:http://blog.csdn.net/stpeace/article/details/46641069 linux中的strings命令简介 之前我们聊过linux strings的用法和用 ...

  3. linux中的strings命令简介

    摘自:http://blog.csdn.net/stpeace/article/details/46641069 linux中的strings命令简介 在linux下搞软件开发的朋友, 几乎没有不知道 ...

  4. Ansible(二) - 配置及命令简介

    Ⅰ. Ansible Inventory Hosts文件配置 # mkdir /etc/ansible # touch /etc/ansible/hosts # cat /etc/hosts 127. ...

  5. Centos下grep命令简介

    grep命令简介 grep 是一个最初用于Unix操作系统的命令行工具.在给出文件列表或标准输入后,grep会对匹配一个或多个正则表达式的文本进行搜索,并只输出匹配(或者不匹配)的行或文本. grep ...

  6. lsof命令简介

    lsof命令简介: lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件 ...

  7. perf 高级命令简介

    perf 高级命令简介 1.使用 tracepoint 当 perf 根据 tick 时间点进行采样后,人们便能够得到内核代码中的 hot spot. 使用ls命令来演示 sys_enter 这个tr ...

  8. Windbg 脚本命令简介 二, Windbg command

    Windbg  脚本命令简介 二, Windbg  script command $<, $><, $$<, $$><, $$>a< (Run Scri ...

  9. Mongo DB命令简介

    引言   最近在学习MongoDB 总结了一些命令及常用的东西做整理   常用目录文件介绍 mongod 数据库部署命令 mongo 连接mongodb数据库而使用的命令 mongoimport 导入 ...

随机推荐

  1. 【java】学习路径23-拆箱与装箱

    拿Integer类型和int类型来举例子. 装箱,基本给引用.下面的代码相当于Integer i_test = Integer.valueOf("100"); 注意!过程是自动的. ...

  2. 【java】学习路径22-关于BigInteger类,大数字类

    //int ooo = 19999999999999; //long ooo = 19999999999999; //这么大的整数,int和long都存不下的时候,我们就使用Math类下的BigInt ...

  3. Go语言学习的坑爹历程

    鄙人暑期实习,需要用Go语言进行编程 在go语言中,结构体的定义只支持变量的声明,成员函数是采用"接口方法"来实现的 留一个成员定义的模板在此 package main impor ...

  4. 使用shell做http web接口,可以传递参数--废弃

    此文章废弃,参考另一篇 参考网址: https://me.jinchuang.org/archives/114.html https://www.cnblogs.com/jinchuang/p/142 ...

  5. 【前端必会】前端开发利器VSCode

    介绍 工欲善其事必先利其器,开发工具方面选择一个自己用的顺手的,这里就用VSCode 安装参考 https://www.runoob.com/w3cnote/vscode-tutorial.html ...

  6. C# 使用原生 System.IO.Compression 实现 zip 的压缩与解压

    zip 是一个非常常见的压缩包格式,本文主要用于说明如何使用代码 文件或文件夹压缩为 zip压缩包及其解压操作, 我们采用的是 微软官方的实现,所以也不需要安装第三方的组件包. 使用的时候记得 usi ...

  7. 7M与N的数学运算

    m=eval(input()) n=eval(input()) a=m+n b=m*n c=m**n d=m%n e=max(m,n) print(a,b,c,d,e)

  8. PAT (Basic Level) Practice 1013 数素数 分数 20

    令 Pi​ 表示第 i 个素数.现任给两个正整数 M≤N≤104,请输出 PM​ 到 PN​ 的所有素数. 输入格式: 输入在一行中给出 M 和 N,其间以空格分隔. 输出格式: 输出从 PM​ 到  ...

  9. 【.NET 6+Loki+Grafana】实现轻量级日志可视化服务功能

    前言:日志功能是几乎所有程序或系统都必备的一个功能.该文章通过使用Loki+Grafana来实现日志记录与可视化查询,欢迎围观. 有关环境: 操作系统:WIN 10 .NET环境:.NET 6 开发环 ...

  10. RAID5 IO处理之replace代码详解

    1 作用 从字面意思理解,replacement即是替换.我们知道硬盘都有一定的使用寿命,可以在硬盘失效之前通过该功能将就盘的数据迁移至新盘.因为replacement的流程是从旧盘中读出数据直接写入 ...