Docker による Windows における Linux 環境の整備

2015年10月31日

はじめに

Docker を使って Windows 上で Linux 環境を整備する方法について。

バージョン

Docker Toolbox 1.8.3, Windows 7 64 bit

ファイル

Docker

Docker は、コンテナ型仮想化技術の一つである。VirtualBox のようなハードウェアレベルの仮想化と異なり、コンテナ型ではホスト OS とゲスト OS がカーネルを共有しているため、仮想化のオーバーヘッドが少なくて済むと言われている。

Docker を Windows で使うためのソフトとして Docker Toolbox がある。Docker はもともと Linux の技術なので、VirtualBox で Linux の仮想マシンを動かして、仮想マシン越しに Docker を利用するようになっている。

Docker Tools

ここ から Docker Tools のインストーラをダウンロードする。ふつうにインストールすればよい。デスクトップに "Docker Quickstart Terminal" というアイコンができるので、これを実行すると、コマンドラインが起動する。

Docker Toolbox では、Docker を利用するために VirtualBox で Linux の仮想マシンを動かしている。VirtualBox を起動してみれば、こっそり仮想マシンが動いているのがわかる。

※最初に実行したとき、"Starting VM..." というメッセージから進まなくなった。ウインドウを閉じて、VirtualBox の仮想マシンを落として再度実行したらうまくいった。

以下のコマンドを打ってみてエラーが出なければ OK。

$ docker version
Client:
 Version:      1.8.3
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   f4bf5c7
 Built:        Mon Oct 12 18:01:15 UTC 2015
 OS/Arch:      windows/amd64

Server:
 Version:      1.8.3
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   f4bf5c7
 Built:        Mon Oct 12 18:01:15 UTC 2015
 OS/Arch:      linux/amd64

作業はこのまま Windows のコマンドラインで行う方法と、仮想マシンに入って行う方法がある。仮想マシンに入るには以下のようにする。

$ docker-machine ssh default

ここで "default" はマシン名である。どういうマシンがあるかは次のように調べる。

$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM
default   *        virtualbox   Running   tcp://192.168.99.100:2376

コンテナの実行

コンテナを実行するには、コンテナのもとになるイメージが必要である。イメージは多数公開されている。たとえば Ubuntu のイメージをダウンロードするには、以下のようにする。

$ docker pull ubuntu:14.04

ここで ":14.04" はタグで、バージョンを指定している。タグを指定してしなければ ":latest" (最新版) が指定される。"docker images" でイメージのリストを表示できる。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              14.04               0a17decee413        12 days ago         188.4 MB

コンテナを実行してみよう。

$ docker run ubuntu:14.04 ls -l
total 64
drwxr-xr-x   2 root root 4096 Oct  9 08:39 bin
drwxr-xr-x   2 root root 4096 Apr 10  2014 boot
drwxr-xr-x   5 root root  360 Oct 25 06:50 dev
drwxr-xr-x  64 root root 4096 Oct 25 06:50 etc
drwxr-xr-x   2 root root 4096 Apr 10  2014 home
drwxr-xr-x  12 root root 4096 Oct  9 08:38 lib
drwxr-xr-x   2 root root 4096 Oct  9 08:38 lib64
drwxr-xr-x   2 root root 4096 Oct  9 08:38 media
drwxr-xr-x   2 root root 4096 Apr 10  2014 mnt
drwxr-xr-x   2 root root 4096 Oct  9 08:38 opt
dr-xr-xr-x 127 root root    0 Oct 25 06:50 proc
drwx------   2 root root 4096 Oct  9 08:39 root
drwxr-xr-x   7 root root 4096 Oct  9 08:38 run
drwxr-xr-x   2 root root 4096 Oct 12 17:27 sbin
drwxr-xr-x   2 root root 4096 Oct  9 08:38 srv
dr-xr-xr-x  13 root root    0 Oct 25 06:50 sys
drwxrwxrwt   2 root root 4096 Oct  9 08:39 tmp
drwxr-xr-x  11 root root 4096 Oct 12 17:27 usr
drwxr-xr-x  12 root root 4096 Oct 12 17:27 var

