⚒️

【Rails / Flutter】Rails API をバックエンド、 Flutter をフロントエンドとする構成を作る Part1

2024/06/30に公開
1

今回は、 Rails をバックエンドに、 Flutter をフロントエンドにした構成を作ってみたいと思います。
インフラ構成に Terraform を使い、 API 通信には GraphQL を採用し、これまで単体でしか勉強したことがなかったものを組み合わせてみることにしました。

さっそく、始めましょう!

1. Docker で Rails API アプリを構築

Docker のインストール方法

まず最初に、Docker をインストールします。各プラットフォームに対応したインストールガイドに従ってください。

Dockerfile の作成

次に、プロジェクトディレクトリを作成し、 Dockerfile を作成します。

mkdir my_app
cd my_app
FROM ruby:3.3.3

RUN apt-get update -qq && apt-get install -y nodejs yarn default-mysql-client

WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN bundle install

COPY . /app

CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose を用いた開発環境の構築

次に、 docker-compose.yml ファイルを作成し、開発環境を構築します。

docker-compose.yml
version: '3'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: myapp_development
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql

  web:
    build: .
    command: bundle exec rails s -b '0.0.0.0'
    volumes:
      - ".:/myapp"
    ports:
      - "3000:3000"
    depends_on:
      - db

volumes:
  db_data:

Rails プロジェクトの作成

そして、新しい Rails API プロジェクトを作成します。
ターミナルを開き、以下のコマンドを実行してください。

# Rails プロジェクトの作成
docker-compose run web rails new . --force --no-deps --database=mysql --api

これで、Docker を用いた Rails API アプリの環境構築が完了しました。
次に、 Rails のセットアップをおこないます。

2. Rails API アプリのセットアップ

必要な Gems のインストールと設定

まず、必要な Gem を追加します。

Gemfile
gem 'rails', '7.1.0'
gem 'mysql2', '>= 0.5.4'

次に、以下のコマンドを実行して Gems をインストールします。

docker-compose run web bundle install

データベースの設定

次に、config/database.yml ファイルを以下のように設定します。

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <your-username>
  password: <your-password>
  host: db

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: myapp_production
  username: <your-prod-username>
  password: <your-prod-password>

データベースを作成するために、以下のコマンドを実行します。

docker-compose run web rails db:create

シンプルな API エンドポイントの作成

最後に、シンプルな API エンドポイントを作成しておきます。

home_controller.rb
class HomeController < ApplicationController
  def index
    render plain: "OK"
  end
end
config/routes.rb
root 'home#index'

次に、Terraform を用いた AWS リソースの構成に進みます。

3. Terraform で AWS リソースを構成

Terraform のインストールと設定

まず、Terraform をインストールします。各プラットフォームに対応したインストールガイドに従ってください。

Terraform プロジェクトの作成

次に、新しいディレクトリを作成し、Terraform プロジェクトを初期化します。

mkdir terraform_project
cd terraform_project
terraform init

AWS クレデンシャルの設定

AWS CLI を使用して、クレデンシャルを設定します。

aws configure

Docker イメージのビルドと Amazon ECR へのプッシュ

まず、Docker イメージをビルドし、Amazon ECR にプッシュします。

# ECR リポジトリの作成
aws ecr create-repository --repository-name my_api

# Docker イメージのビルド
# M1 Mac なので buildx を使用していますが、ARMベースでなければ docker build -t my_app . で問題ないはずです
docker buildx build --platform linux/amd64 -t habit-coach --load .


# ECR ログイン
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com

# Docker イメージのタグ付け
docker tag my_api:latest <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com/my_api:latest

# Docker イメージのプッシュ
docker push <aws_account_id>.dkr.ecr.us-west-2.amazonaws.com/my_api:latest

インフラストラクチャ(VPC、サブネット、セキュリティグループ、Fargate、RDS)の構築

次に、main.tf ファイルを作成し、AWS リソースを定義します。

# main.tf
provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_security_group" "web_sg" {
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "db_sg" {
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 3306
    to_port     = 3306
    protocol    = "tcp"
    security_groups = [aws_security_group.web_sg.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_ecs_cluster" "main" {
  name = "main-cluster"
}

resource "aws_ecs_task_definition" "web" {
  family                   = "web-task"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"

  container_definitions = jsonencode([
    {
      name      = "web"
      image     = "<aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/my_api:latest"
      essential = true
      portMappings = [
        {
          containerPort = 3000
          hostPort      = 3000
        }
      ]
    }
  ])
}

resource "aws_ecs_service" "web" {
  name            = "web-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.web.arn
  desired_count   = 1

  network_configuration {
    subnets          = [aws_subnet.public.id]
    security_groups  = [aws_security_group.web_sg.id]
    assign_public_ip = true
  }
}

resource "aws_db_instance" "default" {
  allocated_storage    = 10
  engine               = "mysql"
  instance_class       = "db.t2.micro"
  name                 = "mydb"
  username             = "root"
  password             = "password"
  parameter_group_name = "default.mysql8.0"
  vpc_security_group_ids = [aws_security_group.db_sg.id]
  skip_final_snapshot  = true
  db_subnet_group_name = aws_db_subnet_group.main.name
}

resource "aws_db_subnet_group" "main" {
  name       = "main-subnet-group"
  subnet_ids = [aws_subnet.private.id]
}


※ 本来は以下のようにリソースごとの tf ファイルを作って管理した方がいいですが、この記事では省略しています

次に、以下のコマンドを実行して Terraform 設定を適用します。

terraform apply

これで、AWS リソースの構築が完了しました。

4. Rails API アプリのデプロイ

Fargate サービスが作成され、 Amazon ECR にプッシュした Docker イメージが使用されるので、作業は不要です。
タスクが正常に開始され、 Rails が Fargate で動作していることを確認してください。

動作確認

デプロイ後の動作確認を行うために、ブラウザで Fargate サービスのパブリックIPにアクセスします。
ECS → クラスター → サービス → タスク → パブリックIP にて確認ができます。

「シンプルな API エンドポイントの作成」にて OK と表示されるように作ったのでそうなっていれば完了です。

まとめと次のステップ

この記事では、 Docker を用いて Rails API アプリを構築し、 Terraform を使って AWS 上にリソースを構成しデプロイする方法を紹介しました。

次の記事では Flutter アプリをフロントエンドとして GraphQL で繋ぐ方法を解説します!

Discussion

豆太郎豆太郎

参考にさせてもらっています。ありがとうございます。

1.の際に、

docker-compose run web rails new . --force --no-deps --database=mysql --api

を実行すると以下のようなエラーがでました。

error waiting for container:                 
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "rails": executable file not found in $PATH: unknown

記事には書かれていないのですが、Gemfileにrailsを追加したり、Gemfile.lockを作成したほうが良いでしょうか?