20200731のAWSに関する記事は18件です。

AWS Honeycodeでガソリン管理アプリを10分で作ってみた

いつも忘れないように、コンセプトから。

コンセプト

・お金かけてまでやりたくないのでほぼ無料でAWSを勉強する
 →ちょっとしたサービスを起動すると結構高額になりやすい。
・高いレベルのセキュリティ確保を目指す
 →アカウントを不正に使われるととんでもない額を請求されるので防ぐ

前回と前々回に簡単にHoneycodeを調べてみました。
https://qiita.com/auf/items/08cde2ce3f88e0f3ed44
https://qiita.com/auf/items/b3a239adda19f7d8079f

今回はちょっとアプリでも作ってみようというトライです。何にしようかなと考えていたんですが、私は興味もあって、ガソリンの給油量を管理しているんですが、いつもはGmailの下書きにメモしておいて、あとでExcel集計していました。特段不便でもなかったのですが、そもそもガソリンスタンドで直接データを入れたら便利じゃね?という発想です。

ちなみに、勘ででやってみようということで、上記の2回調べた以上の前提知識はありません。実際に触るという意味では初見でした。が、想像以上に簡単にアプリが作れてしまい、ローコードって凄いな、と思いました。 
 
 

まずはデータを準備する

今回はスクラッチ開発を選びました。データは元々Excelで持っているのでCSV化します。テキストで見るとこんな感じ。
1.png

次にHoneyCodeにデータを取り込みます。CSVインポートがあるので簡単です。
2.png

無事にインポートされたので1行目のヘッダーだけ書き換えました。
3.png

アプリを作ってみる

今回はウィザードを選んでみました。これが超便利だったんですよ!!
4.png

はじめにソースとなるテーブルを選択します。
5.png

そうするともう表示アプリができました。完全自動で何もやってません。ここまで1分。。。
6.png

次に詳細表示アプリが作られます。これはいらない機能だなと思ったので後から消しました。
7.png

次に行追加のアプリです。テーブルにInsertされます。表示アプリのAddを押すとこれが呼ばれます。
8.png

はい、完成です。ここまで2分。
9.png

平均燃費を確認するアプリも作る

あまりにも簡単すぎて拍子抜けしたので、平均燃費を表示するアプリも作ってみました。同じように作るには、要はTableがあればいいわけなので、平均燃費の値が取れるTableを作ります。ソースデータは別Tableなので、MaxとSum関数を使いながら拾ってきました。ここら辺はExcelと同じように勝手に候補が表示されますし、テーブルやカラム候補も勝手に出てくるので簡単です。関数を調べて作るのに5分くらいという感じです。
10.png

作ったアプリをスマホから確認する

実際にここまでで10分もかかっていません。スマホからログインするともうアプリはできています。一番左がこれまでの履歴表示の画面で、真ん中が平均燃費の表示です。一番右側は、左画面の「+Add」を押すと遷移する画面で、走行距離、入れたガソリン、場所を入力できます。試しに入れてみましたが、Tableの一番下の行に入ってきました。
11.png
 
 
 
個人的にはかなり実用的なアプリが超簡単に作れてしまったので大満足です。現状ではβ版なので機能は限定されていますが、スマホで撮影したデータとかも簡単に送れるようになると思いますし、夢が広がりますね。GSPとかセンサーも拾ってこれないかなぁ。マジでいろいろ妄想が膨らみますww

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

Amazon Redshiftの一時停止/再開のDescriptionはどこに表示されるのか?

はじめに

AWSのRedshiftは停止/再開アクションをスケジュールで自動化することが可能です。
コンソールからスケジュール設定をする際に、スケジュールのDescriptionを付けることが
可能ですが、どこでDescriptionが確認できるのかがわからなかったので、探ってみました。

スケジュール設定

Descriptionを見ることのみが目的なので、設定は適当にします。
以下の赤枠の記載内容を後に確認します。
rapture_20200731223538.png

設定後の確認

Amazon Redshift > クラスター > 設定対象のクラスター > スケジュール(タブ)
赤枠のところが設定されたスケジュールですが、スケジュール名の隣にはDescriptionは表示されていません。
rapture_20200731223627.png

では次に、設定したスケジュールの「test-resume」をクリックします。以下の画面に移ります。
ここにも表示されていません。
rapture_20200731223649.png

どうやら現行のコンソールからDescriptionは確認できないようです。

AWS CLIから確認

AWS CLIの「describe-scheduled-actions」コマンドを使用すると確認できました。
"ScheduledActionDescription": "Scheduled pause and resume"と表示されています。

$ aws redshift describe-scheduled-actions
{
    "ScheduledActions": [
        {
            "ScheduledActionName": "test-pause",
            "TargetAction": {
                "PauseCluster": {
                    "ClusterIdentifier": "redshift-cluster-test"
                }
            },
            "Schedule": "cron(00 00 * * ? *)",
            "IamRole": "*****",
            "ScheduledActionDescription": "Scheduled pause and resume",
            "State": "ACTIVE",
            "NextInvocations": [
                "2020-08-02T00:00:00Z",
                "2020-08-03T00:00:00Z",
                "2020-08-04T00:00:00Z",
                "2020-08-05T00:00:00Z",
                "2020-08-06T00:00:00Z"
            ],
            "StartTime": "2020-08-01T00:00:00Z"
        },
        {
            "ScheduledActionName": "test-resume",
            "TargetAction": {
                "ResumeCluster": {
                    "ClusterIdentifier": "redshift-cluster-test"
                }
            },
            "Schedule": "cron(00 23 * * ? *)",
            "IamRole": "*****",
            "ScheduledActionDescription": "Scheduled pause and resume",
            "State": "ACTIVE",
            "NextInvocations": [
                "2020-08-01T23:00:00Z",
                "2020-08-02T23:00:00Z",
                "2020-08-03T23:00:00Z",
                "2020-08-04T23:00:00Z",
                "2020-08-05T23:00:00Z"
            ],
            "StartTime": "2020-08-01T00:00:00Z"
        }
    ]
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DynamoDBのデータをnode.jsのReadableStreamに流す

DynamoDBテーブルのデータをざーっと ReadableStream に流し込みたかったが、どうやってバックプレッシャーに対応すればいいのか分からなかったので対応させてみた。

やりたいこと

  • DynamoDBからデータを読み込む(基本は内部バッファサイズ分)
  • ReadableStream にpush
  • ひたすら読み込んではpush
  • 詰まったら一時停止
  • 次回開始位置をpushしきれなかったアイテムに設定
  • またpushできるようになったら続きから読み込み再開
  • 以下繰り返し
  • 全て読み込んでpushし終わったら処理終了

DynamoDBのQuery/ScanとLimit

DynamoDBのQuery/Scan操作では Limit を指定することで取得件数を制限することができる。
ただ Limit は検索結果の件数制限でなので、QueryFilterScanFilter を掛けると検索結果がさらにフィルタリングされ指定した件数より少ない件数が返ってくる場合がある。

クエリーで検索し、指定件数分の結果が揃ったら、クエリーフィルターでさらに絞る、というのはDynamoDBに限らずよくあるのでおそらく同じ動作なんだと思う。

まぁこれは Limit を指定してもフィルターを指定すると思ったとおりの件数は返ってこないですよ、という注意事項。

DynamoDBのページネーション

DynamoDBのQuery/Scanは読み込みサイズが1MBを超えるとそこで帰ってくる。Limit を指定した場合も途中で帰ってくる。
まだ全てのデータを読み込み終えてない場合、次ページを取得するための開始地点がレスポンスに含まれる。
レスポンスに含まれる LastEvaluatedKey を次回Query/Scan時のリクエストに ExclusiveStartKey として含めれば次ページを取得できる仕組みになっている。

LastEvaluatedKey は実際にはプライマリーキー(パーティションキーあるいはパーティションキーとソートキーの組み合わせ)のオブジェクトである。
なので LastEvaluatedKey でなく、レスポンスに含まれるアイテムの何番目かをぶち込めば次回の読み込み開始位置はその項目の次からになる。なった。

これを利用して、ReadableStream にpushしている途中で ReadableStream の内部バッファが一杯になった場合は、入り切らなかった次のアイテムを読み込み開始位置として ExclusiveStartKey に設定することにした。

ReadableStreamのハイウォーターマーク

ReadableStream は内部バッファを持っていて、そのサイズ=閾値をハイウォーターマーク(HWM)という。
オブジェクトモードの場合はデフォルトで 16 に設定されている。
データを push し続けてHWMに達すると、

  • pushfalse を返す
  • _read が呼ばれなくなる

それで内部バッファに空きがでると再び _read が呼ばれるらしい。
また pushfalse を返しても内部バッファには詰め込まれてた気がする。たしか。HWMが上がるんだったかな・・・

ということで、Query/Scanで取得したデータをpushしてる最中に false を返した場合はそこで処理を中断し、次のアイテム以降は残念ながら次回のQuery/Scan時に再取得することにした。

諸々踏まえて

最終的にこんな感じの実装になった。
切り貼りしてたのでこのままでは動かないかも。雰囲気を感じ取って欲しい。

class DynamoDBStream extends stream.Readable {
    :
  _read(size) {
    // 何度も呼ばれるので
    if (!this.#querying) {
      this.#querying = true;
      this.#params.Limit = size || this.hwm;
      this._query(this.#params);
    }
  }
  // HWMに達するまで再帰的にQueryを実行する
  _query(params) {
    const recursiveQuery = (params) => {
      // AWS.DynamoDB.DocumentClient.query()
      this.#ddb.query(params, (err, data) => {
          :
        let abort = false;
        for (const item of queryResult.Items) {
          // HWMに達したらそのitemまで処理済みとし次回の開始位置を指定して中断
          if (!this.push(item)) {
            params.ExclusiveStartKey = item;
            abort = true;
            break;
          }
        }
        // 中断したら次の読み込み要求を待つ
        if (abort) {
          this.#querying = false;
        // 次ページがなかったら処理終了
        } else if (typeof queryResult.LastEvaluatedKey === "undefined") {
          this.push(null);
          this.#querying = false;
        // 中断せず、次ページがあれば続けて次ページを取得
        } else {
          params.ExclusiveStartKey = queryResult.LastEvaluatedKey;
          recursiveQuery(params);
        }
      });
    }
    recursiveQuery(params);
  }
    :
}

