nsenter(1)はsetns(2)のCLIインターフェース

docker logo
Docker でコンテナーに入ってゴニョゴニョしたい時、昔ながらのサーバ管理よろしく sshd を起動して ssh でログインするのはコンテナーの特性を活かしておらず、筋が悪い。

WHY YOU DON’T NEED TO RUN SSHD IN YOUR DOCKER CONTAINERS
http://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/

Docker 1.3 以降であれば EXEC オプションを使って

$ sudo docker exec -it a9a6e45b07a3 bash
root@a9a6e45b07a3:/#

というようにすればよい。

Docker 1.3 より昔では util-linux の1プログラムである nsenter(“enter into namespaces” から来ているらしい) を使って コンテナーの名前空間に入れる

使い方は簡単で、コンテナーの PID を取得し、利用する名前空間を引数で指定する

$ PID=$(docker inspect --format {{.State.Pid}} <container_name_or_ID>)
$ nsenter --target $PID --mount --uts --ipc --net --pid

nsenter の実体はというと、ソースコードの冒頭のコメント

nsenter(1) – command-line interface for setns(2)

にあるようにシステムコール setns(2) の CLI ラッパーとわかる。

setns(2)で名前空間と戯れる

Namespaces in operation, part 2: the namespaces API [LWN.net]
http://lwn.net/Articles/531381/

を参考にシステムコール setns(2) を使って名前空間の簡単な実験をやってみる

Linux の /proc/PID/ns 以下には各名前空間のシンボリックリンクがあり、名前空間が同じであれば、シンボリックリンクの i-node も同じという性質を持っている。この i-node は ls -lreadlink で確認できる。

Each process has a /proc/PID/ns directory that contains one file for each type of namespace.

The kernel does some magic to ensure that if two processes are in the same namespace, then the inode numbers reported for the corresponding symbolic links in /proc/PID/ns will be the same.

However, the kernel also constructs each of the /proc/PID/ns symbolic links so that it points to a name consisting of a string that identifies the namespace type, followed by the inode number. We can examine this name using either the ls -l or the readlink command.
http://lwn.net/Articles/531271/

もし複数のプロセスで名前空間の i-node が同じだとすると、それらプロセスは名前空間が同じということになる。

コンテナーを起動

docker でコンテナーを起動

$ sudo docker start naughty_einstein
naughty_einstein

コンテナーの各名前空間の i-node を確認

コンテナーの PID を取得し、名前空間の i-node を確認

$ sudo docker inspect -f '{{ .State.Pid }}' naughty_einstein
30907
$ PID=`sudo docker inspect -f '{{ .State.Pid }}' naughty_einstein`
$ sudo ls -l /proc/$PID/ns
total 0
lrwxrwxrwx 1 root root 0 Nov  5 01:46 ipc -> ipc:[4026532450]
lrwxrwxrwx 1 root root 0 Nov  5 01:46 mnt -> mnt:[4026532447]
lrwxrwxrwx 1 root root 0 Nov  5 01:46 net -> net:[4026532453]
lrwxrwxrwx 1 root root 0 Nov  5 01:46 pid -> pid:[4026532451]
lrwxrwxrwx 1 root root 0 Nov  5 01:46 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Nov  5 01:46 uts -> uts:[4026532449]

シェルのプロセスの各名前空間の i-node を確認

シェルのプロセスの PID を取得し($$)、名前空間の i-node を確認

$ sudo ls -l /proc/$$/ns # $$ is replaced by shell's PID
total 0
lrwxrwxrwx 1 george george 0 Nov  5 01:47 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 george george 0 Nov  5 01:47 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 george george 0 Nov  5 01:47 net -> net:[4026531956]
lrwxrwxrwx 1 george george 0 Nov  5 01:47 pid -> pid:[4026531836]
lrwxrwxrwx 1 george george 0 Nov  5 01:47 user -> user:[4026531837]
lrwxrwxrwx 1 george george 0 Nov  5 01:47 uts -> uts:[4026531838]

この2つのプロセスで名前空間の i-node は user namespace 以外すべて異なっている。

次に システムコールsetns をラップしたプログラム ns_exec を用意。(プログラムは http://lwn.net/Articles/531271/ から)

/* ns_exec.c

   Copyright 2013, Michael Kerrisk
   Licensed under GNU General Public License v2 or later

   Join a namespace and execute a command in the namespace
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

/* A simple error-handling function: print an error message based
   on the value in 'errno' and terminate the calling process */

#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 [arg...]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1], O_RDONLY);   /* Get 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");
}

このプログラムは、名前空間のパス(/proc/PID/ns/FILE)とコマンドを渡すと、指定した名前空間でコマンドを実行できる。

コンテナー naughty_einstein の uts(ホスト名) 名前空間で /bin/bash を実行してみる。

$ sudo ./ns_exec /proc/$PID/ns/uts /bin/bash
# hostname
a9a6e45b07a3

ホスト名がコンテナーのものに置き換わっている。
各名前空間の i-node も確認

# ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Nov  5 01:48 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Nov  5 01:48 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Nov  5 01:48 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Nov  5 01:48 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Nov  5 01:48 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Nov  5 01:48 uts -> uts:[4026532449]

名前空間 ucs の i-node がコンテナーの i-node に置き換わっており、元々同じだった user を除き、他の名前空間はシェルプロセスの i-node のままとなっている。
期待通り名前空間を操作できている。
めでたしめでたし。

References

Tagged with: , , ,
Posted in linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 1 week ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 3 weeks ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 3 weeks ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 6 months ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 9 months ago
%d bloggers like this: