ラベル Docker の投稿を表示しています。 すべての投稿を表示
ラベル Docker の投稿を表示しています。 すべての投稿を表示

8.23.2015

Running Apache Spark Cluster by using Kubernetes

Kubernetes を使って Spark クラスタを立ち上げる

 

環境

 

インストール

 

Kubernetes のインストール (NG)

適当なプロジェクト用ディレクトリに移動し、以下のコマンドを実行。

$ export KUBERNETES_PROVIDER=vagrant
$ curl -sS https://get.k8s.io | bash

早速コケた。

Downloading kubernetes release v1.0.3 to /proj/mogproject/example-spark/kubernetes.tar.gz
--2015-08-23 10:39:46--  https://storage.googleapis.com/kubernetes-release/release/v1.0.3/kubernetes.tar.gz
Resolving storage.googleapis.com... 216.58.220.176
Connecting to storage.googleapis.com|216.58.220.176|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 121767360 (116M) [application/x-tar]
Saving to: ‘kubernetes.tar.gz’

100%[===============================================================================>] 121,767,360 1.76MB/s   in 65s

2015-08-23 10:40:51 (1.80 MB/s) - ‘kubernetes.tar.gz’ saved [121767360/121767360]

Unpacking kubernetes release v1.0.3
Creating a kubernetes on vagrant...
Starting cluster using provider: vagrant
... calling verify-prereqs
... calling kube-up
Using credentials: vagrant:vagrant
Bringing machine 'master' up with 'virtualbox' provider...
Bringing machine 'minion-1' up with 'virtualbox' provider...
==> master: Box 'kube-fedora21' could not be found. Attempting to find and install...
    master: Box Provider: virtualbox
    master: Box Version: >= 0
Request for box's Amazon S3 region was denied.

This usually indicates that your user account with access key ID



is misconfigured. Ensure your IAM policy allows the "s3:GetBucketLocation"
action for your bucket:

    arn:aws:s3:::opscode-vm-bento

どうやら vagrant up コマンドに失敗している模様。
認証周りを管理しているプラグイン vagrant-s3auth をアンインストールしたら、先に進んだ。

$ vagrant plugin uninstall vagrant-s3auth

しかし、今度はこの画面から一向に進まない。

Waiting for each minion to be registered with cloud provider
..................................................................

1時間待っても、2時間待っても状況変わらず。

どうやら、こちらの certificate 関連の厄介な問題に直面したようだ。

解決策は、1個前のバージョンである v1.0.1 にこちらのパッチを適用し、手動でインストールせよ、とのこと。

 

Kubernetes のインストール (リトライ)

まずは VM を初期化。

$ cd kubernetes
$ vagrant destroy -f
$ vagrant box remove kube-fedora21
$ cd ..
$ rm -rf ./kubernetes

Version 1.0.1 を手動でダウンロードしてから、パッチを適用。その後、インストール。

$ curl -LO https://github.com/kubernetes/kubernetes/releases/download/v1.0.1/kubernetes.tar.gz
$ tar zxvf ./kubernetes.tar.gz
$ cd kubernetes/cluster/vagrant
$ curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.0/cluster/vagrant/provision-master.sh
$ curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.0/cluster/vagrant/provision-minion.sh
$ cd ../..
$ export KUBERNETES_PROVIDER=vagrant
$ ./cluster/kube-up.sh

10分ほどで正常終了。ここまで長かった。

Cluster validation succeeded
Done, listing cluster services:

Kubernetes master is running at https://10.245.1.2
KubeDNS is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/kube-dns
KubeUI is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/kube-ui

API との疎通確認

$ ./cluster/kubectl.sh get pods
NAME      READY     STATUS    RESTARTS   AGE

 

Spark クラスタの起動

Spark マスターサービスの起動

$ ./cluster/kubectl.sh create -f ./examples/spark/spark-master.json
$ ./cluster/kubectl.sh create -f ./examples/spark/spark-master-service.json

暫く経つと Running になる。

$ ./cluster/kubectl.sh get pods
NAME           READY     STATUS    RESTARTS   AGE
spark-master   0/1       Pending   0          1m
$ ./cluster/kubectl.sh get pods
NAME           READY     STATUS    RESTARTS   AGE
spark-master   1/1       Running   0          15m
$ ./cluster/kubectl.sh logs spark-master

Spark ワーカーの起動

$ ./cluster/kubectl.sh create -f examples/spark/spark-worker-controller.json

15分経っても、3個中2個は Pending のままだった。

$ ./cluster/kubectl.sh get pods
NAME                            READY     STATUS    RESTARTS   AGE
spark-master                    1/1       Running   0          34m
spark-worker-controller-6cgpd   0/1       Pending   0          15m
spark-worker-controller-tqa4b   0/1       Pending   0          15m
spark-worker-controller-yl7n2   1/1       Running   0          15m
$ ./cluster/kubectl.sh get services
NAME           LABELS                                    SELECTOR            IP(S)           PORT(S)
kubernetes     component=apiserver,provider=kubernetes                 10.247.0.1      443/TCP
spark-master   name=spark-master                         name=spark-master   10.247.70.164   7077/TCP
$ ./cluster/kubectl.sh get nodes
NAME         LABELS                              STATUS
10.245.1.3   kubernetes.io/hostname=10.245.1.3   Ready

 

Spark を使う

spark-master の IPアドレスとポートを確認。

$ ./cluster/kubectl.sh get service spark-master
NAME           LABELS              SELECTOR            IP(S)           PORT(S)
spark-master   name=spark-master   name=spark-master   10.247.70.164   7077/TCP

Kubernetes の minion-1 にログインし、適当な Docker コンテナを起動。
その中で、環境変数や /etc/hosts ファイルを書き換えるユーティリティ用スクリプト setup_client.sh を(同一シェル内で)実行。

[vagrant@kubernetes-minion-1 ~]$ sudo docker run -it gcr.io/google_containers/spark-base
root@49cc6b98cb5f:/# . /setup_client.sh 10.247.70.164 7077

Kubernetes 上の Spark クラスタへアクセスしていることを確認できた。

root@49cc6b98cb5f:/# spark-shell
15/08/23 11:04:18 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 1.4.0
      /_/

Using Scala version 2.10.4 (OpenJDK 64-Bit Server VM, Java 1.7.0_79)
Type in expressions to have them evaluated.
Type :help for more information.
Spark context available as sc.
15/08/23 11:04:31 WARN Connection: BoneCP specified but not present in CLASSPATH (or one of dependencies)
15/08/23 11:04:33 WARN Connection: BoneCP specified but not present in CLASSPATH (or one of dependencies)
15/08/23 11:04:43 WARN ObjectStore: Version information not found in metastore. hive.metastore.schema.verification is not enabled so recording the schema version 0.13.1aa
SQL context available as sqlContext.

scala> sc.isLocal
res0: Boolean = false

scala> sc.master
res1: String = spark://spark-master:7077

Python の API で、Spark ワーカーのホスト名を取得してみる。

root@49cc6b98cb5f:/# pyspark
Python 2.7.9 (default, Mar  1 2015, 12:57:24)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
15/08/23 11:12:06 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 1.4.0
      /_/

Using Python version 2.7.9 (default, Mar  1 2015 12:57:24)
SparkContext available as sc, HiveContext available as sqlContext.
>>> import socket
>>> sc.parallelize(range(1000)).map(lambda x:socket.gethostname()).distinct().collect()
['spark-worker-controller-yl7n2']

 

References

7.03.2014

Docker: docker-attach - Helper Script for Entering a Running Container

Docker: 起動しているコンテナの中に入るための便利スクリプト

 

Dockerコンテナ (libcontainer) に入るときは、ssh でも nsinit でもなく nsenter が一番いいらしい。

そのラッパーシェルを docker-attach という名前で書いていたが、自分が使いやすいようにバージョンアップしてみた。

 

リポジトリ

 

備考
  • Mac上でも boot2docker VM上でも同じシンタックスで利用可能
  • 事前に boot2docker VM上で セットアップ が必要
  • READMEはそのうち書く。リポジトリ構成も整理する予定

 

使い方

