20210727のAWSに関する記事は22件です。

Amplify GraphQLでRDSを使用する

概要 やりたいこと概要:GraphQLでRDSのデータを取得できる環境をAmplifyで作成する。 JS app -> GraphQL -> Lambda (JS) -> RDS proxy -> RDS ほぼ全て初めて触るのでおかしいとこあるかもしれないが、とりあえずできたので備忘録として。 余談:LambdaからのRDSアクセスは、もうアンチパターンではない。 オンラインセミナー「RDS+Lambda が始まる。過去のアンチパターンはどう変わるのか」 の資料が、アンチパターンと言われていた理由とAWSによる改善をわかりやすく説明している。 RDS proxyは入れなくてもRDSへのアクセスは可能だが、これこそがアンチパターン対策の一つなので、詳しくは上記リンクを参照のこと。 前提 aws CLIコマンド使えるくらいの知識はある。 aws CLIコマンド ver.2 インストール済み アクセスできるRDS (AuroraだがServerlessではない) がある。 フロントエンドアプリもLambdaもJS(ちなみに私はJS信者ではない) 手順概要 Amplifyの準備 AmplifyでLambda Layerを作成 AmplifyでLambda関数を作成 Lambda関数にVPCアクセス権限を追加 VPCアクセスを設定する GraphQL APIを作成する LambdaでGraphQLからのパラメータを使用する フロントエンドアプリからGraphQL APIを呼び出す RDS proxyを導入する シークレットを作成 シークレットを使用するポリシーを作成 シークレットを使用するロールを作成 RDS proxyを作成する RDS proxyを使用する 手順 Amplifyの準備 amplify initでAmplifyを使い始めるのだが、ソースツリーをロールバックしたり何が起こっているか把握するためにも、このコマンドの前にプロジェクトをgit管理して変化を確認できるようにしておくことをおすすめする。 まずは、このへんの公式チュートリアルでAmplifyのCLIインストールしたり、アプリのコードベースを作成する。チュートリアルではプロジェクト作成しているが、既存のプロジェクトでAmplifyを使いたければ、amplify initをすればよい。 とりあえずここでコミットしておく。 AmplifyでLambda Layerを作成 Lambda Layerは複数のLambdaで共用可能なコードの塊で、JSではnode_modulesに入るようなライブラリファイル群をLambda関数とは別のLambda Layerとして作成する。Lambda関数に含めてしまっても良いが、大きすぎるとLambdaコンソールのエディタが使えなくなるので分けておいたほうが無難。 今回はRDSへアクセスするためのライブラリ mysql2 などをこのレイヤーに入れる。 amplify add functionコマンドでLambda layerを選んで、順次質問に答えてLambda layerの設定ファイルを作成。 MacBook-Pro.local:amplify-js-app% amplify add function ? Select which capability you want to add: Lambda layer (shared code & resource used across functions) ? Provide a name for your Lambda layer: layer9da0e99d ? Choose the runtime that you want to use: NodeJS ? The current AWS account will always have access to this layer. Optionally, configure who else can access this layer. (Hit <Enter> to skip) ✅ Lambda layer folders & files created: amplify/backend/function/amplifyjsapplayer9da0e99d Next steps: Move your libraries to the following folder: [NodeJS]: amplify/backend/function/amplifyjsapplayer9da0e99d/lib/nodejs ... CLI出力の最後の行にあるように amplify/backend/function/amplifyjsapplayer9da0e99d/lib/nodejs に node_modules が配置されればよいので、このディレクトリに package.json を作成してパッケージをインストールする。 下記の例では、ついでにRDSアクセスのパフォーマンス計測に使用するAWS XRayのパッケージも入れている。不要であれば aws-xray-sdk-* は入れなくていい。 amplify/backend/function/amplifyjsapplayer9da0e99d/lib/nodejs/package.json { "name": "rds-xray-js-lib", "description": "NPM dependencies for RDS access with XRAY support", "version": "1.0.0", "private": true, "dependencies": { "aws-xray-sdk-core": "2.4.0", "aws-xray-sdk-mysql": "2.4.0", "mysql2": "2.1.0" }, "scripts": {} } そして、このpackage.jsonがあるディレクトリでnpm installしてパッケージをダウンロードする。 AmplifyでLambdaを作成 今度は、amplify add functionコマンドでLambda functionを選んでLambda関数の設定ファイルを作成する。 この際、先程設定したLambda layerを指定する。Spaceキーで選択なので注意。 またここで、RDSへアクセスするためのDBホスト名、DB名、ユーザID&パスワードを環境変数として登録する。(IAMやシステムマネージャのシークレットを使用することもできるが、ここでは割愛する。シークレットはこの同ウィザードの "Do you want to configure secret values this function can access?" で設定できそう。) MacBook-Pro.local:amplify-js-app% amplify add function ? Select which capability you want to add: Lambda function (serverless function) ? Provide an AWS Lambda function name: amplifyjsapp92a6207c ? Choose the runtime that you want to use: NodeJS ? Choose the function template that you want to use: Hello World Available advanced settings: - Resource access permissions - Scheduled recurring invocation - Lambda layers configuration - Environment variables configuration - Secret values configuration ? Do you want to configure advanced settings? Yes ? Do you want to access other resources in this project from your Lambda function? No ? Do you want to invoke this function on a recurring schedule? No ? Do you want to enable Lambda layers for this function? Yes ? Provide existing layers or select layers in this project to access from this function (pick up to 5): amplifyjsapplayer9da0e99d ? Do you want to configure environment variables for this function? Yes ? Enter the environment variable name: db_host ? Enter the environment variable value: database.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com ? Select what you want to do with environment variables: Add new environment variable ? Enter the environment variable name: db_schema ? Enter the environment variable value: my_database ? Select what you want to do with environment variables: Add new environment variable ? Enter the environment variable name: db_username ? Enter the environment variable value: my_user ? Select what you want to do with environment variables: Add new environment variable ? Enter the environment variable name: db_password ? Enter the environment variable value: my_password ? Select what you want to do with environment variables: I'm done ? Do you want to configure secret values this function can access? No ? Do you want to edit the local lambda function now? Yes Successfully added resource amplifyjsapp92a6207c locally. これでLambda関数のソースファイルがamplify/backend/function/amplifyjsapp92a6207c/src/index.jsに作成された。 これを下記のように変更。 amplify/backend/function/amplifyjsapp92a6207c/src/index.js const AWSXRay = require('aws-xray-sdk-core') const captureMySQL = require('aws-xray-sdk-mysql') const mysql = captureMySQL(require('mysql2/promise')) exports.handler = async (event) => { const connection = await mysql.createConnection({ host : process.env.db_host, database : process.env.db_schema, user : process.env.db_username, password : process.env.db_password }) const [rows, fields] = await connection.execute('select * from users') console.log(rows) await connection.end() return rows[0].name } XRayが不要ならばはじめの3行を const mysql = require('mysql2/promise'); に置き換える。 'select * from users'とrows[0].nameも実際のテーブルに合わせて変更する。 amplify push を実行。数分かかるのでコーヒーを淹れに行く。 MacBook-Pro.local:amplify-js-app% amplify push ✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------------- | --------- | ----------------- | | Function | amplifyjsapplayer9da0e99d | Create | awscloudformation | | Function | amplifyjsapp92a6207c | Create | awscloudformation | ? Are you sure you want to continue? Yes Suggested configuration for new layer versions: amplifyjsapplayer9da0e99d - Description: Updated layer version 2021-07-27T10:49:26.931Z ? Accept the suggested layer version configurations? Yes ⠇ Updating resources in the cloud. This may take a few minutes... ... ✔ All resources are updated in the cloud これで実際にLambdaが作成されて、Lambdaのコンソールで見られるようになった。 左のメニューのLayersから、作成したlayerも確認できる。 LambdaコンソールからLambda関数を開いてCode -> Testで実行できる(パラメータは使用していないので何でもいい)が、まだエラーになる。RDSがあるVPCにアクセスするための権限がないためだ。 Lambda関数にVPCアクセス権限を追加 Lambda関数の Configuration -> Permissions と開いて、RoleをクリックするとIAMのコンソールが開く。 開いたIAMのRolesのPermissionsタブで「Attach policies」をクリックして AWSLambdaVPCAccessExecutionRole ポリシーを追加する。 Lambda関数のページに戻ってリロードすると、Resource summaryのドロップダウンで表示される権限が増えているはずだ。 VPCアクセスを設定する 左のメニューのVPCから開いてEditで追加する。 そして、接続したいRDSがあるVPCを選択。 SubnetsはよくわからんのでRDSと同じものを設定。 Security groupsはRDSにアクセスできるものを設定。(RDSがデフォルトのままdefaultセキュリティグループで特に変更していなければ同じdefaultセキュリティグループでアクセスできるはず。) Saveボタンを押して設定反映。反映完了まで1分くらいかかる。 ここで、Lambda関数のコンソールでTestボタンを押すと、DBからデータが読み込めるようになっている。 GraphQL APIを作成する プロジェクトルートに戻って `` を実行してGraphQLのAPIを定義する。 MacBook-Pro.local:amplify-js-app% amplify add api ? Please select from one of the below mentioned services: GraphQL ? Provide API name: amplifyjsapp ? Choose the default authorization type for the API API key # ① ? Enter a description for the API key: amplifyjsapp ? After how many days from now the API key should expire (1-365): 7 ? Do you want to configure advanced settings for the GraphQL API No, I am done. ? Do you have an annotated GraphQL schema? No ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) # ② ... ① 認証はとりあえず一番手間のかからないAPI keyを使用する。値はAmplifyが設定ファイルに書き込んで勝手に使ってくれるので気にしなくて良い。 ② とりあえずTodoテンプレートで作成したが、下記で書き換える。 amplify-js-app/amplify/backend/api/amplifyjsapp/schema.graphql にschemaの定義ファイルができるので、ここにLambdaを使用するリゾルバを定義する。 amplify-js-app/amplify/backend/api/amplifyjsapp/schema.graphql type Query { queryRds(query: String): String @function(name: "amplifyjsapp92a6207c-${env}") } そして、amplify pushで環境に反映する。 MacBook-Pro.local:amplify-js-app% amplify push ✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------------- | --------- | ----------------- | | Api | amplifyjsapp | Create | awscloudformation | | Function | amplifyjsapp92a6207c | Update | awscloudformation | | Function | amplifyjsapplayer9da0e99d | No Change | awscloudformation | ? Are you sure you want to continue? Yes GraphQL schema compiled successfully. Edit your schema at /Users/kanji/development/aws/amplify-js-app/amplify/backend/api/amplifyjsapp/schema.graphql or place .graphql files in a directory at /Users/kanji/development/aws/amplify-js-app/amplify/backend/api/amplifyjsapp/schema ? Do you want to generate code for your newly created GraphQL API Yes ? Choose the code generation language target javascript ? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] 2 ⠹ Updating resources in the cloud. This may take a few minutes... ... ✔ Generated GraphQL operations successfully and saved at src/graphql ✔ All resources are updated in the cloud GraphQL endpoint: https://xxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql GraphQL API KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx API KEYはAmplifyが自動的にアプリに適用してくれるので気にしなくて良い。 今度はAWS AppSyncのコンソールでGraphQLのクエリを実行できるので試してみる。RDSからデータが取れているはずだ。 LambdaでGraphQLからのパラメータを使用する 上記のqueryRdsでqueryを渡すようにしているので、Lambdaのコードでそれを使用するように変更する。 amplify/backend/function/amplifyjsapp92a6207c/src/index.js ... exports.handler = async (event) => { ... const [rows, fields] = await connection.execute(event.arguments.query) ... } フロントエンドアプリからGraphQL APIを呼び出す まず、npm i aws-amplifyで必要になるパッケージをインストール。 次に、src/app.jsにフロントエンドアプリのコードを記述し、そこからGraphQL APIにアクセスする。 import Amplify, { API, graphqlOperation } from "aws-amplify"; import awsconfig from "./aws-exports"; import { queryRds } from "./graphql/queries"; Amplify.configure(awsconfig); const button = document.getElementById("my_button"); button.addEventListener("click", (evt) => { API.graphql(graphqlOperation(queryRds, { query: 'select * from internal_users' })) .then((evt) => { console.log(evt.data.queryRds) }); }); これでようやく、フロントエンドアプリからGraphQLでRDSのデータへアクセスできた。 RDS proxyを導入する このままだと、LambdaがRDSにリクエスト単位でコネクションを作ってしまうのですぐに上限に達してしまいエラーになる。 そこでRDS proxyを使用する。これがコネクションプールの役割を果たしてくれる。 シークレットを作成 まず、RDS proxyがRDSにアクセスするためのDBクレデンシャルをAWS Secrets Managerで作成する。 Auroraの場合は「Credentials for RDS database」を選択してuser nameとpasswordを入れ、DBを選択してNextで次へ行ってシークレット名を入力し、他はデフォルトのままで進む。 作成できたシークレットのARNをメモしておく。 シークレットを使用するポリシーを作成 次にIAMコンソールのCreate PolicyでJSONタブを開き、下記JSONを貼り付ける。参考ドキュメント { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue", "secretsmanager:GetResourcePolicy", "secretsmanager:DescribeSecret", "secretsmanager:ListSecretVersionIds" ], "Resource": [ "arn:aws:secretsmanager:{region}:{account_id}:{secret_name}" ] }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": "kms:Decrypt", "Resource": "arn:aws:kms:{region}:{account_id}:key/{key_id}", "Condition": { "StringEquals": { "kms:ViaService": "secretsmanager.{region}.amazonaws.com" } } } ] } 上の"Resource"は先にメモったシークレットのARNを入れる。 {account_id}と{region}は適宜置き換え。{key_id}はKMSのコンソールで確認できる "aws/secretsmanager" のKey IDに置き換える。 シークレットを使用するロールを作成 更にIAMコンソール で作成したポリシーを適用したRoleを作成する。 AWS service -> RDS -> "RDS - Add Role to Database" -> Next:Permissions Select permission policyで、上で作成したポリシーを選択 名前をつけて保存 ロールの Trust relationship タブから Edit trust relationship をクリックしてTrust policyを下記に変更する。 { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "rds.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } RDS proxyを作成する Lambda関数のコンソールに戻って、Configurationタブの左のサイドメニューからDatabase proxiesの画面を開いて、Add database proxyをクリック。 SecretやIAM roleに上で作成したものを指定して、Add。 RDS proxyを使用する Lambda関数のRDS proxyの画面に作成したRDS proxyの詳細画面へのリンクが表示されるので開く。 Endpointのホスト名をコピーする。 Lambda関数のページに戻り環境変数のdb_hostをコピーしたRDS proxyのホスト名に変更する。 これでLambdaからのRDSアクセスがRDS proxy経由になり下記が完成した。 JS app -> GraphQL -> Lambda (JS) -> RDS proxy -> RDS 以上、お疲れさまでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SSL証明書】AWS上でのCertbot手動更新方法

