20201227のAWSに関する記事は16件です。

サブネットをインターネットと接続する方法

■ インターネットに接続するための回線を引き込む

 Amazon VPCにおいて、あるサブネットをインターネットに接続するには、「インターネットゲートウェイ(Internet Gateway)」を用いる。

 これは、「自分のネットワークにインターネット回線を引き込む」というイメージの作業となる。

■ 作業手順

① メニューから[インターネットゲートウェイ]をクリックして開き、[インターネットゲートウェイの作成]をクリックする。

Image from Gyazo

② [作成]をクリックする。

Image from Gyazo

③ 作成したインターネットゲートウェイにチェックを付けて、[アクション]をクリックし、[VPCにアタッチ]メニューを選択する。

Image from Gyazo

④ 結びつける先のVPC領域を尋ねられるので、使用可能なVPCに「前回作成したVPC領域」を選択して、[インターネットゲートウェイのアタッチ]をクリックする。

Image from Gyazo

■ ルーティング情報

 ネットワークにデータを流すためには、「ルーティング情報」と呼ばれる設定が必要となる。この設定は、「ルートテーブル(Route Table)」などと呼ばれる。

 インターネットで使われる「TCP/IP」というプロトコルでは、データを細切れにした「パケット(Packet)」という単位で、データを送受信されている。

 パケットは、さまざまな「ヘッダー情報」と「データの実体」を含んでいる。ヘッダー情報の1つに、「宛先IPアドレス」がある。

 TCP/IPでは、ネットワーク機器である「ルーター」が、この「宛先IPアドレス」を見ながら、「もっとも宛先IPアドレスに近い方のネットワーク」へと、次々とパケットを送信していき、最終目的地までパケットを到達させる。

 パケットをうまく到達させるために、「宛先IPアドレスの値が、いくつのときには、どのネットワークに流すべきか」設定する必要がある。この設定のことを「ルートテーブル」という。

 ルートテーブルは、宛先アドレスを流すべきネットワークの入り口となるルーターという書式で設定する。

 宛先アドレスのことを「ディスティネーション」という。「流すべきネットワーク先」は「ターゲット」などという名称でよぶ。

■ Amazon VPCでルートテーブルを設定する。

◯ デフォルトのルートテーブル

 VPC領域を作成したあとは、デフォルトのルートテーブルが作られる。そしてサブネットを作成したときには、そのデフォルトのルートテーブルが適用される。サブネットを作成した後に、どのようなルートテーブルが設定されているのかは次のように確認できる。

■ 作業手順

 サブネットに対して設定されているルートテーブルを確認する。

① [サブネット]メニューを選択して、確認したいサブネットにチェックを入れて[詳細]から[ルートテーブル]の値を確認する。

Image from Gyazo

② [ルートテーブル]メニューを選択して、先ほど確認した[ルートテーブル]にチェックをつける。その後に[ルート]タブを選択して[設定値]を確認する。

Image from Gyazo

■ デフォルトゲートウェイをインターネットに向けて設定する。

 このままでは、「10.0.0.0/16以外の宛先のパケット」は全て破棄されるため、インターネットに接続することができません。

 インターネットに接続するために、インターネットゲートウェイを作成しました。そこで、「10.0.0.0/16以外の宛先のパケット」をインターネットゲートウェイに転送するように、ルートテーブルを変更することで、インターネットと通信できるようになります。

 具体的には、「0.0.0.0/0の範囲の宛先のパケットは、インターネットゲートウェイに転送する」という設定をルートテーブルに追加する。

 「0.0.0.0/0」は、すべてのIPアドレス範囲を示している。つまり、「0.0.0.0/0に対するターゲットの設定」は、「転送先が何も設定されていない時の、デフォルトの転送先」を示している。このデフォルトの転送先を「デフォルトゲートウェイ」をよぶ。

■ 作業手順

 パブリックサブネットをインターネットに接続する。

① [ルートテーブル]メニューを選択して、[ルートテーブルの作成]をクリックする。

Image from Gyazo

② 名前タグに「パブリックルートテーブル」を、VPCに「前回作成したVPC領域」をそれぞれ入力・選択して[作成]ボタンをクリックする。

Image from Gyazo

③ 先ほど作成したルートテーブルにチェックを入れて[サブネットの関連付けの編集]タグを選択し、[サブネットの関連付け]をクリックする。

Image from Gyazo

④ 割り当てたいサブネットにチェックを入れて、[保存]ボタンをクリッっくする。

Image from Gyazo

⑤ [ルート]タグを選択して、[ルートの編集]をクリックする。

Image from Gyazo

⑥ [ルートの追加]をクリックして、送信先に「0.0.0.0/0」を、ターゲットに「igwから始まる宛先(インターネットゲートウェイ)」をそれぞれ入力・選択する。

Image from Gyazo

⑦ [サブネット]メニューを選択して、関連付けしたサブネットにチェックを入れる。その後に[ルートテーブル]タグを選択して、[ルートテーブル]が先ほど作成したパブリックルートテーブルに変わっていることを確認する。また、[0.0.0.0/0]がインターネットゲートウェイに設定されていることも確認する。

Image from Gyazo

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでのドメイン取得からhttps化まで

はじめに

AWSで独自ドメインを取り、証明書を発行しALBをhttps化するまでのメモ

やり方

ドメイン取得

  1. AWSコンソールからRoute53を開き、右側の「ドメイン」をクリックします。
  2. 上部にある「ドメインの登録」ボタンをクリックします。
  3. 好きなドメインを検索し、「カートにいれる」→「続行」→「注文を完了」と進めます。

ドメインの購入には最大で1日ほど時間がかかるようですが、自分の場合は30分ほどで手続きが完了しました。手続きが完了すると、登録してあるメールアドレスにメールが届きます。

証明書の作成

  1. AWSコンソールからCertificate Managerを開きます。
  2. 「証明書をリクエスト」をクリックし、パブリック証明書にチェックを入れ次のページへ進みます。
  3. ドメイン名に先ほど購入したドメインを入力します。この時「*.<ドメイン名>」という形式で入力してください。このようにしないと、サブドメインを切った時に、この証明書を使い回すことができなくなります。
  4. 「検証方法」ではEメールにチェックを入れます。(DNSの登録では、自分はうまく行きませんでした。。。)
  5. しばらく待つと、登録してあるメールアドレスに検証用のメールが届くので、案内にしたがって検証を完了します。

ALBの作成

  1. AWSコンソールからロードバランサー」のページを開き、上部の「ロードバランサーの作成」をクリックします。
  2. 「Application Load Balancer」を選択し、「ロードバランサーのプロトコル」をhttpsにします。スクリーンショット 2020-12-27 22.22.32.png
  3. セキュリティ設定の構成で、「証明書タイプ」を「ACM から証明書を選択する」にし、「証明書の名前」で先ほど作成した証明書を選択します。

Route53の設定

今のままでは、ALBのドメイン名と証明書のドメイン名が異なっているので、証明書のエラーが発生するので、ALBのドメインにエイリアスを貼ります。

  1. AWSコンソールからRoute53を開き、左側の「ホストゾーン」
  2. 先ほど取得したドメインをクリックし、「レコードを作成」ボタンをクリックします。
  3. ルーティングポリシーから「シンプルルーティング」を選択し、次へ進みます。この画面にならない時は、「ウィザードへ切り替える」をクリックしてください。スクリーンショット 2020-12-27 22.40.08.png 5. 「シンプルなレコードを定義」をクリックし、サブドメインを入力します。「エンドポイント」は「Application Load BalancerとClassic Load Balancerへのエイリアス」にし、先ほど作成したALBを選択して保存します。スクリーンショット 2020-12-27 22.43.57.png

以上で作業は終わりです。
Route53に登録したレコードの値で、httpsでアクセスできます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ALBにCognitoの認証をかける

はじめに

ベーシック認証のようなものを、ALBとCognitoを使って実現する話です。
こんなやつ。

スクリーンショット 2020-12-27 21.28.29.png

やり方

userpoolの作成

AWSコンソールからcognitoを開き、右上の「ユーザープールを作成する」ボタンをクリックします。
スクリーンショット 2020-12-27 21.05.25.png

プール名を入力し、「デフォルトを確認する」をクリックし、「プールの作成を」クリックします。
設定を変更したい方は、お好みで変更してから作成してください。
スクリーンショット 2020-12-27 21.05.48.png

続いて、アプリクライアントを作成します。左側のメニューのアプリクライアントから作成します。設定内容はデフォルトのままで大丈夫です。
スクリーンショット 2020-12-27 21.13.23.png