引数なしで実行
$ docker-attach
 NO.  CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
 [1]  77c4f673e22d        centos:latest       /bin/sleep 1000     6 seconds ago       Up 5 seconds                            insane_poincar
 [2]  bf55f24e0231        centos:latest       /bin/sleep 1000     7 seconds ago       Up 6 seconds                            goofy_darwin
 [3]  ae214f93be56        centos:latest       /bin/sleep 1000     8 seconds ago       Up 8 seconds                            furious_laland
 [4]  2af5192ee5a6        centos:latest       /bin/sleep 1000     10 seconds ago      Up 9 seconds                            mad_einstein
 [5]  ff2b4987e717        centos:latest       /bin/sleep 1000     11 seconds ago      Up 11 seconds                           agitated_meitn
 [6]  0b2168db31db        centos:latest       /bin/sleep 1000     13 seconds ago      Up 12 seconds                           focused_bell
 [7]  89bce218d973        centos:latest       /bin/sleep 1000     14 seconds ago      Up 14 seconds                           stoic_fermi
 [8]  d9767d3d8551        centos:latest       /bin/sleep 1000     16 seconds ago      Up 15 seconds                           insane_tesla
 [9]  ddd3b0e67a1c        centos:latest       /bin/sleep 1000     17 seconds ago      Up 17 seconds                           furious_meitne
 [10] d4eea7f883d6        centos:latest       /bin/sleep 1000     19 seconds ago      Up 18 seconds                           condescending_

Enter number (1-10): 5
-sh-4.1# hostname
ff2b4987e717
-sh-4.1# exit
logout

docker ps の結果とプロンプトが出るので、入りたいコンテナを数字で指定する。

コンテナIDを引数に指定して実行
$ docker-attach 77c4
-sh-4.1# hostname
77c4f673e22d
-sh-4.1# exit
logout

コンテナID(識別可能な先頭の数文字でよい)がわかっている場合は、直接そのコンテナの中に入れる。

コンテナIDとコマンドを引数に与えて実行
$ docker-attach bf55 cat /etc/redhat-release
CentOS release 6.5 (Final)

コンテナの中で指定したコマンドが実行される。

ヘルプ
$ docker-attach --help
Usage: /usr/local/bin/docker-attach [CONTAINER [COMMAND [CMD_ARGS...]]]

 

 

 

Related Posts

7.02.2014

Docker: How to Dive Into a Container on the boot2docker VM, Improved

Docker: boot2docker VM で起動中のコンテナの中に入ってシェルを立ち上げる方法, 改良版

 

boot2docker 上で起動したコンテナに入ってシェル操作を行う方法。

nsinit を使う よりも、nsenter の方が少ない手順とコストで実現できた。

 

環境

  • OS: Mac OS X 10.9 (Mavericks)
  • boot2docker: v1.0.1

 

リポジトリ

 

事前準備

  • VirtualBox のインストール
  • boot2docker 最新版のインストール (OS X インストーラ の利用を推奨: 詳細手順)
    • インストール前に VirtualBox の全ての VM、GUI を停止すること
    • 既に boot2docker VM が存在している場合は以下のコマンドを実行 (元の環境は削除される)
      $ boot2docker halt
      $ boot2docker delete
      $ boot2docker download

 

セットアップ

 

1. boot2docker VM の作成・ログイン
Mac$ boot2docker init
Mac$ boot2docker up
Mac$ boot2docker ssh

以下の手順は boot2docker VM 上で実行。

 

2. util-linux 拡張機能のダウンロード
docker@boot2docker:~$ tce-load -w util-linux
ncurses.tcz.dep OK
Downloading: udev-lib.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
udev-lib.tcz         100% |*********************************************************************************************| 45056   0:00:00 ETA
udev-lib.tcz: OK
Downloading: ncurses-common.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
ncurses-common.tcz   100% |*********************************************************************************************| 12288   0:00:00 ETA
ncurses-common.tcz: OK
Downloading: ncurses.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
ncurses.tcz          100% |*********************************************************************************************|   148k  0:00:00 ETA
ncurses.tcz: OK
Downloading: util-linux.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
util-linux.tcz       100% |*********************************************************************************************|   992k  0:00:00 ETA
util-linux.tcz: OK

/tmp/tce/optional (/mnt/sda1/tmp/tce/optional) 配下にファイルがダウンロードされる。

 

3. util-linux 拡張機能 ファイルの永続化

先ほどダウンロードしたファイル群のある optional ディレクトリごと、永続ディスクである /var/lib/boot2docker 配下に移動する。

docker@boot2docker:~$ sudo mv /tmp/tce/optional /var/lib/boot2docker/

 

4. docker-attach スクリプトのダウンロード

コンテナにアタッチするための便利シェル、docker-attach を /var/lib/boot2docker/bin 配下にダウンロードする。

docker@boot2docker:~$ sudo mkdir -p /var/lib/boot2docker/bin
docker@boot2docker:~$ sudo curl -o /var/lib/boot2docker/bin/docker-attach \
https://raw.githubusercontent.com/mogproject/docker-attach/master/docker-attach
docker@boot2docker:~$ sudo chmod +x /var/lib/boot2docker/bin/docker-attach
  • スクリプトの中身は、コマンドライン引数として渡されたコンテナIDからプロセスIDを検索して
    そこにオプション付きの nsenter を実行しているのが主処理。
    CONTAINER_ID=$1
    PID=$(docker inspect --format '{{.State.Pid}}' $CONTAINER_ID)
    sudo nsenter --mount --uts --ipc --net --pid --target $PID

 

5. bootlocal.sh スクリプトのダウンロードと実行

boot2docker VM は起動時に /var/lib/boot2docker/bootlocal.sh が存在すれば、それを実行する。

このシェルの中で util-linux のインストールとコマンドのリンクを行い、boot2docker 再起動後も元の状態を復元できるようにしている。

docker@boot2docker:~$ sudo curl -o /var/lib/boot2docker/bootlocal.sh \
https://raw.githubusercontent.com/mogproject/docker-attach/master/bootlocal.sh
docker@boot2docker:~$ sudo chmod +x /var/lib/boot2docker/bootlocal.sh
docker@boot2docker:~$ sudo /var/lib/boot2docker/bootlocal.sh
boot2docker: 1.0.1
             master : cad5ece - Fri Jun 20 02:03:40 UTC 2014
udev-lib.tcz: OK
ncurses-common.tcz: OK
ncurses.tcz: OK
util-linux.tcz: OK

 

実行例

  • 適当なコンテナを起動し、コンテナIDを確認 (boot2docker上でも、Macからでも可)
    $ docker run -d -t centos /bin/sleep 1000
    $ docker run -d -t centos /bin/sleep 1000
    $ docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    f8c149734c7a        centos:latest       /bin/sleep 1000     5 minutes ago       Up 2 minutes                            naughty_sinoussi
    701810969c35        centos:latest       /bin/sleep 1000     5 minutes ago       Up 2 minutes                            sleepy_bell
    
  • boot2docker上で docker-attach を実行してコンテナにアタッチ
    docker@boot2docker:~$ docker-attach 701810969c35
    root@701810969c35:/# cat /etc/redhat-release
    CentOS release 6.5 (Final)
    root@701810969c35:/# exit
    logout
    docker@boot2docker:~$ 
    
  • Macからも、同じ docker-attach スクリプトを使って直接アタッチ可能
    Mac$ sudo curl -o /usr/local/bin/docker-attach \
    https://raw.githubusercontent.com/mogproject/docker-attach/master/docker-attach
    Mac$ sudo chmod +x /usr/local/bin/docker-attach
    Mac$ docker-attach f8c149734c7a
    -sh-4.1# cat /etc/redhat-release
    CentOS release 6.5 (Final)
    -sh-4.1# exit
    logout
    Mac$
    

 

ワンライナーでのセットアップ

セットアップ用のシェルも作った。

docker-attach/setup_boot2docker.sh at master · mogproject/docker-attach

  • boot2docker上で
    $ curl https://raw.githubusercontent.com/mogproject/docker-attach/master/setup_boot2docker.sh | /bin/sh
    
  • または、Mac上で
    $ boot2docker ssh '/bin/sh -c "curl https://raw.githubusercontent.com/mogproject/docker-attach/master/setup_boot2docker.sh | /bin/sh"'
    

 

References

6.24.2014

Docker: How to Run 'nsinit' in the boot2docker VM

Docker: boot2docker VM の中で nsinit を実行する方法

 

2014-07-02 追記:
nsinit ではなく nsenter を使えば、同じことをよりシンプルに実現できました。

参考: docker - nsinit が上手く行かなくて困っていたら nsenter があまりにもあっさりだった件 - Qiita

環境

  • OS: Mac OS X 10.9 (Mavericks) (boot2docker が動作すれば他の環境でも手順は同じはず)
  • boot2docker: v1.0.1

 

事前準備

  • VirtualBox のインストール
  • boot2docker 最新版のインストール (OS X インストーラ の利用を推奨: 詳細手順)
    • インストール前に VirtualBox の全ての VM、GUI を停止すること
    • 既に boot2docker VM が存在している場合は以下のコマンドを実行 (元の環境は削除される)
      $ boot2docker halt
      $ boot2docker delete
      $ boot2docker download

 

作業手順

 

