ECS, Fargateで立てたサーバーをPrismaでRDSにつなぐ

使用技術

TS, Node(Hapi), Prisma, ECS, Fargate, RDS(MySQL)

現在既にAWS上でサーバーが起動しているものとする。

index.ts

import Hapi from "@hapi/hapi";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

const init = async () => {
  const server = Hapi.server({
    port: 3030,
  });

  server.route([
    {
      method: "GET",
      path: "/",
      handler: () => {
        console.log("run");
        return "Hello World!";
      },
    },
    {
      method: "GET",
      path: "/users",
      handler: async () => {
        console.log("users");
        const users = await prisma.user.findMany();

        return users;
      },
    },
  ]);

  await server.start();
  console.log("サーバー起動");
  console.log("Server running on %s", server.info.uri);
};

init();

schema.prisma

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
 // ローカルでエラー起きたのでここ書いてる。いらないかも
  binaryTargets = ["native", "debian-openssl-1.1.x"]
}

model User {
  id  Int @id @default(autoincrement())
  name String?
  age Int?
}

Dockerfile

FROM node:12
RUN apt-get update
RUN apt-get install -y openssl
WORKDIR /app
COPY package*.json /app
RUN npm install
COPY . /app
EXPOSE 3030
EXPOSE 5555
CMD npx ts-node src/index.ts

ローカルでは.envファイルやdocker-composeにDATABASE_URLを定義すればPrismaがそれを読み取ってくれる。だが、AWS上ではそのようなものを持たせてないので環境変数を別途AWS上で定義する。

方法はいくつかあると思うがSystems Managerを使うことにする。公式にも

Amazon ECS を使用すると、AWS Systems Manager パラメータストア パラメータに機密データを保存してコンテナの定義でそれを参照することによって、コンテナに機密データを挿入できます。

とあるのでECSとの相性は良さそう。

パラメータストアにパラメータを定義

Systems Managerという機能群の中の1つであるパラメータストアを使い実際に環境変数を定義していく。

f:id:ri99:20211012131012p:plain

↑ここから作成

ここでは sampleServerDatabaseUrl というキーで作成する

f:id:ri99:20211012135055p:plain

コンテナに環境変数を定義

作成したキーを実際にコンテナから使用する

タスク定義を修正する必要があるので対象のタスク定義から「新しいリビジョンの作成」をクリック。

現在のコンテナをクリックして、モーダルの下の方にある「環境変数」の部分で指定する。

この時、ValueFromを選択するよう気を付ける

f:id:ri99:20211012140527p:plain

使用するタスク定義の変更

最初に書いた通り、今回はすでにサーバーが動いている状態からの変更である。

先程タスク定義の新たなリビジョンを作成したが、サービスが使用しているタスク定義は依然として古いもののままである。

なのでサービスが使うタスク定義を最新のものに変更する

変更は↓から行うことができる

f:id:ri99:20211012141137p:plain

f:id:ri99:20211012141350p:plain

なお、ここで権限のエラーが出た場合

ECSからSystems Managerの値参照しようとしたらResourceInitializationErrorが出た - プログラミングとかのお雑な備忘録

を参照

Prismaマイグレーション

今のままだとmigrateできてないのでする。DockerfileのCMDで行うことにする。CMDで複数コマンド実行したい場合は自分はシェルで定義しているのでここでもそうする。

start.sh

if [ "$NODE_ENV" = "development" ]
then
npx prisma migrate dev
npx ts-node src/index.ts
elif [ "$NODE_ENV" = "production" ]
then
npx prisma migrate deploy
npx ts-node src/index.ts
fi

Prismaは環境によってマイグレーション方法が prisma migrate dev とprisma migrate deployで別れるので環境変数NODE_ENVを定義して、その値で判断させることにする。

なのでまた「新しいリビジョンの作成」-> 既存コンテナクリック -> 環境変数のとこで追加する

f:id:ri99:20211012165034p:plain

ローカルでは.envファイルやcomposeなどにdevelopmentを定義

次に上記Dockerfileの

CMD npx ts-node src/index.ts

CMD ["./start.sh"]

に変更する。

権限を与えるため

chmod 744 ./start.sh

を実行

ECRの「プッシュコマンドの表示」に従い新たなImageをプッシュする。

タスク定義から「新しいリビジョンの作成」で先程プッシュしたImageのURIに更新。

サービスで使用するリビジョンを更新したバージョンに変更。

そうしてログを見てみると

f:id:ri99:20211012175816p:plain

こんな感じのエラーが出る。

セキュリティグループを修正してこのエラーに対応する。

セキュリティグループの修正

ECSとFargateとALBでNodeサーバーを立てる - プログラミングとかのお雑な備忘録

ここでも書いたのと同じような要領で修正セキュリティグループを修正していく。

まずサービスのセキュリティグループを確認。

ここではSample-6411

次にRDSのセキュリティグループを修正する。

[Eidit inbound rules]をから修正ページに入る。

↓このルールを追加

f:id:ri99:20211012182550p:plain

この中の選択されているグループは先程確認した Sample-6411

これで修正はOK

強制的に再デプロイして確認

↓強制的に再デプロイする方法

AWSコンソールでサービスを見つけます。 右上隅にある[更新]をクリックします。 [新しい展開を強制する]チェックボックスをオンにします。 他の構成をスキップして、[サービスの更新]をクリックします。 サービスは再デプロイされます。

<ALBのDNS>/users をブラウザのURL欄に打ち込んで確認

エラーが出ずに[]が帰って来れば無事成功!

TODO

DBは多分パブリックでアクセスできる必要ないのでプライベートにする。

プライベートバージョンからちゃんと接続できるか確認

Route53を用いた独自ドメインでの構築

参考

AWS ECS(バックエンド編)|Nuxt.js + Ruby on Rails + AWS Fargate の開発・デプロイチュートリアル

AWSの公式の記事色々

ECSからSystems Managerの値参照しようとしたらResourceInitializationErrorが出た

ECS, Fargateで起動させてたサーバーで環境変数が必要だったのでSystems Managerで定義してタスク定義のコンテナ追加の部分の環境変数でValueFromでそのSystems Managerの値を参照しようとしたら ResourceInitializationError ~ が出た。

タスク実行ロールには [ecsTaskExecutionRole] を使っていた。(覚えてないけど確かデフォルトで作成されてたやつ)

他のAWSのサービスに対してこのタスクから色々やる場合はここで権限を与えないといけない

エラーの原因

Systems Managerに対する権限がない

解決法

タスク実行ロールのecsTaskExecutionRoleにSystems Managerの値を参照できるように権限を与える。

まずポリシーの作成で

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters"
            ],
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:<アカウントID>:parameter/<Systems Managerに定義したパラメータ>"
            ]
        }
    ]
}

を定義して作成

これで指定したリソースに指定したアクションが許可される。

次にecsTaskExecutionRoleにこのポリシーをアタッチ

これで権限が与えらた

参照

AWS IAMポリシーを理解する | DevelopersIO

Systems Manager パラメータストアを使用した機密データの指定 - Amazon Elastic Container Service

ECSとFargateとALBでNodeサーバーを立てる

Hapi製APIサーバーをDockerコンテナ化したのでAWS上で動かしたかったのでECS, Fargateを使いデプロイすることにした。その手順を書いていく。

index.js

const Hapi = require("@hapi/hapi");

const init = async () => {
  const server = Hapi.server({
    port: 3030,
  });

  server.route({
    method: "GET",
    path: "/",
    handler: (req, h) => {
      console.log("run");
      return "Hello World!";
    },
  });

  await server.start();
  console.log("サーバー起動");
  console.log("Server running on %s", server.info.uri);
};

init();

Dockerfile

FROM node:12
RUN apt-get update
WORKDIR /app
COPY package*.json /app
RUN npm install
COPY . /app
EXPOSE 3030
CMD node index.js

1. リポジトリの作成

ここからリポジトリを作成。

リポジトリ名を入力し、「リポジトリを作成」をクリック

2. Imageのプッシュ

f:id:ri99:20211009182216p:plain

ここの「プッシュコマンドを表示」をクリックして手順を表示

ローカルで全て手順通り打ち込む

3. クラスタの作成

「ネットワーキングのみ」を選択して「次のステップ」

名前などつける