やること https化で取得していたSSL証明書(90日間)が切れてしまったので、 Certbotでの手動更新を行う。 小難しいことはなく、至って簡単でした。 *既にcertbotでの設定は終えている想定となります。 備忘録的な感じなので、ご理解ください。 背景 及び 簡単な状況説明 AWSを使用して、PFをデプロイしている。 久しぶりに開いてみたら、SSL期限切れのためPFが開けず。 https化のためにCertbotを使用している。 Certbotを使用してLet's Encrypt認証局からSSL証明書を取得している。 ただ、期日は90日間しかないため、証明書が切れた。 Certbotの設定でnginxを再起動すれば更新できる。 だが、cronを使用してCertbotを自動更新することもできたが、イマイチ動いておらず。cronの再起動をしていなかったからかと思われる。 なので、せっかくなら、手動でやってみようと思い実行してみました。 更新方法(手動) ①EC2に接続する まずは、awsのターミナルでEC2に接続します。 AmazonLinux2 ssh -i ~/.ssh/****.pem ec2-user@[IPアドレス]  ②helpコマンドでcertbotに関して確認 -hでcertbotで打てるコマンドを確認してみます。 AmazonLinux2 $ certbot -h 実行すると以下のように、バーと出てきます。 AmazonLinux2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ... Certbot can obtain and install HTTPS/TLS/SSL certificates. By default, it will attempt to use a webserver both for obtaining and installing the certificate. The most common SUBCOMMANDS and flags are: obtain, install, and renew certificates: (default) run Obtain & install a certificate in your current webserver certonly Obtain or renew a certificate, but do not install it renew Renew all previously obtained certificates that are near expiry enhance Add security enhancements to your existing configuration -d DOMAINS Comma-separated list of domains to obtain a certificate for - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 中身を確認していくと、下記の記述を発見! AmazonLinux2 renew Renew all previously obtained certificates that are near expiry 訳すと、以前に取得した証明書を新しく(取得)すると書いてあります。 これならうまくいきそう。 ③certbot renewを実行 ②で見つけたコマンドで実行してみます。 AmazonLinux2 $ certbot renew The following error was encountered: [Errno 13] Permission denied: '/var/log/letsencrypt/.certbot.lock' Either run as root, or set --config-dir, --work-dir, and --logs-dir to writeable paths. あれ、うまくいかない、、、 よく見てみると、Permission deniedと書いてあります。 実行許可がないためなので、sudoを付けて再実行します。 $ sudo certbot renew Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/answer-ly.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cert is due for renewal, auto-renewing... Plugins selected: Authenticator nginx, Installer nginx Renewing an existing certificate for answer-ly.com Performing the following challenges: http-01 challenge for answer-ly.com Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed with reload of nginx server; fullchain is /etc/letsencrypt/live/answer-ly.com/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all renewals succeeded: /etc/letsencrypt/live/answer-ly.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Suucessと出力されました。 これで無事PFも開けるようになりました。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React+Django+AWSでペットの成長をサポートするアプリを開発したのでTipsをまとめた

biby diary https://biby-diary.studio.site/ 本日、ペットの成長を記録できるアプリ「biby diary」をリリースしました。 biby diaryは日々のペットの成長を記録できるほか、家族間でのペットの情報を共有することができます。 さらにbiby検索から動物病院を検索することもできるので、緊急時などにお近くの住所から迅速に動物病院を見つけることもできます。 スマホ推奨で、PWAにも対応しているのでブラウザからホーム画面に追加することで快適にご利用いただけます。 追加する際はこちらのリンクから追加してください。 https://diary.biby.live アーキテクチャーにはDjango、React、AWSを採用しており、これらの技術で実務にあたられている方や採用を検討している方の参考になればと思いまとめました。 もし紹介する内容以外でベストプラクティスがあれば、ぜひコメント欄へ共有いただきたいです。 目次 アーキテクチャ詳細 ReactのTips DjangoのTips ngrok 今後の計画 アーキテクチャ詳細 全体のシステムはこのような図になっています。 biby diaryはReact+TypeScriptとDjangoで開発しました。サーバーはAWSのものを使用しており、フロントエンドにはamplify、バックエンドにはEC2を使用しています。 amplifyを使うことでURLのSSL化やデプロイの設定などを簡単に行うことができます。 ドメインはお名前.comで取得しているのですが、ネームサーバーをAWSに向けておけばCNAMEなどの設定も自動で行ってくれるので非常に楽でした。 バックエンドにEC2を使うことで、CodeDeployとCodePipelineを使って容易に自動デプロイの設定ができます。 フロントエンドとバックエンドを分けたのは、後々スマホアプリ化も検討しているため、あえて分離しています。 現在はWebブラウザだけですが、PWAにも対応しているのでホーム画面にアプリを追加していただければアプリのような操作感でお使いいただけます。 今回初めてAWSを使用しましたが、ほかのクラウドサービスと比べて非常に使いやすい印象でした。 さすがクラウドサービス市場のトップです。ちなみに2位はAzureです。 G○Pだとなぜそうなる?みたいことになりがちだったので、これからはAWSを推していこうと思います。 ReactのTips Reactは以前から使用した経験があったのと、Adobeが提供しているReact Spectrumを使ってみたかったこともあり採用しました。 React Spectrumについては本記事では詳しく説明しないので、別の機会にまとめようと思っています。 Reactでクッキーを操作する方法 biby diaryではログインした時にAPIにリクエストを送るためのトークンを発行しているのですが、そのトークンを持っておくためにクッキーを使用しています。 Reactにはreact-cookiesというクッキーの操作が簡単にできるパッケージがあります。 https://github.com/reactivestack/cookies/tree/master/packages/react-cookie 利用例 まずはアプリ全体をCookiesProviderラップします。 import React from 'react'; import App from './App'; import { CookiesProvider } from 'react-cookie'; export default function Root() { return ( <CookiesProvider> <App /> </CookiesProvider> ); } アプリを登録する場合は下記のようになります。 aaa import React from 'react'; import { useCookies } from 'react-cookie'; import NameForm from './NameForm'; function App() { const [cookies, setCookie] = useCookies(['name']); function onChange(newName) { setCookie('name', newName, { path: '/' }); } return ( <div> <NameForm name={cookies.name} onChange={onChange} /> {cookies.name && <h1>Hello {cookies.name}!</h1>} </div> ); } export default App; useCookiesを使用したい箇所でインポートし、const [cookies, setCookie] = useCookies(['name']);でクッキーを登録できるようにします。 実際の登録は、setCookie('name', newName, { path: '/' });となっており、pathオプションを指定することでクッキーの保存対象にしたいページを選択できます。/を指定した場合はアプリのページ全てに対してクッキーを保存することになります。 ログアウト時などにクッキーを削除したい場合は、removeCookieを使用します。 removeCookieにもpathオプションを指定することができ、removeCookie('name', { path: '/' })としておくことでページ全体に対して選択したクッキーを削除できます。 あえて{ path: '/' }を指定しておくことをおすすめします。 環境変数の設定 Reactの構築にはCreate React Appを用いており、Create React Appにはデフォルトで環境変数を供給する機構が備わっています。 使い方は簡単でプロジェクト直下に.envファイルを作成し、REACT_APP_をプレフィックスにした変数を入れておくことでReactアプリ内で呼び出すことができます。 呼び出す時はprocess.env.REACT_APP_[変数名]で変数を取得できます。 利用例 REACT_APP_TEST="test" 環境変数を呼び出す時はこのようになります。 const test = process.env.REACT_APP_TEST; amplifyでの環境変数設定 .envファイルは機密情報を含める場合もあるため.gitignoreに入れておくことが望ましく、デプロイ時に.envを生成する必要があります。 amplifyでは管理画面のサイドバーにある環境変数から設定を行い、同じくサイドバーのビルドの設定から.envファイルの生成コマンドを追記します。 環境変数設定画面を開くとこのような画面が出てきます。 変数と値をそれぞれ入力します。 続いてビルドの設定を開くと、下記のようなビルド設定が表示されていると思います。 amplify.yml version: 1 frontend: phases: preBuild: commands: - yarn install build: commands: - yarn run build artifacts: baseDirectory: build files: - '**/*' cache: paths: - node_modules/**/* .envの生成はbuild>commandsに- echo "REACT_APP_TEST=$REACT_APP_BACKEND_TEST" >> .envを追記します。 同じ画面からymlファイルの編集ができるので、そのまま追記できます。 画像アップロード 画像アップロードに手こずってしまったので、同じような境遇に会った方、会われるであろう方に向けてTipsを紹介しておきます。 基本的にAPIリクエスト時にデータを渡すときは、JSON形式にしているのですが画像をアップロードする時にどうしてもJSONではリクエストに失敗するということがありました。 調べるとJSON形式でもファイル送信ができるみたいな情報が出てきたのですが、実現できなかったのでファイルアップロードによく使われるmultipart/form-data形式を使うことにしました。 最終的にmultipart/form-dataで無事に画像ファイルは送信できるようになったのですが、もう一つ躓いたポイントがあり、リクエストヘッダのcontent-typeに明示的にmultipart/form-dataを指定するとリクエストに失敗する現象が発生しました。 理由はいまだにわからないので、もし分かる方いたら教えてください。 DjangoのTips DjangoにはDjango Rest FrameworkというREST APIを実装できるパッケージがあるのですが、これがかなり使いやすく容易にAPIを開発できるのでDjangoを採用しました。 デフォルトで管理画面が付いている点も理由の1つです。 CORS問題 API開発でやっかいなのが、CORS問題です。CORSは同一生成元ポリシーと呼ばれるブラウザのセキュリティ仕様で、あるサーバーにWebからリクエストを送る時に同じドメインであればエラーは起きないのですが、違うドメインからのリクエストは脆弱性から守るために拒否されます。 このサイトがわかりやすくCORS問題について説明しているので、気になる方はご参照ください。 https://coliss.com/articles/build-websites/operation/work/cs-visualized-cors.html CORS問題を解消するにはバックエンド側でCORSの制御をする必要があります。 corsheadersというパッケージを使うことで、CORS問題を片付けることができます。 https://github.com/adamchainz/django-cors-headers 環境変数 Djangoは環境を立ち上げた時にシークレットキーが作られます。このシークレットキーはユニークIDの生成などに使用されるので、gitで管理するようなことはしたくありません。 そのため、環境変数で持っておくことが望ましいです。 Djangoには、django-environという環境変数を設定するためのパッケージがあります。 https://github.com/joke2k/django-environ このパッケージを使うことで容易に環境変数の設定ができます。 使い方 プロジェクト直下に.envファイルを作成し変数を追記していきます。 具体的にはこのような形で追記します。 代入する値はダブルクオートなどで囲む必要はないのでご注意ください。 DEBUG=on SECRET_KEY=your-secret-key 呼び出す時はこのようになります。 settings.py import environ env = environ.Env( # set casting, default value DEBUG=(bool, False) ) # reading .env file environ.Env.read_env() # False if not in os.environ DEBUG = env('DEBUG') # Raises django's ImproperlyConfigured exception if SECRET_KEY not in os.environ SECRET_KEY = env('SECRET_KEY') ngrok アプリを開発しているとスマホでも実際に挙動を確認したいということがあると思うのですが、フロントエンドとバックエンドを分離していると、ローカル環境ではAPIのドメインがlocalhostなため確認できません。 フロントにはアクセスできるけどバックエンドにはつながらないといったことになると思います。 このときにおすすめなのがngrokという開発ツールで、ngrokを使用することで一時的に作られるドメインとローカルのドメインを結びつけることができ、他のデバイスからアクセスすることができます。 https://ngrok.com/ 開発途中のアプリをngrokを使ってクライアントに見てもらうこともできるので、実務でも大活躍します。 会員登録なしでも使用することができるのですが、その場合一時的に発行されるドメインの有効期限が決まっています。 有効期限なしで使用したい場合は、無料プランの登録もできるので会員登録しておくことをおすすめします。 起動はターミナルを開いてngrok http ポート番号と入力するだけなので簡単にアプリを外部に向けて公開できます。 今後の計画 biby diaryはまだまだ未完成なので、継続的に開発をしていきます。 今は無料プランしかないので近々有料プランを追加する予定しているほか、bibyユーザーがペットへの寄付を外部に募ることができる機能も考えています。 追加して欲しい機能も絶賛募集中なので、ぜひご連絡ください。本記事のコメント欄もしくは、こちらのお問い合わせフォームからご要望いただくこともできます。 https://forms.gle/EJdXYLNWbeLfCccm9 形だけですがロードマップを公開しているので、気になる方はぜひご覧ください。 https://uichi.notion.site/5bead4691e344d6b946a09f9aa8c7d1f?v=3fccb1b559ef4c27ab0872db33a95690!%5BFireShot
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reac+Django+AWSでペットの成長をサポートするアプリを開発したのでTipsをまとめた