boot2docker VM の作成・ログイン
Mac$ boot2docker init
Mac$ boot2docker up
Mac$ boot2docker ssh
GCC + golang コンパイル環境の準備
docker@boot2docker:~$ tce-load -wil compiletc go
nsinit のビルド
docker@boot2docker:~$ sudo GOPATH=$HOME/go go get github.com/docker/libcontainer/nsinit
docker@boot2docker:~$ sudo cp -f $HOME/go/bin/nsinit /usr/local/bin/

これで /usr/local/bin/nsinit が作成される。

2014-07-02 追記
nsinit リポジトリの状況によって、ここでハマるケースがいくつか報告されています。

やはり nsenter の利用をお勧めします。

参考: RHEL/CentOS 6で Docker に nsinit/nsenter する - Qiita

 

コンテナアタッチ用 便利シェルの作成

以下の内容で /usr/local/bin/docker-attach というシェルを書き、実行権限を付ける。

#!/bin/sh
[[ -n "$1" ]] || { echo "Usage: `basename $0` <partial container ID or name>"; exit 0 ; }
FULL_ID=$(docker inspect --format='{{.Id}}' $1)
sudo /bin/sh -c "cd /var/lib/docker/execdriver/native/$FULL_ID && nsinit exec bash"
コマンドの永続化

このままだと、nsinit, docker-attach は boot2docker VM を停止すれば消滅してしまう。

永続化されるディスクの /var/lib/boot2docker の中にコマンドを移動し、bootlocal.sh を使って起動時に再配備されるようにする。

docker@boot2docker:~$ sudo mkdir /var/lib/boot2docker/bin
docker@boot2docker:~$ sudo mv -i /usr/local/bin/nsinit /usr/local/bin/docker-attach /var/lib/boot2docker/bin/

/var/lib/boot2docker/bootlocal.sh を以下の内容で作成し、実行権限を付与。

#!/bin/sh
ln -s /var/lib/boot2docker/bin/nsinit /usr/local/bin/nsinit
ln -s /var/lib/boot2docker/bin/docker-attach /usr/local/bin/docker-attach

シェルを実行してリンクを張り直す。

docker@boot2docker:~$ sudo /var/lib/boot2docker/bootlocal.sh

 

稼働中の Docker コンテナの中に入る例

 

適当なコンテナの起動

任意のコンテナをデーモンモードで起動し、コンテナIDを確認

docker@boot2docker:~$ docker run -d -t centos /bin/sleep 1000
af1ebb57b1cbd0c1f3e02e3f164deb30d2b2ead95f5291545c757c5d33ac6ef7
docker@boot2docker:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
af1ebb57b1cb        centos:latest       /bin/sleep 1000     3 seconds ago       Up 3 seconds                            jolly_poincare
コンテナにアタッチして bash で操作
docker@boot2docker:~$ docker-attach af1e
bash-4.1# cat /etc/redhat-release
CentOS release 6.5 (Final)
bash-4.1# exit
exit

sshd なしで、稼働中のコンテナで bash を扱うことができた。

 

ワンライナーでセットアップする

コピペも面倒、という人向け(自分も含む)にセットアップ用のシェルを書いた。

実行例

docker@boot2docker:~$ curl https://raw.githubusercontent.com/mogproject/docker-attach/scripts-for-nsinit/setup_boot2docker.sh |/bin/sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2552  100  2552    0     0   7973      0 --:--:-- --:--:-- --:--:--  9522
2014-06-24 15:59:05 [INFO] Script started.
2014-06-24 15:59:05 [INFO] Creating directory /var/lib/boot2docker/bin
2014-06-24 15:59:05 [INFO] Installing compilers
gcc.tcz.dep OK
gcc_libs-dev.tcz.dep OK
cloog.tcz.dep OK

(中略)

Downloading: compiletc.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
compiletc.tcz        100% |*********************************************************************************************|  4096   0:00:00 ETA
compiletc.tcz: OK
Downloading: go.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
go.tcz               100% |*********************************************************************************************| 36288k  0:00:00 ETA
go.tcz: OK
2014-06-24 16:02:15 [INFO] Building /var/lib/boot2docker/bin/nsinit
2014-06-24 16:03:19 [INFO] Downloading /var/lib/boot2docker/bin/docker-attach
2014-06-24 16:03:19 [INFO] Downloading /var/lib/boot2docker/bootlocal.sh
2014-06-24 16:03:20 [INFO] Linking commands in /usr/local/bin
2014-06-24 16:03:20 [INFO] Script ended successfully.

 

 

References

5.31.2014

Docker: Python: Getting Internal IP Address of the Container

コンテナを Detached Mode で起動して、その内部アドレスを知る一行野郎 (ワンライナー)

grep や jq でもできるけど、Python で JSON をパーズするほうが好み。

docker inspect $(docker run -d IMAGE_NAME) | \
python -c 'import json,sys;print json.loads(sys.stdin.read())[0]["NetworkSettings"]["IPAddress"]'

 

実行例

Docker の動作するホスト上で実行

# docker inspect $(docker run -d mogproject/sshd) | python -c 'import json,sys;print json.loads(sys.stdin.read())[0]["NetworkSettings"]["IPAddress"]'
Unable to find image 'mogproject/sshd' locally
Pulling repository mogproject/sshd
db38b1b40c65: Download complete
539c0211cd76: Download complete
154a1496c839: Download complete
0e8300b2772a: Download complete
43af4236d0da: Download complete
fc0cc09b2562: Download complete
172.17.0.2
# ssh ssh-user@172.17.0.2
The authenticity of host '172.17.0.2 (172.17.0.2)' can't be established.
RSA key fingerprint is e7:77:e5:bc:b0:3e:c4:78:27:cf:57:3a:2e:98:69:30.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.17.0.2' (RSA) to the list of known hosts.
ssh-user@172.17.0.2's password:
[ssh-user@1e53a210ba41 ~]$ cat /etc/redhat-release
CentOS release 6.4 (Final)

5.27.2014

Docker: Pull From the Private Registry Failed with I/O Timeout

Docker: プライベートレジストリからの docker pull でタイムアウトエラー

 

Mac + boot2docker 環境で、プライベートリポジトリから docker pull した時に
タイムアウトになってしまう問題。

$  docker -H tcp://127.0.0.1:4243 pull my-private-reg:5000/image_name:base
2014/05/27 00:59:47 Error: Invalid Registry endpoint: Get http://my-private-reg:5000/v1/_ping: dial tcp: i/o timeout

どうやら、名前解決で時間がかかっているようだった。(5秒以上)

以下いずれかの対応でとりあえずタイムアウトは回避できた。

  • 1. boot2docker-vm 上 (Mac上ではない!) の /etc/hosts にホストを登録し、ローカルで名前解決
  • 2. docker pull の際にプライベートリポジトリの IPアドレスを直接指定

 

追記:
よく見たら boot2docker-vm 上の /etc/resolv.conf 先頭に到達不能な IPアドレスが記述されていた。
boot2docker 起動時に Mac の設定情報をコピーするようなので、IPアドレスの変わりやすい ノートPC 利用時などでは特に注意が必要。

4.07.2014

Docker: b2d-pipework - Installation Helper Script

b2d-pipework - 固定IPアドレス持ちDockerコンテナ環境構築のためのヘルパースクリプト

 

Mac + boot2docker + pipework を導入するための一連の作業をシェルスクリプトにした。

たとえば、インストール処理は以下のような流れ。

  • Homebrew を利用して docker および boot2docker パッケージの最新版をインストール
  • あらかじめ作成した profile ファイルに従って boot2docker VM (VirtualBox) を作成
  • SSH鍵($HOME/.boot2docker/id_rsa)を新規作成し、boot2docker VM にパスワードなしでログイン可能に
  • boot2docker VM 内に最新版の pipework をインストール
  • 192.168.99.0/24 のアドレスで、VirtualBox ホストオンリーネットワークを構築

ドキュメントは今のところ日本語のみ。 

 

 

Related Posts

3.24.2014

Docker: How the networking of Mac + boot2docker + Pipework works

Docker: Mac + boot2docker + Pipework で構築した環境のネットワーク周りについて

 

自分自身の理解のためにネットワーク構成を可視化してみる。

 

環境

  • OS: Mac OS X 10.9 (Mavericks)
  • VirtualBox: 4.3.6
  • boot2docker: v0.7.0 (Docker 0.9.0 を含む)
  • jpetazzo/pipework (最終コミット: 2014-03-15 時点)

 

構成イメージ

 

1. boot2docker インストール直後

Docker の内部ネットワークとして 172.17.0.0/16 のIPアドレスが各コンテナに割り当てられるが、
静的なアドレスを指定することはできない。
また、Macから直接そのIPアドレスへは到達できないので、VirtualBox と Docker の二段階の
ポートフォワーディングを経由してアプリケーションへアクセスするのが一般的である。

