AWS上にDocker環境構築を、EC2に直で入れる方法とECS(マネジメントコンソール上で操作)の導入の2つの方法で入れてみた。

Dockercon 16で今、Dockerの話題で盛り上がっています。
http://2016.dockercon.com/

AWS上にDocker環境をEC2上とECSの2つの方法で導入してみたのでメモ。
ECSはマネジメントコンソールからの操作で、2016年6月での地点での操作方法となります。
ECSを使用した場合はクラスタ管理から、負荷分散、スケーリングなどの面倒も見てくれます。

#EC2インスタンス上に直で入れる方法
DockerイメージはDocker Hubからpullしてきたものを使用する場合

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

$ sudo docker pull '<Docker Hubユーザー名>/<イメージ名>:<Tag名>'
'<Tag名>': Pulling from '<Docker Hubユーザー名>/<イメージ名>'
efd26ecc9548: Pull complete
a3ed95caeb02: Pull complete
d1784d73276e: Pull complete
72e581645fc3: Pull complete
：
：
Digest: sha256:c8beee1a15bbc67edee9e4532a105d500ebc5935879557b020ba7dd1cfa0bdb1
Status: Downloaded newer image for '<Docker Hubユーザー名>/<イメージ名>:<Tag名>'

$ sudo docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
'<Docker Hubユーザー名>/<イメージ名>'   '<Tag名>'                 83602403dcba        10 weeks ago        856.4 MB

#コンテナを作成する
$ sudo docker run -d --name '<適当なコンテナ名>' -p '<Dockerで受け付けるポート番号>':'<アプリケーション側で受け付けるポート番号>' (-p '<Dockerで受け付けるポート番号>':'<アプリケーション側で受け付けるポート番号>') '<Docker Hubユーザー名>/<イメージ名>:<Tag名>'
65e1dd030dc25c46aa046c4daa5bac194931657e7188305df9723f77c2a9434f
# 又は docker run -ti タグ名(docker build -t タグ名　などで指定したもの)

$ sudo docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                                      NAMES
65e1dd030dc2        '<Docker Hubユーザー名>/<名>:<Tag名>'   "~~~"   5 minutes ago       Up 5 minutes        0.0.0.0:'<Dockerで受け付けるポート番号>'->'<アプリケーション側で受け付けるポート番号>'/tcp, 0.0.0.0:'<Dockerで受け付けるポート番号>'->'<アプリケーション側で受け付けるポート番号>'/tcp, 8181/tcp   '<コンテナ名>'

#コンテナを起動する
$ sudo docker start <コンテナ(CONTAINER ID or NAMES)>
65e

#ホストからコンテナにログイン
$ sudo docker exec -it '<コンテナ名>' bash
```


```http://'<IP address>':'<Dockerで受け付けるポート番号>'/'<PATH>'``` にアクセス。




#ECSを導入する方法
DockerイメージをDockerfileからビルドする場合

ECSで出てくるタスクとサービスの語句についてですが、
タスクはバッチ処理のような一回限りの処理、サービスは永続的な処理のものです。

ECSを導入する場合について、初めてのウォークスルーの場合と自分で構築する場合の2つのパターンを取り上げる。

##初めての場合
以下のURLから行うことでいつでも初めのウォークスルーの状態で設定を行うことができる。

https://console.aws.amazon.com/ecs/home#/firstRun
### Get Startedを選択する。

```
Deploy a sample application onto an Amazon ECS Cluster
Store container images securely with Amazon ECR
```
の2つにチェックを入れた状態でContinueを選択する。

###Step 1: Configure repository 
Repository name: ecs-test
として、Next stepを選択する。

###Step 2: Build, tag, and push Docker image

ローカルで以下の作業を行う。OSはUbuntu14.04を使用した場合のものとなる。

```bash
$ mkdir docker-test
$ cd docker-test/
 $ aws ecr get-login --region us-west-2 
docker login -u AWS -p '<Password>' -e none https://~~~.ecr.us-west-2.amazonaws.com
```

