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

[@saxsir](https://twitter.com/saxsir)さん主催のawsもくもく会で自作のgolangで書いたアプリをデプロイしてみたのでその時の話をまとめてみたいと思います。

# デプロイ方法
## 設計と方針
設計としては、適当EC2インスタンスを立ててそこに手元でbuildしたbinaryを配置して実行するというシンプというか一番初歩的なものにしてみました。
アプリ自体はgolangとreactで書いていて起動に必要なのはこのbinaryとbundle.jsだけです。
プロキシとしてnginxを配置して、80ポートで受け取って8000ポートのアプリに繋ぐという設計に途中から変えました。

[Dragon-taro/go-react](https://github.com/Dragon-taro/go-react)

このリポジトリにのせているのでよかったらみてください。（回りくどくhello worldするだけのアプリですw）

## 手順
開発環境のディレクトリ構成としては、

```
main.go
frontend/
	static/
		js/
			bundle.js
	index.js
	package.json
	webpack.config.json

templates
	index.html
```

です。また、本番環境の構成としては、

```
~/app
	main(binary)
	frontend/
		static/
			js/
				bundle.js
templates/
	index.html
```


です。実際の手順は、

```shell
# golangのtargetをlinuxにしたbuild
$ GOOS=linux GOARCH=amd64 go build  main.go

# golangのアップロード
$ scp -i ~/.ssh/[MY_SSH_KEY].pem main ec2-user@[AWS_IP_ADRESS].compute.amazonaws.com:~/app
$ scp -r -i ~/.ssh/[MY_SSH_KEY].pem template ec2-user@[AWS_IP_ADRESS].compute.amazonaws.com:~/app

# jsのbuild
$ cd frontend
$ npm run build

# jsのアップロード
$ scp -r -i ~/.ssh/[MY_SSH_KEY].pem static/ ec2-user@[AWS_IP_ADRESS].compute.amazonaws.com:~/app
```
これでbuildとアップロードが完了です。次にawsにsshにして、

```shell
$ cd app
$ ./main
```

これでbinaryを実行できます。

## nginxのインストール
調べてでてきたやつを実行してもエラーが出てきて入らなかったので、エラーのいう通りにnginxを入れました。

```shell
# インストール
$ sudo amazon-linux-extras install nginx1.12

# 起動
$ sudo service nginx start

# 再起動
$ sudo service nginx restart
```

デフォの設定だとrootにアクセスした時にnginxのデフォのページに飛びます。表示されることを確認しましょう。

設定を変えるときは、以下のファイルをrootで編集します。

```shell
$ sudo vi /etc/nginx/nginx.conf
```

# つまったところ（そもそも編）
## sshがタイムアウトする
これはどう考えてもインスタンスを立てたところがおかしいと思いそのあたりを調べてみました。
最初はインスタンスが何か間違っていると思って立て直すことを検討したのですが、そもそもインスタンスに接続できていないだけな気がして、セキュリティグループとルートテーブルあたりを調査してみました。

結果的にはサブネットがインターネットゲートウェイに接続されていませんでした。プライベートなサブネットになっていてそりゃ繋がりませんわ、、って話。

# 詰まったところ（golang編）
## binaryが実行できない
手元では動いていたから流石にこれぐらいは実行できるだろうと思って軽い気持ちでやったら、、

```shell
$ ./main
-bash: ./main: バイナリファイルを実行できません
```

実行することすらできませんでした。手元で動いていたのになんで！？って思ったんですけど、そりゃ手元で動くから本番環境で動くわけがないんですよね。だってmac用にコンパイルしたbinaryですからw
それを解決する方法として手元でlinux用にコンパイルしてからアップロードする必要があったんです。

```shell
$ GOOS=linux GOARCH=amd64 go build main.go
```
だから、いつもと違うコマンドでコンパイルしていたんです。うん、goってすごく便利。

## リクエストを送ったらなぜかpanicしてる
さて、起動したからと思ってリクエストを送ってみるとなぜかpanicしていました。
エラーを調べてみると、templateファイルがないですよ、と。

いやいや手元では動いてたしbuildもしたのに、、って、htmlがbinaryにコンパイルされるわけないやんってオチでした。大人しくtemplates/をデプロイして解決。

## ssh下でのcurlは通るのにブラウザからアクセスすると何も表示されない
アプリケーションサーバーは8000で開いていたけど、HTTPでリクエストがきたときは80です。当たり前。
リッスンするのを80に変えたんですがうまくいかなかったので、プロキシを導入することに。（もしかしたらこのときpanicしてたのかもしれないって後から思いました。次は80で開けるのも試してみよう）

プロキシとしてnginxを導入したのですが、こいつが一番の悩みの種でした、、w

# 詰まったところ（nginx編）
## 502が返ってくる
confファイルを編集して、80でlistenして8000でアプリに繋ぐ設計にしたはずが、502の嵐になってしまいました。
そのときのコードは、コピペしたもので

```conf:nginx.conf
location / {
	fastcgi_pass  localhost:8000;
	include       fastcgi_params;
}
```

としてました。で、リクエストを投げると502でした。ssh下でのcurlは通っていたので、接続の問題のようです。

logを見てみると、どうもfastcgiとかいうやつが悪いみたい。てか、fastcgiって何って思ったらcacheみたいなやつなんですね。
それの接続先？をgolangのサーバーにしてるからそりゃ動かんわって話です。

プロキシとして動かしたいときはproxyに変更する必要があったようです。そこで、以下のように変更したらいいらしいとわかったので変更しました。（次のエラーに続く）

```conf:nginx.conf
location / {
	proxy_pass  localhost:8000;
	include       proxy_params;
}
```

## confを変更したらnginxが起動しなくなった

```shell
$ sudo service nginx start
Redirecting to /bin/systemctl start nginx.service
Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.
```
変更後に、再起動しようとしたらこんなエラーがでました。

エラーを探すコマンドを使って調べてみると、

```shell
$ nginx -t
2019/01/11 04:51:26 [emerg] 5221#0: invalid URL prefix in /etc/nginx/nginx.conf:48
nginx: configuration file /etc/nginx/nginx.conf test failed
```
ふむ、、
とりあえず、そのままググってみると、どうも`proxy_pass`にはhttpから書かないといけないらしい。

```conf:nginx.conf
location / {
	proxy_pass    http://localhost:8000;
	include       proxy_params;
}
```

ってしたらいけました！

これで晴れてhello worldが表示されました。長かった、、

# 反省点
## ディレクトリ構成がよくなかった
デプロイするときのことを考えた構成にいなっておらず、buildされたファイルがばらばらに配置されてしまう構成になっていました。これだとデプロイが面倒なのでもう少し設計を考えた方が良さそう。。
少なくともpackage.jsonをrootに設置した方が楽に柔軟にアウトプットの場所を設定できたんじゃないかと思います。

## デバッグの手際が悪かった
何が悪いのかのもんだいの切り分けがうまくいってなかたで、低レイヤーの知識や何をやっているのかの整理をした方がよかったかなと思います。
まあ、何事も経験なのでちょうどいい勉強になりました。

# 次やりたいこと
## デプロイの自動化
完全に手動でやっていたのでめちゃくちゃめんどかったです。
次はMakefileを書くなりしてコマンド一発でデプロイが完了するようにできたらと思います。
aws-cliとかもいい感じに使いたいお気持ち。

## DBの接続
今回はアプリの準備の関係で間に合わなかったのですが、次はDBにも接続できるアプリを開発してRDSのインスタンスを立ててみたいと思います。
さらにつまるポイントが増えそうですが、めげずに頑張っていきます！

# 疑問点
## nginxはgit管理すべき？
今回は途中からnginxを入れたのでアプリの方には入ってません。インスタンス上にしかないので消えたら終わりです。変更もしにくいです。
ただ、そんなに変更することもないからいいのかなと思ったり、、
それともdockerとかで開発環境でnginxも入れといた方がいいのでしょうか？

## dockerのコンテナとしてデプロイすべき？
今回は一番初歩的なデプロイだったのでバイナリを直接触りましたが、これはdockerとかにした方がいいのでしょうか？
モダンな設計だとdockerとかでデプロイしてるイメージですが、実際その辺はどうなんだろ、、

知り合いに聞いてみたところ、マイクロサービスにするときはdocker-composeで一気にデプロイすることができて便利なようです。なるほど、それなら確かに有効ですね。

※これは、[自分のブログの記事](https://dragon-taro.com/technology/post-500/)からの転載です。