吉岡です。11月に開催されるRubyWorld Conferenceに登壇することが決まりました。弊社が提供する「Redmine」のクラウドサービス「My Redmine」のインフラストラクチャーを、Rubyと最新のクラウド技術を活用した構成へと再構築したことについて発表します。ぜひ会場までお越しください!
さて、今日のブログはAWSのFargateを利用して、RailsのDB作成とマイグレーションを複数サイトで同時実行したことについての話です。
AWSのFargateというサービスを利用して、複数のサイト(複数のDB)に対して同時にバッチ処理を実行します。 今回はサンプルとして rails db:create と rails db:migrate を実行してみます。
以下は、事前に準備
今回は既存のイメージを使用します。
https://hub.docker.com/_/redmine
ただし、既存のイメージだとDB作成の処理がないので、rake db:create
の処理を追加したDocker imageを作ります。
以下のファイルを参考に起動時にENTORYPOINTで実行される docker-entorypoint.sh
にDB作成の処理を追加します。
https://github.com/docker-library/redmine/blob/53046fd6e8e9557cf2c6c73bcf76af7a5bd14e96/4.0/alpine/docker-entrypoint.sh
(rake db:migration の前に追加する)
docker-entorypoint.sh
if [ "$1" != 'rake' -a -z "$REDMINENODB_CREATE" ]; then rake db:create fi
修正した、docker-entorypoint.sh
を読み込んだDocker imageを作成します。
Dockerfile
FROM redmine:4.0.4-alpineCOPY docker-entrypoint.sh / RUN chmod +x /docker-entrypoint.sh
ビルド
docker build -t redmine-test --no-cache .
ビルドが終了したらローカルで動作確認をします。
# SQLで検証 docker run -d -p 3000:3000 redmine-test # PostgreSQL(RDS)で検証 docker run -d -p 3000:3000 -e REDMINE_DB_POSTGRES=[rds-endpoint] -e REDMINE_DB_USERNAME=postgres -e REDMINE_DB_PASSWORD=[postgres-password] redmine-test
動作確認ができましたら、以下のサイトを参考にECRへDocker imageをプッシュします。 https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/ECR_AWSCLI.html (またはdockerhubでも大丈夫です)
続いて、Docker imageを動かすためのECSのクラスターとタスク定義をAWS CloudFormationを利用して一気に作ります。 (CloudFormationの詳細説明は省きます。)
まずは、以下のyamlファイルを作成します。 Parameters のDefaultの値は適宜変更してください。
fargate-template.yml
WSTemplateFormatVersion: '2010-09-09' Metadata: 'AWS::CloudFormation::Interface': ParameterGroups: - Label: default: 'Service Size' Parameters: - DesiredCount - MaxCapacity - MinCapacity - MemorySize - CpuSize - Label: default: 'Task Enviroment' Parameters: - RedmineDatabasePostgres - RedmineDatabase - RedmineDatabaseUser - RedmineDatabasePassword Parameters: DesiredCount: Type: Number Default: 1 MaxCapacity: Type: Number Default: 1 MinCapacity: Type: Number Default: 1 MemorySize: Type: String Default: '1024' AllowedValues: [ 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216, 10240, 11264, 12288, 13312, 14336, 15360, 16384, 17408, 18432, 19456, 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720 ] CpuSize: Type: String Default: '256' AllowedValues: [256, 512, 1024, 2048, 3072, 4096] Image: Type: String Default: [ECR-path] ContainerPort: Type: String Default: '3000' ContainerName: Type: String Default: 'redmine-app' RedmineDatabasePostgres: Type: String Default: [RDS-Endpoint] RedmineDatabase: Type: String Default: [your-dbname] RedmineDatabaseUser: Type: String Default: [db-username] RedmineDatabasePassword: Type: String Default: [db-password]Resources:
# Cluster ################################################################### Cluster: Type: 'AWS::ECS::Cluster' Properties: ClusterName: !Sub 'Redmine-${AWS::StackName}'
# Role ################################################################### TaskDefinitionRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Sub 'TaskDefRole-${AWS::StackName}' Path: / AssumeRolePolicyDocument: Statement: - Action: 'sts:AssumeRole' Effect: Allow Principal: Service: - ecs-tasks.amazonaws.com ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
# Log ################################################################### LogGroup: Type: 'AWS::Logs::LogGroup' Properties: LogGroupName: !Join ['-', ['/esc/redmine', !Ref 'AWS::StackName']]
# Task ################################################################### Taskdefinition: Type: AWS::ECS::TaskDefinition Properties: Family: !Join ['', [!Ref 'AWS::StackName', -my-redmine]] NetworkMode: awsvpc RequiresCompatibilities: - FARGATE ExecutionRoleArn: !Ref TaskDefinitionRole Cpu: !Ref CpuSize Memory: !Ref MemorySize ContainerDefinitions: - Name: !Ref ContainerName Essential: 'true' Image: !Ref Image LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: ecs-redmine-app PortMappings: - ContainerPort: !Ref ContainerPort Environment: - Name: REDMINEDBPOSTGRES Value: !Ref RedmineDatabasePostgres - Name: REDMINEDBDATABASE Value: !Ref RedmineDatabase - Name: REDMINEDBUSERNAME Value: !Ref RedmineDatabaseUser - Name: REDMINEDBPASSWORD Value: !Ref RedmineDatabasePassword
コマンドラインで以下を実行します。
aws cloudformation create-stack \ --stack-name redmine-test \ --template-body file://fargate-template.yml \ --capabilities CAPABILITY_NAMED_IAM
実行後、AWSコンソールからCloudFormationの画面でスタックの作成が完了するまで待ちます。redmine-testのステータスがCREATE_COMPLETEになっていることを確認します。
CloudFormationのスタック作成が完了していることを確認します。
クラスターの作成とタスク定義が終了したら、いよいよタスクを実行してバッチ処理(DB作成とマイグレーション)を行います。 今回はローカルで実行しましたので、IAMで権限を作成して実行していますが、Lambdaで実行する実行する場合、access_key_id, secret_access_keyを設定するのではなく、ロールを設定して対応します。
また、今回は利用するDocker Imageが起動時にDBの作成とマイグレーションを実行するため、コマンドは特に指定をしません。(通常のバッチ処理ですとコマンドでバッチ処理のスクリプトを実行したりします。)
your-access-key-id, your-secret-access-key, subnet-id, security-group-id の値は適宜変更して実行してください。
fargate-migration.rb
#!/usr/local/bin/ruby require 'aws-sdk' require 'yaml' client = Aws::ECS::Client.new( access_key_id: "your-access-key-id" secret_access_key: "your-secret-access-key" ) # タスク実行時のデフォルトの設定情報を定義します。 task_prop = { cluster: "Redmine-redmine01", task_definition: "redmine01-my-redmine", launch_type: "FARGATE", overrides: { container_overrides: [ { name: "redmine-app", environment: [ { name: "REDMINE_DB_DATABASE", value: "redmine01" }, ] } ] }, network_configuration: { awsvpc_configuration: { subnets: [ "[subnet-id]" ], security_groups: ["[security-group-id]"], assign_public_ip: "DISABLED" }, }, } # 上記設定情報を利用して、Fargateを40個同時に立ち上げて、DBの作成とマイグレーションを行います。 # それぞれ、DBの情報を書き換えることによって、40個のDBを作成します。 40.times do |i| task_prop[:overrides][:container_overrides][0] = { name: "redmine-app", # ここで任意のコマンドを実行する # 今回は既存のイメージを使用するため、特にコマンドの上書きを行いませんでした。 # command: ["echo 'success'"], environment: [ { name: "REDMINE_DB_DATABASE", value: "db-name-#{i}" } ] } resp = client.run_task(task_prop) p resp end
上記必要箇所を書き換えて保存後に実行します。
ruby fargate-migration.rb
ECSのタスクを確認するとすると一気に複数のタスクが立ち上がって、DBの作成とマイグレーションが実行されます。
指定した数のタスクが起動していることを確認します。
注意点
PostgreSQLにアクセスして実際にDBが作成されているかどうか確認します。
psql -h [RDS-Endpoint] -U [USERNAME]
Before
postgres=> \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+-------------+-------------+----------------------- postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | rdsadmin | rdsadmin | UTF8 | en_US.UTF-8 | en_US.UTF-8 | rdsadmin=CTc/rdsadmin template0 | rdsadmin | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/rdsadmin + | | | | | rdsadmin=CTc/rdsadmin template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres + | | | | | postgres=CTc/postgres (4 rows)
After
postgres=> \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges ------------+----------+----------+-------------+-------------+----------------------- db-name-0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-10 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-11 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-12 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-13 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-14 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | db-name-15 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | ...(省略) postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | rdsadmin | rdsadmin | UTF8 | en_US.UTF-8 | en_US.UTF-8 | rdsadmin=CTc/rdsadmin template0 | rdsadmin | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/rdsadmin + | | | | | rdsadmin=CTc/rdsadmin template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres + | | | | | postgres=CTc/postgres
無事に40個のDBが作成され、マイグレーションも実行されてました。
Redmineが動いていることも確認します。
これまでは Lambda でrake taskを実行するの難しかったですが、Docker imageを利用することによってRailsに依存したコマンドを簡単に実行できるようになりました。
今回はDB作成とマイグレーションを実行すると言うだけの簡単な処理でしたが、例えば rake task
を利用してバッチ処理を作り、それを複数のサイトに実行した場合などに威力を発揮しそうです。
![]() |
幕張メッセで開催された「AWS Summit Tokyo」に参加。機械学習、サーバレス、コンテナに関する知識をアップデートしました。 |
![]() |
Redmineのファイル添付機能は地味ですがとても便利機能。その内側を紹介します。 |
![]() |
IT先進国台湾で開催されたオープンソースソフトウェアのイベントに参加してきました。 |
![]() |
中国の深圳大学に語学留学して、中国語を4週間学んできました。授業の様子と滞在中の生活を紹介。 |
![]() |
redmine.tokyoでRedmine4.1の新機能16個を紹介。そのほか紹介できなかった便利な新機能10個をピックアップして紹介します。 |
![]() |
社員研修に伴うサポート体制変更・休業のお知らせ(5/20〜23) 社員研修に伴い、5月20日〜23日はサポート体制の変更および休業とさせていただきます。 |
![]() |
オープンソースカンファレンス2025 Nagoyaに弊社代表の前田が登壇(ブース出展あり) オープンソースカンファレンス(OSC)2025 Nagoyaに弊社代表の前田が登壇。『Redmineの意外と知らない便利な機能(Redmine 6.0 対応版)』をテーマに発表します。 |
![]() |
エンタープライズプラン向け「優先サポート」を開始 My Redmineでは、エンタープライズプランをご契約のお客様向けにサポート対応を優先的に行う「優先サポート(プライオリティサポート)」を開始いたしました。 |
![]() |
プロジェクト管理ツール「RedMica」バージョン 3.1.0をリリース Redmine互換のオープンソースソフトウェア ファーエンドテクノロジー株式会社は、2024年11月19日(日本時間)、Redmine互換のプロジェクト管理ソフトウェア「RedMica 3.1.0」をリリースしました。 |
![]() |
Redmineの最新情報をメールでお知らせする「Redmine News」配信中 新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け |