Edited at

CoreOS で起動中のコンテナの中に入る

More than 3 years have passed since last update.


はじめに

起動させたコンテナの中に入るには ssh を使うのではなく nsenter を使うのがオフィシャルに推奨されていた。

2014年10月17日追記: Docker1.3 からは $ docker exec というコマンドが追加された。これは nsenter を replcace するものなので、今後はこれを利用する。

ssh を使わない方が良いという話は Docker の公式ブログ WHY YOU DON'T NEED TO RUN SSHD IN YOUR DOCKER CONTAINERS に詳しく書かれている。(Qiita に翻訳もある 【翻訳】Docker コンテナー内で SSHd を実行する必要がない理由 - helical618


CoreOS で docke exec を使ってコンテナの中に入る。

hello コンテナを起動したあと、

core@core-01 ~ $ docker run -d --name hello busybox /bin/sh -c "while true; do echo Hello World; sl

eep 1; done"

ad6aa31ae8194515e3ffb2e1632849f4cd79fed6af61c36ec6d1d144e86e13f9

exec を使ってコンテナの中に入る。

core@core-01 ~ $ docker exec -it hello /bin/sh

/ # ls
bin etc lib linuxrc mnt proc run sys usr
dev home lib64 media opt root sbin tmp var
/ # exit

より正確な説明としては、exec はコンテナにログインするためだけにあるのではなく、起動中のコンテナに 別のプロセスを注入する コマンドである。


CoreOS で nsenter を使ってコンテナの中に入る

Ubuntu や CentOS だと nsenter は公式リポジトリの README に書かれた方法でインストールする必要があるが、最新の CoreOS にはデフォルトで nsenter が入っている(素晴らしい)。

参考: nsinit (or nsenter) should be installed and available #18

# dd-agent という名前のコンテナが起動している

core@core-01 ~ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
60990982b230 datadog/docker-dd-agent:latest /usr/local/bin/run-d 8 minutes ago Up 8 minutes 8125/udp dd-agent
# dd-agent の PID を取得
core@core-01 ~ $ PID=$(docker inspect --format {{.State.Pid}} dd-agent)
# nsenter でコンテナの中に入る
core@core-01 ~ $ sudo nsenter --target $PID --mount --uts --ipc --net --pid
root@dd-agent:/#


CoreOS で docker-enter を使ってコンテナの中に入る

docker-enter は nsenter をラップしたシェルスクリプトで、コンテナ ID やコンテナ名を元にコンテナの中に入れるコマンドとして使う。

$ docker-enter <コンテナ ID>

# または
$ docker-enter <コンテナ名>

nsenter の README に書かれた方法でインストールした場合、デフォルトでは /usr/local/bin/docker-enter に置かれるが、CoreOS では docker-enter までは入ってないみたい。 CoreOS の /usr 以下は Read Only で、Cloud-Config を使っても書き込めなかった。 yungsang さんが作られている yungsang/coreos - Vagran Cloud を使えば docker-enter 入りの CoreOS が利用可能である。今のところ Cloud-Config + Docker Image のみだけでいけたらめっちゃシンプルでいいなと思っているので Cloud-Config を使って何とか出来ないかと考え core ユーザの PATH を調べてみると

core@core-01 ~ $ echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin:/usr/x86_64-cros-linux-gnu/gcc-bin/4.6.3

/opt/bin がある。調べるとここは書き込み可能。ということで /opt/bin/docker-enter に置く事にした。 /opt/bin はデフォルトで存在しないのでディレクトリを作成しないといけないかと思ったが、 write_files でファイルを存在しないディレクトリに置こうとした場合は、root 権限でディレクトリも作ってくれた。

#cloud-init

# 一部抜粋

write_files:
- path: /opt/bin/docker-enter
permissions: 0755
content: |
#!/bin/sh
#
# Original Source:
# https://github.com/jpetazzo/nsenter/blob/master/docker-enter
#

if [ -e $(dirname "$0")/nsenter ]; then
# with boot2docker, nsenter is not in the PATH but it is in the same folder
NSENTER=$(dirname "$0")/nsenter
else
NSENTER=nsenter
fi

if [ -z "$1" ]; then
echo "Usage: docker-enter CONTAINER [COMMAND [ARG]...]"
echo ""
echo "Enters the Docker CONTAINER and executes the specified COMMAND."
echo "If COMMAND is not specified, runs an interactive shell in CONTAINER."
else
PID=$(docker inspect --format "{{.State.Pid}}" "$1")
if [ -z "$PID" ]; then
exit 1
fi
shift

OPTS="--target $PID --mount --uts --ipc --net --pid --"

if [ -z "$1" ]; then
# No command given.
# Use su to clear all host environment variables except for TERM,
# initialize the environment variables HOME, SHELL, USER, LOGNAME, PATH,
# and start a login shell.
"$NSENTER" $OPTS su - root
else
# Use env to clear all host environment variables.
"$NSENTER" $OPTS env --ignore-environment -- "$@"
fi
fi


メモと感想


/opt/bin をこんな感じで使って良いのか

/etc が書き込み出来るのは知っていたが、/etc にコマンドを置くのはアレだと思っていたら、/opt が writable で場所的にも悪くはないと思って使っているけどどうなんだろう。

-> 2014/09/07 追記 @yungsang さんからコメント頂きました!


  • linux のお作法的にも問題ない(例えば Chef や Sensu のインストーラとかも /opt/chef/opt/sensu とかに置くしね、自分も ubuntu のとき /opt/app にアプリケーション置いてる)

  • CoreOS チームも /opt 以下に Kubernetes 置いてる




nsenter について(または ssh でログインしないことについて)

コンテナの中で sshd を立てておくというのはコンテナ内に複数プロセス動かすことになる。

これはコミュニティの中で意見が割れている:


  • コンテナの中は 1 プロセスのみ動かす派

  • コンテナの中で複数プロセス動かす派

にかかわるのかもしれない。この話は Docker Misconceptions[翻訳] Dockerについてよくある勘違い)でよく検討されている。コンテナの中を 1 プロセスにすると、プロセスのログを stdout にしておくことで $ docker logs でログの取得・閲覧が済むので、コンテナの中にログインしてどうこうする、ということも減ったり、コンテナの扱い方としてもシンプルになる気がしている。ただ Sensu とか色んなコンポーネントが必要になるものについては複数プロセスにした方が良い気がしている。適材適所なのかなぁ。


Docker on Ubuntu の時は Chef を使って docker-enter を入れている

Chef から docker を扱う bflad/chef-dockerdocker_container resource を使って esenter を入れている。

コマンドラインから esenter をインストールする場合は

$ docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter

とする。それを docker_container resource を使って行うため以下のように書いてインストールしている。

# your_recipe.rb

#
# Install docker-enter to /usr/local/bin/docker-enter
# https://github.com/jpetazzo/nsenter
#
docker_container "nsenter" do
image "jpetazzo/nsenter"
detach false
remove_automatically true
volume "/usr/local/bin:/target"
not_if { ::File.exists?("/usr/local/bin/docker-enter") }
action :run
end


REF