猫好きモバイルアプリケーション開発者記録

SSHを使ってWindowsからDockerコンテナ内のコマンドを直接実行する

| Comments

Dockerを使い始めて最初にひっかかるポイントとしては「WindowsにはDockerクライアントがない」という点です。 それは boot2docker を導入したところで同じで、Windows上では docker コマンドを結局直接実行することができません。 どの記事をみても、Windowsで利用する場合は boot2docker ssh してゲストOS(dockerコマンドを実行するための軽量Linux OS)から実行するようなことが紹介されています。 (次期Windows Serverに docker.exe が付属することが最近発表されましたが、まだいつになるかわかりません)

これではせっかくdockerを導入しようにも「Macを利用しないといけないのか」というお話になり、 (Macにはdockerクライアントがあるので、dockerコマンドを直接OSX上で実行出来ます) 大多数いるWindowsユーザを考慮すると、結局開発環境が構築できないから利用をあきらめる、みたいになっている人も多いかもしれません。

もちろん、ApacheやNginxのようなサーバを立ち上げるという用途であるなら、
boot2docker sshしてゲストOS上でコマンドを叩いてサーバを立ち上げるような用途でも問題なくそのサーバへアクセス出来ますが、
問題はコンテナ上のコマンドを直接実行したい場合です。

例えば以前記事にさせて頂いた「Imlib2」は、Linux上でしか現状利用できません。
このようなミドルウェアを利用したいケースをカバーできるとしたら docker なわけですが、 Windows上でdockerコマンドを直接実行できなければWindowsでは利用出来ないのも同じです。

では、そのようなケースをカバーするにはどうすればいいのか。 それについてご紹介します。

boot2dockerのインストールとコマンド実行用イメージの作成

まずは boot2docker のインストールです。
インストールは公式サイトのとおり、セットアップ用のexeファイルをダウンロード&実行してインストールします。

インストール後に、コマンドプロンプトを立ち上げ、以下のコマンドを実行してゲストOSへログインします。

1
2
3
boot2docker init
boot2docker up
boot2docker ssh

ゲストOSへログインしましたら、イメージを作成します。
この辺りは詳しくは紹介しませんが、
例として以下のようなDockerfileを記述しておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
FROM tianon/centos:5.8

MAINTAINER kkoudev

# Install general commands
RUN yum groupinstall -y "Japanese Support"
RUN yum install -y lrzsz wget vim vim-enhanced openssh openssh-server openssh-clients man mlocate zip unzip rsync perl sysstat bzip2-devel
RUN yum install -y gcc gcc-c++ make cmake libtool patch ncurses-devel

# Lang & Locale
RUN echo 'LANG="ja_JP.UTF-8"' > /etc/sysconfig/i18n
RUN cp -f /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# Update
RUN yum update -y bash openssl

# Install EPEL
RUN rpm -ivh --force http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/x86_64/epel-release-5-4.noarch.rpm

# Install Tools
RUN yum --enablerepo=epel install -y git ImageMagick GraphicsMagick

# local ldconfig
RUN echo "/usr/local/lib" >> /etc/ld.so.conf.d/local.conf
RUN echo "/usr/local/lib64" >> /etc/ld.so.conf.d/local.conf
RUN /sbin/ldconfig

最新の boot2docker (現時点ではv1.3.1)であれば最初から C:\Users配下のディレクトリがゲストOSにマウントされており、 /c/Users といったパスでWindows側のファイルへアクセスすることが可能です。
そのため、今回は Dockerfileを作成した後に、C:\Users\Public\Tempのようなディレクトリを作成しておき、 そこにDockerfileを置いておきます。
その上で、以下のコマンドをゲストOSで実行して「test/command」というイメージを作成してみます。

1
2
cd /c/Users/Public/Temp
docker build -t test/command .

これで新しいイメージ「test/command」を作成できました。

SSHサーバを立ち上げる

Dockerクライアントが無いのであればどうすればいいのか、 といいますと、実は単純な話で、SSHサーバをコンテナ内で立ち上げてWindows側からリモートでコンテナに対してコマンドを実行すればいいわけです。 これであれば、Dockerクライアントがなくてもコマンドを実行できます。 WindowsでもGit for Windowsをインストールすると ssh のコマンドが付いてきますので、これを利用します。


01. SSH公開鍵と秘密鍵を作成する

まず、Git for Windowsをインストールします。
こちらも公式サイトからダウンロードし、全てデフォルトの設定のままインストールします。
インストール後、C:\Program Files (x86)\Git\bin を環境変数PATHへ登録しておきます。
PATHへ登録後、以下のコマンドをコマンドプロンプト上で実行します。

1
2
mkdir %USERPROFILE%¥.ssh
ssh-keygen -t rsa

パスワードは全て無しで公開鍵と秘密鍵を作成します。
すると、%USERPROFILE%¥.ssh ディレクトリに「id_rsa」と「id_rsa.pub」ファイルが作成されます。