余談: DynamoDBのコスト

リクエスト単位で課金される。また、結果整合性のある読み込みが一番安い。
Limit を小さく指定しすぎるとリクエスト数が増えてしまい、ちゃりんちゃりん課金されてしまう。

かと言って、あんまり Limit を大きくしてしまうと、詰め込みきれなかったデータが無駄になってしまうのでうまく調整しないといけない。

頻繁にリクエストが細分化してしまう場合は、いっそのことStreamとは別にバッファを持ったほうがいいかもしれない。

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

EC2をリバースプロキシサーバ化してElasticSearchServiceのAPIを外部から叩けるようにする

経緯

Amazon Elasticsearch Serviceを使用してELasticSearchの検証環境を用意したが
Globalに公開できず検証しづらかったので、EC2をプロキシサーバにして外部からAPIを叩けるようにした。
セキュリティ的な問題はあるが検証環境であるということと自宅IPに絞ったので今回は甘めに

ざっくり構成

構成図.png

EC2を作成

EC2を作成し、PublicSubnetを紐づけます。

EC2用のセキュリティーグループを作成し、EC2に紐づける

今回EC2はHTTPでAPIを叩くようとSSHでリバースプロキシの設定をするために
インバウンドルールで80ポートと22ポートを解放するようにセキュリティーグループを設定します。

スクリーンショット 2020-07-10 19.06.01.png

ElasticSearchを作成

ElasticSearchを作成し、PrivateSubnetを紐づけます。

ElasticSearch用のセキュリティーグループを作成し、ElasticSearchに紐づける

ElasticSearchはEC2からのみのアクセスを受け付けたいので、
インバウンドルールにポート443 ソースにEC2のセキュリティーグループを設定します。
スクリーンショット 2020-07-10 19.16.55.png

EC2をリバースプロキシ化

今回はnginxで設定していきます。

sudo yum update -y
# nginxのインストール
sudo amazon-linux-extras install nginx1.12 -y
sudo vim /etc/nginx/nginx.conf

/etc/nginx/nginx.conf内に
locationの記述があると思うのでそちらにElasticSearchのVPCエンドポイントを記載します

/etc/nginx/nginx.conf
location / {
    proxy_pass ElasticSearchVPCエンドポイントを記載;
}
# nginxの起動
sudo systemctl start nginx
# EC2起動時に動くように
sudo systemctl enable nginx

EC2のpublic IPをブラウザ等で叩く