コンテナ間通信可否については、Docker デーモンの -icc オプション、および
コンテナ起動時の --link オプションによって制御可能。

1

2-1. Pipework でブリッジを作成

boot2docker VM上で Pipework を実行し、各コンテナをブリッジインタフェース br1 に対応させたところ。
コンテナ内部に eth1 インタフェースが作成され、指定した IPアドレスが割り振られる。

これで固定のIPアドレスを使ったコンテナ間通信が可能になる。
ただし、boot2docker VM からそのアドレスへのアクセスはまだできない。

以下は、コンテナに 192.168.99.0/24 ネットワークのアドレスを割り当てる例。

2 1

2-2. ブリッジにIPアドレスを割り当てる

boot2docker VM 上で、ブリッジに接続するためのIPアドレスを割り振れば、
boot2docker VM から各コンテナに固定アドレスを使った接続が可能になる。

ただし、この方法では Mac から直接コンテナにアクセスすることはできない。

2 2

3-1. VirtualBox のホストオンリーアダプタを作成する

VirtualBox の機能でホストオンリーネットワークを作成し、boot2docker VM のアダプタにも静的なIPアドレスを
設定する。

これで、Mac から boot2docker VM へ固定のIPアドレスでアクセスできるようになる。

3 1

3-2. ホストオンリーアダプタに対して Pipework を実行する

さいごに、boot2docker のホストオンリーネットワーク用アダプタ eth1 に対して Pipework を実行する。
ブリッジインタフェース以外に対して Pipework を実行すると、macvlan ブリッジが設定されるようだ。 

このようなネットワーク構成であれば、もはや VirtualBox + Docker の多段ポートフォワーディングは必須でない。

3 2

 

 

 

Related Posts

 

References

3.23.2014

Docker: boot2docker SSH Login without Password

Docker: boot2docker VM にパスワード無しで SSH 接続する方法

 

通常の Linux サーバ同様、ssh-keygen コマンドで作ったキーペアの公開鍵をリモートサーバ上に置けば
boot2docker VM に対してもノンパスワードで接続できる。

ただし、例によって boot2docker を再起動しても設定が永続化されるようにするためには
少々の細工が必要となる。

 

作業手順

 

1. 秘密鍵、公開鍵のペアを作成

キーペアを新規に作成し、~/.boot2docker/id_rsa として保存する例。

Mac$ ssh-keygen -t rsa -f $HOME/.boot2docker/id_rsa

プロンプトが出るのでパスフレーズを2回入力。(空でもよい)

 

2. 公開鍵を boot2docker VM の中に置く

公開鍵を永続化するため、/home/docker などではなく、/var/lib/boot2docker/keys というディレクトリを作って
そこに配備することにする。

(ちなみに /var/lib/boot2docker/ssh という既存のディレクトリには sshd サーバの設定情報が保存されている)

Mac$ scp -P 2022 ~/.boot2docker/id_rsa.pub docker@localhost:
docker@localhost's password:    ### tcuser と入力
Mac$ boot2docker ssh
docker@localhost's password:    ### tcuser と入力
boot2docker$ sudo mkdir /var/lib/boot2docker/keys
boot2docker$ sudo mv -i ./id_rsa.pub /var/lib/boot2docker/keys/

 

3. VM起動時に実行されるシェルスクリプトを記述する

もちろん、このままではSSH鍵を使ったログインを行うことはできない。

以下にような内容の /var/lib/boot2docker/bootlocal.sh を作成して、authorized_keys ファイルを動的に生成する。
このシェルスクリプトはVM起動時に自動的に実行される。

boot2docker$ sudo vi /var/lib/boot2docker/bootlocal.sh
boot2docker$ cat /var/lib/boot2docker/bootlocal.sh
#!/bin/sh

