Proxy環境でdockerを外に繋ぐ方法

# はじめに
docker-swarmとか全く知らないdocker素人です。
Proxy環境でdockerの仮想環境を作ったらapt-getが通らなくて無事死亡したので調べてみました。

>あ、一つ言っておきますが、ネットワークの知識はほぼありません。
>ProxyとDNSがどういう機能・役割であるかはわかりますが、どのように連携してシステムになってるのかすら知りません。

前編（？）：[Dockerで開発環境を仮想化する](http://qiita.com/Riliumph/items/3b09e0804d7a04dff85b)

# Docker DaemonにProxy設定を教える
dockerコマンドを実行するときは外部のネットワークに接続する必要があるならDocker Daemon自体にプロキシ設定が必要です。
たとえば、Docker DaemonがProxy設定を知らないと、FROMコマンドでイメージをDLしてくることができないと思います。

Proxy設定には以下の５種類ぐらいがあると思いますが、必要なのは赤色の設定だけみたいです。
Dockerが大文字設定を見るのか、小文字設定を見るのか知らないので両方定義していきます。

- <font color=red>NO_PROXY</font>
- <font color=red>HTTP_PROXY</font>
- <font color=red>HTTPS_PROXY</font>
- SOCK_PROXY
- FTP_PROXY

## upstartの人

```bash:/etc/default/docker
export no_proxy='export no_proxy=127.0.0.1,localhost,xxx.xxx.xxx.xxx'
export NO_PROXY="${no_proxy}"
export http_proxy='http://your.proxy.url:port/'
export HTTP_PROXY="${http_proxy}"
export https_proxy='https://your.proxy.url:port/'
export HTTPS_PROXY="${https_proxy}"
```

```bash:docker再起動
$ sudo service docker restart
```

## Systemdの人

```bash:/etc/systemd/system/docker.service
[Service]
Environment="HTTP_PROXY=http://your.proxy.url:port/" "HTTPS_PROXY=https://your.proxy.url:port/"
```

```bash:docker再起動
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
```


## 確認する

`docker info`コマンドを使って最後辺りに表示されているハズです。

```bash:dockerの設定確認
$ docker info
... ... ...
... ...
HTTP_PROXY: http://your.proxy.url:port/
HTTPS_PROXY:https://your.proxy.url:port/
NO_PROXY: 127.0.0.1,localhost,xxx.xxx.xxx.xxx'
....
```

## もし、次のエラーメッセージが表示されたら

```bash:エラーメッセージ
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: Get https://registry-1.docker.io/v2/: proxyconnect tcp: tls: oversized record received with length 20527.
See 'docker run --help'.
```

このエラーの詳細な原因は正直分かっていません。
だが、HTTPSでエラーを吐いているのでHTTPでアクセスしたら？という乱暴な発想でProxy設定を変えてみたら繋がりました。
`docker info`コマンドで以下の出力になるように、今までのdockerの設定を変えてください。

```bash:dockerの設定確認
$ docker info
... ... ...
... ...
HTTP_PROXY: http://your.proxy.url:port/
HTTPS_PROXY:http://your.proxy.url:port/
NO_PROXY: 127.0.0.1,localhost,xxx.xxx.xxx.xxx'
....
```

……本当にいいのか？


# 現状のDocker Containerの問題点

Docker DaemonにProxy設定を教えたことで、FROMコマンドを使ってイメージをDLすることができたと思います。
しかし、Container側でapt-getなど、インターネットに接続する必要があるコマンドを使う場合はおそらく通りません。

以下の２点を設定したら繋がりました。

- DNS設定
- Proxy環境変数

# Docker ContainerにDNS設定を教える

私の環境ではDNSサーバーが自前に用意されている環境でした。
つまり、そのDNSサーバーにProxyサーバーの名前解決を依頼しているネットワークです。（多分。。。詳しくは知りません）
なので、Proxyサーバーの設定云々の前にDNSサーバーの所在をDocker Container側に教えてあげる必要があります。

## 方法１：コマンドオプションで渡す

dockerのrunコマンド時に設定できます。

```
$ docker run --dns xxx.xxx.xxx.xxx （略）
```

でも、デスクトップPCとか持ち運ばない系の作業環境だと毎回入力するのメンドウですよね
※デスクトップだからといって、テストサーバーと本番サーバーとかProxyの設定が変わるようなお仕事では注意が必要。

## 方法２：デフォルト設定に加える

### upstartの人

``` bash:/etc/default/docker
DOCKER_OPTS="--dns xxx.xxx.xxx.xxx"
```

```bash:再起動コマンド
$ sudo service docker restart
```
### Systemdの人

なんか、実験環境のSystemdは`/etc/systemd/system/multi_user/docker.service`になっていました。
結局、シンボリックリンクで`/lib/docker.d/docker.conf`かなんかに飛ぶんだけど。

以下のパスで見つからない人は、`find`コマンドかなんかで探してみてください。

```bash:/etc/systemd/system/docker.service.d/docker.conf
[Service]
Environment="HTTP_PROXY～" "HTTPS_PROXY～" "DOCKER_NETWORK_OPTIONS=--dns xxx.xx.xxx.xxx"
ExecStart=/usr/bin/dockerd -H fd:// $DOCKER_NETWORK_OPTIONS
```

```bash:docker再起動
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
```

# Docker ContainerにProxy設定を教える

```dockerfile:dockerfile
# 仮想化するベースOS
FROM ubuntu:14.04

########## ROOT WORKS ##########
# Proxy設定
ENV ftp_proxy 'ftp://your.porxy.url:port'
ENV http_proxy 'http://your.porxy.url:port'
ENV https_proxy 'https://your.proxy.url:port'
ENV socks_proxy 'socks://your.proxy.url:port'
ENV no_proxy '127.0.0.1,localhost,xxx.xxx.xxx.xxx'

# 一般ユーザーを追加
RUN :ユーザーアカウント追加 \
 && useradd -m tester \
 && :sudo権限を付与 \
 && gpasswd -a tester sudo \
 && :パスワード設定 \
 && echo "tester:test_pass" | chpasswd

# ディレクトリの移動
WORKDIR /home/tester

# apt-get
RUN apt-get update \
 && apt-get install -y build-essential

########## USER WORKS ##########
# 以降のRUN/CMDを実行するユーザー
USER tester
```

Dockerfileに環境変数として設定してみたんですが、何故かapt-getコマンドが動きませんでした。

```bash:aptエラーメッセージ
Something wicked happened resolving 'your.proxy.co.jp:http'
```

DNSサーバーも設定しましたし、Proxyサーバーの名前解決が出来ないのは正直分かりません。
適当にrootユーザーではapt-getを実行してみると繋がりました。

環境変数もしくはホスト側のresolveを追加したユーザーで参照できていない、とか？

## apt-get用の設定

apt-getコマンドが処理時に参照するapt.confを設定することで通りました。
apt-getは解決できても他のコマンドで外へ行くことができないのではないかと思いますが。

```bash:/etc/apt/apt.conf
Acquire::http:proxy "http://your.proxy.url:port"
Acquire::https:proxy "https://your.proxy.url:port"
Acquire::ftp:proxy "ftp://your.proxy.url:port"
Acquire::socks:proxy "socks://your.proxy.url:port"
```
apt-get解決時は上記のファイルを見るらしいので、このファイルを自動生成させます。
すでに存在するファイルをADDやCOPYでコンテナ側へ持って行ってもいいと思います。

```Dockerfile:Dockerfile
（略）

# Proxy設定
ENV ftp_proxy 'ftp://your.porxy.url:port'
ENV http_proxy 'http://your.porxy.url:port'
ENV https_proxy 'https://your.proxy.url:port'
ENV socks_proxy 'socks://your.proxy.url:port'
ENV no_proxy '127.0.0.1,localhost,xxx.xxx.xxx.xxx'

RUN : "apt-getコマンドに対するProxy設定" \
 && { \
  echo 'Acquire::http:proxy "'${ftp_proxy}'";'; \
  echo 'Acquire::https:proxy "'${http_proxy}'";'; \
  echo 'Acquire::ftp:proxy "'${ftp_proxy}'";'; \
  echo 'Acquire::socks:proxy "'${socks_proxy}'";'; \
    } | tee /etc/apt/apt.conf

（略）
````

echoした内容をファイルをリダイレクトしているだけなので難しくはありませんね。

一応、環境変数とapt.confなどの組み合わせは確かめてみました。

|＼|何もしない|環境変数|apt.conf|
|:--|:--:|:--:|:--|
|root|○|○|○|
|user|×|×|○|

# おわり

いろいろとDockerには落とし穴だったりがあるようですが、さすがに流行っているソフトウェアなだけあって情報は多いです。
頑張って使っていきましょう！！

あと、Dockerfileは[hadolint](https://github.com/hadolint/hadolint)を使ってチェックしてあげてくださいね。