Dockerでforward proxyサーバーを立てる with centos6.5 on virtualbox

## Dockerでforward proxyサーバーを立てる with centos6.5 on virtualbox

* __[2014/05/08 追記] Dockerfile使って簡潔にまとめ直した版はこちら__ [Dockerでforward proxyサーバー(squid)を立てる using Dockerfile - Qiita](http://qiita.com/jpshadowapps/items/018923a86dff637720a6)

* centos6.5 on virtualboxな環境（dockerコンテナもcentos6.5）な環境で公式のチュートリアルを試してみた備忘
* dockerとは何ぞやレベルの話は割愛。というか有用な情報が多く存在するのでいらんよね？という

### やること

1. virtualbox上にcentos6.5のVMを構築する
1. 構築したVM上でdockerコンテナを動かす（同じくcentos）
1. dockerのcentral repositryにあるcentosイメージはver 6.4なので、6.5にアップデート
1. コンテナ上にsquidでプロキシサーバを立てて、数パターンのアクセス経路で通信を確認する

### 環境

* Virtualbox 4.3.8
    * 作業PCはMac
* Vagrant 1.4.3
* Docker 0.8.0

### 作業ログ

#### Virtualbox上にベースとなるVMを作成

* vagrant1.4からprovisionerにdockerを指定できるようになったので、dockerのインストールはvagrantに任せる
* [Docker - Provisioning - Vagrant Documentation](http://docs.vagrantup.com/v2/provisioning/docker.html)

```bash
% vagrant box add centos65_86 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box
% vagrant init centos65_86
% vi Vagrantfile
```
```ruby
# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos65_86"

  config.vm.define :vmdocker1 do |vmdocker1|
    vmdocker1.vm.hostname = "vmdocker1"
    vmdocker1.vm.network :private_network, ip: "192.168.56.110"

    # for VirtualBox
    vmdocker1.vm.provider :virtualbox do |vb|
      vb.customize ["modifyvm", :id, "--memory", 512]
    end

    # provisioner = docker
    # pull_images でimageのダウンロード
    vmdocker1.vm.provision :docker do |d|
      d.pull_images "centos"
    end
  end
end
```
```bash
% vagrant up
% vagrant ssh

[vagrant@vmdocker1 ~]$ cat /etc/redhat-release
CentOS release 6.5 (Final)
```

#### dockerの確認

* vagrant経由でも現時点で最新のdocker 0.8.0がインストールされている

```bash
[vagrant@vmdocker1 ~]$ docker -v
Docker version 0.8.0, build cc3a8c8/0.8.0
```

* Driverがdevicemapperとなっているが、これはdockerのデフォルトドライバであるaufsにRHEL系ディストリビューションが対応していない為
    * 参考 : [Docker ストレージドライバによる RHEL/CentOS 対応について - Shin x blog](http://www.1x1.jp/blog/2013/12/storage-driver-on-docker07.html)
    * ベースとなっているコピーオンライト・ストレージは __lvm thin provisioning__ だが、centosでは6.4から対応している。dockerのcentral repositoryで公開されてるcentosは6.4なので対応状況としては問題なし

```bash
[vagrant@vmdocker1 ~]$ docker info
Containers: 0
Images: 1
Driver: devicemapper
 Pool Name: docker-8:1-131916-pool
 Data file: /var/lib/docker/devicemapper/devicemapper/data
 Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
 Data Space Used: 626.1 Mb
 Data Space Total: 102400.0 Mb
 Metadata Space Used: 0.9 Mb
 Metadata Space Total: 2048.0 Mb
```

#### コンテナ起動確認

* docker runの際にsudoしてる例をよく見るが、vagrantなんでsudo不要
* 適当なコマンドを実行し、実行結果を確認してみる

```bash
[vagrant@vmdocker1 ~]$ docker run -t centos /bin/bash -c "echo hello; echo world;"
hello
world
```

* interactiveモード(-i)ではない場合、処理実行後にコンテナは停止するので、普通に`docker ps`しただけでは該当コンテナは表示されない。なので`-a`オプションを付加する
* 加えて、通常は情報を省略して表示するので省略を無効化する為に`--no-trunc`オプションを付加する
* STATUS欄がコマンドの結果コードになる

```bash
[vagrant@vmdocker1 ~]$ docker ps -a --no-trunc 58a7ec06afa0
CONTAINER ID                                                       IMAGE               COMMAND                                CREATED              STATUS              PORTS               NAMES
58a7ec06afa04cbd28ca9536cc8ba651c227197ca2b1c24b750c3c154e514997   centos:6.4          /bin/bash -c echo hello; echo world;   About a minute ago   Exit 0                                  focused_brattain
```

* コンテナで実行した処理の結果は`docker logs`でも確認可能。 docker logsの出力は __stdout/stderrのキャプチャ__ とイメージすればよいかと

```bash
[vagrant@vmdocker1 ~]$ docker logs 58a7ec06afa0
hello
world
```

#### コンテナのcentosを6.5にアップグレードする

* interactive-modeでシェルにログインし、centosのアップグレードを行う
* 普通に yum update するだけ

```bash
[vagrant@vmdocker1 ~]$ docker run -t -i centos /bin/bash

bash-4.1# cat /etc/redhat-release
CentOS release 6.4 (Final)

bash-4.1# yum -y update
  : (なんだかんだupdateされる)
Complete!

bash-4.1# cat /etc/redhat-release
CentOS release 6.5 (Final)  # <= 6.5になった
```

* この6.5にアップデートだけした時点で、一旦`docker commit`しておく
* 業務で使用する場合とか、管理面を考慮してTAGとコミットコメントはちゃんと書いといた方が良さそうな気がした

```bash
[vagrant@vmdocker1 ~]$ docker commit -m 'upgrade 6.4 => 6.5' dabf3c7c0bcb centos:6.5
```

* 作成したイメージを`docker images`で確認。6.5にしただけでサイズが2倍...

```bash
[vagrant@vmdocker1 ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              6.5                 ccc8bfd8399d        51 seconds ago      600 MB
```

* 不要になった他のイメージは`docker rmi`して削除しておく
    * が、ここで __削除したのに容量が減らない__ という症状に出くわす...
    * 参考 : [Docker on CentOS 6.5 で詰んだのでメモ - sonots:blog](http://blog.livedoor.jp/sonots/archives/36644399.html)
    * 今後コミュニティが活発になるにつれdockerのcentral repositoryのストレージとかどんでもない容量に膨らむんじゃないかと推測されるので、その辺りどう解決していくのか注目

```bash
[vagrant@vmdocker1 ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       7.3G  2.3G  4.8G  32% /
tmpfs           246M     0  246M   0% /dev/shm
/vagrant        465G   94G  371G  21% /vagrant

[vagrant@vmdocker1 ~]$ docker rmi 539c0211cd76
Untagged: 539c0211cd76cdeaedbecf9f023ef774612e331137ce7ebe4ae1b61088e7edbe
Untagged: 539c0211cd76cdeaedbecf9f023ef774612e331137ce7ebe4ae1b61088e7edbe

[vagrant@vmdocker1 ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              6.5                 ccc8bfd8399d        5 minutes ago       600 MB

[vagrant@vmdocker1 ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       7.3G  2.3G  4.8G  32% /            # <= 減っちゃいねぇ...
tmpfs           246M     0  246M   0% /dev/shm
/vagrant        465G   94G  371G  21% /vagrant

[root@vmdocker1 ~]# du -h /var/lib/docker --max-depth=1
32K     /var/lib/docker/graph
576K    /var/lib/docker/containers
4.0K    /var/lib/docker/volumes
1.7G    /var/lib/docker/devicemapper
6.5M    /var/lib/docker/init
```

#### コンテナにsquidをインストール

* サービス開始時になんかスクリプトのエラーが出るけどスルー
* ポート＝デフォルト＝3128でサービス開始出来るところまで確認し、自動起動をonにしておく

```bash
[vagrant@vmdocker1 ~]$ docker run -t -i ccc8bfd8399d /bin/bash

bash-4.1# yum -y install squid

bash-4.1# /etc/init.d/squid start
/etc/init.d/squid: line 25: /etc/sysconfig/network: No such file or directory
/etc/init.d/squid: line 51: [: =: unary operator expected
Starting squid: .                                          [  OK  ]
 
bash-4.1# netstat -anp | grep squid
tcp        0      0 :::3128                     :::*                        LISTEN      73/(squid)
udp        0      0 0.0.0.0:58841               0.0.0.0:*                               73/(squid)
udp        0      0 :::37744                    :::*                                    73/(squid)

bash-4.1# chkconfig squid on

bash-4.1# /etc/init.d/squid stop
/etc/init.d/squid: line 25: /etc/sysconfig/network: No such file or directory
Stopping squid: ................                           [  OK  ]
```

* centos6.5 + squid、まで完了したこの時点でもコミットしてイメージ作成しておく
    * 業務でdockerを使うことになった場合も、おそらくこのようにコンテナ仕様に変更があれば適宜コミットしていくイメージ。
    * __OSレベルで履歴を管理し、さらには任意の時点からの更新・派生・削除も行える。__ という点をメリットと考えるなら、こうした適宜コミットがベターかと思ふ。
        * 自分の場合普段の仕事でgitを使っている事もあってか、コミットがマメになる癖はあるかも 
        * centos65だと不要なイメージを削除しても容量が減らないという問題はありますがそのうち解決されると信じる
        
```bash
[vagrant@vmdocker1 ~]$ docker commit -m 'add squid' $(docker ps -q -a | head -1) centos:6.5-squid  # docker ps -q -a | head -1 => 直近実行していたコンテナIDを取得
[vagrant@vmdocker1 ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
centos              6.5-squid           9a8172a27111        About a minute ago   660.3 MB
centos              6.5                 ccc8bfd8399d        22 hours ago         600 MB
```

* ちなみにこのイメージを再度docker runしても、 __squidが自動起動されるわけではなかった（"状態"を持ってるわけではない）。後述

#### ホストマシンとポートバインディングしてみる

* コンテナのsquid起動ポートをホストマシンにバインディングして、そのポートからsquidにアクセス出来るか確認する

```bash
[vagrant@vmdocker1 ~]$ docker run -p 13128:3128 -i -t 9a8172a27111 /bin/bash
bash-4.1#
```

* バインドに成功したかは docker ps とか docker port で確認可能

```bash
[vagrant@vmdocker1 network-scripts]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                     NAMES
ff60f536aaeb        centos:6.5-squid    /bin/bash           52 seconds ago      Up 52 seconds       0.0.0.0:13128->3128/tcp   high_ritchie

[vagrant@vmdocker1 network-scripts]$ docker port ff60f536aaeb 3128
0.0.0.0:13128
```

* コンテナのsquidを起動する

```bash
bash-4.1# /etc/init.d/squid start
/etc/init.d/squid: line 25: /etc/sysconfig/network: No such file or directory
/etc/init.d/squid: line 51: [: =: unary operator expected
Starting squid: .                                          [  OK  ]
```

* ホストマシンからバインドしたポートへのアクセスを試してみる。squidに / アクセスした際のエラーページが取得できてる

```bash
[vagrant@vmdocker1 ~]$ curl http://localhost:13128/ | head -10
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
101  3139  101  3139    0     0  1449k      0 --:--:-- --:--:-- --:--:-- 3065k
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ERROR: The requested URL could not be retrieved</title>
<style type="text/css"><!--
 /*
 Stylesheet for Squid Error pages
 Adapted from design by Free CSS Templates
 http://www.freecsstemplates.org
 Released for free under a Creative Commons Attribution 2.5 License
```

* 折角プロキシとしてsquidを動かしているので、これを経由して外部（今回はapache1.3で稼働してるwebサーバを使用）にもアクセスしてみる。

```bash
[vagrant@vmdocker1 ~]$ curl -x localhost:13128 http://www.byaaaaaaaaaaaaaa.com/
(ズラズラと取得したコンテンツを表示)
```

* コンテナのsquidのaccess.logに該当アクセスが記録されている

```bash
bash-4.1# view /var/log/squid/access.log

1393929530.175    298 172.17.42.1 TCP_MISS/200 24888 GET http://www.byaaaaaaaaaaaaaa.com/ - DIRECT/10.0.yy.yy text/html
```

* アクセスしたサイトのaccess.logを確認、コンテナのIPアドレスが記録されている

```
172.17.42.1 - - [04/Mar/2014:19:39:58 +0900] "GET / HTTP/1.1" 200 24156 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"
```

* 同様に、ホストVMを稼働させているローカルPC（Mac）からもコンテナのsquid経由で外部にアクセスしてみる
    * ホストVMのIPアドレスを 192.168.56.110 とする

```bash
% curl -x 192.168.56.110:13128 http://www.byaaaaaaaaaaaaaa.com/
```

* コンテナのsquidで401で弾かれている
    * アクセス元はvirtualboxのgatewayアドレス＝192.168.56.1 になっている

```bash
bash-4.1# view /var/log/squid/access.log

1393929684.408    132 192.168.56.1 TCP_MISS/401 310 GET http://www.byaaaaaaaaaaaaaa.com/ - DIRECT/10.0.yy.yy text/html
```

* サイトのaccess.logも401で弾かれてる

```
192.168.56.1 - - [04/Mar/2014:19:42:32 +0900] "GET / HTTP/1.1" 401 5 "-" "curl/7.30.0" "-"
```

* ホストVMのVagrantfileにローカルPC => ホストVMのポートフォワード設定を追加してみる
    * 最終的に ローカルPC(23128) => ホストVM(13128) => コンテナ(3128) というフォワード経路が完成

```diff
@@ -11,6 +11,8 @@
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
     vmdocker1.vm.hostname = "vmdocker1"
     vmdocker1.vm.network :private_network, ip: "192.168.56.110"

+    vmdocker1.vm.network :forwarded_port, host: 23128, guest: 13128
+
```

* この状態で再度ローカルPCからチャレンジ
    * ホストはlocalhost、ポートはフォワード設定した番号に変更している

```bash
% curl -x localhost:23128 http://www.byaaaaaaaaaaaaaa.com/
(ズラズラと取得したコンテンツを表示)
```

* コンテナのsquidのaccess.logに該当アクセスが記録されている
    * アクセス元IPはローカルPCのもの

```bash
bash-4.1# view /var/log/squid/access.log

1393930836.500    276 10.0.xxx.xxx TCP_MISS/200 24888 GET http://www.byaaaaaaaaaaaaaa.com/ - DIRECT/10.0.yy.yy text/html
```

* アクセスしたサイトのaccess.logを確認、コンテナのIPアドレスが記録されている
    * アクセス元IPは同じくローカルPCのもの

```
10.0.xxx.xxx - - [04/Mar/2014:20:00:37 +0900] "GET /welcome HTTP/1.1" 200 24156 "-" "curl/7.30.0" "-"
```

#### squid起動状態のコンテナをバックグラウンドで稼働させる

* squid起動状態でコンテナを終了し、コミットする
* このコミットにて作成されたイメージでコンテナを実行した際に、既にsquidが起動状態にあるかを確認する

```bash
[vagrant@vmdocker1 ~]$ docker commit -m 'update running squid' $(docker ps -q -a | head -1) centos:6.5-squid-running

[vagrant@vmdocker1 ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              6.5-squid-running   a2ca5119cdcf        19 seconds ago      660.3 MB

[vagrant@vmdocker1 ~]$ docker run -p 13128:3128 -i -t a2ca5119cdcf /bin/bash

[vagrant@vmdocker1 ~]$ docker run -p 13128:3128 -i -t a2ca5119cdcf /bin/bash
bash-4.1# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3  11436  1596 ?        S    23:27   0:00 /bin/bash
root         6  0.0  0.2  13364  1056 ?        R+   23:27   0:00 ps aux
```

* squidは停止状態でした。普通にdocker runしただけではdocker run時に同時にサービス起動する事は出来ないよう。
    * __イメージは状態を保持しているわけではない__
* non-interactiveにdocker runしたコンテナでsquidサービスを起動（しっぱなし）にしておきたいが
    * docker run時のコマンド引数で /etc/init.d/squid start しても、コンテナ内でバックグラウンドとして動くので、フォワグラウンドジョブが無い＝すぐにコンテナが終了してしまう

```bash
[vagrant@vmdocker1 ~]$ docker run -p 13128:3128 -i -t a2ca5119cdcf /bin/bash -c '/etc/init.d/squid start'
/etc/init.d/squid: line 25: /etc/sysconfig/network: No such file or directory
/etc/init.d/squid: line 51: [: =: unary operator expected
Starting squid:                                            [  OK  ]
# ココでコンテナ終了
```

* これを解決するために、「コンテナ内ではフォアグラウンドに稼働し、docker runをバックグラウンドで実行」させてみる
* コンテナのsquidスクリプトにフォアグラウンドで稼働させるオプション `starti` を追加する

```bash
[vagrant@vmdocker1 ~]$ docker run -p 13128:3128 -i -t a2ca5119cdcf /bin/bash

bash-4.1# vi /etc/init.d/squid
```
```bash
     99 starti() {
    100         probe
    101
    102         SQUID_OPTS="-N"
    103
    104         parse=`$SQUID -k parse -f $SQUID_CONF 2>&1`
    105         RETVAL=$?
    106         if [ $RETVAL -ne 0 ]; then
    107                 echo -n $"Starting $prog: "
    108                 echo_failure
    109                 echo
    110                 echo "$parse"
    111                 return 1
    112         fi
    113         for adir in $CACHE_SWAP; do
    114                 if [ ! -d $adir/00 ]; then
    115                         echo -n "init_cache_dir $adir... "
    116                         $SQUID -z -F -f $SQUID_CONF >> /var/log/squid/squid.out 2>&1
    117                 fi
    118         done
    119         $SQUID $SQUID_OPTS -f $SQUID_CONF >> /var/log/squid/squid.out 2>&1

    174 starti)
    175         starti
    176         ;;
```

* 追加したフォアグラウンド用オプションでsquidを起動し、動作確認してみる => OK

```bash
bash-4.1# /etc/init.d/squid starti

[vagrant@vmdocker1 ~]$ curl -x localhost:13128 http://www.byaaaaaaaaaaaaaa.com/
(OK!!)
```

* ここまでをコミットし、作成したイメージで __デタッチモードで__ docker runする

```bash
[vagrant@vmdocker1 ~]$ docker commit -m 'add squid foreground start option' $(docker ps -q -a | head -1) centos:6.5-squid-running
170451d7b035ba10e96b798b5d83c9b99b67ed2a9cc586d3404ba4d3738a1739

[vagrant@vmdocker1 ~]$ docker run -p 13128:3128 -t -d 170451d7b035 /etc/init.d/squid starti
```

* squidが起動した状態でコンテナが実行されているので、アクセス確認してみる
* 実行中のコンテナは docker killで止める

```bash
[vagrant@vmdocker1 ~]$ docker ps
CONTAINER ID        IMAGE                      COMMAND                CREATED             STATUS              PORTS                     NAMES
210e3a97b2ab        centos:6.5-squid-running   /etc/init.d/squid st   4 seconds ago       Up 4 seconds        0.0.0.0:13128->3128/tcp   romantic_albattani

[vagrant@vmdocker1 ~]$ curl -x localhost:13128 http://www.byaaaaaaaaaaaaaa.com/
(OK!)

[vagrant@vmdocker1 ~]$ docker kill 210e3a97b2ab
```