ls でルートディレクトリが表示されている。ちなみに、もしダウンロードしていないイメージを指定した場合、ダウンロード可能であれば勝手にダウンロードされる。

ふつうの Linux のようにログインすることもできる。

$ winpty docker run -it ubuntu:14.04 bash
root@d07fdf761298:/# ls -l
total 64
drwxr-xr-x   2 root root 4096 Oct  9 08:39 bin
drwxr-xr-x   2 root root 4096 Apr 10  2014 boot
drwxr-xr-x   5 root root  380 Oct 25 06:54 dev
drwxr-xr-x  64 root root 4096 Oct 25 06:54 etc
drwxr-xr-x   2 root root 4096 Apr 10  2014 home
drwxr-xr-x  12 root root 4096 Oct  9 08:38 lib
drwxr-xr-x   2 root root 4096 Oct  9 08:38 lib64
drwxr-xr-x   2 root root 4096 Oct  9 08:38 media
drwxr-xr-x   2 root root 4096 Apr 10  2014 mnt
drwxr-xr-x   2 root root 4096 Oct  9 08:38 opt
dr-xr-xr-x 128 root root    0 Oct 25 06:54 proc
drwx------   2 root root 4096 Oct  9 08:39 root
drwxr-xr-x   7 root root 4096 Oct  9 08:38 run
drwxr-xr-x   2 root root 4096 Oct 12 17:27 sbin
drwxr-xr-x   2 root root 4096 Oct  9 08:38 srv
dr-xr-xr-x  13 root root    0 Oct 25 06:50 sys
drwxrwxrwt   2 root root 4096 Oct  9 08:39 tmp
drwxr-xr-x  11 root root 4096 Oct 12 17:27 usr
drwxr-xr-x  12 root root 4096 Oct 12 17:27 var
root@d07fdf761298:/# exit
exit

$

ここでオプション "-i" はインタラクティブモード、"-t" は TTY の指定である。はじめの winpty は TTY を使うためのものである。もし仮想マシンに入って作業をする場合は、winpty は必要ない。

注目すべき点は、VirtualBox で仮想マシンを動かすのとは異なり、Ubuntu の起動の手順が不要なことである。注意すべき点は、コンテナは一時的な環境なので、コンテナ内で作業した内容は永続せず、コンテナを破棄した時点で消滅することである。

コンテナの情報

コンテナの情報を得るには "docker ps" を用いる。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
a271558fd737        ubuntu:14.04        "bash"              15 seconds ago      Exited (0) 12 seconds ago                       pensive_davinci
aaa8be548bc4        ubuntu:14.04        "ls -l"             29 seconds ago      Exited (0) 29 seconds ago                       elated_davinci

オプション "-a" で終了したコンテナも含めて表示する。オプション "-q" で ID だけ表示される。

$ docker ps -qa
a271558fd737
aaa8be548bc4

最後に実行したコンテナ ID を得るには、次のようにする。

$ docker ps -ql

コンテナ情報は、一見するとコンテナの「実行履歴」のように見えるが、そうではない。コンテナはイメージをもとにコマンドとセットで作成され、実行される。ここで表示されているものは、実行を終了しているものも含めて実体を持ったもので、コンテナ内で作業した内容を保持しているし、実行を再開することもできる。

コンテナの実行の再開

コンテナの実行を終了すると、STATUS が "Exited" になるが、コンテナ自体はまだ存在しており、再度実行することができる。実行には "docker start <ID>" を用いる。

$ winpty docker run -it ubuntu:14.04 bash
root@1315d9e96a4a:/# touch test
root@1315d9e96a4a:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  test  usr
boot  etc  lib   media  opt  root  sbin  sys  tmp   var
root@1315d9e96a4a:/# exit
exit

$ docker start `docker ps -ql`
1315d9e96a4a

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
1315d9e96a4a        ubuntu:14.04        "bash"              43 seconds ago      Up 14 seconds                           trusting_bhaskara

STATUS で再開したのはわかるが、bash には戻っていない。bash に (コンテナに) 戻るには "docker attach <ID>" を用いる。

