はじめに
以下は『なんか流行ってるみたいだし、試しにDocker使ってみたい』そんな手段の目的化メモです。
いやいや、カスな情報はいらないよ(=^・^=)
と、いう人は下記のリンクを読むと良さそうです。
http://blog.gopheracademy.com/advent-2014/easy-deployment/
http://deeeet.com/writing/2015/01/08/dockerhub-hook/
自動デプロイの流れ
以下のような感じでしょうか。
- ローカルでgoのアプリを書く
- リモートリポジトリにpushする
- リポジトリ側がwebhookでデプロイ先サーバに対してPOSTを投げる
- デプロイ先サーバはPOSTを受けてデプロイ用のシェルスクリプトを実行する
- リポジトリから
git pull
する - Dockerfileに基づいて
docker build
- 起動中のコンテナを
docker kill
- 新しいコンテナを
docker run
ローカルでgoのアプリを書く
書きましょう。
Productionモードで実行したら80ポートをみる、Developmentモードだったら9999番をみる、のような処理は追加しておくとよさそうです。
当方は標準のhttp
を使ってサーバを実装してしまいました。
なので、下記のようにos.Getenv()
を使って環境変数を読み込み、リッスンするポートを指定するようにしました。
import (
"net/http"
"os"
)
func main() {
http.HandleFunc("/", awesomeHandler)
port := os.Getenv("AWESOME_PORT")
if port == "" {
port = ":9999"
}
http.ListenAndServe(port, nil)
}
イケてるWAFだったらモード切り替えあるかもしれませんね。
https://github.com/go-martini/martini#martini-env
リモートリポジトリにpushしたタイミングでwebhookを発行する
今回はbitbucketを使いました。
githubとかでもあるはず。
https://confluence.atlassian.com/display/BITBUCKET/Manage+Bitbucket+hooks
ここにかいてるPOST
ってヤツを使いました。
リポジトリへのPushを検知したらhttps://"自分のドメイン"/deploy
を叩くように設定します。
デプロイ先のサーバでwebhookなPOSTを受け取る
bitbucketから飛んでくるPOSTを捌く必要があります。
どのように捌くのが理想形か分かりませんが、とりあえずデプロイ先のサーバにwebhookなPOSTを受け取るようにしました。
こちらを使います。
https://github.com/bketelsen/captainhook
これはgoで書かれたwebhook処理に特化したサーバです。
1エンドポイントを1つのjsonファイルで表現することができます。
公式のリポジトリに書いてるQuickStartをコピペしただけですが、こんな感じ。
{
"scripts": [
{
"command": "ls",
"args": [
"-l",
"-a"
]
},
{
"command": "echo",
"args": [
"hello"
]
}
]
}
このようなjsonファイルを用意してcaptainhook -configdir ~/captainhook
でwebhook専用のWEBサーバが立ち上がります。
ちなみにポートはデフォルト8080です。
自分はこんな感じで動かしました。0.0.0.0
指定しないと動かなかったからです。
captainhook -listen-addr=0.0.0.0:8080 -echo -configdir ~captainhook
で、このサーバに対して
curl -X POST http(s)://"自分のドメイン"/endpoint
ってやるとhelloとか表示されるはず。
上の例ではls
やecho
を実行しましたが、もちろんシェルスクリプトも実行できます。
{
"scripts": [
{
"command": "/script/path/awesome.sh"
}
]
}
で、実際に実行してるスクリプトは下記のとおり。
#!/bin/bash
cd /app/path # アプリのコードが設置されているディレクトリに移動
git pull origin master # リポジトリから最新版のコードを取得(なので鍵登録は事前に必要)
docker build --tag="awesome/base:latest" .
# アプリのコードにDockerfileを含めて、それをbuildするようにしてます
OLDPORTS=( `docker ps | tail -1 | awk '{print $1}'` ) # 起動中のコンテナのid一覧を取得
for i in ${OLDPORTS[@]}
do
docker kill $i # 古いコンテナを消す
done
sleep 3s
docker run -d -p 80:80 -t awesome/base:latest # 新しいコンテナを実行する
これでwebhookからdockerを再起動するところまでできました。
Dockerfileのなかみ
コンテナ内で何をするかDockerfileに書きます
FROM ubuntu
MAINTAINER tokyotaro
RUN apt-get update
RUN apt-get install -y sqlite golang git gcc
RUN mkdir go
ENV GOPATH /path/go
ENV PATH /path/go/bin:$PATH
ADD . /var/www/awesome # ここでアプリのコードをコンテナ内に配置
# 必要なライブラリを導入
RUN ["go", "get", "github.com/go-gorp/gorp"]
RUN ["go", "get", "github.com/mattn/go-sqlite3"]
# 80番ポートを開放
EXPOSE 80
WORKDIR /var/www/awesome
ENV AWESOME_PORT :80 # アプリ側でポート指定するために使う環境変数
ENTRYPOINT ["go", "run", "app.go"]
# docker runしたときに実行される処理
# 今回はgo標準のhttpパッケージを使ったのでgo run app.goです。
なかなかカスっぽい感じですが、これでhttp(s)://"自分のドメイン"
をブラウザで見ると自分のアプリが表示されるはずです。