mkdir -p /home/docker/.ssh
cat /var/lib/boot2docker/keys/*.pub > /home/docker/.ssh/authorized_keys
boot2docker$ sudo chmod +x /var/lib/boot2docker/bootlocal.sh
boot2docker$ exit

 

4. boot2docker の再起動と接続確認

これで、boot2docker を再起動し ssh コマンドに秘密鍵のパスを渡せば、
ノンパスワードでのログインができるはずだ。

Mac$ boot2docker restart
Mac$ boot2docker ssh -i ~/.boot2docker/id_rsa

問題が発生した場合は、boot2docker ssh -v コマンド(-vvv でも可)で調査。

 

作業シェル記述例

楽をするため、この一連の作業をシェルスクリプトで書く。

適当な作業ディレクトリで実行。

#!/bin/bash

KEY_NAME=id_rsa
PRIVATE_KEY=$HOME/.boot2docker/$KEY_NAME
PUBLIC_KEY=$PRIVATE_KEY.pub
BOOTLOCAL_TMP=/tmp/bootlocal.sh

# Create SSH key pair
if [[ ! -f $PRIVATE_KEY ]]; then
    mkdir -p $HOME/.boot2docker
    ssh-keygen -t rsa -f $PRIVATE_KEY
fi

# Create bootlocal.sh
cat <<EOF > $BOOTLOCAL_TMP
#!/bin/sh

mkdir -p /home/docker/.ssh
cat /var/lib/boot2docker/keys/*.pub > /home/docker/.ssh/authorized_keys
EOF

# Upload public key and bootlocal.sh to VM
boot2docker start
scp -P 2022 $PUBLIC_KEY $BOOTLOCAL_TMP docker@localhost:

# Install files in VM
boot2docker ssh sudo install -D -o root -g root -m 644 ./$KEY_NAME.pub /var/lib/boot2docker/keys/
boot2docker ssh sudo install -o root -g root -m 755 ./bootlocal.sh /var/lib/boot2docker/

# Clean bootlocal.sh
rm -f $BOOTLOCAL_TMP

# Restart boot2docker
boot2docker restart

途中でプロンプトが何回か出るので、その都度、手で入力すること。(改善の余地あり)

 

 

 

Related Posts

3.22.2014

How to Connect Directly to the Docker Container on boot2docker with Static IP Address

Docker: boot2docker 上の Docker コンテナに狙ったIPアドレスで直接接続する方法

 

boot2docker を導入した Mac OS 環境で {Mac, boot2docker VM, Dockerコンテナ} の三者を
同一のネットワークに所属させ、それぞれに対して固定IPアドレスでアクセスできるようにしたい。

ただし pipework の README にもあるように、闇雲にこの方式を採用するのはお勧めしない。

 

環境

  • OS: Mac OS X 10.9 (Mavericks)
  • VirtualBox: 4.3.6
  • boot2docker: v0.7.0 (Docker 0.9.0 を含む)
  • jpetazzo/pipework (作業手順の中でダウンロードする)

 

事前準備

  • boot2docker のセットアップ
    boot2docker ssh が実行できる状態となっていること。
  • Mac から docker コマンドが利用できること
    Docker クライアントがインストールされていて、環境変数 DOCKER_HOST が
    tcp://127.0.0.1:4243 のように設定されていること
  • IP アドレス/ネットワークの決定
    VirtualBox のホストオンリーネットワーク上に静的に設定するIPアドレスを決める。
    今回の例では以下のように設定。
    • Gateway: 192.168.99.1/24
    • boot2docker VM (Dockerホスト): 192.168.99.2/24
    • Dockerコンテナ1: 192.168.99.101/24
    • Dockerコンテナ2: 192.168.99.102/24

作業手順

コマンドはすべて Mac上で実行するべく記述している。

1. boot2docker VM 起動時処理の追加

boot2docker では、Docker の実行環境それ自体が Docker コンテナとして構築されている。
そのため、boot2docker ssh でネットワーク周りの設定をしても、boot2docker VM を再起動すると
設定が初期化されてしまう。

/var/lib/boot2docker/bootlocal.sh を作成すれば、VM(の中のコンテナ?)起動時にこのシェルが実行される。
永続化したい処理はここに書くのがセオリーだ。

  • boot2docker 起動
    $ boot2docker up
  • pipework 最新版のダウンロード
    $ boot2docker ssh " \
    sudo mkdir -p /var/lib/boot2docker/bin && \
    sudo curl -o /var/lib/boot2docker/bin/pipework https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework && \
    sudo chmod +x /var/lib/boot2docker/bin/pipework"
    

    pipework の実行ファイル(実態はシェルスクリプト)を永続化対象の /var/lib/boot2docker/bin 配下に
    置き、VM 起動時に /usr/local/bin/pipework へシンボリックリンクを張る戦略。
  • bootlocal.sh の作成
    $ boot2docker ssh "sudo bash -c '
    echo -e \"ln -s /var/lib/boot2docker/bin/pipework /usr/local/bin/pipework
    \nip addr add 192.168.99.2/24 dev eth1
    \nip link set eth1 up\" > /var/lib/boot2docker/bootlocal.sh
    && chmod +x /var/lib/boot2docker/bootlocal.sh
    '"
    

    ここで、boot2docker VM のIPアドレスを指定する。

    実際の bootlocal.sh は以下のような内容になる。(shebang を付けたほうがいいかもしれない)

    ln -s /var/lib/boot2docker/bin/pipework /usr/local/bin/pipework
    ip addr add 192.168.99.2/24 dev eth1
    ip link set eth1 up

 

2. VirtualBox ホストオンリーアダプタの設定

VirtualBox の GUI でも操作可能だが、スクリプト化したいのでコマンドで実行してみる。

  • 目的に適したアダプタが既に存在するかどうか確認
    $ VBoxManage list -l hostonlyifs
    IPアドレス(192.168.99.1)、ネットワークマスク(255.255.255.0)が一致するものが存在していれば、
    そのアダプタを利用する。
  • アダプタが存在していなければ、新規作成
    $ VBoxManage hostonlyif create
    0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
    Interface 'vboxnet0' was successfully created
    $ VBoxManage hostonlyif ipconfig vboxnet0 --ip 192.168.99.1 --netmask 255.255.255.0
    $ VBoxManage list -l hostonlyifs
    
    表示されたインタフェース名に対して変更を施し、確認。
  • boot2docker 停止
    $ boot2docker stop
  • boot2docker VM にホストオンリーインタフェースを追加
    $ VBoxManage modifyvm boot2docker-vm --nic2 hostonly --hostonlyadapter2 vboxnet0
    $ VBoxManage modifyvm boot2docker-vm --nicpromisc2 allow-all
    先ほど作成したホストオンリーアダプタの名前(ここでは vboxnet0)を指定。
    nic1 は NATアダプタとして既に存在しているので、ここでは nic2 を作成する。
    Docker コンテナ間の通信を可能とするため、Promiscuous Mode の設定を変更する。
  • boot2docker 起動
    $ boot2docker up
  • 疎通確認
    $ ping 192.168.99.2
    $ ssh docker@192.168.99.2
    まずは boot2docker VM (Dockerホスト) に対して直接アクセスできるようになった。

 

3. コンテナの起動と pipework の実行

ここでは、CentOS 6.4 上で sshd が動くだけの Docker コンテナ (mogproject/sshd Repository | Docker Index) を
2つ立ち上げて、pipework の動作を確認してみる。

  • コンテナの起動
    $ CID1=$(docker run -d -t mogproject/sshd)
    $ CID2=$(docker run -d -t mogproject/sshd)
    $ docker ps
  • pipework の実行
    $ boot2docker ssh sudo pipework eth1 $CID1 192.168.99.101/24
    $ boot2docker ssh sudo pipework eth1 $CID2 192.168.99.102/24
    コンテナID と IPアドレスを指定し、boot2docker VM上のホストオンリーアダプタ(eth1)と対応付ける。
  • 疎通確認
    $ ping 192.168.99.101
    $ ping 192.168.99.102
    $ ssh ssh-user@192.168.99.101    ### パスワードも ssh-user
    [ssh-user@xxxxxxxxxxxx ~]$ ping 192.168.99.102
    [ssh-user@xxxxxxxxxxxx ~]$ exit
    $ ssh ssh-user@192.168.99.102
    [ssh-user@xxxxxxxxxxxx ~]$ ping 192.168.99.101
    [ssh-user@xxxxxxxxxxxx ~]$ exit
    
    Mac から Docker コンテナに直接アクセスできる。
    さらに、そのIPアドレスを使ってコンテナ間通信もできるようになった。

このように、pipework のおかげで Docker コンテナの活用の幅はますます広がりそうだ。

なお、pipework の設定は、コンテナが停止すると同時に削除される。

 

 

 

References

3.14.2014

Using Docker 0.9 in Mac

Mac で Docker 0.9 を使う

 

Mac 用に最適化された docker クライアントと boot2docker VM を使って Docker 0.9 環境を構築する。

2014-03-15 追記

すでに boot2docker が Docker 0.9 に対応された(Releases · boot2docker/boot2docker)ので
もはや下記手順は必要ない。

また、Homebrew でのインストールを半自動化するシェルを書いた。

 

環境

  • ホストOS: Mac OS X 10.9 (Mavericks)
  • VirtualBox: 4.3.6

 

事前準備

既に docker クライアントや boot2docker をインストールしてある場合は
不要な混乱を避けるため一旦削除しておく。

  • Homebrew でインストールした場合の例
$ brew install docker
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/docker-0.8.0.mavericks.bottle.tar.gz
Already downloaded: /Library/Caches/Homebrew/docker-0.8.0.mavericks.bottle.tar.gz
==> Pouring docker-0.8.0.mavericks.bottle.tar.gz
    /usr/local/Cellar/docker/0.8.0: 8 files, 18M
$ docker version
Client version: 0.8.0
Go version (client): go1.2
Git commit (client): cc3a8c8d8ec57e15b7b7316797132d770408ab1a

xxxx/xx/xx xx:xx:xx Can't connect to docker daemon. Is 'docker -d' running on this host?

現時点では、まだ 0.9 に対応していないようだった。

  • アンインストール
$ brew uninstall docker
$ brew uninstall boot2docker

 

手順

 

Docker OS X Client のインストール

以下の公式ドキュメントに従って作業。

適当な作業ディレクトリに移動して、シェルで以下のコマンドを実行する。
環境変数 DOCKER_HOST は今後も使い続けるのでシェルのrcスクリプトに書いておくと良い。

$ curl -o docker https://get.docker.io/builds/Darwin/x86_64/docker-latest
$ chmod +x docker
$ sudo mv -i docker /usr/local/bin/
$ export DOCKER_HOST=tcp://127.0.0.1:4243

確認

$ docker version
Client version: 0.9.0
Go version (client): go1.2.1
Git commit (client): 2b3fdf2
xxxx/xx/xx xx:xx:xx Cannot connect to the Docker daemon. Is 'docker -d' running on this host?

この時点ではサーバ接続が失敗する。

 

boot2docker のインストール

引き続き作業ディレクトリで実行。

$ curl https://raw.github.com/boot2docker/boot2docker/master/boot2docker > boot2docker
$ chmod +x ./boot2docker
$ sudo mv -i boot2docker /usr/local/bin/

起動とログイン

$ boot2docker init
$ boot2docker up
$ boot2docker ssh
docker@localhost's password:    <=== tcuser と入力
                        ##        .
                  ## ## ##       ==
               ## ## ## ##      ===
           /""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
           \______ o          __/
             \    \        __/
              \____\______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   <  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
boot2docker: 0.6.0
docker@boot2docker:~$ docker version
Client version: 0.8.1
Go version (client): go1.2
Git commit (client): a1598d1
Server version: 0.8.1
Git commit (server): a1598d1
Go version (server): go1.2
Last stable version: 0.9.0, please update docker

バージョン 0.8.1 だった。

  • SSHの問題が発生した場合は、「ssh -v -o StrictHostKeyChecking=no -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -p 2022 docker@localhost」といったコマンドで原因調査。

/etc/ssh_config に以下のようにパスワード認証を優先する設定を追加することで改善する場合がある。

PreferredAuthentications password,gssapi-with-mic,hostbased,publickey,keyboard-interactive
  • boot2docker コマンドには、b2d などのエイリアスを付けると便利だと思う

 

boot2docker のビルド

現時点(2014-03-13)では、boot2docker は Docker 0.9 に対応していない。

Issue もあるので近日中に対応するものと思われるが、今回は練習も兼ねて
自分でisoファイルをビルドし直して最新化してみる。

boot2docker 自体が Docker を利用しているので、先ほど導入した古い boot2docker 環境でビルドを行う。

$ boot2docker up
$ boot2docker ssh
docker@boot2docker:~$ git clone https://github.com/boot2docker/boot2docker.git
docker@boot2docker:~$ cd boot2docker
docker@boot2docker:~/boot2docker$ docker pull boot2docker/boot2docker:base
docker@boot2docker:~/boot2docker$ docker build -t boot2docker --rm rootfs/

docker@boot2docker:~/boot2docker$ docker rm build-boot2docker    # 未作成ならエラーとなるが問題なし
docker@boot2docker:~/boot2docker$ docker run --privileged -name build-boot2docker boot2docker
docker@boot2docker:~/boot2docker$ sudo docker cp build-boot2docker:/boot2docker.iso .
docker@boot2docker:~/boot2docker$ exit

docker pull は長時間(数分〜数十分)を要する場合がある。
rootfs のビルドの過程で最新版の docker プログラムが取得される。

ISO ファイルが出来上がったら、Mac上にコピーしてくる。
boot2docker-docker-0.9.iso とリネームして保存するなら以下のようなコマンドだ。

$ scp -P 2022 docker@localhost:boot2docker/boot2docker.iso ~/.boot2docker/boot2docker-docker-0.9.iso

 

boot2docker VM の再作成

先ほど作成したVMは一旦削除。

$ boot2docker stop
$ boot2docker delete

boot2docker 用の profile ファイルを書くことで ISOファイルのパス、VMのメモリサイズなどを指定できる。
今回はISOのパス変更のほか、メモリ容量を 2GB に増やしてみた。

$ vi ~/.boot2docker/profile
$ cat ~/.boot2docker/profile
VM_MEM=2048
BOOT2DOCKER_ISO=${BOOT2DOCKER_CFG_DIR}/boot2docker-docker-0.9.iso

VM作成

$ boot2docker init
$ boot2docker up
$ docker version
Client version: 0.9.0
Go version (client): go1.2.1
Git commit (client): 2b3fdf2
Server version: 0.9.0
Git commit (server): 2b3fdf2
Go version (server): go1.2.1

無事、Docker 0.9 の環境が Mac + boot2docker で使えるようになった。

最後にポートフォワーディングの設定を実施。

$ boot2docker stop
$ for i in {49000..49900}; do
   VBoxManage modifyvm "boot2docker-vm" --natpf1 "tcp-port$i,tcp,,$i,,$i";
   VBoxManage modifyvm "boot2docker-vm" --natpf1 "udp-port$i,udp,,$i,,$i";
  done
$ boot2docker start

設定には数分かかる。

運用にあたっては、コンテナ起動時に各種サービスポートをVM上の 49000-49900 ポートとバインドさせる。
その後 Mac から localhost:49000-49900 へアクセスすれば、コンテナの内部へ接続することが可能となる。

 

 

References

 

Related Posts

2.04.2014

Docker: Getting Started with boot2docker

Docker: 母艦 boot2docker に乗り換える

 

こちらのブログポストを読み、

Mac のローカルで docker run するためだけに CentOS を立ち上げるのは贅沢すぎると反省。
そして、母艦を boot2docker に鞍替え。

非常に軽快。
そして今まで使っていたコンテナをそのまま新しい母艦へ運搬する。

この感覚がとても新鮮。

2.02.2014

Docker: Creating Development Environment with SSH

Docker: SSH標準装備の開発環境コンテナイメージの作成

 

Docker コンテナ内で完結する開発環境を作って immutable infra を実現したいというのが動機。
コンテナ内の各種ログを確認したり、ちょっとしたファイルを転送したりするために SSH によるログインも可能としたい。 

 

ファイル構成

 

複数プロセスをまとめる Supervisor

Docker は 1つのアプリケーションを 1つのコンテナで動かすのが基本的な考え方。

複数のプロセスをまとめてデーモンとして動作させるためにはプロセス管理ツールが必要。
その現時点のデファクトスタンダードが Python で書かれたツール、Supervisor である。

 

ファイル内容

作業ディレクトリ配下に Dockerfile と supervisord.conf、そして必要であればミドルウェア等の設定ファイルを置く。

CentOS ベースのコンテナであれば、各ファイルの内容は以下のような構成になる。

  • Dockerfile

    ssh-user という OSユーザを作成し(パスワードも ssh-user)、sudo も可能にする。

    FROM centos:6.4
    
    #
    # SSH
    #
    
    RUN yum install -y --nogpgcheck openssh-server sudo
    RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key && ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key && sed -i 's/UsePAM.*/UsePAM no/g' /etc/ssh/sshd_config
    
    # create user
    RUN useradd ssh-user && echo 'ssh-user:ssh-user' | chpasswd && echo 'ssh-user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
    
    #
    # Supervisor
    #
    
    # enable EPEL repository
    RUN yum install -y --nogpgcheck http://dl.iuscommunity.org/pub/ius/stable/Redhat/6/x86_64/epel-release-6-5.noarch.rpm
    
    # install supervisord
    RUN yum install -y --nogpgcheck supervisor
    
    # settings
    ADD supervisord.conf /etc/supervisord.conf
    RUN mkdir -p /var/log/supervisor
    
    #
    # Other applications here!
    #
    
    EXPOSE 22
    CMD ["/usr/bin/supervisord"]
    
    

    尚、ADD コマンドの後は常に新しいイメージが生成されるようで、キャッシュが使えなくなる。
    Dockerfile を編集して何度もコンテナを作りたい場合は ADD を後回しにした方がいいかもしれない。

  • supervisord.conf
    [supervisord]
    nodaemon=true
    
    [program:sshd]
    command=/usr/sbin/sshd -D
    stdout_logfile=/var/log/supervisor/%(program_name)s.log
    stderr_logfile=/var/log/supervisor/%(program_name)s.log
    autostart=true
    autorestart=true
    

    起動するプロセスを増やしたい場合は、[program:xxx] を増やしてゆけばよい。
    sshd だけは autorestart を有効にした方がいいだろう。

 