$ winpty docker attach `docker ps -ql`
root@1315d9e96a4a:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  test  usr
boot  etc  lib   media  opt  root  sbin  sys  tmp   var

作成したファイル "test" はちゃんと残っている。

逆に、コンテナを止めずにコンテナから出るには、Ctrl+P, Ctrl+Q と入力すればよい。

$ winpty docker attach `docker ps -ql`
root@1315d9e96a4a:/#  (ここで Ctrl+P, Ctrl+Q と入力)

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
1315d9e96a4a        ubuntu:14.04        "bash"              6 minutes ago       Up 6 minutes                            trusting_bhaskara

コンテナの停止と削除

コンテナの停止には "docker stop <ID>" を用いる。コンテナの削除には "docker rm <ID>" を用いる。

すべてのコンテナを削除したい場合は、次のようにすればよい。

docker stop `docker ps -qa`
docker rm `docker ps -qa`

コンテナ実行時にオプション "--rm" を付けておくと、コンテナは終了時に自動的に削除される。

コンテナ名の設定

コンテナを指定する場合、ID は使いにくい。そのため、コンテナに名前を付けることができる。オプション "--name" を使う。

$ winpty docker run --name myubuntu -it ubuntu:14.04 bash
root@a2c20bf18851:/# exit
exit

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
a2c20bf18851        ubuntu:14.04        "bash"              17 seconds ago      Exited (0) 15 seconds ago                       myubuntu

上で見てきたコンテナ情報を見ると、それぞれのコンテナに勝手な名前が付けられていたことがわかる。"--name" はこれを任意の名前にできるわけである。同じ名前のコンテナを作ることはできないので、いろいろやっているうちにコンテナが大量に作成されるのが嫌な場合は、コンテナに名前を付けてしまえばよい。

Docker 仮想マシンが終了した場合のコンテナの扱い

Docker 仮想マシンが終了してもコンテナは消えず、仮想マシンを起動したらまた扱える。ただし、コンテナは終了した状態になっているので、再開させる必要がある。

イメージの作成

自分のイメージを作成する場合は、Dockerfile というファイルを用意する。もっとも簡単なものは、以下のようなものだろう。

FROM ubuntu:14.04

MAINTAINER Yuu Kasuga

ここで "FROM" はもとにするイメージの指定、"MAINTAINER" はメンテナの名前の指定である。Dockerfile があるディレクトリで、次のようにイメージを作成する。

$ docker build -t myubuntu .

ここで "myubuntu" のところは好きな名前 (およびタグ) を指定する。うまく行ったら、イメージリストに表示されるはずである。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
myubuntu            latest              9c766eb34f04        20 seconds ago      188.4 MB
ubuntu              14.04               0a17decee413        12 days ago         188.4 MB

注意: 最初に "Sending build context to Docker daemon ..." というメッセージが出るが、これはカレントにあるファイルをすべて Docker daemon に送っているそうで、うっかり大きなファイルをその辺に置いといたりすると延々とデータを送り続けるので、気を付けること。

"docker build" は Dockerfile のコマンドごとに結果をキャッシュしており、Dockerfile の部分的な修正に対して、変更されていない部分にはキャッシュを適用する。そのため、再実行は高速である。ただし、キャッシュが効かなくなるのは Dockerfile の修正された箇所だけではなくて、修正された箇所以降すべてである。したがって、プログラムのインストールなど、時間がかかるものは最初のほうに書いたほうがよいかもしれない。

参考

ユーザーの作成

ユーザーを作成したい場合は、Dockerfile に次のように追加する。

RUN useradd -s /bin/bash -m penguin
RUN echo penguin:penguin | chpasswd
RUN usermod -G sudo penguin

"RUN" はイメージ作成時にコマンドを実行させるものである。実行させたい Linux コマンドをそのまま書けばよい。上の例では、1 行目でユーザーを作成、2 行目でパスワードの設定 ("名前:パスワード" の形式)、3 行目で sudo できるように sudo グループに追加している。

ただ、このまま実行しても root で実行されてしまう。ユーザーを指定するには、コンテナ起動時にオプション "-u" でユーザーを指定する。

$ winpty docker run -u penguin -it myubuntu bash
penguin@34717bbe999c:/$ pwd
/

