docker
proxy
FujitsuDay 9

防火壁の中の Docker

More than 1 year has passed since last update.

この投稿は Fujitsu Advent Calender 2016 の 9日目 の記事です。
この記事に書かれた見解は、個人のものであり、所属する会社・組織を代表するものではありません。


proxy に阻まれて Docker が思うように動かないという声を、他のソフトと比べるとよく聞きます。Docker の場合、proxy を意識しなければいけない箇所が少なくとも4つあって、何をしたいかによって、そのうちのどれを設定しないといけないかが変わるので、混乱しやすいのでしょう。加えて、公式ドキュメントに、その4つをまとめて説明してくれるページがないのも、それを助長しているのかもしれません。
そこで、その4つをまとめて説明してみます。

以下の説明で、proxy の URL や、NO_PROXY に設定するドメイン名/IPアドレスは、各自の環境に合わせて読み替えてください。

1. docker pull/push したい

Docker daemon に設定します。
daemon プロセスの環境変数 (HTTP_PROXY, HTTPS_PROXY, NO_PROXY) に設定してあれば効きます。
daemon を systemd で起動している場合は、公式ドキュメントに従って、
/etc/systemd/system/docker.service.d (なければ掘る) の下に、以下のような内容の http-proxy.conf を作って置くだけです。

http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.hogehoge.com:8080/" "HTTPS_PROXY=https://proxy.hogehoge.com:8080/" "NO_PROXY=localhost,127.0.0.1,.hogehoge.com"

作ったあと

systemctl daemon-reload
systemctl restart docker

をお忘れなく。

正しく設定できていれば、docker info で最後の方に以下のように出ます。

Http Proxy: http://proxy.hogehoge.com:8080/
Https Proxy: https://proxy.hogehoge.com:8080/
No Proxy: localhost,127.0.0.1,.hogehoge.com

公式ドキュメント

https://docs.docker.com/engine/admin/systemd/#/http-proxy

余談

swarm mode でノードにアクセスするときも、この設定が効くかも。(swarm mode が daemon に入った v1.12 以降をいまだに触れてないのでよくわかりません。すいません。)
proxy のこっち側のノードのアドレスを NO_PROXY に設定しておかないと、proxy を越えてアクセスしに行ってエラーになり (そうな気がし) ます。

2. コンテナの中から curl したり git clone したりしたい

コンテナ内の環境変数 (HTTP_PROXY, HTTPS_PROXY, NO_PROXY、Java なやつは _JAVA_OPTIONS など) に設定しておけば、たいていのやつはいけます。
docker run --env か docker-compose.yml の environment: でコンテナ実行時に設定します。docker/docker-compose を起動するプロセスの環境変数に HTTP_PROXY などが設定されていれば、それをそのまま使うと楽ちん。

docker run --env HTTP_PROXY=${HTTP_PROXY} -it centos:7 bash
docker-compose.yml
version: '2'
services:
  centos:
    image: centos:7
    environment:
    - HTTP_PROXY=${HTTP_PROXY}

公式ドキュメント

余談

Dockerfile の ENV に書いてビルドしてしまってもいいのですが、そうすると、イメージに環境変数が焼き込まれてしまいます。会社固有の proxy URL や、まして認証 proxy のクレデンシャルが入ったイメージとか、社外には配布できませんから、お勧めしません。
かつて、Dockerfile で UNSETENV HTTP_PROXY みたいに書いて環境変数を unset した上で、環境変数が残ったレイヤーを潰す (squash) という PR もあったのですが、前者の PRは、レビューの結果、まっとうなユースケースが考えられないという理由でクローズされました。後者の PR は v1.13 で experimental として入るようです。

3. Docker build 中に yum install とか curl とか git clone とかしたい

build-arg を使います。
まず、Dockerfile に ARG で、ビルド中にだけ設定する環境変数を指定しておきます。

Dockerfile
ARG HTTP_PROXY

