メインコンテンツへスキップ

API サーバを Rust で実装する 〜ローカル開発からデプロイまで〜

·3 分
Rust Heroku GitHubActions

前回の記事で作成した Rust で実装した API サーバーをローカルで動作確認できる環境を整えます。また Heroku にもスムーズにデプロイできるように設定します。いずれも、Docker を利用します。
GitHub のリポジトリは こちら

ローカル開発環境を整える
#

基本的には以前の記事: docker-compose で DB とアプリサーバを立ち上げ、接続する で行ったことと同じです。

DB 設定のための Dockerfile
#

/db/Dockerfile に DB のための設定を書きます:

1ENV LANG ja_JP.utf8
2FROM postgres:14-alpine AS db

/db/docker-entrypoint-initdb.d のディレクトリの下に DB 立ち上げ直後に実行する SQL を置いておきます。

  • 今回は、DB 立ち上げ時にロール root とデータベース root がないよ、というエラーメッセージが出るのに対応するための SQL を置いてます。
    • なぜこのエラーが出るのかはよく分かってません。
1CREATE ROLE root WITH LOGIN;
2CREATE DATABASE root;

##Q# API サーバのための Dockerfile

/server/Dockerfile に API サーバを立ち上げるための設定を書きます:

  • マルチステージビルドを利用します。
  • 開発環境(develop-stage)ではローカル環境でサーバーを稼働させるためのコマンド cargo-watch 、Diesel で PostgreSQL を扱う際に必要な libpg-dev 、Diesel でマイグレーションなどを行うのに必要なコマンド diesel_cli をインストールします。また、本番環境のために成果物をコピーしておきます。
  • ビルド環境(build-stage)では、開発環境を用いて本番環境向けのビルドを実行します。
  • 本番環境(production-stage) では、ビルド環境の成果物をコピーしてきて実行します。
 1# 開発環境
 2FROM rust:1.57.0 as develop-stage
 3WORKDIR /app
 4RUN cargo install cargo-watch
 5RUN apt install -y libpq-dev
 6RUN cargo install diesel_cli
 7COPY . .
 8
 9# ビルド環境
10FROM develop-stage as build-stage
11RUN update-ca-certificates
12RUN cargo build --release
13
14# 本番環境
15FROM rust:1.57.0-slim-buster as production-stage
16RUN apt-get update
17RUN apt-get install libpq-dev -y
18COPY --from=build-stage /app/target/release/actix_web_sample .
19CMD ["./actix_web_sample"]

docker-compose.yml の記述
#

DB とサーバーの両方をローカル環境で立ち上げるために、 docker-composed.yml を用意します。

  • DB は pg_isready コマンドでヘルスチェックを行い、正常に起動していることを確認します。
  • DB の環境変数は後に紹介する .env ファイルの内容に合わせて設定します。
  • サーバーは DB が正常に起動したことを確認してから起動します。
    • 起動の際、diesel によるマイグレーション(テーブル作成)と cargo watch によるアプリ起動を行います。
    • cargo watch により、ソースの保存を自動で検知して再ビルドしてくれるため、開発が楽になります。
 1version: '3.7'
 2services:
 3  server:
 4    build:
 5      context: ./server
 6    target: 'develop-stage'
 7    ports:
 8      - "8080:8080"
 9    depends_on:
10      db:
11    condition: service_healthy
12    volumes:
13      - ./server:/app
14      - cargo-cache:/usr/local/cargo/registry
15      - target-cache:/app/target
16    command: /bin/sh -c "diesel setup && cargo watch -x run"
17    tty: true
18  
19   db:
20     build:
21       context: ./db
22     ports:
23       - '5432:5432'
24     environment:
25       POSTGRES_USER: admin
26       POSTGRES_PASSWORD: password
27       POSTGRES_DB: postgres
28     volumes:
29       - ./db/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
30     healthcheck:
31       test: ["CMD-SHELL", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "postgres"]
32       interval: 10s
33       timeout: 5s
34       retries: 5
35       
36  volumes:
37    cargo-cache:
38    target-cache:

.env の用意
#

ローカルで立ち上げる際に利用する環境変数の設定を記述します。

  • Rust の dotenv で環境変数を設定するようにしています。
1SERVER_PORT=8080
2DATABASE_URL=postgres://admin:password@db:5432/postgres
3SERVER_ADDRESS=0.0.0.0

ローカル開発環境でのサーバ&DB立ち上げ
#

docker-compose を用いてローカル環境を立ち上げます。
前述の通り、開発中は立ち上げっぱなしにしておけば、Rust のコードを変更した場合でも自動的にビルドして反映してくれます。

1docker-compose up -d

Heroku の設定
#

コマンドラインから設定します。アプリの追加と、 PostgreSQL のアドオンを追加します。

  • PostgreSQL のアドオンを追加すると自動的にアプリの環境変数 DATABASE_URL が追加されます。
 1# Heroku に CLI でログイン(ブラウザが立ち上がるのでログインを行う)
 2$ heroku login
 3heroku: Press any key to open up the browser to login or q to exit:
 4Opening browser to https://cli-auth.heroku.com/auth/cli/browser/XXXXX
 5Logging in... done
 6Logged in as hoge@mail.com
 7heroku create rust-web-api-server-sample
 8
 9# アプリの作成
10$ heroku create rust-web-api-sample
11Creating ⬢ rust-web-api-sample... done
12https://rust-web-api-sample.herokuapp.com/ | https://git.heroku.com/rust-web-api-sample.git
13
14# アプリに PostgreSQL のアドオン(Hobby プラン)を追加
15$ heroku addons:create heroku-postgresql:hobby-dev --app rust-web-api-sample
16Creating heroku-postgresql:hobby-dev on ⬢ rust-web-api-sample... free
17Database has been created and is available
18 ! This database is empty. If upgrading, you can transfer
19 ! data from another database with pg:copy
20Created postgresql-contoured-25420 as DATABASE_URL
21Use 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 オプション)
  • ヘルスチェックも入れています。
 1name: Deploy to heroku
 2
 3on:
 4  push:
 5    branches:
 6      - main
 7  workflow_dispatch:
 8
 9jobs:
10  build:
11    runs-on: ubuntu-latest
12    steps:
13      - uses: actions/checkout@v2
14      - uses: akhileshns/heroku-deploy@v3.12.12
15        with:
16          heroku_api_key: ${{secrets.HEROKU_API_KEY}}
17          heroku_app_name: "rust-web-api-server-sample"
18          heroku_email: ${{secrets.HEROKU_EMAIL}}
19          buildpack: "https://github.com/emk/heroku-buildpack-rust.git"
20          usedocker: true
21          appdir: server
22          healthcheck: "https://rust-web-api-server-sample.herokuapp.com/health"
23          checkstring: "Ok"
24          rollbackonhealthcheckfailed: true
25      - run: |
26          heroku config:set SERVER_ADDRESS=0.0.0.0          

Heroku へのデプロイにあたっての補足:環境変数について
#

Heroku でのサーバー起動の際のポートの指定は、環境変数 PORT の値を使用するようにすればOKですので、以下のように書けばOKです。

1 let port = std::env::var("PORT")
2        .ok()
3        .and_then(|val| val.parse::<u16>().ok())
4        .unwrap_or(CONFIG.server_port)

今回は、ローカルでの起動も考慮に入れているので、環境変数 PORT の指定がなければ、 .env ファイルの SERVER_PORT の値を利用するという設定にしています。

まとめ
#

  • Docker を用いて、ローカルでの開発と Heroku へのデプロイを簡単にすることができました。
    • 今回の成果物をテンプレとすると、 Heroku に載せる Rust で実装した API サーバーを簡単に構築できる気がします。
  • 他のサーバー環境へのデプロイ方法も試してみたいです。
  • GitHub Actions のワークフローをもう少し工夫して、テストなどを走らせるようにもしておきたいです。