Dockerをインストールしていない場合はインストールする。

```bash
$ wget -qO- https://get.docker.com/ | sh
$ sudo usermod -aG docker $USER
$ su - $USER
```


```bash
#先ほどaws ecrコマンドで生成されたdockerログインコマンドをコピペする。
$ docker login -u AWS -p '<Password>' -e none https://~~~.ecr.us-west-2.amazonaws.com
Warning: '-e' is deprecated, it will be removed soon. See usage.
Login Succeeded
```

Login Succeededと出ればログイン成功である。
次にDockerfileを作成する。

```bash
$ vim Dockerfile
```


例えば、以下のように作成する。

```
FROM ubuntu:1４.04

RUN apt-get update -y
RUN apt-get install -y git curl apache2 php5 libapache2-mod-php5 php5-mcrypt php5-mysql

RUN rm -rf /var/www/*
ADD src /var/www

RUN a2enmod rewrite
RUN chown -R www-data:www-data /var/www
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2

EXPOSE 80

CMD ["/usr/sbin/apache2", "-D",  "FOREGROUND"]
```
コマンドはroot権限で実行される。

Dockerfileと同じ階層にsrcディレクトリを作成し、デプロイ対象となるファイルを用意する。

```bash
mkdir src && cd src
vim index.php
```

```index.php
<html>
    <body>Hello, world!</body>
</html>
```



作成したDockerfileを元にイメージファイルをビルドする。

```bash
$ docker build -t ecs-test .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM php:5.6-apache
 ---> 306370e37ec1
Step 2 : RUN a2enmod rewrite
 ---> Using cache
 ---> e1523614a754
Step 3 : RUN docker-php-ext-install pdo_mysql mbstring
 ---> Using cache
 ---> 320d68c3ce69
Step 4 : RUN echo 'date.timezone = Asia/Tokyo' >> /usr/local/etc/php/conf.d/99_myconf.ini
 ---> Using cache
 ---> c404ecfa957f
：
：
Successfully built 4b0569678402
```

タグ付けをする。

```bash
$ docker tag ecs-test:latest ~.ecr.us-west-2.amazonaws.com/ecs-test:latest
```

ECSにPUSHする。

```bash
$ docker push ~.ecr.us-west-2.amazonaws.com/ecs-test:latest
The push refers to a repository [~.ecr.us-west-2.amazonaws.com/ecs-test]
4a7543f178f3: Pushed 
40da234f8043: Pushed 
749b6ec33d91: Pushed 
293489163a1e: Pushed 
61cd28b6b81d: Pushed 
7e9e746ecede: Pushed 
63e87343ae05: Pushed 
98fe2d81486b: Pushed 
0dcf6f5af493: Pushed 
8f86eea0e277: Pushed 
358d48af1ad0: Pushed 
72a004f2419f: Pushed 
f1c36d11715d: Pushed 
2f141c453bd9: Pushed 
ee63decf49d1: Pushed 
42755cf4ee95: Pushed 
latest: digest: sha256:5fa6532df8ee23369ee533afbc1cfdd53897839775c6c6d7207abc2e4486d7a6 size: 18332
```

Next Stepを押す

###Step 3: Create a task definition 

```
Task definition name: console-sample-app-static-ecs-test

Container name: sample-app-ecs-test
Image: ~~.ecr.us-west-2.amazonaws.com/ecs-test:latest(デフォルト値)
Maximum memory (MB)*: 300(デフォルト値)
Port mapping: Host port 80, Container port 80, Protocol tcp
```

Next stepを選択する。

###Step 4: Configure service

Configure serviceの項目を以下のように入力する。

```
Service name: sample-webapp-ecs-test
Desired number of tasks*: 2
```

Elastic load balancingの項目を以下のように入力する。

```
Configure service: sample-app-ecs-test:80
ELB listener protocol*: HTTP
ELB listener protocol*: 80
ELB health check: http:80/
```

Service IAM roleの項目が以下のようになっていることを確認する。

