AWS Elastic Beanstalk for Docker で Go の Web アプリを動かしてみた

先日発表された [Elastic Beanstalk for Docker](http://aws.typepad.com/aws_japan/2014/04/aws-elastic-beanstalk-for-docker.html) で、任意の言語で書いたアプリケーションを Elastic Beanstalk 上で動かすことができることができるようになりました。

なので、さっそく Go で書いた Web アプリを動かしてみました。
ここで使用したソースコードは以下から入手できます。

https://github.com/hakobera/docker-golang-on-elasticbeanstalk-sample

## Web アプリケーションの作成

作成する Web アプリケーションは、gitで管理する必要があります。
以下、Helloと出力するだけの Go の Webアプリを `hello.go` という名前で保存してください。

```
package main

import (
  "fmt"
  "net/http"
)

type Hello struct{}

func (h Hello) ServeHTTP(
  w http.ResponseWriter,
  r *http.Request) {
  fmt.Fprint(w, "Hello!")
}

func main() {
  var h Hello
  http.ListenAndServe(":8080", h)
}
```

## Dockerfile と Dockerrunaws.json の用意

### Dockerfile

Ubuntu 14.04 をベースに go の環境を構築しています。

```bash
FROM ubuntu:14.04

RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y curl git mercurial make binutils bison gcc build-essential golang

# Set GOPATH/GOROOT environment variables
RUN mkdir -p /go
ENV GOPATH /go
ENV PATH $GOPATH/bin:$PATH

# Set up app
ADD . /app
RUN cd /app && go build hello.go

# Removed unnecessary packages
RUN apt-get autoremove -y

# Clear package repository cache
RUN apt-get clean all

EXPOSE 8080
CMD ["/app/hello"]
```

### Dockerrun.aws.json

Elastic Beanstalk の用の設定ファイルです。`hello.go` で `8080` ポートで Web アプリを起動しているので、ここでは `8080` を指定しています。`docker run -p 8080:8080` と同じ動きをします。なお、ポートは複数かけますが、最初のやつしかバインドされないので、複数ポートを待受ける Docker ファイルは Elastic Beanstalk のELB経由では使えません。

```json
{
  "AWSEBDockerrunVersion": "1",
  "Ports": [
    {
      "ContainerPort": "8080"
    }
  ]
}
```

## git リポジトリの用意

* hello.go
* Dockerfile
* Dockerrun.aws.json

の３つのファイルを同一のディレクトに作成し、`git init && git add . && git commit -m 'First commit'` して、git リポジトリとして管理します。

## アプリケーションのデプロイ

準備ができたので、これを Elastic Beanstalk にデプロイします。今回、アプリケーションのデプロイには拙作の [ebfly](https://github.com/hakobera/ebfly) を利用します。Docker サポートは、0.0.7 からなので、既にインストールしている人はアップデートしてください。

```
$ gem install ebfly
```

Elastic Beanstalk の操作をするので、環境変数でアクセスキーを設定してください。

```
$ export AWS_ACCESS_KEY=[your access key]
$ export AWS_SECRET_ACCESS_KEY=[your secret key]
$ export AWS_REGION=us-east-1 # ap-northeast-1 でもOK
```

### アプリケーションと Docker environment の作成

Elastic Beanstalk はアプリケーションを作成し、その中に environment (production, staging, dev, ...)を作成していくという構成になっているので、まず、アプリケーションを作成します。ここでは docker という名前のアプリケーションを作成してみます。

```
$ ebfly app create docker
```

アプリケーションを作成後、Docker 用の environment を作成します。現状、Docker 0.9のみがサポートされているので、以下のコマンドで作成します。ここでは `web` という名前の environment を作成しています。

```
$ ebfly env create web -s docker0.9 -a docker
```

コマンドを打つと environment の作成が始まりますが、これに5分程度かかります。
environment の状態は、`ebfly env info` で確認できます。`status` が `Ready` になれば準備完了です。

```
$ ebfly env info web -a docker
ebfly env info web -a docker

=== environment info ===
application name: docker
environment id:
environment name: docker-web
description:
status:     Ready
health:     Green
tier:     WebServer Standard 1.0
solution stack name:  64bit Amazon Linux 2014.03 v1.0.2 running Docker 0.9.0
endpoint url:   awseb-e-w-AWSEBLoa-956NXWFC8619-1360484152.us-west-1.elb.amazonaws.com
cname:
updated at:   2014-04-25 01:57:49 UTC
```

### デプロイ

以下のコマンドで、`master` ブランチをデプロイします。

```
$ ebfly env push web master -a docker
```

ここでも、デプロイに10分〜20分程度の時間がかかります。Docker build が走っているので当然ですね。ここの時間を短縮したい場合は、事前にイメージを作っておきましょう。

`ebfly env info` で状態を確認するか、Managementコンソールで以下のようになればデプロイ完了です。

![スクリーンショット 2014-04-25 11.34.44.png](https://qiita-image-store.s3.amazonaws.com/0/25774/07d15b4e-b431-99ff-005d-f2be51d3ea81.png)

### アプリにアクセスする

`ebfly env info` で表示される `cname` がデプロイされたアプリケーションのURLになります。これをブラウザで開いて、`Hello` と表示されるか確認してください。

```
$ ebfly env info web -a docker

=== environment info ===
...
cname: docker-web-1234556677.elasticbeanstalk.com
...
```

Mac の人は、`ebfly env open` コマンドを使うと簡単です。

```
$ ebfly env open web -a docker
```

![スクリーンショット 2014-04-25 11.39.56.png](https://qiita-image-store.s3.amazonaws.com/0/25774/8eae661b-1f04-da46-7804-afeceee60218.png)

できた！

## まとめ

- Elastic Beanstalk for Docker すごい！簡単！
- ebfly 便利！簡単！

すごく簡単なので、皆さん、色々な言語で試してみてください。