02. SSH公開鍵の登録とSSHサーバの起動

SSH公開鍵を作成したあとは、作成した鍵をコンテナ内へインストールしてあげる必要があります。
そこで、まずは以下のコマンドをゲストOSで実行し、コンテナ内へログインします。

1
docker run -it -v /c/Users:/c/Users test/command /bin/bash

上記のコマンドでログインすると、コンテナ内でも /c/Users へアクセスできるようになっていますので、 %USERPROFILE%¥.ssh¥id_rsa.pubを以下のコマンドでインストールします。(%USERPROFILE%はWindowsの環境変数のため、当然コンテナ内からは使えないので以下のように直接ユーザディレクトリを指定します)

1
2
3
4
mkdir ~/.ssh
cat /c/Users/kkoudev/.ssh/id_rsa.pub >> authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

コンテナから exit でログアウトし、作業した内容を反映するために再度イメージを作り直します。

1
docker commit `docker ps -lq` test/command

これで作成した公開鍵をインストールしたイメージが作成されました。 準備は完了です。

コンテナ内でSSHサーバを立ち上げるには、以下のようにゲストOSにてdockerコマンドを実行します。

1
docker run -itd -p 22 -v /c/Users/Public/Temp:/HostTemp --name test-container test/command /usr/sbin/sshd -D

これで、コンテナ内の22番ポートが、Windows側の49153以上のポート番号へ割り当てられます。 実際にどのポート番号へ割り当てられたかは docker ps をしてみると、 「0.0.0.0:49153->22/tcp」と表示されるので、ここで判断します。

Windows側からコンテナに対してコマンドを実行してみる

いよいよ本番です。 Windows側からアクセスするには、
上記の手順で確認した割り当て先ポート番号(49153)と
ゲストOSのIPが必要になります。 ゲストOSのIPは以下のコマンドをコマンドプロンプトで実行することで確認できます。

1
boot2docker ip 2>nul

特にバッティング等していなければ 192.168.59.103 が返却されるかと思います。
そして、以下のようなバッチファイルを作成します。
(ここではファイル名を container_command.bat と仮定します)

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off

rem #---------------------------------------------------------
rem # 各種変数を定義する
rem #
rem # DOCKER_SSH_HOST : コンテナへのSSHアクセス用ホストIP
rem # DOCKER_SSH_PORT : コンテナへのSSHアクセス用ポート番号
rem #---------------------------------------------------------
set DOCKER_SSH_HOST=192.168.59.103
set DOCKER_SSH_PORT=49153

rem # コマンドを実行する
ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -o "LogLevel=QUIET" -i %USERPROFILE%\.ssh\id_rsa -p %DOCKER_SSH_PORT% root@%DOCKER_SSH_HOST% "%*"

これでコンテナに対してコマンドを実行する準備が出来ました。
試しにImageMagickを実行してみたいと思います。

ImageMagickはいわずもがな画像変換を行うツールですが、 コンテナ内で画像を変換してどのようにWindows側へ画像を返すのでしょうか。 そのためには、上記のSSHサーバ起動時にマウントした /c/Users/Public/Temp:/HostTemp のディレクトリを利用します。
つまり、C:\Users\Public\Temp に変換したい画像ファイルを予めコピーしておき、 コンテナ内では /HostTemp というパスで参照および書き込みを行うことで、変換結果をWindows側へ返すことが可能になるわけです。

このことを考慮した上で、Windows側で実際にコンテナ内のコマンドを実行してみます。 例として、Windows側のマウントディレクトリであるC:\Users\Public\Tempに sample.jpg を配置し、 これをコンテナ内のImageMagickを利用して PNG へ変換します。 さきほど作成した container_command.bat を使用し、以下のコマンドをWindows側のコマンドプロンプトで実行してみましょう。

1
container_command.bat convert /HostTemp/sample.jpg /HostTemp/sample.png

実行してみると、C:\Users\Public\Temp に sample.png が作成されていることがわかるかと思います。 当然ImageMagick以外のコマンドも実行することが可能です。

まとめ

このように、Windowsでも少し工夫をすることでコンテナ内のコマンドを簡単に実行できるようになります。 そのためこの方法を使えばLinuxでしか動かないImlib2のようなミドルウェアをWindowsで実行することが出来るようになります。

また、実際にこの方法を利用してプロジェクト等の開発環境を構築する場合は手動で行うと大変面倒なので、 接続に使用するSSH公開鍵をインストール済みのコンテナを用意し、 イメージの作成とSSHサーバの起動を自動的に行うスクリプトを作成することをオススメします。

一点注意として、SSHでリモートコマンドを実行している関係上、どうしても動作速度はネイティブでコマンドを実行するよりも遅くなってしまいます。 あくまで本番環境同等の機能をWindowsでも実現したい場合に利用する手段の1つという程度の認識にしておいた方が無難かと思われます。

Comments