iPhone/iPad実機からMacのdockerコンテナ環境にリクエストする

iOSアプリを開発するにあたって、APIを受けるサーバ側の開発環境を用意することにした。 Vagrantでもいいのだけど、Dockerにも触っておきたくて入れてみた時の備忘録的なアレです。 正直ネットワーク周りはあまり深く知らないので、もっとうまくやる方法がきっとあると思います。

まずはMacにdockerをインストール

docs.docker.com

dockerはLinuxカーネル機能を使っているため直接Macで使えないので、 dockerのdaemonを動かす専用の軽量VMとその管理ツール(boot2docker)が用意されています。 Macから動かすには、このboot2dockerを通じて動かします。

ざっくりこんなイメージ。

Mac <==> VirtualBox(VM) <==> Docker container

boot2dockerの導入

まずMacVirtualBoxを入れます。 Downloads – Oracle VM VirtualBox

次にこちらを参考にhomebrewからdockerとboot2dockerをインストールします。 qiita.com

初期化

  1. VMを作成。初回の1度のみ。
mac$ boot2docker init

VM起動

mac$ boot2docker up

環境変数の確認

VMが起動するたびに、VMの$DOCKER_HOSTが変動したりするので以下のコマンド結果を実行します。

mac$ boot2docker shellinit
Writing /Users/edy/.boot2docker/certs/boot2docker-vm/ca.pem
Writing /Users/edy/.boot2docker/certs/boot2docker-vm/cert.pem
Writing /Users/edy/.boot2docker/certs/boot2docker-vm/key.pem
    export DOCKER_CERT_PATH=/Users/edy/.boot2docker/certs/boot2docker-vm
    export DOCKER_TLS_VERIFY=1
    export DOCKER_HOST=tcp://192.168.59.103:2376

毎回手動で行うのも面倒なのでshellの*rcなどに書いておきます。

# to connect docker server
if [ "`boot2docker status`" = "running" ]; then
    eval $(boot2docker shellinit 2>/dev/null)
fi

状態の確認

mac$ boot2docker status
running

ここまででとりあえずboot2dockerの導入は終わり。

dockerを使う

使えるイメージを確認する。最初は空。

mac$ docker images

imageの用意

dockerコンテナの元にする、使いたいimageを落とす (debian:wheezyの例)

$ docker pull debian:wheezy

searchサブコマンドでdocker hubから探せる (例はdebian)

mac$ docker search debian

起動してみる

debian/wheezyから起動してみる。 -iはinteractive -tはttyの割り当て --nameはコンテナ名の割り当て

mac$ docker run -i -t --name www debian:wheezy /bin/bash

ポートを紐付けたい場合は-p。 例はホスト側8080をゲスト側80番にフォワードしてる状態。

mac$ docker run -i -t -p 8080:80 --name www debian:wheezy /bin/bash

起動するとシェルに入る

root@cbc18d9ebc9c:/#

-dでデタッチする

mac$ docker run -d -p 80:8080 --name www debian:wheezy /bin/bash

シェルに入りたいとき

mac$ docker exec -it www bash

状態確認

コンテナの存在確認はpsサブコマンドを使う。 停止されているコンテナも含めるときは-aオプションを指定する。

mac$ docker ps -a
#=> CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
#=> cbc18d9ebc9c        debian:wheezy       "/bin/bash"         4 minutes ago       Exited (0) 3 minutes ago                       www

イメージづくり

Dockerfileを作る

できればここで頑張りすぎず、他のオーケストレーションツール(ansibleとかchefとか)に任せたほうが幸せな気がします。 下記では最新版のnginxを取ってくるためdotdebの設定を追加しています。

Instructions | Dotdeb

mac$ mkdir nginx
mac$ cd nginx
mac$ wget http://www.dotdeb.org/dotdeb.gpg
mac$ vi Dockerfile
FROM debian:wheezy
MAINTAINER edy <xxxxxxxxxxx@gmail.com>