ルートディレクトリから始まってしまう。ふつうの Linux のようにログインした状態で始まりたい。その場合、Dockerfile で作業ディレクトリを指定する。

WORKDIR /home/penguin

ただし、作業ディレクトリを指定した後の "RUN" はその作業ディレクトリで実行されることに注意。

実行すると、ホームディレクトリから始まる。

$ winpty docker run -u penguin -it myubuntu bash
penguin@8bfaead642b9:~$ pwd
/home/penguin

ユーザーは Dockerfile で指定することもできる。

USER penguin

ただし、ユーザーを指定した後の "RUN" はそのユーザーで実行されることに注意。

ホスト名の指定

せっかくなのでホスト名も指定したい。ホスト名の指定は、コンテナ実行時にオプション "-h" で行う。

$ winpty docker run -u penguin -h penguin-machine -it myubuntu bash
penguin@penguin-machine:~$

だいぶそれっぽくになってきた。

コマンドの指定

コンテナ実行時にコマンドを指定しているが、この指定を Dockerfile で行うこともできる。

CMD ["ls", "-la"]

コマンドなしで実行すると、ls が起動する。

$ winpty docker run -u penguin -h penguin-machine -it myubuntu
total 20
drwxr-xr-x 2 penguin penguin 4096 Oct 25 07:29 .
drwxr-xr-x 3 root    root    4096 Oct 25 07:29 ..
-rw-r--r-- 1 penguin penguin  220 Apr  9  2014 .bash_logout
-rw-r--r-- 1 penguin penguin 3637 Apr  9  2014 .bashrc
-rw-r--r-- 1 penguin penguin  675 Apr  9  2014 .profile

コマンドラインのほうでコマンドを指定すると、そちらが使われる。

$ winpty docker run -u penguin -h penguin-machine -it myubuntu bash
penguin@penguin-machine:~$

複数のコマンドを実行したい場合は、スクリプトを渡せばよい。たとえば、次のようなスクリプトを用意する。

script.sh

#!/bin/sh
pwd
ls -la

Dockerfile に次のように書く。

ADD ./script.sh /tmp/script.sh
RUN chown penguin:penguin /tmp/script.sh
RUN chmod +x /tmp/script.sh

WORKDIR /home/penguin
CMD ["/tmp/script.sh"]

ここで、"ADD" はファイルをコンテナに渡す指示である。

$ winpty docker run -u penguin -h penguin-machine -it myubuntu
/home/penguin
total 20
drwxr-xr-x 2 penguin penguin 4096 Oct 25 07:29 .
drwxr-xr-x 3 root    root    4096 Oct 25 07:29 ..
-rw-r--r-- 1 penguin penguin  220 Apr  9  2014 .bash_logout
-rw-r--r-- 1 penguin penguin 3637 Apr  9  2014 .bashrc
-rw-r--r-- 1 penguin penguin  675 Apr  9  2014 .profile

Windows フォルダの共有

コンテナではデータは永続しないので、必要なデータを Windows 側にもっていく手段が必要である。Windows のフォルダをコンテナから見えるようにするには、以下のようにする。

まず、Dockerfile で Windows フォルダを共有するためのディレクトリを作成する。

RUN mkdir /home/penguin/user
RUN chown penguin.penguin /home/penguin/user

コンテナ実行時にフォルダの共有を指定する。

$ winpty docker run -u penguin -h penguin-machine -v /c/Users/`whoami`:/home/penguin/user -it myubuntu

オプション "-v" で Windows 側とコンテナ側のディレクトリを指定している。ほんとうはこれで上手くいきそうなのだが、Docker Toolbox がコマンドラインとして使っている MinGW がパスを変換してしまうため、それを抑えるために次のようにする。

$ winpty docker run -u penguin -h penguin-machine -v //c/Users/`whoami`:/home/penguin/user -it myubuntu

これでもうまく行かなくて、結局つぎのようにすればうまくいく。

run

#!/bin/sh
docker run -u penguin -h penguin-machine -v //c/Users/`whoami`:/home/penguin/user -it myubuntu

実行