この時、VPCの作成はしない(多分しても平気)。理由はここで色々設定しなくちゃならなくなるから。

右下の「作成」

f:id:ri99:20211010160708p:plain

4. タスク定義

サイドバーの「タスク定義」を開き、「新しいタスク定義の作成」をクリック

「Fargate」を選択し「次のステップ」をクリック

名前やメモリ割り当てなど色々入力していく

「タスクロール」はそのタスクから他のAWSのサービスを使いたい時には必要。ここでは空欄にする

f:id:ri99:20211010162017p:plain

「コンテナの追加」でリポジトリからイメージを選択する

リポジトリのイメージのURLは↓ここからとる

f:id:ri99:20211010162422p:plain

コンテナの追加の設定を行なっている時に「メモリ制限」という欄があるが、ここでサンプルだからって適当に1とか入れるとメモリ足りなくて起動できない可能性があるのである程度の数値入れる

「ポートマッピング」という欄があるが、ここではDockerfileでEXPOSEしてるポートを入れる。今回は3030

右下の「追加」をクリック

f:id:ri99:20211010163207p:plain

コンテナを追加できたら右下の「作成」をクリックして作成する

5. ロードバランサの作成

検索タブでec2を検索しそのページを開き、サイドバーの「ロードバランサー」を選択し、「ロードバランサーの作成」をクリック

ロードバランサーのタイプを選択する画面になるので、ALBを選択

f:id:ri99:20211010164103p:plain

Basic configuration

名前などの設定をしていく

f:id:ri99:20211010165051p:plain

Network mapping

ネットワークの設定をする。AZは最低2つ選択しなければいけない。

f:id:ri99:20211010165236p:plain

Security groups

セキュリティグループの設定

[Create new security group] を選択し新規のものを作成。既存のものでもいいが新規の方が何やってるかわかりやすいのでここでは新規作成する。

インバウンドルールに「カスタムTCP, ポート範囲80, ソースAnywhere 0.0.0.0/0」を追加

これにより、全てのipからのポート80へのリクエストが許可される。

f:id:ri99:20211010170322p:plain

新規作成が完了したらALBの設定画面で選択する。defaultなどが指定されてる場合は削除

f:id:ri99:20211010170629p:plain

Listeners and routing

リッスンするポートやルーティングの設定をする。

それぞれのリクエストに対してどこにルーティングするかを決めるために「ターゲットグループ」を指定する

ここではcreate target groupで新規作成する

ターゲットタイプはIPを選択して、Portにはフォワーディングしたいポートを指定。今回は3030

f:id:ri99:20211010171821p:plain

Nextを押したらRegister targetsというページが出てくる。ここではまだ対象のFargateは作成されていないので特に何もしない。

右下の[create target group]をクリックし作成。

作成されたらロードバランサ作成の画面に戻り、今作ったターゲットグループを選択する。

f:id:ri99:20211010172413p:plain

もしhttpsにしたい場合は[add listner]で追加する。

ここまでできたら右下の[Create load balancer]で作成

作成できたらロードバランサーのページで状態を見てみる。ここが「プロビジョニング」だと動かないので注意

f:id:ri99:20211010172851p:plain

6. サービスの作成

クラスターの詳細から「サービス」タブを選択。青いボタンの「作成」をクリック。

f:id:ri99:20211010173410p:plain

名前だとタイプだのを入力していく

「タスクの数」という項目があるがこれはなるべく少ない数の方がいい。後々スケールできるので。逆にここを50とかにすると常に50起動してしまうことになりコストがかかる。

f:id:ri99:20211010173846p:plain

入力が終わったら右下の「次のステップ」

ネットワーク構成の画面が出てくるのでネットワークを設定してく

ここではvpcはデフォルトのもの。

サブネットはALBでAZとして指定したものを使う。(ただこれはもしかしたら間違ってるかもしれない)

f:id:ri99:20211010174623p:plain

下に行くと「ロードバランサーの種類」があるのでALBを選択

ロードバランサー名」には先程作成したものを選択

f:id:ri99:20211010174934p:plain

「ロードバランス用のコンテナ」という欄の中に「ロードバランサーに追加」というボタンがあるので追加

