20190727のAWSに関する記事は8件です。

AWS EC2インスタンスへのSSH接続の仕方

AWS EC2へのSSH接続の仕方

今更だが、AWSに触ったキッカケはServerlessアプリを作るところからだったのでEC2を使うのが始めてだったりする。備忘録。マイマシンはubuntu18.04です。

AWSでEC2インスタンスを作成

なんでもいいけど
"Ubuntu Server 16.04 LTS (HVM), SSD Volume Type - ami-bec974d8"で作成した。t2.microというタイプ。ウィザードは全部デフォルト。これだと月に10ドルくらい。
AWS 月額料金チートシート
最後に.pemファイルをダウンロードすること。キー名というのを設定するがそれがpemファイルの名前になるだけ。

.pemファイルのアクセス権を変更

.pem ファイルを自分のマシンの適当な場所に移動して(たとえば~/pathフォルダなどを作る)、そこに対するアクセス許可を0777 ではなく 0400にする。

$ cd ~/
$ mkdir path
$ mv ***.pem path
$ chmod 400 path

sshで接続

以下のコマンド。

$ sudo ssh -i path/***.pem ubuntu@ec2-11-11-111-111.ap-northeast-1.compute.amazonaws.com
@の前のデフォルトユーザ名は以下の通り。

Screenshot from 2019-07-27 18-04-41.png
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/connection-prereqs.html

以下のようなレスポンスが来て、yesと答えると、

The authenticity of host 'ec2-111-11-111-1.compute-1.amazonaws.com (10.254.142.33)'
can't be established.
RSA key fingerprint is 1f:51:ae:.
Are you sure you want to continue connecting (yes/no)?

こう返ってくる。

Warning: Permanently added 'ec2-111-11-111-1.compute-1.amazonaws.com' (RSA) 
to the list of known hosts.

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html

プロンプトはこうなっているはず。繋がった。

ubuntu@ip-111-11-11-111:~$

rootユーザーになるには?

rootのパスワードは設定されていない。次のコマンドでrootになれる。

~$ sudo su -

作業を終えたらexitコマンド。


ファイルをEC2へアップロードする

Googleのdebパッケージをアップロードするには下記のコマンド

sudo scp -i path/***.pem google-chrome-stable_current_amd64.deb ubuntu@ec2-1-111-11-111.ap-northeast-1.compute.amazonaws.com:/home/ubuntu
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Cloud9にSymfonyを構築する

テスト環境としてSymfonyが使える環境が欲しいと思い、Cloud9上に構築してみました。
今回構築したときの備忘録です。

PHPのインストール

Cloud9のOSは、2019年7月時点でAmazon Linux2ではなく、以前のAmazon Linuxでした。
なので、amazon-linux-extrasは使えませんでした。

$ cat /etc/system-release
Amazon Linux AMI release 2018.03
$ sudo yum -y update
$ sudo yum -y install php72 php72-mbstring php72-pdo
$ sudo unlink /usr/bin/php
$ sudo ln -s /etc/alternatives/php7 /usr/bin/php
$ php -v
PHP 7.2.19 (cli) (built: Jun 12 2019 20:55:29) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

https://qiita.com/kidatti/items/2d6a4a24f89dc71eb66e

Symfonyのインストール

$ wget https://get.symfony.com/cli/installer -O - | bash
$ export PATH="$HOME/.symfony/bin:$PATH" >> .bash_profile
$ sudo mv /home/ec2-user/.symfony/bin/symfony /usr/local/bin/symfony
$ source .bash_profile
$ symfony -v
Symfony CLI version v4.6.2 (c) 2017-2019 Symfony SAS

https://symfony.com/download

Composerのインストール

$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"
$ sudo mv composer.phar /usr/local/bin/composer
$ composer -v
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.8.6 2019-06-11 15:03:05

https://getcomposer.org/download/
http://symdoc.kwalk.jp/doc/book/installation

Symfonyの起動

Cloud9では外部からアクセスできるポート番号が決まっているため、ポート番号を指定して起動する必要があります。

$ symfony new --full my_project
$ cd my_project/
$ php bin/console server:start *:8080

メニュー > Preview > Preview Running Applicationを押すと実行ページのプレビューが表示されます。

Symfonyの停止

$ php bin/console server:stop

(補足)アプリの状態をするコマンド

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

手動で立ち上げた EC2 に ECS をつなぐ方法

EC2 で ECS を動かす記事をよく見ますが
ECS から設定された EC2 を立ち上げる記事はありますが
自分で立ち上げた EC2 から ECS に連携させる方法の記事が見つからなかったのでメモします
尚、個人的見解ですが、ECS やるなら Fargate 使うほうがおすすめです
EC2 にはパブリック IP が付与されているものとします

AMIを確認する

AWS で ECS 向けに最適化された AMI が公開されているので使います。
まずは、ECS用に最適化されたAMIの情報を確認します
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-optimized_AMI.html
英語版のほうが更新が早いので英語版も念の為見ておくと良いです
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html

ECSを動かせるロールを作成

AmazonEC2ContainerServiceforEC2Role ポリシーを付与した EC2 向けのロールを作成します
すでにロールがある場合は、ポリシーをアタッチしてください

EC2の設定

EC2 の インスタンスの設定画面で
ECS を動かすのに必要なロールを付与

高度な詳細をクリックして開き、
ユーザーデータの部分に以下を追記
HOGE_CLUSTER_NAME を自分の名前クラスター名にする

#!/bin/bash
echo ECS_CLUSTER=HOGE_CLUSTER_NAME >> /etc/ecs/ecs.config

image.png

あとはよしなににしてどうぞ

参考: https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/launch_container_instance.html

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

手動で立ち上げる EC2 に ECS をつなぐ方法

EC2 で ECS を動かす記事をよく見ますが
ECS から設定された EC2 を立ち上げる記事はありますが
自分で立ち上げた EC2 から ECS に連携させる方法の記事が見つからなかったのでメモします
尚、個人的見解ですが、ECS やるなら Fargate 使うほうがおすすめです
EC2 にはパブリック IP が付与されているものとします

AMIを確認する

ECS用に最適化されたAMIの情報を確認します
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-optimized_AMI.html
英語版のほうが更新が早いので英語版も念の為見ておくと良いです
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html

ECSを動かせるロールを作成

AmazonEC2ContainerServiceforEC2Role ポリシーを付与した EC2 向けのロールを作成します
すでにロールがある場合は、ポリシーをアタッチしてください

EC2の設定

EC2 の インスタンスの設定画面で
ECS を動かすのに必要なロールを付与

高度な詳細をクリックして開き、
ユーザーデータの部分に以下を追記
HOGE_CLUSTER_NAME を自分の名前クラスター名にする

#!/bin/bash
echo ECS_CLUSTER=HOGE_CLUSTER_NAME >> /etc/ecs/ecs.config

image.png

あとはよしなににしてどうぞ

参考: https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/launch_container_instance.html

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

Amazon EC2 のスポットインスタンスを AWS Cloud9 から使用する

目的

  • AWS Cloud9 (以降 Cloud9 とする) を使うためにかかる費用をとにかく安くする!!!
  • 初回の環境構築手順は多少複雑でもよい。
  • 環境構築後の日々の Cloud9 起動手順はできるかぎり簡単にする。

Cloud9 の使用構成

どれだけ費用を削減できるのか比較するために、今回作成するオレオレ構成とは別に基本構成を考える。

  • 基本構成
    • Environment Type を EC2 にして出来る限りデフォルト設定で作成した構成。
  • オレオレ構成。
    • 費用が安くなるように工夫した構成。

基本構成

Environment Type を EC2 にして出来る限りデフォルト設定で作成した構成。

cloud9-Page-3 (1).png

  • 概要
    • t2.micro のオンデマンドインスタンス。
    • ルートデバイス用 EBS 8G。
    • AMI は 2019 年 3 月 28 日時点では Cloud9Default-2019-02-18T10-14 (ami-0f5f5c726eabf14a2) で任意変更はできない。
  • メリット
    • 環境構築が楽。
    • 自動停止によってインスタンス停止を忘れても安心。
  • デメリット
    • オンデマンドインスタンスなので高い。
    • ルートデバイス用の EBS 8G を維持し続ける必要があり、月あたり約1ドルの固定費が必要。
    • AMI を変更できない。
    • 作成した EC2 はほぼ Cloud9 専用。

オレオレ構成

費用が安くなるように工夫した構成。

cloud9-Page-1.png

  • 概要
    • EC2 のリージョン。
      • Cloud9 以外の目的での使用も考慮して EC2 は東京リージョンで運用。
    • EC2 のアベイラビリティーゾーン。
      • インスタンスと EBS のアベイラビリティーゾーンは同じにする必要あるので、スポットインスタンス作成時は要注意。
    • t2.micro のスポットインスタンス。
      • 基本構成との比較用に同じ t2.micro としている。
    • ルートデバイス用 EBS 8G。
      • スポットインスタンスを停止する際は削除する。
    • データ用 EBS 1G。
      • スポットインスタンスの停止に関係なく維持する。
    • 独自ドメイン。
      • 他目的で使用している独自ドメインを流用。
    • Route53
      • 他目的で使用している独自ドメインの管理で使用しているのを流用。
  • メリット
    • EC2 を好きに用意できる。
  • デメリット
    • 自動停止がない。
    • 日々の Cloud9 起動手順にスポットインスタンス作成という手順が増えている。

費用比較

費用単価

簡易化のために1ヶ月は 750 時間で計算する。

項目 単価 Type=EC2構成 オレオレ構成
t2.micro (On-Demand) 0.0152 [USD/1H] 利用時間
t2.micro (Spot) 0.0046 [USD/1H] 利用時間
EBS 8G (root device) 0.00128 [USD/1H] 750時間 利用時間
EBS 1G (data) 0.00016 [USD/1H] 750時間
独自ドメイン 1.000 [USD/1M] 750時間
Route53 Hosted Zone 0.500 [USD/1M] 750時間

利用時間に対する費用

費用グラフ.png

オレオレ1:独自ドメインと Route53 の費用を含めた場合。
オレオレ2:独自ドメインと Route53 の費用を含めない場合。

私個人としては独自ドメインおよび Route53 は別目的で運用しているものを流用するので、Cloud9 を使うためにかかる費用としては オレオレ2 が該当。

オレオレ構成の構築手順

初回の環境構築

cloud9-Page-2.png

  1. ≪AWS Console≫ Route53 に A レコードを作成。
  2. ≪AWS Console≫ 初期設定用にインスタンス(インスタンスA)を作成。
  3. ≪AWS Console≫ 永続データ用の EBS を作成。
  4. ≪AWS Console≫ インスタンスAに永続データ用 EBS をアタッチ。
  5. ≪SSH Client≫ インスタンスAに SSH ログイン。
  6. ≪SSH Client≫

力尽きた・・・。
Cloud9が東京リージョンにきたし、そのうち整理しなおして投稿しなおす。

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

AWS Chatbot(beta版)を使ってみた

AWSがチャットボートサービス「AWS Chatbot」のβ版を更改(2019/7/24)されました。
https://aws.amazon.com/jp/chatbot/
公式に簡単なハンズオン(英語)があったので、日本語で手順を書いておきます。

AWS ChatBot概要

Amazon Simple Notification Service(SNS)を使って、SlackやAWS Chimeにメッセージ通知を行える。

連携可能なサービス

現時点(7/27)で連携可能なサービスは以下。
https://docs.aws.amazon.com/ja_jp/chatbot/latest/adminguide/related-services.html
- Amazon CloudWatch
- AWS Health
- AWS Budgets
- AWS Security Hub
- Amazon GuardDuty
- AWS CloudFormation

料金

AWS Chatbotは無料(7/27時点)

ハンズオン(15分程度)

公式でお試し設定方法が書いてたので試してみました。
https://docs.aws.amazon.com/ja_jp/chatbot/latest/adminguide/setting-up.html
公式は英語なので、日本語で手順書きます。
今回はSlackへのメッセージ通知をやります。

概要

CloudWatchアラームを設定し、アラーム通知をSlackチャンネルに送ります。

0.事前確認

  • Slackチャンネルの用意
  • AWS SNSのトピック設定(DynamoDBなど使ってる人はデフォルトで設定されてます)

1.IAMユーザ作成

Adminレベルのユーザを作ります。
1. IAMコンソール( https://console.aws.amazon.com/iam/) 開く
2. 「ユーザ」選択
3. 「ユーザを追加」選択
3. ユーザ名:Administrator
4. 「AWS マネジメントコンソールへのアクセス」をチェック
5. コンソールのパスワード:カスタムパスワード
6. パスワード入力
7. 「パスワードのリセットが必要」チェック外す(どっちでもOK)
8. 「次のステップ:アクセス権限」選択
9. 「グループの作成」選択
10. グループ名:Administrators
11. 「ポリシーのフィルタ」を選択
12. 「AWS 管理のジョブ機能」をチェック
13. 「AdministratorAccess」をチェック
14. 「グループの作成」を選択
15. 「次のステップ:タグ」を選択
16. 「次のステップ:確認」を選択
17. 「ユーザの作成」を選択

2.ChatBotの設定

AWS ChatBotを設定します。
1. ChatBotコンソール(https://console.aws.amazon.com/chatbot/) を開く
2. 「Configure new client」を選択
3. Slackの連携ページが開くので、連携先Slackワークスペースを選択して「confirm」を選択
4. Slack Channel:連携したいChannelを選択
5. IAM permissions(RoleName):適当なロール名を入力(なんでもOK)
6. SNS topics(SNS Region):どこでもOK(SNSトピックがあるとこ)
7. SNS topics(SNS topics):どれでもOK
8. 「configure」を選択

3.CloudWatchアラームの設定

アラームを設定します。
1. CloudWatchコンソール(https://console.aws.amazon.com/cloudwatch/) を開く
2. 「アラーム」を選択
3. 「アラームの作成」を選択
4. 「メトリクスの選択」を選択
5. 検索欄:SNS
6. SNS>トピックメトリクス
7. 適当なトピック名をチェック(どれでもOK)
8. 条件(しきい値の種類):静的
9. 条件(アラーム条件を定義):以下
10. 条件(しきい値を定義します):1
11. 「次へ」を選択
12. 通知(アラーム状態):アラーム状態
13. 通知(SNSトピック):既存のSNSトピックを選択
14. 通知(通知の送信先):適当に選択
15. 「次へ」を選択
16. アラーム名:なんでもOK
17. アラームの説明:なんでもOK
18. 「次へ」を選択
19. 「アラームの作成」を選択

4.結果を確認

Slackチャンネルをみましょう。3分くらい待てば通知が来るはずです。
aws-chatbot.png

おわりに

今までチャットボットはLambdaなどで手作りでしたが、これだと設定だけで簡単にBotが使えますね。
GuardDutyの不正アクセス通知とかは欲しかったので試してみようかと思います。
今後の機能拡張に期待です。

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

CloudWatchのロググループに手動でテスト用ログを投入したい。

はじめに / やりたいこと

前回の続きで、AWSを使っての作業メモになります。
いくつか挙げていたやりたいことの中の、こちらを試します。

  • ログをよしなに見れるようにする
    • ログで何かのパターンを検出したらアラームを生成する
    • 必要に応じて通知設定をする

ログ周りの設定なのですが、基本はアプリケーションが出すログをチェックするので、メトリクスやアラームの設定が妥当なのかどうかは、タイミングが悪いとなかなか確認できません。
アプリケーション作成側にお願いするには、ちょっと手間がかかります。

手動でテスト用ログを突っ込みたい

ある程度はコンソールで出来ますが、やはりログ生成(ベント送信)のところが悩みどころ。

  • CloudWatchにロググループを作成する (コンソールで作成可能)
  • ロググループ宛にログストリームを作成する (コンソールで作成可能)
  • ログストリームに対してイベントを流す(ログを送る)
    • これはコンソールからはできない...(キャプチャ参照)
    • ここはアプリケーションにエージェントを仕込むか送信する処理を組み込む
  • メトリクスを作成する (コンソールで作成可能)
  • アラームを作成する (コンソールで作成可能)

ログストリームはつくれてもイベントが送れない...

もしかしたら画面からでもイベントを登録できるかもしれませんが...。

logstream-console.png

AWSの操作は、実際はAPIを介して色々な操作をしますので、正しいお作法でデータを投げれば、あアプリケーションに代わってダミーのログを投入することができるはず...。(わざわざログを送信するアプリケーションを作るのは辛い)

aws cliを眺めていたら、どうやら aws logs put-log-events が使えることが判明。
下記の記事にもとても詳しく書かれていて、非常に助かりました!

これでなんとか試せそうです。

また、イベントの送信以外にコンソールから出来る作業も、できれば何をしたか記録がわかりやすいようにTerraformでやってみたい。
ということで、今回は以下を使うことにしました。

  • Terraform
  • aws cli (とshell script)

Terraformを使ってロググループ&メトリクスを作る

Terraformの設定自体は省略します。
また、今回はCloudWatchの機能を使うだけなので、特に他のリソース(EC2やS3ちったもの)も必要ありません。

ロググループの作成

sample.tf
# TestLogGroupというロググループを定義
resource "aws_cloudwatch_log_group" "test-log-group" {
  name              = "TestLogGroup"
  retention_in_days = 0
  tags              = {}
}

実行は以下。(ターゲットを指定して1つずつやっていきます)

% terraform plan --target=aws_cloudwatch_log_group.test-log-group
Terraform will perform the following actions:

  # aws_cloudwatch_log_group.test-log-group will be created
  + resource "aws_cloudwatch_log_group" "test-log-group" {
      + arn               = (known after apply)
      + id                = (known after apply)
      + name              = "TestLogGroup"
      + retention_in_days = 0
    }

Plan: 1 to add, 0 to change, 0 to destroy.

% terraform apply --target=aws_cloudwatch_log_group.test-log-group

....

aws_cloudwatch_log_group.test-log-group: Creating...
aws_cloudwatch_log_group.test-log-group: Creation complete after 0s [id=TestLogGroup]

ということで、空っぽのロググループが出来ました。この段階では、ログストリームはありませんね。

test-log-group.png

メトリクスの作成

こちらも続けてメトリクスを設定します。対象のロググループがわかればいいのですが、そこはTerraformが解決してくれるので、設定は以下の通り。

log_group_name = "${aws_cloudwatch_log_group.test-log-group.name}"

ここでどのロググループへのフィルタかを設定しています。
(直接、AWSのコンソールからarnを参照して、その値を入れても大丈夫)

metric.tf
# ERRORというパターンを拾います
resource "aws_cloudwatch_log_metric_filter" "error_test-log-group" {
  name           = "ErrorCount_TestLogGroup"
  pattern        = "ERROR"
  log_group_name = "${aws_cloudwatch_log_group.test-log-group.name}"

  metric_transformation {
    name      = "ErrorCount_TestLogGroup"
    namespace = "Logs"
    value     = "1"
  }
}

こちらも、target指定でplan & applyします。

% terraform apply --target=aws_cloudwatch_log_metric_filter.error_test-log-group
aws_cloudwatch_log_group.test-log-group: Refreshing state... [id=TestLogGroup]

...

aws_cloudwatch_log_metric_filter.error_test-log-group: Creating...
aws_cloudwatch_log_metric_filter.error_test-log-group: Creation complete after 0s [id=ErrorCount_TestLogGroup]

こんな感じで、ロググループにフィルタが1つ設定されました。(ここからコンソール側でも調整できます)

metric-filter.png

aws cliを利用してログストリームを作成しログを投入

さて、メトリクスフィルタを設定しても、ここからログを実際に流さないとなにも表示がされません。ここから先は、aws cliを使っていきます。

参考にさせていただいた記事 の通りで、以下の流れになります。

  • ログストリームを作る
  • 1回目はこのログストリームに対して、まず初回のイベントを登録
  • 同じログストリームにイベントをつなげる場合は、2回目はログストリームのuploadSequenceTokenを指定して送信する

参考: 文字列をCloudWatchLogsにPUTしてみた (by DevelopersIO)

わたしの場合は、こんな感じにしています。

  • スクリプトを実行するたびに、別のログストリームを作成する
  • そのログストリームに対して11個分のイベントを送信する
  • 2回目以降のイベントはERRORを含むものと含まないものを偶数奇数で分ける
log-stream-push.sh
TIME_EPOCH="`date +%s`000"
STREAM_DATETIME="`date +%Y%m%d%H%M%S`"

LOG_GROUP_NAME="TestLogGroup"
LOG_STREAM_NAME="test-stream-${STREAM_DATETIME}"

# イベントメッセージを生成する関数
function generate_msg() {
  LOG_TIMESTAMP=`date +%Y-%m-%dT%H:%M:%S%z`

  # サンプルのログメッセージを生成(適度に場合わけ)
  if [ $1 -eq 0 ]; then
    LOG_MSG="[INFO]${LOG_TIMESTAMP}:  :   Rendered layouts/_common.html.erb (2.5ms)"
  else
    LOG_MSG="[ERROR]${LOG_TIMESTAMP}:  :   Something error occurred: xxxxx"
  fi

  # shellの関数はreturnだと数値しか返せないので、echoで送って、
  # MSG=`generate_msg 引数` で受ける
  echo ${LOG_MSG}
}

# 1回目はテスト用のstreamを作成
LOG_MSG=`generate_msg 0`
LOG_EVENTS="timestamp=${TIME_EPOCH},message=\"${LOG_MSG}\""

aws logs create-log-stream \
        --log-group-name ${LOG_GROUP_NAME} \
        --log-stream-name ${LOG_STREAM_NAME}


aws logs put-log-events \
        --log-group-name ${LOG_GROUP_NAME} \
        --log-stream-name ${LOG_STREAM_NAME} \
        --log-events "${LOG_EVENTS}"

# 2回目以降はstreamのトークンを取得して実行

for i in {1..10} ; do
  MSG_PATTERRN=$(( $i & 1 ))

  LOG_MSG=`generate_msg $MSG_PATTERRN`

  # 出来上がったログストリームから、uploadSequenceTokenを取得
  LOG_PUT_TOKEN=`aws logs describe-log-streams --log-group-name ${LOG_GROUP_NAME} \
        --log-stream-name-prefix ${LOG_STREAM_NAME} \
        --query 'logStreams[].uploadSequenceToken' --output text` \
        && echo ${LOG_PUT_TOKEN}
  LOG_EVENTS="timestamp=${TIME_EPOCH},message=\"${LOG_MSG}\""

  aws logs put-log-events \
        --log-group-name ${LOG_GROUP_NAME} \
        --log-stream-name ${LOG_STREAM_NAME} \
        --log-events "${LOG_EVENTS}" \
        --sequence-token ${LOG_PUT_TOKEN}
done

実行してみます。確認用にuploadSequenceTokenを出力しています。

% sh log-stream-push.sh
{
    "nextSequenceToken": "49594xxxxxxxxxxx082"
}
{
    "nextSequenceToken": "49594xxxxxxxxxxx634"
}
....

aws cliからの結果はダンマリですが、コンソール側を確認すると、ちゃんとログが登録されています。(タイムスタンプ改良の余地が...)

test-log-stream-result.png

ログストリーム投入後のメトリクス

イベント(ログ)が投入される様になると、メトリクス側にも反映されてきます。
何回かスクリプトを回して、5回エラーが出たり、10回エラーが出ていることが分かります。

start-metrics.png

Terraformでアラームを設定してみる

手動でのベント登録でもなんとかなりそう!
では、上記で設定したメトリクスをつかって、アラーム設定をしてみます。
こちらもTerraformで設定します。

  • テスト用なので、perod (間隔)を60秒に
  • 合計で6回以上検出されたらアラームを出す
    • 手元で1分につきスクリプトを1回だけ実行のときはアラームが出ません
    • 手元で1分につきスクリプトを2回以上実行すると、アラームが出るはず
  • Emailなどでアクションを起こす場合はalarm_actionsに対象のトピックを登録
    • 今回は空っぽにします
alarm.tf
# 上記で設定したメトリクスをつかって、アラーム設定
resource "aws_cloudwatch_metric_alarm" "alarm_error_test-log-group" {
    actions_enabled           = true
    alarm_actions             = [ ]
    alarm_description         = "ERRORを検出してアラート"
    alarm_name                = "Alerm_ErrorCount_TestLogGroup"
    comparison_operator       = "GreaterThanOrEqualToThreshold"
    datapoints_to_alarm       = 1
    dimensions                = {}
    evaluation_periods        = 1
    insufficient_data_actions = []
    metric_name               = "ErrorCount_TestLogGroup"
    namespace                 = "Logs"
    ok_actions                = []
    period                    = 60
    statistic                 = "Sum"
    tags                      = {}
    threshold                 = 6
    treat_missing_data        = "notBreaching"
}

こちらもapplyしてみます。

 terraform apply --target=aws_cloudwatch_metric_alarm.alarm_error_test-log-group

コンソールでの画面

数回試してみました。
以下は、3回出現の場合はOKに。その後9回出現してアラームが発生した例です。

3回なので閾値には該当しません。

alarm-close.png

その後、9回検出したので、アラームが1件上がって来ました。

alarm-again.png

こんな感じでうまくいったようです。

あとは通知先のアクションに、トピックを紐づければよさそうです。

まとめ

以上、ちょっとした作り込みでしたが、なんとかスクリプトからログを生成してテスト用のアラームを発生させるところまで出来ました。
アラーム自体が適切に設定されていれば、あとは実際のアプリケーションのロググループに対して同じ様にしていけば良さそうです。
また、アプリケーションがなくても手動でログを流してアラアーム発生させて、Lambdaのテストをするといったこともやりやすそうです。

あらためて、インフラの設定をしつつも、「API叩いてるんだなあ...」というのを感じた作業です。

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

Amazon Linux 1 に acme.sh (HTTP-01 challenge)で Let's Encrypt を入れる

概要

  • とある有償SSLサーバ証明書の有効期限がそろそろ切れるので、これを機に、これまでの有償+手動更新から、Let's Encrypt を利用した 無償+自動更新 で幸せになろうと思ったけど、インストールするサーバが Amazon Linux 1 でしかも古いバージョンのため、Let's Encrypt の SSLサーバ証明書をインストールするのにいろいろ試行錯誤した記録。

環境

  • Let's Encrypt の SSLサーバ証明書を入れるサーバ
    • Amazon Linux 1(AMI release 2015.09)←古すぎ!
      $ cat /etc/system-release
      Amazon Linux AMI release 2015.09
      
  • Webサーバ

    • Nginx(BASIC認証でアクセス制限している)
    • ドキュメントルート /usr/share/nginx/html
  • ドメイン

    • foo.staging.co.jp(今回の説明用のダミードメイン)
  • IPアドレス/ポート番号制限

Certbot の断念

無償のSSLサーバ証明書といえば『Let's Encrypt』ということで、公式サイトのGetting Startedを読むと、Certbot ACME client を推奨している。ということで、まずはサーバに Certbot のインストールを試みる。

Amazon Linux 1 は CentOS 6 のパッケージを使えるので、Let's Encrypt 総合ポータル サイトの "CentOS 6 / RHEL 6" のインストール方法 に書いてある通り、EPEL6 リポジトリから入れようと思ったら、certbot パッケージが無いというエラー。確かに、EPEL 6: x86_64 リポジトリ にcertbotパッケージが無い。。

なので、Let's Encrypt 総合ポータル サイトの "その他の UNIX 系 OS" のインストール方法に書いてある通り実行すると・・・FATAL: Amazon Linux support is very experimental at present...

$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
$ ./certbot-auto
Requesting to rerun ./certbot-auto with root privileges...
FATAL: Amazon Linux support is very experimental at present...
if you would like to work on improving it, please ensure you have backups
and then run this script again with the --debug flag!
Alternatively, you can install OS dependencies yourself and run this script
again with --no-bootstrap.
「--debug」オプションを付けて再度実行してみると、次のパッケージの Install/Update をしろと、、
$ ./certbot-auto --debug
〜略〜
Installing:
 augeas-libs
 gcc48
     replacing  libquadmath48-devel.x86_64 4.8.3-9.109.amzn1
 gcc48-c++
     replacing  libstdc++48-devel.x86_64 4.8.3-9.109.amzn1
 libffi-devel
 python27-tools

Updating:
 ca-certificates
 gcc
 openssl
 openssl-devel
 python27-devel
 python27-pip
 python27-virtualenv
 system-rpm-config
Installing for dependencies:
 libgfortran
Updating for dependencies:
 cpp48
 gcc-c++
 gcc-gfortran
 gcc48-gfortran
 libffi
 libgcc48
 libgomp
 libstdc++48
 libtool
 python27
 python27-libs

Let's Encrypt 総合ポータル サイトに、しれっと注意書きがある。。
うーん、、 Install/Update するのは怖いよね。。

ということで、certbot は諦めて、別の ACME client を使ってみようということで、ACME v2 Compatible Clientsからacme.sh を選択。acme.sh はシェルスクリプトで書かれていて、シェルが動く環境であれば導入が楽ですぐに使えるらしい。

acme.sh を使って Let's Encrypt SSLサーバ証明書をインストールする

acme.sh をインストールする

公式サイトの通りに実行、

 # cd /root
 # curl https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed   
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

curl: (35) Cannot communicate securely with peer: no common encryption algorithm(s).    

あれ、curl が古い。。nssをアップグレードする。

 # yum upgrade nss

再度、、

 # cd /root
 # curl https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   705    0   705    0     0   4617      0 --:--:-- --:--:-- --:--:--  4607
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  184k  100  184k    0     0   492k      0 --:--:-- --:--:-- --:--:--  492k
[Fri Jul 19 12:11:23 JST 2019] Installing from online archive.
[Fri Jul 19 12:11:23 JST 2019] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Fri Jul 19 12:11:24 JST 2019] Extracting master.tar.gz
[Fri Jul 19 12:11:24 JST 2019] It is recommended to install socat first.
[Fri Jul 19 12:11:24 JST 2019] We use socat for standalone server if you use standalone mode.
[Fri Jul 19 12:11:24 JST 2019] If you don't use standalone mode, just ignore this warning.
[Fri Jul 19 12:11:24 JST 2019] Installing to /root/.acme.sh
[Fri Jul 19 12:11:24 JST 2019] Installed to /root/.acme.sh/acme.sh
[Fri Jul 19 12:11:24 JST 2019] Installing alias to '/root/.bashrc'
[Fri Jul 19 12:11:24 JST 2019] OK, Close and reopen your terminal to start using acme.sh
[Fri Jul 19 12:11:24 JST 2019] Installing alias to '/root/.cshrc'
[Fri Jul 19 12:11:24 JST 2019] Installing alias to '/root/.tcshrc'
[Fri Jul 19 12:11:24 JST 2019] Installing cron job
[Fri Jul 19 12:11:24 JST 2019] Good, bash is found, so change the shebang to use bash as preferred.
[Fri Jul 19 12:11:24 JST 2019] OK
[Fri Jul 19 12:11:24 JST 2019] Install success!

入った。
crontab (/var/spool/cron/root) に次のような設定(自動更新用)が追加される。

24 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

Nginx mode で SSLサーバ証明書を発行してみる

  • acme.sh のオプション
    • --issue・・・SSLサーバ証明書発行
    • --nginx・・・Nginx mode
    • -d・・・SSLサーバ証明書を発行するドメイン
 # cd /root/.acme.sh
 # ./acme.sh --issue --nginx -d foo.staging.co.jp
[Fri Jul 19 12:17:47 JST 2019] Create account key ok.
[Fri Jul 19 12:17:47 JST 2019] Registering account
[Fri Jul 19 12:17:48 JST 2019] Registered
[Fri Jul 19 12:17:48 JST 2019] ACCOUNT_THUMBPRINT='1W2zh17CQyWXgr3TCI5DVgl_728ut2X7oi5r4RByFwg'
[Fri Jul 19 12:17:48 JST 2019] Creating domain key
[Fri Jul 19 12:17:48 JST 2019] The domain key is here: /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 19 12:17:48 JST 2019] Single domain='foo.staging.co.jp'
[Fri Jul 19 12:17:48 JST 2019] Getting domain auth token for each domain
[Fri Jul 19 12:17:49 JST 2019] Getting webroot for domain='foo.staging.co.jp'
[Fri Jul 19 12:17:49 JST 2019] Verifying: foo.staging.co.jp
[Fri Jul 19 12:17:49 JST 2019] Nginx mode for domain:foo.staging.co.jp
[Fri Jul 19 12:17:49 JST 2019] Found conf file: /etc/nginx/nginx.conf
[Fri Jul 19 12:17:49 JST 2019] Backup /etc/nginx/nginx.conf to /root/.acme.sh/foo.staging.co.jp/backup/foo.staging.co.jp.nginx.conf
[Fri Jul 19 12:17:49 JST 2019] Check the nginx conf before setting up.
[Fri Jul 19 12:17:49 JST 2019] OK, Set up nginx config file
[Fri Jul 19 12:17:49 JST 2019] nginx conf is done, let's check it again.
[Fri Jul 19 12:17:49 JST 2019] Reload nginx
[Fri Jul 19 12:17:54 JST 2019] foo.staging.co.jp:Verify error:Invalid response from http://foo.staging.co.jp/.well-known/acme-challenge/6Tb5KM3d0jhtiRJ6eoqfa68u_0BFBF1eqgdvtH7g4sU [x.x.x.x]: 401
[Fri Jul 19 12:17:54 JST 2019] Restoring from /root/.acme.sh/foo.staging.co.jp/backup/foo.staging.co.jp.nginx.conf to /etc/nginx/nginx.conf
[Fri Jul 19 12:17:54 JST 2019] Reload nginx
[Fri Jul 19 12:17:54 JST 2019] Please add '--debug' or '--log' to check more details.
[Fri Jul 19 12:17:54 JST 2019] See: https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh

エラー出た。。
Verify error:Invalid response from http://foo.staging.co.jp/.well-known/acme-challenge/6Tb5KM3d0jhtiRJ6eoqfa68u_0BFBF1eqgdvtH7g4sU [x.x.x.x]: 401

そもそも、何やっているの?

acme.sh は SSLサーバ証明書を発行する際に HTTP-01 challenge で検証を行っている。
acme.sh --issue 実行時に、acme.sh は Nginx の "ルートディレクトリ/.well-known/acme-challenge/" ディレクトリ内にトークンファイルを一時的に作成して、acme.sh がトークンファイルの準備が出来たことを Let's Encrypt に伝えたら、Let's Encrypt は "http://foo.staging.co.jp/.well-known/acme-challenge/トークン" にアクセスを試みる。Let's Encrypt が正しい応答を得られたら検証は成功とみなされ、SSLサーバ証明書が発行される。

Nginx mode は、自動的に nginx コンフィグファイルを探して、"ルートディレクトリ/.well-known/acme-challenge/トークンファイル" へのアクセス設定を施して、検証が終わったら、設定を元に戻す。

今回は、Nginx で Basic 認証を行っているため、Let's Encrypt からのアクセスが 401 となり、検証が失敗してしまった。

ちなみに、HTTP-01 challenge アクセスは、Let's Encryptの接続元IPアドレスは公開していないため、クライアント側は HTTP ポート80番で任意のIPアドレスからの接続を許可しておく必要がある。

トークンへのアクセスはBASIC認証対象外として、ノーマルモードで SSLサーバ証明書を発行してみる

まずは、HTTP-01 challenge 用のディレクトリを手動で作成する。

 # cd /usr/share/nginx/html
 # mkdir -p .well-known/acme-challenge
 # chmod -R 755 .well-known/acme-challenge
 # chown -R nginx:nginx /usr/share/nginx/html/.well-known/acme-challenge

"http://foo.staging.co.jp/.well-known/acme-challenge/トークン" へのアクセスは BASIC認証対象外とする
/etc/nginx/nginx.conf 設定例

〜
  location /.well-known  {
    satisfy any;
    allow   all;
  }
  location /.well-known/acme-challenge  {
    satisfy any;
    allow   all;
  }
〜

nginx 再起動

 # service nginx configtest
 # service nginx restart

ノーマルモードでSSLサーバ証明書を発行する

  • acme.sh のオプション
    • -w・・・ドキュメントルート
 # cd /root/.acme.sh
 # ./acme.sh --issue -d foo.staging.co.jp -w /usr/share/nginx/html
[Fri Jul 26 12:48:13 JST 2019] Create account key ok.
[Fri Jul 26 12:48:13 JST 2019] Registering account
[Fri Jul 26 12:48:14 JST 2019] Registered
[Fri Jul 26 12:48:14 JST 2019] ACCOUNT_THUMBPRINT='6k1I3oh7R3iEIrnWIEWMsRt0rmUzf4T2CXgn9TaXvoE'
[Fri Jul 26 12:48:14 JST 2019] Creating domain key
[Fri Jul 26 12:48:14 JST 2019] The domain key is here: /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 12:48:14 JST 2019] Single domain='foo.staging.co.jp'
[Fri Jul 26 12:48:14 JST 2019] Getting domain auth token for each domain
[Fri Jul 26 12:48:15 JST 2019] Getting webroot for domain='foo.staging.co.jp'
[Fri Jul 26 12:48:15 JST 2019] Verifying: foo.staging.co.jp
[Fri Jul 26 12:48:18 JST 2019] Success
[Fri Jul 26 12:48:18 JST 2019] Verify finished, start to sign.
[Fri Jul 26 12:48:18 JST 2019] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/62039946/782487887
[Fri Jul 26 12:48:20 JST 2019] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/03d9bc51c44775de73e376f19b890cc6f5bf
[Fri Jul 26 12:48:20 JST 2019] Cert success.
-----BEGIN CERTIFICATE-----
MIIFcjCCBFqgAwIBAgISA9m8UcRHdd5z43bxm4kMxvW/MA0GCSqGSIb3DQEBCwUA
〜略〜
5Kh5eZhP
-----END CERTIFICATE-----
[Fri Jul 26 12:48:20 JST 2019] Your cert is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.cer
[Fri Jul 26 12:48:20 JST 2019] Your cert key is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 12:48:20 JST 2019] The intermediate CA cert is in  /root/.acme.sh/foo.staging.co.jp/ca.cer
[Fri Jul 26 12:48:20 JST 2019] And the full chain certs is there:  /root/.acme.sh/foo.staging.co.jp/fullchain.cer

おぉ、発行された!

発行されたSSLサーバ証明書を確認する。

 # ./acme.sh --list
Main_Domain        KeyLength  SAN_Domains  Created                       Renew
foo.staging.co.jp  ""         no           Fri Jul 26 03:48:20 UTC 2019  Tue Sep 24 03:48:20 UTC 2019

発行されたSSLサーバ証明書を Nginx にインストールする

cert keyファイルと、full chain certs ファイルをを置くディレクトリを作成する。

 # mkdir -p /etc/nginx/certs/letsencrypt/foo.staging.co.jp

/etc/nginx/certs/letsencrypt/foo.staging.co.jp ディレクトリに、foo.staging.co.jp.key ファイルと、fullchain.cer ファイルをインストール (--install-cert) する。

 # cd /root/.acme.sh/
 # ./acme.sh --install-cert -d foo.staging.co.jp --key-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key --fullchain-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer

nginx コンフィグファイルで証明書のパスを設定する
/etc/nginx/conf.d/ssl.conf 設定例

〜
  ssl_certificate     /etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer;
  ssl_certificate_key /etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key;
〜

nginx 再起動

 # service nginx configtest 
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

 # service nginx restart
Stopping nginx:     [  OK  ]
Starting nginx:     [  OK  ]

ブラウザから https://foo.staging.co.jp へアクセスして、SSLサーバ証明書を確認する。
image.png

インストール出来た。

自動更新設定

自動更新では、SSLサーバ証明書発行後に、"--install-cert でインストールしてnginxをリロード" させる。
そのジョブを実行する。

 # /root/.acme.sh
 # ./acme.sh --install-cert -d foo.staging.co.jp --key-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key --fullchain-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer --reloadcmd "service nginx force-reload"
[Fri Jul 19 17:18:49 JST 2019] Installing key to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 19 17:18:49 JST 2019] Installing full chain to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer
[Fri Jul 19 17:18:49 JST 2019] Run reload cmd: service nginx force-reload
Reloading nginx:          [  OK  ]

これで、cronによる自動更新ジョブで、SSLサーバ証明書発行後に、"--install-cert でインストールしてnginxをリロード" するようになる。

acme.sh インストール時に crontab に自動更新のコマンドが追加されているので、
crontab (/var/spool/cron/root)

24 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

これを使って、手動で強制的 (--force) に更新処理を実行してみる。

 # "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" --force
[Fri Jul 26 13:24:13 JST 2019] ===Starting cron===
[Fri Jul 26 13:24:13 JST 2019] Renew: 'foo.staging.co.jp'
[Fri Jul 26 13:24:14 JST 2019] Single domain='foo.staging.co.jp'
[Fri Jul 26 13:24:14 JST 2019] Getting domain auth token for each domain
[Fri Jul 26 13:24:15 JST 2019] Getting webroot for domain='foo.staging.co.jp'
[Fri Jul 26 13:24:15 JST 2019] foo.staging.co.jp is already verified, skip http-01.
[Fri Jul 26 13:24:15 JST 2019] Verify finished, start to sign.
[Fri Jul 26 13:24:15 JST 2019] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/62039946/782634405
[Fri Jul 26 13:24:16 JST 2019] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/037597c36c53830c77a547a4c949f57d0974
[Fri Jul 26 13:24:17 JST 2019] Cert success.
-----BEGIN CERTIFICATE-----
MIIFcjCCBFqgAwIBAgISA3WXw2xTgwx3pUekyUn1fQl0MA0GCSqGSIb3DQEBCwUA
〜略〜
7AFa7o7V
-----END CERTIFICATE-----
[Fri Jul 26 13:24:17 JST 2019] Your cert is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.cer
[Fri Jul 26 13:24:17 JST 2019] Your cert key is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 13:24:17 JST 2019] The intermediate CA cert is in  /root/.acme.sh/foo.staging.co.jp/ca.cer
[Fri Jul 26 13:24:17 JST 2019] And the full chain certs is there:  /root/.acme.sh/foo.staging.co.jp/fullchain.cer
[Fri Jul 26 13:24:17 JST 2019] Installing key to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 13:24:17 JST 2019] Installing full chain to:/etc/nginx/certs/letsencrypt/fullchain.pem
[Fri Jul 26 13:24:17 JST 2019] Installing full chain to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer
Reloading nginx:                                           [  OK  ]
[Fri Jul 26 13:24:17 JST 2019] Reload success
[Fri Jul 26 13:24:17 JST 2019] ===End cron===

SSLサーバ証明書発行して、インストールして、nginx 再起動してる。
ブラウザからSSLサーバ証明書の有効期限を確認する。
有効期限が新しく発行したSSLサーバ証明書になっていれば、成功。

ちなみに、Let's Encrypt の SSLサーバ証明書の有効期限は90日だけど、
acme.sh は cronで毎日更新処理を試行して、SSLサーバ証明書発行から60日経過している場合に、更新処理を実行する。

補足

バージョン管理している場合は、"ルートディレクトリ/.well-known/acme-challenge/"の追加を忘れずに。
デプロイしたら"ルートディレクトリ/.well-known/acme-challenge/"ディレクトリが消えないように。

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