$ winpty sh run
penguin@penguin-machine:~$ ls user
...

参考

コミット

コンテナに対する修正結果をイメージに適用することができる。それには "docker commit <ID>" を用いる。

たとえば、コンテナにファイルを追加する。

$ winpty sh run
penguin@penguin-machine:~$ ls
user
penguin@penguin-machine:~$ touch file
penguin@penguin-machine:~$ ls
file  user

そのまま再実行しても、ファイルは消える。

$ winpty sh run
penguin@penguin-machine:~$ ls
user

コンテナにファイルを追加した直後、次のようにする。

$ docker commit `docker ps -ql` myubuntu

このあと実行すると、ファイルが追加されている。

$ winpty sh run
penguin@penguin-machine:~$ ls
file  user

イメージの削除

イメージを削除するには、"docker rmi <image>" を用いる。ふつうはイメージを使用しているコンテナを先に停止・削除してから行う。

イメージを作成する過程で、次のように失敗したものが無名で残る場合がある。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
myubuntu            latest              62a361974965        7 minutes ago       188.7 MB
<none>              <none>              72280c94523f        9 minutes ago       188.7 MB
<none>              <none>              cb568cba0c4a        15 minutes ago      188.7 MB
<none>              <none>              ec605e8e590d        16 minutes ago      188.7 MB
<none>              <none>              5ab94691159a        21 minutes ago      188.7 MB
<none>              <none>              d60c1a716ef2        21 minutes ago      188.7 MB
<none>              <none>              6432ea58d388        24 minutes ago      188.7 MB
<none>              <none>              34f21cdfe2a4        35 minutes ago      188.7 MB
<none>              <none>              e5ab9efefebe        36 minutes ago      188.7 MB
ubuntu              14.04               0a17decee413        12 days ago         188.4 MB

これらを削除するには、次のようにすればよい。まず、"<none>" を含む行だけ取り出す。

$ docker images | grep "<none>"
<none>              <none>              72280c94523f        9 minutes ago       188.7 MB
<none>              <none>              cb568cba0c4a        15 minutes ago      188.7 MB
<none>              <none>              ec605e8e590d        16 minutes ago      188.7 MB
<none>              <none>              5ab94691159a        21 minutes ago      188.7 MB
<none>              <none>              d60c1a716ef2        21 minutes ago      188.7 MB
<none>              <none>              6432ea58d388        24 minutes ago      188.7 MB
<none>              <none>              34f21cdfe2a4        35 minutes ago      188.7 MB
<none>              <none>              e5ab9efefebe        36 minutes ago      188.7 MB

3 列目だけ欲しいので、awk で 3 列目だけ取り出す。

$ docker images | grep "<none>" | awk '{print $3}'
72280c94523f
cb568cba0c4a
ec605e8e590d
5ab94691159a
d60c1a716ef2
6432ea58d388
34f21cdfe2a4
e5ab9efefebe

これを "docker rmi" に渡せばよい。

$ docker rmi -f `docker images | grep "<none>" | awk '{print $3}'`

ここでオプション "-f" は強引に消すためのものである。

イメージの保存と復元

イメージを保存するには、次のようにする。

$ docker save image > filename.tar

保存したイメージを復元するには、次のようにする。

$ docker load < filename.tar

テストすると、以下のとおりである。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
myubuntu            latest              1b514a102904        About an hour ago   231 MB
ubuntu              14.04               0a17decee413        12 days ago         188.4 MB

$ docker save myubuntu > myubuntu.tar
$ docker rmi myubuntu
...
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              14.04               0a17decee413        12 days ago         188.4 MB

$ docker load < myubuntu.tar
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
myubuntu            latest              1b514a102904        About an hour ago   231 MB
ubuntu              14.04               0a17decee413        12 days ago         188.4 MB

gzip を使うこともできる。

$ docker save image | gzip > filename.tar.gz

復元は tar の場合と同じである。

$ docker load < filename.tar.gz

イメージの名前の変更

イメージの名前 (タグ) を変えるには、"docker tag <src> <new>" を使う。

$ docker tag myubuntu myubuntu2

X を使う/SSH で接続する