RUN echo "deb     http://packages.dotdeb.org jessie all" >   /etc/apt/sources.list.d/dotdeb.list
RUN echo "deb-src http://packages.dotdeb.org jessie all" >>  /etc/apt/sources.list.d/dotdeb.list
RUN echo "deb     http://mirrors.teraren.com/dotdeb/ stable all" >   /etc/apt/sources.list.d/jp-mirror.list
RUN echo "deb-src http://mirrors.teraren.com/dotdeb/ stable all" >>  /etc/apt/sources.list.d/jp-mirror.list
RUN echo "deb     http://mirrors.teraren.com/dotdeb/ wheezy-php56 all" >   /etc/apt/sources.list.d/php56.list
RUN echo "deb-src http://mirrors.teraren.com/dotdeb/ wheezy-php56 all" >>  /etc/apt/sources.list.d/php56.list

ADD dotdeb.gpg /tmp/

WORKDIR /tmp

RUN apt-key add dotdeb.gpg && \
    apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y nginx && \
    apt-get clean && \
    rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

ENTRYPOINT /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

こまかい記法はここを参考にしました。 www.atmarkit.co.jp

イメージをbuildする

mac$ docker build -t siny/debian:1.0 .

出来上がったら、イメージを指定して先ほどと同じようにrunします。

mac$ docker run -d -p 8080:80 --name www siny/debian:1.0

ポートフォワード設定

現状のネットワーク

ここまでの設定と状態を整理します。 LAN上のMacのIPは192.168.11.2となっている状態で、こんな感じになります。

mac$ ifconfig
...略
vboxnet1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
        ether XX:XX:XX:XX:XX:XX
        inet 192.168.59.3 netmask 0xffffff00 broadcast 192.168.59.255
mac$ boot2docker ip
192.168.59.103
     iPhone
[192.168.11.3]
         ||
      ルータ
[192.168.11.1]
         ||
[192.168.11.2]
       Mac
[192.168.59.3]
         ||
[192.168.59.103]
   VirtualBox

まずMacのローカルポートをVMへフォワード

この状態でnginxにアクセスするには、Mac上からは192.168.59.103:8080でアクセスしなくてはいけません。 なので、まずlocalhostのポートをVMのポートへforwardします。

mac$ VBoxManage controlvm "boot2docker-vm" natpf1 "nginx,tcp,127.0.0.1,8080,,80"

qiita.com

これでlocalhost(Mac)の8080番がboot2docker(VM)の80へforwardされるようになり、 127.0.0.1:8080またはlocalhost:8080でアクセスできるようになります。

     iPhone
[192.168.11.3]
         ||
      ルータ
[192.168.11.1]
         ||
[192.168.11.2]
       Mac
[192.168.59.3]           localhost:8080
         ||                                 ||
[192.168.59.103]      192.168.59.103:8080
   VirtualBox

MacのLAN側からもフォワード

そしてようやく本題の実機からのアクセスを行うため、Macのいずれかのportを更にフォワードさせます。 色々やりかたはあると思いますが、今回はMacにnginxを入れて、8080ポートをListenしてlocalhost:8080へforwardさせることにしました。

Macにnginxを入れる

==> Downloading https://homebrew.bintray.com/bottles/nginx-1.6.2.yosemite.bottle.1.tar.gz
######################################################################## 100.0%
==> Pouring nginx-1.6.2.yosemite.bottle.1.tar.gz
==> Caveats
Docroot is: /usr/local/var/www

The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.

To have launchd start nginx at login:
    ln -sfv /usr/local/opt/nginx/*.plist ~/Library/LaunchAgents
Then to load nginx now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist
Or, if you don't want/need launchctl, you can just run:
    nginx

WARNING: launchctl will fail when run under tmux.
==> Summary
🍺  /usr/local/Cellar/nginx/1.6.2: 7 files, 920K

configファイル確認

mac$ nginx -t
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

リバースプロキシとしてlocalhostの8080番へフォワード

// 略
        location / {
            proxy_pass http://127.0.0.1:8080;
        }
}

最終的にはこんな感じ。

     iPhone
[192.168.11.3]
         ||
      ルータ
[192.168.11.1]
         ||
[192.168.11.2]           192.168.11.2:8080  <----- nginx listen
       Mac                              ||  <------ nginx forward
[192.168.59.3]           127.0.0.1:8080
         ||                                 ||  <------ virtualbox forward
[192.168.59.103]      192.168.59.103:8080
   VirtualBox

これで、iPhone実機から192.168.11.2:8080にアクセスすると見えるようになりました。 試した時はDHCPなので、安定稼働させるにはIPを固定化するとか、内部DNS立てるかすればよいと思います。