Docker 上で bind を動かす

おさらい

前回までのエントリでは、Photon OS をデプロイして初期設定を行いました。

もともとの目的は、vCenter Server Appliance 6.5(vCSA 6.5)をお行儀のよい構成でインストールするために必要な DNS サーバを作ることです。よって今回は、前回構築した Photon OS 上で、bind をサービスする Docker コンテナを作ります。

設計

こんな感じにします。

  • ベース OS は Alpine Linux にする
  • bind を追加する
  • bind の設定ファイル群はホスト OS 上のディレクトリに置き、コンテナからマウントさせる
  • Docker Compose で起動・停止できるようにする

ベース OS の選択には特にこだわりはないので、流行に乗って Alpine Linux にします。最新版を使います。

設定ファイルの置き場所は悩みどころですが、設定の変更などがしやすくなるのでこうしました。ポータビリティは下がりますが、配布予定もないし単一のサーバでしかホストしないのでよしとします。

Docker Compose は蛇足です。本来は複数のコンテナを連携させるときに使うものですが、使ってみたかったので使ってみます。

以降、やっぱり文字ばかりになるので、きれいな孔雀の写真を載せておきます。配色が Photon OS のそれと似ていますね。

Dockerfile を作る

まずは Dockerfile です。作ります。コンテナイメージのレシピみたいなものです。

kuro@kuro-ph01 [ ~ ]$ vim Dockerfile
kuro@kuro-ph01 [ ~ ]$ cat Dockerfile
FROM alpine:latest
RUN apk add bind --no-cache
EXPOSE 53/udp
CMD ["/usr/sbin/named", "-c", "/etc/bind/named.conf", "-u", "named", "-g"]

bind の設定ファイル群を作る

コンテナの中で動かす bind のための設定ファイルを作ります。

メインの named.conf と、ゾーン定義の二つのファイルを作りました。作ったファイルはひとつのディレクトリにまとめておきます。コンテナの中の named ユーザが読み取れるように、パーミッションも変更します。

kuro@kuro-ph01 [ ~ ]$ mkdir conf
kuro@kuro-ph01 [ ~ ]$ vim conf/named.conf
kuro@kuro-ph01 [ ~ ]$ cat conf/named.conf
acl localnet {
    127.0.0.1;
    192.168.0.0/24;
};

options {
    version "unknown";
    directory "/var/bind";
    pid-file "/var/run/named/named.pid";
    recursion yes;
    notify no;

    listen-on { any; };
    listen-on-v6 { none; };

    allow-query { localnet; };
    allow-query-cache { localnet; };
    allow-recursion { localnet; };
    allow-transfer { none; };

    forwarders { 192.168.0.1; };
};

zone "kuro.local" IN {
    type master;
    file "/etc/bind/kuro.local.zone";
};
kuro@kuro-ph01 [ ~ ]$ vim conf/kuro.local.zone
kuro@kuro-ph01 [ ~ ]$ cat conf/kuro.local.zone
$TTL 1h
@ IN SOA ns.kuro.local postmaster.kuro.local. (
    2017030501 ; serial
    1h         ; refresh
    15m        ; retry
    1d         ; expire
    1h         ; minimum
);