コンテナで X を用いたプログラムを実行したい場合は、コンテナに SSH で接続して X11 のポートフォワーディングを使えばよい。

コンテナに SSH で接続するためには、まず SSH をインストールする必要がある。Dockerfile に以下を追加する。

RUN apt-get install -y ssh

apt-get で ssh をインストールしている。そしてサーバーを起ち上げるのだが、"RUN" はイメージを作る時のコマンドを指定するためのものなので、"RUN" によってサービスを開始させることはできない。そこで、最後に実行する script.sh の中に書く。

sudo service ssh start

SSH で接続するために、鍵を用意する。

$ ssh-keygen -t rsa

パスフレーズは設定しなくてよい。これで C:\Users\ユーザー名\.ssh に公開鍵 id_rsa.pub と秘密鍵 id_rsa が生成される。公開鍵をコンテナに設定する必要があるのだが、Windows のフォルダは見えているので、script.sh の中で設定してしまう。

script.sh

#!/bin/sh
sudo service ssh start

mkdir /home/penguin/.ssh
cat /home/penguin/user/.ssh/id_rsa.pub >> /home/penguin/.ssh/authorized_keys

/bin/bash

イメージを作成する。途中で apt-get が実行され、SSH がインストールされるのがわかる。

コンテナを実行するとき、オプション "-p" でポートを指定する。

docker run -u penguin -h penguin-machine -v //c/Users/`whoami`:/home/penguin/user -p 22 -it myubuntu

sudo しているのでパスワードを聞かれる。パスワードが通ると、SSH のサービスが開始され、bash が実行される。

$ winpty sh run
[sudo] password for penguin:
[ OK ]rting OpenBSD Secure Shell server sshd
penguin@penguin-machine:~$

別のターミナルを開く ("Docker Quickstart Terminal" を実行すればよい)。今実行したコンテナの情報を表示する。

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                   NAMES
c612f5c4a90a        myubuntu            "/tmp/script.sh"    19 seconds ago      Up 19 seconds       0.0.0.0:32769->22/tcp   prickly_babbage

"PORTS" で "0.0.0.0:32769->22/tcp" となっている。仮想マシンの IP を確認する。

$ docker-machine ip default
192.168.99.100

192.168.99.100 のポート 32769 に SSH で接続すればコンテナにつながる。

$ ssh -p 32769 penguin@192.168.99.100
The authenticity of host '[192.168.99.100]:32769 ([192.168.99.100]:32769)' can't be established.
ECDSA key fingerprint is SHA256:LjKzt2jnfQz5kSWAmCdTQmEZEX05iEYN7ee7lxQptOU.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.99.100]:32769' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 14.04 LTS (GNU/Linux 3.19.0-30-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

penguin@penguin-machine:~$

ここでユーザー作成時にユーザーシェルを指定していない場合、sh が使われる。useradd でシェルを指定しているのはこのためである。

ポートは次のように指定することもできる。

docker run -u penguin -h penguin-machine -v //c/Users/`whoami`:/home/penguin/user -p 32779:22 -it myubuntu

指定したポートになっているのがわかる。

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                   NAMES
8d2b1d11180a        myubuntu            "/tmp/script.sh"    32 seconds ago      Up 32 seconds       0.0.0.0:32779->22/tcp   jolly_fermat

注意: いろいろやっているうちにエラーが出て接続できなくなることがある。その場合、~/.ssh/known_hosts を消せば解決するかもしれない。

参考: DockerコンテナのLinux GUIアプリをWindowsで起動する (Qiita)

SSH サーバーのバックグラウンド実行

SSH を使うことにすれば、bash を実行する必要はない。SSH サーバーだけ立てればよいのだが、かといって bash を起動しないと、コンテナがすぐに終了してしまう。これは、コンテナがフォアグラウンド実行のコマンドを前提としているからである。フォアグラウンドでじっとしてさえいればよいので、苦肉の策として、tail を使う。また、bash を使わないなら一般ユーザーで実行する必要もないので (一般ユーザーだと sudo でパスワードを聞かれるので)、ユーザー指定なしで (root で) コンテナを起動する。