biby diary https://biby-diary.studio.site/ 本日、ペットの成長を記録できるアプリ「biby diary」をリリースしました。 biby diaryは日々のペットの成長を記録できるほか、家族間でのペットの情報を共有することができます。 さらにbiby検索から動物病院を検索することもできるので、緊急時などにお近くの住所から迅速に動物病院を見つけることもできます。 スマホ推奨で、PWAにも対応しているのでブラウザからホーム画面に追加することで快適にご利用いただけます。 追加する際はこちらのリンクから追加してください。 https://diary.biby.live アーキテクチャーにはDjango、React、AWSを採用しており、これらの技術で実務にあたられている方や採用を検討している方の参考になればと思いまとめました。 もし紹介する内容以外でベストプラクティスがあれば、ぜひコメント欄へ共有いただきたいです。 目次 アーキテクチャ詳細 ReactのTips DjangoのTips ngrok 今後の計画 アーキテクチャ詳細 全体のシステムはこのような図になっています。 biby diaryはReact+TypeScriptとDjangoで開発しました。サーバーはAWSのものを使用しており、フロントエンドにはamplify、バックエンドにはEC2を使用しています。 amplifyを使うことでURLのSSL化やデプロイの設定などを簡単に行うことができます。 ドメインはお名前.comで取得しているのですが、ネームサーバーをAWSに向けておけばCNAMEなどの設定も自動で行ってくれるので非常に楽でした。 バックエンドにEC2を使うことで、CodeDeployとCodePipelineを使って容易に自動デプロイの設定ができます。 フロントエンドとバックエンドを分けたのは、後々スマホアプリ化も検討しているため、あえて分離しています。 現在はWebブラウザだけですが、PWAにも対応しているのでホーム画面にアプリを追加していただければアプリのような操作感でお使いいただけます。 今回初めてAWSを使用しましたが、ほかのクラウドサービスと比べて非常に使いやすい印象でした。 さすがクラウドサービス市場のトップです。ちなみに2位はAzureです。 G○Pだとなぜそうなる?みたいことになりがちだったので、これからはAWSを推していこうと思います。 ReactのTips Reactは以前から使用した経験があったのと、Adobeが提供しているReact Spectrumを使ってみたかったこともあり採用しました。 React Spectrumについては本記事では詳しく説明しないので、別の機会にまとめようと思っています。 Reactでクッキーを操作する方法 biby diaryではログインした時にAPIにリクエストを送るためのトークンを発行しているのですが、そのトークンを持っておくためにクッキーを使用しています。 Reactにはreact-cookiesというクッキーの操作が簡単にできるパッケージがあります。 https://github.com/reactivestack/cookies/tree/master/packages/react-cookie 利用例 まずはアプリ全体をCookiesProviderラップします。 import React from 'react'; import App from './App'; import { CookiesProvider } from 'react-cookie'; export default function Root() { return ( <CookiesProvider> <App /> </CookiesProvider> ); } アプリを登録する場合は下記のようになります。 aaa import React from 'react'; import { useCookies } from 'react-cookie'; import NameForm from './NameForm'; function App() { const [cookies, setCookie] = useCookies(['name']); function onChange(newName) { setCookie('name', newName, { path: '/' }); } return ( <div> <NameForm name={cookies.name} onChange={onChange} /> {cookies.name && <h1>Hello {cookies.name}!</h1>} </div> ); } export default App; useCookiesを使用したい箇所でインポートし、const [cookies, setCookie] = useCookies(['name']);でクッキーを登録できるようにします。 実際の登録は、setCookie('name', newName, { path: '/' });となっており、pathオプションを指定することでクッキーの保存対象にしたいページを選択できます。/を指定した場合はアプリのページ全てに対してクッキーを保存することになります。 ログアウト時などにクッキーを削除したい場合は、removeCookieを使用します。 removeCookieにもpathオプションを指定することができ、removeCookie('name', { path: '/' })としておくことでページ全体に対して選択したクッキーを削除できます。 あえて{ path: '/' }を指定しておくことをおすすめします。 環境変数の設定 Reactの構築にはCreate React Appを用いており、Create React Appにはデフォルトで環境変数を供給する機構が備わっています。 使い方は簡単でプロジェクト直下に.envファイルを作成し、REACT_APP_をプレフィックスにした変数を入れておくことでReactアプリ内で呼び出すことができます。 呼び出す時はprocess.env.REACT_APP_[変数名]で変数を取得できます。 利用例 REACT_APP_TEST="test" 環境変数を呼び出す時はこのようになります。 const test = process.env.REACT_APP_TEST; amplifyでの環境変数設定 .envファイルは機密情報を含める場合もあるため.gitignoreに入れておくことが望ましく、デプロイ時に.envを生成する必要があります。 amplifyでは管理画面のサイドバーにある環境変数から設定を行い、同じくサイドバーのビルドの設定から.envファイルの生成コマンドを追記します。 環境変数設定画面を開くとこのような画面が出てきます。 変数と値をそれぞれ入力します。 続いてビルドの設定を開くと、下記のようなビルド設定が表示されていると思います。 amplify.yml version: 1 frontend: phases: preBuild: commands: - yarn install build: commands: - yarn run build artifacts: baseDirectory: build files: - '**/*' cache: paths: - node_modules/**/* .envの生成はbuild>commandsに- echo "REACT_APP_TEST=$REACT_APP_BACKEND_TEST" >> .envを追記します。 同じ画面からymlファイルの編集ができるので、そのまま追記できます。 画像アップロード 画像アップロードに手こずってしまったので、同じような境遇に会った方、会われるであろう方に向けてTipsを紹介しておきます。 基本的にAPIリクエスト時にデータを渡すときは、JSON形式にしているのですが画像をアップロードする時にどうしてもJSONではリクエストに失敗するということがありました。 調べるとJSON形式でもファイル送信ができるみたいな情報が出てきたのですが、実現できなかったのでファイルアップロードによく使われるmultipart/form-data形式を使うことにしました。 最終的にmultipart/form-dataで無事に画像ファイルは送信できるようになったのですが、もう一つ躓いたポイントがあり、リクエストヘッダのcontent-typeに明示的にmultipart/form-dataを指定するとリクエストに失敗する現象が発生しました。 理由はいまだにわからないので、もし分かる方いたら教えてください。 DjangoのTips DjangoにはDjango Rest FrameworkというREST APIを実装できるパッケージがあるのですが、これがかなり使いやすく容易にAPIを開発できるのでDjangoを採用しました。 デフォルトで管理画面が付いている点も理由の1つです。 CORS問題 API開発でやっかいなのが、CORS問題です。CORSは同一生成元ポリシーと呼ばれるブラウザのセキュリティ仕様で、あるサーバーにWebからリクエストを送る時に同じドメインであればエラーは起きないのですが、違うドメインからのリクエストは脆弱性から守るために拒否されます。 このサイトがわかりやすくCORS問題について説明しているので、気になる方はご参照ください。 https://coliss.com/articles/build-websites/operation/work/cs-visualized-cors.html CORS問題を解消するにはバックエンド側でCORSの制御をする必要があります。 corsheadersというパッケージを使うことで、CORS問題を片付けることができます。 https://github.com/adamchainz/django-cors-headers 環境変数 Djangoは環境を立ち上げた時にシークレットキーが作られます。このシークレットキーはユニークIDの生成などに使用されるので、gitで管理するようなことはしたくありません。 そのため、環境変数で持っておくことが望ましいです。 Djangoには、django-environという環境変数を設定するためのパッケージがあります。 https://github.com/joke2k/django-environ このパッケージを使うことで容易に環境変数の設定ができます。 使い方 プロジェクト直下に.envファイルを作成し変数を追記していきます。 具体的にはこのような形で追記します。 代入する値はダブルクオートなどで囲む必要はないのでご注意ください。 DEBUG=on SECRET_KEY=your-secret-key 呼び出す時はこのようになります。 settings.py import environ env = environ.Env( # set casting, default value DEBUG=(bool, False) ) # reading .env file environ.Env.read_env() # False if not in os.environ DEBUG = env('DEBUG') # Raises django's ImproperlyConfigured exception if SECRET_KEY not in os.environ SECRET_KEY = env('SECRET_KEY') ngrok アプリを開発しているとスマホでも実際に挙動を確認したいということがあると思うのですが、フロントエンドとバックエンドを分離していると、ローカル環境ではAPIのドメインがlocalhostなため確認できません。 フロントにはアクセスできるけどバックエンドにはつながらないといったことになると思います。 このときにおすすめなのがngrokという開発ツールで、ngrokを使用することで一時的に作られるドメインとローカルのドメインを結びつけることができ、他のデバイスからアクセスすることができます。 https://ngrok.com/ 開発途中のアプリをngrokを使ってクライアントに見てもらうこともできるので、実務でも大活躍します。 会員登録なしでも使用することができるのですが、その場合一時的に発行されるドメインの有効期限が決まっています。 有効期限なしで使用したい場合は、無料プランの登録もできるので会員登録しておくことをおすすめします。 起動はターミナルを開いてngrok http ポート番号と入力するだけなので簡単にアプリを外部に向けて公開できます。 今後の計画 biby diaryはまだまだ未完成なので、継続的に開発をしていきます。 今は無料プランしかないので近々有料プランを追加する予定しているほか、bibyユーザーがペットへの寄付を外部に募ることができる機能も考えています。 追加して欲しい機能も絶賛募集中なので、ぜひご連絡ください。本記事のコメント欄もしくは、こちらのお問い合わせフォームからご要望いただくこともできます。 https://forms.gle/EJdXYLNWbeLfCccm9 形だけですがロードマップを公開しているので、気になる方はぜひご覧ください。 https://uichi.notion.site/5bead4691e344d6b946a09f9aa8c7d1f?v=3fccb1b559ef4c27ab0872db33a95690!%5BFireShot
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

言語はtypescript.CDKでLambda Layers の設定をする