{
  "name" : "hogehoge",
  "cluster_name" : "hoge",
  "cluster_uuid" : "hogeid",
  "version" : {
    "number" : "7.4.2",
    "build_flavor" : "oss",
    "build_type" : "tar",
    "build_hash" : "unknown",
    "build_date" : "2020-05-05T04:47:06.936807Z",
    "build_snapshot" : false,
    "lucene_version" : "8.2.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

成功?

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

「ほん訳コンニャク」 を食べて 論文を読もう

TL;DR;

以下の「ほん訳こんにゃく」を作った。

Translation-Gummy.png

作りたかったもの

 研究室配属で、かねてから取り組みたかった 「シナプス可塑性におけるmiRNA機能とそれらが記憶や学習などの高次認知機能に与える影響の解明」 を自分の研究テーマ(※暫定)にすることができ、生物系の論文を読む機会が圧倒的に増えたのですが、元々深層学習系統の論文しか読んでいなかったため、常識や背景知識不足に悩まされ、DeepLGoogle Translateなしには論文が読めないという日々が続いていました。

 論文を読みながら適宜翻訳するのは非効率ですし、iPadにPDFを保存して電車内で読むなんてこともできず、かといって全部一度翻訳するのは非常に面倒で、「これ、自動化したいな〜」と思ったので、「論文のURLを投げれば、全文翻訳付きのPDFを生成するプログラム」を作りました。

Python Package

image.png

 まず、Pythonでライブラリ(Translation-Gummy)を作成しました。先に述べたことを実現するために必要な機能は

  1. URLから論文の内容をスクレイピング → requests,Beautiful Soup,pdfminer, pylatexenc
  2. スクレイピングした内容(英語)を日本語に翻訳 → selenium, DeepL, Google Translate
  3. 両者を並べたPDFを作成 → jinja2,pdfkit

ですが、それぞれ右に記した各種ライブラリ/サービスを使えば、実現可能です。

 なお、翻訳の際に seleniumWebDriver を使っていますが、これは以下のように requests.get でサイト(https://translate.google.co.jp/#ja/en/これはペンです)の内容を取得しても、動的なサイトであるため翻訳結果を取得することができないためです。

# coding:utf-8
import requests
from bs4 import BeautifulSoup

html = requests.get("https://translate.google.co.jp/#ja/en/これはペンです").content
soup = BeautifulSoup(markup=html, features="html.parser")
:

※ より詳しいプログラムコードが見たい方は、Githubからどうぞ。

PDF converter

 
 ローカルにあるPDFファイルを翻訳してくれるWebサイトがあれば良いなと思い、作成しました。ここでは pdfminerを用いて翻訳を行っていますが、かなり雑な実装になってしまっているので、Pull requests をお待ちしています。gummy.journals.LocalPDFCrawler というクラスを用いています。)

 なお、WebサイトはAWSのEC2を使って公開しております。

image.png

 PythonしかかけないけどWebサイトを公開したい、という方はこの記事:『PythonかければWebアプリぐらい作れる。』が参考になるかと思います。(自分の記事を紹介してしまい、すみません。)

Slack App

 Slack App として、コマンドで呼び出せれば楽だと思い、作成し公開しました。

Screen Shot 2020-07-31 at 16.34.44.png

 この時、

  • OAuth認証を用いてワークスペースに基づいた SLACK ACCESS TOKEN を取得し、BOT投稿するためにPrivate Subnet内のRDSに SLACK ACCESS TOKEN を保存する。
  • lambdaからデータベースへの多すぎるコネクションにより過負荷になることを避けるため、RDS PRoxyを使用し、コネクションプールを確立および管理をする。
  • Slack Botに3秒以内にレスポンスを返さないといけないためlambdaからlambdaを非同期で呼ぶ。
  • Private Subnet内のlambdaから直接lambdaを呼べないため、Public subnetに配置したNAT Gatewayを利用する。
  • 負荷を分散するためにLoad Balancerを設置する。

などの機能を実現するため、以下のような少し複雑な構成となってしまいました。(改良点等あればコメントでご指摘ください?‍♂️)

image.png

終わりに

 自分の欲しい機能を全て実現したサービスができたと思います。対応ジャーナルの数を増やしたり、PDFの解析をより正確に行ったり、PDFをより綺麗に整えたりとまだまだ足りない機能は多々ありますが、使っていただけると幸いです。

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

Lake Formation の使い方的な③(クロスアカウントアクセス)

クロスアカウントアクセスとは

ログ収集AWSアカウント(データレイクAWSアカウント)のデータカタログを、別のAWSアカウントからアクセスして利用する。

S3上の実データもGlueデータカタログのテーブル情報などのメタデータもログ収集アカウントにある状態で、別のAWSアカウントからリソースリンク(シンボリックリンクみたいな定義)を介して権限制御しつつアクセスできる。
例えばログ収集AWSアカウントのGlueテーブルを、別のAWSアカウントのAthenaで使いクエリ実行する。

前提環境

  • AWSアカウントA:ログ収集用アカウント
  • AWSアカウントB:それ以外の自社サービスで使うアカウント

アカウントAの操作

アカウントAの状況

Athenaでクエリできている

S3にデータがあり、Glueでデータベースやテーブルを作成し、以下のようにAthenaでクエリができる状態

データベース名:default
テーブル名:in0

スクリーンショット 0002-07-31 11.59.12.png

Glueでテーブル確認

スクリーンショット 0002-07-31 12.03.05.png

LakeFormationで権限管理されている

DataLakeLocationに、データがあるS3パスが登録されている
この登録したS3パスに対してLakeFormationはアクセス許可を行える

スクリーンショット 0002-07-31 12.04.58.png

DataLocationsでロールにS3パスへのアクセス許可を与えている
Glueクローラがテーブル作成するために、Glueクローラが使うロールにアクセス許可を与えている

スクリーンショット 0002-07-31 12.07.38.png

共有する

IAMAllowedPrincipalsの許可削除

IAMAllowedPrincipalsに対しての許可があると今回の共有ができないので、これらを削除します。
このプリンシパルは従来のIAMのみのアクセス制御との後方互換のためにあるので実際やる時はご注意ください。
ここでは、IAMAllowedPrincipalsが含まれた許可が2つあります。defaultデータベースへの許可とin0テーブルへの許可です。それらを削除(Revoke)します。

スクリーンショット 0002-07-31 12.10.28.png

データベース側に「Use only IAM access control for new tables in this database」(このデータベースの新しいテーブルにIAMAllowPrincipalsを付ける)の設定があるか確認します。あればチェックを外しておきます
今回はdefaultという名前のデータベース

スクリーンショット 0002-07-31 12.14.41.png

アカウントBに共有する

DataPermissionsで[Grant]をクリックする。
"External account"にチェックを入れ、AWS account IDでアカウントBのIDを選択する。
※同じAWS Organizationに属するアカウントだとリストに表示されます。そうでない場合はRAM(AWS ResourceAccessManager)で共有する必要があります。
データベースは共有したいデータベースのdefault、テーブルは全てとしました。Permissionもテストなのでとりあえず全部チェック入れます。

スクリーンショット 0002-07-31 15.42.37.png

Permissionが作成されるとこんな感じです。
プリンシパルがAWSアカウントBで、アカウントBがアカウントAがオーナーのテーブルにアクセスする許可を与えています。

スクリーンショット 0002-07-31 12.15.28.png

(補足)今回スイッチロールして各AWSアカウントにログインしています。IAMAllowedPrincipalsを削除したので、アカウントAで行っていたAthenaでのクエリは失敗します。引き続きAthenaでクエリする必要があれば、スイッチロールに使っているロールに引き続きAthenaクエリできるように許可付与します。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f32373933322f39303434346366382d656233342d376130352d663938312d6138376462333737306465382e706e67.png

アカウントBの操作

データベースやテーブルを確認

共有されたアカウントAのデータベースやテーブルが確認できる。

スクリーンショット 0002-07-31 12.17.23.png

Resource Linkの作成

Resource Linkというものを作ります。元のデータ/テーブルを指し示すシンボリックリンクみたいなものでしょうか?
作成すると普通にテーブルのように見えます。テーブルの[Create]をクリックし、以下の値を入力し[Create]をクリックします。

  • Resource linkにチェック
  • Resource link name:share-in0 (なんでもいい)
  • Database:lf01 (Resource linkを保存するアカウントBのデータベース)
  • Shared table:in0 (アカウントAの共有したテーブルin0)

スクリーンショット 0002-07-31 16.06.29.png

作成されたResource Linkです。share-in0という名前です。

スクリーンショット 0002-07-31 16.56.14.png

View Dataしてみます。これはAthenaでのクエリ実行です。

スクリーンショット 0002-07-31 16.57.58.png

アカウントBからAthenaでResource Linkを使い、アカウントAのデータをクエリできました。

スクリーンショット 0002-07-31 16.59.28.png

ログ

CloudTrailのログも指定したアカウントに集約できそうです。

レイクフォーメーションは、データレイク内のデータへのすべてのクロスアカウントアクセスの集中監査証跡を提供します。受信者のAWSアカウントが共有テーブルのデータにアクセスすると、レイクフォーメーションはCloudTrailイベントを所有アカウントのCloudTrailログにコピーします。コピーされたイベントには、Amazon AthenaやAmazon Redshift Spectrumなどの統合サービスによるデータに対するクエリ、およびAWS Glueジョブによるデータアクセスが含まれます

こちらも是非

公式ドキュメント(クロスアカウントアクセス)
https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/access-control-cross-account.html

公式ドキュメント(AWSアカウント間でデータカタログなどの共有)
https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/sharing-catalog-resources.html

公式ドキュメント(リソースリンクの作成)
https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/creating-resource-links.html

Lake Formationの使い方まとめ
https://qiita.com/pioho07/items/76554a7ac4252858b450

Glueの使い方まとめ
https://qiita.com/pioho07/items/32f76a16cbf49f9f712f

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

【AWS×WordPress】504 Gateway Time-outが出た

はじめに

AWS(AWS Linuxインスタンス)上でWordPressをたてて使用していたが、ある日突然504 Gateway Time-outが出た。
スクリーンショット 2020-07-31 8.31.56.png

504 Gateway Time-outの意味

Wikipediaによると、5XXはサーバエラーを指し、504は「ゲートウェイタイムアウト。ゲートウェイ・プロキシサーバはURIから推測されるサーバからの適切なレスポンスがなくタイムアウトした。」を意味する。サーバ側のエラーということでAWS上での問題っぽそう。

色々試してみる

試したこと①

AWS上に構築したWordPressで「504 gateway timeout」が頻発した時の対処法
ALBのターゲットグループをhttp&https→httpに変更したということだが、もともとhttpだった。

試したこと②

【504 Gateway Time-out】【Bad Request】でブログがつながらない!AWSのELBをチェック
DNS名・IPアドレス直打ちで入れたらアクセスできたというが、今回はできず。

試したこと③

【WordPress】504 Gateway Time-out が出た時の対処法

$ df -h

ストレージの容量を確認できるとのことだが、使用% を見ても、特に割合が高くなかった。

試したこと④

【AWS】【WordPress】「504 Gateway Timeout Error」の原因
Apacheが起動しているかを確認するとのこと。

$ sudo systemctl status httpd.service

この時Active: active (running)になっていたため、てっきりApache側での問題ではないと勘違いしていたが、これは間違いだった。

解決策

$ sudo systemctl restart httpd.service

今回はApahceがいつの間にか落ちてしまったらしく、上記コマンドでApacheを再び起動させ、504エラーが解消されWordPressの画面が表示された。
statusで確認してもたまにうまく反映できてないことがあるらしいので、psコマンドでちゃんとプロセスが起動しているかを確認する方が確実とのこと。

反省点

ネット記事を探して手当たり次第、試していたが下記記事のようにもう少し問題点を切り分けて行うべきだった。

インフラ苦手な人が知っておくといい、Webサイトにつながらない障害パターンと解決方法

ググった方が早い場合もあるが、インフラ力を高めるためにもログなどで原因を予測した上で確認したい。

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

PHPでAWS CLIを用いてS3に画像をアップする

EC2環境上でPHPのexec関数とAWS CLIを用いてS3に画像をアップしようと思ったら、嵌ったので備忘録メモです。

まず、事前にEC2上で

$ aws configure 

を叩いて、アクセスキー ID、シークレットアクセスキー、AWSリージョン、出力形式を入力した後に、PHPファイルに

exec('aws s3 sync  ./upload  s3://{バケット名}/img --acl public-read --delete', $out);

を挿入し、実行してみましたが$outに結果が返らず、S3にもアップされませんでした。
そこで、PHPのファイル上に事前に次の形式でアクセスキー ID、シークレットアクセスキー、AWSリージョンを記述したあとにexec関数を記述すると動作しました。

$region = 'ap-northeast-1';
$key = '****';
$secret = '**********************';
putenv('AWS_DEFAULT_REGION=' . $region);
putenv('AWS_ACCESS_KEY_ID=' . $key);
putenv('AWS_SECRET_ACCESS_KEY=' . $secret);
exec('aws s3 sync  ./upload  s3://{バケット名}/img --acl public-read --delete', $out);

ただし、PHPファイルにキー情報を直接書いてしまうのはセキュリティ的に問題ありそうなので、APPACHと同じキーをユーザーに割り当てる方法もあるようです。

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

CloudWatchEventsの変数と定数をAWS BatchのJobコマンドに渡したい

やりたいこと

CloudWatchEvents(CWE)を使ってAWS BatchにJobを投入する。
JobコマンドではCWE動いた時の時刻と任意の定数を使いたい。
そのために必要なものを設定していく。

Jobコマンドでやりたいことのイメージ

$ exec --time {時刻} --target {定数}

Jobコマンドで変数や定数を扱うために

CWE動いた時刻や任意の定数をコマンドで扱うために、CWEから値を受け取って扱う。
Jobでは受け取った値が特定のJSON形式でないと扱うことができず、コマンドで扱う際は特殊な書き方をする必要がある。

Jobが扱える形式

Jobコマンドから扱えるのはJSON形式のParameters配下のプロパティのみ。
この形式を考慮してCWEのインプットの設定をする必要がある

{
    "Parameters":{
        "time": "2015-11-11T21:29:54Z",
        "target": "01234"
    }
}

Parameters配下のプロパティでもオブジェクト形式だと扱えない。
そのためプログラムでオブジェクトや配列を扱いたい場合は、jsonを受け取ってプログラム内でパースする。

{
    "Parameters":{
        "time": "2015-11-11T21:29:54Z",
        "targets": "{\"targets\":[\"01234\", \"56789\"]}"
    }
}

Jobコマンドで受け取った値を呼び出すための設定をする

受け取った値はRef::パラメータ名で呼び出すことができる。
ジョブ定義でコマンドの引数にパラメータが入るように設定しておく。

$ exec --time Ref::time --target Ref::target

CWEの情報をターゲットに渡すために

CWEはターゲットに対して情報を渡すことができる。
渡す情報は以下の四種類がある。

  • 一致したイベント
    • 時刻やアカウントIDなど、CWEがもつ変数
  • 一致したイベントの一部
  • 定数(JSON テキスト)
    • 任意の定数
  • インプットトランスフォーマー
    • CWEがもつ変数と定数の両方を渡すことができる

今回は変数と定数の両方を渡すのでインプットトランスフォーマーを使う。

インプットトランスフォーマーを設定する

インプットトランスフォーマーはマッピングとテンプレートの二種類を設定することでターゲットに定数を変数を混ぜた値を渡せるが、設定が面倒。

マッピングではCWEの変数をテンプレートで扱うために、変数と対応する名前を設定する。
テンプレートでは実際にターゲットに渡す情報を設定する。
どちらもJSON形式で指定する。

マッピング

変数$.timeに対して、mytimeと言う名前をつけている。

{
    "mytime":"$.time"
}

テンプレート

JSONの形式で定数を表現する。
<名前>形式を使うことでマッピングで設定した変数を組み込むことができる。
変数を呼び出す際は<>を""で挟むと文字列として認識されてしまうため、""で挟んではいけない。

{
    "Parameters":{
        "time": <mytime>,
        "target": "01234"
    }
}

ターゲットに渡される値

<mytime>の部分が実行時間に置き換わってターゲットに渡される

{
    "Parameters":{
        "time": "2015-11-11T21:29:54Z",
        "target": "01234"
    }
}

別のアプローチ

別のアプローチなら面倒なインプットトランスフォーマーは不要だった、、、

定数をジョブ定義設定する

ジョブ定義のコマンドを以下のようにしておけば、CWEで定数を扱わなくなる。
変数だけなら面倒なインプットトランスフォーマーじゃなくて良い。

$ exec --time Ref::time --target "01234"

変数をコマンドから取得する

コマンドで動き出した時刻を取得すれば、CWEで変数を扱わなくなる。
定数だけなら面倒なインプットトランスフォーマーじゃなくて良い。

$ exec --time `date --iso-8601="minutes"` --target Ref::target
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】暗号化されたスナップショットからボリュームの再作成ができない

問題

暗号化されたボリュームのスナップショットからボリュームの再作成ができない。
リクエストは成功するけど、コンソール上に一向に表示されない。
暗号化されていないボリュームを、暗号化ボリュームとして再作成することはできてたのに。

原因

KMSポリシーにて、IAMユーザにkms:ReEncryptがアタッチされていないことが原因でした。
CloudTrailのイベント履歴からフィルター:イベント名:ReEncryptと検索してみると、当該リクエストがエラーコード:AccessDeniedとなっていました。

解決

IAMにkms:ReEncryptがアタッチして、ボリューム作成するとすぐにコンソールにひょうじされました。暗号化(Encryp)と再暗号化(ReEncryp)で権限が区別されていることに注意が必要ですね。

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

【初学者向け】Route53でドメイン取得してポートフォリオに紐つける手順

前提条件

EC2でアプリをデプロイしている事
→もしまだデプロイ出来ていないのならこちらの記事を参考にしてください。私も同じ様にやってきました。

手順

他にも沢山の参考記事がある為そちらを参考にしましたが、ドメイン取得に関してはこちらの記事で問題なく出来ます。
ただしこの記事にもある様にnginxで確実にエラーが出ます。
nginx.png

なので、デプロイの時と同様に

[name(自分の名前)|~]$cd /etc/nginx/conf.d/

[name(自分の名前)|conf.d]$ sudo vi ◯◯.conf #自分のアプリ名

# log directory
error_log  /var/www/rails/〇〇/log/nginx.error.log; #自分のアプリケーション名に変更
access_log /var/www/rails/〇〇/log/nginx.access.log; #自分のアプリケーション名に変更
# max body size
client_max_body_size 2G;
upstream app_server {
  # for UNIX domain socket setups
  server unix:/var/www/rails/mumu/tmp/sockets/.unicorn.sock fail_timeout=0; #自分のアプリケーション名に変更
}
server {
  listen 80;
  server_name ~~~.~~~.~~~.~~~;(#アプリのElastic IPに変更してください)←ここを取得したドメインに書き換えてください
  # nginx so increasing this is generally safe...
  keepalive_timeout 5;
  # path for static files
  root /var/www/rails/〇〇/public; #自分のアプリケーション名に変更
  # page cache loading
  try_files $uri/index.html $uri.html $uri @app;
  location @app {
    # HTTP headers
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }
  # Rails error pages
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /var/www/rails/〇〇/public; #自分のアプリケーション名に変更
  }
}

保存出来たらnginxを再起動します。

[name(自分の名前)|conf.d]$ cd /var/www/rails/〇〇 #自分のアプリ名
[name(自分の名前)|〇〇]$ sudo service nginx restart

Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]

こうなっていれば大丈夫です!
後は取得したドメインを検索し問題なくサイトを開けるか確認してください!

追記

まだSSL証明などは出来ていないので完成したら投稿します!

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

Elastic Beanstalkのデプロイポリシーまとめ

Elastic Beanstalkのデプロイポリシーまとめ

AWS デベロッパーアソシエイトを勉強中です。

公式がこちらのページで説明していますが、
文章が分かりづらく、整理のために投稿します。
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/using-features.rolling-version-deploy.html

前提

  • ELBが1つ
  • ELBに4つのEC2インスタンスが紐付いている

ローリングの時に使用するバッチについて説明

設定の画面で、バッチサイズの割合を決めることができると思います。

その割合と、インスタンス数を掛け算したものが、
バッチのインスタンス数になります。


バッチサイズ:25%
インスタンス数:4台
0.25(25%) × 4(台) = 1(台)

All at once(一度に全て)

説明

名前の通り、全てのEC2インスタンスに対して、
同時にデプロイを行います。

デメリット

短時間ではありますが、サービスが停止する時間があります。

それぞれのインスタンスで、デプロイ完了までの時間差が生まれ、
新旧アプリが混在する時間が存在します。

Rolling (ローリング)

説明

  1. バッチサイズ分のインスタンス数、ELBからEC2インスタンスをデタッチします。

  2. デタッチしたインスタンスに対して、デプロイをします。

  3. デプロイしたインスタンスにヘルスチェックをかけ、問題なければELBにアタッチされます。

全てのインスタンスにデプロイされるまで、1~3を繰り返します。

デメリット

デタッチされている分、ELBで捌けるインスタンス数が減るので、パフォーマンスに影響が出る可能性がある。

新旧アプリが混在する時間が存在します。(All at onceより基本長い時間)

Rolling with additional batch (追加バッチによるローリング)

説明

  1. バッチサイズ分のインスタンスを新規立ち上げ、デプロイを実行します。

  2. ヘルスチェックをし、問題なければ、ELBにアタッチします。

  3. 2でアタッチした分、ELBからデタッチし、デプロイを実行

  4. デプロイ済みのインスタンスをELBにアタッチします。

3~4を繰り返すことで、全てのインスタンスを更新します。

Rollingでは、一時的にリクエストを受け付けるEC2インスタンスの数が減っていたが、
こちらでは、新規立ち上げするため、減りません。

実際の本番環境では、こちらが一番よく使われるみたいです。

デメリット

すごく短時間だが、EC2インスタンスの数が増え、料金がかかってしまう。

こちらも新旧アプリが混在する時間が存在します。

Immutable (変更不可)

説明

  1. EC2インスタンス実際に稼働している分とを同じ数、用意し、そちらにデプロイします。

  2. ヘルスチェックに通るかどうかを確認します。

  3. 問題なければ、EC2インスタンスを実際に稼働しているELBにアタッチします。

  4. 古いEC2インスタンスをデタッチし、終了させます。

デメリット

一時的に、EC2インスタンスの数が倍増し、費用がかかる。

こちらも新旧アプリが混在する時間が存在します。

新旧アプリが混在する時間をなくしたい場合

Blue-Green Deploymentを使いましょう。

Blue-Green Deploymentとは

Elastic Beanstalkのクローンを作成し、
そちらにデプロイをします。

動作に問題なければ、環境URLのスワップを行うことで、
新旧アプリが混在する時間がなくなります。

詳しくはこちら
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/using-features.CNAMESwap.html

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

【初心者】AWS Cloud Practitioner Essentials (Second Edition) をやった

はじめに

AWSにはトレーニングの一環としてAWS Cloud Practitioner Essentials (Second Edition)というコンテンツが用意されています。

レベル感は「基礎レベルのコース」であるため、AWSのプラクティショナーを目指して勉強を始めた方向けくらいだと思います。

実際にこちらを一通り視聴し、学習した結果の所感を書いてみようと思います。

行ったコンテンツ

  • M0-コースの紹介
  • M1-AWSクラウドの紹介
  • M2-AWSの主要なサービス
  • M3-AWSの統合サービス
  • M4-AWSのアーキテクチャ
  • M5-AWSのセキュリティ
  • M6-料金とサポート
  • M7-コースのまとめ
  • コースの最後の知識の確認

前提条件

  • AmazonアカウントまたはAPNパートナーセントラルアカウントを所持していること。

注意事項

  • 以下の知識があること(推奨)

    • IT技術の一般知識
    • ITビジネスの一般知識
  • 所要時間

    • 6時間
      6時間と言っていますが、まじめに受けるともっとかかります。詳しくは後述します。
  • 実施形式

    • デジタルトレーニング
      画面に映し出されたスライドを見ながら解説を聞く。
  • 受講料

    • 無料

受講する上での問題点

  • 英語での解説

    (Japanese)と書いてあるものの、解説は英語で行われます。
    幸いにも字幕があり、パワーポイントも日本語ですが、英語の聞き取りができる方でないと字幕を常に目で追いかけて受講することとなります。

  • 解説スピード
    進むスピードが結構速いです。
    パワーポイントを見ながら、字幕も見て、メモをとるということをする必要があるため、適宜動画を止めて考えをまとめる時間が必要だと感じました。
    そのため、上記の6時間に収まりきらなかったです。

各モジュールについて

M0-コースの紹介(2分)

このコースでどういったものを学ぶのかの解説がされます。
特筆するようなことはないので飛ばしてもいいと思います。

M1-AWSクラウドの紹介(20分)

クラウドの概念的な話です。クラウドを利用することによりどういった利点があるのかという話を聞いた後、AWSの3つのインタフェース

  • AWSマネジメントコンソール
  • コマンドラインインターフェイス(CLI)
  • ソフトウェア開発キット

の使い方について学びます。
それぞれGUI,CUI,SDKとなっています。
基本的にはGUIのマネージドコンソールは簡単にクリックだけでリソースを立ち上げたりできることが特徴です。
CLIはLinuxのコマンドが使え、慣れてきたらこちらのほうをメインにすることが多いのかなと思います。

M2-AWSの主要なサービス(45分)

  • EC2(Amazon Elastic Compute Cloud)
    汎用型の仮想サーバのリソース

  • EBS(Amazon Elastic Block Store)
    EC2インスタンスに接続できるブロックレベルのストレージ

  • S3(Amazon Simple Storage Service)
    AWSの高い耐久力と拡張性があるオンラインストレージ

  • VPC(Amazon Virtual Private Cloud)
    リージョン内にAZをまたがって作成できるプライベートなネットワーク

といった基本的なAWSのサービスについての説明を受けます。
触り位までAWSを学習した方は知っているような内容でした。
ユースケースの説明があったのがよかったです。

M3-AWSの統合サービス(100分)

  • Application Load Balancer
  • Auto Scaling
  • Amazon Route53
  • Amazon Relational Database Service(RDS)
  • AWS Lambda
  • AWS Elastic Beanstalk
  • Amazon Simple Notification Service(SNS)
  • Amazon CloudWatch
  • Amazon CloudFront
  • AWS CloudFormation

総合サービスとして以上のサービスそれぞれの概要を学びます。
AWS Elastic Beanstalkなどは初めてみたサービスで使い勝手がよさそうでいいなと思えました。
全体を通して知りたいなと思ったような内容はしっかりと解説されており、使えるサービスの選択肢が個人的に多くなったなと受講して感じました。

M4-AWSのアーキテクチャ(30分)

AWSクラウドへのデプロイや移行の際のアーキテクチャ上の検討事項についての解説を受けます。
Well Architected フレームワークの5つの柱

  • セキュリティ
  • 信頼性
  • パフォーマンス効率
  • コスト最適化
  • 運用上の優位性

を中心とした解説になります。
また、オンプレとの「耐障害性・高可用性」の比較もされていました。
オンプレとの比較をされていたので、移行する際にどういった利点があるのかがわかりやすくまとめられていました。

M5-AWSのセキュリティ(50分)

AWSのセキュリティにおける考え方を学びます。
まず、責任共有モデルと呼ばれるAWS上でシステムを動かしていく中で考えなければならない責任問題について学びます。
その後

  • Amazon Inspector(システムの評価)

  • AWS Shield(DDoS攻撃に対する対策)

を学びます。
具体的にAWSでシステム動かすうえで責任がどこに生じるかなどを知りたい方はこちらのコンテンツを参照するといいかと思います。

M6-料金とサポート(30分)

AWSの料金体系について学びます。
基本は従量課金制で使った分だけの支払いとなるのですが、リソースごとに「使った」とカウントされるものが異なります。
そのため、それぞれのリソースでどういう形で請求されるのかを調べる必要があるのですが、本コンテンツではリソースごとに解説されています。

  • EC2
  • ELB
  • モニタリング
    • Amazon Cloud Watch
    • Auto Scaling
    • Elastic IP アドレス
  • ソフトウェア
  • S3
  • Amazon EBS
  • Amazon RDS
  • AWS サポート

M7-コースのまとめ(1分)

M0-コース紹介と同じような感じです。受ける必要性は感じません。

コースの最後の知識の確認

コース全体を通しての知識確認です。
プラクティショナーに出てくるような問題が出題され、解いていく感じです。

まとめ

全体を通してプラクティショナー出題範囲が大まかにカバーされていると感じました。
また、プラクティショナー取得予定がない方もセキュリティ面や基本的なサービスなどAWSを扱ううえで参考にできる部分はあるのかなと思います。

しかし、言語が英語であることを考えると日本語でプラクティショナー対策ができるイベントはほかにもあると思うので、そちらを先に受講していただき、知識定着や深めるといった意味で閲覧されるのがいいと思います。

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

はじめてのアマゾン ウェブ サービス for Developerを受けてみて...

はじめに

AWSには「はじめてのアマゾンウェブサービスforDeveloper」というセミナーがあります。

はじめてのアマゾンウェブサービス」という名前に釣られ、参加してみました。

開発経験がない新卒の自分でも「今までAWSについて調べたことを総動員すれば楽勝だろう」と受ける前は思っていました。

が、いざセミナーを受けてみると...
セミナー前半は基本的なAWSの話で、理解は簡単にできましたが

後半の「クラウドを活用した開発手法のトレンド」がDeveloper向けで、オンプレミスの開発経験すらない自分にとっては、何言ってるんだ???状態でした。

ですがそのままにしておくのはもったいないので、自分なりに他ソースも利用して簡単にアウトプットしてみようと思います。

大まかな流れ

  • クラウドの基本的な特徴

  • AWS のビジネス規模および成長率

  • 主要サービスのまとめ

  • クラウド のセキュリティ

  • クラウドを活用した開発手法のトレンド

    • Microservices / Serverless
    • Infrastructure as a Code / DevOps
    • 非同期型処理

クラウドを活用した開発手法のトレンド

Microservices

クラウドネイティブ開発の場合、取り入れると有効であると思われるアーキテクチャです。

  • それぞれのリソースをブラックボックス化してしまい、定義されたAPIでやり取りをする。

  • 他のリソースへの影響を考えずに機能改善が行えるため、楽。

ということでした。

  • メリット

    • サービスごとに小規模なチームで開発を行う為、自主的かつ迅速に仕事に取り組める。
    • サービスごとにスケーリングを行えるため、効率化できる。
    • デプロイが容易なため新しいアイデアを試すことや、うまくいかなかった際にロールバックすることが簡単にできる。
    • 一つのサービスに障害が発生してもアプリケーション全体のクラッシュは回避できる。

マイクロサービスの対義語には「モノリシック」というものがあり、全体で単一のサービスとして実行されるものを言います。

参考:マイクロサービス


AWS Lambda(Serverless)

ここが正直一番具体的に想像しにくい部分でした。

そもそもAWS Lambdaは、「ただリソースにくっつけてトリガーで書いてあるコードを実行するもの」だという認識があり、Serverlessといわれたときには、???という感じでした。

普通に考えれば当たり前なんですけどね...

  • Serverless

システムの実行には、

  1. プログラムを書き
  2. 実行する

という手順が必要です。

したがって、従来のシステムではプログラムを実行するための実行環境のサーバの構築やメンテナンスをする必要があります。

実行環境であるサーバにリクエストが集中して実行環境に障害が起きてしまうと元も子もないため、リクエストが増えることがわかっている場合にはスケーリングする必要があります。

そういった煩わしさをなくしたのが、サーバレスコンピューティングを担う「AWS Lambda」です。

  • AWS Lambdaのメリット
    • Lambdaの実行環境設定をすれば、設定をもとにAWSが面倒を見てくれる
    • 複数の言語サポート(アクセスがまばらの場合は初動が速いPythonなどがおすすめ)

ということでした。

料金体系は

サービス 料金体系
Lambda 実行したリクエストと時間分だけ料金発生

となっています。

参考:サーバーレスって何が便利なの ? AWS でサーバーレスを構築するためのサービスをグラレコで解説


Infrastructure as Code

こちらは概念的に理解するのか少し難しかったです。
簡単に言うと「インフラストラクチャーをコード化する」ということみたいです。
そのまんまですね。

  • リソースは API 経由でプロビジョニング
  • 定義ファイルによる効率化と自動化
  • エラーやセキュリティ違反の除去

といったメリットがあります。
AWSではAWS CloudFormationを用いて実現することができるようです。


非同期型処理

  • Amazon SQS
    • 完全マネージド型のメッセージキューイングサービス
    • AWSで非同期型処理をやるならこれ

ECサイトを例にとった具体的な構成図は以下の通りです。
非同期処理 (1).png

非同期処理をしない場合には、ユーザが注文をした際に在庫を確認し、発送できる状態であるかの確認をするまで待機してもらうことになるのですが、AmazonSQSを間に挟むことによってフロントエンドとバックエンドを非同期にすることができ、ユーザビリティの向上につながります。

まとめ

ここまで「はじめてのアマゾンウェブサービス for Developer」で説明受けたクラウドを活用した開発手法のトレンドをまとめてみました。

前提知識がないとなかなかイメージしずらいなと感じることがあったので、今後参加しようと考えている僕みたいな現場経験がない新人のような方は一旦タイムテーブルの内容の概要程度は頭に入れてから参加することをお勧めします。

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

せっかく RAML で API ドキュメントを作ってもローカルでしか閲覧できないのは悲しいよねって話

はじめに

API ドキュメントの管理を Excel や Word などから Raml のような API ドキュメンテーションツールに切り替えるチームも増えていると思います。しかし作成した API ドキュメントはプロジェクトを git clone してローカルで閲覧する、というようなちょっと残念な方法をとっているチームもあるような気がします。

そこで本記事では、作成した API ドキュメントを AWS にサクッと数分でホスティングする方法をまとめました。本記事では API ドキュメントを対象としますが、ホスティング対象は静的コンテンツであれば何でも構いません。

注意

本記事では Route 53 のような 無料利用枠のない サービスを使うため、静的コンテンツを公開したままにしておくと一月あたり 100 円程度ですが課金されます ( Amazon Route 53 料金表 ) 。

もし課金されたくない場合は本記事の内容を実施後、下記の流れで作成した AWS リソースを必ず削除してください。

  1. S3 バケットにアップロードしたコンテンツを削除する
  2. コマンド terraform destroy を実行する
  3. Route 53 ホストゾーンを削除する

システム構成図

本記事で構築するシステム構成は以下の通りです。今回は Raml で作成した API ドキュメントをデプロイします。対象プロジェクトは fizzbuzz とします。

system-configuration.png

上記構成図で概要が分からないサービスがある場合は本記事後半の「使用サービスの一言メモ」にサービスの概要を記載したので、そちらを参考にしてください。

前提

  • デプロイ対象のコンテンツ ( 今回は API ドキュメント ) が作成済みであること

  • AWS への登録と IAM ユーザの作成が完了していること

    • IAM ユーザには S3, CloudFront, ACM, Route 53 へのフルアクセス権限 を付与しておく
  • terraform がインストール済みであること

システム構築

独自ドメインを取得する

  1. freenom にアクセスする
  2. 右上にある サインイン からログインする ( 初回利用時は登録が必要 )
  3. 画面上部の Services から Register a New Domain を選択する
  4. 検索ボックスに取得したいドメイン名を入力して Check Availability を押下する
  5. 取得したいドメインの Get it Now! を選択後に画面上部の Checkout を押下する
  6. 内容を確認して I have read and agree to the Terms & Conditions にチェックを入れ、Complete Order を押下する

以上で独自ドメインの取得は完了です。

ネームサーバを変更する

まずは Route 53 ホストゾーンを作成する。

  1. AWS マネジメントコンソールから Route 53 のページを開く
  2. ホストゾーンを作成する ボタンを押下して、ドメイン名 に先ほど登録したドメイン名を入力後 作成 ボタンを押下する
  3. 表示された NS レコード をメモする

続いてネームサーバの変更作業を実施する。

  1. freenom にアクセスする
  2. 画面上部の Services から My Domains を選択する
  3. 対象ドメインの Manage Domain を押下する
  4. 管理画面が開いたら Management Tools から Nameservers を選択する
  5. Use custom nameservers (enter below) を選択して先ほどメモした NS レコード 4 つを入力、Change Nameservers ボタンを押下する

以上でネームサーバの変更作業は完了です。

ACM, S3, CloudFront を作成する

私が用意した ACM, S3, CloudFront を作成する terraform ファイルを clone します。

$ git clone https://github.com/mizuhara/terraform.git
$ cd terraform/https-static-site

続いて clone したファイル内の <your-domain> を先ほど取得したドメイン名に置き換えます。例えば取得したドメインが api-doc.tk であれば以下のコマンドを実行すれば置き換わります。

$ grep -l '<your-domain>' ./* | xargs sed -i.bak -e 's/<your-domain>/api-doc.tk/g'

続いて variables.tf の 10 行目 <your-bucket-name> を好きな S3 バケット名に置き換えてください。

ここまで来たら下記コマンドを実行して完了です。

$ terraform init
$ terraform apply

もし実行前に実行後の状態を知りたい場合は init の後に下記コマンドを実行してください。terraform ファイルのエラー有無も知ることができます。

$ terraform plan

apply コマンド実行が完了したら CloudFront ディストリビューション ID が出力されるのでメモしておいてください。S3 バケットへのファイルアップロードが完了した後 https://www.api-doc.tk/ にアクセスできれば Web サイトの公開は完了です ( api-doc.tk の箇所は適宜読み替えてください ) 。

GitHub Actions を設定する

さて、API ドキュメントを更新する度に手動で S3 バケットに最新のドキュメントをアップロードするのは面倒ですよね?ということで、もう一手間かけて API ドキュメントを自動アップロードできるようにしましょう。

  1. GitHub リポジトリを開く
  2. Secrets に下記情報を設定する

    key value
    AWS_ACCESS_KEY_ID 事前準備で用意した IAM ユーザの access key id
    AWS_SECRET_ACCESS_KEY 事前準備で用意した IAM ユーザの secret access key
    S3_UPLOAD_BUCKET variables.tf に指定した S3 バケット名
    DISTRIBUTION terraform apply コマンドの実行後にメモした ID
  3. Actions を押下する

  4. set up a workflow yourself のリンクを押下する

  5. yaml ファイルを入力する画面が開かれるので下記内容をコピペして画面右にある Start commit ボタンを押下する

    main.yml
    name: deploy to s3
    on: [push]
    jobs:
    build:
     runs-on: ubuntu-latest
     timeout-minutes: 5
    
     steps:
       - name: checkout
         uses: actions/checkout@v2
    
       - name: setup jdk
         uses: actions/setup-java@v1
         with:
           java-version: 1.8
    
       - name: build with gradle
         run: ./gradlew build
    
       - name: configure aws credentials
         uses: aws-actions/configure-aws-credentials@v1
         with:
           aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
           aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
           aws-region: us-east-1
    
       - name: upload file to s3
         env:
           S3_UPLOAD_BUCKET: ${{ secrets.S3_UPLOAD_BUCKET }}
         run: |
           aws s3 cp ./doc/index.html s3://$S3_UPLOAD_BUCKET/ --quiet
    
       - name: invalidate cloudfront cache
         uses: chetan/invalidate-cloudfront-action@master
         env:
           DISTRIBUTION: ${{ secrets.DISTRIBUTION }}
           PATHS: '/*'
           AWS_REGION: 'us-east-1'
           AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
           AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    
  6. コミットメッセージを入力してコミットする

以上で GitHub に push すると gradle build が実行され、成功すると S3 バケットに API ドキュメントをアップロードして CloudFront のキャッシュをクリアします。

使用サービスの一言メモ

Amazon Certificate Manager ( ACM )

ACM は AWS 自身が認証局となって SSL 証明書を発行するサービスです。発行した証明書の有効期限は 13 ヶ月で、自動更新することも可能です。

2020 年 7 月時点で ACM を利用可能なサービスは ALB や CloudFront などの一部サービスに限定されますが、無料で利用できます。

Route 53

Route 53 は AWS が提供する DNS サービスです。DNS は権威 DNS とキャッシュ DNS に大別されますが、Route 53 は前者なので、必要に応じてキャッシュ DNS を別途用意する必要があります。

Simple Storage Service ( S3 )

S3 は非常に優れた耐久性を持つ容量無制限のオブジェクトストレージサービスです。

CloudFront

CloudFront は AWS が提供する Contents Delivery Network ( CDN ) サービスです。

通常であればキャッシュした HTML や CSS などの静的コンテンツをオリジンサーバの代わりに配信することで、オリジンサーバの負荷軽減やユーザへのレスポンス速度を向上させる目的で利用しますが、今回は https 接続するために利用します。

Terraform

Terraform は HashiCorp が手がけるインフラストラクチャ定義ツールです。クラウド上のリソースを定義ファイルの状態になるように生成・操作してくれます。

今回作成した Terraform の解説は tf ファイルに記載したコメントを参照してください。

GitHub Actions

GitHub Actions は GitHub のイベントをトリガに任意の Docker コンテナの実行を連係させることで、ユーザが自由にワークフローを定義できるサービスです。

まとめ

本記事では AWS に https 対応した静的サイトをサクッと公開する方法を説明しました。弊社では現在オンプレから AWS への移行を進めつつあり、アプリケーションエンジニアでもインフラ環境を ( ある程度は ) 触れる必要があるなぁと実感しています。

これを機に AWS を触れるようになりましょう。

参考資料

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

aws wafv2の動作確認方法

ルール

  • countモードはブロックは行わず記録のみする
  • Override rules actionは、有効化すると、そのルールのみcountモードになる image.png

NoUserAgent_HEADERルールの場合の動作確認

  • HEADERにUserAgentが含まれていない場合はブロックします。というルール

ブロック

yuta@DESKTOP-PT34LID:~$ curl  https://test.vamdemic.black.jp  -H "User-Agent:"
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
</body>
</html>
yuta@DESKTOP-PT34LID:~$ 

許可

yuta@DESKTOP-PT34LID:~$ curl  https://test.vamdemic.black  -H "User-Agent:a"
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://test.vamdemic.black/dist/">here</a>.</p>
</body></html>
yuta@DESKTOP-PT34LID:~$ 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

herokuでホストしてたlaravelアプリをlightsailに移動してドメイン張り替えとかした作業ログ

1.インスタンス立ち上げ

スクリーンショット 2020-07-30 20.46.17.png

2.documentroot確認

スクリーンショット 2020-07-30 20.47.00.png
スクリーンショット 2020-07-30 20.47.39.png

3.デプロイ

https://readouble.com/laravel/7.x/ja/envoy.html

Envoyと言う公式のデプロイタスク管理ツールがあるのでコレでデプロイスクリプトを管理してみる

Envoy.blade.php
@servers(['web' => ['-i ~/.ssh/LightsailDefaultKey-ap-northeast-1.pem bitnami@54.95.226.129']])

@task('deploy', ['on' => 'web'])
cd ~/htdocs/ta9to/
git pull origin master
composer install --optimize-autoloader --no-dev
php artisan config:cache
php artisan route:cache
php artisan view:cache
@endtask

4.Laravel動いてるの確認

# DocumentRoot設定
$ vim /opt/bitnami/apache2/conf/bitnami/bitnami.conf
<VirtualHost _default_:80>
  #DocumentRoot "/opt/bitnami/apache2/htdocs"
  DocumentRoot "/opt/bitnami/apache2/htdocs/ta9to/public"
# apache再起動
$ sudo /opt/bitnami/ctlscript.sh restart apache

スクリーンショット 2020-07-30 21.22.03.png

5.mysql設定

# mysqlパスワード確認
$ bitnami@ip-172-26-11-51:~$ cat /home/bitnami/bitnami_application_password

6.表示確認

スクリーンショット 2020-07-30 22.09.20.png

7.ドメイン張り替え
スクリーンショット 2020-07-30 22.15.30.png

8.Aレコード追加+レジストリのネームサーバ置き換え
スクリーンショット 2020-07-31 1.38.23.png

9.https設定
https://aws.amazon.com/jp/premiumsupport/knowledge-center/linux-lightsail-ssl-bitnami/

$ sudo /opt/bitnami/bncert-tool

↑のコマンドがapacheのconfに追記してくれるのでまたdocumentroot書き換える

/opt/bitnami/apache2/conf/bitnami/bitnami.conf
<VirtualHost _default_:443>
  #DocumentRoot "/opt/bitnami/apache2/htdocs"
  DocumentRoot "/opt/bitnami/apache2/htdocs/ta9to/public"
  SSLEngine on

10.完了
https://www.ta9to.com/blog/posts

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

Terraformのディレクトリパターン集

AWSやGCPのみならず、GitHubやDatadog等、様々なサービスをTerraformでコード化して管理できるようになり、世はまさに大Terraform時代となりました。
そんな時代の中、人類誰もが一度はTerraformのディレクトリ構造で悩んだことがあるのではないでしょうか?

様々なベストプラクティスが提案されてきましたが、デファクトスタンダードと言えるものが未だ登場していないイメージがあります。

背景

なかなかスタンダードな構成が決まらない背景には、以下の要因が影響していると思われます。

環境ごとの差異がどれだけあるかは人それぞれ

本番環境とステージング環境は完全に同一の構成であることが理想です。
同一の構成をとることで、ステージング環境では発生せず、本番環境でのみ発生する不具合を減らすことができるからです。

しかし、様々な理由でどうしてもステージング環境と本番環境の間に差異が発生してしまうケースがあります。

  • コスト的にインスタンスタイプ、インスタンス数を小さくしなければならない
  • 連携するシステムの都合でステージング環境と本番環境でネットワーク構成を変える必要がある
  • 既に構築された本番環境とステージング環境で差分がある環境をterraform importする場合

こういった理由で発生する差異がどれだけあるか、今後どれだけ増えていくかによって、Terraformのディレクトリ構成を決定する際に考慮する必要があります。
特にterraformのリポジトリを用意するサービス開発の序盤では将来的な構成まで見通すことが難しいため、「ディレクトリ構成で失敗した」となりがちです。

宣言的コードにおいてDRY原則と可読性のどちらを優先するかは人それぞれ

Terraformはインフラストラクチャを宣言的に記述する仕様です。
(反対に、インフラストラクチャを手続き的に記述するのがCDKだったりします)

宣言的に記述する言語仕様において、DRY原則と可読性のどちらを優先すべきかという議論がしばしば発生します。(主に私の脳内で)

重複する部分をすべてmoduleを使って共通化することもできますが、過度にmoduleを使いすぎるとリソースの設定値が各ファイルに散らばってしまい、可読性が低くなってしまうケースもあります。

Terraformにおけるmoduleによる共通化は、一般的なプログラミング言語における関数等による共通化よりも実装コストが高い(詳細は後述)ため、あまり気軽に使えるものとは考えないのが無難かもしれません。

最初はキレイに共通部分を切り出す実装にできていたとしても、度重なるリソースの追加とリソース間の値の参照によりmodule間で参照しまくりな可読性の低いヤバイ実装を生み出してしまった体験は誰しもがあるのではないでしょうか?(私は何度もあります)

パターン集

この記事では、Terraormのディレクトリ構成に関して、いくつかの所謂デザインパターンのようなものを紹介します。
それぞれのパターンごとに向き不向きがあるので、自分の担当サービスや社内全体のサービスの傾向などから適切なパターンを選択し実装するのが良いでしょう。

前提条件

  • 想定IaaS: AWS
  • 本番環境とステージング環境を構築するためのコードを書く
  • Terraform Cloud等でも利用できるように、DockerやMakefileによるラッパースクリプトは使用しない

1ディレクトリでworkspaceを使うパターン

TerraformのWorkspace機能を利用して、1ディレクトリに本番環境とステージング環境で利用するtfファイルを全て配置するパターンです。

例としてファイルは下記のようにフラットに並べます。

terraform-repo
├── alb.tf
└── s3.tf

本番環境、ステージング環境でそれぞれリソースが作成されるように terraform.workspace 変数を利用しリソースの名前の衝突などを回避します。

resource "aws_s3_bucket" "bucket" {
  bucket = "${terraform.workspace}-foo-bucket"
}

上記コードを staging というworkspaceで実行すれば、 staging-foo-bucket というS3バケットが作成され、 production というworkspaceで実行すれば、production-foo-bucket というS3バケットを作ることができます。

インスタンスタイプやAutoScalingによる台数程度であれば、variableを利用して各環境で異なる値を設定するといったことも可能です。

メリット

  • DRYに記述できる
  • 本番環境、ステージング環境以外に環境を増やす際も楽
  • (意図的にif文などを使わなければ)本番環境とステージング環境の構成を同一にできる
  • この構成を理由に「ステージング環境だけにxxxのリソース作ってください」という依頼を一蹴できる

デメリット

  • 本番環境だけに作成するリソース等がある場合は、countとif文を使う必要がある
  • ステージング環境に自動applyし、動作確認後、本番環境に自動applyする運用の実現には工夫が必要
    • masterブランチにマージ→自動applyだけでは上記を実現できない
    • ブランチ運用を工夫するか、Terraform Cloudのconfirm機能を使う等が必要になる

環境ごとにディレクトリを切るパターン

本番環境、ステージング環境でそれぞれディレクトリを分け、tfファイルもそれぞれで用意するパターンです。

このパターンでは、下記のようにstaging環境にだけWAFのリソースを作成する、といったような対応を楽に行うことができます。

terraform-repo
├── production
│   ├── alb.tf
│   └── s3.tf
└── staging
    ├── alb.tf
    ├── s3.tf
    └── waf.tf

また、本番環境のtfファイルは production ディレクトリ配下に全て存在し、workspaceのような変数や後述のmodule等を利用しないため、各リソースの実装がよりシンプルで可読性が高いといった利点があります。

メリット

  • 本番環境とステージング環境に構成的な差があっても柔軟に対応できる
  • 可読性が極めて高い
  • 実装難易度が低い

デメリット

  • DRY原則を完全にあきらめている
    • コピペですむケースが多いが、環境の数だけtfファイルを実装する必要がある
    • 本番環境だけ実装漏れ等が発生するリストが増える

共通部分をmoduleに切り出すパターン

環境ごとにディレクトリを切るパターン を少しでもDRYに書こうとすると行き着く1つのパターンです。

以下には、共通するリソースをcommonというmoduleに切り出した場合のディレクトリ構成を示します。

terraform-repo
├── common
│   ├── alb.tf
│   ├── output.tf
│   ├── s3.tf
│   └── variable.tf
├── production
│   └── main.tf
└── staging
    ├── main.tf
    └── waf.tf

productionからcommonモジュールを呼び出すには下記のようにtfファイルを記述します。

production/main.tf
# common moduleを呼び出す
module "common" {
  source = "../common"

  # moduleに渡す変数を列挙していく
  # ここで渡すことで、commonモジュール内で var.bucket_prefix で参照できるようになる
  bucket_prefix = "production"
}

moduleへ値を渡す、module内の値を参照する実装コストが高い点に注意が必要です。

# common/s3.tf
resource "aws_s3_bucket" "foo" {
  bucket = "${var.bucket_prefix}-foo-bucket"
}

# common/output.tf
output "foo_bucket_name" {
  value = aws_s3_bucket.foo.name
}

# production/bar.tf
# commonモジュール内のバケット名を参照したいなんらかのリソース
resource "aws_xxxx" "bar" {
  # outputで定義した値しか参照できない
  bucket = module.common.foo_bucket_name
}

moduleの境界を超えて値を参照するリソースが増えると、値を参照するための設定のコードを大量に書く羽目になってしまいます。
また、この値の参照は複数ファイル辿らないと実際の値にたどり着かない実装になりがちなので、多用すると可読性が大きく損なわれてしまいます。

メリット

  • DRYに書きつつも、無理なく環境毎の差異も実装できる

デメリット

  • moduleの境界をうまく考えて実装する必要がある
    • 単純に、本番環境とステージング環境で同じ実装だからcommonモジュール内に入れてしまうと、属性の参照で辛い目を見るケースもある
  • 変数参照が多いと可読性が低くなりがち

どのパターンを使うべきか?

簡単に分類すると下記のようになると思います。

  • 質問1: 環境ごとに差異はある?

    • Yes: 質問2へ
    • No: 1ディレクトリでworkspaceを使うパターン
  • 質問2: 環境固有のリソースは独立している?(共通リソースへの参照が少ない?)

    • Yes: 質問3へ
    • No: 環境ごとにディレクトリを切るパターン
  • 質問3: moduleを使いこなす自身はある?

    • Yes: 共通部分をmoduleに切り出すパターン
    • No: 環境ごとにディレクトリを切るパターン

まとめ

個人的にできることなら1ディレクトリでworkspaceを使うパターンを採用するのが望ましいです。
ステージング環境はできる限り本番環境と同一の環境を再現することで、様々な問題をステージング環境で事前に検証することができるためです。

しかし、実際の開発の現場では、コストや既存の構成の都合で、どうしてもステージング環境と本番環境間に構成の差分が存在するケースがあります。
そんな場合は環境ごとにディレクトリを切るパターンが最も柔軟性に優れるパターンなので、とりあえずこれを採用するケースが多いです。

「こういう構成もいいよ!」とかあればコメントに記載していただけると喜んで参考にさせていただきます。

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