次に、docker build--build-arg オプションか、docker-compose.yml の build: オプションの args: で、ビルド環境変数の値を指定します。
ここでも、docker/docker-compose を起動するプロセスの環境変数が設定されていれば、それをそのまま使えます。

docker build -t myimage --build-arg HTTP_PROXY=${HTTP_PROXY} .
docker-compose.yml
version: '2'
services:
  myimage:
    build:
      context: .
      args:
      - HTTP_PROXY=${HTTP_PROXY}

Dockerfile の ARG で指定していない環境変数を docker build --build-arg や docker-compose.yml で指定するとエラーになります。ただし、HTTP_PROXY など proxy 関係の環境変数は predefine されています。つまり、ARG で指定していなくても、docker build や docker-compose.yml で指定できます。build-arg が、まさにこの目的でサポートされたのがわかりますね。

公式ドキュメント

余談

build 中のコンテナには、docker run のように --env を渡せません。なので、v1.9.0 以前は Dockerfile に ENV で指定するしかなかったのですが、前項で書いたようにイメージに環境変数が残ってしまいます。前述の PR がクローズされた代わりかどうかわかりませんが、v1.9.0 から build-arg がサポート されました。

4. Docker CLI からリモートの daemon を操作したい

CLI のプロセスの環境変数 (HTTP_PROXY, HTTPS_PROXY, NO_PROXY) に設定してあれば効きます。

export HTTP_PROXY="http://proxy.hogehoge.com:8080/"
export NO_PROXY="localhost,127.0.0.1,.hogehoge.com"

proxy の向こうの daemon を操作するというよりは、NO_PROXY をちゃんと設定しておかないと proxy のこっち側の daemon にアクセスできない、という場合の方が多そうです。

公式ドキュメント

https://docs.docker.com/engine/reference/commandline/cli/#/environment-variables

5. 全部に共通の注意事項

5.1 NO_PROXY に IP アドレスを指定する場合

NO_PROXY に IP アドレス を指定する際、CIDR記法やワイルドカードは書けない、というか、書いても効きません。

NO_PROXY=localhost,127.0.0.1,.hogehoge.com,10.0.0.0/8
NO_PROXY=localhost,127.0.0.1,.hogehoge.com,10.*
NO_PROXY=localhost,127.0.0.1,.hogehoge.com,10.

のように書いてもだめということ。IP アドレスは、手書きなり、シェルスクリプトで展開するなりして、個別アドレスを全部列挙するしかありません。

余談

CIDR記法がサポートされていない理由が「curl も wget もサポートしていない、つまり非標準だから」って言うんだけど、curl や wget と一緒になって、みんな一斉にサポートしよー、ってならないもんですかねぇ。(他力本願
https://github.com/docker/docker/issues/9145
https://github.com/docker/docker/issues/19256

5.2 認証 proxy のクレデンシャル

認証 proxy のクレデンシャルは、URL に書けば、たいていの場合効きます。

HTTP_PROXY=http://proxy-user:proxy-pw@proxy.hogehoge.com:8080/

Java なやつは、

_JAVA_OPTIONS="-Dhttp.proxyUser=proxy-user -Dhttp.proxyPassword=proxy-pw -Dhttp.proxyPort=8080 -Dhttp.proxyHost=proxy.hogehoge.com -
Dhttp.nonProxyHosts=localhost|127.0.0.1|*.hogehoge.com"

みたいに、オプションで -D しないとだめなようです。(私は、Java に関してはからっきし知識がないので、つっこみはご容赦のほどを...)

あとアがき

このところ手を動かせてなくて、ずいぶん前に書いた Dockerfile やら docker-compose.yml やらと、公式ドキュメントと、首っぴきで思い出しながら書いてみました。不備や誤りにお気づきの場合は、お手数ですが編集リクエストを投げていただけましたら、謹んで訂正させていただきます。

ちなみに関係ないけど、この記事はせっかくなので Kobito で書いたのですが、残念ながら Kobito さんは proxy を通過してくれなかったので、やむなく md ソースをコピペしました。proxy サポート、期待して待ってますー。