【学習メモ】 DockerでPoundとApacheを使って負荷分散しつつリクエストヘッダを確認する

#はじめに
復習するための学習メモです。
以下の流れで学習しました。
[1.Poundの設定](http://qiita.com/Hige-Moja/items/d214b14ced7c0cd814e8#1pound%E3%81%AE%E8%A8%AD%E5%AE%9A)
[2.Dockerコンテナの準備](http://qiita.com/Hige-Moja/items/d214b14ced7c0cd814e8#2%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E5%89%8D%E6%BA%96%E5%82%99)
[3.ブリッジネットワークの設定](http://qiita.com/Hige-Moja/items/d214b14ced7c0cd814e8#3%E3%83%96%E3%83%AA%E3%83%83%E3%82%B8%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E8%A8%AD%E5%AE%9A)
[4.起動と負荷分散の確認](http://qiita.com/Hige-Moja/items/d214b14ced7c0cd814e8#4%E8%B5%B7%E5%8B%95%E3%81%A8%E5%88%86%E6%95%A3%E3%81%AE%E7%A2%BA%E8%AA%8D)
[5.リクエストヘッダの差を確認](http://qiita.com/Hige-Moja/items/d214b14ced7c0cd814e8#5%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%98%E3%83%83%E3%83%80%E3%81%AE%E5%B7%AE%E3%81%AE%E7%A2%BA%E8%AA%8D)

5では直接アクセスとPound経由のアクセスで、Apacheが受け取るリクエストヘッダで差が出ていることを確認しました。

つまづいたところや、途中でやろうとしたことも残しているので、冗長になっています。

```:環境と出来上がるもの(Dockerfileなど)
Docker Host
    OS     : CentOS 7.3.1611
    docker : 17.06.0-ce, build 02c1d87
    IP     : 192.168.221.134/24
Container(Pound)
    OS     : CentOS 7.3.1611
    image  : centos:pound
    IP     : 172.18.25.25/16
Container(Apache1)
    OS     : Debian jessie
    image  : httpd:apa24-1
    IP     : 172.19.46.46/16
Container(Apache2)
    OS     : Debian jessie
    image  : httpd:apa24-2
    IP     : 172.19.86.86/24
```


#1.Poundの設定
Poundをインストールしてから動作する。
###インストール
```bash:インストール
yum install -y epel-release # epelリポジトリの追加
yum install -y Pound        # Poundのインストール
```
- リポジトリの追加が必要。2017/8/23現在 2.7がインストールされる。


参考資料
[Pound を yum でインストールする手順 (CentOS/RedHat)](https://weblabo.oscasierra.net/install-pound-01/)

コマンドの確認。

```bash:コマンド関連
systemctl start pound  # スタート
systemctl stop pound   # ストップ
pound -V               # バージョン確認
pound -c               # pound.cfgの整合性確認
journalctl -u pound    # ログ確認
systemctl enable pound # サービス登録
```
- インストール直後はサービスの登録が必要。
- pound.cfgを調整するのに、```pound -c```と```systemctl restart pound```、```journalctl -u pound```は良く使った。

###設定ファイルの構成
yumでPoundを入れると設定ファイルが /etc/pound.cfg に生成される。
設定ファイルの構成は、グローバルディレクティブ部とHTTPListener部とServer部の3部から成る。
グローバルディレクティブ部で全体の動作設定を記述し、
HTTP Listener部やServer部で個別のWeb設定を記述していく。
HTTP Listener部やServer部では、グローバルディレクティブ部の設定の一部をオーバーライドして動作できる。
iptablesのように上から順に処理される。

####Global Directives
```bash:/etc/pound.cfg(グローバルディレクティブ部)
### Global Directives
# userとgroup
User "pound"
Group "pound"
# socketの場所
Control "/var/lib/pound/pound.socket"
# 大文字小文字の無視 0:無視しない 1:無視する 
IgnoreCase 1
# LogLevelの設定 0～5まで。大きいほど詳細。1がデフォ。
LogLevel 2
```
- グローバルディレクティブ部はなくても動作する。
- "pound"のUserとGroupはyumインストール時に作成される。
- ```poundctl -c /var/lib/pound/pound.socket```でリクエストのキューが確認できる。(ソケットを指定する)
- Controlでソケットの場所が指定できるのは不思議。[ここ](http://qiita.com/hirata-qwt/items/38ca15b39624a086282c)では拡張子cfgで作ってる。[ここ](https://www.zivtech.com/sites/default/files/etc.pound_.pound_.cfg_.txt)では拡張子socket。mysqlはsockだし、何が適切なのか不明。ソケットだけど、コントロールディレクティブ！
- デフォルトでどんなSocketファイルを使っているのか調べようとした。[調べなきゃ寝れない！と調べたら余計に寝れなくなったソケットの話](http://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748)を参考に、```netstat -alZ | grep pound```で調べる。(先に```yum install net-tools```を入れる必要がある。) poundは見つかったが、Pathが空白。I-Node番号は分かったので[ここ](http://q.hatena.ne.jp/1281884424)を参考に```find -inum [inode_number]```を実行したけど、結果は無し。yumではないけど[Pound2.7のソースコード](http://www.apsis.ch/pound/Pound-2.7.tgz)のpound.hを見ると308行目から329行目あたりに答えがありそうだけど、読めずに断念。
- IgnoreCaseはListenのCheckURLと、ServerのURLで機能する。


####HTTP Listener
```bash:/etc/pound.cfg(HTTPリスナー部)
### HTTP Listener
# Main listening ports
ListenHTTP                  # HTTP Listener部の開始
    Address 192.168.25.25   # poundのIPアドレス
    Port 80                 # poundのポート
    Client 10               # タイムアウト(標準は10秒)
    RewriteDestination 1    # ヘッダのリクエストを書き換える 0:no | 1:yes
    CheckURL ".*"           # GETリクエストをパターンマッチ
```
- ```Server "name"```で定義できるみたいだけど、どうやって使うのか不明。
- Addressは0.0.0.0も可能。

####Service
```bash:/etc/pound.cfg(サービス部、HTTPListnerの中に作る)
# Everybody Server
Service                         # Service部の開始(複数設定可能)
    URL ".*"                      # GETリクエストをパターンマッチ
    HeadRequire "Host: .*"        # スペースを空けた後に設定
    BackEnd                       # BackEndの宣言
        Address 192.168.46.46     # WebサーバーのIP
        Port 80                   # Webサーバーのポート
        Priority 5                # 複数のBackEndを設定するときの比重
    End                           # BackEnd部の終わり
End                             # Service部の終わり
End                         # HTTP Listener部の終了
```
- Listenerの中に複数記述でき、処理を分けられる。
- URLはListenerのCheckURLと同様でGETの内容が対象。```http://qiita.com/privacy``` でアクセスされると、```/privacy```が対象となる。ドメイン名でフィルタリングできない。
ドメイン名でフィルタするときはHeadRequireを使用する。
- HeadRequireはスペースの後に文字列を記載。httpやhttpsなどのスキームは記述できない。
- 正規表現の```.``` ```*``` ```+``` ```\``` ```[]``` ```[^]``` ```{}```は使える。先頭の```^```は使えない。
- Priorityの設定は1～9で5がデフォルト。数字が大きいほど使用頻度が高い。

Poundの設定の学習おわり。

参考資料
[pound(8) - Linux man page](https://linux.die.net/man/8/pound)



#2.コンテナの前準備
##Poundコンテナの前準備
設定で学んだ内容を参考に、pound用のDockerfileを作成する。

```bash:ベースイメージ
[root@localhost]# docker pull centos:7.3.1611
```
```bash:Dockerfile-pound
FROM centos:7.3.1611
RUN yum install epel-release
RUN yum install Pound
RUN mv /etc/pound.cfg /etc/pound.cfg.backup
RUN echo -e \
"ListenHTTP\n \
  Address 172.18.25.25\n \
  Port 80\n \
  Client 10\n \
  RewriteDestination 1\n \
  CheckURL \".*\"\n\n \
  Service\n \
    URL \".*\"\n \
    HeadRequire \"Host: .*\"\n \
    #Redirect \"http://hoge.hoge\"\n \
    BackEnd\n \
      Address 172.19.46.46\n \
      Port 80\n \
      Priority 5\n \
    End\n \
    BackEnd\n \
      Address 172.19.86.86\n \
      Port 80\n \
      Priority 5\n \
    End\n \
  End\n \
End" > /etc/pound.cfg
RUN systemctl enable pound
```
```bash:ビルド
ビルド
[root@localhost]# docker build -t centos:pound - < Dockerfile-pound
```


##Apacheコンテナの前準備
2台で分散するので2つ準備する。
共通のdocker imageをbuildしたあと、さらに個別にBuildする。

```bash:公式のhttpdイメージ
[root@localhost]# docker pull httpd:2.4.27
```
まずApache共通のDockerfileを作る。

```bash:Dockerfile-apa24-base
FROM httpd:2.4.27
### ETagの除去
RUN echo "FileETag None" >> /usr/local/apache2/conf/httpd.conf

### アクセスログとエラーログを記録
# mod_logio.soを有効にする
RUN sed -ie "s@#LoadModule\ logio_module\ modules/mod_logio.so@LoadModule\ logio_module\ modules/mod_logio.so@g" /usr/local/apache2/conf/httpd.conf && \
# アクセスログを追加し、フォーマットをcombinedioに変える。
   sed -ie "s@CustomLog\ /proc/self/fd/1\ common@CustomLog\ \"logs/access_log\" combinedio@g" /usr/local/apache2/conf/httpd.conf && \
# エラーログを追加する
sed -ie "s@ErrorLog\ /proc/self/fd/2@ErrorLog /usr/local/apache2/logs/error_log@g" /usr/local/apache2/conf/httpd.conf

### vimとlessをインストール
RUN apt-get update
RUN apt-get install -y vim less
```
- ETagを外す。2台とも同じ時刻とサイズのファイルが準備されている状態なら、```FileETag MTime Size```を設定すればキャッシュが働くはず。
- デフォルトはアクセスログとエラーログが残らないので、コンテナ起動前にhttpd.confを編集しておく。
- access_logのフォーマットを変更する。httpdイメージのhttpd.confには、LogFormatとしてcomibnedとcombinedioが定義されているので、アクセスログをcombinedioに書き換える。combinedioはmod_logio.soが必要。
- httpdは```cat /etc/os-release```によるとdebian jessieらしい。コマンドラインでテキストを編集するのは至難なので、vimとlessを入れる。

```bash:httpd.confのcombinedとcombinedio
/usr/local/apache2/conf/httpd.conf
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
```
```bash:mod_logio.soを読み込み忘れたとき
# /usr/local/apache2/conf/httpd.conf
# CustomLog "logs/access_log" combinedio
root@apa24-1:/usr/local/apache2# cat logs/access_log
combinedio
combinedio
combinedio
```
参考資料
[負荷分散環境でブラウザキャッシュが効かないときは - ETagの解説 -](http://dsas.blog.klab.org/archives/50602499.html)
[Apache コア機能-FileETag ディレクティブ](https://httpd.apache.org/docs/2.4/ja/mod/core.html#fileetag)
[Apacheのログを活用しよう-atmarkIT](http://www.atmarkit.co.jp/ait/articles/0202/16/news001_2.html)
[(公式)Apache モジュール mod_log_config](https://httpd.apache.org/docs/2.4/ja/mod/mod_log_config.html)


Apache用の共通のイメージをビルドする。

```bash:apa24-baseタグを付けてビルドする。
[root@localhost]# docker build -t httpd:apa24-base - < Dockerfile-apa24-base
```
2台のDockerfileとイメージを準備する。

```bash:Dockerfile-apa24-1
FROM httpd:apa24-base
RUN cd /usr/local/apache2/htdocs ; mkdir tmp ; \
    echo "This is the 4646" > tmp/index.html
```
```bash:Dockerfile-apa24-2
FROM httpd:apa24-base
RUN cd /usr/local/apache2/htdocs ; mkdir tmp ; \
    echo "This is the 8686" > tmp/index.html
```
```bash:2台をビルド
[root@localhost]# docker build -t httpd:apa24-1 - < Dockerfile-apa24-1
[root@localhost]# docker build -t httpd:apa24-2 - < Dockerfile-apa24-2
```
PoundとApacheのDockerイメージの準備おわり。

#3.ブリッジネットワークの設定
###ブリッジの動作確認
初めは docker0 のネットワークに所属するIPアドレスを指定してコンテナを立ち上げるだけで、コンテナ間通信ができると思っていた。でも、IPを指定して起動することができなかった。(ipを指定せず起動したコンテナ=デフォルトでdocker0のbridgeに接続しているコンテナ同士は可能)

```bash:デフォルトでdocker0のbridgeに接続されたコンテナ間のIP疎通を確認する
# httpdコンテナを立ち上げる (cent1 / cent2 とする)
[root@localhost]# docker run -d --privileged -h cent1 --name cent1 centos:7.3.1611 /sbin/init
[root@localhost]# docker run -d --privileged -h cent2 --name cent1 centos:7.3.1611 /sbin/init

# ホストでbridgeにコンテナのネットワークが追加されたことを確認する。cent2のIPアドレスを確認する。
[root@localhost]# docker network inspect bridge

# cent1に入って、cent2にpingを打つ
[root@localhost]# docker exec -it cent1 /bin/bash   
[root@cent1]# ping cent2のIPアドレス
```

- コンテナをデフォルトのbridgeの範囲内(172.17.0.0/16)に指定して起動しても、エラーが出る。

```bash:エラーの例
[root@localhost]# docker run (中略) --ip=172.17.46.46 (後略)
docker: Error response from daemon: user specified IP address is supported on user defined networks only.
```
- docker runでIPを指定するためには、dockerネットワークを自分で作らなければならない。


docker.serviceに対して、bipでdocker0のIPを指定してみた。指定=createと同じ意味になるかと思った。

```bash:docker0のIPを172.17.0.1に変更する例
# docker.serviceファイルの編集
sed -ie "s@\/usr\/bin\/dockerd@\/usr\/bin\/dockerd\ --bip=\"172.17.0.1\"@g" \
    /usr/lib/systemd/system/docker.service

# 設定の反映
systemctl daemon-reload

# 設定の確認
systemctl show docker | grep ExecStart

# Dockerの再起動
systemctl restart docker
```
```bash:エラーその1
/usr/lib/systemd/system/docker.service
 ExecStart=/usr/bin/dockerd --bip=172.17.0.1

(journalctl -u dockerのエラー)
Error starting daemon: Error initializing network controller: invalid CIDR address: 172.17.0.1
```
```bash:エラーその2
/usr/lib/systemd/system/docker.service
 ExecStart=/usr/bin/dockerd --bip="172.17.0.1/16"
Error starting daemon: Error initializing network controller: invalid CIDR address: "172.17.0.1/16"
```
```bash:正しい設定
/usr/lib/systemd/system/docker.service
 ExecStart=/usr/bin/dockerd --bip=172.17.0.1/16
```
- エラーその1:IPアドレスだけだと起動できない。
- エラーその2:ダブルコーテーションを使うと動かない
- 正しい設定:ダブルコーテーション```"```無し。
- 正しい設定でdocker0のIPを指定してコンテナを起動しても、結果は同じくuser defined networks onlyのエラー。


あくまでデフォルトの設定を変えているだけのようで、``--ip=``を付けてコンテナを起動するには、createコマンドを使って新しくネットワークを作る必要がありそう。

###ブリッジネットワークの作成
作成も、コンテナ追加も簡単。
bridge-poundネットワークを作成する。

```bash:bridge-poundの作成
docker network create \
  --driver=bridge \
  --subnet=172.18.0.0/16 \
  --ip-range=172.18.0.0/16 \
  bridge-pound
```
```bash:コンテナ起動
docker run -d -h pound --name pound --net=bridge-pound --ip=172.18.25.25 -p 80:80 centos:pound /sbin/init
```
コンテナ起動時に、```--net=[networkname]```を付けるだけで、指定したネットワークに接続されて起動する。そのとき、デフォルトのbridgeに対して接続されない。

参考資料
[(公式)Docker コンテナ・ネットワークの理解](http://docs.docker.jp/engine/userguide/networking/dockernetworks.html)…公式の図と説明が一番わかりやすい。
[(公式)network コマンドを使う](http://docs.docker.jp/engine/userguide/networking/work-with-networks.html)…実際にどうすればよいかはこちら。
[(公式)Docker daemonのオプション](http://docs.docker.jp/engine/reference/commandline/dockerd.html?highlight=bip)…dockerdに対して引数で何ができるかがわかる。
[systemd と Docker の管理・設定](http://docs.docker.jp/engine/articles/systemd.html#systemd-docker)…外部の設定ファイルの使い方がわかる。
[docker v1.10 から追加される docker run 時の ip指定オプションに関して](https://gist.github.com/mapk0y/3dd5d270e9c058ef3ab9)
[(公式)docker0 ブリッジのカスタマイズ](http://docs.docker.jp/engine/userguide/networking/default_network/custom-docker0.html)…bipがCIDR形式である
[(公式)コマンドラインリファレンス-docker network create](https://docs.docker.com/engine/reference/commandline/network_create/#examples)

###オーバーレイネットワークで失敗した話(未解決)
欲張ってブリッジでなくオーバーレイの中でコンテナをつなげようと思って試そうとしたら失敗した話。
表題の部分とは関係ないので完全にメモ状態。
[ここ](https://www.slideshare.net/zembutsu/whats-new-aobut-docker-2015-network-and-orchestration/33
)の図を見ると、bridgeと同じような感じで、すごく簡単そうに見える。

用語の整理。

```text:用語の整理
swarm   : ホスト同士がリンクするためのグループ
Overlay : Swarm上で動作するブリッジ
ingress : swarmのホストが利用するブリッジ。ロードバランサーのように使える。
```
そもそもingressの機能だけで、負荷分散すればよかったのではないかと思う。

まずはswarmをホスト上で作成し、マネージャーとなる。

```bash:swarmの作成
# swarmを初期化する
[root@localhost]# docker swarm init --listen-addr 192.168.221.134:2377
Swarm initialized: current node (mntrugis8lq5cz9zu0v32zaek) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-0md38fga77iua94azo2ob1py28n1t61il3k5ovrsw7f8lhs9ci-atggm3tunsn7dbdpp0ihmn8qp 192.168.221.134:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# swarmのmanagerか確認する
[root@localhost]# docker info | grep "Is Manager"
WARNING: bridge-nf-call-ip6tables is disabled
 Is Manager: true
```
参考資料 : [(公式)swarm （群れ）の作成](http://docs.docker.jp/engine/swarm/swarm-tutorial/create-swarm.html)

Overlayネットワークを作って、cent1コンテナをつなげてみる。

```bash:失敗例!!
# overlayネットワーク overlay0 を作る
[root@localhost]# docker network create -d overlay --subnet=172.19.0.0/24 overlay0

# cent1コンテナの立ち上げ
[root@localhost]# docker run -d -h cent1 --name cent1 \
  --net=overlay0 --ip=172.19.1.1 -p 81:80 centos:7.3.1611 /sbin/init

docker: Error response from daemon: Could not attach to network overlay0: rpc error: code = 7 desc = network overlay0 not manually attachable.
```
エラーの内容は、[attachableオプションが足りなかった](https://github.com/moby/moby/issues/27064)みたい。
overlay0のオプションを変えてもう一度立ち上げる。

```bash:失敗例2!!
# overlay0の削除
[root@localhost]# docker network rm overlay0
# overlay0の再作成
[root@localhost]# docker network create --attachable -d overlay --subnet=172.19.0.0/24 overlay0
# cent1コンテナの再立ち上げ
[root@localhost]# docker run -d -h cent1 --name cent1 \
  --net=overlay0 --ip=172.19.1.1 -p 81:80 centos:7.3.1611 /sbin/init
docker: Error response from daemon: attaching to network failed, make sure your network options are correct and check manager logs: context deadline exceeded.
```
またエラー。どうやら[dockerのbug?のよう](https://github.com/prometheus/prometheus/issues/2459)
ということで、コンテナをネットワークに接続させずに立ち上げ、立ち上げたあとにoverlay0に参加するようにしてみる。

```bash:
# cent1コンテナの再々立ち上げ(--net=none)
[root@localhost]# docker run -d -h cent1 --name cent1 \
  --net=none --ip=172.19.1.1 -p 81:80 centos:7.3.1611 /sbin/init
# cent1をoverlay0に接続する
[root@localhost]# docker network connect --ip=172.19.1.1 overlay0 cent1
Error response from daemon: attaching to network failed, make sure your network options are correct and check manager logs: context deadline exceeded
```
失敗…

```bash:やってみる
# オプション無しでcent1をoverlay0につないでみる
[root@localhost ~]# docker network connect overlay0 cent1
Error response from daemon: attaching to network failed, make sure your network options are correct and check manager logs: context deadline exceeded

# cent1を既存のブリッジにつないでみる
[root@localhost ~]# docker network connect bridge-pound cent1
Error response from daemon: container cannot be connected to multiple networks with one of the networks in private (none) mode
```
エラーが変わった。```--net=none```はprivate modeだから後からネットワークに接続できないのかもしれない。[noneはネットへの接続を禁止するもの](https://github.com/moby/moby/issues/21132)じゃないかというコメントがあった。

既存のBridgeにつないでコンテナを立ち上げたあと、overlay0につないでみる。

```bash:
# cent1コンテナの再々々立ち上げ(--net=bridge-pound)
[root@localhost]# docker run -d -h cent1 --name cent1 \
  --net=bridge-pound -p 81:80 centos:7.3.1611 /sbin/init

# cent1をoverlay0につなげる
docker network connect --ip=172.19.1.1 overlay0 cent1
Error response from daemon: attaching to network failed, make sure your network options are correct and check manager logs: context deadline exceeded
```
失敗…(´;ω;`)
とりあえずpoundとapacheが終わるまで保留。

参考資料:
[(公式)network connect -コマンドリファレンス](http://docs.docker.jp/engine/reference/commandline/network_connect.html)
[(公式)swarmを検証環境で試すには](http://docs.docker.jp/swarm/install-w-machine.html)
[Dockerのoverlayネットワークでコンテナを分散実行](http://qiita.com/zembutsu/items/4cd756ff8be4be76e5a3)
余談:探せば探すほど、Zembutsu Masahitoさんの資料がヒットする。しかもわかりやすい。

###apache1/2を外部からアクセス不可にする
swarmで大失敗したが、bridgeだけでは物足りないので、少し欲張ってみる。
Webサーバーを外部から切り離して、Poundサーバー経由でのみアクセスできるようにする。

```bash:やってみること
ブリッジ名    : 用途
bridge-pound : poundが外部と通信するためのネットワーク
bridge-int   : poundとapa1とapa2だけが通信するネットワーク

ブリッジ名    : コンテナ名
bridge       : cent0
bridge-pound : cent1
bridge-int   : cent2, cent3

ブリッジ名    : ネットワーク
bridge       : 172.17.0.0/16
bridge-pound : 172.18.0.0/16
bridge-int   : 172.19.0.0/16
```
```bash:ブリッジ(bridge-int)の作成
[root@localhost]# docker network create \
  --driver=bridge \
  --internal \
  --subnet=172.19.0.0/16 \
  --ip-range=172.19.0.0/16 \
  bridge-int
```
- internalオプションを付けて作成する、外部につながらないネットワークになる。

cent0をbridge(デフォルト)
cent1をbridge-pound(pound)
cent2とcent3(httpd)をbridge-int(Internal)
につなげて立ち上げる。

```bash:立ち上げ
[root@localhost]# docker run -d -h cent0 --name cent0 \
  centos:7.3.1611 /sbin/init
[root@localhost]# docker run -d -h cent1 --name cent1 \
  --net=bridge-pound centos:7.3.1611 /sbin/init
[root@localhost]# docker run -d -h cent2 --name cent2 \
  --net=bridge-int centos:7.3.1611 /sbin/init
[root@localhost]# docker run -d -h cent3 --name cent3 \
  --net=bridge-int -p 8080:80 httpd
```
#####疎通確認(Internalから)
```bash:疎通確認(internalネットワークから)
# ping cent2からcent3へ
[root@localhost]# docker exec -it cent2 /bin/bash
[root@cent2]# ping cent3 -c1 
PING cent3 (172.19.0.3) 56(84) bytes of data.
64 bytes from cent3.bridge-int (172.19.0.3): icmp_seq=1 ttl=64 time=0.200 ms

# ping cent2からcent0へ
[root@cent2]# ping cent0 -c1
ping: cent0: Name or service not know

# ping cent2からcent1へ
[root@cent2]# ping cent1 -c1
ping: cent1: Name or service not known

# ping cent2から8.8.8.8へ
[root@cent2]# # ping 8.8.8.8 -c1
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

# ping 192.168.221.134 -c1
[root@cent2]PING 192.168.221.134 (192.168.221.134) 56(84) bytes of data.
```
- cent2(bridge-int)から、cent0(bridge)やcent1(bridge-pound)、外部へpingは通らない。
- cent2から、dockerホストへは通る。

#####疎通確認(他のコンテナをInternalにつなげる)
cent1(bridge-pound)をbridge-intにつなげる。

```bash:cent1をbridge-intをつなげる
[root@localhost]# docker network connect bridge-int cent1
```
```bash:疎通確認#2
# ping cent2からcent1へ
[root@localhost]# docker exec -it cent2 /bin/bash
bash: printf: write error: Interrupted system call
[root@cent2]# ping cent1 -c1
PING cent1 (172.19.0.4) 56(84) bytes of data.
64 bytes from cent1.bridge-int (172.19.0.4): icmp_seq=1 ttl=64 time=0.163 ms
```
- cent1をbridge-intをつなげたので、cent2からcent1へpingが通るようになった。
- cent1はbridge-poundとbridge-intの両方に属している。


#####疎通確認(Hostから)
Hostからの通信はどうか。

```bash:疎通確認#3
# Hostからcent3へ #1
[root@localhost]# ping cent3 -c1
ping: cent3: 名前またはサービスが不明です
[root@localhost]# curl localhost:8080
curl: (7) Failed connect to localhost:8080; 接続を拒否されました

# Hostからcent3へ #2 (IP)
[root@localhost]# ping 172.20.0.3 -c1
PING 172.20.0.3 (172.20.0.3) 56(84) bytes of data.
64 bytes from 172.19.0.3: icmp_seq=1 ttl=64 time=0.093 ms
[root@localhost]# curl 172.19.0.3
<html><body><h1>It works!</h1></body></html>
```
- ホストからinternalへ名前解決はできない。
- ホストのport:8080に対して、cent3は```-p```オプションでport:80をつなげて起動しているはずだが、ホストのport:8080に接続しようとしても接続拒否されている。
- IPではアクセスできる。


#####疎通確認(bridgeとinternal)
既存のbridgeとinternalの疎通はどうか。

```bash:疎通確認#4(bridgeからbridge-intへの接続確認)
# cent0からcent3へ #1
[root@cent0]# ping 172.19.0.3 -c1
PING 172.19.0.3 (172.19.0.3) 56(84) bytes of data.
--- 172.19.0.3 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

# cent0からcent3へ #2
# ホストの8080(cent3の80)に通じるか確認
[root@cent0]# ping 192.168.221.134 -c1
PING 192.168.221.134 (192.168.221.134) 56(84) bytes of data.
64 bytes from 192.168.221.134: icmp_seq=1 ttl=64 time=0.095 ms
[root@cent0]# curl 192.168.221.134:8080
curl: (7) Failed connect to 192.168.221.134:8080; No route to host
```
- bridge経由でinternalネットワークには接続できない。

```bash:cent2からホストの8080を経由してcent3に接続する。
[root@cent2]# curl 192.168.221.134:8080                                                                                                      
curl: (7) Failed connect to 192.168.221.134:8080; No route to host
```
- ```-p```オプションでポートフォワーディング(NAPT?)したとしても、結局のところホストからしか接続できない。


第二課題としたネットワークの分離はうまくいった
(\*´ω`\*)


参考資料
[(公式)Docker runリファレンス-ネットワーク設定](http://docs.docker.jp/engine/reference/run.html#network-settings)…docker runのネットワーク系オプションがわかる。
[Dockerで固定IPアドレスを使う。](http://qiita.com/paihu/items/17aa47906dd2bf935a25)
[(公式)Customize the docker0 bridge](https://docs.docker.com/engine/userguide/networking/default_network/custom-docker0/)
[Dockerの内部ネットワークのプライベート・アドレス帯を変更する](http://qiita.com/martymarron/items/b3e73aa1ea5a939d53e1)
[(公式)Docker コンテナ・ネットワークの理解](http://docs.docker.jp/engine/userguide/networking/dockernetworks.html)
#4.起動と分散の確認
###コンテナの起動
準備したイメージから、Pound、Apache2つの計3つを起動する。

poundの起動

```bash:poundの起動
[root@localhost]# docker run --privileged -d -h pound \
   --name pound --net=bridge-pound \
   --ip=172.18.25.25 -p 80:80 centos:pound /sbin/init
```
Apacheの起動

```bash:apacheの起動
[root@localhost]# docker run -d -h apa24-1 \
   --name apa24-1 --net=bridge-int \
   --ip=172.19.46.46 -p 8081:80 httpd:apa24-1
[root@localhost]# docker run -d -h apa24-2 \
   --name apa24-2 --net=bridge-int \
   --ip=172.19.86.86 -p 8082:80 httpd:apa24-2
```
Poundをbridge-intに接続する

```bash:poundをbridge-intに追加
[root@localhost]# docker network connect bridge-int pound
```

###いざ接続！
####失敗した場合
設定ミスによる失敗。

```bash:ホストからpoundへ接続
[root@localhost]# curl localhost
curl: (56) Recv failure: 接続が相手からリセットされました
```
( д) ﾟ ﾟ

```bash:エラーを探す
[root@localhost]# docker exec -it pound bash
[root@pound]# systemctl status pound
Failed to get D-Bus connection: Operation not permitted
[root@pound]# journalctl -u pound
No journal files were found.
-- No entries --
```
D-Busエラー。
そうだ、```--privileged```を付けてコンテナ立ち上げないとsystemctlが動かないんだった。

```bash:エラーの確認
# コンテナを削除して
[root@localhost]# docker stop pound && docker rm pound

# 改めて立ち上げて
[root@localhost]# docker run --privileged -d -h pound \
   --name pound --net=bridge-pound \
   --ip=172.18.25.25 -p 80:80 centos:pound /sbin/init

# poundサービスのステータスを見る
[root@localhost]# docker exec -it pound bash
[root@pound]# journalctl -u pound

# エラーの一部
/etc/pound.cfg line 6: unknown directive - aborted
```
Dockerfile-poundの/etc/pound.cfgで、ダブルコーテーションにエスケープシーケンスつけ忘れが原因だった。(上述のDockerfileは修正しているので、流れに沿ってやってもこのエラーは起きない)

####成功した場合
分散しているか確認する。

```bash:Ctrl+Cで終了
[root@localhost]# while true ; do curl localhost/tmp/ --noproxy localhost; sleep 1; done
This is the 4646
This is the 8686
This is the 4646
This is the 4646
This is the 4646
This is the 8686
This is the 4646
This is the 8686
This is the 4646
```
分散されている！
#5.リクエストヘッダの差の確認
###ヘッダを見る
Pound経由と直接接続で、アクセスログにどのように記録されているか確認する。

```bash:アクセスする
### pound経由のアクセス(一部省略)
[root@localhost]# curl -i localhost/tmp/
Date: Sat, 26 Aug 2017 08:31:54 GMT
This is the 4646

### 直接アクセス(一部省略)
[root@localhost]# curl -i 172.19.46.46/tmp/
Date: Sat, 26 Aug 2017 08:32:23 GMT
This is the 4646
```
```bash:ログ確認
### アクセスログ
[root@localhost]# docker exec -it apa24-1 bash
root@apa24-1:/usr/local/apache2# cat logs/access_log

# pound経由でアクセス
172.19.0.2 - - [26/Aug/2017:08:31:54 +0000] "GET /tmp/ HTTP/1.1" 200 20 "-" "curl/7.29.0" 106 235

# ホストから直接アクセス
172.19.0.1 - - [26/Aug/2017:08:32:23 +0000] "GET /tmp/ HTTP/1.1" 200 20 "-" "curl/7.29.0" 80 235
```
- pound経由は、poundに割り振られたbridge-int側のIP 172.19.0.2がリモートホストとなっている。
- pound経由でも、user-agentがリクエストそのままになっている。
- ホストから直接は、IP172.19.0.1。ホストで```ip a | grep 172.19.0.1```を実行してみると、インターフェースのひとつに```172.19.0.1```が割り振られていることを確認できる。
- Apacheが受け取ったバイト数、poundは106バイト、直接は80バイト。おかしい。

####curlで調べる
Apacheが受け取ったバイト数が違うので、Pound経由と直接接続で、どんなリクエストをしているか調べてみる。

```bash:curlの通信を分析する(pound経由)
# curlで trace.logにPound経由を記録する
[root@localhost]# curl --trace-ascii /tmp/trace.log localhost/tmp/ > /dev/null  && cat /tmp/trace.log
(略)
=> Send header, 77 bytes (0x4d)
0000: GET /tmp/ HTTP/1.1
0014: User-Agent: curl/7.29.0
002d: Host: localhost
003e: Accept: */*
004b: 
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 37 bytes (0x25)
0000: Date: Sat, 26 Aug 2017 09:47:55 GMT
<= Recv header, 30 bytes (0x1e)
0000: Server: Apache/2.4.27 (Unix)
<= Recv header, 46 bytes (0x2e)
0000: Last-Modified: Sat, 26 Aug 2017 09:26:12 GMT
<= Recv header, 22 bytes (0x16)
0000: Accept-Ranges: bytes
<= Recv header, 20 bytes (0x14)
0000: Content-Length: 20
<= Recv header, 19 bytes (0x13)
0000: Connection: close
<= Recv header, 25 bytes (0x19)
0000: Content-Type: text/html
<= Recv header, 2 bytes (0x2)
0000: 
<= Recv data, 17 bytes (0x11)
0000: This is the 4646.
== Info: Closing connection 0
"Request  : 77 bytes"
"Response : 235 bytes" #(17+37+30+46+22+20+19+25+2+17)
```
```bash:curlの通信を分析する(直接アクセス)
# /tmp/trace2.logに直接アクセスを記録する
[root@localhost]# curl --trace-ascii /tmp/trace2.log 172.19.46.46/tmp/ > /dev/null  && cat /tmp/trace2.log     
(略)
=> Send header, 80 bytes (0x50)
0000: GET /tmp/ HTTP/1.1
0014: User-Agent: curl/7.29.0
002d: Host: 172.19.46.46
0041: Accept: */*
004e: 
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 37 bytes (0x25)
0000: Date: Sat, 26 Aug 2017 09:49:55 GMT
<= Recv header, 30 bytes (0x1e)
0000: Server: Apache/2.4.27 (Unix)
<= Recv header, 46 bytes (0x2e)
0000: Last-Modified: Sat, 26 Aug 2017 09:26:12 GMT
<= Recv header, 22 bytes (0x16)
0000: Accept-Ranges: bytes
<= Recv header, 20 bytes (0x14)
0000: Content-Length: 20
<= Recv header, 19 bytes (0x13)
0000: Connection: close
<= Recv header, 25 bytes (0x19)
0000: Content-Type: text/html
<= Recv header, 2 bytes (0x2)
0000: 
<= Recv data, 17 bytes (0x11)
0000: This is the 4646.
== Info: Closing connection 0
"Request  : 80 bytes"
"Response : 235 bytes" #(17+37+30+46+22+20+19+25+2+17)
```
- pound経由で77バイトのリクエスト
- 直接アクセスは80バイトのリクエスト
- 直接アクセスは、curlとApacheのログで差分がない。
- pound経由のヘッダサイズが一致しない。

|アクセス元|情報元|ヘッダサイズ|レスポンスサイズ|
|:--|:--|:--|:--|
|pound経由|curl|__77__|235|
|pound経由|access_log|__106__|235|
|*直接*|*curl*|*80*|*235*|
|*直接*|*access_log*|*80*|*235*|

どういうこと？
直接接続とpound経由のヘッダサイズの3バイト差は、IPアドレスの文字列(12文字)とlocalhostの文字列(9文字)の差。
pound経由はあきらかに増えている。何かが付け加えられているのではないか。
調べてみる。

####apacheのログで探す
Apacheのログでどんなリクエストを受け付けたのか確認できるか、LogFormatを見てみたけれど、リクエストヘッダすべてを確認できるような設定は見当たらなかった。余談、apacheのLogFormatで ```%r``` を**リクエスト**としているページが多いけど、[リファレンス](https://httpd.apache.org/docs/2.4/ja/mod/mod_log_config.html)では**リクエストの最初の行**とある。**リクエスト**の定義がずれているのかな。

ヘッダ情報を探していくと、X-Forwarded-forなるヘッダがあるらしい。

####tcpdumpで調べる
流れているパケットを見ようと思ってツールを探した。
tcpdumpというツールがあったので、パケットを見てみることにする。
apa24-1に入れてアクセスしてみる。

```bash:tcpdumpのインストール
# apa24-1を外部と通信できるbridge-poundにつなぐ
[root@localhost]# docker network connect bridge-pound apa24-1

# apa24-1のbashを起動して
[root@localhost]# docker exec -it apa24-1 bash

# tcpdumpをインストール
root@apa24-1# apt-get install -y tcpdump

# tcpdumpを起動する
root@apa24-1# tcpdump -A
```
別端末から、pound経由でapa24-1へアクセスする。

```bash:
# !! 別端末から !! pound経由でアクセスする
[root@localhost]# curl localhost/tmp/
```
発見！

```bash:pound経由のtcpdumpの結果(適宜改行済み)
root@apa24-1# tcpdump -A
(略)
10:41:49.207229 IP pound.bridge-int.40954 > apa24-1.http: Flags [P.],
 seq 1:107, ack 1, win 229, options [nop,nop,TS val 1378139628 ecr 1537662961],
 length 106: HTTP: GET /tmp/ HTTP/1.1
E...R.@.@.a............P.5*!{..............
R$..
[...GET /tmp/ HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost
Accept: */*
X-Forwarded-For: 172.18.0.1
(空行)
(空行)
(略)
```
- length:106の文字を発見。
- さらに```X-Forwarded-For: 172.18.0.1```の文字列もある。

文字列は27文字で、元のリクエスト77文字。77+27=104で、~~空行のLF2つ(正確には172.18.0.1の後のLFと空行のLF)をあわせて、104+2=106ということで合ってそう。~~文字列末尾のCR+LFの2バイトで104+2=106バイトだった。

改行文字を調べた。

```bash:tcpdumpのオプションでバイナリが見れる。
root@apa24-1# tcpdump -x
(略)
length 106: HTTP: GET /tmp/ HTTP/1.1
(略)
        0x0080:  2d46 6f72 7761 7264 6564 2d46 6f72 3a20
        0x0090:  3137 322e 3138 2e30 2e31 0d0a 0d0a
```
- 末尾にCR+LFの0d0aが見れる。


念のため、Length:80の直接アクセスをtcpdumpでもサイズが同じか調べる。

```bash:直接アクセスのtcpdumpの結果(適宜改行済み)
root@apa24-1# tcpdump -A
(略)
 length 80: HTTP: GET /tmp/ HTTP/1.1
E.....@.@..............Pi.lD'..x...........
._.:..}.GET /tmp/ HTTP/1.1
User-Agent: curl/7.29.0
Host: 172.19.46.46
Accept: */*
(空行)
(略)
```
同じだった。


参考資料
[curlでヘッダを見る方法いろいろ](http://qiita.com/takc923/items/1b508bb370c78b7a9d44)
[超絶初心者むけtcpdumpの使い方](http://qiita.com/tossh/items/4cd33693965ef231bd2a)


おしまい！
お付き合いありがとうございました。