AWS Lambdaを使った外形監視

福岡拠点の野田です。
AWSを使ったサーバー構築が最近増えてきました。今回は、AWS Lambdaを使った外形監視の方法を紹介しようと思います。

AWS Lambdaは、軽量なアプリケーションを動作するときに活躍するサービスです。5分おきのバッチサービス、管理画面でのSSRサービスなど、 常に常駐しておく必要がないプログラムを動かすのに適しています。 今回紹介する監視系のバッチもLambdaと相性のよいアプリケーションの1つとして考えています。

AWS Lambdaを使った外形監視の作成を以下の手順ですすめていきます。

  1. 関数の作成
  2. 関数コードの作成
  3. トリガーの追加

関数の作成

「1から作成」を選択します。関数名は「check_http」のような名前を付けます。ランタイムは、javascriptサーバーの「node.js12.x」を選択して関数を作成します。

アクセス権限については、「基本的な Lambda アクセス権限で新しいロールを作成」を選ぶとAmazon CloudWatch Logsの権限が付与されるので、これを選びます。

関数コードの作成

index.jsに以下をコピペします。

function getNativeRequest(url) {
  const https = require('https')
  const options = {timeout: 1000};
  const promise = new Promise(function(resolve, reject) {
    const req = https.get(url, options, (res) => {
        resolve(res.statusCode)
      }).on('error', (e) => {
        reject(Error(e))
      }).on('timeout', () => {
        reject("Timeout Error")
      })
  })
  return promise
};

exports.handler = async (event, context, callback) => {
  const url = process.env['ENV_URL'];
  return getNativeRequest(url);
};

ページ末尾の環境変数に以下のような監視対象URLを指定します。

ENV_URL https://(監視対象URL)

トリガーの 追加

EventBridge(CloudWatchEvents)を追加します。
ルールは、新規ルールでルール名を「every5min」を指定します。ルールタイプはスケジュール式で以下を設定します。

説明: 5分毎
イベントバス: default
スケジュール式: cron(0/5 * * * ? *)

あとは、CloudWatch側で以下のようなアラートルールを設定することで監視完了です。

5 分内の1データポイントのErrors > 0

Lambdaはいろいろな使い方ができると思うので、引き続き使っていこうと思います。

S3+CloudFront+Route 53を使った静的コンテンツ配信

福岡拠点の野田です。

WordPressで運用していた個人サイトをメンテしなくなったので、S3とCloudFrontとRoute 53を使って静的コンテンツ配信方式に切り替えてみました。手順の大きな流れは以下のようになります。

  1. S3にコンテンツを配置
  2. CloudFrontを設定
  3. Route53でCloudFrontへ振り分け

S3 にコンテンツを配置

まずは、wget で既存サイトを取得します。

wget --mirror --page-requisites --span-hosts --quiet --show-progress --no-parent --convert-links --no-host-directories --adjust-extension --execute robots=off (サイトURL)

日本向けに配信することを考え、 S3 の東京リージョンにて新規バケットを作成して、上記取得したファイルを配置します。

S3における設定ですが、アクセス権限の設定を行います。静的コンテンツとして公開するため、以下のバケットポリシーのブロックをオフにすることで外部からのアクセスを行えるようにします。

  • 新規のパブリックバケットポリシーまたはアクセスポイントポリシーを介して付与されたバケットとオブジェクトへのパブリックアクセスをブロックするオフ
  • 任意のパブリックバケットポリシーまたはアクセスポイントポリシーを介したバケットとオブジェクトへのパブリックアクセスとクロスアカウントアクセスをブロックするオフ

バケットポリシーは、以下のようなCloudFrontからの接続を許可する設定を行いますが、CloudFront側から設定ができるため、ひとまずスキップで大丈夫です。

{
     "Version": "2012-10-17",
     "Statement": [
         {
             "Sid": "2",
             "Effect": "Allow",
             "Principal": {
                 "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity OAIのID"
             },
             "Action": "s3:GetObject",
             "Resource": "arn:aws:s3:::バケット名/"
         }
     ]
 }

CloudFrontを設定