docker run -h penguin-machine -v //c/Users/`whoami`:/home/penguin/user -p 32779:22 -d myubuntu

script.sh

#!/bin/sh
service ssh start

mkdir /home/penguin/.ssh
cat /home/penguin/user/.ssh/id_rsa.pub >> /home/penguin/.ssh/authorized_keys
chown -R penguin.penguin /home/penguin/.ssh

tail -f /dev/null

ここで "docker run" のオプションに "-it" ではなく "-d" を使っている。これはコンテナをバックグラウンド実行させるものである。この場合、winpty は必要ない。

参考: Dockerfileを書く時の注意とかコツとかハックとか

X を使う

SSH がつながったところだが、まだ X は使えない状態である。Windows で X を使うには、以下のような方法がある。

  • VcXsrv のような X サーバーを用いる。
  • MobaXterm を用いる。

MobaXterm は X サーバー内蔵の SSH クライアントであり、これでコンテナにつなげば何の問題もなく X が使える。ここではあえて VcXsrv を用いる方法を採る。

VcXsrv は ここ から入手する。VcXsrv を起動すると、画面右下の通知領域にアイコンが表示される。それを右クリックして、"Show log" を選択する。表示されるログの中に、DISPLAY 変数の値が書いてある。

Welcome to the VcXsrv X Server
Vendor: The VcXsrv Project
Release: 1.17.2.0

...

winClipboardThreadProc - DISPLAY=127.0.0.1:0.0

これを環境変数に設定してから、オプション "-X" 付きで SSH を実行する。

$ export DISPLAY=127.0.0.1:0.0
$ ssh -X -p 32779 penguin@192.168.99.100

試しに xterm を入れて実行してみる。

$ sudo apt-get install xterm
$ xterm

Windows における Linux コマンドの実行

Linux にログインしてシェル上でコマンドを実行するのではなく、Windows のコマンドラインで Linux コマンドを実行したい。SSH はもともとコマンドを実行できるし、Windows のフォルダは見えているのだから、パスを変換することでカレントのファイルに Linux コマンドを適用することが可能だろう。以下のようなスクリプトでよいかもしれない。

ssh_exec

#!/bin/sh

