Dockerコンテナ上にdns環境を構築する


この記事は「[Dockerコンテナ上に開発環境一式を構築する](http://qiita.com/hakaicode/items/5522a5ad1c280b9cf757)」の一部です。

## 現在のステータス

* [VPSにホストにUbuntuを入れる](http://qiita.com/hakaicode/items/5522a5ad1c280b9cf757)
* dnsを構築する  <-イマココ
* [nginxを構築する](http://qiita.com/hakaicode/items/38cbed889a03a33cdfcf)
* [Selenium用のJenkins環境を構築する](http://qiita.com/hakaicode/items/9a4ccf9faf63a4d9d3e4)
* [GitBucket環境を構築する](http://qiita.com/hakaicode/items/4fb257c8e05727a8abf5)
* [Desktop環境(IntelliJ IDEA)を構築する](http://qiita.com/hakaicode/items/79de9b24ad28070fbab2)

## Dockerfile

gitbucketやJenkinsにコンテナの外からも中からも同じドメインでアクセスしたいですよね。そこでコンテナの中のホストのためにネームサーバーを立ち上げます。

```Docker:Dockerfile
# Bind9 Dockerfile
FROM ubuntu:14.04
ENV DEBIAN_FRONTEND noninteractive

# Install.
RUN apt-get update && apt-get -y upgrade
 && apt-get install -y bind9 dnsutils
```

Ubuntu14.04をベースにbindとdnsutilsだけをインストールします。EXPOSEディレクティブで使用するポートをdockerに伝える事も出来ますが、コンテナ起動時に-pで外部公開用と同時に指定もできるのでここで書いていません。
https://docs.docker.com/engine/reference/run/#expose-incoming-ports

Dockerfileを作ったらビルドします。ビルドはスクリプトにするほどでもないのですが、しばらく触らないとオプションを忘れてしまうのでファイルにしてしまいます。

```bash:build.sh
#!/bin/sh

PROJECT=dns

cd /home/docker/$PROJECT/docker
docker build -t "lavans-$PROJECT:latest" .
```

bindの設定ファイルを取得するため、一旦起動して/etc/bindディレクトリをまるまる持ってきます。

```shell-session
$ docker run  --name dns  -d lavans-dns named -f
$ docker cp dns:/etc/bind bind
```

バックアップをとってファイルを変更します。

```bash
cp named.conf.options named.conf.options.org
cp named.conf named.conf.org
echo include \"/etc/bind/named.conf.example\" >> named.conf
```

```conf:named.conf.example
zone "dev.example.jp" {
        type master;
        file "/etc/bind/db.example";
};
```

dockerホストのブリッジIPですが、原則172.17.42.1で衝突するときだけ移動するのかと思っていたのですが違いました。今回は172.17.0.1でした。そこで下記のような環境設定ファイルを用意します。こいつは他のコンテナからも使うので/home/dockerに置きます。
参考:[Dockerコンテナ内部からホストのSMTPを利用したい](http://qiita.com/rerofumi/items/a6307f3db7c1cbfaaef2)

```bash:/home/docker/setenv.sh
DOCKER_HOME=/home/docker
IP=`ip route | grep docker0 | awk '{print $9}'`
```

/etc/default/dockerに`DOCKER_OPTS='--bip=<ip-addr>'`と指定することで
dockerサービスの起動時に指定できるので、固定にしてしまってもいいと思います。
https://docs.docker.com/v1.8/articles/networking/

dbファイルを下記のように設定しておきます。

```conf:db.example.org
; BIND data file for example.jp
$TTL    604800
@       IN      SOA     dns.example.jp. root.localhost. (
                       20160101         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      dns.example.jp.
@       IN      A       127.0.0.1
@       IN      AAAA    ::1
dev    IN      A       $IP
dns     IN      A       $IP
gitbucket       IN      CNAME   dev
jenkins IN      CNAME   dev
devtop  IN      CNAME   dev
www     IN      CNAME   dev
```

コマンドラインでIPをセットします。

```shell-session
$ . ../setenvn.sh
$  sed  -e s/\$IP/$IP/g db.example.org > db.example
```

これでgitbucket.dev.example.jpやjenkins.dev.example.jpがみんなブリッジIPを向くようになるはずです。起動/終了スクリプトを用意して確認します。ました。bindディレクトリをマウントして確認してみます。

```bash:run.sh
#/bin/sh

. ../setenv.sh

docker run -v $DOCKER_HOME/dns/bind:/etc/bind --name dns -p 53:53/udp -d \
  lavans-dns named -f
```
-vオプションでホストのディレクトリをコンテナのディレクトリにマウントします。-pで53/udpをDockerの外にも公開してます。

```bash:kill.sh
#/bin/sh

docker stop dns
docker rm dns
```

Dockerコンテナをストップした後はコンテナファイルが残ります。そのコンテナを再利用したりそこからイメージを作成することもできますが、Dockerの「毎回同じ環境で動作する」メリットを生かすために、基本的には一度落としたコンテナは削除して次回は新しいコンテナを立ち上げ直します。

```shell-session
$ ./kill.sh
$ ./run.sh
```

コンテナが立ち上がっているか確認します。

```shell-session
$ docker exec -it dns bash
```

「dns」という名前で先ほど立ち上げたコンテナでbashをインタラクティブモードで動かします。

```
Error response from daemon: No such container: dns
```

おっと！怒られてしまいました。docker psで確認してみます。

```shell-session
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
docker@133-130-118-36:~/dns$
```

docker psはpsコマンドのように立ち上がっているコンテナ一覧を表示します。なにもいませんね。コンテナをbashで立ち上げてみましょう。

```shell-session
$ docker run -v $DOCKER_HOME/dns/bind:/etc/bind --name  dns -it lavans-dns bash
```

これでbindディレクトリをマウントした状態でシェルにつながりました。
このようなプロンプトになります。

```shell-session
root@c37b3eb2d61c:/#
```

bind設定ファイルをチェックしてみましょう。

```shell-session
root@c37b3eb2d61c:/# named-checkconf
/etc/bind/named.conf:13: missing ';' before end of file
root@c37b3eb2d61c:/#
```

セミコロンが抜けていたようです。修正して再確認します。zoneファイルも確認しておきましょう。

```shell-session
root@c37b3eb2d61c:/# named-checkzone dev.lavans.jp /etc/bind/db.dev-lavans
zone dev.lavans.jp/IN: loaded serial 20160101
OK
```

コンテナを再起動して、hostかnslookupを叩いて確認します。

```shell-session
$ ./kill.sh
$ ./run.sh
$ docker exec -it dns bash
$ host jenkins.lavans.jp localhost
Using domain server:
Name: localhost
Address: ::1#53
Aliases:

jenkins.example.jp is an alias for dev.example.jp.
dev.example.jp has address 172.17.0.1
```

ようやく動きました。