Create DistributionでCDNを新規作成します。

  1. Web/RTMPの選択でWebを選択
  2. Origin Domain NameにS3のバケットを選択
  3. Origin Pathは空欄でOK
  4. Origin IDは任意のIDを設定(S3-バケット名みたいな感じで設定しました)
  5. Restrict Bucket Accessは、YES
  6. Origin Access Identityは、 Create a New Identity。
  7. Grant Read Permissions on Bucketは Yes, Update Bucket Policy (これが先ほどのS3バケットポリシーに反映されますので、一応S3側でも設定されているか確認)
  8. Viewer Protocol Policyは、Redirect HTTP to HTTPS (httpからhttpsリダイレクト)
  9. Allowed HTTP Methodsは、GET/HEADのみで対応(CORSを考えるとOPTIONSまでやってもいいかもしれません)
  10. Compress Objects Automaticallyは、true(圧縮化。転送量削減)
  11. Price Classはベストパフォーマンス
  12. AWS WAF Web ACLは、None
  13. Alternate Domain Namesは割り当てるドメイン名を改行区切りで入力。
  14. 証明書については、独自ドメインで割り当てる場合、ACMに登録したものを選択。
  15. 残りはデフォルトで登録

Distribution作成後、 GeneralタブでEditボタンを押下して、以下を設定します。

  1. Default Root Objectにindex.htmlを設定

続いて Restrictionsタブを選択して、GeoRestrictionをEditします。

今回は、日本のみを対象とします。全世界を対象とするとコストと直結します。1日1000円以上かかってもいい!どんな攻撃もどんとこい!という方以外は、対象を絞ったほうが良いと思います(私もこれで当初1日放置して1000円かかってしまい冷や汗、急遽制限を追加しました)。

Route53でCloudFrontへ振り分け

仕上げにRoute53からCloudFrontへ振り分けします。A(IPv4アドレス)およびAAAA(IPv6アドレス)のエイリアス指定でCloudFrontにつなげることができます。

まとめ

Cloudは設定をミスると高額な請求が発生してしまうリスクはありますが、うまく使えば個人で使っても安く運用することができます。最近では予算設定や請求が高額になりそうなときにアラートも出せる機能もありますので、そうしたものを組み合わせて、安全に運用すると良いと思います。先月からの運用の感じだとアクセス数次第なところがありますが、100円~300円/月ぐらいで運用できそうな感じでした。

初心者にはおすすめはしませんが、興味ある方は是非チャレンジしてみてください。

JQコマンドをおぼえた

福岡拠点の野田です。久しぶりの投稿です。

ある案件でAWSを扱っており、ELBからインスタンスが切り離されたか確認してほしいと連絡がありました。生憎、AWS Consoleへの接続アカウントをもらっていない状態で調べられない…と思ったのですが、aws-cliがインストールされてあるサーバーがあることに気づき、そちらで確認してみました。

ELBの一覧を表示するコマンドは以下のようなものになります。

 aws --output json --region ap-northeast-1 elb describe-load-balancers

けれども、awsのコマンドで出てくる内容はjsonで縦に長い!必要な情報だけに整理したいときに役立つのがjq(恐らくjson queryの略)コマンドになります。jsonの中からDNSNameと紐づくインスタンスを見たい場合は、以下のようなクエリをjqに投げます。

 aws --output json --region ap-northeast-1 elb describe-load-balancers | jq -r '.LoadBalancerDescriptions[] | {DNSName, Instances}'

jqコマンドの基本は、配列のフィルターと生成。.から始まる要素がフィルターされる内容になります。{}や[]で配列を再構築します。詳しいやり方は以下をご参考ください。

すぐれものなのは、jsonからcsvを作れること。以下のような感じでフィルターと配列の再構築を利用すれば、@csvでcsvの作成も楽々です。

aws --output json --region ap-northeast-1 elb describe-load-balancers | jq -r '.LoadBalancerDescriptions[] | [.DNSName, .Instances[].InstanceId] | @csv'

zabbixと連携させたりとか、 レポート出力を簡易化できないかとか夢が広がりますよね?是非、jqを使ってより良いjsonライフを!

AWSでインスタンスを停止し、再起動した時にパブリックIPが割り振られない場合

AWS特訓中の宮里です。