続いて、ALB認証時に使うドメインの登録をします。同じく左メニューの「ドメイン名」から好きなドメインを登録してください。

最後に、左メニューから「アプリクライアントの設定」をクリックし、以下のようにします。は実際に認証をかけるALBの物に書き換えてください。

  • 有効なIDプロバイダの「Cognito User Pool」にチェック
  • 認証されているOAuthフローの「Authorization code grant」にチェック
  • 許可されている OAuth スコープの「openid」にチェック
  • コールバックURLに「https:///oauth2/idpresponse」を入力

スクリーンショット 2020-12-27 21.27.05.png

ALBの設定

httpsで接続できるALBが必要です。作成の仕方はこちらを参考にしてください。https://qiita.com/ggg-mzkr/items/9924f729df15762dda28

AWSのALB一覧から、認証をかけるALBを選択し、したのメニューから「リスナー」タブを開きます。そして、認証をかけたいリスナーに対して「ルールの表示/編集」をクリックします。

詳細画面が出てくるので、上にある鉛筆マークをクリックすると、リスナーの横に鉛筆マークが現れるので、そちらをクリックします。

スクリーンショット 2020-12-27 21.39.39.png

スクリーンショット 2020-12-27 21.40.02.png

「アクションを追加」 → 「認証」 を選択し、先ほど作ったuserpoolとアプリクライアントを選択して、右上の「更新」ボタンを押します。

画面表示

この時点で、ALBへアクセスを行うと、冒頭のような画面にリダイレクトされます。
ログインするためのユーザーは、cognitoの「ユーザーとグループ」から作成することができます。

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【非公式】re:Invent 2020 IoT関連発表リスト

はじめに

re:Invent 2020では大量の発表がありましたが、その中からIoT関連のものを収集しました。

元データ

元データとなったリストはこちらにあります。元データが更新されたらこちらも随時更新します。

ご注意

このリストは公式のものではありません。記載されている情報の正確さについては保証しません。サービスや機能の利用に当たっては、必ずAWS公式の資料を参照してください。

関連情報

re:Invent 2020で行われたIoT関連の講演はAWSから公式にリストが公開されています。ぜひこちらもご覧ください。

協力のお願い

見逃し、勘違いなどあるかと思います。何か気付いたことがあればこちらからIssue、PRなどお寄せください。

re:Invent 2020 IoT関連発表リスト

種別 名称 内容 ステータス URL 追加日
新機能 AWS Wavelength 東京リージョンサービス開始 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-announces-general-availability-aws-wavelength-tokyo/ 2020/12/26
新サービス AWS Location 位置情報活用 パブリックプレビュー https://aws.amazon.com/jp/location/ 2020/12/26
新機能 AWS IoT Greengrass Version 2.0 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-greengrass-2-0-provides-open-source-edge-runtime-capabilities-building-operating-iot-device-software/ 2020/12/26
新サービス AWS IoT Core for LoRaWAN マネージドLoRaWANネットワークサーバ GA(東京以外) https://aws.amazon.com/jp/about-aws/whats-new/2020/12/introducing-aws-iot-core-lorawan/ 2020/12/26
新サービス FreeRTOS LTS開始 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-freertos-long-term-support/ 2020/12/26
新サービス AWS IoT EduKit IoT教育 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/introducing-aws-iot-edukit/ 2020/12/26
新サービス Amazon Lookout for Equipment 既存センサーデータにAWSのMLを摘要 プライベートプレビュー https://aws.amazon.com/jp/lookout-for-equipment/ 2020/12/26
新サービス Amazon Lookout for Vision 画像から欠陥・異常を検出 パブリックプレビュー https://aws.amazon.com/jp/lookout-for-vision/ 2020/12/26
新サービス AWS Panorama Appliance 現地に設置する画像解析システム パブリックプレビュー https://aws.amazon.com/jp/panorama/ 2020/12/26
新サービス Amazon Monitron センサー+ゲートウェイの完成品 GA(東京以外) https://aws.amazon.com/jp/monitron/ 2020/12/26
新機能 AWS IoT SiteWise テーブルチャートが利⽤可能に GA https://aws.amazon.com/jp/iot-sitewise/ 2020/12/26
新サービス AWS IoT Core Device Advisor フルマネージドのデバイステスト パブリックプレビュー https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-core-device-advisor-now-available-in-preview/ 2020/12/26
新機能 AWS IoT Core Apache Kafkaへのデータ配信に対応 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-core-adds-the-ability-to-deliver-data-to-apache-kafka-clusters/ 2020/12/26
新機能 AWS IoT Core Amazon Sidewalkに対応 GA(東京以外) https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-amazon-sidewalk-integration-for-aws-iot-core/ 2020/12/26
新機能 AWS IoT SiteWise 対応プロトコルを拡充 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-sitewise-launches-support-for-modbus-tcp-ethernet-ip-protocols/ 2020/12/26
新サービス AWS IoT SiteWise Edge エッジ側でSiteWiseの前処理を行う パブリックプレビュー https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-aws-iot-sitewise-edge-preview/ 2020/12/26
新サービス AWS IoT SiteWise plugin for Grafana SiteWiseからGrafanaに連携して可視化 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/introducing-aws-iot-sitewise-plugin-for-grafana/ 2020/12/26
新機能 AWS IoT Analytics Apache Parquet形式をサポート GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-analytics-store-processed-iot-data-data-stores-apache-parquet-format/ 2020/12/26
新機能 AWS IoT Analytics エラーハンドリング機能を強化 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/enhanced-error-handling-capabilities-in-aws-iot-analytics-data-processing-pipelines/ 2020/12/26
新機能 AWS IoT Device Management Fleet Hub(フリート管理、可視化) パブリックプレビュー https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-device-management-introduces-fleet-hub/ 2020/12/26
新サービス AWS IoT Device Defender ML Detect MLによる自動異常検出 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-aws-iot-device-defender-ml-detect-public-preview/ 2020/12/26
新機能 AWS IoT Device Defender カスタムメトリクス対応 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-device-defender-adds-support-for-custom-metrics/ 2020/12/26
新機能 AWS IoT Events/SiteWise アラームに対応 パブリックプレビュー https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-support-alarms-preview-aws-iot-events-aws-iot-sitewise/ 2020/12/26
新機能 AWS SiteWise アラームに対応 パブリックプレビュー https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-support-alarms-preview-aws-iot-events-aws-iot-sitewise/ 2020/12/26
新機能 FreeRTOS セルラーLTE-Mをサポート パブリックプレビュー https://aws.amazon.com/jp/about-aws/whats-new/2020/12/freertos-adds-cellular-lte-m-library-support-cellular-iot-based-applications/ 2020/12/26
新機能 AWS IoT Device SDK for Embedded C バージョン202012.00リリース GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-iot-sdk-for-embedded-c-version-202012-00-includes-over-the-air-update-ota-library-and-pkcs-number-11-implementation/ 2020/12/26
新サービス AWS CloudShell コンソールから起動可能なシェル GA https://aws.amazon.com/jp/blogs/news/aws-cloudshell-command-line-access-to-aws-resources/ 2020/12/26
新機能 AWS SDK for JavaScript Version 3提供開始 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/aws-sdk-javascript-version-3-generally-available/ 2020/12/26
新サービス Amazon Forecast Weather Index 気象情報をForecastに利用 GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-amazon-forecast-weather-index-include-local-weather-increase-forecasting-model-accuracy/ 2020/12/27
新機能 Amazon Corretto 11 Linux on ARM32 および Windows on x86サポート GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/announcing-the-general-availability-of-amazon-corretto-11-for-arm32-linux-on-arm32-and-for-windows-on-x86-32bit/ 2020/12/27
新機能 AWS IoT Device Tester for FreeRTOS FreeRTOS 202012.00サポート GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/updated-aws-iot-device-tester-freertos-aws-iot-device-tester-aws-iot-greengrass-now-available/ 2020/12/27
新機能 AWS IoT Device Tester for AWS IoT Greengrass Greengrass Version 2サポート GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/updated-aws-iot-device-tester-freertos-aws-iot-device-tester-aws-iot-greengrass-now-available/ 2020/12/27
新機能 FreeRTOS OTA時の複数ファイル形式サポート GA https://aws.amazon.com/jp/about-aws/whats-new/2020/12/freertos-now-supports-over-the-air-update-ota-for-multiple-file-types/ 2020/12/27
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】WordPressのサイトをSSL化した際のエラー対処

はじめに

ドメインをお名前.comで取得して、AWSで作成したWordpressのサーバーでSSLを試みた際に
いくつかエラーに遭遇したので備忘録として残します。

