cloudpack大阪の佐々木です。
内部で開発しているGolangのWebアプリをECSで稼働させるように、CI環境をつくってみました。
パブリックのgithubとかであれば、CircleCIとかでもう少しいい感じにできるとおもうのですが、ローカルのGitリポジトリで開発しているため、パブリックアクセスできないCodeCommitを使う形にしました。
概要
動作イメージは下記のような感じです。
- CodeCommitにプッシュ
- CodePipelineでコミットを検知し、CodeBuildを起動
- CodeBuildでコンパイルし、実行ファイルをビルド、S3に保存
- S3に保存後、ECSのサービスの既存タスクを停止
- サービスが新しいタスクを起動
- 新しいコンテナがS3から実行ファイルをダウンロードし、実行
環境
アプリケーション
もとのアプリケーションのファイルは下記のような感じです。
.
├── app
│ ├── model
│ ├── util
│ └── view
├── bindata.go
├── glide.yaml
├── server.go
└── static
└── js
リージョン
CodeCommitが東京リージョン未対応のため、すべてus-east-1
で作成します。
設定
CodeCommitリポジトリの作成
リポジトリの作成
CodeCommitリポジトリにアクセスするユーザを作成
IAMでユーザを作成します。
AccessKeyを使ったHTTPSでのアクセスも可能ですが、SSH接続でやってみます。
ユーザを作成したら、認証情報のタブを開いて、AWS CodeCommitのSSHキー
の SSH 公開キーのアップロード
をクリックし、SSH公開鍵を貼り付けます。
SSHのconfigに情報を追加します。
Host git-codecommit.*.amazonaws.com
User `SSH キー ID`
IdentityFile ~/.ssh/秘密鍵
コードをpushしておきます。
$ git push origin master
Counting objects: 41, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (34/34), done.
Writing objects: 100% (41/41), 73.27 KiB | 0 bytes/s, done.
Total 41 (delta 3), reused 0 (delta 0)
To ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/gotest
* [new branch] master -> master
Buildしたファイル保存用のS3バケットを作成
保存用S3バケットを作成します。
CodeBuildプロジェクトを作成
下記のようなプロジェクトを作成します。
環境イメージはあらかじめ用意されているGolangのものを使用しました。
当初、自分でコンテナを作成し、ビルド+Dockerコンテナ作成+ECRにプッシュという感じでやりたかったのですが、独自コンテナでDocker on Docker はできない(下記参照)、DockerイメージにGolangをインストールするとビルドのたびに時間がかかりすぎるということで、独自コンテナはあきらめました。
https://forums.aws.amazon.com/thread.jspa?messageID=761184
ロールには自動で生成されるPolicyに、ECSのタスクをコントロールするためにAmazonEC2ContainerServiceFullAccess
を追加しています。
CodeBuild用のファイルを作成
下記の2つのファイルを作成します。
version: 0.1
phases:
install:
commands:
- go get github.com/Masterminds/glide
- go get -u github.com/jteeuwen/go-bindata/...
build:
commands:
- ./build.sh
post_build:
commands:
- aws s3 cp /go/src/git.local/sasaki/gotest/server s3://gotest-pkg/server
- aws ecs stop-task --task `aws ecs list-tasks --cluster $ECS_CLUSTER_NAME --service-name $ECS_SERVICE_NAME --region $AWS_DEFAULT_REGION --query taskArns --output text` --cluster $ECS_CLUSTER_NAME --region $AWS_DEFAULT_REGION
mkdir -p $GOPATH/src/git.local/sasaki/gotest
cp -r app $GOPATH/src/git.local/sasaki/gotest
cp -r static $GOPATH/src/git.local/sasaki/gotest
cp server.go $GOPATH/src/git.local/sasaki/gotest
cp bindata.go $GOPATH/src/git.local/sasaki/gotest
cp glide.yaml $GOPATH/src/git.local/sasaki/gotest
cd $GOPATH/src/git.local/sasaki/gotest
glide up
go-bindata app/view
GOOS=linux GOARCH=amd64 go build server.go bindata.go
buildspec.yml
がCodeBuildの設定ファイルになります。
CodeBuildeでGolang用のコンテナを起動しますので、goコマンドは使える状態になっています。
まずinstallフェーズでgolangのglide
とgo-bindata
をインストールします。
次にbuildフェーズでbuild.sh
を実行します。
開発時はローカルにあるGitリポジトリを使っているので、glide up
でエラーになることを回避するために、ローカル環境と同じパスにソースをコピーしています。
そのあと、go build
でコンパイルしています。
post_buildフェーズでは、S3に実行ファイルをアップロードし、ECSの現在のタスクを停止しています。
artifactでもアップロードはできるのですが、post_buildのECSタスク停止後にアップロードされ、タスクに新しいファイルが反映されないため、このようにしています。
ECRにDockerイメージを作成
下記のDockerfileでDockerイメージを作成し、ECRにプッシュしておきます。
FROM ubuntu:16.04
RUN apt-get -y update
RUN apt-get install -y python-pip
RUN yes | pip install --upgrade awscli
RUN apt-get remove -y python-pip
ADD start_gotest.sh /
ENTRYPOINT ./start_gotest.sh
aws s3 cp s3://gotest-pkg/server ./
chmod +x server
./server
UbuntuにS3でビルドしたファイルをダウンロードし、実行するだけです。
Alpineでやると、 ./server
がどういうわけか、not foundになるので、断念・・・
ECS設定
ECRに保存したイメージを常時稼働するように、タスク定義、サービスの設定します。
CodePipeline
CodePipelineを作成します。
ソースの場所
は作成したCodeCommitリポジトリを指定します。
デプロイ
はCodeDeploy等は使用しないので、デプロイなし
を選択します。
実行
作成したリポジトリに、buildspec.yml
と、build.sh
を追加し、プッシュすると、CodePipelineが反応し、ECSのタスクが再起動されるまで自動実行されます。
イケてないところ
- CodeBuildで使うコンテナが融通がきかない
- 独自コンテナではDocker on Dockerができないとか
- Docker + Go等 みたいなコンテナがないとか
- 本来はビルドプロセスでコンテナイメージつくってECRにプッシュするまでやりたい
- Artifactが使えてない
- そもそもあんまりよくわかってない・・・
- ECRに保存しているアプリケーションを動かすだけのコンテナのサイズがデカすぎ
- S3からダウンロードするためにubuntuにpipインストールして、aws-cliをpipインストールしただけで、こんなに・・・
ubuntu 16.04 117 MB
gotest-image latest 462 MB