Makefileを書いてgoアプリケーションのデプロイを1コマンドで自動化する

Makefileを書いてgoアプリケーションのデプロイを1コマンドで自動化する

前回はゴリ押しでgoアプリケーションをawsにデプロイできるようになるところまでやったので、今回はMakefileを書いてコマンド一発でデプロイできるようにしていきたいと思います。

Goアプリケーションを初めてawsにデプロイした話

ディレクトリ構成の変更

前回は、適当に構築していたのでbuildするといろんなところにbuildされたファイルが生成されて管理がとても面倒でした。
そこで、今回はディレクトリ構成を変更してまとめてデプロイできるようにしました。

こんな感じ。package.jsonとかをrootに持ってきて、それによってwebpackの出力先がrootに変更になったのが大きな変更点です。

Makefile

Makefileとは

Makefileとは、よしなにコマンドを生成できる設定ファイルです。(説明あってるか不安。。)

[タスク名]: [依存ターゲット1] [依存ターゲット2] ...
    [コマンド]

というフォーマットに従って書いていきます。依存ファイルに関しては後述します。

$ make [タスク名]

とすれば実行できます。

依存ターゲット

これはそのタスクが依存するタスクを選択することができる機能です。複数選択できますが順番は保障されません。
また、ターゲットとなるファイルが存在している場合はそのタスクは実行されずにパスされます。

言葉だけではわかりにくいので具体例を挙げると、

build: main static/js/bundle.js

main:
    GOOS=linux GOARCH=amd64 go build main.go

static/js/bundle.js
    npm run build

このように記述しておくことで、

$ make build

を実行すると、

$ make main
$ make static/js/bundle.js

が実行されてからbuildのコマンドが実行されます。(今回の例では何もコマンドがありませんが。)

しかし、一回実行するとbinaryとbundle.jsが生成されるので2回目は何も起こりません。
これはbuildに時間がかかるファイルなどに有効なそうです。逆に毎回変更したいアプリケーションのbinaryやbudle.jsには向いていません。毎回rmするか依存ターゲットを使わない設計にしましょう。

今回は、

clean:
    rm main

のようにして、必要がなくなったら削除するようにしています。

[追記]
逆にターゲットとなるタスクもファイルもない場合は、makeの時点でこけるようになっています。

build: server.go
    GOOS=linux GOARCH=amd64 go build main.go
    npm run build

として実行すると

$ make build
make: *** No rule to make target `server.go', needed by `build'.  Stop.

となってしまいます。

要は、makeは依存関係をよしなにやってくれるといいうことです。

デプロイの手順

方針

デプロイ用にrootにbuildしたものと静的ファイルが作られます。

main(binary)
static/
templates/

これらをtarで固めて、scpでアップロードしてsshしてその中でtarを展開してbinaryを起動という手順です。(tarに関してはこちらを参照

tarで固める

build:
    GOOS=linux GOARCH=amd64 go build main.go
    npm run build

zip: build
    tar zcvf main.tar.gz main templates/ static/

tarコマンドを使って圧縮されたアーカイブファイル(?ちょっと自信ない)を作成します。zipするにはbuildされたファイが必要になるのでbuildをターゲットにしています。

これを実行することでrootにmain.tar.gzというアップロード用のファイルが作成されます。

scpでアップロード

これは前回とそんなに変わりません。makeに変更しただけです。

upload:
    scp -i ~/.ssh/$(PEM_NAME).pem main.tar.gz ec2-user@$(AWS_IP_ADRESS).compute.amazonaws.com:~/app
    scp -i ~/.ssh/$(PEM_NAME).pem provisioning/Makefile ec2-user@$(AWS_IP_ADRESS).compute.amazonaws.com:~/app

ついでに、本番環境で実行する用のMakefileもアップロードしています。

汎用性とセキュリティを高めために環境変数を使用しています。

sshしてその先でMakefileを実行

このように分離しているのは、sshで接続した先では同じmakeタスクを継続することができないからです。その代わりにsshと同時に、ssh先で実行するコマンドを指定してtarの展開とbinaryの起動を行います。

ssh:
    ssh -i ~/.ssh/$(PEM_NAME).pem ec2-user@$(AWS_IP_ADRESS).compute.amazonaws.com \
    cd app/ \&\& \
    make start

$ ssh [host]に続いコマンドを並べることでssh先で実行するコマンドを指定することができます。知らなかった。。

tarの展開とbinaryの実行

ついでにリモートのawsも起動しています。binaryの実行に上二つのタスクを依存させてるだけです。

unzip:
    tar zxvf main.tar.gz
    rm main.tar.gz

nginx:
    sudo service nginx start

# うまくバックグラウンド起動にできないから出力先を変更
start: unzip nginx
    ./main >> /dev/null &

binaryの実行の出力先をdev/nullにしているのはそうしないとバックグラウンドでうまく起動してくれなかったからです。makeからsshでコマンド指定して実行するには無理があったのかな、、
これの問題点は再起動(再デプロイ)の際にいちいちプロセスを終了させないといけないことです。何か管理するアプリとかを使った方がいいのかな、、

$ make deploy

はい、満を持しての、

deploy: zip
    make upload
    make clean
    make ssh

これで、全部が

$ make deploy

の一発で終わるようになります。感動。

謝辞

@saxsirさん!いつも開催ありがとうございます!
毎週毎週めっちゃ成長できてるのでありがたい限りです!!

来週以降もよろしくお願いします。。そろそろ、VGオフィスにも、、w

Makefileたち

build: main.go
    GOOS=linux GOARCH=amd64 go build main.go
    npm run build

zip: build
    tar zcvf main.tar.gz main templates/ static/

upload:
    scp -i ~/.ssh/$(PEM_NAME).pem main.tar.gz ec2-user@$(AWS_IP_ADRESS).compute.amazonaws.com:~/app
    scp -i ~/.ssh/$(PEM_NAME).pem provisioning/Makefile ec2-user@$(AWS_IP_ADRESS).compute.amazonaws.com:~/app

ssh: # \の前にスペースないと怒られる
    ssh -i ~/.ssh/$(PEM_NAME).pem ec2-user@$(AWS_IP_ADRESS).compute.amazonaws.com \
    cd app/ \&\& \
    make start

clean:
    rm main
    rm main.tar.gz

deploy: zip # make deploy前にzipを実行
    make upload
    make clean
    make ssh

# 以下provisioning/Makefile

unzip:
    tar zxvf main.tar.gz
    rm main.tar.gz

nginx:
    sudo service nginx start

# うまくバックグラウンド起動にできないから出力先を変更
start: unzip nginx
    ./main >> /dev/null &

リポジトリはこちら
Dragon-taro/go-react