AWSでインスタンスを作成したときに、少し困った現象があったため情報共有したいと思います。

AWSで作成したインスタンスを一旦停止後、ネットワークインターフェイスを追加し、再起動した後にパブリックIPが割り振られない現象が発生しました。

?????となりながら、再起動を何度か試すもパブリックIPは復活せず。
作成したばかりのインスタンスだったので、インスタンスを一から作成し直し、特に問題は起こりませんでした。
その後、気になったので原因を調べてみると、AWS公式のドキュメントに今回の現象の解答がありました。

下記、公式のドキュメントから一部引用します。

手動でパブリック IP アドレスをインスタンスに関連付けること、また、手動でインスタンスから割り当て解除することはできません。場合によって、パブリック IP アドレスはインスタンスから解放されたり、新しいインスタンスに割り当てられたりします。
インスタンスが停止または終了されると、インスタンスのパブリック IP アドレスは解放されます。停止していたインスタンスが再起動されると、そのインスタンスには新しいパブリック IP アドレスが送信されます。
VPC 内のインスタンスのパブリック IP アドレスが既に解放されている場合には、複数のネットワークインターフェイスがインスタンスにアタッチされていると、インスタンスに新しいパブリック IP アドレスは送信されません。

出典:
AWS ドキュメント「パブリック IPv4 アドレスと外部 DNS ホスト名」

インスタンスを再起動するときは、プライマリENI以外はインスタンスにアタッチしてはいけないということでした。。。

少しづつ身につけていきたいと思います。

AWSのタイムゾーン設定でハマった件

福岡拠点の宮里です。
先日、アパッチのアクセスログをアーカイブして一定期間保存する作業を行っていたのですが、

apache-loggenでダミーアクセスログの生成

ログを確認していると、何か違和感が…

|02/May/2018:09:24:59 +0000|

私「あれ?もう出社して9時間は経ってて、もう今日も終わっちまうのかなぁって気がしていたけど」

心の中の金子賢「バカヤロー、まだ始まってもいねーよ」


サーバー時刻をUTCからJSTへ変更

あれあれあれと心を鎮めながら、サーバーに設定されているタイムゾーンを確認してみると、

このサーバーのタイムゾーンはUTC。
このままだとなにかと不都合です。
心臓にもわるいので、JST時間帯へ変更したいと思います。

sysconfigディレクトリの設定ファイル(clock)からタイムゾーンを修正します。
ひとまずバックアップ取って、

シンボリックリンクの向き先Asia/Tokyoへ変更します。

サーバーのタイムゾーンがUTCからJSTに変わりました。

これでひと安心かと思いきや、
この作業を行う前にすでに記録されているログの時刻をJSTに修正しないと何かと収まりがわるいです。
なので、USTで記録されているログをJSTへと変換するスクリプトを作りたいと思います。


既に出力されているログの時刻をJSTへ修正

スクリプトは、サーバーに標準でインストールされているpythonで作成してライブラリをひとつだけ追加します。

pytzの取得

JSTへ変換するスクリプト
changeJst.py

粗いスクリプトですが、ひとまず期待通りに動くことが確認できましたので、作業するサーバーのhttpdを停止してスクリプトを実行しました。ちなみにAWSのELBがunhealthyを認識して振り分けを開始するまで約1分~2分ほどかかるようです。認識する前にhttpdを止めると502で振り分けもされなくなるので注意が必要です。

処理するディレクトリのバックアップを取って実行。

なんとか完了です。
サーバー時刻もUTCからJSTへ変更します。
その後、httpdを再起動。
無事に現在時刻が18時に修正されました。
やっと終わっちまえます。


あれ、cronの実行がおかしい

それから様子をみること数日。
dateコマンドやログに出力される時刻はしっかりUTCからJSTに変更がされているけど、cronに設定したタスクの実行時間がおかしい!どうにも9時間遅れて実行されている!と気づきました。
調べてみるとサーバーのタイムゾーンを変更しただけではcronの実行時刻への反映がされないようです。crondを再起動してJSTタイムゾーンの反映が必要でした。。。

crontabの設定ファイルへtimezoneを記述して、crondの再起動。

これでcronの実行時間にもJSTが反映されました。