環境

  • Route53
  • ACM( AWS Certificate Manager)
  • ALB( Application Load Balancer)
  • EC2( Elastic Compute Cloud)
  • Apache
  • WordPress

エラー内容

①リダイレクトループのエラー

osozaki-engineer_work.png

②wordPress管理画面の権限エラー

WordPress_›_アクセス権限エラー.png

修正内容

Route53の設定変更

  • AレコードにELBのDNS名を設定

お名前.comの設定変更

  • NSレコードをRoute53と同様のNSを設定

※digコマンドでDNS情報を確認

WordPress管理画面の一般設定変更

  • WordPressアドレスとサイトアドレスをhttpからhttpsのURLへ変更

wp-config.phpの編集

以下をrequire_once ABSPATH . 'wp-settings.php';より上に追加

if (empty($_SERVER['HTTPS'])) {
    $_SERVER['HTTPS'] = 'on'; $_ENV['HTTPS'] = 'on';
}

最後に

リダイレクト設定は行なっていなかったのに、リダイレクトループのエラーが出たので
最初Apache(.htaccess)を確認していきましたが、特にそちらには問題ありませんでした。
おそらくWordPressにプラグイン等をインストールしていたのでそちらの設定が影響していたと思われます。
環境によって対応方法は異なりますので参考程度に見ていただければ幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Protonのメモ

メモ

AWSが用意してくれた、ECSのサンプルをベースに自分なりに整理
(注:どんなサービスも環境側とサービス側で定義可能)

proton1.png

解釈が微妙な奴もあるかも(特にサービスとサービスインスタンスがまだ微妙)

Proton概要

AWS Proton:コンテナおよびサーバーレスデプロイメント向けの管理の自動化

Protonのサンプル(READMEがチュートリアルみたくなってる)

aws-samples/aws-proton-sample-templates

ProtonのECSのサービスサンプル

aws-samples/aws-proton-sample-fargate-service

ProtonのLambdaのサービスサンプル

aws-samples/aws-proton-sample-lambda-crud-service

Protonのロードマップ

aws/aws-proton-public-roadmap

Protonのクォータ

Protonのクォータ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DocumentDBの初期値が不親切な件

DocumentDBとは

MongoDB互換のAWSマネージドサービスです。ドキュメント指向型のNoSQLでJSONやXMLをそのまま保管できます。

DocumentDBのインスタンス料金

image.png