やりたいこと  CDK でAPIを作成しており、axiosを使ってAPIを叩けるようにしたい。 ただ axios は lambda の中に用意されていないライブラリなのでインポートする必要がある。 Lambda layers とは Lambda で利用するライブラリを保存して利用可能にしてくれる AWS のサービスである。 これを活用することで axios が Lambda でも利用可能になる 他にもaxiosをLambdaで使う方法があるけどこれが便利 使い方 まずは Lambda Layer に保存するライブラリの保存箇所を作成する必要があります。 通常利用しているプロジェクトからlayer/nodejsというディレクトリを作成し、そこにパッケージを一通り定義して保存します。 mkdir layer cd layer mkdir nodejs cd nodejs layer/nodejsに移動したらyarn init をしてプロジェクトを作成。そこでライブラリをインストールして保存します。 yarn init yarn add --dev axios その後Lambda関数のインスタンス化をしている箇所でlayersを定義。Lambda関数を作成します。 そうするとLambda関数でインストールしたライブラリが利用可能になります。 const func = new lambda.Function(context ,functionName, { functionName: functionName, runtime: lambda.Runtime.NODEJS_12_X, // 下記追加 layers: [nodeModulesLayer], }); 最後に Youtubeチャンネルはじめてました! エンジニアの姿は十人十色。 今後も色んなエンジニア像を捉えて、それを目指す人たちの参考になる動画を作っています。 あと、プログラミングもメンタで教えているので興味があったらみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EBS のサイズを拡張しようとしたら `FAILED: failed: sfdisk --list /dev/nvme0n1` とか言われる

問題 / ファイルシステムが 100% になったので以下を参考にボリュームの拡張を行ってみたところ growpart コマンドのところでエラーが発生した。 ご存知でしたか?EC2インスタンスは再起動なしにディスクサイズ(EBSボリュームサイズ)を増やせます | DevelopersIO ボリュームサイズ変更後の Linux ファイルシステムの拡張 - Amazon Elastic Compute Cloud $ sudo growpart /dev/nvme0n1 1 failed [sfd_list:1] sfdisk --list --unit=S /dev/nvme0n1 FAILED: failed: sfdisk --list /dev/nvme0n1 原因 どうやら Disk 100 % ままコマンドを実行してしまったのがダメだったみたいで、原因となった巨大なファイルを一旦削除した後、同じコマンドを実行したら普通に正常終了した…というオチだった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LPをデプロイする技術

目的 仕事がら、短期のキャンペーンのランディングページ(LP)を構築することが多いです。AWSを使ってS3にコンテンツを置きCloudFrontで公開します。またデータベースとしてDyanamoDBを使っていてレコードを追加したりテーブルを追加したりします。ここではLPをデプロイするための簡単なCLIを紹介します。 各種AWS ここではaws-cliはjqはインストール済としています。 CloudFront まず既に登録されているCloudFrontの情報を取得します。無い場合は最初はがんばってコンソールから作りましょう(^^; export AWS_PROFILE=my-profile export AWS_DEFAULT_REGION=ap-northeast-1 export CF_ID=zzzz aws cloudfront get-distribution-config \ --id ${CF_ID}| \ jq -r ".DistributionConfig" \ > base.json 次に今回用に書き換えたいデータをJSONで準備します。ここで書き換えたいのは「ドメイン」と「S3のパス」と「コメント」です。 cloud_front.json { "cloud_front": { "domain": "xxxx.example.com" "origin_path": "/my-bucket/xxxx/public" "comment": "xxxx campaign" } } Rubyプログラムで書き換えます。 make.rb require "json" require 'securerandom' def read_json(path) File.open(path) do |j| hash = JSON.load(j) end end base = read_json("#{__dir__}/base.json") params = read_json(ARGV[0]) base["Aliases"]["Items"][0] = params["cloud_front"]["domain"] base["Origins"]["Items"][0]["OriginPath"] = params["cloud_front"]["origin_path"] base["Comment"] = params["cloud_front"]["comment"] base["CallerReference"] = SecureRandom.uuid puts JSON.pretty_generate(base) デプロイします。作成後IDが取得できるので、回収しておきます。 export AWS_PROFILE=my-profile export AWS_DEFAULT_REGION=ap-northeast-1 ruby make.rb ./cloud_front.json > cloud_front_result.json aws cloudfront create-distribution \ --distribution-config file://cloudfront_result.json \ | jq -r '.Distribution.Id' S3 ローカルにあるpublicディレクトリのファイルをS3なデプロイします。 またCloudFrontにキャッシュしているデータをクリアーしています。 ここで先ほど回収したIDを設定します。 export AWS_PROFILE=my-profile export AWS_DEFAULT_REGION=ap-northeast-1 export CAMPAIGN_NAME=xxxx export CF_ID=yyyy # デプロイ aws s3 sync public/ s3://my-bucket/${CAMPAIGN_NAME}/public # キャッシュクリアー aws cloudfront create-invalidation --distribution-id ${CF_ID} --paths "/*" | cat aws cloudfront get-distribution --id ${CF_ID} | jq -r '.Distribution.InProgressInvalidationBatches' DynamoDB レコード追加 キャンペーンテーブルにレコードを追加します。キャンペーン名とキャンペーン開始時間とキャンペーン終了時間を設定します。 data_json { "campaign_name": { "S": "xxxx" }, "start_at": { "N": "1577804400" }, "end_at": { "N": "1640962800" } } export AWS_PROFILE=my-profile export AWS_DEFAULT_REGION=ap-northeast-1 aws dynamodb put-item \ --table-name campaigns \ --item file://data.json \ --return-consumed-capacity TOTAL テーブル作成 例えば参加者を登録するテーブルを作ります。これは一度だけデプロイします。 table.json { "TableName": "paticipants_xxx", "AttributeDefinitions": [ { "AttributeName": "uuid", "AttributeType": "S" } ], "KeySchema": [ { "KeyType": "HASH", "AttributeName": "uuid" } ], "ProvisionedThroughput": { "WriteCapacityUnits": 1, "ReadCapacityUnits": 1 } } export AWS_PROFILE=my-profile export AWS_DEFAULT_REGION=ap-northeast-1 aws dynamodb create-table --cli-input-json file://table.json まとめ AWS CLIを使ってLPでよく使うコマンドなどをまとめてみました。 CloudFrontを作るところやDynamoDBのテーブルを作る作業は1度しか行いませんが、コンソールでやると意外と時間がかかります。 S3にアップロードしたり、DymanoDBのレコード修正は頻繁に行われます。これは絶対にCLIを用意しておくべきで、だいぶ作業がはかどるようになります。 シェルスクリプトとして準備しておくとよいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gem fog-awsを使用して画像アップロード機能を実装しようとしてハマった話

起こった出来事 現在作成しているアプリで画像アップロード機能を実装しようと思い、fog-aws carrierwave を組み合わせてS3にアップロードしようと思ったところでかなりの時間を消費していましたが無事に解決できましたので、それまでの過程を備忘録として記入したいと思います。 そもそも何故ダメだったのか argumenterror ( is not a recognized provider):`というエラーが出ており、そもそもプロバイダー自体が読み込めていないと判断し carriewave.rbとimage-uploder.rbでaws設定関連の記述ミスや環境変数がちゃんと適用されてるか、バケット名が間違えてないかなど思い当たるところは片っぱしから調べましたが全部あっており、完全にわからない状態でした。 carriewave.rb require 'carrierwave/storage/abstract' require 'carrierwave/storage/file' require 'carrierwave/storage/fog' CarrierWave.configure do |config| case Rails.env when 'development', 'test' config.storage = :file config.cache_storage = :file else config.fog_credentials = { provider: 'AWS', aws_access_key_id: Rails.application.credentials.aws[:access_key_id], aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key], region: 'ap-northeast-1', } config.fog_provider = 'fog/aws' config.storage :fog config.cache_storage = :fog config.fog_directory = 'バケット名' config.fog_public = false end end image-uploder.rb class ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick if Rails.env.production? storage :fog else storage :file end def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end def default_url(*_args) '/images/' + [version_name, 'default.png'].compact.join('_') end version :thumb do process resize_to_fit: [128, 128] end version :medium do process resize_to_fit: [360, 360] end def extension_whitelist %w[jpg jpeg gif png] end end 解決策 しかし見落としがありました、私が参考にしたサイトには記述されていませんでしたが config.asset_host = "https://s3.ap-northeast-1.amazonaws.com/バケット名" この記述をcarriewave.rbに書いているサイトがあり、まさかと思い試しに追記してみたらこれが動くんですよね。 carriewave.rb require 'carrierwave/storage/abstract' require 'carrierwave/storage/file' require 'carrierwave/storage/fog' CarrierWave.configure do |config| case Rails.env when 'development', 'test' config.storage = :file config.cache_storage = :file else config.fog_credentials = { provider: 'AWS', aws_access_key_id: Rails.application.credentials.aws[:access_key_id], aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key], region: 'ap-northeast-1', } config.fog_provider = 'fog/aws' config.storage :fog config.cache_storage = :fog:    config.asset_host = "https://s3.ap-northeast-1.amazonaws.com/バケット名" config.fog_directory = 'バケット名' config.fog_public = false end end 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon KendraのWebCrawlingを試してみる

Amazon Kendraとは AWSが提供するエンタープライズ向けのインテリジェント検索サービスです。 ユースケースとしては、社内のナレッジなどのコンテンツをKendraに渡すことで複雑化した文書を簡単に検索できるようにすることなどが挙げられます。 以下公式説明引用 Amazon Kendra は、機械学習を原動力とする高精度のインテリジェント検索サービスです。Kendra を使用すると、ウェブサイトやアプリケーションのエンタープライズ検索に対する考えが変わります。従業員や顧客は、企業内の複数の場所やコンテンツリポジトリにコンテンツが分散して保存されている場合であっても、目的のコンテンツを簡単に見つけることができます。 Amazon Kendra を使用すると、膨大な非構造化データを隅から隅まで検索する必要がなくなり、質問に対する適切な回答を必要なときに見つけることが可能となります。Amazon Kendra はフルマネージド型のサービスであるため、サーバーのプロビジョニングも、機械学習モデルの構築、トレーニング、デプロイも不要です。 WebCrawling機能について 先日の発表で、ウェブクローラを用いてWeb上にあるコンテンツのインデックスの作成・検索を行えるようになったようです。 本稿ではこちらを試してみます。 試してみる 検証用のWebサイト用意 S3の静的ホスティング機能を用い、クロールを行うWebサイトを準備します。 まず適当なhtmlファイルを作成し、公開します。 index.htmlにリンクを貼る形で作成しました。 サブディレクトリにファイルを配置し、ランダムな文章を記載しました。 ちなみに、robots.txtは以下の通りです。 robots.txt User-agent: amazon-kendra-customer-id-<AccountID> # Amazon customer's user agent/ID Allow: / # allow this directory 参考: CloudFrontの設定 KendraのWebCrawlingを扱う際は、データソースを https化しないとエラー になるようなので、CloudFrontを使用してhttps化します。 こちらの公式の案内を参考にしてご設定ください。 https化しない状態でデータソースを作成すると以下のようなエラーが表示されました。 Crawling isn't allowed for these sites: http://<S3 バケット名>.s3-website-us-east-1.amazonaws.com/. Remove the URLs and try your request again. - ValidationException - 400 Request ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXX インデックスの作成 Amazon Kendraのインデックスの作成を行います。 残念ながら東京リージョンにGAされていない(2021-07現在)為、バージニア北部リージョンを利用します。 適当にindex nameをつけ、ロールを新規に作りました。 今回は検証なので「Developer edition」を利用します。 データソースの作成 データソースを作成します。 作製したindexを選択して「Add Data source」を押下し、WebCrawlerを選択します。 設定値は以下の通りとしました。 Data source name: test-datasource Source SourceURLs:< CloudFrontのエンドポイント > Web proxy:設定せず Hosts with authentication :設定せず IAM role:Create a new roleから作成 Crawl scope:デフォルト Sync run schedule:Run on demand 完了したら以下のボタンからクローラを実行しましょう。 検証 クローラの実行が終了したら検索を試してみましょう。 「Search Console」を押下してください。 中心の検索窓に適当な単語を入力してみると、検索が行われて関連度が高い部分が表示されることがわかります。 「What is ○○ ?」などと検索すると解説記事を優先的に表示したり、表示順を検索ワードによって変えている?みたいです。 まとめ Amazon Kendraを用いることで、文書検索が簡単に行えました。たまったデータの検索ツールとして便利だと思います。 今回は試していませんが、他にもQ&Aが登録できたりするみたいです。 本稿ではHTMLで試しましたが、PDFも対応しているということで機会があれば試してみたいです。(日本語対応しないかなぁ)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】CodeDeployを実装した時のエラーとその解決まで

はじめに CodeDeployでデプロイ自動化を実装しようとした時、エラーがいくつか出たため記録として残します。 ほぼ凡ミスのような感じですが、参考になれば幸いです。 条件 macOS: "11.2.3 Big Sur" php artisan -V # > Laravel Framework 6.20.30 php -v # > PHP 8.0.8 nginx -v # > nginx version: nginx/1.12.2 git --version # > git version 2.32.0 1. DownloadBundleでエラー The specified key does not exist. (訳)指定されたキーは存在しません。 解決 これは単純に、.circleci/config.ymlディレクトリにs3へのアップロードの記載をしていなかったことが原因でした。 config.yml // 省略 - run: name: upload artifacts to s3 command: aws s3 cp lantern.zip s3://${AWS_S3_BUCKET_NAME} // 省略 参考 2. AfterInstallでエラー : ① Script does not exist at specified location (訳)スクリプトが指定された場所に存在しません 解決 原因は、AfterInstallイベントで使用されていたシェルスクリプトが反映されていなかったことでした。 scripts/after_install.shを追加したけれど、これが本番環境の方には反映されていませんでした。 なので、本番環境の方に同じようにディレクトリとファイルを作成。 # ① scriptsディレクトリ作成 $ mkdir scripts # ② scriptsディレクトリ内に移動して、after_install.shファイルを作成 [scripts] $ touch after_install.sh # ③ ファイルの中を編集 [scripts] $ vi after_install.sh 以下は、after_install.shファイルの記述内容です。 after_install.sh #!/bin/bash set -eux cd ~/Lantern/lantern php artisan migrate --force php artisan config:cache よし、これでいけるだろう!と思い、もう一度デプロイ。。。 3. AfterInstallでエラー : ② Could not open input file: artisan (訳)入力ファイルを開くことができませんでした:artisan artisanコマンドが使えないということは指定しているディレクトリが違うのかな...と思ったので確認。 案の定、ディレクトリを指定しているところが違っていたため以下のように修正。 after_install.sh - cd ~/Lantern/lantern + cd ~/Lantern/lantern-ssh-deploy php artisan migrate --force php artisan config:cache よし、これでいけるだろう!! 無事デプロイ完了! おわりに 改めて振り返ると、ディレクトリの指定ミスがちらほらありました...。 少しはAWSと仲良くなれた気がします。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSを活用した機械学習モデルの継続的な運用改善

初版: 2021年7月27日 著者: 河野泰隆, 橋本恭佑、株式会社 日立製作所 はじめに AI技術への注目の高まりに伴い、機械学習技術をビジネスへ適用するニーズが増えています。 ビジネスに機械学習技術を用いる場合、企業は機械学習モデルを作成・検証し実際の業務へ適用した後で、機械学習モデルの性能を監視します。 監視を通して機械学習モデルの精度劣化などを検知した場合は、企業は機械学習モデルの作成に利用したデータを見直し、機械学習モデルを再学習させることで、機械学習モデルの精度を維持する必要があります。 特に、データの傾向が時間経過とともに変化するビジネスにおいては、業務適用後に、データの傾向が機械学習モデルの作成・検証時のデータの傾向から変わる事象が何度も起こるため、再学習が繰り返し行われます。この投稿では、機械学習モデルの再学習効率化を目的として、パブリッククラウド(AWS)上で機械学習モデルの監視を通して精度劣化を検知し、精度劣化した機械学習モデルを自動で再学習させる方法を解説します。 今回作成する機械学習モデルの再学習機能の概要 題材とする問題の内容 この投稿では業務システムで利用されるコンピューティングリソースの性能値を予測する機械学習モデルの監視と再学習を題材とします。 今回の機械学習モデルは、業務システムで利用されるコンピューティングリソースのプロビジョニングを目的として、業務システムのワークロードから必要なコンピューティングリソースの性能値を推論します。 業務システムのワークロードは時間とともに変化するため、機械学習モデルの作成・検証時と推論時とでデータの傾向が変わり、推論精度が劣化する可能性があります。 そこで、機械学習モデルの推論の精度を監視し(機能1)、精度劣化を観測した場合に機械学習モデルを再学習させる(機能2)ことで、機械学習モデルの推論の精度を維持する仕組みための仕組みが必要となりました。 アーキテクチャ 上記の機能1、機能2を実現する、機械学習モデルの運用管理のためのシステムのアーキテクチャを図1に示します。「機能1: 推論精度監視」は、実際の性能値(正解ラベル)がアップロードされる都度、機械学習モデルの推論結果と正解ラベルを自動で比較し、精度を算出して、前もって定めた精度のしきい値を下回らないかを確認します。また、「機能2: 精度劣化検知時の再学習」は、推論精度が前もって定めた精度のしきい値を下回った場合に、機械学習モデルの推論精度が劣化したとみなして、自動で機械学習モデルを再学習させます。 Amazon SageMakerにより実現可能な範囲と追加開発項目 図 1に示したアーキテクチャをAWSで実現する場合の構成図の例を図 2に示します。機械学習モデルの学習ジョブ・モデル管理・バッチでの推論処理はAmazon SageMakerを用いて実装できます。一方で、今回の様なバッチでの推論精度監視には、SageMaker Model Monitorを利用できないため、別途開発が必要となります。 さらに、機械学習モデルの再学習についても、Amazon SageMakerの学習ジョブを起動する機能の開発が必要となります。今回は、バッチでの推論精度監視を、AWS Lambda、Amazon CloudWatch、Amazon SNSを利用して開発しました。また、精度劣化検知時の再学習機能を、AWS Step FunctionsとAWS Lambdaを利用して開発しました。 各機能の動作フロー 機能1:推論精度監視 (1) 推論パイプラインの実行 まず、推論パイプラインをAWS Step Functionsのワークフローとして実現します。ワークフローを定義したステートマシンのソースコードを下記に示します。推論パイプラインは、推論用データが到着してから、AWS Lambda関数(図2のLambda③)によって開始されます。AWS Lambda関数(図2のLambda③)は、推論用データに含まれる、予測対象となるコンピューティングリソースや性能値(以降、メタデータと呼称)を抽出して、推論パイプラインに送信します。 { "StartAt": "Resolve Model", "States": { "Resolve Model": { ・・・図2のLambda④を呼び出して、対象のモデルを検索 "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "FunctionName": "computer-pred-demo-model-resolver", "Payload": { "ThingName.$": "$.thing_name", "ResourceType.$": "$.res_type", "PredictionColumn.$": "$.pred_col" } }, "ResultPath": "$.resolve_result", "Next": "Model found or not" }, "Model found or not": { ・・・対象のモデルの有無を判定 "Type": "Choice", "Choices": [ { "Variable": "$.resolve_result.Payload.statusCode", "NumericEquals": 200, "Next": "Batch transform" } ], "Default": "Model not found" }, "Batch transform": { ・・・対象のモデルを使用して推論を実行 "Type": "Task", "Resource": "arn:aws:states:::sagemaker:createTransformJob.sync", "Parameters": { "ModelName.$": "$.resolve_result.Payload.body", "BatchStrategy": "MultiRecord", "MaxConcurrentTransforms": 1, "MaxPayloadInMB": 6, "TransformInput": { "CompressionType": "None", "ContentType": "text/csv", "DataSource": { "S3DataSource": { "S3DataType": "S3Prefix", "S3Uri.$": "States.Format('{}', $.inference_data_path)" } }, "SplitType": "Line" }, "TransformOutput": { "S3OutputPath.$": "States.Format('{}/output/', $.output_path)" }, "TransformResources": { "InstanceCount": 1, "InstanceType": "ml.m5.xlarge" }, "TransformJobName.$": "$$.Execution.Name" }, "End": true }, "Model not found": { "Type": "Fail", "Cause": "No model having ResourceType and PredictionColumn specified!" } } } AWS Lambda関数(図2のLambda③)からメタデータを受信した推論パイプラインは、"Resolve Model"でAWS Lambda関数(図 2のLambda④)を呼び出し、予測対象とする機械学習モデルをロードします。機械学習モデルにはメタデータがタグとして付与されているため、AWS Lambda関数ではこれらのタグを元に、推論に用いる機械学習モデルの有無を検索します。タグは機械学習モデルの学習パイプライン内で付与されます(詳細は後述します)。指定された機械学習モデルが存在する場合は、SageMakerのバッチ変換ジョブが呼び出されて("Batch Transform")、機械学習モデルに推論用データが入力され、推論を実行します。推論が実行されて結果がS3ストレージに格納されると、処理を終了します。 (2) 推論精度の算出 次に、精度算出を行う処理の実現方法について述べます。今回のサンプルでは簡単に、AWS Lambda関数(図 2のLambda⑤)を用いて実現しました。 精度算出のためのAWS Lambda関数は、S3ストレージのイベント通知機能を利用して、正解ラベルがS3ストレージへ格納されると実行されます。推論結果と正解ラベルをS3ストレージからロードして、あらかじめ実装したアルゴリズムに基づき精度を算出して、算出結果をCloudWatchに出力すると終了します。 (3) 推論精度の監視と再学習のトリガー 次に、推論精度の記録としきい値超過監視のためのCloudWatchの設定画面を図 4に示します。図 4の上側のグラフは、縦軸を精度、横軸を時刻としており、青のグラフが推論精度、赤のグラフがあらかじめ定めたしきい値を示します。たとえば図 4ではしきい値を0.8と設定しており、青の凡例で示した精度が0.8を下回った場合、CloudWatchはAWS SNSへメッセージを送信します。AWS SNSはCloudWatchからメッセージを受信すると、再学習を実行する様にAWS Lambda関数へのトリガーをかけます。 再学習のトリガーをかけるAWS Lambda関数では、再学習に用いる学習用データの範囲を選択します。学習用データを決定した後で、学習用データのcsvファイルを生成し,学習パイプラインを実行します。 機能2:精度劣化検知時の再学習 学習パイプラインをAWS Step Functionsのワークフローとして実現します。ワークフローを定義したステートマシンのソースコードを下記に示します。 学習パイプラインは、AWS Lambda関数(図2のLambda⑥)から再学習リクエストを受信すると開始されます。AWS Lambda関数(図2のLambda⑥)は、再学習の対象とする機械学習モデルのメタデータ(予測対象となるコンピューティングリソースやメトリクス)を学習パイプラインへ送信します。 { "StartAt": "Decide targets", "States": { "Decide targets": { ・・・図2のLambda①を実行して、対象のモデルを検索 "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "FunctionName": "computer-pred-demo-train-target-decider", "Payload.$": "$" }, "ResultPath": "$.target_result", "Next": "Target found or not" }, "Target found or not": { ・・・対象のモデルの有無を判定 "Type": "Choice", "Choices": [ { "Variable": "$.target_result.Payload.statusCode", "NumericEquals": 200, "Next": "Train models" } ], "Default": "Target not found" }, "Train models": { ・・・対象のモデルを再学習して保存 "Type": "Map", "ItemsPath": "$.target_result.Payload.body.targets", "Iterator": { "StartAt": "Resolve experiment", "States": { "Resolve experiment": {・・・図2のLambda②を実行して、再学習の対象とする機械学習モデルの過去の学習の記録を取得 "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "FunctionName": "computer-pred-demo-experiment-resolver", "Payload.$": "$" }, "ResultPath": "$.experiment_result", "Next": "Train model" }, "Train model": { ・・・対象のモデルを再学習させる "Type": "Task", "Resource": "arn:aws:states:::sagemaker:createTrainingJob.sync", "Parameters": { "AlgorithmSpecification": { "TrainingImage": "012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/computer-pred-training:latest", "TrainingInputMode": "File", "MetricDefinitions": [ { "Name": "RMSE", "Regex": "RMSE=(.*?);" }, { "Name": "R2", "Regex": "R2=(.*?);" } ] }, "EnableManagedSpotTraining": true, "CheckpointConfig": { "S3Uri.$": "$.checkpoint_path" }, "OutputDataConfig": { "S3OutputPath.$": "$.output_path" }, "StoppingCondition": { "MaxRuntimeInSeconds": 3600, "MaxWaitTimeInSeconds": 3600 }, "ResourceConfig": { "InstanceCount": 1, "InstanceType": "ml.m5.xlarge", "VolumeSizeInGB": 30 }, "RoleArn": "arn:aws:iam::012345678901:role/SageMakerExecutionRole", "InputDataConfig": [ { "DataSource": { "S3DataSource": { "S3DataDistributionType": "ShardedByS3Key", "S3DataType": "S3Prefix", "S3Uri.$": "States.Format('{}', $.train_data_path)" } }, "ChannelName": "train", "ContentType": "text/csv" }, { "DataSource": { "S3DataSource": { "S3DataDistributionType": "ShardedByS3Key", "S3DataType": "S3Prefix", "S3Uri.$": "States.Format('{}', $.test_data_path)" } }, "ChannelName": "testing", "ContentType": "text/csv" } ], "HyperParameters": { "fit_args.$": "$.fit_args", "feature_importance": "true" }, "ExperimentConfig": { "ExperimentName.$": "$.experiment_result.Payload.body.experiment" }, "TrainingJobName.$": "States.Format('{}-{}', $$.Execution.Name, $.id)" }, "ResultPath": "$.train_result", "Next": "Save Model" }, "Save Model": { ・・・再学習させたモデルを保存する "Type": "Task", "Resource": "arn:aws:states:::sagemaker:createModel", "Parameters": { "PrimaryContainer": { "Image": "012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/computer-pred-inference:latest", "Environment": {}, "ModelDataUrl.$": "$.train_result.ModelArtifacts.S3ModelArtifacts" }, "Tags": [ { "Key": "ThingName", "Value.$": "$.thing_name" }, { "Key": "ResourceType", "Value.$": "$.res_type" }, { "Key": "PredictionColumn", "Value.$": "$.pred_col" } ], "ExecutionRoleArn": "arn:aws:iam::012345678901:role/SageMakerExecutionRole", "ModelName.$": "$.train_result.TrainingJobName" }, "ResultPath": "$.save_result", "End": true } } }, "End": true }, "Target not found": { "Type": "Fail", "Cause": "No target!" } } } 学習パイプラインが再学習リクエストを受信すると、学習パイプラインは"Decide targets"で、別のAWS Lambda関数(図 2のLambda①)を実行して、再学習リクエストと同時に受信したメタデータと一致するタグを持つ機械学習モデルを取得します。機械学習モデルを取得できた場合("Target found or not")、学習パイプラインは"Resolve experiment"でAWS Lambda関数(図 2のLambda②)を実行して、再学習の対象とする機械学習モデルの、過去の学習の記録を取得します。そして、再学習のための学習用データを用いて機械学習モデルの学習処理を"Train model"で実行します。ここで、SageMakerの学習ジョブが実行されます。 SageMakerの学習ジョブが完了すると、学習パイプラインは"Save Model"で、メタデータを機械学習モデルのタグとして付与して、機械学習モデルをS3ストレージに保存し、再学習の記録をモデル管理に保存します。 動作検証 検証シナリオ 機能検証のため、推論用データを毎日23時にアップロードするAWS Lambda関数を用意して、作成したサンプルに入力しました。また、精度評価は毎日0時に前日の推論結果に対する精度評価を実施しました。推論精度劣化を起こすダミーデータを用意して、精度がしきい値を下回る現象(しきい値超過)を起こして再学習させました。再学習に利用する学習用データの範囲は、推論精度がしきい値を下回った時刻から1週間前までとしました。 動作検証 まず、AWS Lambda関数が23時に推論用データを入力すると、推論パイプラインが実行されます。この推論パイプラインでは、対象の機械学習モデルをロードし、データを入力して推論を実行し、推論結果をS3ストレージに格納します。推論が完了した時のイメージを図5に示します。図5の下の左側のステートマシンは推論パイプラインであり、実行が成功した部分が緑色で示されています。 次に、0時になるとAWS Lambda関数が実行されて、前日の推論結果に対する精度を評価します。精度を評価してCloudWatchで可視化した画面を図6に示します。図6の上側のグラフの縦軸が推論精度、横軸が時刻です。推論精度(青色の凡例)がしきい値(赤色の凡例。今回は0.8)を下回ったことから、精度劣化を検知したことがわかります。このような状態になると、CloudWatchはAWS SNSへメッセージを送信して、AWS SNSが再学習のトリガーをかけると、AWS Lambda関数によって再学習のための学習パイプラインが実行されます。 最後に、再学習時の学習パイプラインの実行完了イメージを図7に示します。図7の下の左側のステートマシンが学習パイプラインを示しており、実行が成功した部分が緑色で示されています。 図5、図6、図7で示した結果より、今回作成したサンプルの推論精度監視機能と、精度劣化検知時の再学習機能の動作を確認できました。 おわりに 本記事では機械学習モデルの監視を通して精度劣化などを検知した後の、機械学習モデルの再学習を、AWSのSageMakerの機能を用いて実現する方法について紹介しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Amplify の Todo アプリチュートリアルをやってみた

目的 こちらのAWS Amplify チュートリアルを通して AWS の Amplify に触れ、主にどういったサービスがあるのかをざっくりと知る事。 主な使用技術 React Redux Toolkit Material-Ui React-Hook-Form Amplify 公式サイトでは言語がJS、状態管理がHooks、入力フォームがuseStateでの管理となっておりました。 学習目的の為少しでもオリジナリティーを持たせることを考え、言語をTS、状態管理をRedux Toolkit、入力フォームをReact-Hook-Formに変更し簡単なバリデーションを付けました。 UI ログイン / サインアップ 使用したAmplifyのサービス API Auth Hosting 認証のためのログイン・サインアップページについて 自分でデザインなども含めてログインページをどうしても作らなければいけない場合でなければ、amplifyのサービスでコンポーネントが用意されているのでインポートすればそのまま使えてしまいます。 使用したいコンポーネントをインポートして AmplifyAuthenticatorでラップします。 今回はサインアップに必要なユーザーデータを、Username, Email, Passwordの3つにしました。 App.tsx import Amplify from 'aws-amplify'; import { AmplifyAuthenticator, AmplifySignUp, AmplifySignOut, } from '@aws-amplify/ui-react'; const App: React.VFC = () => { return return ( <AmplifyAuthenticator> <AmplifySignUp slot="sign-up" formFields={[ { type: 'username', }, { type: 'email', inputProps: { required: true, autocomplete: 'username' }, }, { type: 'password', inputProps: { required: true, autocomplete: 'new-password' }, }, ]} /> <AmplifySignOut /> </AmplifyAuthenticator> ); } GraphQL これまでAPI通信を行う際には、基本的なHTTPメソッドである get, post, put, delete などを用いてコードを書いてきましたが、GraphQLでは記述の仕方が異なります。 Query getに当たる部分 Mutation post, put, deleteに当たる部分。 引数などを用いて変更を加える。 Subscription 情報を観察し、自動的に変更を反映させる。Twitterのタイムラインなどに使われる機能。 (今回は使用しない) これら全て初めて見るものでありそれらがどういったものなのかを知る為にドキュメントを最初にひたすら読み込む必要があった。しかし今回のようなTodoアプリ程度であれば、Subscription機能を用いないのでQueryとMutationの使い方を公式ドキュメントで参考しデータの取得、更新を行う事ができた。 感想 GraphQLでは型関係の対処の所がとても辛い部分であり、どうしても解決できない部分はanyを使用してしまっている部分があるので、今後アップデートしていきたい。 ただ最終的になんとか動くものは作れた事と、amplifyのサービスを実際に使用して大まかにでも知ることができた事は今後にとても役に立つと感じた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配布されているCloudFormationテンプレートをいい感じに書き換える小ネタ

こんにちは。AWS歴3年生の人です。 CloudFormation、便利ですよね。AWSから公式配布されているテンプレートや各ソフトウェアメーカーから公式配布されているテンプレートを使ってサクっと環境を再現することも簡単です。 ただ、会社などの業務利用しているAWSアカウントなどは「ガードレール」といった品質・セキュリティ統制が整備されていることもあり、そう簡単にサクッといかないのでその場合に、書き方を変えるだけで回避できるポイント、あるあるのチェックポイントの一例をご紹介します。 YAMLからJSONに、JSONからYAMLに変換する 「自分はYAML派なんだけど配布されているテンプレートがJSONで書かれている・・・」など CloudFormationを流す前からやる気がそがれてしまいそうですが、以下の方法で簡単に変換ができます。 1.CloudFormation > デザイナー を起動する 2.タブ「テンプレート」を選択(下記図の①) 3.変換前のコードをペーストする(下記図の②) 4.右上の「テンプレート言語の選択」を変換後を選択する(下記図の③) 無事に変換ができました! ロールのインラインポリシーをカスタマー管理ポリシーに書き換える インラインポリシーは、ロール作成と同時に記述できて便利なのですが、後からポリシー単体で管理したい場合などがあるので、カスタマー管理ポリシーに書き換えます。 変更前 インラインポリシーの例 AdminGroupRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Join - '' - - !Ref pUserPoolId - '-AdminGroupRole' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: '' Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: 'sts:AssumeRoleWithWebIdentity' Condition: StringEquals: 'cognito-identity.amazonaws.com:aud': !Ref pIdentityPoolId 'ForAnyValue:StringLike': 'cognito-identity.amazonaws.com:amr': authenticated Policies: - PolicyName: vodaws-admin-group-policy PolicyDocument: Version: 2012-10-17 Statement: - Sid: VisualEditor0 Effect: Allow Action: - 's3:PutObject' - 's3:DeleteObject' Resource: !Sub 'arn:aws:s3:::${pInputBucket}/public/*' 書き換えポイント Policies以下を削除し、ロールとは別にカスタマー管理ポリシーとして作成するように書き換えます ロールからは作成したポリシーを参照するように記述します 変更後 ロールとカスタマー管理ポリシーを分けた後の例 AdminGroupPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: vodaws-admin-group-policy PolicyDocument: Version: 2012-10-17 Statement: - Sid: VisualEditor0 Effect: Allow Action: - 's3:PutObject' - 's3:DeleteObject' Resource: !Sub 'arn:aws:s3:::${pInputBucket}/public/*' AdminGroupRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Join - '' - - !Ref pUserPoolId - '-AdminGroupRole' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: '' Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: 'sts:AssumeRoleWithWebIdentity' Condition: StringEquals: 'cognito-identity.amazonaws.com:aud': !Ref pIdentityPoolId 'ForAnyValue:StringLike': 'cognito-identity.amazonaws.com:amr': authenticated ManagedPolicyArns: - !Ref AdminGroupPolicy ロールとポリシーを分けることで、ひとつのリソースの記述行が短くなり、あとからポリシーを別のロールにアタッチすることもできるようになりました。 ロールにPermissions Boundaryをつけたい場合 配布されているCloudFormationはPermissions Bounbaryがないことが多いので、必要に応じて以下のようにロールに追加します。 前述のロールにPermissions Boundary(ポリシー名PermissionBoundary)をつけた例 AdminGroupRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Join - '' - - !Ref pUserPoolId - '-AdminGroupRole' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: '' Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: 'sts:AssumeRoleWithWebIdentity' Condition: StringEquals: 'cognito-identity.amazonaws.com:aud': !Ref pIdentityPoolId 'ForAnyValue:StringLike': 'cognito-identity.amazonaws.com:amr': authenticated ManagedPolicyArns: - !Ref AdminGroupPolicy PermissionsBoundary: !Join - "" - - "arn:aws:iam::" - !Ref "AWS::AccountId" - ":policy/PermissionBoundary" その他 命名ルールが定められている場合は従いましょう。 cognitoのidentityPoolIdのCloudFormationで、「identityPoolIdの作成が先か、アタッチするロールの作成が先か」のニワトリタマゴ問題にぶつかります。 AWSから配布されているCloudFormationではカスタムリソースを使って動的にロールのマッピングを行って解決しています。カスタムリソースが使えない場合は、最初にどちらかの設定を空で作成し、あとから手作業でAuthRole,UnauthRoleにロールアタッチ、もしくはAssumeRolePolicy(信頼関係)を修正しています。(他に正解があるかもしれません・・・) 最後に 配布されているCloudFormationを作ってくれている方には、ただただ感謝の一言です。書き方の勉強にもなるのでいつも参考にさせてもらっています。ありがとうございます! この記事で参考にしたCloudFormation Creating a secure video-on-demand (VOD) platform using AWS https://aws.amazon.com/blogs/media/creating-a-secure-video-on-demand-vod-platform-using-aws/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Cloud9 を OCI Always Free インスタンスでセットアップして無料で使う

はじめに 最近ビッグバンド演奏をリモート録音、ミックスしたりしているのですが、困ったことが。たぶん自分の使い方が悪いのですが、DAW(Studio One)で録音した音にモニターヘッドホンで鳴らしていた他の楽器の音がのってしまっている...! 最悪録音し直さないとならないけどなんとか録音した音だけ抜き出せないかと調べていたらSpleeterというpythonのライブラリでそんなようなことができるらしいということを知り、(python全然できないけど)とりあえず試してみよう!と思いました。 そして、どうせpythonの開発環境作るなら前から気になっていたAWS Cloud9とかいうブラウザのIDE使ってみたい!無料で!と思い立ってのこの記事です。 調べてみるとAWS Cloud9そのものの利用料はかからないようでしたが、このバックで稼働するEC2インスタンスのお金がかかる、というものでした。 ただAWS Cloud9はリモートSSHサーバーを設定できるようで、この機能を使えばOracle Cloud Infrastructure(OCI)のAlways Freeインスタンスでセットアップ=無料で使えるのではないかと考え、セットアップしてみました。 個人的なメモかつ、セットアップ完了後に書いているのであくまで参考程度にしてください。また設定の仕方、使い方によっては課金される可能性もありますので、自己責任でお願いします。 前提 前提として以下のアカウント登録などのセットアップは完了しており、各コンソールを利用できる必要があります。 - AWSアカウント登録 - Oracle Cloud Infrastructureアカウント登録 OCIインスタンス事前準備 まずはCloud9で利用するインスタンスをセットアップします。 OCIコンソールにログイン。インスタンスの画面へ。 インスタンスの作成をクリックします。まず「イメージとシェイプ」は変更する必要があります。「編集」をクリックしてください。 「シェイプの変更」をクリックしてください。(ARM版インスタンスもOCIではAlways Freeリソースとして利用できますが、Cloud9側がARM対応していないようです。(参考:AWS Cloud9 SSH環境ホスト要件)) 「専門と前世代」を選択し、[VM.Standard.E2.1.Micro]を選択します。 イメージはこのままでもいいですが、最新OSにしておきます。(AWS Cloud9はUbuntuでもセットアップできるようですが、使い慣れているRHEL系の最新バージョンにしています。) 「秘密キーの保存」から秘密鍵をダウンロードします。他は設定してもしなくてもよいです。画面下の「作成」をクリックし、インスタンスを作成します。 作成したインスタンスに[Always Free]と出ていたらAlways Freeインスタンスとして作成できていると思います。この画像の右にインスタンスに割あたっているPublicIPが表示されていると思いますので、これを使用してSSHします。 インスタンスのセットアップ OSの初期設定と合わせてCloud9前提条件から必要な作業を実施します。 インスタンスにSSHしてまずはrootユーザーで初期設定を行っておきます。 /bin/bash # インスタンスにログイン(macOSターミナル等SSHクライアントから接続してください) chmod 400 <ダウンロードした秘密鍵> ssh -i <ダウンロードした秘密鍵> opc@<OCIインスタンスのPublicIP> # rootユーザーにスイッチ sudo su - # とりあえずアップデート yum -y update # 言語設定を日本語に localectl list-locales #ja_JP.utf8がない yum install glibc-langpack-ja #インストール localectl set-locale ja_JP.utf8  #ja_JP.utf8にセット source /etc/locale.conf #反映 locale # 確認 # timezoneをJSTに timedatectl set-timezone Asia/Tokyo #セット date #確認 # 次作業のためにopcユーザーへ exit opcユーザーでcloud9の事前作業を行います。(opcユーザーのhomeディレクトリで実施) /bin/bash # Pyhon3の確認 python -V #おそらくインストール済み # Node.jsのインストール sudo curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash . ~/.bashrc nvm install node node --version #確認 # Cloud9起動ディレクトリの権限変更(opcユーザーhomeディレクトリ) sudo chmod u=rwx,g=rx,o=rx ~ # Cloud9インストール sudo curl -L https://raw.githubusercontent.com/c9/install/master/install.sh | bash このターミナル接続はまだ使うのでこのままにしておいてください。 AWS Cloud9環境作成 AWSマネジメントコンソールへアクセスし、Cloud9のページを開きます。 「Create Environment」をクリックします。 任意の名前をつけて[Next Step] ここで[Create and run in remote server]を選択し、接続先情報として先程作成したOCIインスタンスのユーザー名[User]、PublicIP[Host]を入力します。 [Copy key to clipboard]でクリップボードに公開鍵をコピーします。 先程ログインしたままになっているSSHセッション(切断されている場合は再度入り直してください)で公開鍵を登録します。 /bin/bash echo '<クリップボード貼り付け>' >> ~/.ssh/authorized_keys マネジメントコンソールに戻り、「Next Step」をクリックします。検証が走り、正常に完了すると「Create environment」が押せるようになるのでクリックして完了です。 終わりに 今回これをやってみようと思ったときに一番ハマったのがOCIのインスタンス作成でした。最初はOracle Cloud Deveroper Imageを選択してインスタンス作成したのですが、なぜかSSHログインできずに何度も作り直して再度接続を試して...(もちろんセキュリティリストの設定は0.0.0.0/0:22で開いているデフォルトの状態で、ちゃんとそのサブネットにインスタンスが配置されていたのでその部分では問題なかったです。) インスタンス名に日本語があると接続できないという情報は見つけましたが入っておらず、インスタンス名変更したり、Image別のものにしたりいろいろしましたが、極力何も手をいれないデフォルトの状態で作成してみた結果、うまくいきました。単にSSH接続確認するのが早すぎたのか?うーん... とりあえずできたので、AWS Cloud9の使い方とか調べながらpythonでSpleeter使ってみたいと思います! 参考URL Qiita記事:AWS Cloud9をSSH接続で使うまでの手順 AWS公式ドキュメント:SSH 環境ホスト要件 AWS公式ドキュメント:AWS Cloud9 インストーラを使用する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ECSのタスク定義で環境変数をS3から取得しようとしてハマったポイント

やりたかった事 { "family": "", "containerDefinitions": [ { "name": "", "image": "", ... "environment": [ { "name": "variable", "value": "value" } ], ... } ], ... } こんな感じで定義していた環境変数をS3バケットに入れた.envファイルから取得したい。 公式ドキュメントをみるとenvironmentFilesっていうのでいけそう。 やってみる ECSで紐づけていたIAMロールに別途S3へのアクセスを付与する必要がある。 ここは本題ではないので割愛。こちらの記事を参考にする。 最初IAMロールにS3の権限なくて怒られたけど、↑を参考にやったら怒られなくなった。 しかしログを確認してみると。migrationでこけてる。。なぜだ。。 原因 タスク定義で指定する環境変数はenvironment→environmentFilesの優先順位で動いている。 つまりenvironmentで指定していると上書きしてしまう挙動だった。 間違い "containerDefinitions": [ { ... "environmentFiles": [ { "value": "arn:aws:s3:::sample-bucket/prod.env", "type": "s3" } ], "environment": [], ... } ] 正しい "containerDefinitions": [ { ... "environmentFiles": [ { "value": "arn:aws:s3:::sample-bucket/prod.env", "type": "s3" } ], "environment": null, ... } ] "environment": nullにする事で上書きされる事なくmigration実行されるようになった。 どうやらDockerの仕様らしい ECSのタスク定義で設定したenvironmentとenvironmentFilesは以下のように対応している(はず)。認識間違ってたらコメントください。 environment environmentFiles docker run --env docker run --env-file docker run --env 'HOGE=hoge' --env-file='prod.env' こうした場合は--envでの指定が優先される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CLIの振り返り ~CloudFormation入門編~

本記事の目的 JAWS-UG CLI オンラインハンズオンの復習として、ハンズオンで取り上げられたAWSサービスに関する用語と、使用したAWS CLI コマンドを箇条書きでまとめる。 今回はCloudFormation入門編を取り上げる。 主なAWSサービス用語 テンプレート AWSのリソース情報を記述したファイル。 スタック テンプレートを用いてプロビジョニングされるリソースの集合体。 主なAWS CLI コマンド一覧 テンプレートの検証 $ aws cloudformation validate-template [--template-body file://<テンプレートのパス>] ※このコマンドを実行した後、echo $?を実行する事で直前のコマンドの終了ステータスを参照し、0が表示されればばテンプレートは有効である事が確認できる。 スタックの作成 $ aws cloudformation create-stack --stack-name <スタックの名前> [--template-body file://<テンプレートのパス>] スタックの一覧表示 $ aws cloudformation list-stacks ※スタックのステータスがCREATE_COMPLETEと表示されている場合、スタックは正常に作成されている。 ※スタックのステータスがROLLBACKと表示されている場合、スタックにエラーが生じているため、スタック関連イベントを参照し原因を特定する。 スタック関連イベントの詳細表示 aws cloudformation describe-stack-events [--stack-name <スタックの名前>] スタックの削除 $ aws cloudformation delete-stack --stack-name <スタックの名前> 参考文献 AWSCLIコマンドリファレンス:CloudFormation JAWS-UG CLI専門支部:ハンズオン(簡易版): CloudFormation入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloudfrontのホスティングでページリフレッシュすると403が出るときの対処法

単純に自分がエラーページ対処をするのを忘れていました。 CloudfrontからCreate error page responseをクリックしてその中で設定を行います。 Customize error response で Yesを選んでResponse page pathに/index.html、response codeに200を選びます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Lookout for Metricesにおけるメトリクス数の注意点

Amazon Lookout for Metricsとは 一言で言うと、メトリクスをもとに機械学習を使用して、メトリクスの異常値を自動的に検出するサービスです。 S3等に上げられたメトリクスをソースとし、自動的に分析を行ってくれます。 以下公式説明文 Amazon Lookout for Metrics は、機械学習 (ML) を使用して、売上高や顧客獲得率の急激な低下など、ビジネスおよび運用データの異常 (標準からの外れ値など) を自動的に検出および診断します。数回クリックするだけで、Amazon Lookout for Metrics をAmazon S3、Amazon Redshift、Amazon Relational Database Service (RDS) などの一般的なデータストアや、Salesforce、Servicenow、Zendesk、および Marketo などのサードパーティーの SaaS アプリケーションに接続できます。これにより、ビジネスにとって重要なメトリクスの監視を開始します。Amazon Lookout for Metrics は、これらのソースからのデータを自動的に検査して準備し、異常検出に使用される従来の方法よりも高速かつ正確に異常を検出します。また、検出された異常に関するフィードバックを提供して、結果を調整し、時間の経過とともに精度を向上させることもできます。Amazon Lookout for Metrics を使用すれば、同じイベントに関連する異常をグループ化し、潜在的な根本原因の概要を含むアラートを送信することで、検出された異常を簡単に診断できます。また、異常を重大度の順にランク付けするため、ビジネスにとって最も重要なことを優先できます。 料金は「一か月あたりに分析を行ったメトリクス数に応じて請求されます。 メトリクス メトリクスあたりのUSD 最初の1000メトリクス 0.75USD 1,000 メトリクス~5,000 メトリクス 0.50 USD 5,000 メトリクス~20,000 メトリクス 0.25 USD 20,000 メトリクス~50,000 メトリクス 0.10 USD 50,000 メトリクス超え 0.05 USD 引用(2021/07現在): この「分析を行ったメトリクス数に応じて課金される」という点がミソで、時間課金ではないので一度に大量のメトリクスを投入すると一気に料金が上がってしまうことが考えられます。 メトリクスとは Amazon Lookout for Metricesのメトリクスの総数は、Measureと、Dimentionに含まれる値(一意)を乗算した数となります。 後程解説しますが、絶対に適当な値をDimensionsに設定しないでください。 (コンソールに表示されるメッセージ引用) Amazon Lookout for Metrics derives metrics from on your dataset's measures, dimensions names, and dimension values. The total number of metrics is the number of measure names times the number of unique combinations of dimension name and dimension value. The number of metrics analyzed can vary from interval to interval. 分析を行う上での注意点 仮に以下のようなデータを使用するとします。 EventTime EventID EventName UserName Value Data 2021/XX/XX 00:03:10 ajt6gait7 EventA hoge 743 2021/XX/XX 00:03:00 2021/XX/XX 00:15:45 bjys9gkit EventB hoge 384 2021/XX/XX 00:15:00 2021/XX/XX 00:29:30 oit8bisr4 EventC hoge 204 2021/XX/XX 00:29:00 2021/XX/XX 00:29:43 gti8rmvbf EventB huga 764 2021/XX/XX 00:29:00 2021/XX/XX 00:31:12 rjugyalot EventB hogehuga 322 2021/XX/XX 00:31:00 2021/XX/XX 00:40:36 jut75ljga EventA hogehuga 743 2021/XX/XX 00:40:00 2021/XX/XX 00:41:38 git6ugt74 EventC hoge 384 2021/XX/XX 00:41:00 MeasuresとDimensionsを Measures:Value Dimensions:UserName とした場合、Dimensionsの値は「hoge,huga,hogehuga」の3種類となるため、 1(Measures)×3(Dimensions)=3メトリクスという計算になり料金は0.75*3=2.25USDとなります。 次にMeasuresとDimensionsを Measures:Value Dimensions:UserName,EventName とした場合、Dimensionsの値は「hoge,huga,hogehuga」と「EventA,EventB,EventC」の3*3種類となるため、 1(Measures)×3(Dimensions)×3(Dimensions)=9メトリクスという計算になり料金は0.75*9=6.75USDとなります。 このように、メトリクス数はMeasuresの数とDimensionsの数・値に大きく依存します。 基本的に本サービスで扱うようなデータは大量(数万行)であることが考えられる為、Dimensionsの設定を間違えてしまいますとすぐに数万メトリクスに到達してしまいます。 仮に5万行のデータのDimensionsを今回のEventIDのような一意な値にしてしまうと約$90001と大変高額になってしまう計算なので、サービスを利用する場合はMeasuresとDimensionsをあらかじめ考えて使用するようにしましょう。 1000*0.75+4000*0.5+15000*0.25+30000*0.1=9500 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSにzabbix5.0をインストールするの大変だったから

初投稿で見辛いところがあります。次から頑張ります。 環境や前提条件 AWS(無料枠内で)セキュリティグループ→デフォルト EC2インスタンス(仮想サーバー) OS:Red Hat Enterprise Linux8 ZABBIX5.0LTS DB:MYSQL WEBサーバ:Apache インストールの前に AWSのインスタンスを起動する awsのコンソールからログイン→ec2インスタンス→右クリック「インスタンスの開始」→しばらくしてインスタンスが起動する→下の方にスクロール→パブリックipアドレスをコピーする。 自分はteratermを使っているのでec2ユーザーでコンソールにログインしておく。 自分のpcでteratermを起動。 さっきコピーしたパブリックipアドレスを張り付ける。 ec2-userでログイン。 インストール 公式サイト様 →※こちらの公式ページを参考に。しかしほかのページも見といたほうがいい。 公式ページの下に動画があるのでそれも結構わかりやすかった。バージョンが違ったので手順はちょっと違ったけど見ていたほうがいいかも。 公式ページはダウンロードするときにバージョンを選んでいくと下の方にインストールするためのコマンドを載せてくれる。丁寧だ。 !インストールするためのコマンドについて注意! ユーザーはrootユーザーでやりました。 一般ユーザーだとsudoとかつけてインストールしないといけなかったのでちょっと面倒。 [ec2-user@ip-honyahonya ~]$ sudo su ※honyahonya数字です、どこまで隠せばいいかわからなかったのでとりあえず。 →これであなたもrootユーザー! 公式サイトの「2.Install and configure Zabbix server for your platform」のa→bまで無事終わったらちょっと止まってください。 !注意!ここでデータベースのmysqlをインストールしておきます。 この手順は公式サイトになかったので気を付けてください。 zabbixの構築手順... 上記のサイト様を参考にさせていただきました。 #dnf -y install @mysql:8.0/server #systemctl start mysqld #systemctl enable mysqld #mysql_secure_installation ※mysqlの場所です、、 ls /usr/share/doc/zabbix-server-mysql/ 公式サイトの手順cから順番に打ち込んでいきます。 公式サイト様 c. Create initial database # mysql -uroot -p 上記コマンドの後にpasswordと出てくると思うのですが、これはenter押せばmysql>のモードにいけました。 [root@ip-honyahonya ec2-user]# mysql -uzabbix -p zabbix Enter password:  →公式サイト手順cの「mysql> create user zabbix@localhost identified by 'password';」の「password」を打っています。 ....色々出てくるけど省略してます。 Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases; →データベースの中を確認すると.... +--------------------+ | Database | +--------------------+ | information_schema | | zabbix | +--------------------+ 2 rows in set (0.00 sec) これはテーブルができているのか? mysql> show tables; +----------------------------+ | Tables_in_zabbix | +----------------------------+ | acknowledges | | actions | | alerts | | application_discovery | できてるっぽい。 公式サイトに戻って「d. Configure the database for Zabbix server」からじゅんぐりコマンドを打っていきます。 参考にさせていただきました vi /etc/zabbix/zabbix_server.conf このzabbix_server.confは863行ほどあったのでここから下の文字を探して一番前の#を消して、書き直していきます。探すとき「/DBHost」と探したい文字の前に/を付けると簡単に探せます。 DBHost=localhost DBName=zabbix DBUser=zabbix DBPassword=password →公式サイト手順cの「mysql> create user zabbix@localhost identified by 'password';」の「password」 SELinuxを無効にしておかないとだめらしい。※公式サイトにはなかった手順です↓ vi /etc/selinux/config configの中身を下のように#を外しておきます。 SELINUX=disabled でrebootすれば無効になります。 vi /etc/php-fpm.d/zabbix.conf ファイルの書き換え↓日付の変更です。 php_value[date.timezone] = Asia/Tokyo apache 起動 systemctl start httpd サービス有効化 systemctl enable zabbix-server systemctl enable httpd systemctl enable zabbix-agent サービス起動 systemctl start httpd systemctl start zabbix-server systemctl start zabbix-agent できているか確認 下記コマンドは指定したurlのファイルをダウンロードします。 現在指定しているのはzabbixのweb画面からアクセスするためのページなのでhtmlやcssの形式のファイルが表示されればokです。 curl -L localhost/zabbix ここまでやってchromeに http://パブリックipアドレス/zabbix/ を打ち込んでみたが画面に表示されなかった。 AWS側の操作 →AWSの方でEC2にアタッチしているセキュリティグループのインバウンドルールを変更。 AWSコンソール画面にログイン →検索窓でEC2を検索 →EC2のダッシュボード→インスタンス(実行中)を選択 →自分のインスタンスを選択する。 →下の方にスクロール →「詳細」「セキュリティ」「ネットワーキング」「ストレージ」… とタブが並んでいるかと思います。 →「セキュリティ」をクリック。 下にスクロールして、セキュリティグループが青色の文字でリンクになっているのでクリック。 →次ページで下にスクロール・・・「インバウンドルール」のタブを選択 右端の「edit inbound rules 」 → タイプ http ソース→マイIP 右下のルール保存から更新できます。 ここまでやってchromeに http://パブリックipアドレス/zabbix/ を打ち込んでみると 表示されました! 他にも詰まったところがあったのですが、別記事に書きます。 引用元や参考元 https://qiita.com/ysato0730/items/a705062f2b7ff1b34ab7 https://qiita.com/akiko-pusu/items/c7a39a5319e663bc89a3 https://www.site24x7.jp/blog/zabbix-construction/ https://teratail.com/questions/48027 https://teratail.com/questions/81066
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AKIBA.MAD 実践から学ぶ AWS Lambdaのテスト戦略 参加レポート

かなり遅くなってしまったのですが、今回は2021/4/22に実施されたクラスメソッドさんのLambdaのテストに関するイベントに参加した際のメモを書きました。 メモは当日話を聞きながら残していたのですが、時間が経って記憶が曖昧な部分もありますので、何か間違いがあればご指摘ください。 クラスメソッドMADとは Modern Application Developmentチームの略で、最新のアーキテクチャを積極的に活用するサーバレス・コンテナ導入支援チームだそうです。 本イベントはクラスメソッドMADチームを中心に、クラスメソッドでサーバレスシステムを開発してるエンジニアが集まって座談会をしつつ、 参加者のサーバレスやテストに関する利用状況のアンケートを取ったり、質問に答えたりといった内容でした。 アンケートは勉強会の合間合間で取られていたのですが、文章で合間に書くと読みづらくなってしまうので、最後の方にまとめようかなと思います。 テスト用語の解説 Mock テスト対象が呼び出す依存コンポーネントを擬似的に作成すること Spy テスト対象が呼び出す依存コンポーネントへの呼び出し回数や入力値などを観測すること Fake インメモリDBやLocalStackなど、実サービスと同等の動きをするツールを利用すること ↑このあたりの用語は切り分けづらいので、各ツール(Jestなど)ごとに切り分けが異なっていたりすることもあるとのことでした。 (特にMockとSpyが一緒くたにされていたりなど) テストの種類 クラスメソッドでは以下のように定義して行っているとのこと。 Unit Test 各Lambdaのfunctionごとのテスト Integration Test 各Lambdaの入出力に対するテスト Lambda Aの出力 → Lambda Bの入力等の部分まではやりそうなイメージでした。 (でないとUnit Testとの差別化ができないので) E2Eテスト 実際のサービス同士を組み合わせたテスト 例:Test Code→Amazon API Gateway→Lambda→DynamoDB 実際のテストのやり方 DynamoDB等はローカルにFakeを作るか、開発環境を用いてテスト E2Eテストの後に全体を通してのテストは行う 最後はやはりスプレッドシートでテストケースを作ることももちろんある E2Eテストも可能なところは自動化している 例:Auth0が関わる部分はトークンを自動で発行してE2Eテストをする等 悩みポイント 非同期部分のテストは難しい 特にIoT CoreはAWSアカウントごとにエンドポイントが1つだったりするので、テストがしづらい 質疑応答コーナー どこまで単体テストをするのか? クリーンアーキテクチャの層を意識して層単位でUnit Testを書いてるとのこと。 Entities Usecases Controllers・Gateways・Presenters Frameworks & Drivers(Web・UI・External Interfaces・DB・Devicesなど) 設計や実装の段階で階層構造を意識していれば、テストの責務もそれに合わせて切り分けられるということですね。 テストデータやテストテーブルの管理について DynamoDBに流し込むテストデータの管理はJest等のツールでデプロイした環境にテストデータを流し込んでやってる CRUDのテストをやる時にデータを混ぜないようにしている Fakeのデータは事前にまとめて流し込んでいる ある程度テストをパラレルで動かせない部分が出るのは仕方ないという割り切りも必要 NoSQL Workbenchでテーブル定義を吐き出したりしてできなくはないが、実際のリソースと吐き出したテーブル定義での二重管理問題が出そう CI/CDでどこまでやるか CI/CDではユニットテストと、書けていればIntegrationテストまでを行う E2Eテストは非同期通信を伴うので、テストだと上手くいかなかったりする 時間がかかったり、回線が弱いと思いもよらぬところで落ちたりもする テストに使える外部ツール 以下の2つが話題に出ていたので紹介しておきます。 MinIO https://min.io/ https://openstandia.jp/oss_info/minio/ Go言語で実装されたAmazon S3クラウド・ストレージ・サービスと互換性のあるオブジェクト・ストレージ・サーバー。 本番環境ではAmazon S3を使用するが、開発時には、MinIOで開発/テストを行うなどの用途で利用することができる。 LocalStack pro クラウドアプリケーションを開発するための使いやすいテスト/モックフレームワークを提供してくれるサービス。 実際のAWSクラウド環境と同じ機能とAPIを提供するローカルマシン上のテスト環境を起動できる。 プロジェクト内でのテストの強制について VS CodeやEclipseのテストは強制はしてないが、CI/CDは強制している。 手元でのテストは強制はしていないが、実際は各自の手元でテストを一度回してからデプロイする人が多いとのこと。 サービスレベルで落ちる場合のテストについて DynamoDBやAmazon S3そのものが落ちた場合は、テストコードレベルでは対処はできないことがあるので無理はしない だが、ある程度エラーハンドリングで解決できるようにはしている 500エラーが返るような時は追加調査を行う AWSのリージョン単位で死ぬようなケースはある程度諦めも必要ですね。 単体テストで外部との通信を行うか ユニットテストは外部との通信は行わず、できる範囲で行う Googleのユニットテストの指標でもそうなっているとのこと https://testing.googleblog.com/ FakeはあくまでIntegrationテストまでで、シナリオテストで実際のデータを使う シナリオテストは実際のログインからのシナリオ等を作るテストのこと たとえば、Integrationテストまででは認証はテストに含めないみたいな割り切りも大事 E2Eテストは得られるものも多いが、時間がかかるのと、どこがダメだったのかを調べるにしても影響範囲が大きいので、Integrationテストの割合を徐々に増やしたいとのこと。 また、Fakeを作ってテストするのはDynamoDBだけなど、そこも割り切るのがいい DynamoDBをマルチテーブルかシングルテーブルのどちらで用いてるかでFakeの使い方が変わってくるが、DynamoDBは現実のUsecaseと密結合な設計がいいと言われてる。 参加者アンケート 参加者のサーバレステストコードの利用状況について サーバレスをよく使っていて、なおかつテスト書いてる人は2割ぐらい サーバレスのコードは普段Pythonで書いてる人が6割ぐらい、Nodeが3割ぐらい 正直Nodeがもうちょっと多いかなと思ってました。 NodeはTypeScriptでも書けるので、フロントエンドとの相性が良さそう Pythonは機械学習やBoto3ライブラリを使ったAWSの別リソースとの連携などと相性が良さそう 自分が普段書いてるRubyはサーバレスにおけるメリットはあまり無いかなと・・・ 世の中のテスト状況 実際にサーバレスのテストを現在書いてる人がどの辺のテストを書いてるのかというアンケートでは、UnitTest(Mock/Spy)とE2Eテスト(AWS上)が50%超え 単体テストや結合テストを通しても、E2Eテストは最終的に必須になるという感じでした。 特に単体のテストから動作が変わりやすい点としては、IAMの権限で失敗や、SQS等のメッセージングサービスを組み合わせたりするタイミングが挙げられてました。 実際に使われてるテストツール LambdaテストはPytestが50%・Jestが27% ここはPython勢が多いという事前の結果を踏まえてですかね。 APIのvalidationはUnit Testで行っているが、E2Eでも400とか404とかのステータスが出ているかはある程度確認するようにしているとのこと まとめ マイクロサービス同士を組み合わせたサービス全体としての品質を担保するために重要な部分は当然手動でテストを行う必要がありますが、各マイクロサービスごとの品質の担保は自動テストでもかなり実現できそうだなと感じました。 マイクロサービス単位で動作検証ができていれば、全体テストではある程度細かい部分はマイクロサービスのテストに任せられるのはいいですね。 疎結合やカプセル化の原則はマイクロサービスやそのテストでも活かせそうだと感じました。 ただ、マイクロサービスにすると中身が隠蔽化されてしまい、開発のチーム分け等の都合によっては別のマイクロサービスの内部実装を全く知らないということもよくあると思うので、エラー出力の切り分けやそれに対するエラーハンドリングはより気をつけないといけなさそうだなと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【6日目】AWS認定ソリューションアーキテクト合格までの道

ネットワークサービス VPCと外部との接続 AWS Direct Connect(DX) オンプレミス環境(自社環境)とAWS(VPC)の間を専用線で接続するサービス 通常はオンプレ環境とVPCを一対一で接続する プライベート環境と高速ネットワークで安定した通信を行うことができる Direct Connectゲートウェイを利用することでオンプレ環境と全リージョンの複数VPCを一対多で接続可能となる Site to Site VPN(Virtual Private Network) IPsecを用いてオンプレミス環境とAWS間を接続する方法 DXと比べると品質は下がるが低コストかつ短期間で導入できるメリットがある IPsec(Security Architecture for Internet Protocol) IPパケットを暗号化して盗聴や改ざんを防ぐ技術 仮想プライベートゲートウェイ(VGW:Virtual Private Gateway) オンプレ環境とAWSを接続するときに利用するゲートウェイ DXやSite to Site VPN接続するときにあらかじめ用意しておく VPCピアリング 異なるVPC間をプライベート接続するサービス インターネットを経由せずにAWSプライベートネットワーク内で直接通信できる 異なるAWSアカウント間でも接続可能だがネットワークアドレスが一致or重複する場合は接続できない VPCはAWS上に独立したプライベートネットワーク空間を作成するため本来はVPC間での直接的な通信はできない VPCピアリングとDirect Connectゲートウェイの接続例 AWS Transit Gateway VPC内にハブ機能を持ったゲートウェイを配置するサービス ネットワークを簡素化し一元管理することができる VPCエンドポイント AWSのプライベートネットワークから各種AWSサービスへのアクセスやAPIコールを行うことができる 通常はインターネットを経由して行う ゲートウェイ型 ルートテーブルに指定されたターゲットを追加する方法 Amazon Simple Storage Service(S3)/Amazon DynamoDBなどのAWSサービスへプライベート接続することができる インターフェイス型(AWS PrivateLink) APIコールに対してインターネットを経由せずにプライベート接続する方法 Amazon CloudWatch/Amazon Simple Queue Service(SQS)などのAPIコールに対してプライベート接続することができる API(Application Programing Interface) 他サービスのソフトウェアの一部を共有できる仕組み 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DynamoDB入門編(チュートリアル実施)

概要 DynamoDBのチュートリアルを実施する。 本記事はAWS公式のチュートリアルを実施しております。 1. TBL作成 下記4つのTBLを作成。 ○作成テーブル ・ProductCatalog ・Forum ・Thread ・Reply ProductCatalog Forum Thread Reply TBL状態を確認 4つのTBLができている事を確認しましょう。 1. データのロード 各TBLにサンプルデータをロードします。 サンプルデータ Forum.json { "Forum": [ { "PutRequest": { "Item": { "Name": {"S":"Amazon DynamoDB"}, "Category": {"S":"Amazon Web Services"}, "Threads": {"N":"2"}, "Messages": {"N":"4"}, "Views": {"N":"1000"} } } }, { "PutRequest": { "Item": { "Name": {"S":"Amazon S3"}, "Category": {"S":"Amazon Web Services"} } } } ] } ProductCatalog.json { "ProductCatalog": [ { "PutRequest": { "Item": { "Id": { "N": "101" }, "Title": { "S": "Book 101 Title" }, "ISBN": { "S": "111-1111111111" }, "Authors": { "L": [ { "S": "Author1" } ] }, "Price": { "N": "2" }, "Dimensions": { "S": "8.5 x 11.0 x 0.5" }, "PageCount": { "N": "500" }, "InPublication": { "BOOL": true }, "ProductCategory": { "S": "Book" } } } }, { "PutRequest": { "Item": { "Id": { "N": "102" }, "Title": { "S": "Book 102 Title" }, "ISBN": { "S": "222-2222222222" }, "Authors": { "L": [ { "S": "Author1" }, { "S": "Author2" } ] }, "Price": { "N": "20" }, "Dimensions": { "S": "8.5 x 11.0 x 0.8" }, "PageCount": { "N": "600" }, "InPublication": { "BOOL": true }, "ProductCategory": { "S": "Book" } } } }, { "PutRequest": { "Item": { "Id": { "N": "103" }, "Title": { "S": "Book 103 Title" }, "ISBN": { "S": "333-3333333333" }, "Authors": { "L": [ { "S": "Author1" }, { "S": "Author2" } ] }, "Price": { "N": "2000" }, "Dimensions": { "S": "8.5 x 11.0 x 1.5" }, "PageCount": { "N": "600" }, "InPublication": { "BOOL": false }, "ProductCategory": { "S": "Book" } } } }, { "PutRequest": { "Item": { "Id": { "N": "201" }, "Title": { "S": "18-Bike-201" }, "Description": { "S": "201 Description" }, "BicycleType": { "S": "Road" }, "Brand": { "S": "Mountain A" }, "Price": { "N": "100" }, "Color": { "L": [ { "S": "Red" }, { "S": "Black" } ] }, "ProductCategory": { "S": "Bicycle" } } } }, { "PutRequest": { "Item": { "Id": { "N": "202" }, "Title": { "S": "21-Bike-202" }, "Description": { "S": "202 Description" }, "BicycleType": { "S": "Road" }, "Brand": { "S": "Brand-Company A" }, "Price": { "N": "200" }, "Color": { "L": [ { "S": "Green" }, { "S": "Black" } ] }, "ProductCategory": { "S": "Bicycle" } } } }, { "PutRequest": { "Item": { "Id": { "N": "203" }, "Title": { "S": "19-Bike-203" }, "Description": { "S": "203 Description" }, "BicycleType": { "S": "Road" }, "Brand": { "S": "Brand-Company B" }, "Price": { "N": "300" }, "Color": { "L": [ { "S": "Red" }, { "S": "Green" }, { "S": "Black" } ] }, "ProductCategory": { "S": "Bicycle" } } } }, { "PutRequest": { "Item": { "Id": { "N": "204" }, "Title": { "S": "18-Bike-204" }, "Description": { "S": "204 Description" }, "BicycleType": { "S": "Mountain" }, "Brand": { "S": "Brand-Company B" }, "Price": { "N": "400" }, "Color": { "L": [ { "S": "Red" } ] }, "ProductCategory": { "S": "Bicycle" } } } }, { "PutRequest": { "Item": { "Id": { "N": "205" }, "Title": { "S": "18-Bike-204" }, "Description": { "S": "205 Description" }, "BicycleType": { "S": "Hybrid" }, "Brand": { "S": "Brand-Company C" }, "Price": { "N": "500" }, "Color": { "L": [ { "S": "Red" }, { "S": "Black" } ] }, "ProductCategory": { "S": "Bicycle" } } } } ] } Replyjson { "Reply": [ { "PutRequest": { "Item": { "Id": { "S": "Amazon DynamoDB#DynamoDB Thread 1" }, "ReplyDateTime": { "S": "2015-09-15T19:58:22.947Z" }, "Message": { "S": "DynamoDB Thread 1 Reply 1 text" }, "PostedBy": { "S": "User A" } } } }, { "PutRequest": { "Item": { "Id": { "S": "Amazon DynamoDB#DynamoDB Thread 1" }, "ReplyDateTime": { "S": "2015-09-22T19:58:22.947Z" }, "Message": { "S": "DynamoDB Thread 1 Reply 2 text" }, "PostedBy": { "S": "User B" } } } }, { "PutRequest": { "Item": { "Id": { "S": "Amazon DynamoDB#DynamoDB Thread 2" }, "ReplyDateTime": { "S": "2015-09-29T19:58:22.947Z" }, "Message": { "S": "DynamoDB Thread 2 Reply 1 text" }, "PostedBy": { "S": "User A" } } } }, { "PutRequest": { "Item": { "Id": { "S": "Amazon DynamoDB#DynamoDB Thread 2" }, "ReplyDateTime": { "S": "2015-10-05T19:58:22.947Z" }, "Message": { "S": "DynamoDB Thread 2 Reply 2 text" }, "PostedBy": { "S": "User A" } } } } ] } Thread.json { "Thread": [ { "PutRequest": { "Item": { "ForumName": { "S": "Amazon DynamoDB" }, "Subject": { "S": "DynamoDB Thread 1" }, "Message": { "S": "DynamoDB thread 1 message" }, "LastPostedBy": { "S": "User A" }, "LastPostedDateTime": { "S": "2015-09-22T19:58:22.514Z" }, "Views": { "N": "0" }, "Replies": { "N": "0" }, "Answered": { "N": "0" }, "Tags": { "L": [ { "S": "index" }, { "S": "primarykey" }, { "S": "table" } ] } } } }, { "PutRequest": { "Item": { "ForumName": { "S": "Amazon DynamoDB" }, "Subject": { "S": "DynamoDB Thread 2" }, "Message": { "S": "DynamoDB thread 2 message" }, "LastPostedBy": { "S": "User A" }, "LastPostedDateTime": { "S": "2015-09-15T19:58:22.514Z" }, "Views": { "N": "0" }, "Replies": { "N": "0" }, "Answered": { "N": "0" }, "Tags": { "L": [ { "S": "items" }, { "S": "attributes" }, { "S": "throughput" } ] } } } }, { "PutRequest": { "Item": { "ForumName": { "S": "Amazon S3" }, "Subject": { "S": "S3 Thread 1" }, "Message": { "S": "S3 thread 1 message" }, "LastPostedBy": { "S": "User A" }, "LastPostedDateTime": { "S": "2015-09-29T19:58:22.514Z" }, "Views": { "N": "0" }, "Replies": { "N": "0" }, "Answered": { "N": "0" }, "Tags": { "L": [ { "S": "largeobjects" }, { "S": "multipart upload" } ] } } } } ] } 実行 ~/develop/study/aws/dynamo/migration/sampledata $ tree . ├── Forum.json ├── ProductCatalog.json ├── Reply.json └── Thread.json 0 directories, 4 files ~/develop/study/aws/dynamo/migration/sampledata $ aws dynamodb batch-write-item --request-items file://ProductCatalog.json { "UnprocessedItems": {} } ~/develop/study/aws/dynamo/migration/sampledata $ aws dynamodb batch-write-item --request-items file://Forum.json { "UnprocessedItems": {} } ~/develop/study/aws/dynamo/migration/sampledata $ aws dynamodb batch-write-item --request-items file://Thread.json { "UnprocessedItems": {} } ~/develop/study/aws/dynamo/migration/sampledata $ aws dynamodb batch-write-item --request-items file://Reply.json { "UnprocessedItems": {} } データの確認 4つのTBL共にデータがロードされている事を確認。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む