はじめに
MacでDockerを使いたい場合、Dockerから正式に出ているboot2dockerというちょっと便利なツールがあります。
これを使ってChefのレシピをDocker Imageに対して適用しながら開発する環境の準備について共有します。
いろいろアレンジはあると思いますが、ご参考まで。
この方法の良い所
- MacのLocalファイルにあるレシピをDocker Imageに適用できるので開発が楽です
- Docker Imageに対しては
knife-solo
を使ってレシピを送ります
- Docker Imageに対しては
- 「やり直し」や「適用後の状態の確認」を比較的簡単に行えます
- Dockerfile+chef で Docker Imageを作成していく際にとても便利です
構成イメージ
最終的には以下のような構成にします。Windowsでも同様の構成が取れると思います。
また、見てもわかるようにDockerはVirtualBox上で動いている必要はなく、Remote Serverのdockerに対してもMacのLocal環境はほとんど変更せずに作業ができます(TCPさえ通ればOK)。
各Version
- MacOS: 10.9.2
- VirtualBox: 4.3.8
- boot2docker: 0.6.0
- docker: 0.8.0, build cc3a8c8d8ec57e15b7b7316797132d770408ab1a
- chef: 11.10.2
- knife-solo: 0.4.1
手順
- (前準備) boot2dockerのInstall と setup
- (前準備) Host Only アダプターの有効化
- (本題) Docker Imageの作成
- (本題) knife-solo環境の準備
- (本題) knife-solo 実行!
(前準備) boot2dockerのInstall
VirtualBoxのInstall
まだ、VirtualBoxをInstallしていなければ、まず、下記からVirtualBoxをInstallします。
https://www.virtualbox.org/wiki/Downloads
Homebrew の Install
まだ、Homebrew をInstallしていなければ、何も考えずに
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
と実行しておきます。(公式サイト: http://brew.sh/ )
boot2docker の Install
brew update
brew install boot2docker
これで boot2docker の導入は終わりです。
※ちなみに、MacのDocker Clinetも一緒にInstallされます。
boot2docker の setup と 起動
boot2docker は VirtualBox上にVM Imageを作成し、その上でさらにdockerを動かします。
多分内部的には、vagrantを使っているのだと思います。
最初にVM Imageをダウンロードする必要があるので、以下のように実行します。
boot2docker init
boot2docker up
boot2docker up
した後にVirtualBoxのGUIを起動すると、
が動いているのがわかります。
docker は VirtualBoxのVMで起動しているので、MacのDocker Clientはそこと通信して動作する必要があります。
VMの設定で4243ポートがNATされているので、以下の様な設定を.zshrc
や.bashrc
に書いておくと良いでしょう。
※ 現在のShellでも実行しておくことも忘れずに。
# .zshrc or .bashrc
export DOCKER_HOST=tcp://127.0.0.1:4243
(前準備) HostOnly アダプターの有効化
knife-solo で DockerコンテナのSSHに直接繋ぎたいので、VirutualBoxのGUIでHostOnlyアダプターを有効にします。
NATを設定しても良いですが、boot2dockerVMが最初からよしなに設定してくれているので、後々便利そうなのでHostOnlyアダプターを使います。
まだホストオンリーアダプターを作ったことがない場合
下記ブログの「ホストOSにネットワークを作成」などが参考になると思います。
http://blog.kjirou.net/p/2171
ホストオンリーアダプタは、1回作れば他のVMでも使えるので作っておきましょう。
ホストオンリーアダプターを設定
一旦 boot2dockerVMを停止します。
boot2docker stop
その後、VirutualBox GUIで 「boot2docker-vm」を選択し、「設定」→「ネットワーク」 から下記のように選択します。
再度、boot2dockerVMを起動してIPを調べておきます。
% boot2docker up
% boot2docker ssh
## .
## ## ## ==
## ## ## ## ===
/""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\______/
_ _ ____ _ _
| |__ ___ ___ | |_|___ \ __| | ___ ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__| < __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
boot2docker: 0.6.0
docker@boot2docker:~$ ifconfig
docker0 Link encap:Ethernet HWaddr FE:AF:63:C4:4F:02
inet addr:10.1.42.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::ecb3:3cff:fe83:cb3f/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:26537 errors:0 dropped:0 overruns:0 frame:0
TX packets:64098 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1354694 (1.2 MiB) TX bytes:60164533 (57.3 MiB)
eth0 Link encap:Ethernet HWaddr 08:00:27:99:66:E4
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe99:66e4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:89688 errors:0 dropped:0 overruns:0 frame:0
TX packets:49580 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:61443437 (58.5 MiB) TX bytes:31615700 (30.1 MiB)
eth1 Link encap:Ethernet HWaddr 08:00:27:6A:AD:03
inet addr:192.168.33.100 Bcast:192.168.33.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe6a:ad03/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1628 errors:0 dropped:0 overruns:0 frame:0
TX packets:841 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:534878 (522.3 KiB) TX bytes:129582 (126.5 KiB)
Interrupt:16 Base address:0xd040
大抵、eth1
になると思います。上記の場合ですと、192.168.33.100
がboot2dockerVMのIPになります。
(本題) Docker Imageの作成
以下の様な設定で Chefが使えるDocker Imageを作っていきます。
- CentOS 6.4ベース
- sshd の Install
- centos ユーザを作成し nopasswordでのsudoを許可
dockerfile 用のDIR と SSH鍵の作成
mkdir centos64-base
cd centos64-base
ssh-keygen -t dsa -C "centos64-base" -f centos
次に Dockerfileを作ります。
FROM centos:6.4
MAINTAINER mokemokechicken <mokemokechicken@twitter>
ENV PATH $PATH:/usr/bin
RUN yum update -y
# Timezone to UTC
RUN cp /usr/share/zoneinfo/UTC /etc/localtime
# Chef & git
RUN curl -L https://www.opscode.com/chef/install.sh | bash
RUN yum install -y git
# SSH Server
RUN yum install -y zsh openssh-server sudo
RUN mkdir -p /var/run/sshd
RUN ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa
RUN ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa
# add centos user (password=password)
RUN useradd -d /home/centos -m -s /bin/zsh centos
RUN echo centos:password | chpasswd
RUN echo 'centos ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN mkdir -p /home/centos/.ssh
ADD centos.pub /home/centos/.ssh/authorized_keys
RUN chown centos:centos -R /home/centos
RUN chmod 700 /home/centos/.ssh
Docker Image の Build
割と何度もBuildすることになるので、Makefileなど作っておくと便利です。この辺は適当に。
IMG=mokemokechicken/centos64-base
build:
docker build -t $(IMG) .
rebuild:
docker build --no-cache -t $(IMG) .
delete:
docker rmi $(IMG)
push:
docker push $(IMG)
では、Buildを実行しましょう。
make build
成功しましたか?
(本題) knife-solo環境の準備
LocalのChefの環境構築や knife-solo のInstall方法は省略します。gemやbundlerなどでサクッと作っておきましょう。
今回は bundler で vendor/bundle などにknife-soloがInstallされている想定で話を進めます。
###余談
Dockerで起動しているコンテナに chefのレシピを送るために、`docker run` の `-v`オプションでマウントできれば簡単なのですが、間にboot2dockerのVMが挟まっているためにそれができません。docker run で -v をした場合、boot2dockerVMとDockerコンテナの間でファイル共有されます。
Macとboot2dockerの間でもファイル共有すればいいのかもしれませんが、少しやり過ぎな感じがあるので、普通にknife-soloを使ったSSHでの転送を使うことにします。
実行スクリプトの作成
Dockerコンテナにknife-soloを接続するには、
- Dockerコンテナの起動 with sshd
- knife-solo 実行
- Dockerコンテナの停止
- 実行後のコンテナのCommit (実行検証用に)
というような手順が必要です。
面倒なのでスクリプト化します。
ちなみに、knife-docker
という gem もあったのですが、IPアドレスの変更方法がわからなかったので使いませんでした。
#!/bin/bash
IMAGE=$1
shift
ARGS=$@
EXTRA_ARGS=""
if [[ "$ARGS" == "" ]]; then
cat <<EOM
= Usage: $0 <Docker Image> <knife-solo cook args>
= Environment variables
DOCKER_PORT: Docker container SSH port (default: 8022)
SSH_IP : knife-solo ssh IP (default: 127.0.0.1)
SSH_USER : knife-solo ssh user (default: centos)
SSH_KEY : knife-solo ssh key (optional)
= Environment file
if ~/.chef-docker-env exists, evaluated automatically.
~/.chef-docker-env must be a shell script.
= To See knife solo cook help
bundle exec knife solo cook --help
EOM
exit
fi
CHEF_DOCKER_ENV_FILE=${CHEF_DOCKER_ENV_FILE:-~/.chef-docker-env}
cd $(dirname $0)/..
if [[ -e $CHEF_DOCKER_ENV_FILE ]]; then
. $CHEF_DOCKER_ENV_FILE
fi
DOCKER_PORT=${DOCKER_PORT:-8022}
SSH_IP=${SSH_IP:-127.0.0.1}
SSH_USER=${SSH_USER:-centos}
if [[ "$SSH_KEY" != "" ]]; then
EXTRA_ARGS="-i ${SSH_KEY} ${EXTRA_ARGS}"
fi
NEW_IMAGE=last
echo docker run -d -p ${DOCKER_PORT}:22 ${IMAGE} /usr/sbin/sshd -D
DID=$(docker run -d -p ${DOCKER_PORT}:22 ${IMAGE} /usr/sbin/sshd -D)
if [[ $? == 0 ]]; then
rm nodes/*.json 2>&1 >/dev/null
echo bundle exec knife solo cook ${SSH_USER}@${SSH_IP} -p ${DOCKER_PORT} --no-berkshelf $EXTRA_ARGS $ARGS
bundle exec knife solo cook ${SSH_USER}@${SSH_IP} -p ${DOCKER_PORT} --no-berkshelf $EXTRA_ARGS $ARGS
echo docker stop $DID
docker stop $DID > /dev/null
(docker images | grep ${NEW_IMAGE} && docker rmi ${NEW_IMAGE}) 2>&1 > /dev/null
docker commit $DID ${NEW_IMAGE} > /dev/null
cat <<EOM
Finish Successfully!
If you want to login the chef configured image,
docker run -t -i ${NEW_IMAGE} /bin/bash
EOM
else
echo docker run fail.
fi
knife-soloで接続するときには boot2dockerVMのIPやSSH鍵(先ほどssh-keygenで作った秘密鍵)が必要になります。
環境変数で指定できるようにしてありますが、毎度指定したりするのも面倒なので ~/.chef-docker-env
にファイルがあればそれを読み込むようにしてあります。
例えば以下のように書いておくと便利です。
export SSH_IP=192.168.33.100
export SSH_KEY=~/.ssh/centos
(本題) knife-solo 実行
やっと準備は終わりです。
実行
以下のようにして、レシピを実行できます。
bin/chef-docker mokemokechicken/centos64-base -r 'recipe[mysystem]'
上記の mokemokechicken/centos64-base
以降の引数は knife solo cook
にそのまま渡されます。
オプション周りは使いやすいように都度都度変更していくのかと思います。
結果の確認
レシピ実行後のコンテナを last
という名で自動Commitするようにしてあるので、
docker run -t -i last /bin/bash
とすれば、コンテナに入ることができます。
やり直しと続きからのレシピ適用
やり直したければ、前と同じImage(mokemokechicken/centos64-base
)を再度chef-docker
で指定すればよく、
続きから行いたければ、Image last
をchef-docker
で指定すれば良いです。
レシピ実行時間短縮のために続きからやりたいことも多いので、結構重宝します。
最初から通してやりたければ、すぐにできるというのも魅力ですね。