API サーバを Rust で実装する 〜ローカル開発からデプロイまで〜
— Rust Heroku GitHubActions — 8 min read
前回の記事で作成した Rust で実装した API サーバーをローカルで動作確認できる環境を整えます。また Heroku にもスムーズにデプロイできるように設定します。いずれも、Docker を利用します。
GitHub のリポジトリはこちら。
ローカル開発環境を整える
基本的には以前の記事:docker-compose で DB とアプリサーバを立ち上げ、接続する で行ったことと同じです。
DB 設定のための Dockerfile
/db/Dockerfile
に DB のための設定を書きます:
ENV LANG ja_JP.utf8FROM postgres:14-alpine AS db
/db/docker-entrypoint-initdb.d
のディレクトリの下に DB 立ち上げ直後に実行する SQL を置いておきます。
- 今回は、DB 立ち上げ時にロール
root
とデータベースroot
がないよ、というエラーメッセージが出るのに対応する ための SQL を置いてます。- なぜこのエラーが出るのかはよく分かってません。
CREATE ROLE root WITH LOGIN;CREATE DATABASE root;
##Q# API サーバのための Dockerfile
/server/Dockerfile
に API サーバを立ち上げるための設定を書きます:
- マルチステージビルドを利用します。
- 開発環境(develop-stage)ではローカル環境でサーバーを稼働させるためのコマンド cargo-watch 、Diesel で PostgreSQL を扱う際に必要な libpg-dev 、Diesel でマイグレーションなどを行うのに必要なコマンド
diesel_cli
をインストールします。また、本番環境のために成果物をコピーしておきます。 - ビルド環境(build-stage)では、開発環境を用いて本番環境向けのビルドを実行します。
- 本番環境(production-stage) では、ビルド環境の成果物をコピーしてきて実行します。
# 開発環境FROM rust:1.57.0 as develop-stageWORKDIR /appRUN cargo install cargo-watchRUN apt install -y libpq-devRUN cargo install diesel_cliCOPY . .
# ビルド環境FROM develop-stage as build-stageRUN update-ca-certificatesRUN cargo build --release
# 本番環 境FROM rust:1.57.0-slim-buster as production-stageRUN apt-get updateRUN apt-get install libpq-dev -yCOPY --from=build-stage /app/target/release/actix_web_sample .CMD ["./actix_web_sample"]
docker-compose.yml
の記述
DB とサーバーの両方をローカル環境で立ち上げるために、 docker-composed.yml
を用意します。
- DB は
pg_isready
コマンドでヘルスチェックを行い、正常に起動していることを確認します。 - DB の環境変数は後に紹介する
.env
ファイルの内容に合わせて設定します。 - サーバーは DB が正常に起動したことを確認してから起動します。
- 起動の際、diesel によるマイグレーション(テーブル作成)と
cargo watch
によるアプリ起動を行います。 cargo watch
により、ソースの保存を自動で検知して再ビルド してくれるため、開発が楽になります。
- 起動の際、diesel によるマイグレーション(テーブル作成)と
version: '3.7'services: server: build: context: ./server target: 'develop-stage' ports: - "8080:8080" depends_on: db: condition: service_healthy volumes: - ./server:/app - cargo-cache:/usr/local/cargo/registry - target-cache:/app/target command: /bin/sh -c "diesel setup && cargo watch -x run" tty: true db: build: context: ./db ports: - '5432:5432' environment: POSTGRES_USER: admin POSTGRES_PASSWORD: password POSTGRES_DB: postgres volumes: - ./db/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d healthcheck: test: ["CMD-SHELL", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "postgres"] interval: 10s timeout: 5s retries: 5 volumes: cargo-cache: target-cache:
.env
の用意
ローカルで立ち上げる際に利用する環境変数の設定を記述します。
- Rust の dotenv で環境変数を設定するようにしています。
SERVER_PORT=8080DATABASE_URL=postgres://admin:password@db:5432/postgresSERVER_ADDRESS=0.0.0.0
ローカル開発環境でのサーバ&DB立ち上げ
docker-compose を用いてローカル環境を立ち上げます。
前述の通り、開発中は立ち上げっぱなしにしておけば、Rust のコードを変更した場合でも自動的にビルドして反映してくれます。
docker-compose up -d
Heroku の設定
コマンドラインから設定します。アプリの追加と、 PostgreSQL のアドオンを追加します。
- PostgreSQL のアドオンを追加すると自動的にアプリの環境変数
DATABASE_URL
が追加されます。
# Heroku に CLI でログイン(ブラウザが立ち上がるのでログインを行う)$ heroku loginheroku: Press any key to open up the browser to login or q to exit:Opening browser to https://cli-auth.heroku.com/auth/cli/browser/XXXXXLogging in... doneLogged in as hoge@mail.comheroku create rust-web-api-server-sample
# アプリの作成$ heroku create rust-web-api-sampleCreating ⬢ rust-web-api-sample... donehttps://rust-web-api-sample.herokuapp.com/ | https://git.heroku.com/rust-web-api-sample.git
# アプリに PostgreSQL のアドオン(Hobby プラン)を追加$ heroku addons:create heroku-postgresql:hobby-dev --app rust-web-api-sampleCreating heroku-postgresql:hobby-dev on ⬢ rust-web-api-sample... freeDatabase has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pg:copyCreated postgresql-contoured-25420 as DATABASE_URLUse heroku addons:docs heroku-postgresql to view documentation
GitHub Actions の設定
ここから、手でコマンドをポチポチ打ってデプロイをしても良いのですが、せっかくなので、 GitHub Action でデプロイを自動化します。
以下のように .github/workflows/deploy-to-heroku.yml
を作成します:
- AkhileshNS/heroku-deploy を利用してデプロイします。
- Heroku へのデプロイは Docker を利用する設定にしています。
- あらかじめ、リポジトリのシークレットに以下を設定しておきます:
HEROKU_API_KEY
: Heroku の Account Settings 画面 -> API Key で取得できる API キーを指定HEROKU_EMAIL
: Heroku のアカウントで使用しているメールアドレスを指定
- 起動したサーバーの IP アドレスは環境変数で設定します。
- 環境変数の設定は
heroku config:set
コマンドで行います。 - Github Actions 内で
heroku
コマンドを利用するためには Heroku へのログイン処理を行う必要があるのですが、AkhileshNS/heroku-deploy を一度でも使うことで Heroku へのログインを行なってくれます。- このツールを使ってログイン処理だけ行うということもできます(
justlogin
オプション)
- このツールを使ってログイン処理だけ行うということもできます(
- 環境変数の設定は
- ヘルスチェックも入れています。
name: Deploy to heroku
on: push: branches: - main workflow_dispatch:
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: akhileshns/heroku-deploy@v3.12.12 with: heroku_api_key: ${{secrets.HEROKU_API_KEY}} heroku_app_name: "rust-web-api-server-sample" heroku_email: ${{secrets.HEROKU_EMAIL}} buildpack: "https://github.com/emk/heroku-buildpack-rust.git" usedocker: true appdir: server healthcheck: "https://rust-web-api-server-sample.herokuapp.com/health" checkstring: "Ok" rollbackonhealthcheckfailed: true - run: | heroku config:set SERVER_ADDRESS=0.0.0.0
Heroku へのデプロイにあたっての補足:環境変数について
Heroku でのサーバー起動の際のポートの指定は、環境変数 PORT
の値を使用するようにすればOKですので、以下のように書けばOKです。
let port = std::env::var("PORT") .ok() .and_then(|val| val.parse::<u16>().ok()) .unwrap_or(CONFIG.server_port)
今回は、ローカルでの起動も考慮に入れているので、環境変数 PORT
の指定がなければ、 .env
ファイルの SERVER_PORT
の値を利用するという設定にしています。
まとめ
- Docker を用いて、ローカルでの開発と Heroku へのデプロイを簡単にすることができました。
- 今回の成果物をテンプレとすると、 Heroku に載せる Rust で実装した API サーバーを簡単に構築できる気がします。
- 他のサーバー環境へのデプロイ方法も試してみたいです。
- GitHub Actions のワークフローをもう少し工夫して、テストなどを走らせるようにもしておきたいです。