注目キーワード
  1. react
  2. docker
  3. インターン

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

@saxsirさん主催のawsもくもく会で自作のgolangで書いたアプリをデプロイしてみたのでその時の話をまとめてみたいと思います。

デプロイ方法

設計と方針

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

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

です。実際の手順は、

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

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

# jsのbuild
$ cd frontend
$ npm run build

# jsのアップロード
$ scp -r -i ~/.ssh/[MY_SSH_KEY].pem static/ [email protected][AWS_IP_ADRESS].compute.amazonaws.com:~/app

これでbuildとアップロードが完了です。次にawsにsshにして、

$ cd app
$ ./main

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

nginxのインストール

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

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

# 起動
$ sudo service nginx start

# 再起動
$ sudo service nginx restart

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

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

$ sudo vi /etc/nginx/nginx.conf

つまったところ(そもそも編)

sshがタイムアウトする

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

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

詰まったところ(golang編)

binaryが実行できない

手元では動いていたから流石にこれぐらいは実行できるだろうと思って軽い気持ちでやったら、、

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

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

$ 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の嵐になってしまいました。
そのときのコードは、コピペしたもので

location / {
    fastcgi_pass  localhost:8000;
    include       fastcgi_params;
}

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

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

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

location / {
    proxy_pass  localhost:8000;
    include       proxy_params;
}

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

$ 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.

変更後に、再起動しようとしたらこんなエラーがでました。

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

$ 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から書かないといけないらしい。

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とかでデプロイしてるイメージですが、実際その辺はどうなんだろ、、

詳しい人だれか教えてください!