CLOVER🍀

That was when it all began.

docker image build時に、DNSの設定をなんとかしたい

これは、なにをしたくて書いたもの?

Dockerコンテナ内で、たまに名前解決できなくて困ることがあります。

それを、Dockerイメージをビルドする時に同じようにハマったら、どうしましょうか?という話。

環境

今回の環境は、こちら。Ubuntu Linux 18.04 LTS。

$ uname -srvmpio
Linux 4.18.0-25-generic #26~18.04.1-Ubuntu SMP Thu Jun 27 07:28:31 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux


$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:    18.04
Codename:   bionic


$ docker -v
Docker version 19.03.8, build afacb8b7f0

まずは、コンテナ実行時を確認してみる

Ubuntu Linuxのlatestイメージを起動してみます。

$ docker container run -it --rm ubuntu:latest bash

現時点では、実質Ubuntu Linux 18.04 LTSです。

# cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.4 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

ここで、apt updateしてみると…失敗する時があります。

# apt update -y
Err:1 http://security.ubuntu.com/ubuntu bionic-security InRelease        
  Temporary failure resolving 'security.ubuntu.com'
Err:2 http://archive.ubuntu.com/ubuntu bionic InRelease                  
  Temporary failure resolving 'archive.ubuntu.com'
Err:3 http://archive.ubuntu.com/ubuntu bionic-updates InRelease
  Temporary failure resolving 'archive.ubuntu.com'
Err:4 http://archive.ubuntu.com/ubuntu bionic-backports InRelease
  Temporary failure resolving 'archive.ubuntu.com'
Reading package lists... Done        
Building dependency tree       
Reading state information... Done
All packages are up to date.
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/bionic/InRelease  Temporary failure resolving 'archive.ubuntu.com'
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/bionic-updates/InRelease  Temporary failure resolving 'archive.ubuntu.com'
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/bionic-backports/InRelease  Temporary failure resolving 'archive.ubuntu.com'
W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/bionic-security/InRelease  Temporary failure resolving 'security.ubuntu.com'
W: Some index files failed to download. They have been ignored, or old ones used instead.

名前解決ができていないようです。

このような場合は、コンテナ内で/etc/resolv.confを修正してDNSサーバーを設定してあげるか、

# echo 'nameserver 8.8.8.8' >> /etc/resolv.conf

コンテナ起動時に「--dns」オプションでDNSサーバーを指定すれば解決します。

$ docker container run -it --rm --dns 8.8.8.8 ubuntu:latest bash

やるなら、後者ですよね。

docker image build時にどうしましょう

ところで、これと同じ事象が「docker image build」時に発生すると、けっこう困ります。

「docker image build」には、「--dns」オプションがないからです。

たとえば、ちょっと適当ですがこんなDockerfileを用意。
Dockerfile

FROM ubuntu:latest

RUN apt-get update -y && \
    apt-get install -y apache2

EXPOSE 80

ENTRYPOINT service apache2 start && tail -f /dev/null

Dockerイメージをビルドしてみます。

$ docker image build -t kazuhira/apache2:latest .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM ubuntu:latest
 ---> 4e5021d210f6
Step 2/4 : RUN apt-get update -y &&     apt-get install -y apache2
 ---> Running in 4d98785d4719
Err:1 http://archive.ubuntu.com/ubuntu bionic InRelease
  Temporary failure resolving 'archive.ubuntu.com'
Err:2 http://security.ubuntu.com/ubuntu bionic-security InRelease
  Temporary failure resolving 'security.ubuntu.com'
Err:3 http://archive.ubuntu.com/ubuntu bionic-updates InRelease
  Temporary failure resolving 'archive.ubuntu.com'
Err:4 http://archive.ubuntu.com/ubuntu bionic-backports InRelease
  Temporary failure resolving 'archive.ubuntu.com'
Reading package lists...
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/bionic/InRelease  Temporary failure resolving 'archive.ubuntu.com'
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/bionic-updates/InRelease  Temporary failure resolving 'archive.ubuntu.com'
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/bionic-backports/InRelease  Temporary failure resolving 'archive.ubuntu.com'
W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/bionic-security/InRelease  Temporary failure resolving 'security.ubuntu.com'
W: Some index files failed to download. They have been ignored, or old ones used instead.
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package apache2
The command '/bin/sh -c apt-get update -y &&     apt-get install -y apache2' returned a non-zero code: 100

まあ、コンテナ実行時に失敗していたのだから、こちらもやっぱり失敗します。

さて、どうしましょう。

Dockerfileで/etc/resolv.confを修正する

単純には、Dockerfile内で/etc/resolv.confを修正します。

RUN echo 'nameserver 8.8.8.8' >> /etc/resolv.conf && \
    apt-get update -y && \
    apt-get install -y apache2

これでうまくいくにはいきますが、ちょっと嫌です。

また、RUN命令を跨ぐとうまくいかなかったりします。

Dockerデーモンのオプションに「--dns」を指定する

Dockerデーモンの起動オプションには「--dns」が使えるので、こちらで回避する選択肢もありそうです。

手元の環境では、Dockerデーモンはsystemdで管理されているので、サービスユニット定義ファイルの「ExecStart」を修正して、
「--dns」オプションを加えます。
/etc/systemd/system/multi-user.target.wants/docker.service

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --dns 8.8.8.8

設定変更を反映して、Dockerデーモンを再起動。

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

これで、Dockerコンテナ内で利用するDNSサーバーが変わります。

とはいえ、起動オプションに設定するのもなぁ、と。

ホスト側のネットワークを使う

もうひとつ思ったのが、ホスト側のネットワークを使って問題ない場合は、ホスト側のネットワークを使ってビルドすればよいのでは?と。

幸い、「docker image build」には「--network」オプションはあるので。こんな感じで。

$ docker image build --network host -t kazuhira/apache2:latest .

Docker Networking overview

これでもうまく行きます。

まあ、今後これで困った場合は、ホストネットワークでビルドですかねぇ。

オマケ

この話に関するissueがありました。

Support --dns or --addn-hosts for docker build · Issue #5779 · moby/moby · GitHub

結局、ここで書いた内容と似たようなことを言っています。

・ configure the daemon to use a different default DNS for containers (this can be configured through daemon.json)
・ use a custom network for your containers (and during build), so that the embedded DNS is used, which allows forwarding requests to the 127.0.0.x DNS server in the host's networking namespace
・ use --network=host to make the container run in the host's networking namespace

Dockerデーモンの設定を変更する、DNSを組み込んだカスタムネットワークを作成して使う、ホスト側のネットワークを使う。

2つ目はやっていませんが、まあいいかな、と。