```
Select IAM role for service: ecsServiceRole
```

Next stepを選択する。

###Step 5: Configure cluster 

Configure clusterの項目を以下のように入力する。

```
Cluster name: default
EC2 instance type: t2.micro
Number of instances: 2
Key pair : '<EC2のネットワーク＆セキュリティの項目中のキーペアで作成するなどして所持しているキーペア>'
```

Security groupの項目は以下のようになっていることを確認する。

```
Allowed ingress source(s)*: Anywhere, 0.0.0.0/0
```

Container instance IAM roleの項目が以下のようになっていることを確認する。

```
Container instance IAM role: Create new role
```

Review & launchを選択する。


###Step 6: Review 
Launch instance & run serviceを選択する。


###Launch status
ECS statusが4項目の内すべてが"pending"から"created"の状態になり、
EC2 instance statusが15項目の内すべてが"pending"から"created"の状態になったら、ECS clusterの準備が完了である。

View serviceを選択する。

###動作確認
EC2のロードバランサの項目から先ほど作成したロードバランサを選択する。

説明というタブのDNS名を確認して、ブラウザからアクセスする。
Dockerで作成した通りのものがでれば動作確認ができたことになる。

##自分で構築する場合
###リポジトリの作成
画面左のリポジトリを選択し、[リポジトリの作成]を選択。

####ステップ 1: リポジトリの設定
例えば、以下のようにリポジトリ名を入力して、[次のステップ]を選択。

リポジトリ名: sample-ecr

####ステップ 2: Docker イメージの構築、タグ付け、プッシュ
以下のコマンドを入力して[次のステップ]

```
docker-machine start default
eval $(docker-machine env default)
```

```
$ aws ecr get-login --region us-east-1 --profile private
$ docker login -u AWS -p XXXXXXXXXX -e none https://<AWSアカウント番号>.dkr.ecr.us-east-1.amazonaws.com\n
$ docker build -t sample-ecr .
$ docker tag sample-ecr:latest <AWSアカウント番号>.dkr.ecr.us-east-1.amazonaws.com/sample-ecr:latest
$ docker push <AWSアカウント番号>.dkr.ecr.us-east-1.amazonaws.com/sample-ecr:latest
```

--profileに注意


###タスク定義の作成
画面左のタスク定義を選択し、[新しいタスク定義の作成]を選択。

タスク定義名: sample-task

コンテナの定義から[コンテナの追加]を選択。

以下のように情報を入力して、[追加]を選択。
コンテナ名: sample-container
イメージ: arn:aws:ecr:us-east-1:<AWSアカウント番号>:repository/sample-repository:latest
メモリ制限(MB): ハード制限 128
ポートマッピング: ホストポート 80, コンテナポート 80, プロトコル tcp

[作成]を選択。

以降、イメージに変更がある時は[新しいリビジョンの作成]からリビジョンを作成してサービスの方も関連づけるリビジョンを更新する。

#クラスターの作成
クラスターを作成する前に、ECSに最適化されたAMIでEC2インスタンスを作成する。もしくはエージェントをインストールする方法 (http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-agent-install.html )もある。

####EC2インスタンスの作成
マネジメントコンソールからEC2を選択。
[インスタンスの作成]を選択。

#####1. AMI の選択
コミュニティAMIを選択し、検索ウィンドウからecs-optimizedと入力し出てくる以下のAMIを選択。

amzn-ami-2016.09.a-amazon-ecs-optimized - ami-1924770e

#####2. インスタンスタイプの選択
t2.microを選択し、[次の手順: インスタンスの詳細の設定]を選択。

#####3. インスタンスの設定
IAMロール: ecsInstanceRole
(※ Amazon EC2 Container Service Role中のAmazonEC2ContainerServiceRoleのロールを割り当てる。)

自動割り当てパブリックIP: [サブネット設定を使用(有効)]もしくは[有効化]で有効にしておく。

[次の手順: ストレージの追加]を選択。

#####4. ストレージの追加
[次の手順: インスタンスのタグ付け]を選択。