Apache + MySQL + PHP 環境のサンプル

例えばお手軽な LAMP環境を使いたい場合は以下のような Dockerfile を作る。

ローカルのみの使用を前提としているので、セキュリティの制限は設けていない。

 

コンテナイメージの作成と起動

 

Docker ホスト SELinux の無効化

まず最初に、Dockerホスト側の SELinux を無効化(/etc/selinux/config -> SELINUX=disabled)する。

これをしないと Dockerコンテナ側で getenforce コマンドがエラーになってしまい、SSH 接続ができなくなる。

(getenforce の中で呼ばれている security_getenforce 関数が負数を返し、結果としてパスワード認証後に通信が切断される。このトラブルシューティングで1日を費やしてしまった)

 

イメージ作成

Docker Registry との連携を考えるなら、イメージの名前にはハイフンではなくアンダースコアを使った方がいい。

$ docker build -t local/apache_mysql_php .

 

コンテナ起動

Dockerホストのポートとの転送設定を -p オプションで指定する。

$ docker run -d -t -p 50000:22 -p 3306:3306 -p 8000:80 local/apache_mysql_php

この例では、SSH(22) を 50000番に、MySQL(3306) はそのまま、Apache(80) は 8000番に割り当てている。

尚、実際の運用では -v /vagrant/app:/app のようにディスクのマウント設定を行い、
アプリケーションの領域を Dockerホストと連携させると良さそうだ。

(さらに Vagrant の synced folder 機能を使って、自分のPCとDockerコンテナのディレクトリを直接リンクさせることもできる)

2014-03-15 追記
環境によっては、docker run に「-t」オプション(pseudo tty 有効化) が必須となるので付けたほうがいい。

 

動作確認

 

Docker

コンテナが起動していることを確認

$ docker ps
CONTAINER ID        IMAGE                           COMMAND                CREATED             STATUS              PORTS                                                                 NAMES
c636e5dbaebf        local/apache_mysql_php:latest   /usr/bin/supervisord   16 seconds ago      Up 14 seconds       0.0.0.0:3306->3306/tcp, 0.0.0.0:50000->22/tcp, 0.0.0.0:8000->80/tcp   angry_pare

 

SSH

ログイン先は Dockerホストの 50000番ポート。

「Dockerホスト」の箇所は環境に合わせて適宜置換(以下同じ)。ログインパスワードは ssh-user。

$ ssh -p 50000 -l ssh-user Dockerホスト
(パスワード入力など)
[ssh-user@70a19c076413 ~]$

 

Apache, PHP

Docker コンテナの中で適当なPHPページを作る。

$ sudo /bin/bash -c "echo '<?php phpinfo(); ?>' > /var/www/html/info.php"

ブラウザで http://Dockerホスト:8000/info.php にアクセスして、phpinfo() の結果が表示されればOK。

 

MySQL

root アカウントで直接操作できるようにしている。

$ mysql -uroot -h Dockerホスト
mysql> select host, user from mysql.user;
mysql> exit

 

これで immutable な開発環境のベースができあがった。

 

 

References

 

1.11.2014

Scala: Parallel SBT Test with Docker Containers

Scala: Docker を使って sbt test を並列化してみた

 

Docker + CI が巷で流行しているそうなので便乗。

 

動機

ここに、残念なテストがある。

ローカル上の Redis サーバに接続し、100件のレコードを投入した後で 1件削除。
そのときにレコード数が 99件かどうかを検証するだけの簡単なプログラムだ。

これが全部で 4セットほどある。

