CentOS7上のDockerでコンテナを固定IP化して幸せになりたい

# CentOS7上のDockerでコンテナを固定IP化して幸せになりたい

## 背景

普段、開発環境としてWindows+VirtualBox+Vagrantを利用しているのですが、VMはリソースの消費が大きいことに日々悩んでいます。そこで、リソースが節約できそうなDockerでコンテナを固定IP化してVagrant的に利用できたら幸せになれる！という思惑から設定を考えてみました。(Docker的にはアンチパターンかもしれませんが。。。)

## ゴール

- VirtualBoxのホストオンリーアダプタまたはブリッジアダプタのネットワーク上で指定した固定IPアドレスを持つコンテナを稼働させる。

私はWindows上のエディタ/IDEで開発するためにVMに固定IPを割り当ててSambaを稼働させてます。その兼ね合いでコンテナにも個別に固定IPアドレスを割り当てたいので上記のゴールを設定しています。

実は以前に似たような目的でpipeworkを使った下記の記事を投稿しましたが、pipeworkの手間が煩わしく使わなくなってしまいました。するとDockerが似たような機能を取り込んで手間が減ったので、今回あらためて記事にまとめてみました。

- [VirtualBoxのVM上のCentOS7.1+Dockerでpipeworkを使って固定IP化してみる](http://qiita.com/sadapon2008/items/cf441471d3c881f478be)



## 前提

- VirtualBox+Vagrant上に構築します。
- CentOS7のVagrantのBoxは作成済みとします。  
　　※ CentOS公式のVagrantのBoxはファイルシステムがxfsではなくext4になっています。  
　　※ 私はDockerのストレージエンジンにoverlayを使いたかったためxfsベースのBoxを自作しました。
- ホストオンリーアダプタのネットワークは192.168.77.0/24とします。  
　　※ VirtualBoxのホストオンリーアダプタの設定と一致させます。
- ブリッジアダプタのネットワークは10.0.0.0/24とします。  
　　※ ホストPCが参加しているネットワークと一致させます。


## 参考記事

下記の記事を参考にさせていただきました。

- [CentOS 7.2以降でDockerをインストールしOverlayFSを設定する](http://qiita.com/kentarosasaki/items/84a1786cc64f942bdbfc)
- [Dockerのネットワークをbridgeで外と繋げる](http://qiita.com/kjtanaka/items/c2fd862ca91a63d74166)
- [コンテナに固定IPを割り振って外部からアクセスする](http://qiita.com/Ki4mTaria/items/3425c48e8c6006564cba)

## 構築手順

### 1. VagrantでCentOS7のVMを起動

Vagrantfileを用意してvagrant upします。

ホストオンリーアダプタの場合は以下の内容でVagrantfileを用意します。Box名は適切なものに置換してください。

```
Vagrant.configure("2") do |config|
  config.vm.box = "(Box名)"

  config.vm.provider "virtualbox" do |v|
    v.customize ['modifyvm', :id, '--nicpromisc2', 'allow-all']
  end

  config.vm.network "private_network", ip: "192.168.77.10", auto_config: false
end
```

ブリッジアダプタの場合は以下の内容でVagrantfileを用意します。

```
Vagrant.configure("2") do |config|
  config.vm.box = "(Box名)"

  config.vm.provider "virtualbox" do |v|
    v.customize ['modifyvm', :id, '--nicpromisc2', 'allow-all']
  end

  config.vm.network "public_network", auto_config: false
end
```

Vagrantfileが用意できたらvagrant upします。


```
$ vagrant up
```

### 2. Dockerのインストール

VMが起動したらVMのCentOS7上でDockerの設定を行います。ここではrootで作業する前提で話を進めます。

(オプション)私はストレージエンジンにoverlayを使いたかったため、以下の設定を行ってvagrant reloadして再起動しています。

```
# tee /etc/modules-load.d/overlay.conf <<-'EOF'
overlay
EOF
```

Dockerのリポジトリを設定します。

```
# tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF
```

(オプション)ストレージエンジンにoverlayを使う場合は以下の追加設定を行います。

```
# mkdir -p /etc/systemd/system/docker.service.d
# tee /etc/systemd/system/docker.service.d/override.conf <<-'EOF'
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --storage-driver=overlay
EOF
```

Dockerをインストールして起動させます。

```
# yum -y install docker-engine
# systemctl enable docker.service
# systemctl start docker.service
```

Dockerが動作しているか確認します。

```
# docker info
```

(オプション)ストレージエンジンにoverlayを使う場合はdocker infoで設定されているか確認できます。

### 3. ブリッジの設定

CentOS7上にブリッジを作成していきます。

```
# yum -y install bridge-utils
# ip addr flush dev eth1
```

ホストオンリーアダプタの場合は以下のようにします。

```
# docker network create --driver bridge --subnet=192.168.77.0/24 --gateway=192.168.77.10 --opt "com.docker.network.bridge.name"="docker1" my_bridge

```

ブリッジアダプタの場合は以下のようにします。

```
# docker network create --driver bridge --subnet=10.0.0.0/24 --gateway=10.0.0.10 --opt "com.docker.network.bridge.name"="docker1" my_bridge
```

なお、「--gateway=」で設定するIPアドレスが作成されるブリッジのIPアドレスとして利用されますので、他で利用されていないIPアドレスを指定する必要があります。


その後、以下の設定を行います。

```
# mkdir -p /etc/systemd/system/docker.service.d
# tee /etc/systemd/system/docker.service.d/override2.conf <<-'EOF'
[Service]
ExecStartPost=/bin/sh -c "/usr/bin/sleep 1s; /usr/sbin/ip addr flush dev eth1; /usr/sbin/brctl addif docker1 eth1"
ExecStopPost=/bin/sh -c "/usr/sbin/brctl delif docker1 eth1"
EOF
# systemctl daemon-reload

```

設定が完了したらvagrant reloadして設定に問題ないか確認します。以下のようにdocker1にeth1が関連付けられていたらOKです。


```
# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02426630bacc       no
docker1         8000.0242cdcc83a0       no              eth1
```

## コンテナイメージの作成

今回構築した環境用にCentOS6とCentOS7のイメージを作成します。

### 作成するイメージのゴール

- ログインID/パスワードがdocker/dockerのユーザを作成します。
- dockerユーザにsudo権限を与えます。
- sshdを稼働させます。(Docker的にはアンチパターンかもしれませんが。。。)

### CentOS6のイメージ作成

以下の内容のDockerfileを作成します。

```
FROM centos:6.8
ENV container docker
RUN yum -y update && yum clean all
RUN yum -y install openssh-server openssh-clients passwd sudo && yum clean all && chkconfig sshd on
RUN groupadd docker && useradd -g docker docker && (echo "docker" | passwd docker --stdin) && sed -i 's/Defaults.*requiretty/#Defaults requiretty/g' /etc/sudoers && ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key && ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key && sed -ri 's/session    required     pam_loginuid.so/#session    required     pam_loginuid.so/g' /etc/pam.d/sshd && (echo "docker ALL=(ALL) NOPASSWD: ALL" >/etc/sudoers.d/docker)
CMD ["/sbin/init"]
```

ビルドします。

```
# docker build --rm -t local/c6 .
```

### CentOS7のイメージ作成

```
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
RUN yum -y update && yum clean all
RUN yum -y install openssh-server openssh-clients passwd sudo && yum clean all && systemctl enable sshd.service
RUN groupadd docker && useradd -g docker docker && (echo "docker" | passwd docker --stdin) && sed -i '/^session.*pam_loginuid.so/s/^session/# session/' /etc/pam.d/sshd && sed -i 's/Defaults.*requiretty/#Defaults requiretty/g' /etc/sudoers && rm /usr/lib/tmpfiles.d/systemd-nologin.conf && /usr/bin/ssh-keygen -A -v && (echo "docker ALL=(ALL) NOPASSWD: ALL" >/etc/sudoers.d/docker)
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]
```

ビルドします。

```
docker build --rm -t local/c7-systemd .
```

## コンテナの起動

固定IPを指定して起動します。ここではホストオンリーアダプタのネットワーク上のIPアドレスの例になっていますが、ブリッジアダプタの場合は「--ip=192.168.77.11」の代わりに「--ip=10.0.0.11」などを指定することになります。指定した固定IPに対してsshなどが可能です。

### CentOS6

```
# docker run --name container1 --network=my_bridge --ip=192.168.77.11 -d local/c6
```

### CentOS7

```
# docker run --privileged --name container1 --network=my_bridge --ip=192.168.77.11 -v /sys/fs/cgroup:/sys/fs/cgroup:ro --stop-signal=$(kill -l RTMIN+3) -d local/c7-systemd
```

## コンテナの停止と再稼働

通常通りのdocker stop/docker startで行います。

### 停止

```
# docker stop container1
```

### 再稼働

```
# docker start container1 
```