#####5. インスタンスのタグ付け
[次の手順: セキュリティグループの設定]を選択。

#####6. セキュリティグループの設定
[ルールの追加]を選択し、HTTP TCP 80 0.0.0.0を追加しておく。
[次の手順: 確認]を選択。
#####7. 確認
内容を確認したら、インスタンスの作成をする。鍵は適切なものを選択しておく。


画面左のクラスターを選択し、[クラスターの作成]を選択。
クラスター名を以下のように入力し、[作成]を選択。

EC2の起動が完了すると、ECSのクラスターに```登録済みコンテナインスタンス: 1```の状態でdefaultという名前で追加される。


####クラスターの作成
クラスターから先ほどのdefaultを選択。

[サービス]タブから[作成]を選択し、以下のように入力。

タスク定義: sample-task
クラスター: default
サービス名: sample-service
タスクの数: 1
最小ヘルス率: 50
最大率: 200

[サービスの作成]を選択。
ELBやAuto Scalingの設定も可能だが、ここでは省略。

[タスク]タブから[新しいタスクの実行]を選択。
以下のように入力して、[タスクの実行]を選択。

タスク定義: sample-task
クラスター: default
タスクの数: 1


以上で完了。


#Amazon ECS CLI 一覧

```
create-cluster
create-service
delete-cluster
delete-service
deregister-container-instance
deregister-task-definition
describe-clusters
describe-container-instances
describe-services
describe-task-definition
describe-tasks
discover-poll-endpoint
list-clusters
list-container-instances
list-services
list-task-definition-families
list-task-definitions
list-tasks
register-container-instance
register-task-definition
run-task
start-task
stop-task
submit-container-state-change
submit-task-state-change
update-container-agent
update-service
wait
```

#Dockerfile命令 一覧

```
FROM
MAINTAINER
RUN
CMD
LABEL
EXPOSE
ENV
ADD
COPY
ENTRYPOINT
VOLUME
USER
WORKDIR
ARG
ONBUILD
STOPSIGNAL
```


#Dockerコマンド 一覧

```
#管理系
daemon
info
inspect
version

#イメージ系
build
commit
export
history
images
import
load
rmi
save
tag

#コンテナ系
attach
cp
create
diff
events
exec
kill
logs
pause
port
ps
rename
restart
rm
run
start
stats
stop
top
unpause
wait

#Hub ・レジストリ系
login
logout
pull
push
search

#ネットワーク・接続系
network_connect
network_create
network_disconnect
network_inspect
network_ls
network_rm

#共有データ・ボリューム系
volume_create
volume_inspect
volume_ls
volume_rm
```
例えば、以下のように操作する。

```bash
$ sudo docker start 65e
65e
$ sudo docker stop 65e
65e
$ sudo docker restart 65e
65e

$ sudo docker rm 65e
Error response from daemon: You cannot remove a running container 65e1dd030dc25c46aa046c4daa5bac194931657e7188305df9723f77c2a9434f. Stop the container before attempting removal or use -f

$ sudo docker stop 65e
65e
$ sudo docker rm 65e
65e
```


#コマンドチートシート
http://qiita.com/voluntas/items/68c1fd04dd3d507d4083
http://qiita.com/suzukihi724/items/961112f6578a60dd6266

#ユースケース
##PHPの場合のDockerサンプル
src/以下にアプリケーション本体を置く。

```
FROM ubuntu:1４.04

RUN apt-get update -y
RUN apt-get install -y git curl apache2 php5 libapache2-mod-php5 php5-mcrypt php5-mysql

RUN rm -rf /var/www/*
ADD src /var/www

RUN a2enmod rewrite
RUN chown -R www-data:www-data /var/www
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2

EXPOSE 80

CMD ["/usr/sbin/apache2", "-D",  "FOREGROUND"]
```

##PHPの場合のDocker Composeによるサンプル
Dockerfileを作成。