追加すると新たなコンテンツが現れる。その中の「プロダクションリスナーポート」を80:HTTPに指定

その少し下にある「ターゲットグループ名」で先程作成したターゲットグループを選択

f:id:ri99:20211010175620p:plain

右下の次のステップを押す。Auto Scalingを設定するページが出てくるがここは今は何もしない。次のステップ。

確認ページが出てくるので確認したら「サービスの作成」で作成する。

7. セキュリティグループの修正

今のままでALBのDNSにアクセスすると503エラーが返ってくる。

クラスタに割り当てたセキュリティグループを確認

f:id:ri99:20211010181701p:plain

セキュリティグループの画面で該当のものを選択し、[Edit inbound rules]をクリック

f:id:ri99:20211010181846p:plain

既存のルールを一旦削除する。

追加ボタンを押し↓のように設定する

f:id:ri99:20211010182438p:plain

セキュリティグループではALBで指定したものを選択。保存する。

8. 確認

該当のALBのDNSをコピーしてブラウザのURL欄に打ち込んで確認

参照

https://www.youtube.com/watch?v=o7s-eigrMAI

Hapiの公式チュートリアルに載ってるコードだとDockerでうまく動かない

https://hapi.dev/tutorials?lang=en_US

↑これに載ってる

'use strict';

const Hapi = require('@hapi/hapi');

const init = async () => {

    const server = Hapi.server({
        port: 3000,
        host: 'localhost'
    });

    await server.start();
    console.log('Server running on %s', server.info.uri);
};

process.on('unhandledRejection', (err) => {

    console.log(err);
    process.exit(1);
});

init();

このコードだとDockerで起動はできるがリクエストをちゃん受け付けなかった。

host: 'localhost'の部分を消すとうまく動く。

const server = Hapi.server({
        port: 3000,
        // host: 'localhost'
    });

react-native-background-geolocationについてのあれこれをまとめてく(随時更新予定)

https://github.com/transistorsoft/react-native-background-geolocation

を使っているのだが、機能が結構豊富だったり使い方が少し複雑に感じたので調べたこととか気づいたことをまとめてく。

既にある程度使っているのでセットアップから順にとかではない。メモりたい事をその都度書いていく。

検証は全て個人で行なったものなので保証はできません。

State.enabledはユーザー許可のことではない

これはラッキングが有効になっているかどうか

位置情報使用の許可のことだと思っていたので少し戸惑った。

falseの場合はトラッキングが行われない。つまり移動してもアクションは起こらない。

true when the SDK has been enabled via methods start or startGeofences. When false, the SDK is completely OFF. No tracking of any kind will occur. The SDK will consume no energy.

一度trueにしたらアプリをリロードしてもtrue。そして端末から位置情報を無効にしてもtrue

なので基本的にアプリで一回trueにすればいい。

BackgroundGeolocation.start()

でtrueにできる。

iOS, Firebase周りでのアプリ開発で調べた用語(随時更新予定)

Bundle ID

アプリを一意に識別するためのもの。Appleのエコシステム。
Xcode -> General -> Bundle Identifier で確認可能。
Firebaseプロジェクト内でiOSアプリを作成するときに必要。あとで変更できないので注意。

https://firebase.google.com/docs/cloud-messaging/ios/client?hl=ja


GoogleService-Info.plist

プロジェクト用の機密ではない一意の識別子が含まれているファイル。
いつでも再ダウンロード可能。
GoogleService-Info (1).plist のように(1)とか入ってたらだめ。
Xcode プロジェクトのルートに移動させて使う。


APNs

Apple Push Notificationサービスのコア。
デベロッパが通知機能使うにはこのサービスを使うことが必要。
使用にはApple Developer Programに加入することが必要。
FCMを使っている場合でもAPNsを介してプッシュ通知が行われている。
認証されなければ使えないが、現在認証方法は2つある。

APNs証明書

APNsの認証方法の1つ。
証明書を作って認証する。

APNs認証キー

APNsの認証方法の1つ。
こっちが推奨されている。
キーを発行して認証する。
同じキーの発行は1度だけ。

https://qiita.com/MYamate_jp/items/994a31d5408bc09998bc

発行はこれがわかりやすい