小〜中規模程度の Flutter プロジェクトにおけるフォルダ構成について
フォルダ構成について
プロジェクトの初期やグロースするタイミングでは結構大きめの変更なども多いので、「極力シンプルに、かつ最低限のルールは守られるように。」という観点で、以下のようなフォルダ構成で開発を始めます。
lib
- ui
- component/
- page_name/
- hoge_page.dart(ページそのもの)
- hoge_view_model.dart(ファイル名の通り。providerのChangeNotifierを継承したクラス)
- fuga.dart(hoge_page.dartで使用するviewなど)
- model/
- utils/
- constants/
もっときっちりやるのであれば
開発の人数が増えてきたり、プロダクトとして安定してきたタイミングなどで、よりきっちりやるのであれば、wasabeef/flutter-architecture-blueprints を参考にしてやろうと思っています(まだ私のプロダクトは検証が多く、頻繁に大きめの変更が入ったりしているのでいつになるのかはわかりませんが....)。
参考
Nuxt.jsで作成したSPAをS3 + CloudFrontにデプロイする
環境
- Node.js 12.18.0
- Nuxt.js 2.14.5
手順
基本的にはNuxt.jsの公式ドキュメント通り進めてください。 しかし、そのままだとエラーが発生してしまいます。以下でその解消方法について説明します。
ポリシーの変更
AWS上で作成するポリシーを以下のように変更してください。
example.comのところはS3のバケット名に変更してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::example.com"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:DeleteObject",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload"
],
"Resource": ["arn:aws:s3:::example.com/*"]
},
{
"Effect": "Allow",
"Action": [
"cloudfront:CreateInvalidation",
"cloudfront:GetInvalidation",
"cloudfront:ListInvalidations"
],
"Resource": "*"
}
]
}
gulpfile.jsの変更
gulpfile.jsのheadersの部分を以下のように変更してください。
headers: {
'x-amz-acl': 'private',
/* 'Cache-Control': 'max-age=315360000, no-transform, public', */
},
以上の変更を加えることで、適切にデプロイができるようになります。
直接アクセスするとエラーが発生する
Nuxt.jsで作ったSPAをS3 + CloudFrontの環境にデプロイすると、トップ以外に直接アクセスをした場合にエラーが発生してしまいます。 それを回避する方法としては、CloudFrontのカスタムエラーレスポンスの設定を行います。
CloudFrontのErrorPagesのタブにて以下の情報でカスタムエラーレスポンスを作成してください。
- HTTP Error Code -> 403
- Error Caching Minimum TTL -> 0
- Response Page Path -> /
- HTTP Response Code -> 200
参考
S3 + Lambda + Transcoderを使用したサーバーレスな動画変換
AWSのS3, Lambda, Transcoderを使用したサーバーレスな動画変換の仕組みの作り方を紹介します。 処理の流れとしては以下のようになります。
- S3のBucketに動画ファイルをアップロード
- S3に設定したイベントからLambda関数を実行する
- Lambda関数内でTranscoderが実行され、動画をMP4形式に変換およびサムネイル画像の作成
- 変換した動画および作成されたサムネイル画像を別のS3のBucketにアップロード
Transcoderの設定
基本情報
- Pipeline Name
- XXXXXVideoTranscoding
- Input Bucket
- 元ファイルをアップする用のS3 Bucketを選択
- IAM Role
- Create console default roleを選択
- すでにElastic_Transcoder_Default_Roleがあればそれを選択
- Create console default roleを選択
Configuration for Amazon S3 Bucket for Transcoded Files and Playlists
Configuration for Amazon S3 Bucket for Thumbnails
Lambdaの設定
関数の作成を押して、「一から作成」を選択してください。
ポリシーおよびロールの作成
Lambda関数を作成する際に、Roleを指定する必要があります。事前に必要なポリシーおよびロールを作成しておきます。
ポリシーの作成
以下のJSONを入力して「LambdaElasticTranscoder」という名前でポリシーを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"elastictranscoder:Read*",
"elastictranscoder:List*",
"elastictranscoder:*Job",
"elastictranscoder:*Preset"
],
"Resource": [
"arn:aws:elastictranscoder:*:*:*"
]
}
]
}
ロールの作成
先ほど作成したLambdaElasticTranscoderのポリシーを選択し、「lambda_elastictranscoder_exec_role」という名前のロールを作成します。
基本的な情報
- 関数名
- XXXXXVideoTranscoding
- ランタイム
- Node.js 12.x
- 実行ロール
- 既存のロールより「lambda_elastictranscoder_exec_role」を選択
これらを入力して、「関数の作成」をクリックしてください。そうすると関数が作成されます。
コード
実行するコードは以下を参考にしてください。以下を実行すると、指定したS3 Bucketに「MP4形式に変換された動画」と「サムネイル画像」がアップされます。
'use strict';
var AWS = require('aws-sdk');
var s3 = new AWS.S3({
apiVersion: '2012-09-25'
});
var eltr = new AWS.ElasticTranscoder({
apiVersion: '2012-09-25',
region: 'ap-northeast-1'
});
exports.handler = function(event, context) {
console.log('Executing Elastic Transcoder Orchestrator');
var bucket = event.Records[0].s3.bucket.name;
var key = event.Records[0].s3.object.key;
var pipelineId = '<PipelineのIDを入力してください>';
if (bucket !== '<元のファイルをアップするS3のBucket名を入力してください>') {
context.fail('Incorrect Video Input Bucket');
return;
}
var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); //the object may have spaces
var newKey = key.split('.')[0];
var params = {
PipelineId: pipelineId,
Input: {
Key: srcKey,
FrameRate: 'auto',
Resolution: 'auto',
AspectRatio: 'auto',
Interlaced: 'auto',
Container: 'auto'
},
Outputs: [{
Key: newKey + '.mp4',
ThumbnailPattern: newKey + '-{count}',
PresetId: '1351620000001-000010',
Watermarks: [{
InputKey: 'watermarks/logo.png',
PresetWatermarkId: 'BottomRight'
}],
}]
};
console.log('Starting Job');
eltr.createJob(params, function(err, data){
if (err){
console.log(err);
} else {
console.log(data);
}
context.succeed('Job well done');
});
};
S3の設定
- 元ファイル
- 変換したファイル
を保存するために、2つのBucketを作成します。作成時に特殊な設定は必要ありません。
ウォーターマーク用の画像
動画を変換する際に、動画自体にウォーターマークを入れることができます。
ウォーターマークに使用したい画像を、元ファイルのBucket内にwatermarks/logo.pngという名前でアップをしてください。
イベントの追加
元ファイルをアップするS3のBucketのプロパティからイベントの設定を行います。
- 名前
- VideoTranscoding
- イベント
- 「すべてのオブジェクト作成イベント」を選択
- 送信先
- 「Lambda関数」を選択
- Lambda
- 先ほど作成したLambda関数名を選択
以上の設定を終えて、S3のBucketに動画ファイルをアップロードすると処理が実行され、変換された動画およびサムネイル画像が作成されるのが確認できるようになります。
参考
RailsをAWS Fargateにデプロイする (AWS CLI / ECS CLIを使用)
AWS CLIとECS CLIを用いてRailsをFargateにデプロイする方法について書きます。 イメージのビルドや自動デプロイの仕組みはまた別で書こうと思っています。今回は、MacOSで実行することを想定しています。
目次
- CLIツールのインストール
- IAMアカウントおよびロールの作成
- アカウント設定 / ESC設定
- ECRの作成 & Push
- クラスタの作成
- デプロイについて
- スケールについて
- DB Migration等のコマンドについて
CLIツールのインストール
ターミナルでAWSを操作できるように、CLIツールをインストールします。今回はbrewを使ってインストールします。 他の方法は以下を参照してください。
# インストール $ brew install awscli # 確認 $ aws --version # インストール $ brew install amazon-ecs-cli # 確認 $ ecs-cli --version
IAMアカウントおよびロールの作成
ターミナルでAWSを操作するためのアカウントを作成します。 こちらは、画面上で行います。IAMのページにアクセスして行ってください。
グループの作成
初めにポリシー(アカウントがどの操作を行えるかを制限するもの)を紐づけたグループを作成します。
今回の要件で必要となるポリシーは以下です。
- AmazonEC2ContainerRegistryFullAccess
- AmazonEC2ContainerServiceFullAccess
- AmazonECSTaskExecutionRolePolicy
- CloudWatchLogsFullAccess
ユーザーの作成
続いてIAMユーザーを作成します。先ほど作ったグループを紐づけてユーザーアカウントの作成を完了させてください。完了したら、以下を必ず控えておいてください。
- アクセスキーID
- シークレットアクセスキー
ロールの作成
最後にロールの作成を行います。Fargateでは、ecsTaskExecutionRoleが必要となります。以下を参考にして、作成を行ってください。
アカウント設定 / ESC設定
ターミナル上で操作するための情報が出揃ったので、続いてAWSのアカウントの設定およびESCを操作するための設定を行います。
AWS CLIの設定
先ほど作成したIAMの認証情報を設定します。PROFILE_NAMEのところには、サービス等の名前を入れておくと管理しやすいと思います。
$ aws configure --profile PROFILE_NAME AWS Access Key ID [None]: ACCESS_KEY_ID AWS Secret Access Key [None]: SECRET_ACCESS_KEY Default region name [None]: ap-northeast-1 Default output format [None]: json
これを実行すると、~/.aws以下にconfigとcredentialの2つのファイルが作成されます。
※aws configureだけを実行して設定をするパターンがありますが、それだと複数のアカウントで運用する場合に切り替えが面倒になるので、私はprofile名を設定して運用しています。
ECS CLIの設定
AWS CLIと同様に、ECS CLIの設定も行います。CLUSTER_NAMEとCONFIG_NAMEには、PROFILE_NAMEと同様にサービス等の名前を入れておくと管理しやすいと思います。
※もし本番環境とテスト環境を分ける場合などはそれも含めておくと管理しやすくなります。
# 認証情報の設定 $ ecs-cli configure profile --profile-name PROFILE_NAME --access-key ACCESS_KEY_ID --secret-key SECRET_ACCESS_KEY # Fargateに関する設定 $ ecs-cli configure --cluster CLUSTER_NAME --default-launch-type FARGATE --region ap-northeast-1 --config-name CONFIG_NAME
ECRの作成 & Push
ビルドしたDockerイメージはECRにPushするようにします。 ECRでリポジトリを作成し、URLを控えてください。
docker-compose.ymlの作成
最新のECS CLIであれば、Docker ComposeのVersion3の記述を使うことができます。
以下のように作成することができます。
version: '3'
services:
app:
build:
context: ./
dockerfile: Dockerfile
image: ECR_REPOSITORY_URL
ports:
- "3000:3000"
environment:
RAILS_ENV: production
logging:
driver: awslogs
options:
awslogs-group: SERVICE_NAME
awslogs-region: ap-northeast-1
awslogs-stream-prefix: PREFIX
この場合、ECS上ではbuildは無視されます。ローカルでのbuildのために定義しています。
イメージの作成 ~ ECRへのPush
以下のコマンドを実行してECRへのPushを行うことができます。
# イメージビルド $ docker-compose -f docker-compose.yml build # AWS ECRへのログイン $ $(aws ecr get-login --no-include-email --region ap-northeast-1 --profile PROFILE_NAME) # ECRへのPush $ docker push ECR_REPOSITORY_URL:latest
クラスタの作成
VPC / Subnet / Security Groupの作成
クラスタを作成するにあたり、VPC、Subnet、Security Groupを事前に作成しておく必要があります。 クラスタを作成する際に指定をしなければ自動で作成されるのですが、自分で作成しておいた方が管理しやすいので、私は事前に作っておくようにしています。
VPCとSubnetについては、AWSネットワークルールを参考にして作ってみてください。 Fargateで配置するコンテナはPublicなSubnet(インターネットゲートウェイでインターネットとつながっている状態)に配置する必要があるので、そこだけ注意してください。
コマンド実行
クラスタを作る際には、ecs-cli upコマンドを使用します。PROFILE_NAMEとCONFIG_NAMEに関しては、「アカウント設定 / ESC設定」で指定したものと同じにしてください。
$ ecs-cli up \
--security-group SECURITY_GROUP_ID \
--vpc VPC_ID \
--subnets SUBNET_ID_1,SUBNET_ID_2 \
--aws-profile PROFILE_NAME \
--ecs-profile PROFILE_NAME \
--cluster-config CONFIG_NAME
デプロイについて
ロードバランサーとターゲットグループの作成
サービスを起動する際に、ロードバランサーまたはターゲットグループを指定する必要があるので、事前に作成しておきます。作成時の注意としては、ターゲットグループを作成する際に、「ターゲットの種類」の項目で「IP」を指定するようにしてください。
ecs-params.ymlの作成
デプロイをする際に、どのVPC, Subnetに配置するか等を指定する必要があります。それらを指定するためにecs-params.ymlというファイルを作成します。
subnet IDとsecurity Group IDを自分で作成したものに置き換えてください。
version: 1
task_definition:
task_execution_role: ecsTaskExecutionRole
ecs_network_mode: awsvpc
task_size:
mem_limit: 1.0GB
cpu_limit: 512
run_params:
network_configuration:
awsvpc_configuration:
subnets:
- "subnet ID 1"
- "subnet ID 2"
security_groups:
- "security group ID"
assign_public_ip: ENABLED
デプロイコマンド
デプロイをするために、ecs-cli compose service upコマンドを使用します。
$ ecs-cli compose \
--project-name PROJECT_NAME \
--file docker-compose.yml \
--ecs-params ecs-params.yml \
service up \
--target-group-arn TARGET_GROUP_ARN \
--container-name app \
--container-port 3000 \
--cluster CLUSTER_NAME \
--aws-profile PROFILE_NAME \
--ecs-profile PROFILE_NAME \
--cluster-config CONFIG_NAME \
--create-log-groups \
--force-deployment
これでクラスター内にサービスと1つのタスクが起動するようになります。 もしサービスがすでに作成されていた場合は、タスクのみが新規で作成され、立ち上がり次第古いものと入れ替わります。
スケールについて
先ほどのコマンドでデプロイを行なった場合に、タスクの数は1となります。ここからタスクの数をさらに増やしたいという場合は、スケールさせるためのコマンドを実行する必要があります。
$ ecs-cli compose \
--project-name PROJECT_NAME \
service scale 2 \
--cluster CLUSTER_NAME \
--aws-profile PROFILE_NAME \
--ecs-profile PROFILE_NAME \
--cluster-config CONFIG_NAME
こちらを実行するとタスクが2つに増え、自動で不足している分のタスクが起動します。
DB Migration等のコマンドについて
Docker Composeファイルの作成
Dockerは1コンテナ1プロセスが原則ですので、この状態だとrake db:migrate等のコマンドを実行することができません。なので、そういったタスクを実行するためのDocker Composeファイルを作成します。
例として、rake db:migrateを実行するためのDocker Composeファイル(db-migration.yml)を作成します。
app用のRailsイメージのコマンドを上書きすることで同じものを利用することができます。
version: '3'
services:
app:
image: ECR_REPOSITORY_URL
command: ["bundle", "exec", "rails", "db:migrate"]
environment:
RAILS_ENV: production
logging:
driver: awslogs
options:
awslogs-group: SERVICE_NAME
awslogs-region: ap-northeast-1
awslogs-stream-prefix: PREFIX
コマンド
先ほど作成したdb-migration.ymlを指定して、タスクを実行します。ここで、PROJECT_NAMEの部分は通常のappとは別の名前にしてください。もし同様の名前で実行してしまった場合に、app用のタスク定義が変わってしまい、アプリケーションが起動しなくなってしまいます。
$ ecs-cli compose \
--project-name PROJECT_NAME \
--file db-migration.yml \
--ecs-params ecs-params.yml \
up \
--cluster CLUSTER_NAME \
--aws-profile PROFILE_NAME \
--ecs-profile PROFILE_NAME \
--cluster-config CONFIG_NAME
参考
AWS ネットワークの設定ルール
VPC
AWS の VPC アドレスでよく使用されるのは以下の範囲となります。
- 10.0.0.0 ~ 10.255.255.255 (10.0.0.0/8)
- 172.16.0.0 ~ 172.31.255.255 (172.16.0.0/12)
- 192.168.0.0 ~ 192.168.255.255 (192.168.0.0/16)
今回は VPC アドレスを172.16.0.0/16に設定して解説をしてきます。
Subnet
Subnet に関しては、奇数を Private(ネットワーク内部との通信のみ)、隅数を Public(ネットワーク外部との通信あり) にするなどのルールを設定しておくとわかりやすくなります。今回は Private な Subnet を奇数に、Public な Subnet を隅数にします。 数字以外にも AWS のネットワークには Availability Zone が存在するので、a と c の2つをそれぞれ作成します。
例として以下の Subnet を作成します。
- {サービス名}-private-a-{環境(prod や dev など)}
- 172.16.1.0/24
- {サービス名}-private-c-{環境(prod や dev など)}
- 172.16.3.0/24
- {サービス名}-public-a-{環境(prod や dev など)}
- 172.16.2.0/24
- {サービス名}-public-c-{環境(prod や dev など)}
- 172.16.4.0/24
Internet Gateway
インターネットゲートウェイは、xxxxxx
{サービス名}-{環境(prodやdevなど)}の名前で新たに Internet Gateway を作成してください。
その後、作成したインターネットゲートウェイを使用したい VPC にアタッチしてください。
Route Table
ルートテーブルは、サブネット内の通信のルールを設定するものです。
- {サービス名}-public
- {サービス名}-private
今回は Public と Private の 2 つを作成します。
Subnet の紐付け
作成したルートテーブルに、利用したいサブネットを紐付けます。 今回は、外部との通信を行うものを Public、そうでないものを Private に割り振ってください。
Internet Gateway の紐付け
外部に公開するルートテーブルにインターネットゲートウェイを紐付けてください。 ルートの編集より、以下の情報にて新しいルートを作成してください。