修行のために何かを作る話(最初のページ作成)
この話の続き
今回でトップページ一枚作って、リクエストに応じてトップページを返すだけのwebページまで辿り着く。
構成
RDBMSはPostgreSQL 12.5。仕事で使ってるから。
ローカルのDBはPostgreSQL on Dockerとする。他の開発環境とDBのバージョンでかち合っても良いことないので。
RailsはとりあえずDockerには乗せない。Docker for Macが遅いのが主な理由。
PostgreSQL on Docker
そんなに書くことはない。
DockerイメージはDockerHubから持ってきたものを動かすくらいだし、特段他のDockerイメージと協調することもない。
ただ起動のオプションを覚えておくのが面倒なので、起動シェル作った、くらい。
#!/bin/bash
docker run --rm -d \
-p 5432:5432 \
-v postgres-tmp:/var/lib/postgresql/data \
-e POSTGRES_HOST_AUTH_METHOD=trust \
--name blog_postgresql \
postgres:12.5
止めるときは以下のコマンドを手打ちしているが、上記シェルに起動オプション/停止オプションを受け付けられるようにしても良いかも。そのうち。
$ docker stop blog_postgresql
DB接続と最初のページ
ローカルの接続は大した苦労はなし。
pg gemを導入し、sqlite3 gemを消し、database.ymlをよくある感じで設定したらdb:createが通った。
database.ymlのローカル部分はこんな感じ。
# config/database.yml
default: &default
adapter: postgresql
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
encoding: unicode
username: postgres
password:
host: localhost
port: 5432
development:
<<: *default
database: blog_development
最初のページは伝統の「工事中」ページにした。controller、route、viewだけある状態。
RDS設定
事前の予定通りdb.t3.microでインスタンスを作成する。
RDSを作るときに以下のエラーで怒られた。
DB Subnet Group doesn't meet availability zone coverage requirement. Please add subnets to cover at least 2 availability zones. Current coverage: 1
エラーメッセージの通り、RDSインスタンスに割り当てるDBサブネットは少なくとも2つのAZにまたがっていないといけない模様。
なんでこの制約があるのかは分かっていない。
が、zennの記事でprivateとpublicのsubnetを違うAZで作っていた理由が今分かった。
対処としてはprivateのsubnetをap-northeast-1cに作り直した。
dbインスタンスは自動でprivateの方のsubnetに入ってくれていたが、これはpublicアクセス無しの設定で作ったからかな…?
databaseアクセス用のSecurity Groupを作成して、EC2からのPostgreSQLのポートへのアクセスのみ許可としてRDSインスタンスに設定して完了。
本番環境でRDSと接続する
PostgreSQLと接続するために pg gemをインストールする。
インストール時にヘッダーの不足で怒られた。postgresql-develが必要だった。
なお、この時にこちらの別Noteで記載したPLATFORMSの問題が発生したので、一回EC2にインストールしたgemを全部消して入れ直した。
$ bundle exec gem uninstall -aIx
# bundlerも消しちゃったので入れ直し。Gemfile.lockとバージョンをあわせる。
$ gem install bundler:2.2.8
$ bundle install
とりあえずdb:create…でもうちょっと苦しんだ。
$ bin/rake db:create
rake aborted!
Cannot load database configuration:
YAML syntax error occurred while parsing /path/to/config/database.yml. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Error: (<unknown>): found character that cannot start any token while scanning for the next token at line 31 column 13
Caused by:
Psych::SyntaxError: (<unknown>): found character that cannot start any token while scanning for the next token at line 31 column 13
エラーの箇所はdbのpasswordを指定してる箇所で、文字列の先頭が特殊文字だったためパースに失敗していた模様。
それなりに複雑なパスワードを生成して、それを環境変数参照で取ってきた時にうまく扱えなかった。
database.ymlの当該箇所の値をダブルクォーテーションで囲んでOK。
# in config/database.yml
password: "<%= ENV['POSTGRESQL_PASSWORD'] %>"
デプロイ&確認
以前port 3000で立ち上げてアクセスを確認していたが、あれではダメだったっぽい。
pumaとnginxの設定をして立ち上げた。
設定内容はだいたいzennの記事と以下のページを参考にした。
なおdaemonizeはPuma 5.0.0で削除されている。理由は以下の通り。
OSのsystemdに管理を任せるべきだという話の模様。理解した。
systemdでpumaの起動を管理する
pumaのドキュメンテーションは凄くしっかりしてて、systemdの書き方もサンプルがあった。
とはいえRailsでの場合もサンプルで欲しかったので以下も参考にした。
なるほどbinstubでsbin下を作ってそこにpumaコマンドを通すやり方があるのね。
なお、上記ページそれぞれにサンプルのsystemd設定があるが、自分の構築した環境ではどれも期待通り動かなかった。詳しくはこちらの別Note参照。
以下の方法で環境を構築している場合、systemdのserviceの記載はその環境を意識して設定をする必要がある。
①anyenvやrbenvなどのenv系ユーティリティツールでrubyをインストール
②そのrubyでbundler gemをインストールし、そのbundlerで他gemをインストール
③anyenvなどのinit処理を~/.bashrcに記載
上記の場合、以下のpuma.serviceで上手く動いた。
binstubは不要だった。
# /usr/lib/systemd/system.puma.service
[Unit]
Description=Puma HTTP Server
After=network.target
[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/var/www/blog
ExecStart=/bin/bash -lc "puma"
Restart=always
[Install]
WantedBy=multi-user.target
nginxの導入をしたのでこちらもsystemd経由で自動起動を設定する。
デプロイフローを作る時に忘れないように。
# Nginxのインストール
$ sudo amazon-linux-extras install -y nginx1
# Nginxの起動と自動起動の設定
$ sudo systemctl start nginx && sudo systemctl enable nginx
It's work!
疲れたよー。
まだただの教務だけどこちらです。
その他
git configのpush.defaultを知った。便利。
systemdは概略をどこかで整理したい。
binstubもなんとなくしか理解していないのでどこかで整理したい。
次はデプロイフローの自動化周りをやっていく。
Capistrano…?いや、CodeDeployとかその辺を使うと勉強になりそう。