if [ $# -lt 1 ]; then
    echo "usage: ssh_exec <command>"
    exit 0
fi

LOCAL_USER_DIR=/c/Users/`whoami`
REMOTE_USER_DIR=/home/penguin/user

PWD=`pwd | sed -e "s%$LOCAL_USER_DIR%$REMOTE_USER_DIR%"`

export DISPLAY=127.0.0.1:0.0
ssh -X -p 32779 penguin@192.168.99.100 ". ~/.bashrc; cd $PWD; $*"

ここで、~/.bashrc を読み込ませているのは、この中にパスなどの設定がある場合に対応するため。

テスト

$ ./ssh_exec cat ssh_exec
Warning: No xauth data; using fake authentication data for X11 forwarding.
#!/bin/sh

if [ $# -lt 1 ]; then
    echo "usage: ssh_exec <command>"
    exit 0
fi

LOCAL_USER_DIR=/c/Users/`whoami`
REMOTE_USER_DIR=/home/penguin/user

PWD=`pwd | sed -e "s%$LOCAL_USER_DIR%$REMOTE_USER_DIR%"`

export DISPLAY=127.0.0.1:0.0
ssh -X -p 32779 penguin@192.168.99.100 ". ~/.bashrc; cd $PWD; $*"

実は docker のコマンドでも同じことができる。"docker exec <container>" でコンテナにコマンドを実行させることができる。

cnt_exec

#!/bin/sh

if [ $# -lt 1 ]; then
    echo "usage: cnt_exec <command>"
    exit 0
fi

LOCAL_USER_DIR=/c/Users/`whoami`
REMOTE_USER_DIR=/home/penguin/user

PWD=`pwd | sed -e "s%$LOCAL_USER_DIR%$REMOTE_USER_DIR%"`

docker exec penguin-machine bash -c "~/.bashrc; cd $PWD; $*"

ここで、コンテナ名を "penguin-machine" と想定している。

テスト

$ ./cnt_exec cat cnt_exec
bash: /home/penguin/.bashrc: Permission denied
#!/bin/sh

if [ $# -lt 1 ]; then
    echo "usage: cnt_exec <command>"
    exit 0
fi

LOCAL_USER_DIR=/c/Users/`whoami`
REMOTE_USER_DIR=/home/penguin/user

PWD=`pwd | sed -e "s%$LOCAL_USER_DIR%$REMOTE_USER_DIR%"`

docker exec penguin-machine bash -c "~/.bashrc; cd $PWD; $*"

X を使うにはどうせ SSH を使わざるを得ないし、SSH のほうでよいかと。

パスを通す

スクリプトのパスを通すには、つぎのようにする。ユーザーフォルダに .profile というファイルを以下の内容で作成する。

. ~/.bashrc
また、ユーザーフォルダの .bashrc に以下の行を追加する。
export PATH=~/docker:$PATH

ここで、スクリプトの置場をユーザーフォルダの "docker" フォルダとしている。

アイコンの作成

アイコンから起動できるようにする。コンテナの起動用と SSH 接続用の 2 つを作る。"Docker Quickstart Terminal" を 2 つコピーして、たとえば "run container"、"terminal" などという名前にする。

"Docker Quickstart Terminal" は "C:\Program Files\Docker Toolbox\start.sh" を bash により実行している。このファイルをコピーして "start_docker.sh" などという名前にする。エディタで開いて、最後の exec の行をコメントにし、コンテナ実行用スクリプトを指定する。

#exec "$BASH" --login -i
docker/run

"run container" のリンク先を "start_docker.sh" に修正する。

SSH のほうは、つぎのようなスクリプトを用意する。

start_ssh.sh

#!/bin/sh
set -e
cd
docker/ssh_container

ここで ssh_container は SSH 接続用スクリプトである。

"terminal" のリンク先を "start_ssh.sh" に修正する。

コンテナの作成と再開の切り替え

コンテナは最初の実行時に作成され、Docker 仮想マシンを停止しても残る。ただし、仮想マシン再起動時にはコンテナは停止しているので、再開する必要がある。したがって、パソコンを起動後コンテナを使おうとする場合、コンテナがなければ "docker run" でコンテナを作る必要があるが、すでにコンテナがあるなら "docker start" で再開させればよい。(コンテナ名が指定された) コンテナがすでにあるのに "docker run" した場合、作成に失敗する。

コンテナの作成と再会を切り替えるために、起動スクリプトを次のようにしたらよいかもしれない。

EXIST=`docker ps -a -f "name=penguin-machine" | grep penguin-machine`

if [ "$EXIST" = "" ]; then
    echo "Create container"
    docker run -h penguin-machine --name penguin-machine -v //c/Users/`whoami`:/home/penguin/user -p 32779:22 -d myubuntu
else
    echo "Start container"
    docker start penguin-machine
fi

ここではコンテナ名を "penguin-machine" としている。"docker ps" でその名前のコンテナがあるか調べて、コマンドを切り替えている。

ホームディレクトリを共有フォルダにする?

ホームディレクトリに Windows 共有フォルダを置くぐらいなら、ホームディレクトリ自体を共有フォルダにしたらどうか、と思って試してみたが、SSH がスムーズにつながらなかったのでやめた。

CPU 数とメモリサイズ

コンテナの CPU 数とメモリサイズは、Docker 仮想マシンの設定に従う。設定を変えたい場合は、VirtualBox を起動して、仮想マシンをシャットダウンし (メニュー[仮想マシン]-[閉じる]-[ACPI シャットダウン])、[設定] の [システム] で設定を行う。

時計がずれる場合

コンテナと Windows の時計がずれている場合、ずれているのは Windows のほうの時計のはずなので、Windows のほうの時計を時刻サーバーと同時させる。

Ubuntu の日本語化

Ubuntu を日本語化するには、Dockerfile に次のように書く。

RUN sudo apt-get -y install language-pack-ja
RUN update-locale LANG=ja_JP.UTF-8
ENV LANG ja_JP.UTF-8

参考: Ubuntu 14.04 LTS: 日本語環境にする (Server World)

参考