```
FROM php:5.6-apache

# Configuration of Redis
ENV PHP_REDIS_VERSION 2.2.7

RUN docker-php-source extract \
      && curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHP_REDIS_VERSION.tar.gz \
      && tar xfz /tmp/redis.tar.gz \
      && rm -r /tmp/redis.tar.gz \
      && mv phpredis-$PHP_REDIS_VERSION /usr/src/php/ext/redis \
      && docker-php-ext-install redis

RUN usermod -u 1000 www-data \
    && groupmod -g 1000 www-data

RUN echo 'date.timezone = Asia/Tokyo' >> /usr/local/etc/php/conf.d/99_myconf.ini
RUN echo 'error_reporting = E_ALL' >> /usr/local/etc/php/conf.d/99_myconf.ini

RUN apt-get update
RUN apt-get install -y libmysqlclient-dev mysql-client libpng12-dev libjpeg-dev libmagickwand-dev

# Install PHP GD
RUN docker-php-ext-configure gd --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j $(nproc) gd

# Install PHP Imagick
RUN pecl install imagick
RUN docker-php-ext-install pdo_mysql mbstring
RUN docker-php-ext-enable imagick

RUN a2enmod rewrite

WORKDIR /var/www
USER root

EXPOSE 80

# Update the default apache site with the config we created.
ADD docker-etc/apache-config.conf /etc/apache2/sites-enabled/000-default.conf
```
docker-compose.ymlを以下のように作成。

```docker-compose.yml
web:
  build: .
  ports:
    - "80:80"
  volumes:
    - .:/var/www
  links:
    - db
    - redis
db:
  image: mysql
  environment:
    MYSQL_ROOT_PASSWORD: root
redis:
  image: redis
```

docker-etc/apache-config.conf

```
<VirtualHost *:80>

DocumentRoot /var/www

<Directory /var/www>
   Options FollowSymLinks
   AllowOverride All
   Require all granted
</Directory>

ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
</VirtualHost>
```

##Railsを動かす場合のDocker composeによるサンプル

```Dockerfile
FROM ruby:2.3.1

ENV APP_ROOT /app

RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       libmysqld-dev \
                       libmysqlclient-dev \
                       mysql-client \
                       --no-install-recommends && \
    rm -Rf /var/lib/opt/lists/*

RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT

ADD . /app
RUN bundle install
```

```docker-compose.yml
version: '2' 
services:
  app:
    build: .
    command: bundle exec rails s -b 0.0.0.0
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    links:
      - db
  db: 
    image: mysql:5.6.30
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
```

##Node.jsを動かす場合のDockerfileサンプル

```
FROM ubuntu:14.04

RUN apt-get update -y
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y nodejs npm build-essential

RUN update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10

RUN rm -rf /var/www/*
ADD src /var/www

WORKDIR /var/www
RUN npm install

RUN chown -R www-data:www-data /var/www

EXPOSE 3000
CMD ["node", "index.js"]
```

サービスを作成してタスクを実行すると、Node.jsが実行される。

##注意点
- ```RUN update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10```を追加することが重要。
- タスクの定義でコンテナ定義時にポートマッピング設定でDockerfile中のポート番号と合わせることを忘れないようにする。
    - EXPOSEで設定したポート番号とコンテナ側のポート番号を一致させ、ホスト側のポート番号で実際に公開に使用するポート番号を設定する。WebSocketなどを使用するときはホスト側のポート番号で指定する。


#参考
http://qiita.com/toshihirock/items/dfd42314158aff073e9d
http://qiita.com/con_mame/items/1df441d86c703a0e6fa6

ECSのデプロイサンプル
https://github.com/awslabs/ecs-demo-php-simple-app

Dockerコマンド 一覧
https://docs.docker.com/v1.11/engine/reference/run/
Docker 公式
https://www.docker.com/
Docker Hub
https://hub.docker.com/

Amazon ECS CLI
http://docs.aws.amazon.com/cli/latest/reference/ecs/

Node.js Dockerfile
https://gist.github.com/thom-nic/724a2af410e90a654e35