このテストは明らかに

  • 無駄な sleep が入っていて実行時間が長い (1クラスごとに10秒待たされる)
  • テストを並列で実行するとカウントがずれてテストが通らない場合がある
  • テストを流す前に既に DB に他のデータが入っていたら、カウントがずれてテストが通らない

というような問題を抱えている。

 

単に実行すると 48秒かかる
$ sbt test
(略)
[info] Passed: Total 4, Failed 0, Errors 0, Passed 4
[success] Total time: 48 s, completed Jan 11, 2014 1:35:59 AM

 

parallelExecution を有効にするとテストが通らない
$ sbt 'set parallelExecution in Test := true' test
(略)
[info] test1 should
[error] x insert 100 records and delete 1 record
[error]    'Some(96)' is not Some with value '99' (RedisConnectionSpec.scala:27)
(略)
[error] Failed: Total 4, Failed 4, Errors 0, Passed 0
[error] Failed tests:
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec1
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec2
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec3
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec4
[error] (test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 14 s, completed Jan 11, 2014 1:37:52 AM

 

テストデータ以外が含まれていると、やはりテストが通らない
$ redis-cli set 'foo' 'bar'
OK
$ sbt test
(略)
[info] test1 should
[error] x insert 100 records and delete 1 record
[error]    'Some(100)' is not Some with value '99' (RedisConnectionSpec.scala:27)
(略)
[error] Failed: Total 4, Failed 4, Errors 0, Passed 0
[error] Failed tests:
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec4
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec3
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec2
[error]         com.github.mogproject.dockersbttest.example.RedisConnectionSpec1
[error] (test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 46 s, completed Jan 11, 2014 1:39:52 AM
$ redis-cli del 'foo'
(integer) 1

 

アイデア

当然ながら、不要な待ち時間を減らしたり、モックを作ったりして状態への依存を極力少なくした
正しいテストを書くのが最もよいアプローチだと思う。

しかし現実にはそれが難しかったり、対応に時間がかかってしまう場合もある。

そこで、Docker を使ってテストクラスの単位で独立したコンテナを立ち上げ
その中で個別のテスト(sbt 'test-only クラス名')を実行したいと考えた。

処理の流れは以下のとおり。

  1. [事前準備] あらかじめ、テストで使う Docker イメージを作成しておく。(以下、ベースイメージとする)
  2. [前処理] ベースイメージに対して、2種類の変更を行い新規イメージを作成。
    そのイメージ(以下、テストイメージとする) に対して新しいタグを付ける。
    • 事前コマンド
      テストの度に実行するコマンドがあれば、それを登録。
      モジュールの最新化 (git pull) などを想定。
    • コンパイル
      テストを流すたびにコンパイルが走ると遅くなるので、事前に sbt compile test:compile を実行
  3. [テスト一覧取得] テストイメージを使って、テスト対象クラス名の一覧を取得する。
    これは sbt 'show test:defined-test-names' コマンドで確認できた。
     
  4. [テスト並列実行] 手順 3 で取得したクラス名に対して、並列で以下の処理を実行。
    • テストイメージを使って新規コンテナを起動
    • コンテナの中で sbt 'test-only クラス名' を実行 (実行中の標準出力はバッファリング)
    • テストが完了したら、コンテナは停止する
    • コマンドのリターンコードでテスト成功/失敗を判断
    • バッファリングしていた標準出力を全て出力
    • コンテナを削除 

    並列処理の多重度は、Docker ホストマシンの CPU数と同じにする。

  5. [結果表示] 全てのテストが完了したら、結果一覧を表示。
  6. [後処理] テストイメージを削除。

 

動作イメージ

Docker sbt test

 

作ってみた

上記の 2. 〜 6. の処理を行うスクリプトを Python で書いてみた。

docker_sbt_test.py というスクリプトに対して、ベースイメージのリポジトリ名(+タグ名)、テストイメージ内部にある sbt プロジェクトのディレクトリ、ベースイメージに対して行う事前実行コマンドを指定して実行する。

docker_sbt_test.py REPOSITORY[:TAG] [options]

Options:
  -h, --help            show this help message and exit
  -d DIR, --dir=DIR     path to the sbt project directory in the container
  -s SETUP, --setup=SETUP
                        commands to be executed in the shell before testing

より詳しい実行例はこちらを参照。

 

実行結果

手元 (4CPU, 4GB MEM の VirtualBox環境) で実行してみた結果はこちら。

それぞれのテストが並列で実行され、全て成功。sbt test-only は14〜15秒ほどで完了した。

ログ中の starting sbt と finished sbt の間で 30秒ほどかかっているように見えるが、
これは docker コンテナの起動/終了処理と sbt 自体の起動処理にかかる時間が含まれるためと考えられる。
(ちなみに、単体実行時でも sbt の起動には 5秒程度かかっていた。)

VirtualBox 環境でなければもっと速いかもしれない。

  • 抜粋
    $ ./docker_sbt_test.py local/sbt-test --dir /workspace/docker-sbt-test/example --setup " \
    rm -fr /workspace/docker-sbt-test && \
    git clone --depth 1 https://github.com/mogproject/docker-sbt-test.git /workspace/docker-sbt-test \
    "
    2014-01-11 07:24:53,272 [INFO] Creating test image (local/sbt-test:test-1389425093.27) ...
    (略)
    2014-01-11 07:25:13,073 [INFO] Compiling...
    (略)
    2014-01-11 07:25:41,707 [INFO] Getting test class names...
    2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec1' ...
    2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec2' ...
    2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec3' ...
    2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec4' ...
    2014-01-11 07:26:13,257 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec3' ...
    Starting redis-server: [  OK  ]
    (略)
    [info] RedisConnectionSpec3
    [info]
    [info] test3 should
    [info] + insert 100 records and delete 1 record
    [info]
    [info]
    [info] Total for specification RedisConnectionSpec3
    [info] Finished in 39 ms
    [info] 1 example, 0 failure, 0 error
    [info]
    [info] Passed: Total 1, Failed 0, Errors 0, Passed 1
    [success] Total time: 15 s, completed Jan 11, 2014 2:26:13 AM
    
    2014-01-11 07:26:13,600 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec4' ...
    (略)
    [info] Passed: Total 1, Failed 0, Errors 0, Passed 1
    [success] Total time: 14 s, completed Jan 11, 2014 2:26:13 AM
    
    2014-01-11 07:26:13,911 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec2' ...
    (略)
    [info] Passed: Total 1, Failed 0, Errors 0, Passed 1
    [success] Total time: 14 s, completed Jan 11, 2014 2:26:13 AM
    
    2014-01-11 07:26:14,035 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec1' ...
    (略)
    [info] Passed: Total 1, Failed 0, Errors 0, Passed 1
    [success] Total time: 14 s, completed Jan 11, 2014 2:26:13 AM
    
    2014-01-11 07:26:14,080 [INFO] *** SUMMARY ***
    2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec1 -> OK
    2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec2 -> OK
    2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec3 -> OK
    2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec4 -> OK
    2014-01-11 07:26:14,081 [INFO] ***************
    2014-01-11 07:26:14,081 [INFO] Removing test image (local/sbt-test:test-1389425093.27) ...
    (略)
    
  • 全文 -> docker-sbt-test-example-result.txt

 

注意事項

こちらに dstat の結果を載せたが、複数のコンテナを同時に起動すると
思ったよりメモリリソースの消費が激しいようだ。

スワップが起こるような状況であれば、もちろん並列化しない時よりもずっと遅くなる。

 

まとめ

  • sbt test を並列化する最善のアプローチは parallelExecution を有効にしても通るテストを書くこと。

    ただ、同時に実行できない多数のテストが存在する場合でも、Docker + 並列化スクリプトで
    高速化できる可能性がある。
     
  • テストが外部環境の状態に依存する場合も、Docker を使えばクリーンな環境を保証できる。

 

 

 

References

1.08.2014

Running shogi-server in a Docker Container

shogi-server を Docker コンテナで動かす

 

コンピュータ将棋協会規定のTCP/IPプロトコルに従った将棋のネット対局サーバ shogi-server
Docker コンテナ上で動かしてみたのでメモ。

事前準備

  • Docker ホスト環境の構築
  • 適当なプロトコル対応クライアントソフトの入手
    • こちら から適当なものをインストール。
      Mac 環境であれば SFICP (Shogi Framework Implements CSA Protocol) がおすすめ。
    • telnet コマンドでも一通りの動作確認は可能。

 

遊び方

手順は全て Docker リポジトリに書いておいた。

基本的には、Docker ホスト上で以下のコマンドを叩けばコンテナが起動する。

$ docker pull mogproject/shogi-server
$ docker run -d -p 4081:4081 mogproject/shogi-server

forwarding するポートを指定して (-p 4081:4081)、 Docker ホストの 4081 番ポートがそのまま
Docker コンテナの 4081 番ポートに繋がるようにしているが、ここはお好みで。

その後は将棋ソフト(クライアント)から Docker ホストに接続するか、telnet で 4081 番ポートに接続すれば
shogi-server が応答を返すはずだ。

 

Dockerfile

# Dockerfile for shogi-server
#
#

# use the centos image provided by dotCloud
FROM centos:6.4

# install packages
RUN yum install -y ruby
RUN yum install -y git

# download shogi-server module
RUN git clone git://git.sourceforge.jp/gitroot/shogi-server/shogi-server.git /opt/shogi-server

# create shogi user and change owner of the directory.
RUN useradd shogi
RUN chown -R shogi /opt/shogi-server

# launch shogi-server when launching the container
ENTRYPOINT ["ruby", "/opt/shogi-server/shogi-server", "event1", "4081"]

# run shogi-server as shogi user
USER shogi

# expose shogi-server port
EXPOSE 4081

ruby のバージョン指定もしていないし、shogi-server のログ出力も十分でない、超手抜き実装。

ベースは dotCloud 公式の CentOS 6.4 イメージ。

モジュールは /opt/shogi-server 配下に置かれ、shogi という OS ユーザで、event1 というイベント名でプロセスが立ち上がる。

 

自分でビルドを行う場合は Dockerfile のあるディレクトリに移動し、

$ docker build -t ユーザ名/イメージ名 .
$ docker run -d -p 4081:4081 ユーザ名/イメージ名

と実行すればよい。

 

起動中の shogi-server の出力を見たい場合は、Docker ホストサーバ上で

$ docker ps

を叩いてコンテナID (CONTAINER ID/CID) を確認し、

$ docker logs コンテナID

で OK。

 

 

 

Related Posts

1.06.2014

Vagrant: Creating CentOS 6.5 Docker Host

Vagrant: CentOS 6.5 Docker 母艦の建造

 

Vagrant 1.4 で Docker へのプロビジョニング対応 (Vagrant 1.4 - Vagrant) が行われたことで
ローカルマシン(VirtualBox)上での Docker ホストOS の構築はより一層簡単になった。

尚、Ubuntu ホストであればこちらの手順通り、公式リポジトリをそのまま使えばよい。

 

事前準備

 

環境
  • ローカルマシン: Mac OS X 10.9.1
    • Vagrant: 1.4.2 (確認は「vagrant -v」)
    • VirtualBox: 4.3.6
  • Docker ホスト (母艦): CentOS 6.5
    • Docker: 0.7.2 (確認は「docker -v」)
  • Docker コンテナ: CentOS 6.4

 

母艦の立ち上げ

基本的には適当なディレクトリに Vagrantfile を作成して、「vagrant up」するだけ。

Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "opscode-centos65"
  config.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box"

  config.vm.provider :virtualbox do |vb|
    vb.name = "docker1"
    vb.customize ["modifyvm", :id, "--memory", 1024]
  end

  config.vm.network :private_network, ip: "192.168.33.10"

  config.vm.provision :docker do |d|
    d.pull_images "centos:6.4"
  end
end
  • 8行目: OpsCode 提供の CentOS 6.5 box をダウンロードして利用
  • 11-12行目: VirtualBox の VM に docker1 という名前を付け、メモリを少し増やす
  • 15行目: Docker コンテナへ直接アクセスしたいので、静的アドレスを割り振った方が便利
    (ネットワークアドレスは VirtualBox の仮想ネットワークアダプタの設定に依存。
    デフォルトでは host-only ネットワークが 192.168.33.0/24 として構築されている)
  • 17-19行目: Docker のプロビジョニング設定。ここでは centos:6.4 のイメージを pull している。
    これを記述することで、Docker 自体のインストール、サービスへの登録が自動的に行われる。

 

仮想マシンの作成

単に「vagrant up」でもよいが、ゴミの消去も含めたシェルスクリプトを書いて実行した。

#!/bin/bash


### Clean.
vagrant destroy --force

### Create VirtualBox image and boot.
vagrant up

### Print status.
vagrant status

実行結果は以下のとおり。プロビジョニングの工程で Docker のインストールが行われている。

[default] VM not created. Moving on...
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'opscode-centos65'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Running 'pre-boot' VM customizations...
[default] Booting VM...
[default] Waiting for machine to boot. This may take a few minutes...
[default] Machine booted and ready!
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- /vagrant
[default] Running provisioner: docker...
[default] Installing Docker (latest) onto machine...
[default] Pulling Docker images...
[default] -- Image: centos:6.4
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

ちなみに、vagrant-vbguest プラグインがインストールされている場合には以下のようなビルドエラーが発生した。

Building the OpenGL support module[FAILED]
(Look at /var/log/vboxadd-install.log to find out what went wrong)
Doing non-kernel setup of the Guest Additions[  OK  ]
Installing the Window System drivers[FAILED]
(Could not find the X.Org or XFree86 Window System.)
An error occurred during installation of VirtualBox Guest Additions 4.3.6. Some functionality may not work as intended.
In most cases it is OK that the "Window System drivers" installation failed.

X Window はインストールされていないので無視してよさそうだが、OpenGL のほうは少し気になる。

 

母艦への接続

「vagrant ssh」で母艦に接続した後、docker コンテナを立ち上げて
/etc/redhat-release ファイルの中身を参照してみた。

$ vagrant ssh
Last login: Thu Dec  5 05:57:05 2013 from 10.0.2.2
[vagrant@localhost ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              6.4                 539c0211cd76        9 months ago        300.6 MB
[vagrant@localhost ~]$ sudo docker run centos:6.4 /bin/cat /etc/redhat-release
CentOS release 6.4 (Final)

Cent OS 6.4 のコンテナが起動したことが確認できた。

 

 

 

References

 

Related Posts

12.13.2013

Installing Docker on CentOS 6.5 (with Vagrant and VirtualBox)

Vagrant+VirtualBox 環境の CentOS 6.5 に Docker をインストールする

 

こちらのブログポストの内容を実践してみた。

なぜ Docker か

Docker とは、Linux のホストOS上で複数の仮想 Linux OS(コンテナ)を実行するためのソフトウェア。

VMWare のようなハイパーバイザー型の仮想OSと異なり、ハードウェア層やカーネルの大部分が共通して使われるため、パフォーマンスのオーバーヘッドが少ない。

LXC (Linux Containers) と aufs (Advanced multi layered unification filesystem) という要素技術が使われており、
Docker自体は Go言語で記述されている。

Docker を使うモチベーションを挙げれば以下のような感じだ。

  • とにかく起動が速い
    aufs の恩恵により、差分ベースでファイルシステムが構築されていく。
    一瞬でOSが立ち上がる様は感動的ですらある。

    Immutable Infrastructure (出典: Trash Your Servers and Burn Your Code: Immutable Infrastructure and Disposable Components - Chad Fowler) の前に立ちはだかっていた壁の一つが音もなく崩れ去った瞬間を目の当たりにしている。

    仮想化技術によって、インフラの「作って捨てて」を非常に短いサイクルでできるようになった結果、
    我々はこれまで混然としていた Immutable (OS, ミドルウェアのバージョン、コンフィギュレーションなど) と Mutable (各種ログ、エラー通知、リソース推移など) の意味的な区別に専念できるようになったのだ。
  • 開発PCのローカル環境 -> CI環境 -> ステージング環境 -> プロダクション環境と、 同一性が保証されたインフラをハードウェア構成に依存せずに作成することができ、 しかもその内容をコードとして管理できる。 (Infrastructure as Code)

    Docker at eBay // Speaker Deck
    (プロダクション環境での利用実績はまだ少ないようだが)
  • 従って、たとえばCI環境で使えば、DB の状態に依存するテストでも並列かつ迅速に実行できる。

 

目的

Mac OS (Mavericks) 上で Vagrant を実行し、VirtualBox 上に CentOS 6.5 インスタンスを作成。
そこに Docker をインストールし、さらにその上に複数のゲストコンテナを実行できるようにする。

事前準備

  • Vagrant インストール
  • VirtualBox インストール

ホストOSのセットアップ

とりあえず今日は Mac 上で Vagrant を実行し、VirtualBox 内のホストOS(いわゆる母艦)に Docker をインストールするまで。

  • VirtualBox Guest Additions を更新するため、Vagrant プラグイン vagrant-vbguest をインストール
  • opscode のテンプレートを利用して vagrant init
  • Vagrantfile を編集し、後処理として epel から docker パッケージを取得しインストール、起動
  • vagrant up
  • Docker 上で Hello World!

 

 

References