Infrastructure as codeの思想に感動し、AWSを使い始めて早1年になりますが、なんだかんだで最近はコードを書いてばかりでAWSを触っていませんでした。貴重な無料期間も終了し、非常にもったいないことをしたなと反省中です。
さて気を取り直して、ついに先日 EC2 Container Registry (ECR)がアナウンスされましたね!
まだUSリージョンのみの公開で、相変わらず東京リージョンはもう少しかかりそうですが、これでDockerイメージの管理までAWS上で行えるようになるわけです。
ところで、インフラ構築の自動化といえば、Elastic BeanstalkやOpsWorks、CloudFormationなどなどAWS上でもサービスが乱立しており、少し外に目を向ければ、Chef、Puppet、Ansible、Terraformなどなど、もう困っちゃうぐらい選択肢にあふれている昨今です。
ただ、どうにもこれらの選択肢はアプリケーションのデプロイに難があったり、やたらと複雑だったりでイマイチピンと来ない部分があって、インフラのコード化という視点から見るとDockerが一番しっくり来る感じでしたので、今回はECRの東京リージョン公開を期待しつつ、ECSでInfrastructure as code的なものをやってみたいと思います。
Dockerとは
Dockerとは、2014年辺りから注目され始めたコンテナ型仮想化におけるコンテナ管理ソフトウェアです。詳細な説明はもっと専門的なページに任せるとして、注目され始めた背景としては、コンテナ型仮想化が従来の仮想化に比べて効率がよかったことや、Infrastructure as code、Immutable Infrastructureの思想の高まりがあると個人的には考えています。
DockerではDockerfileと呼ばれるファイルを元にDockerイメージを作成し、それを元にコンテナを立ち上げることができます。Dockerfileはシェルスクリプトを書くようなシンプルさで記述できるので、少ない学習コストでインフラをコードに落としこむことができます。また、作成したDockerイメージはDocker Hubや冒頭に紹介したECRにpushすることで、他のユーザもそのイメージを元にコンテナを立ち上げることができます。
Dockerで立ち上がるものはコンテナなので、簡単に破棄して、立ち上げ直すことができます。Dockerfileを変更することでインフラの変更も管理できるので、Infrastructure as codeとImmutable Infrastructureにはまさにぴったりというわけです。
ECSとは
正式名称はEC2 Container Service、クラスタとして構築されたEC2インスタンス上にDockerでコンテナを立ち上げ、コンテナ群を管理することができます。
個人的に大きなメリットだと思っているのは、ELBが内部的にサポートされていて、コンテナはEC2のAvailability Zoneを意識しながら冗長化するように配置されるというポイントです。また、EC2のAuto Scalingのように、内部のコンテナもコンテナ数が常に一定数起動しているように管理されます。
versions
Dockerイメージを作成するローカルマシンにはMac OS X Yosemiteを使用します。
名前 | バージョン |
---|---|
docker | 1.9.1 |
docker-compose | 1.5.2 |
docker-machine | 0.5.4 |
VirtualBox | 5.0.12 |
Dockerのインストール
Docker toolboxを使用してインストールします。
Mac版のpkgをダウンロードして、展開するとインストーラーが起動しますので、後は案内通りに進めていくだけです。インストールが終わると、QuickstartをTerminal.appまたは類似のコマンドラインツールで実行します。Quickstartが終了すると、ターミナル上にクジラさんのAAが表示されます。愛らしいですね。
準備ができたら、以下のコマンドでコンテナを立ち上げてみます。
$ docker run hello-world
Hello World!を表示するコンテナが立ち上がってすぐに終了します。エラーが特に発生しなければOKです。実行する際に、存在しないhello-worldという名前のDockerイメージをDocker Hubに取得しにいき、取得されたイメージが保存されます。以下のコマンドで登録されているイメージの一覧を確認することができます。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE hello-world latest 0a6ba66e537a 10 weeks ago 960 B
ちなみに、もうhello-worldのイメージは使用しないので、削除しておきましょう。
$ docker rmi 0a6ba66e537a $ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
Dockerfileを作成
さて、Dockerイメージからコンテナが立ち上がることが確認できたので、Dockerイメージを自作してみます。適当なディレクトリにDockerfileという名前でファイルを作成します。今回はGitHubにおいている自作のFuelPHPサンプルコードをデプロイし、Apacheを起動するようなDockerfileを作成します。
できあがったものがこちら。
Dockerfile
FROM centos:centos6 MAINTAINER wata <wata@example.com> RUN yum -y update RUN yum -y install httpd RUN yum -y install php php-devel RUN yum -y install git RUN chkconfig httpd on EXPOSE 80 WORKDIR /var/www/html RUN git clone https://github.com/wata727/twitterloginsample.git WORKDIR twitterloginsample RUN php composer.phar self-update RUN php composer.phar update CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
使っているDockerfileのコマンドはたった6種類です。
コマンド名 | 説明 |
---|---|
FROM | 元となるDockerイメージを指定します。今回はCentOSの6を使用します。 |
MAINTAINER | Dockerfileの作成者です。記載することが望ましいようです。 |
RUN | ビルド時に実行されるコマンドです。普通にシェルスクリプトを書く感覚でスラスラ書けます。 |
WORKDIR | ビルド時の作業ディレクトリです。移動することができます。 |
CMD | コンテナ起動時に実行されるコマンドです。今回はhttpdを実行するように指定しています。 |
もちろん、他にもDockerfileのコマンドはあるので、必要に応じて様々な操作をすることができますが、今回のような簡単なサンプルであれば6種類もあれば十分に記述できるわけです。
作成できたらDockerfileのある場所でビルドを実行します。
$ docker build -t wata727/fuel-example:0.1 .
このとき、Dockerイメージの名前の構成は {作者名}/{イメージ名}:{タグ} のようになります。この作者名が後述のDocker Hubでのユーザ名と一致しない場合、Dockerイメージの登録に失敗するので注意してください。
ビルドが完了したらイメージを確認。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE wata727/fuel-example 0.1 00c9e0908686 About a minute ago 609.6 MB centos centos6 1a895dd3954a 10 weeks ago 190.6 MB
DockerfileのFROM句にcentos6のイメージを指定したので、存在しないイメージをDocker Hubからpullしてきています。それはともかく、肝心のDockerイメージも無事ビルドできているようですね。さっそくコンテナを立ち上げてみます。
$ docker run -d -p 80:80 wata727/fuel-example:0.1 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 53bef123ad58 wata727/fuel-example:0.1 "/usr/sbin/httpd -D F" 31 seconds ago Up 30 seconds 0.0.0.0:80->80/tcp berserk_cori
docker psで現在立ち上がっているコンテナについて確認することができます。DockerfileのCMDで指定したhttpdの起動が実行されているようですね。docker runのオプション-pでコンテナ側とホスト側のポート番号のマッピングをしています。この例では双方のポート80番がマッピングされているので、ホストの80番に接続すると、コンテナの80番が対応します。つまり、ホストIPにアクセスすれば、Dockerfileでデプロイしたアプリケーションを確認することができます。
Docker toolboxでインストールした場合、docker-machineを使用しているので、以下のコマンドでホストのIPアドレスを確認できます。
$ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS default * virtualbox Running tcp://192.168.99.100:2376 v1.9.1
これで、192.168.99.100/twitterloginsample/publicにアクセスすると、いつものFuelPHPのWelcome画面が表示されます。
コンテナの動作が確認できたら、コンテナは終了しておきましょう。以下のコマンドで実行中のすべてのコンテナを終了させられます。
$ docker rm -f $(docker ps -a -q) $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Docker HubへDockerイメージを登録
Dockerイメージの確認ができましたので、ECSでこのDockerイメージを使用できるようにするために、Docker HubにDockerイメージを登録します。まずはユーザ登録をしましょう。
Docker HubはDocker社が公開しているDockerイメージのためのレジストリサービスです。ここに登録されているイメージはdockerコマンドで参照することができます。ECSもデフォルトでDocker Hubを参照するようになっています。
ユーザ登録ができたら、コマンドラインからログインし、先ほど作成したDockerイメージを指定してpushします。
$ docker login $ docker push wata727/fuel-example:0.1
これでしばらくすれば、Docker Hubにイメージが公開されます!
ECSの設定
Dockerイメージの作成および公開ができましたので、後はこれを使ってECS側の設定を行います。ECSはタスク、サービス、クラスタといった独自の概念を持っておりますので、ひとつずつ説明していきます。
タスク定義
タスクはDockerのコンテナ起動のために行う設定です。使用するDockerイメージ、コンテナに割り当てるメモリ量、ホストとコンテナのポート番号のマッピングなどが該当します。今回はDocker Hubに公開したDockerイメージを使用するので、そのままpushしたときと同様のフォーマットで指定します。
動作確認
以上で設定完了になりますので、ECSを起動します。すると、EC2とECSのステータスが進行中になりますので、それらがすべて完了するのを待ちます。
上記のようにECSのステータスがオールグリーンになったら、デプロイ完了です。クラスタ一覧から起動しているインスタンスの状態を確認できるので、ちょっと見てみましょう。
ここにあるIPアドレスで公開されているので、ローカルでの動作確認時同様、{ホスト名}/twitterloginsample/publicでFuelPHPのWelcome画面を見ることができます。
まとめ
DockerとECSの今後の展開に期待
ECSを触ってみた感じ、簡単かつ違和感なくローカル同様にコンテナを起動できたのはよかったのですが、セキュリティグループやVPCが自動でECS用に作成されてしまうようでした。できれば既存のセキュリティグループやVPCを使えるようになってくれると、部分的なコンテナ化と柔軟な構成ができていいなーと思います。
他のInfrastructure as codeの技術としては、OpsWorksやTerraformに注目していたのですが、OpsWorksはひとつのコードですっきり管理!という感じではなくて結構がっちりした大規模なアプリケーションを管理するためのもので、小規模なインフラにはあまり向かないかなと感じました。Terraformはもともとインフラ構築のみで、サーバの中身そのものはPackerなどを使ってAMIを作って、それをTerraformでデプロイするという役割分担をしていたため、2つのツールを使うのではなく、できれば1つのツールで完結したいなぁと考えた結果、Dockerに今回行き着きました。
とはいえ、実はECSもそれなりに規模の大きなアプリケーションを対象にしていたりするのでは?とやっているうちに感じることがあったので、Packer + Terraformの構成も少し試してみたいですね。
Dockerのコンテナによるアプリケーションの実運用事例はあまり多く聞かないので、今後過酷な本番環境でも問題なく運用できる事例が増えて普及が進んでくれたらいいなと思います。ただ、Dockerには色々な批判もあるので、最終的にはDockerである必要はないかもしれませんが、アプリケーションの開発者もインフラの担当者も幸せになれるようなツールが出てきてくれることを望んでいます。
インフラがコードでプログラミングできるようになったこの時代、プログラミングが好きでプログラマになったバックエンドエンジニアの人たちもインフラは担当領域に入ってきてもいいのではないかなと最近感じています。俺は私はプログラマだから〜と言わず、AWSでInfrastructure as codeの楽しさを体験してみては。
Reference
Mac OS X へのDockerのインストール方法 - Qiita
Dockerfile の書き方「私的」なベストプラクティス - ようへいの日々精進XP
【まだまだ未完成】CentOS+Apache+Mysqlを自動起動するコンテナのDockerfileを書いてみた | ログってなんぼ
Qoosky - Dockerfile ベストプラクティス (仮)
Dockerを使って開発イメージを作成し、公開します :: Co-mit Engineers Blog
Amazon EC2 Container Service(ECS)でPrivate Docker Registryを立ててみた - tehepero note(・ω<)