AWS Amplifyを使用した爆速バックエンド構築[認証/Auth編]
概要
この記事は以下の連続した記事の2本目です。 細かい説明や背景などは以前の記事を参照してください。
この記事では以前作成したReactアプリケーションに認証機能を追加してきます。
認証機能について
Amplifyでは認証機能をCognitoを使用して提供しています。
詳細はCognitoの公式を調べていただきたいですがCognitoでは様々な機能が搭載されています。 今回はその中でもAmplifyの用意したデフォルトの最低限の認証機能を使用していきます。
- IAMによる認可機能
- MFA機能OFF
- パスワードポリシー
- 最低8文字
- 必須文字種なし
- 登録に必要なパラメータ
- Eメールアドレス
- ユーザ名でログイン
- 登録時にメールアドレスの確認
認証機能構築
$ amplify add auth Using service: Cognito, provided by: awscloudformation The current configured provider is Amazon Cognito. Do you want to use the default authentication and security configuration? Default configuration Warning: you will not be able to edit these selections. How do you want users to be able to sign in? Username Do you want to configure advanced settings? Yes, I want to make some additional changes. Warning: you will not be able to edit these selections. What attributes are required for signing up? Email Do you want to enable any of the following capabilities? Successfully added auth resource reacttutorial1b2d47db locally Some next steps: "amplify push" will build all your local backend resources and provision it in the cloud "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud $ amplify push ✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev ┌──────────┬───────────────────────┬───────────┬───────────────────┐ │ Category │ Resource name │ Operation │ Provider plugin │ ├──────────┼───────────────────────┼───────────┼───────────────────┤ │ Auth │ reacttutorial1b2d47db │ Create │ awscloudformation │ └──────────┴───────────────────────┴───────────┴───────────────────┘ ? Are you sure you want to continue? Yes ⠇ Updating resources in the cloud. This may take a few minutes... ~~ 略 ~~ ✔ All resources are updated in the cloud
上記手順を実行するとCognitoコンソールのユーザプールからユーザプールが作成されていることが確認できます。
細かいパラメータの変更が必要な場合は一つ目の対話で Default configuration
以外を選択することで可能になります。
フロントエンド実装
以下の機能を実装しました。
- 未ログインの場合、ログイン画面を表示
- 必須項目の入力画面を備えたサインアップ画面
- ログイン後にゲーム画面の表示
- ゲーム画面にサインアウトボタン
- ゲーム画面にユーザ情報表示
各種画面は @aws-amplify/ui-react
の物を使用しています。
最終的なコードはここ (GitHubリポジトリ)を参照してください。
公式サイトを参照して実装しています。
動作確認
構築した資材をmasterブランチにマージし、AmplifyコンソールからCI/CDが修了したことを確認して動作確認を行います。
フロントにアクセスし、ログイン画面を確認する。
新規登録へのリンクをクリックし、サインアップ画面を確認。
サインアップし、メールアドレス宛に認証コードが届くことを確認。
Game画面が表示され、自身のユーザ名が表示されていることを確認。
ログアウト/ログインができることを確認。
Cognitoにユーザが追加されていることを確認。
最後に
無事に、認証機能をReactチュートリアルに追加することができました!
ただ、Amplifyは簡単に構築できたのですが、Reactはかなり苦戦してしまいました。。。
次回はAPIを使用したGameの進行状況の保存を試してみたいと思います。
AWS Amplifyを使用した爆速バックエンド構築[ホスティング/Hosting編]
背景
AWS Amplifyというサービスに業務で触れる機会があったのですが、こんな便利なサービスが存在したのかと衝撃を受けてしまいました。。。 これまでサーバを立ててサービスをインストールして最新を保つように運用をしてきたのですが、それらをマネージドで提供してくれる。しかも環境面の考慮まで、、、今後何らかのサービス開発をする際は積極的に使っていこうと思いましたのでここにメモを残したいと思います。
前提
- webアプリケーションを構築する前提で話を進めますがバックエンドはそのままネイティブアプリにも流用/併用可能です。
- webアプリケーションはnodejs + reactで開発します。
- amplify cli のインストールは公式手順をご参照ください。
- AWSアカウントにおいてamplify関連のIAMポリシーを所持しているものとします。
- webフロントについては詳しくない為、実装の解説は最低限とします。
環境
$ cat /etc/os-release NAME="Ubuntu" VERSION="20.04.2 LTS (Focal Fossa)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 20.04.2 LTS" VERSION_ID="20.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=focal UBUNTU_CODENAME=focal $ npm --version 6.14.15 $ node --version v14.17.6 $ amplify --version 5.4.0
概要
react tutorialで作成したwebアプリケーションをクラウド上にamplifyを使用してホスティングする。
web application作成
- react tutorialをもとに構築していく。
- 最終的にはここ(GitHubリポジトリ)の形式となります。
- 構築したアプリケーションはAmplifyからアクセス可能なリモートリポジトリにアップしてください。
Amplify CLIの準備
$ amplify configure
- 上記コマンドでは
~/.aws/config
と~/.aws/credentials
の設定を行っている。つまり、すでにAWS CLIなどで認証情報を設定している場合は実行する必要はない。 - ただし、
~/.aws/config
のProfile Nameに紐づくリージョンで作成されるため、実行権限を持つアカウントかつAmplifyのリージョンがデフォルトリージョンと同一であるプロフィールを作成しておく必要がある。
Amplify Applicationの作成
$ pwd /home/ec2-user/react-tutorial $ amplify init Note: It is recommended to run this command from the root of your app directory ? Enter a name for the project reacttutorial The following configuration will be applied: Project information | Name: reacttutorial | Environment: dev | Default editor: Visual Studio Code | App type: javascript | Javascript framework: react | Source Directory Path: src | Distribution Directory Path: build | Build Command: npm run-script build | Start Command: npm run-script start ? Initialize the project with the above configuration? Yes Using default provider awscloudformation ? Select the authentication method you want to use: AWS profile For more information on AWS Profiles, see: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html ? Please choose the profile you want to use default ~~ 略 ~~ ✔ Initialized provider successfully. Initialized your environment successfully. Your project has been successfully initialized and connected to the cloud! Some next steps: "amplify status" will show you what you've added already and if it's locally configured or deployed "amplify add <category>" will allow you to add features like user login or a backend API "amplify push" will build all your local backend resources and provision it in the cloud "amplify console" to open the Amplify Console and view your project status "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud Pro tip: Try "amplify add api" to create a backend API and then "amplify publish" to deploy everything
主にプロジェクトやローカル開発環境、AWSアクセス情報について聞かれるので対話式で入力する。
上記のログが出力されるとAmplifyアプリケーションの作成が完了する。
Amplifyにより提供されるHosting機能について
現状Amplifyでは主に以下の2種類のHosting機能の作成が可能
- Hosting with Amplify Console
- Amazon CloudFront and S3
それぞれにメリット/デメリットある。
Amplify Console
CloudFront + S3
- メリット
- CloudFrontやS3に対し細かな設定をすることができる
- デメリット
- CI/CDが必要な場合自身でその機能を用意する必要がある
- メリット
柔軟なカスタマイズが必要な場合やAmplifyデフォルトの性能で満足できない場合はCloudFront + S3
を選択する価値があると思われる。主にAWS WAFをアタッチする必要性がある場合などが考えられる。
今回はHosting with Amplify Console
を選択する。
Hosting機能の実装
以降はAmplifyコンソールでの操作となる。 以下の前提で手順を説明する。 - アプリケーションコードはGitHubで管理されている。
- Amplifyコンソールから作成したアプリケーションを選択。
Frontend environments
タブを開き、GitHubを選択し、Connect branch
をクリック。- GitHub認証を行い、アプリケーションを管理しているリポジトリをとブランチをドロップダウンリストから選択し
次へ
をクリック。 - 以下のパラメータを入力し、
次へ
をクリック - App name: デフォルト値
- Environment: 前の手順で作成したバックエンド環境名
- Select an existing service role ~~:
amplifyconsole-backend-role
保存してデプロイ
をクリック
Amplifyコンソール画面に戻るとmasterフロントがプロビジョン > ビルド > デプロイ > 検証 の順にステータスが進んでいくのが見えるのですべて終了後に以下の画像の箇所をクリックすることでデプロイされたアプリケーションを閲覧できる。
実際にWebフロントへアクセスしてみるとReactアプリが動いていることが分かる。
最後に
amplifyの資材(バックエンド資材)とフロントエンド資材は分けて管理することもできますが今回は同じリポジトリで管理していく方針で進めました。
Reactアプリケーションさえ準備できていれば、Amplifyアプリケーションの作成からデプロイまで30分もかからずに実現できました。
次回はここに認証機能を追加していきたいと思います。
TerraformでSecretsManagerに秘匿情報を登録する。
はじめに
在宅が始まってからやる気が出ない&職場も変わり忙しいかったと言い訳ばかりで久しぶりの投稿です。
本題ですが、最近AWS上にKubernetesをTerraformで構築するプロジェクトに参加してました。その中で秘匿情報を構築資材内から排除しようという流れになり、AWS Secrets Managerを使用することになりました。流れとしてTerraformから登録することになり調べて実装したので書き残しておきたいと思います。
環境
$ terraform --version Terraform v1.0.1 on linux_amd64 + provider registry.terraform.io/hashicorp/aws v3.48.0
資材
variable "my_secrets" { default = { key1 = "secret_value1" key2 = "secret_value2" } } resource "aws_secretsmanager_secret" "bunsen" { name = "bunsen_blog_secrets" } resource "aws_secretsmanager_secret_version" "bunsen" { secret_id = aws_secretsmanager_secret.bunsen.id secret_string = jsonencode(var.my_secrets) }
まずは上記資材を作成。
リソースはaws_secretsmanager_secretとaws_secretsmanager_secret_versionを使用する。
実行
$ terraform apply ~~~~~~ Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
以下のように確認。
うまくSecretが作成されていることを確認。
秘匿情報外だし
variable "secret_value1" {} variable "secret_value2" {} locals { my_secrets = { key1 = var.secret_value1 key2 = var.secret_value2 } } resource "aws_secretsmanager_secret" "bunsen" { name = "bunsen_blog_secrets" } resource "aws_secretsmanager_secret_version" "bunsen" { secret_id = aws_secretsmanager_secret.bunsen.id secret_string = jsonencode(local.my_secrets) }
最後に変数定義の箇所を変更し、資材から秘匿情報を削除。
$ terraform apply var.secret_value1 Enter a value: secret1 var.secret_value2 Enter a value: secret2 ~~~
apply時に秘匿情報を入力させることができる。
後片付け
Secrets Managerは即削除はされず数日間残り続けます。 その間同名のSecretを作成することはできない為、改めてSecretを作成したい場合はSecret名を変更する必要があります。
$ terraform destroy $ terraform apply ~~~ │ Error: error creating Secrets Manager Secret: InvalidRequestException: You can't create this secret because a secret with this name is already scheduled for deletion. │ │ with aws_secretsmanager_secret.bunsen, │ on main.tf line 11, in resource "aws_secretsmanager_secret" "bunsen": │ 11: resource "aws_secretsmanager_secret" "bunsen" { │
ansibleでdockerインストールからdocker-compose upまで
概要
dockerとdocker-composeをインストールしてそのままdocker-compose up
するplaybookを作ったが、とても詰まったポイントがあったので備忘録として残しておく。
背景
docker-composeで起動しているサービスをVM構築からサービス起動まで一貫したplaybookで構築したい状況でぶつかった。 以下のような処理をするplaybookを作成していた。
- VM構築
- dockerをインストール
- ansibleユーザをdockerグループに追加
- docker-compose.ymlを転送
- ansibleユーザで
docker-compose up
を実施
しかしなぜか初回構築時に以下のエラーで失敗
fatal: [x.x.x.x]: FAILED! => {"changed": false, "msg": "Error connecting: Error while fetching server API version: ('Connection aborted.', error(13, 'Permission denied'))"}
そして一番の謎ポイントが、もう一度実行すると成功するという点だった。
結論から言うとユーザの所属グループを作成してもそれが適用されるのは一度SSHセッションを切らないとだめだよという話。
環境
注意ポイント
- ansible version 2.8以降から
docker_compose
モジュールが追加されています - pipのバージョンが古いと
docker
ordocker-compose
のインストールで失敗します- 詳しいバージョンは忘れましたが手元では9.0.3で成功しています
ファイル構造
. ├── hosts ├── launch_services.yml └── roles ├── docker_install │ └── tasks │ ├── main.yml │ └── to_append_ansible_to_docker.yml └── launch_containers ├── files │ └── docker-compose.yml └── tasks └── main.yml
./launch_services.yml
--- - hosts: docker_hosts gather_facts: no become: yes roles: - docker_install - hosts: docker_hosts gather_facts: no become: no roles: - launch_containers
./roles/docker_install/tasks/main.yml
- name: 関連パッケージインストール yum: name: - gcc - python2-pip - name: dockerインストール yum: name: docker state: present - name: dockerデーモンの起動 systemd: name: docker state: started enabled: yes - name: pipインストール yum: name: python2-pip state: present - name: python用dockerパッケージインストール pip: name: docker state: present - name: docker-composeインストール pip: name: docker-compose state: present - include: to_append_ansible_to_docker.yml
./roles/docker_install/tasks/to_append_ansible_to_docker.yml
- name: ansible実行ユーザをdockerグループへ追加 user: name: ansible state: present groups: - docker append: yes register: ansibles_group - name: ansible実行ユーザのグループ有効化 when: ansibles_group is changed block: - name: sshdの再起動 systemd: name: sshd state: restarted - name: sshd再起動待機 wait_for_connection: delay: 1 timeout: 100
`./roles/launch_containers/tasks/main.yml``
- name: docker-composeファイル転送 copy: src: docker-compose.yml dest: /home/ansible/docker-compose.yml - name: コンテナ起動 docker_compose: project_src: /home/ansible/ state: present
解説
このplaybookでは以下のような処理をたどっています
- docker, docker-composeインストール
- ansibleユーザのdocker使用権限追加※1
docker-compose.yml
転送docker-compose up
実行
※1(to_append_ansible_to_docker.yml
)はdocker_composeモジュールでbecome: yes
を指定する場合は必要ありません
docker, docker-compose インストール
特筆すべき点はあまりありませんがしいて言えば
docker
とdocker-compose
パッケージをインストールできるpip の最低バージョンがあります。
今回のplaybookでは最新のpipを利用しているため問題ありませんがすでにpipがインストールされている環境ではバージョンアップタスクが必要かもしれないです。
ansibleユーザのdocker使用権限追加
ここで記事冒頭の詰まったポイントを回避しています。 docker nodeのsshdを再起動し、ansible nodeとのSSHセッションを強制的に切っています。(もっとスマートなやり方があるのかも、、、)
SSHセッションが切れるとAnsibleは再度SSH接続を仕掛けますが、sshdの再起動が終わっていない場合エラーになってしまう可能性があるため
wait_for_connection
モジュールを利用して1秒待機、最大100秒間リトライし続ける処理をかませています。
docker-compose up
実行
docker-compose
モジュールを利用してコマンドを発行しています。
モジュールの使い方は公式を参照していただきたいのですがdockerを普段から触る方なら直感的に使えるかと思います。
ただ難点があるとすればなかなか要求が厳しいです
- docker関連
- Docker API >= 1.20 (
docker config
で確認) - docker-compose >= 1.7.0
- Docker API >= 1.20 (
- pythonモジュール関連
- Docker SDK
- python2.6: docker-py
- python2.7: docker >= 1.8.0
- PyYAML >= 3.11
- Docker SDK
- ansible関連
- ansible >= 2.8
新規構築ではなくありものでこのモジュールを使う場合、アップグレード処理を入れる必要が出てくるかもしれません。
ansibleの曲者synchronizeモジュール
概要
Ansibleを使っての運用をする機会が増えてきた今日このごろ。
被管理ホストでバックアップをするために、ファイルをansibleホストに転送しようとしたときにこのsynchronize
モジュールに出会いました。
最初のうちは便利だなと思い公式ページを見ていたが 使って/調べていくにつれてやけに自身の認識と実際の動作に差が開いていった。 マジで使いづらい。。。 ということでまとめます。
synchronize
モジュールとは
公式によると「rsyncのラッパーである」と書かれている。 つまり裏ではrsyncが動作しているとのこと。
rsyncは管理/被管理ノードで起動し、その間でファイルの転送を実施する。 そのため両ホストにrsyncがインストールされている必要がある。
rsync実行にはsudo権限が必要であるためansibleユーザにsudo権限が必要。 ※パスワードなしsudo権限でないとNG。パスワードありsudoだとrsyncが起動できずエラーで終了する。
動作パターン①
localhostで動作し、被管理ホストにローカルファイルを送り込む。
- hosts: target - tasks: - synchronize: mode: push src: <local_path_to_file> dest: <remote_path_to_file>
動作パターン②
localhostで動作し、被管理ホストからローカルにファイルを取り込む。
- hosts: target - tasks: - synchronize: mode: pull src: <remote_path_to_file> dest: <local_path_to_file>
動作パターン③
被管理ホストで動作し、localhostにファイルを送り込む(結果はパターン②と同じ)
- hosts: target - tasks: - synchronize: mode: push src: <remote_path_to_file> dest: <local_path_to_file> delegate_to: target
動作パターン④
被管理ホストで動作し、localhostからファイルを取り込む(結果はパターン①と同じ)
- hosts: target - tasks: - synchronize: mode: pull src: <local_path_to_file> dest: <remote_path_to_file> delegate_to: target
batfish/allinoneコンテナにおけるnotebookデータの永続化
概要
batfish/allinoneのコンテナを使用してbatfishをお勉強中。しかし、コンテナを落とし上げするたびにせっかく作ったデータがなくなってしまう。
かといって毎回作成したnotebookをダウンロードして上げなおしてというのはめんどくさいのでホスト側にマウントすることにした。
環境
- Windows10 10.0.16299
- Docker for Windows version 18.09.2, build 6247962
- batfish/allinone c5c630d4bdd3
方法
- ホスト側にマウント先のディレクトリを作成する
- 現存するnotebookをコンテナ内部から救出する
- コンテナを停止する
docker stop <docker id or name>
でコンテナを停止する
- コンテナを起動する
docker run -v <マウント先ディレクトリの絶対パス>:/notebooks -p 8888:8888 batfish/allinone
でコンテナを起動する
まとめ
docker exec -it <docker id or name> /bin/bash
でコンテナ内部をウロチョロできて楽しかったです!
docker run -p において発生するポートフォワード失敗エラー
概要
ネットワーク機器のコンフィグ解析ツールbatfish/allinoneのコンテナを起動しようとしたとき以下のエラーが発生した。
> docker run -p 8888:8888 batfish/allinone C:\Program Files\Docker\Docker\Resources\bin\docker.exe: Error response from daemon: driver failed programming external connectivity on endpoint gracious_poincare (***): Error starting userland proxy: mkdir /port/tcp:0.0.0.0:8888:tcp:172.17.0.2:8888: input/output error. ERRO[0000] error waiting for container: context canceled
- 公式の手順通りやってるのに...
- 昨日まではうまくいっていたのに...
ということで少し調査
原因
どうも2通りの原因があるようです
- ホスト側のポートが他のサービスにより占有されている
- psなどのコマンドで使用したいポートが占有されているか調べる
- Dockerの設定に過去のコンテナ情報が残ってしまっている
- 原因1 でない場合
解決策
原因のパターンにより異なります。
1. ホスト側のポートが他のサービスにより占有されている
- 占有しているサービスを停止する
- ホスト側のフォワーディングポートを変更する
2. Dockerの設定に過去のコンテナ情報が残ってしまっている
- Dockerの再起動
まとめ
なんかあんまりしっくりこないね...