AmazonLinuxのDockerイメージを作ってChefの開発効率を向上させる

AWSでEC2を使う場合、AmazonLinuxを使うことも多いかと思います。
そして、OpsWorksを使ったり、サーバー構成管理をChefなどで行う場合、Chefのレシピを作って確認してテストして。。。ということを何回もやる必要があると思います。

しかし、EC2のAmazonLinuxだと基本的に起動するまでに1分程度かかるので中々開発効率があげられません。
AmazonLinuxはRedhat互換なので事前にCentOSのDockerを使って確認しておくのもありですが、違っている部分もあり、結局最終的にはAmazonLinuxで確認しなきゃと思うとに2度手間だったりします。

じゃあどうすればよいのかーと思って調べていたらAmazonLinuxをDockerイメージにできるらしく実際にやってみて、Chefの開発効率を上げる方法を考えてみたのでメモ。

# 結論

+ AmazonLinuxのDockerイメージは作れる
+ ただし、yumはVPC内でしか使えない
+ 逆に言えばEC2でならいい感じに使える
+ PackerやVagrantを使うとより快適になる。Chefのテスト環境readyなPacker作りました。[toshihirock/chef-test-ec2](https://github.com/toshihirock/chef-test-ec2.git)
+ EC2ではインターネットへのアクセス通信量によっては利用料も発生するのでその点は注意
+ 今回は試してないが、AnsibleやPuppet、UserDataの検証としてもとても有用ではないかと


# 参考

+ [KITCHEN-DOCKERでEC2の料金をかけず、高速にAMAZON LINUXでのCOOKBOOKのテストを行う方法](http://blog.memolib.com/memo/942/)
+ [Docker Meetup Tokyo #2でLTしてきた:「Docker+serverspecで作るconfigspec CI」 #dockerjp](http://dev.classmethod.jp/cloud/aws/docker-serverspec-configspec-ci/)


# AmazonLinuxのDockerイメージを作成する

以下、参考の「Amazon Linux Docker Image」の項に記載してあるように以下のように操作していきます。

[Docker Meetup Tokyo #2でLTしてきた:「Docker+serverspecで作るconfigspec CI」 #dockerjp](http://dev.classmethod.jp/cloud/aws/docker-serverspec-configspec-ci/)

まず、以下をCLIやマネージメントコンソールで実施します。

+ EC2のインスタンスを２つ作成する(AmazonLinux201509)
+ 一つのインスタスを停止する
+ 停止したインスタンスのEBSボリュームをデタッチする
+ デタッチボリュームを起動中のインスタンスにアタッチする

以降、起動中のインスタンスにsshログインして操作を実施します。

ディスクをマウントするあたりの操作がイマイチよくわかっておらず、以下を参考に少し細くやってみました。

[Amazon EBS ボリュームを使用できるようにする](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ebs-using-volumes.html)

## アタッチされたEBSを確認する

lsblkコマンドでEC2で使用可能なディスクデバイスを確認します。

```
$lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk
└─xvda1 202:1    0   8G  0 part /
xvdf    202:80   0   8G  0 disk
└─xvdf1 202:81   0   8G  0 part
```

xvda、xvdfというのが接続されているEBSでxvda1,xvdfというのがそのEBSのパーティションとなっています。（パーティションが複数ある場合、xvda1,xvda2みたいな感じになる）

xvda,xvdfという名称は必ずしもEBSでアタッチした時のデバイス名とは一致しないようです。末尾の文字は変わらない事が多いのでその情報やマウントポイントなどを見て見分けることができるようです。

今回の例でも後でアタッチしたEBSのデバイス名は/dev/sdfとしたが、上記では/dev/xvdfとなっていますが、末尾の文字が同じ事とxvda1はマウントポイントがあるのに対し、xvdf1はマウントポイントがない事からもこのデバイス名が追加したEBSであることがわかるかと思います。

## EBSにファイルシステムを追加する必要が有るか確認

次に追加されたEBSにファイルシステムを作成する必要が有るか確認します。今回のEBSは、既ににファイルシステムが構成されたEBSではありますが、念のため確認します。lsblkコマンドで見たばいのパスは/dev/が抜けているのでこちらでは/dev/を先頭につけています。

```
$sudo file -s /dev/xvdf
/dev/xvdf: GPT partition table, version 1.0, GUID: 1fbb752b-6fa9-4f9c-b708-35541a3c2b73, disk size: 16777216 sectors of 512 bytes
```

上記のように表示された場合、既にファイルシステムが構築されていることを示していますので別途ファイルシステムを作成する必要はありません。

ファイルシステムがない場合には以下のような表示となるようです。（新しくEBSを作成し、アタッチした場合など)

```
$sudo file -s /dev/xvdf
data
```

上記の場合、以下のようにファイルシステムを作成する必要があります。

```
$sudo mkfs -t ext4 /dev/xvdf
```

## マウントする

準備ができたのでいよいよマウントしてディスクを利用できるようにします。

まず、マウント先のディレクトリを作成します。今回は`/data2`/という場所にマウントするようにしますので、そのディレクトリを作成します。

```
$sudo mkdir /data2
```

次にmountコマンドを使ってマウントします。マウントコマンドの第一引数では/dev/xvdf1などのパーティションを指定し、第２引数でマウントするポイント（今回の例では/data2)を指定します。

```
$sudo mount /dev/xvdf1 /data2
```

マウントができているか、以下で確認できます。

```
$lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk
└─xvda1 202:1    0   8G  0 part /
xvdf    202:80   0   8G  0 disk
└─xvdf1 202:81   0   8G  0 part /data2
```

xvdf1のマウントポイントが記載されて無事マウントができたようです。

## docker利用準備

EC2上でdockerコマンドなどを使うためにインストール及びサービスの起動を行います。

```
$sudo yum install docker -y
$sudo docker service start
```

また、あとでDockerHubにpushするためにloginしておきます。(DockerHubのアカウントがない場合、事前に作成しておくこと）

```
$docker login
```

## 不要ファイルの削除

マウントしたボリュームの不要ファイル群を削除します。

```
$cd /data2/
$sudo rm -rf var/log/*
$sudo rm -rf var/cache/yum/*
$sudo rm -rf home/ec2-user/.ssh/authorized_keys
```

# dockerイメージ作成及びDockerHubへのpush

以下のコマンドでdocker imageを作成します。tarコマンドでカレントディレクトリのアーカイブを作成し、それをdocker importを使ってimageを作成します。

```
$sudo su
$tar --numeric-owner -cjp . | docker import - local/amzn
```

少し待って完了したらイメージができているかと思います。

```
$docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
local/amzn          latest              8da3738750f5        10 minutes ago      867 MB
```

起動してsshログイン時にパスワード認証にするなどの事前処理を行います。

```
$docker run -t -i local/amzn /bin/bash
[root@60134c5bf7f7 /]# rm -f /etc/ssh/ssh_host_*
[root@60134c5bf7f7 /]# sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
[root@60134c5bf7f7 /]# exit
```

コンテナIDを確認し、AmazonLinuxリリース日付のタグを切ってDockerHubにpushします。 **toshihirock** となっている部分は自身のDockerHubのアカウントに変更してください。

```
$docker ps -a
$docker commit ${CONTAINER ID} toshihirock/amzn
$docker tag ${IMAGE ID} toshihirock/amzn:2015.09
$docker push toshihirock/amzn
```

成功すればDockerHubのリポジトリでも確認できます。

# AmazonLinuxのDockerイメージを使ってみる

ローカル、EC2上で確認してみました。
結論としてはローカルでも利用できますが、yumが使えません。EC2上からであればyumも使えます。

## ローカル環境(Mac)で利用する

MacにDockerToolBoxをインストールしておき、確認してみます。

```
# imageのダウンロード
$docker pull toshihirock/amzn

# amazonlinuxが利用できる
$docker run -t -i toshihirock/amzn /bin/bash
[root@e1f9894debf7 /]# cat /etc/system-release
Amazon Linux AMI release 2015.09

# yumについては利用不可
[root@e1f9894debf7 /]#sudo yum install nginx
Loaded plugins: priorities, update-motd, upgrade-helper
http://packages.ap-northeast-1.amazonaws.com/2015.09/main/201509419456/x86_64/repodata/repomd.xml?instance_id=fail&region=URLError: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 403 Forbidden"
Trying other mirror.
http://packages.ap-southeast-1.amazonaws.com/2015.09/main/201509419456/x86_64/repodata/repomd.xml?instance_id=fail&region=URLError: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 403 Forbidden"
Trying other mirror.
```

## EC2上で利用する

以降の処理はEC2にsshログインし、確認。
確認はAmazonLinux201509で実施

```
# dcokerをインストール
$sudo yum install docker -y

#dockerグループに追加
$sudo usermod -g docker ec2-user

#サービス起動
$sudo service docker start

# imageダウンロード
$docker pull toshihirock/amzn
$docker pull toshihirock/amzn:2015.09

# VPC内からの通信であればyumも利用できる
$docker run -t -i toshihirock/amzn /bin/bash
[root@111c9900747f /]# sudo yum install nginx
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main/latest                                         | 2.1 kB     00:00
amzn-main/latest/group                                   |  35 kB     00:00
amzn-main/latest/primary_db                              | 3.4 MB     00:00
amzn-updates/latest                                      | 2.3 kB     00:00
amzn-updates/latest/group                                |  35 kB     00:00
amzn-updates/latest/updateinfo                           | 258 kB     00:00
amzn-updates/latest/primary_db                           | 1.2 kB     00:00
Resolving Dependencies
--> Running transaction check

```

# Chefのプロビジョニングをやってみる

AmazonLinux上でchefDKをインストールして、Chefの開発環境を構築し、テストなどできるかやってみます。

まずはchefDKのインストールと設定を行います。

```
# インストール
$wget https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chefdk-0.8.0-1.el6.x86_64.rpm
$sudo rpm --test chefdk-0.8.0-1.el6.x86_64.rpm
$sudo rpm -Uvh chefdk-0.8.0-1.el6.x86_64.rpm

# verifyで確認
$ chef verify
Running verification for component 'berkshelf'
Running verification for component 'test-kitchen'
Running verification for component 'chef-client'
Running verification for component 'chef-dk'
Running verification for component 'chef-provisioning'
Running verification for component 'chefspec'
Running verification for component 'rubocop'
Running verification for component 'fauxhai'
Running verification for component 'knife-spork'
Running verification for component 'kitchen-vagrant'
Running verification for component 'package installation'
Running verification for component 'openssl'
.......................
---------------------------------------------
Verification of component 'rubocop' succeeded.
Verification of component 'kitchen-vagrant' succeeded.
Verification of component 'openssl' succeeded.
Verification of component 'berkshelf' succeeded.
Verification of component 'fauxhai' succeeded.
Verification of component 'test-kitchen' succeeded.
Verification of component 'chef-dk' succeeded.
Verification of component 'knife-spork' succeeded.
Verification of component 'chef-client' succeeded.
Verification of component 'chefspec' succeeded.
Verification of component 'chef-provisioning' succeeded.
Verification of component 'package installation' succeeded.

# chefDK付属のrubyを使うようにする
$echo "eval \"$(chef shell-init `basename $SHELL`)\"" >> ~/.bashrc
$source ~/.bashrc
$which ruby
/opt/chefdk/embedded/bin/ruby
```

以前作ったChefのクックブックを使ってプロビジョニング、テストを実施してみます。

```
$git clone https://github.com/toshihirock/opsworks
$cd opsworks/myweb/

# kitchen-docker_cliをインストール
$bundle install

# .kitchen.ymlにAmazonLinuxイメージの記載があるか確認
$cat .kitchen.yml |grep amzn
  - name: toshihirock/amzn-2015.09

# 起動できるものを確認
$kitchen list
Instance                         Driver     Provisioner  Verifier  Transport  Last Action
default-centos-67                DockerCli  ChefZero     Busser    DockerCli  <Not Created>
default-toshihirock-amzn-201509  DockerCli  ChefZero     Busser    DockerCli  <Not Created>
default-AmazonLinux-20150301     Ec2        ChefZero     Busser    DockerCli  <Not Created>

# AmazonLinuxのイメージを起動。.kitchen.ymlに記載しているように起動時にchefをインストール
$kitchen create default-toshihirock-amzn-20150

# convergeでchefを流してテストする
$kitchen converge default-toshihirock-amzn-201509

# verifyでserverspceを実行
$kitchen verify default-toshihirock-amzn-201509

# 破棄
$kitchen destroy default-toshihirock-amzn-201509

# 起動、chef実行、servespec、破棄を一気に実施
$kitchen test default-toshihirock-amzn-201509
```

コンテナの起動、破棄なので実際にEC2を起動停止するよりめちゃくちゃ早い！！！！最高。

# PackerとVagrantを使ってよりテスト環境を使いやすくする

個別のプロジェクトのインストールは別として以下は常に実施し、時間も掛かるのでPackerでAMI化しちゃいます。

+ ChefDKのインストール
+ Gitのインストール
+ Dockerのインストール、設定

Githubのファイルはこちら。

[toshihirock/chef-test-ec2](https://github.com/toshihirock/chef-test-ec2.git)

```
$git clone https://github.com/toshihirock/chef-test-ec2.git
$cd chef-test-ec2
$packer build chef-test-ec2.json
```

これでできあがり。 **test_chef_ami 1443256201** という感じのAMIができていると思います。

あとVagrantのvagrant-awsプラグインを使って先ほどのAMIを起動すればsync_folderを使って自分の環境のエディタを使ってコードを書き、テストしたい際にはEC2上で実行すれば快適ではないかと思います。