(参考:https://aws.amazon.com/jp/documentdb/pricing/)

料金は、インスタンスの作成から終了あるいは削除までのインスタンス時間単位で計算されます。

一番小さいもので1日あたり0.119USD×24=2.856USDです。

インスタンスクラスの初期値が不親切な件

以下は、AWSでDocumentDBを作成する時の最初の画面です。
image.png

赤字枠の部分が「db.r5.large」になっています。一番小さい「db.t3.medium」ではありません。これ罠ですね。デフォルトのまま作成すると1日あたり8.016USDとなり「db.t3.medium」の約3倍になるのでご注意を!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Aurora Serverlessの接続数について調べた

はじめに

Aurora Serverlessへ接続するアプリで「Too many connections」というエラーが頻発したので接続数について調べたことのメモ。

接続数はいくつに設定されているか

これはパラメータグループで確認できる。以下のようにキャパシティー(ACU)に応じて変化する設定になっている。
image.png

各ACUごとの接続数は以下の通り
1ACU:90
2ACU:180
4ACU:270
8ACU:1000
(参考:https://qiita.com/kaba-chan/items/bfd1d8d333d277d6e11a)

現状ACUの最大値を1に設定しているので、最大90接続までということになる。

ACUに対する接続数を増やすことができるか

例えば以下のように設定すれば1ACUでも180の接続を許可できるのではないか

GREATEST({log(DBInstanceClassMemory/805306368)*90},{log(DBInstanceClassMemory/8187281408)*2000})

やってみるとエラーになる。
image.png

AWSのサポートに問い合わせてみたところ、ACUを増やす以外ないと回答あり。

質問

max_connection のパラメータをデフォルトより増やし、かつスケールさせる設定としたい。

回答

max_connections を大きくするには現状 ACU を大きくとっていただくほかない状況であり、お客さま>ご想定のようにデフォルトより大きくした状態でかつスケールさせるといった設定値には対応いたしておりません。

接続数が足りない場合ACUは増えるか

ACUの最大値を増やしたとして、接続数だけが足りない場合にACUは増えてくれるのか。
マニュアルには以下のように記載されている。

Aurora Serverless DB クラスターに割り当てられたキャパシティーは、クライアントアプリケーションで生成される負荷(CPU 使用率と接続数)に基づいてシームレスにスケールアップ/ダウンされます。また、DB クラスターの容量設定で、一時停止と再開のオプションを有効にすると、接続がないときに容量を 0 にスケールできます。詳細については、「Aurora Serverless の自動的な一時停止と再開」を参照してください。

(参考:https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.how-it-works.html)

接続数が足りなければスケールアップするように見えるが、詳細な条件は公開されてない模様。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SSMオートメーションでDocDB自動停止をやってみた

はじめに

AWSのRDSは停止しても7日間経過すると自動で起動されてしまいます。知らないうちに起動されて課金されることを防ぐためにRDS(今回はDocDB)を自動停止する仕組みをSSMオートメーションで試してみました。

準備

まずSSMドキュメントを実行するロールを作ります。

IAM > ロールの作成から
image.png

AmazonEC2FullAccessは無くても良いですがEC2も停止するケースも考慮して含めています。

作成したロールの信頼関係を編集をします。

作成したロールを選択して信頼関係の編集から

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
            "events.amazonaws.com",
            "ssm.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

保存すると信頼されたエンティティに2つ表示されます。

image.png

events.amazonaws.comはCloudWatch Eventsからcron的に呼び出す際に必要、ssm.amazonaws.comはSSMのマネージメントコンソールからテスト実行する際に必要になります。

次にPassRole用インラインポリシーを作成してアタッチします。

作成したロールのアクセス権限 > インラインポリシーの追加から
image.png
image.png

これでSSMサービスへこのロールを渡して使えるようになります。

自動化用SSMドキュメントを作る

SSM > ドキュメント > オートメーションを作成する と進んでエディタタブから編集します。

description: stop docdb
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  AutomationAssumeRole:
    type: String
    description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
    default: 'arn:aws:iam::xxxxxxxxxxxx:role/xxxxxxxx-ssm-automation-role'
mainSteps:
  - name: "stop_xxxxxxxx_docdb"
    action: 'aws:executeAwsApi'
    maxAttempts: 3
    onFailure: Continue
    inputs:
      Service: docdb
      Api: StopDBCluster
      DBClusterIdentifier: xxxxxxxx-docdb
  - name: "stop_xxxxxxxx_docdb"
    action: 'aws:executeAwsApi'
    maxAttempts: 3
    onFailure: Continue
    inputs:
      Service: docdb
      Api: StopDBCluster
      DBClusterIdentifier: xxxxxxxx-docdb

テスト実行

SSM > ドキュメント > 自己所有 > 作成したドキュメント > オートメーションを実行する
image.png

後はこのSSMドキュメントをCloudWatch Eventsからcronスケジュールで呼び出せば自動停止の完成です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS KMS (Key Management Service) に関する私的メモ

AWS認定 セキュリティ – 専門知識」を受験するにあたって、KMSについて個人的にまとめたメモです。

各CMKの比較表

image.png

Envelope enryption

S3データ格納時のサーバ側暗号化(SSE-S3、SSE-KMS、SSE-C)と、クライアント側暗号化(CSE-KMS、CSE-C)」を参照。

BYOK (Bring Your Own Key)のインポート

BYOKのポイント

  • 外部キーのインポートは対称暗号のみ可能。
  • 外部キーは自動ローテーション不可
  • 再インポートするキーマテリアルは、前回インポートしたものと同じキーマテリアルである必要がある。前回と異なるキーマテリアルをインポートするとIncorrectKeyMaterialExceptionが発生する。
  • 前回と同じキーマテリアルを再インポートできて何が嬉しいの?と思うかもしれないが、再インポートは誤って削除したキーマテリアルを復旧したり、有効期限を過ぎたキーマテリアルを延命する目的で行う。
  • BYOKの手動ローテーションを実現する手段はCMKの再作成になる。

以下に、AWS CLIを使用してBYOKを作成する手順を示します。
(作成するCMKのキーIDは、仮に12345678-abcd-1234-abcd-123456789012とします)

1. CMKの作成

マネージメントコンソールを使用したCMKの作成

AWSマネージメントコンソールのKMSから、キーマテリアルオリジンに外部を指定してCMKを作成します。

AWS CLIを使用したCMKの作成

AWS CLIを使用する場合、以下のコマンドラインを実行します。標準出力の内容に含まれるキーIDを取得します。

KEY_ID=$(aws kms create-key --origin EXTERNAL | jq -r '.KeyMetadata.KeyId')
echo ${KEY_ID}  # 12345678-abcd-1234-abcd-123456789012 

なお、残念ながら、AWS CLIではCMK作成時にエイリアスを作成・アタッチすることはできません。

2. 公開鍵とインポートトークンのダウンロード

公開鍵のアルゴリズムによって、以下のいずれかを実行します。

  • RSAES_PKCS1_V1_5
  • RSAES_OAEP_SHA_1
  • RSAES_OAEP_SHA_256
RSAES_PKCS1_V1_5
# 公開鍵とインポートトークンのダウンロード(RSAES_PKCS1_V1_5)
aws kms get-parameters-for-import \
  --key-id 12345678-abcd-1234-abcd-123456789012\
  --wrapping-algorithm RSAES_PKCS1_V1_5 \
  --wrapping-key-spec RSA_2048 \
  >kms_get-parameters-for-import.txt
RSAES_OAEP_SHA_1
# 公開鍵とインポートトークンのダウンロード(RSAES_OAEP_SHA_1)
aws kms get-parameters-for-import \
  --key-id 12345678-abcd-1234-abcd-123456789012\
  --wrapping-algorithm RSAES_OAEP_SHA_1 \
  --wrapping-key-spec RSA_2048 \
  >kms_get-parameters-for-import.txt
RSAES_OAEP_SHA_256
# 公開鍵とインポートトークンのダウンロード(RSAES_OAEP_SHA_256)
aws kms get-parameters-for-import \
  --key-id 12345678-abcd-1234-abcd-123456789012\
  --wrapping-algorithm RSAES_OAEP_SHA_256 \
  --wrapping-key-spec RSA_2048 \
  >kms_get-parameters-for-import.txt

3. 公開鍵とインポートトークンのBASE64デコード

cat kms_get-parameters-for-import.txt | jq -r '.PublicKey' >PublicKey.b64
cat kms_get-parameters-for-import.txt | jq -r '.ImportToken' >ImportToken.b64

openssl enc -d -a -A -in PublicKey.b64 -out PublicKey.bin
openssl enc -d -a -A -in ImportToken.b64 -out ImportToken.bin

4. キーマテリアルの生成

キーマテリアルの生成
printf "0123456789ABCDEF0123456789ABCDEF" > PlaintextKeyMaterial.bin

5. キーマテリアルの暗号化

2. 公開鍵とインポートトークンのダウンロード」で選択した公開鍵のアルゴリズムによって、以下のいずれかを実行します。

RSAES_PKCS1_V1_5
# 公開鍵でキーマテリアルの暗号化(RSAES_PKCS1_V1_5)
openssl rsautl -encrypt \
  -in PlaintextKeyMaterial.bin \
  -pkcs \
  -inkey PublicKey.bin \
  -keyform DER \
  -pubin \
  -out EncryptedKeyMaterial.bin
RSAES_OAEP_SHA_1
# 公開鍵でキーマテリアルの暗号化(RSAES_OAEP_SHA_1)
openssl rsautl -encrypt \
  -in PlaintextKeyMaterial.bin \
  -oaep \
  -inkey PublicKey.bin \
  -keyform DER \
  -pubin \
  -out EncryptedKeyMaterial.bin
RSAES_OAEP_SHA_256
# 公開鍵でキーマテリアルの暗号化(RSAES_OAEP_SHA_256)
openssl pkeyutl -encrypt \
  -in PlaintextKeyMaterial.bin \
  -pkeyopt rsa_padding_mode:oaep \
  -pkeyopt rsa_oaep_md:sha256 \
  -inkey PublicKey.bin \
  -keyform DER \
  -pubin \
  -out EncryptedKeyMaterial.bin

6. キーマテリアルのインポート

# 暗号化後のキーマテリアルのインポート
aws kms import-key-material \
  --key-id 12345678-abcd-1234-abcd-123456789012 \
  --encrypted-key-material fileb://EncryptedKeyMaterial.bin \
  --import-token fileb://ImportToken.bin \
  --expiration-model KEY_MATERIAL_EXPIRES \
  --valid-to "2020-12-31T12:00:00-08:00"

7. キーマテリアルの削除

キーマテリアルの削除
aws kms delete-imported-key-material --key-id 12345678-abcd-1234-abcd-123456789012
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】WordPress削除の備忘録

はじめに

WordPressをAWS上で立ち上げたものの、その後あまりブログを更新することもなく。。。
EC2使っていたので料金も月数千円ほどかかってしまい、維持が大変だったのでWordPressを削除したいと思います。
特にこれといったAWS上のWordPress削除方法に関する記事もなかったので、普通にAWS上のサーバー等を消したいと思います。もしやり方など間違っていたらご了承ください。

今回削除するWordPressの構成は、以下のURL通り。
https://qiita.com/blackpeach7/items/be28e2249f308bac7592
ドメインはお名前.comで購入。
image.png

ドメインの廃止

お名前.comでドメインの登録をしたが「ドメインの廃止の手続きは必要なのか?」と疑問に思い調べてみると、以下の通りだった。

【ドメイン】ドメインの廃止方法は?

「※ドメイン廃止をご希望の場合、自動更新設定が解除済みであれば、
 更新期限日を迎えると自動的に廃止されますので、特にお手続などを行う必要はございません。」

自分の場合、自動更新設定はされてなかったので、特に設定などは不要であるということがわかった。

手順

EC2

Amazon EC2 リソースを削除または終了する方法を教えてください。
上記に従って削除・終了する。
※終了保護設定している場合は解除を事前にしておく。

インスタンスの削除。
スクリーンショット 2020-12-27 10.20.18.png
ロードバランサーの削除。
スクリーンショット 2020-12-27 10.36.47.png
Auto Scalingの削除。
スクリーンショット 2020-12-27 14.28.33.png
Elastic IPアドレスの削除。
スクリーンショット 2020-12-27 14.13.17.png
AMIの削除。
スクリーンショット 2020-12-27 14.14.35.png

RDS

Amazon RDS リソースを削除または終了するにはどうすればよいですか?
上記に従って削除・終了する。
※終了保護設定している場合は解除を事前にしておく。

RDS DBインスタンスの削除。
スクリーンショット 2020-12-27 10.55.24.png
スナップショットの削除。
スクリーンショット 2020-12-27 14.21.18.png

VPC

Amazon VPC を削除しようとしたのですが、依存関係エラーが発生しました。Amazon VPC を削除する方法を教えてください。
上記に従って削除・終了する。

VPCを削除しようとすると、下記のようなエラーメッセージが出ることがあるので、その場合メッセージに従って実行する。
スクリーンショット 2020-12-27 10.56.33.png
スクリーンショット 2020-12-27 11.11.37.png
スクリーンショット 2020-12-27 10.56.53.png
エラーに従い、今回の場合ネットワークインターフェイスの削除を行う。
スクリーンショット 2020-12-27 11.11.10.png
その後VPCに戻り、無事に削除できた。
スクリーンショット 2020-12-27 11.11.54.png

Route53

Route 53 ホストゾーンを削除する方法を教えてください。
上記にに従ってサービスを削除・終了する。

Route 53ホストゾーンを削除する為、SOAとNS以外のホストゾーンに関連付けられている全てのレコードを削除する。
スクリーンショット 2020-12-27 11.18.52.png
スクリーンショット 2020-12-27 11.19.32.png
パブリックホストゾーンの削除。
スクリーンショット 2020-12-27 11.21.26.png

上記のようにしてWordPressを終了させた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS SAMでAPIGatewayからLambdaをエイリアス指定で呼び出す

はじめに

AWS CloudFormationの拡張機能であるAWS SAMを使って、APIGatewayとLambdaを管理するユースケースは多いと思います。SAMのメリットのひとつとしては、少ない記述量で済むことが挙げられると思います。

さて、このSAM。APIGatewayとLambdaを素直に管理するのには良いのですが、プロジェクトが成熟したときに出てくるかもしれない、このような要望には少々難しいところがあります。

私「できた。SAM最高。」

SRE「そういえば、LambdaをバージョニングしてAPIGatewayからはエイリアス指定のLambdaを呼び出すようにできたりしますかー?」

私「Lambdaをデプロイするタイミングで最新バージョンに向いてしまうのはアレですもんね。エイリアスLambdaをトリガーするようにしつつ、エイリアスは手動で張替えしたいということですよね。バージョニングは何かあったときにROLLBACKもできますし。」

私「調査します!」

... 数時間後 ...

私「SAMだけでは、できなくないかい...これ...?」

Versioningなし & AliasなしのLambda

template.yaml

GET /helloというAPIを作成するtemplateを例として書いていきます。

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Lambda handler for API Gateway.
Globals:
  Function:
    Runtime: go1.x
    Timeout: 30
Resources:
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      EndpointConfiguration: REGIONAL
      Name: MyAPI
      StageName: prod
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: myFunction
      CodeUri: functions/hello/
      Handler: app
      Events:
        GetHelloApi:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /hello
            Method: GET

はい、これだけです。

template.yamlをvalidationする

$ sam validate
template.yaml is a valid SAM Template

注意点として、sam validateは、あくまでテンプレートの構文チェックをしてくれるだけです。つまり、記述内容がデプロイ先のAWS環境と整合性がとれているかといったチェックまではしてくれません。
ですので、初回は、試行錯誤しながら、CloudFormationから吐かれるエラーを潰しながらデプロイしていきました。

build & deploy

$ GOOS=linux GOARCH=amd64 go build -o functions/hello/app ./functions/hello
$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket $DEPLOYMENT_TARGET_BUCKET
$ sam deploy --template-file packaged.yaml --stack-name $STACK_NAME --debug --capabilities CAPABILITY_IAM --no-fail-on-empty-changeset

AWS APIGatewayのマネジメントコンソールで確認

GET /helloがmyFunction関数を呼び出すように設定されました。
image.png

Versioningあり & AliasありのLambda

追加要件はざっくりこのような感じです。

  • Lambda関数のバージョニングをする。
  • Lambda関数には、開発用と本番用のエイリアスを用意する。
    • 開発用エイリアスは常に最新バージョンを参照する
    • 本番用エイリアスは手動でエイリアスの張替えをする。デプロイする度にバージョン変更されたくない
  • APIGatewayからは本番用エイリアスのLambdaを呼び出す。

APIGatewayから呼び出されるLambdaをエイリアス指定しようとすると厳しい現実が突きつけられました。
TypeAWS::Serverless::FunctionではLambda関数を指定するんですが、FunctionNameにはARNの指定ができないため、エイリアスを指し示すLambda関数の指定ができません。

結論、ほぼSAMの記述(AWS::Serverless)は使えずにCloudFormationの記述方法で記載することになりました。

template.yaml

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Lambda handler for API Gateway.
Globals:
  Function:
    Runtime: go1.x
    Timeout: 30
Resources:
#  ApiGateway:
#    Type: AWS::Serverless::Api
#    Properties:
#      EndpointConfiguration: REGIONAL
#      Name: MyAPI
#      StageName: dev
  ApiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties:
      EndpointConfiguration:
        Types: [ REGIONAL ]
      Name: MyAPI
  ApiGatewayResourceHello:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: !GetAtt ApiGateway.RootResourceId
      PathPart: 'hello'
      RestApiId: !Ref ApiGateway
  ApiGatewayMethodGetHello:
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      ResourceId: !Ref ApiGatewayResourceHello
      RestApiId: !Ref ApiGateway
      Integration:
        IntegrationHttpMethod: POST
        Type: AWS_PROXY
        Uri: !Join [ '' , [ 'arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/', !GetAtt MyLambdaFunction.Arn, ':PROD', '/invocations' ] ]
  ApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: [ ApiGatewayMethodGetHello ]
    Properties:
      RestApiId: !Ref ApiGateway
  ApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref ApiGatewayDeployment
      RestApiId: !Ref ApiGateway
      StageName: prod
  MyLambdaFunctionAliasProd:
    Type: AWS::Lambda::Alias
    Properties:
      FunctionName: !GetAtt MyLambdaFunction.Arn
      FunctionVersion: $LATEST
      Name: PROD
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: myFunction
      CodeUri: functions/hello/
      Handler: app
      AutoPublishAlias: DEV
#      Events:
#        GetHelloApi:
#          Type: Api
#          Properties:
#            RestApiId: !Ref ApiGateway
#            Path: /hello
#            Method: GET
  MyLambdaFunctionPermission:
    Type: AWS::Lambda::Permission
    DependsOn: MyLambdaFunctionAliasProd
    Properties:
      FunctionName: !Join [ '' , [ !GetAtt MyLambdaFunction.Arn, ':PROD' ] ]
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Join [ '' , [ 'arn:aws:execute-api:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref ApiGateway, '/*/GET/hello' ] ]

結果、あらたな要件に対応したこともあるのですが、step数が27→63stepと倍以上になりました。
いかにSAMが暗黙的に色々なリソースを作成してくれていたか。SAMへのありがたみを感じると同時にCloudFormationの辛みを感じる経験でした。

build & deploy

さきほどと同様にdeployすると、Cloudformationでエラーになります。
ApiGatewayResourceHelloリソースを作成する際に、CREATE_FAILEDになってしまいました。

Another resource with the same parent already has this name: hello (Service:AmazonApiGateway; Status Code: 409; Error Code: ConflictException; RequestID: f36f9b8b-8275-4fba-bc73-0d3920203333; Proxy:null)

これはつまり、stack内で/helloというリソースが競合しているとのこと。
MyLambdaFunctionリソースに定義していたPath: /helloは削除しているのに競合になってしまうので、一度stackを削除しました。

AWS APIGatewayのマネジメントコンソールで確認

GET /helloがPRODエイリアスのmyFunction関数を呼び出すように設定される。
image.png

AWS Lambdaのマネジメントコンソールで確認

PRODエイリアスのLambdaに向けてトリガーが設定されている。
image.png

まとめ

  • 一度stackを削除してデプロイするということは、「リソース削除→新規作成」される挙動になるため、API IDが変わってしまいます。API IDが変わってしまうということは、APIGatewayのエンドポイントに変更が生じてしまうということです。
    https://{API_ID}.execute-api.{REGION}.amazonaws.com/{STAGE}/{PATH}

  • 要件次第ですが、すべてをSAMで対応することはできないと考えたほうが良いです。VersioningやAlias周りを細かく設定したいのであれば、現時点では素のCloudFormationで書くのが良さそうです。

  • SAMを書いているのか、CloudFormationを書いているのか迷子になることがありました。そんなときはリソースのTypeを見て、AWS::Serverless::と定義していればSAMなので、「今は(SAM|CloudFormation)を書いている」と確認してました。

参考

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-function.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go言語でAWS Lambdaの開発をサポートするパッケージを作った

仕事でGo言語 + AWS Lambdaを用いる機会が多く、特にセキュリティ監視関連基盤のバックエンド処理を開発しています(これとかこれとかこれ)。

開発をすすめる中で「こうすると便利だな」というちょっとしたtipsはいろいろあったのですが、あまりに細切れな処理すぎるのでプロジェクト間でコピーするなどして開発に利用していました。とはいえ管理しているプロジェクトが多くなってきたことで挙動がまちまちになってしまったり、ある程度tipsの数が溜まってきたのもあって、パッケージとして切り出してみました。

https://github.com/m-mizutani/golambda

AWSが公式で提供しているPowertools(Python版Java版)を意識してはいますが、完全に再現する目的では作っていません。また、全てのGo + Lambdaの開発者が「この方法に従うべき!」とも思っていません。例えば、API gatewayによって呼び出されるLambdaは各種Web Application Frameworkで同じような機能がサポートされていることもあり、あまりこのパッケージの恩恵は受けられないと思います。なので「こういう処理をまとめておくと便利」ぐらいな話として見ていただければと思います。

基本的にはデータ処理のパイプラインやちょっとしたインテグレーションなどのためのLambdaを想定しており、以下の4つの機能を実装しています。

  • イベントの取り出し
  • 構造化ロギング
  • エラー処理
  • 秘匿値の取得

実装している機能

イベントの取り出し

AWS Lambdaはイベントソースを指定して、そこからの通知をトリガーに起動させることができます。この際、Lambda functionはSQSやSNSといったイベントソースのデータ構造が渡されて起動します。そのため、各種構造データから自分で使うデータを取り出す作業が必要です。golambda.Start() という関数にcallback(以下の例では Handler )を指定すると golambda.Event に必要な情報が格納され、そこから取り出すことができます。

package main

import (
    "strings"

    "github.com/m-mizutani/golambda"
)

type MyEvent struct {
    Message string `json:"message"`
}

// SQSのメッセージをconcatして返すHandler
func Handler(event golambda.Event) (interface{}, error) {
    var response []string

    // SQSのbodyを取り出す
    bodies, err := event.DecapSQSBody()
    if err != nil {
        return nil, err
    }

    // SQSはメッセージがバッチでうけとる場合があるので複数件とみて処理する
    for _, body := range bodies {
        var msg MyEvent
        // bodyの文字列をmsgにbind(中身はjson.Unmarshal)
        if err := body.Bind(&msg); err != nil {
            return nil, err
        }

        // メッセージを格納
        response = append(response, msg.Message)
    }

    // concat
    return strings.Join(response, ":"), nil
}

func main() {
    golambda.Start(Handler)
}

このサンプルコードは ./example/deployable ディレクトリにおいてあり、デプロイして実行を試すことができます。

データを取り出す処理を実装している DecapXxx という関数とは逆に、データを埋め込む処理を EncapXxx として用意しています。これによって上記のLambda Functionに対して、以下のようにテストを書くことができます。

package main_test

import (
    "testing"

    "github.com/m-mizutani/golambda"
    "github.com/stretchr/testify/require"

    main "github.com/m-mizutani/golambda/example/decapEvent"
)

func TestHandler(t *testing.T) {
    var event golambda.Event
    messages := []main.MyEvent{
        {
            Message: "blue",
        },
        {
            Message: "orange",
        },
    }
    // イベントデータの埋め込み
    require.NoError(t, event.EncapSQS(messages))

    resp, err := main.Handler(event)
    require.NoError(t, err)
    require.Equal(t, "blue:orange", resp)
}

現状はSQS、SNS、SNS over SQS(SNSをsubscribeしているSQSキュー) の3つをサポートしていますが、後々DynamoDB stream、Kinesis streamも実装しようと考えています。

構造化ロギング

Lambdaの標準的なログ出力先はCloudWatch Logsになりますが、LogsあるいはLogsのビュワーであるInsightsはJSON形式のログをサポートしています。そのため、Go言語標準の log パッケージを使うのではなく、JSON形式で出力できるロギングツールを用意するのが便利です。

ログの出力形式も含めて、Lambda上でのロギングは概ね要件が共通化されています。多くのロギングツールは出力方法や出力形式について様々なオプションがありますが、Lambda functionごとに細かく設定を変えるということはあまりしません。また出力する内容についてもほとんどの場合はメッセージ+文脈の説明に必要な変数だけで事足りるため、そのような単純化をしたwrapperを golambda では用意しました。実際の出力部分では zerolog を利用しています。本当はzerologで作成したロガーをそのまま露出させるというのでも良かったのですが、できることを絞っておいたほうが自分にとってもわかりやすいなと思い、あえてwrapする形にしました。

Logger というグローバル変数をexportしており、Trace, Debug, Info, Error というログレベルごとのメッセージを出力できるようにしています。任意の変数を永続的に埋め込める Set と、メソッドチェインで値を継ぎ足していける With を用意しています。

// ------------
// 一時的な変数を埋め込む場合は With() を使う
v1 := "say hello"
golambda.Logger.With("var1", v1).Info("Hello, hello, hello")
/* Output:
{
    "level": "info",
    "lambda.requestID": "565389dc-c13f-4fc0-b113-xxxxxxxxxxxx",
    "time": "2020-12-13T02:44:30Z",
    "var1": "say hello",
    "message": "Hello, hello, hello"
}
*/

// ------------
// request ID など、永続的に出力したい変数を埋め込む場合はSet()を使う
golambda.Logger.Set("myRequestID", myRequestID)
// ~~~~~~~ snip ~~~~~~
golambda.Logger.Error("oops")
/* Output:
{
    "level": "error",
    "lambda.requestID": "565389dc-c13f-4fc0-b113-xxxxxxxxxxxx",
    "time": "2020-11-12T02:44:30Z",
    "myRequestID": "xxxxxxxxxxxxxxxxx",
    "message": "oops"
}
*/

また、CloudWatch Logsはログの書き込みに対する料金が比較的高価であり、詳細なログを常に出力しているとコストに大きく影響します。そのため通常は最低限のログだけを出力し、トラブル対応やデバッグの時だけ詳細な出力ができるようにしておくと便利です。 golambda では LOG_LEVEL 環境変数を設定することで、ログ出力レベルを外部からいじることができるようにしています。(環境変数だけならAWSコンソールなどから容易に変更可能なため)

エラー処理

AWS Lambdaはfunctionごとになるべく単機能になるよう実装し、複雑なワークフローを実現する場合にはSNS、SQS、Kinesis Stream、Step Functionsなどを使って複数のLambdaを組み合わせるようにしています。そのため処理の途中でエラーが起きた場合はLambdaのコード内で無理にリカバリしようとせず、なるべく素直にそのままエラーを返すことで外部からの監視で気づきやすくなったり、Lambda自身のリトライ機能の恩恵を受けやすくなったりします。

一方でLambda自身はエラーをあまり丁寧に処理してくれるわけではないので、自前でエラー処理を用意する必要があります。 先述したとおり、Lambda functionは何かあった場合はそのままエラーを返して落ちる、という構成にしておくと便利です。なので、殆どのケースにおいてエラーが発生した場合はメインの関数(後述する例だと Handler() )がエラーを返した場合に一通りまとめてエラーに関する情報を出力してくれると、あちこちのエラー発生箇所でログを出力したりどこかへエラーを飛ばすという処理を書く必要がなくなります。

golambda では、 golambda.Start() で呼び出した主に以下の2つのエラー処理をしています。

  1. golambda.NewError あるいは golambda.WrapError で生成したエラーの詳細なログの出力
  2. エラー監視サービス(Sentry)へエラーを送信

それぞれ詳しく説明します。

エラーの詳細なログ出力

経験上、エラーが起きたときにデバッグのため知りたいのは大きく分けて「どこで起きたのか」「どのような状況で起きたのか」の2つです。

どこでエラーが起きたのかを知る方法としては、 Wrap 関数を使いコンテキストを追記していく、あるいは github.com/pkg/errors パッケージのようにスタックトレースを持つ、などの戦略があります。Lambdaの場合、なるべく単純な処理になるよう実装する方針であれば、ほとんどの場合はスタックトレースでエラー発生箇所とどのように発生したかを知ることができます。

また、エラーの原因となった変数の中身を知ることでエラーの再現条件を把握できます。これはエラーが発生したら関連しそうな変数を都度ログ出力することでも対応できますが、出力行が複数にわたってログの見通しが悪くなってしまいます(特に呼び出しが深い場合)。また、単純にログ出力のコードを繰り返し書かねばならず冗長になり、単純に書くのも大変だしログ出力に関する変更をしたいときに面倒です。

そこで、golambda.NewError() あるいは golambda.WrapError()1で生成したエラーは、 With() という関数でエラーに関連する変数を引き回せるようにしました。実体は中に map[string]interface{} の変数にkey/valueの形で格納しているだけです。golambda.NewError() あるいは golambda.WrapError()によって生成されたエラーをメインロジック(以下の例の Handler() )が返すと、 With() によって格納した変数と、エラーが生成された関数のスタックトレースをCloudWatch Logsに出力します。以下、コードの例です。

package main

import (
    "github.com/m-mizutani/golambda"
)

// Handler is exported for test
func Handler(event golambda.Event) (interface{}, error) {
    trigger := "something wrong"
    return nil, golambda.NewError("oops").With("trigger", trigger)
}

func main() {
    golambda.Start(Handler)
}

これを実行すると、以下のように error.values の中に With で格納した変数が、 error.stacktrace にスタックトレースが含まれるログが出力されます。スタックトレースは github.com/pkg/errors の %+v フォーマットでもテキストで出力されますが、構造化ログの出力に合わせてJSON形式に対応しているのもポイントです。

{
    "level": "error",
    "lambda.requestID": "565389dc-c13f-4fc0-b113-f903909dbd45",
    "error.values": {
        "trigger": "something wrong"
    },
    "error.stacktrace": [
        {
            "func": "main.Handler",
            "file": "xxx/your/project/src/main.go",
            "line": 10
        },
        {
            "func": "github.com/m-mizutani/golambda.Start.func1",
            "file": "xxx/github.com/m-mizutani/golambda/lambda.go",
            "line": 127
        }
    ],
    "time": "2020-12-13T02:42:48Z",
    "message": "oops"
}

エラー監視サービス(Sentry)へエラーを送信

Sentryでないといけない理由は特にないのですが、APIに限らずLambda functionもWebアプリケーションなどと同様に何らかのエラー監視サービスを使うのが望ましいです。理由は以下のようなものがあります。

  • CloudWatch Logsにデフォルトで出力されるログからは正常終了したか異常終了したかの判定ができないため、異常終了した実行のログだけ抽出するというのが難しい
  • CloudWatch Logsではエラーをグルーピングするような機能はないため、エラー100件のうち1件だけ種類の違うエラーがある、みたいなやつを見つけ出すのが難しい

両方ともエラーログの出力方法を工夫することである程度解決できなくはないですが、色々気をつけて実装しないとならないため素直にエラー監視サービスを使うのがオススメです。

golambda ではSentryのDSN (Data Source Name) を環境変数 SENTRY_DSN として指定することでメインロジックが返したエラーをSentryに送信します(Sentry + Goの詳細)。送るのはどのエラーでも問題ありませんが、golambda.NewErrorgolambda.WrapError で生成したエラーは github.com/pkg/errors と互換性のある StackTrace() という関数を実装しているため、スタックトレースがSentry側にも表示されます。

これはCloudWatch Logsに出力されるものと同じですが、Sentry側の画面でも確認できるため「通知を見る」→「Sentryの画面を見る」→「CloudWatch Logsでログを検索し詳細を確認する」の2番目のステップでエラーの見当をつけられる場合もあります。あとCloudWatch Logsの検索はまあまあもっさりしているので、検索しないですむならそのほうがよい、というのもあります…。

ちなみにSentryにエラーを送信すると error.sentryEventID としてSentryのevent IDをCloudWatch Logsのログに埋め込むので、Sentryのエラーから検索ができるようになっています。

秘匿値の取得

Lambdaでは実行環境によって変更するようなパラメータは環境変数に格納して利用することが多いです。個人で使っているAWSアカウントであれば全て環境変数に格納するでよいのですが、複数人で共有して使うようなAWSアカウントでは秘匿値と環境変数を分離しておくことで、Lambdaの情報のみを参照できる人(あるいはRole)と秘匿値も参照できる人(あるいはRole)を分離することができます。これは個人で使っていても真にヤバい情報をあつかうのであれば何らかのアクセスキーが漏れても即死しないように権限を分離しておくケースもあるかもしれません。

自分の場合は権限を分離するため、AWS Secrets Manager を利用することが多いです2。Secrets Managerからの値の取り出しはAPIを呼び出せば比較的簡単ではあるのですが、それでも同じような処理を100回くらい書いて飽きたのでモジュール化しました。構造体のフィールドに json メタタグをつければそれで値が取得できます。

type mySecret struct {
    Token string `json:"token"`
}
var secret mySecret
if err := golambda.GetSecretValues(os.Getenv("SECRET_ARN"), &secret); err != nil {
    log.Fatal("Failed: ", err)
}

実装しなかった機能

便利そうかなと思いつつ、実装を見送ったものたちです。

  • タイムアウト直前に任意の処理を実行:Lambdaは設定された最大実行時間をすぎると無言で死ぬためパフォーマンス分析の情報を出すためにタイムアウト直前に何らかの処理を呼び出すというテクニックがあります。ただ自分の場合は Lambda function がタイムアウトによって無言で死んで困った経験がほとんどないので、なんか便利そうと思いつつ特に手を付けませんでした。
  • Tracing:Pythonの Powertools では、アノテーションなどを使ってAWS X-Rayでパフォーマンス計測するための機能が提供されています。Goでこれをやろうとすると現状だと普通に公式SDKを使う以上に楽ができる方法があまり浮かばなかったので、特に取り組みませんでした。

まとめ

ということで、Go言語でのLambda実装における自分なりのベストプラクティスのまとめと、それをコード化したものの紹介でした。冒頭に書いたとおりあくまで自分が必要だったものを作っただけなので、万人に使えるものではないかなと思いますが、なにかの参考になれば幸いです。


  1. こういったエラー生成のメソッドは errors.New()errors.Wrap() などとする習わしがあるかと思いますが、個人的にはどのパッケージを使っているのか直感的にわかりにくくなるので、あえて命名法則を変えました。 

  2. 他にも AWS Systems Manager Parameter Store に秘匿値を入れるというやり方もあります。RDSパスワードなどのローテーション機能がある Secrets Manager の方がサービスの思想としては適切かと思い、個人的にはそちらを使っています。ただコストやAPIレートリミットも違ったりするので、本来であれば要件によって使い分けたほうが良さそうです。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudFormationでEC2&RDSの構築

概要

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみようを拝見し、EC2+RDSの構築を学びました。

次のステップとして、CloudFormationを使いEC2とRDSを構築したので共有しようと思います。

対象者

  • AWSの管理コンソールからRDSを構築された経験がある方
  • CloudFormationの知見がある方

構築されるもの

  • VPC
  • InternetGateway
  • Subnet
  • RouteTable
  • EC2
  • RDS
  • SecurityGroup
  • SubnetGroup
  • ElasticIP

構成図

rds (1).png

セクション説明

セクション 意味 備考
AWSTemplateFormatVersion テンプレートバージョン 2010-09-09 であり、現時点で唯一の有効な値
Description テンプレートを説明するテキスト ----
Metadata テンプレートバージョン ----
Parameters パラメーターの定義 ----
Resources スタックに含める AWS リソースの宣言 ----
Outputs 出力値を宣言 他のスタックでインポートできる

組み込み関数説明

組み込み関数 意味 備考
Ref 指定したパラメータまたはリソースの値 ---
Sub 特定した値の入力文字列にある変数の代わりになる 文字列内に変数を挿入する
GetAtt テンプレートのリソースから属性の値を返す ---

テンプレートファイル

下記においてあります。ご自由にお使いください
https://github.com/toyoyuto/cloudformation_rds

テンプレートファイル(解説付き)

ec2-rds.yml
AWSTemplateFormatVersion: 
  "2010-09-09"
Description:
  Server and DB construction

Metadata:
  # コンソールでパラメータをグループ化およびソートする方法を定義するメタデータキー
  "AWS::CloudFormation::Interface":
    # パラメーターグループとそのグループに含めるパラメーターの定義
    ParameterGroups: 
      # Project名に関するグループ
      - Label: 
          default: "Project Name Prefix"
        Parameters: 
          - PJPrefix
      # ネットワーク設定に関するグループ
      - Label: 
          default: "Network Configuration"
        # 記述された順番に表示される
        Parameters: 
          - VPCCIDR
          - PublicSubnetCIDR
          - PrivateSubnet1CIDR
          - PrivateSubnet2CIDR
          - KeyName
    # パラメーターのラベル
    ParameterLabels: 
      VPCCIDR: 
        default: "VPC CIDR"
      PublicSubnetCIDR: 
        default: "PublicSubnet CIDR"
      PrivateSubnet1CIDR: 
        default: "PrivateSubnet1 CIDR"
      PrivateSubnet2CIDR: 
        default: "PrivateSubnet2 CIDR"


# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String

  VPCCIDR:
    Type: String
    Default: "10.0.0.0/16"

  PublicSubnetCIDR:
    Type: String
    Default: "10.0.0.0/24"

  PrivateSubnet1CIDR:
    Type: String
    Default: "10.0.1.0/24"

  PrivateSubnet2CIDR:
    Type: String
    Default: "10.0.2.0/24"

  KeyName:
    Type: "AWS::EC2::KeyPair::KeyName"

Resources: 
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
# VPC Create
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: !Ref VPCCIDR
      # VPC に対して DNS 解決がサポートされているか
      EnableDnsSupport: "true"
      # VPC 内に起動されるインスタンスが DNS ホスト名を取得するか
      EnableDnsHostnames: "false"
      # VPC 内に起動されるインスタンスの許可されているテナンシー
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-vpc"

# InternetGateway Create
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-igw"

# IGW Attach
  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#          
# Public Subnet Create
  PublicSubnet: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnetCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-public-subnet"

# Private Subnet Create
  PrivateSubnet1: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnet1CIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-private-subnet1"
  PrivateSubnet2: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnet2CIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-private-subnet2"
# ------------------------------------------------------------#
#  RouteTable
# ------------------------------------------------------------#          
# Public RouteTable Create
  PublicRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-public-route"

# ------------------------------------------------------------#
# Routing
# ------------------------------------------------------------# 
# PublicRoute Create
  PublicRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 

# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------# 
# PublicRouteTable Associate PublicSubnet
  PublicSubnetRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

# ------------------------------------------------------------#
#  ElasticIP
# ------------------------------------------------------------#
  ElasticIP: 
    Type: "AWS::EC2::EIP"
    Properties: 
      Domain: vpc
  ElasticIPAssociate:
    Type: AWS::EC2::EIPAssociation
    Properties: 
      AllocationId: !GetAtt ElasticIP.AllocationId
      InstanceId: !Ref WebServer
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------# 
  # WebServerインスタンス
  WebServer: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: ami-00f045aed21a55240
      KeyName: !Ref KeyName
      InstanceType: t2.micro
      # 属するサブネット
      SubnetId: !Ref PublicSubnet
      # 属するセキュリティグループ
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      # インスタンスの作成時に実行するコマンドなどを記述
      UserData: !Base64 |
        #!/bin/bash
        sudo yum -y update

        sudo yum -y install mysql

        sudo yum -y install httpd
        sudo systemctl start httpd.service
        sudo systemctl enable httpd.service

      Tags:
          - Key: Name
            Value: !Sub "${PJPrefix}-web-server"

  # WebServerセキュリティグループ
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: web-sg-cf
      GroupDescription: web server sg
      VpcId: !Ref VPC
      SecurityGroupIngress:
        # ssh
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-web-server-sg"

  # # ------------------------------------------------------------#
  # #  DBInstance MySQL
  # # ------------------------------------------------------------#
  DBInstance: 
    Type: "AWS::RDS::DBInstance"
    Properties: 
      DBInstanceIdentifier: !Sub "${PJPrefix}-db-instance"
      Engine: MySQL
      # version
      # EngineVersion: !Sub "${MySQLMajorVersion}.${MySQLMinorVersion}"
      # DB インスタンスのコンピューティング性能とメモリ容量 (例: db.m4.large)
      DBInstanceClass: "db.t2.micro"
      # データベースインスタンスに最初に割り当てるストレージの容量
      AllocatedStorage: "20"
      # スレレージタイプ
      StorageType: "gp2"  # ssd
      DBName: !Sub "${PJPrefix}_db"
      MasterUsername: "admin"
      MasterUserPassword: "password"
      DBSubnetGroupName: !Ref DBSubnetGroup
      # DB インスタンスがインターネットに接するインスタンスかどうかを示します
      PubliclyAccessible: false
      # データベースインスタンスが複数のアベイラビリティーゾーンに配置されているかどうか
      MultiAZ: false
      # 自動バックアップが有効になっている場合に、自動バックアップが作成される毎日の時間範囲
      PreferredBackupWindow: "18:00-18:30"
      # 週 1 回のシステムメンテナンスを実行できる時間帯、世界標準時 (UTC)。
      PreferredMaintenanceWindow: "sat:19:00-sat:19:30"
      AvailabilityZone: "ap-northeast-1a"
      # マイナーバージョン自動アップグレードの有効化
      AutoMinorVersionUpgrade: false
      VPCSecurityGroups:
        - !Ref RDSSecurityGroup
      # タグを DB インスタンスから DB インスタンスのスナップショットにコピーするかどうか
      CopyTagsToSnapshot: false
      # 自動バックアップを保管する日数 0だと自動バックアップ無効
      BackupRetentionPeriod: 7
      Tags: 
        - Key: "Name"
          Value: !Sub "${PJPrefix}-db"
    DeletionPolicy: "Delete"

  # # ------------------------------------------------------------#
  # #  RDSSecurityGroup
  # # ------------------------------------------------------------#
  RDSSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: !Ref VPC
      GroupName: !Sub "${PJPrefix}-db-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-db-sg"
  # Rule
  RDSSecurityGroupIngress: 
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties: 
      IpProtocol: tcp
      FromPort: 3306
      ToPort: 3306
      # 許可するセキュリティグループ
      SourceSecurityGroupId: !GetAtt [ WebServerSecurityGroup, GroupId ]
      # 紐付けるセキュリティグループ
      GroupId: !GetAtt [ RDSSecurityGroup, GroupId ]

  # # ------------------------------------------------------------#
  # #  DBSubnetGroup
  # # ------------------------------------------------------------#
  DBSubnetGroup: 
    Type: "AWS::RDS::DBSubnetGroup"
    Properties: 
      DBSubnetGroupName: !Sub "${PJPrefix}-db-subnet"
      # サブネットグループの説明
      DBSubnetGroupDescription: "-"
      # 紐づけるサブネット(2こ以上)
      SubnetIds: 
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2

# # # ------------------------------------------------------------#
# # # Output Parameters
# # # ------------------------------------------------------------#                
Outputs:
# VPC
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJPrefix}-vpc"

  VPCCIDR:
    Value: !Ref VPCCIDR
    Export:
      Name: !Sub "${PJPrefix}-vpc-cidr"

# Subnet
  PublicSubnet:
    Value: !Ref PublicSubnet
    Export:
      Name: !Sub "${PJPrefix}-public-subnet"

  PublicSubnetCIDR:
    Value: !Ref PublicSubnetCIDR
    Export:
      Name: !Sub "${PJPrefix}-public-subnet-cidr"

  PrivateSubnet1:
    Value: !Ref PrivateSubnet1
    Export:
      Name: !Sub "${PJPrefix}-private1-subnet"

  PrivateSubnet1CIDR:
    Value: !Ref PrivateSubnet1CIDR
    Export:
      Name: !Sub "${PJPrefix}-private-subnet1-cidr"

  PrivateSubnet2:
    Value: !Ref PrivateSubnet1
    Export:
      Name: !Sub "${PJPrefix}-private2-subnet"

  PrivateSubnet2CIDR:
    Value: !Ref PrivateSubnet2CIDR
    Export:
      Name: !Sub "${PJPrefix}-private-subnet2-cidr"

# Route
  PublicRouteTable:
    Value: !Ref PublicRouteTable
    Export:
      Name: !Sub "${PJPrefix}-public-route"

# EC2
  WebServer:
    Value: !Ref WebServer
    Export:
      Name: !Sub "${PJPrefix}-web-server"
# RDS
  DBInstance:
    Value: !Ref DBInstance
    Export:
      Name: !Sub "${PJPrefix}-db-instance"

参考

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう
CloudFormationを使ってMySQLのRDSを構築する
Amazon RDS for MySQLインスタンス作成手順

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

作ったポートフォリオを簡単に解説してみる。

何を作ったのか?

アプリ名 Dig Memo

思考を深掘る事を意識して作ったメモアプリです。
下記URLで公開しております!
http://www.digmemo-app.xyz/

なぜ作ろうと思ったのか?

思考をサポートできるサービスを作りたいと思ったからです。

プログラミング学習をしている時に、ただ勉強するよりも思考して勉強する方が、知識が定着しやすいと感じました。しかし、ただのメモだと楽をしてインプットした情報をそのまま吐き出すこともできてしまうなと思いました。そこを制限して思考をサポートすることができるサービスがあったらいいのではないか?と思い、作ることにしました!

どんな機能を実装したのか?

機能一覧

・メモ投稿・編集・削除機能
・カテゴリー機能
・全文検索機能
・お気に入り機能
・ページネーション機能
・メール認証機能
・パスワードリセット機能
・ゲストログイン機能

ER図

Copy of Untitled Diagram (1).png

こだわった・工夫したポイント

ポイント1

問いを設定し、それに対して答える事で思考を促せるようにしました。

このアプリでは完全自由なメモはしにくいようになっています。言葉では説明が難しい部分もあるので動画を載せます。
DigMemo-how-to.gif
動画のようにこのアプリはタイトルを設定し、そのタイトルに対して自問自答を行います。こうする事で反強制的に思考することができます。

ポイント2

カテゴリーや問い作成機能にajax処理を用いました。

文字を打っている時「あ、この問いも追加したい」ってなった場合に、何も対策しなければ問いを作った後、それまで打っていた文字まで消えてしまいます。それを防ぐためにajax処理を用いて、UXを向上させました。
DigMemo-how-to-ajax.gif

ポイント3

テーブル結合で全文検索を実現させました。

検索機能ではメモのタイトルからだけではなく、メモの内容からも検索を行えるように実装しました。articlesテーブルのtitleカラムと、article_itemsテーブルのbodyカラムという二つのテーブルから取り出したデータを結合させることによって機能を実現させました。

DigMemo-how-to-search.gif

使用技術等

  • 言語: Ruby 2.7.1
  • フレームワーク:Ruby on Rails 6.0.3.2
  • テスト:RSpec
  • フロントエンド:HTML、SCSS、bootstrap4、javascript(jQuery)
  • インフラ:AWS(VPC・EC2・Route53・SES)
  • コード管理:GitHub
  • DB: MySQL
  • その他:docker-compose(開発環境のDBで使用)

追加実装予定

  • ELBを導入して冗長化する
  • ACMでhttps化する

最後に

ここまでご覧いただきありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

練習

練習です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む