@          IN NS ns.kuro.local.
ns         IN A  192.168.0.249
kuro-vc01  IN A  192.168.0.250
kuro-esx01 IN A  192.168.0.251
kuro@kuro-ph01 [ ~ ]$ chmod 755 conf
kuro@kuro-ph01 [ ~ ]$ chmod 644 conf/*

コンテナイメージをビルドして起動する

ここまで用意ができたら、あとはビルドして起動させるだけです。

まずはビルドします。ビルドは build サブコマンドです。末尾でビルドしたい Dockerfile を含むディレクトリを指定します。今回はカレントディレクトリなのでドット(.)です。

kuro@kuro-ph01 [ ~ ]$ docker build -t bind .
Sending build context to Docker daemon 11.78 kB
Step 1 : FROM alpine:latest
latest: Pulling from library/alpine
627beaf3eaaf: Pull complete
Digest: sha256:58e1a1bb75db1b5a24a462dd5e2915277ea06438c3f105138f97eb53149673c4
Status: Downloaded newer image for alpine:latest
 ---> 4a415e366388
Step 2 : RUN apk add bind --no-cache
 ---> Running in 84d3fb22724e
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
(1/5) Installing libgcc (6.2.1-r1)
(2/5) Installing libxml2 (2.9.4-r1)
(3/5) Installing bind-libs (9.10.4_p6-r0)
(4/5) Installing libcap (2.25-r1)
(5/5) Installing bind (9.10.4_p6-r0)
Executing bind-9.10.4_p6-r0.pre-install
Executing busybox-1.25.1-r0.trigger
OK: 9 MiB in 16 packages
 ---> 29500538ea16
Removing intermediate container 84d3fb22724e
Step 3 : EXPOSE 53/udp
 ---> Running in a8ea36f4c12f
 ---> f08f46db2947
Removing intermediate container a8ea36f4c12f
Step 4 : CMD /usr/sbin/named -c /etc/bind/named.conf -u named -g
 ---> Running in a2fd9c1acd90
 ---> bf1581067386
Removing intermediate container a2fd9c1acd90
Successfully built bf1581067386
kuro@kuro-ph01 [ ~ ]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
bind                latest              bf1581067386        41 seconds ago      8.772 MB
alpine              latest              4a415e366388        2 weeks ago         3.984 MB

できたイメージは images サブコマンドで確認できます。無事にできたようなので動かしてみましょう。

起動は run サブコマンドです。バックグラウンドで起動(-d)させ、ホストのポートとディレクトリをコンテナに割り当て(-p、-v)ます。

kuro@kuro-ph01 [ ~ ]$ docker run -d -p 53:53/udp -v $(pwd)/conf:/etc/bind --restart always --name bind bind
442a5494f13328641cc3ba6c526a65591874f01476255a47f962dbc8922e0f3b
kuro@kuro-ph01 [ ~ ]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
442a5494f133        bind                "/usr/sbin/named -c /"   3 seconds ago       Up 2 seconds        0.0.0.0:53->53/udp   bind

起動状態は ps サブコマンドで確認できます。この結果の STATUS が Up になっていれば、少なくともコンテナは正しく動いています。あとはコンテナ内で bind が正しく動いていれば問題ありません。

というわけで、まずは自分自身に DNS のクエリを投げてみます。ミニマル構成の Photon OS には nslookup も dig もないので、bindutils をインストールしてから試します。

kuro@kuro-ph01 [ ~ ]$ sudo tdnf install bindutils
[sudo] password for kuro
Sorry, try again.
[sudo] password for kuro

Installing:
bindutils                                            x86_64              9.10.4-1.ph1                            6.86 M

Total installed size: 6.86 M
Is this ok [y/N]:y

Downloading:
bindutils                              3116681    100%
Testing transaction
Running transaction

Complete!
kuro@kuro-ph01 [ ~ ]$ dig @192.168.0.249 kuro-vc01.kuro.local

; <<>> DiG 9.10.4-P1 <<>> @192.168.0.249 kuro-vc01.kuro.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49190
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;kuro-vc01.kuro.local.          IN      A

;; ANSWER SECTION:
kuro-vc01.kuro.local.   3600    IN      A       192.168.0.250

;; AUTHORITY SECTION:
kuro.local.             3600    IN      NS      ns.kuro.local.

;; ADDITIONAL SECTION:
ns.kuro.local.          3600    IN      A       192.168.0.249

;; Query time: 0 msec
;; SERVER: 192.168.0.249#53(192.168.0.249)
;; WHEN: Tue Mar 21 00:04:46 JST 2017
;; MSG SIZE  rcvd: 98

kuro@kuro-ph01 [ ~ ]$ nslookup google.com 192.168.0.249
Server:         192.168.0.249
Address:        192.168.0.249#53

Non-authoritative answer:
Name:   google.com
Address: 172.217.25.238

定義したゾーンの情報も引けていますし、再帰問い合わせもできています。よさそうです。

別の端末からも叩いてみます。例えば Windows から。

C:\Users\kuro>nslookup kuro-vc01.kuro.local 192.168.0.249
サーバー:  UnKnown
Address:  192.168.0.249

名前:    kuro-vc01.kuro.local
Address:  192.168.0.250

C:\Users\kuro>nslookup google.com 192.168.0.249
サーバー:  UnKnown
Address:  192.168.0.249

権限のない回答:
名前:    google.com
Address:  216.58.197.142

よいですね。

うまく動かない場合は、以下のように logs サブコマンドで named のログを見るか、

kuro@kuro-ph01 [ ~ ]$ docker logs bind
20-Mar-2017 15:00:53.955 starting BIND 9.10.4-P6 <id:a6837d0> -c /etc/bind/named.conf -u named -g
20-Mar-2017 15:00:53.955 running on Linux x86_64 4.4.41-1.ph1-esx #1-photon SMP Tue Jan 10 23:46:44 UTC 2017
20-Mar-2017 15:00:53.955 built with '--build=x86_64-alpine-linux-musl' '--host=x86_64-alpine-linux-musl' '--prefix=/usr' '--sysconfdir=/etc/bind' '--localstatedir=/var' '--with-openssl=/usr' '--enable-linux-caps' '--with-libxml2' '--enable-threads' '--enable-filter-aaaa' '--enable-ipv6' '--enable-shared' '--enable-static' '--with-libtool' '--with-randomdev=/dev/random' '--mandir=/usr/share/man' '--infodir=/usr/share/info' 'build_alias=x86_64-alpine-linux-musl' 'host_alias=x86_64-alpine-linux-musl' 'CC=gcc' 'CFLAGS=-Os -fomit-frame-pointer -D_GNU_SOURCE' 'LDFLAGS=-Wl,--as-needed' 'CPPFLAGS=-Os -fomit-frame-pointer'
20-Mar-2017 15:00:53.955 ----------------------------------------------------
20-Mar-2017 15:00:53.955 BIND 9 is maintained by Internet Systems Consortium,
20-Mar-2017 15:00:53.955 Inc. (ISC), a non-profit 501(c)(3) public-benefit
20-Mar-2017 15:00:53.955 corporation.  Support and training for BIND 9 are
20-Mar-2017 15:00:53.955 available at https://www.isc.org/support
20-Mar-2017 15:00:53.955 ----------------------------------------------------
20-Mar-2017 15:00:53.955 found 1 CPU, using 1 worker thread
20-Mar-2017 15:00:53.955 using 1 UDP listener per interface
.
.
.

あるいは、以下のようにしてコンテナ内のシェルに入って調査します。ps や ls をしているのはただの例なので、実際は自由にトラブルシュートしてください。

kuro@kuro-ph01 [ ~ ]$ docker exec -it bind /bin/ash
/ # ps -ef
PID   USER     TIME   COMMAND
    1 named      0:00 /usr/sbin/named -c /etc/bind/named.conf -u named -g
    8 root       0:00 /bin/ash
   12 root       0:00 ps -ef
/ # ls -l /etc/bind/
total 8
-rw-r--r--    1 1000     1000           306 Mar 20 13:57 kuro.local.zone
-rw-r--r--    1 1000     1000           495 Mar 20 14:04 named.conf

なお、Dockerfile で記述した通り、今回は CMD 行で named を -g で起動させています。このため、exec サブコマンドでなく attach サブコマンドを利用しても、named につながるだけで何のトラブルシュートもできない点には注意が必要です。上記のように、exec サブコマンドを利用して明示的に新規プロセスとしてシェルを起動する必要があります。また、Alpine Linux のシェルは /bin/bash ではなく /bin/ash です。

exec サブコマンドは起動中のコンテナに対してしかできない操作なので、もしそもそもコンテナの起動がコケている場合は、上記のコマンドではシェルに入れません。この場合は、exec サブコマンドを run サブコマンドに置き換えて、明示的に起動させます。これによりコンテナの CMD 指定が /bin/ash でオーバライドされるので、named は起動されずに(=コンテナが落ちずに)シェルがフォアグラウンドで実行されます。

よくあるのは、docker ps したときの STATUS が Restarting のままになるトラブルです。大体の場合、named が設定ファイルの読み込みでコケてこうなります。コケて named が落ちてコンテナが終了するものの、restart オプションによって延々と再度の起動が試行されている状態です。この場合は、ファイルのパーミッションや、ボリュームマウントの指定が正しいか確認します。

Docker Compose で操作する

ここまでで当初の最低限の要件は満たせたので、あとはオマケです。

まっさらな状態にするために、動かしていたコンテナを停止し削除、イメージも全部消しておきます。

kuro@kuro-ph01 [ ~ ]$ docker stop bind
bind
kuro@kuro-ph01 [ ~ ]$ docker rm bind
bind
kuro@kuro-ph01 [ ~ ]$ docker rmi bind
Untagged: bind:latest
Deleted: sha256:bf1581067386a7eb1de3b86773c2d89a4f901ab190d6e74679c541a91e5c1d12
Deleted: sha256:f08f46db294799dbdf60b982abc0431ffb11bb046558888b434ba3cbbe47091e
Deleted: sha256:29500538ea168fc7cee66a294b3ed90a3420cc299ac9d84896b2784ecf81f609
Deleted: sha256:c193e93d56d7b0dc758de256cf867bc724aa3ef55ddb14dfdb804b09303fb84e
kuro@kuro-ph01 [ ~ ]$ docker rmi alpine
Untagged: alpine:latest
Untagged: alpine@sha256:58e1a1bb75db1b5a24a462dd5e2915277ea06438c3f105138f97eb53149673c4
Deleted: sha256:4a415e3663882fbc554ee830889c68a33b3585503892cc718a4698e91ef2a526
Deleted: sha256:23b9c7b43573dd164619ad59e9d51eda4095926729f59d5f22803bcbe9ab24c2
kuro@kuro-ph01 [ ~ ]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
kuro@kuro-ph01 [ ~ ]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

あとは、以下な感じになるようにディレクトリを切って、これまでに作ったファイルを移動させておきます。

[~/docker]
 └ [bind]
   ├ Dockerfile
   └ [conf]
     ├ kuro.local.zone
     └ named.conf
kuro@kuro-ph01 [ ~ ]$ mkdir -p docker/bind
kuro@kuro-ph01 [ ~ ]$ mv Dockerfile conf docker/bind

では、今回作ったコンテナを、Docker Compose で操作できるようにしてみます。

本来は複数コンテナを簡単に連携させるために使うものですが、旨味は少ないながらも単一のコンテナを操作したいだけの目的でも使えます。

まずは Docker Compose をインストールします。上記ドキュメントに記載があるコマンドをそのまま実行するだけです。

kuro@kuro-ph01 [ ~ ]$ sudo curl -L "https://github.com/docker/compose/releases/download/1.11.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   600    0   600    0     0    330      0 --:--:--  0:00:01 --:--:--   330
100 8066k  100 8066k    0     0  23737      0  0:05:47  0:05:47 --:--:-- 15090
kuro@kuro-ph01 [ ~ ]$ sudo chmod +x /usr/local/bin/docker-compose
kuro@kuro-ph01 [ ~ ]$ sudo docker-compose --version
docker-compose version 1.11.2, build dfed245

インストールができたら、設定ファイルを作ります。

kuro@kuro-ph01 [ ~ ]$ vim docker/docker-compose.yml
kuro@kuro-ph01 [ ~ ]$ cat docker/docker-compose.yml
version: "2.0"
services:
  bind:
    container_name: bind
    build: bind
    image: bind
    volumes:
      - ./bind/conf:/etc/bind
    ports:
      - 53:53/udp
    restart: always

あとは起動させるだけです。バックグラウンドで実行させたいので、up サブコマンドに -d を付けます。

kuro@kuro-ph01 [ ~ ]$ cd docker
kuro@kuro-ph01 [ ~/docker ]$ sudo docker-compose up -d
Building bind
Step 1 : FROM alpine:latest
latest: Pulling from library/alpine
627beaf3eaaf: Pull complete
Digest: sha256:58e1a1bb75db1b5a24a462dd5e2915277ea06438c3f105138f97eb53149673c4
Status: Downloaded newer image for alpine:latest
 ---> 4a415e366388
Step 2 : RUN apk add bind --no-cache
 ---> Running in 7cb9b9b1d9c5
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
(1/5) Installing libgcc (6.2.1-r1)
(2/5) Installing libxml2 (2.9.4-r1)
(3/5) Installing bind-libs (9.10.4_p6-r0)
(4/5) Installing libcap (2.25-r1)
(5/5) Installing bind (9.10.4_p6-r0)
Executing bind-9.10.4_p6-r0.pre-install
Executing busybox-1.25.1-r0.trigger
OK: 9 MiB in 16 packages
 ---> 3f4617f7e3e7
Removing intermediate container 7cb9b9b1d9c5
Step 3 : EXPOSE 53/udp
 ---> Running in 828a66a3c5f8
 ---> 47c21a9ae08f
Removing intermediate container 828a66a3c5f8
Step 4 : CMD /usr/sbin/named -c /etc/bind/named.conf -u named -g
 ---> Running in 13e41b8d96c7
 ---> 85edc496264d
Removing intermediate container 13e41b8d96c7
Successfully built 85edc496264d
WARNING: Image for service bind was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating bind

docker-compose.yml の記述に従って、各サービス(コンテナ)が起動されます。build が指定されていた場合は、Dockerfile に従ってコンテナイメージのビルドも行います。

起動状態は、ps サブコマンドで確認できます。

kuro@kuro-ph01 [ ~/docker ]$ sudo docker-compose ps
Name              Command               State         Ports
------------------------------------------------------------------
bind   /usr/sbin/named -c /etc/bi ...   Up      0.0.0.0:53->53/udp

ログの確認は logs サブコマンドです。

kuro@kuro-ph01 [ ~/docker ]$ sudo docker-compose logs
Attaching to bind
bind    | 20-Mar-2017 16:20:24.015 starting BIND 9.10.4-P6 <id:a6837d0> -c /etc/bind/named.conf -u named -g
bind    | 20-Mar-2017 16:20:24.015 running on Linux x86_64 4.4.41-1.ph1-esx #1-photon SMP Tue Jan 10 23:46:44 UTC 2017
bind    | 20-Mar-2017 16:20:24.015 built with '--build=x86_64-alpine-linux-musl' '--host=x86_64-alpine-linux-musl' '--prefix=/usr' '--sysconfdir=/etc/bind' '--localstatedir=/var' '--with-openssl=/usr' '--enable-linux-caps' '--with-libxml2' '--enable-threads' '--enable-filter-aaaa' '--enable-ipv6' '--enable-shared' '--enable-static' '--with-libtool' '--with-randomdev=/dev/random' '--mandir=/usr/share/man' '--infodir=/usr/share/info' 'build_alias=x86_64-alpine-linux-musl' 'host_alias=x86_64-alpine-linux-musl' 'CC=gcc' 'CFLAGS=-Os -fomit-frame-pointer -D_GNU_SOURCE' 'LDFLAGS=-Wl,--as-needed' 'CPPFLAGS=-Os -fomit-frame-pointer'
bind    | 20-Mar-2017 16:20:24.015 ----------------------------------------------------
bind    | 20-Mar-2017 16:20:24.015 BIND 9 is maintained by Internet Systems Consortium,
bind    | 20-Mar-2017 16:20:24.015 Inc. (ISC), a non-profit 501(c)(3) public-benefit
bind    | 20-Mar-2017 16:20:24.015 corporation.  Support and training for BIND 9 are
bind    | 20-Mar-2017 16:20:24.015 available at https://www.isc.org/support
bind    | 20-Mar-2017 16:20:24.015 ----------------------------------------------------
bind    | 20-Mar-2017 16:20:24.015 found 1 CPU, using 1 worker thread
bind    | 20-Mar-2017 16:20:24.015 using 1 UDP listener per interface
.
.
.

コンテナの停止や削除は、それぞれ stop サブコマンドや rm サブコマンドで実行できます。

kuro@kuro-ph01 [ ~/docker ]$ sudo docker-compose stop
Stopping bind ... done
kuro@kuro-ph01 [ ~/docker ]$ sudo docker-compose rm
Going to remove bind
Are you sure? [yN] y
Removing bind ... done
kuro@kuro-ph01 [ ~/docker ]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

当然ながら、それぞれネイティブの docker コマンドによる操作も可能です。

イメージの削除は docker-compose コマンドではできないので、docker コマンドで個別に行う必要があります。ただし、イメージをビルドしなおしたいだけであれば、build サブコマンドを利用するか、up サブコマンドの実行時に –build オプションを付与することで実現できます。

まとめ

Docker 上で bind を動かし、Docker Compose で操作できるようにしました。

自前でがんばりたくない場合は、探せば Docker Hub に既成の bind サービス用コンテナがいくつかある(例えばこれとか)ので、そういうのを使うのも有効です。その手の既成コンテナはだいたい Webmin がくっついていて非常に楽です。

また、今回は Docker Compose を使いましたが、単一コンテナを扱いたいだけなのであれば、Makefile を利用することもできそうです。Photon OS にも make はインストールできます。

次回は vCSA 6.5 の導入を行います。


コメントを投稿