Datadog で Rails Unicorn の Memory, Idle|Busy Worker 監視 〜呉越同舟〜

Datadog で Rails Unicorn の Memory, Idle|Busy Worker 監視 〜呉越同舟〜

概要

Rails の乗っているホストへ Datadog で Unicorn を監視しようとした所、
それらしい Integration がありません((あったら教えてください >_< ))。

ということで独自スクリプトを作成しようと思いました!

独自スクリプトを書こうとしてたら…

同僚「Mackerel なら plugin ありますよ?」

自分「えっ?…」

Mackerel 入ってる

Mackerel に unicorn 監視用の plugin がありました。

mackerel-plugin-unicorn

はてなさんも OSS で出して頂いている、
車輪の再開発は時間の無駄、
人生は一度しかないのでこの Mackerel プラグインを Datadog で使わせて頂こうと思いました。

Mackerel + Datadog 呉越同舟スクリプト

  • /etc/dd-agent/unicorn_check.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from checks import AgentCheck
import subprocess
import re
class UnicornCheck(AgentCheck):
def check(self, instance):
pidfile = instance['pidfile']
cmd = "/usr/bin/mackerel-plugin-unicorn -pidfile=%s" % (pidfile)

res = self.exeCmdWithStripLF(cmd)

for r in res:
y = re.split(r'\t+', r.rstrip('\t'))
metrics = y[0]
out = y[1]
self.gauge(metrics, out)

# コマンド実行結果から改行コードから取り除く
def exeCmdWithStripLF(self, cmd):
res = self.exeCmd(cmd)
return [str(x).rstrip("\n") for x in res]

# コマンド実行
def exeCmd(self, cmd):
return subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
shell=True
).stdout.readlines()
  • /etc/dd-agent/conf.d/unicorn_check.yaml

Unicorn の PID ファイルを指定します。

1
2
3
4
init_config:

instances:
- pidfile: /path/to/rails_project/shared/tmp/pids/unicorn.pid

Datadog Agent 設定ファイルチェック

1
2
3
$ sudo dd-agent configcheck

unicorn_check.yaml is valid

Datadog Agent 再起動

1
$ sudo service datadog-agent restart

数分後グラフを見てみる

出てきた!

総評

これで呉越同舟型モニタリングができました!

自分自身が呉でも越でもない所に若干の背徳感がありますが
手っ取り早く舟をこしらえたことに本記事の意味があるかと
筆を取りました。

参考になれば幸いです。

terraform workspace で環境毎に tfsate 管理

terraform workspace で環境毎に tfsate 管理

terraform workspace で環境毎に tfsate 管理した話です。

追記 2019/04/17

追記時点で workspace は運用時点の問題が多くあった為、利用していません。以下記事ご参考いただければと思います。

概要

Terraform tfstate の管理をかつて
0.8 系では -backend-config でせっせと環境(stg,prod) 毎に bucket を変えて、
なんてコードを見てきました。

ですが、
workspace で 1 つの bucket に 環境毎に保管できる様になりました。

厳密には環境毎でなくとも
リソースの集合毎、module 毎等で管理するイメージですが

今回はイメージを捉えやすく環境毎で分けました。

歴史

  • 0.5 で S3 で管理、
  • < 0.9 では、 remote config で管理場所を設定
  • = 0.9 では、terraform workspace で同一ディレクトリで複数のリソース群を管理

とより利用しやすくなりました。

前提

以下条件とします。

  • tfstate は backend.tf で s3 管理

移行手順

既存 terraform で tfstate 確認

  • 想定の実行計画通りか確認します。
  • 異なる場合は、そもそも現環境と差分が生じている、及び、tfstate が正しく取得できていない等、問題ありなのでそちらを修正します。
1
$ terraform plan

tfstate ファイル取得

local に terraform.tfstate を取得します。
中身を確認してリソースの設定がある程度問題ないか確認しておきます。

  • 0.8 系
1
2
3
4
5
6
$ terraform remote config \
-backend=s3 \
-backend-config="bucket=tfstate.bucket" \
-backend-config="key=terraform.tfstate" \
-backend-config="region=ap-northeast-1" \
-backend-config="profile=aws-hogehoge"
  • 0.9 系以降
1
macOS%$ terraform state pull > terraform.tfstate

terraform 0.11.x (2017 年 12 月現在最新) へバージョンアップ

Homebrew ならば upgrade で!

1
macOS%$ brew upgrade terraform

state 管理を backent.tf で記述

既にこの様に設定されている方はスキップです。特に普遍的な書き方です。

1
2
3
4
5
6
7
8
9
terraform {
backend "s3" {
bucket = "tfstate.bucket"
key = "terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
profile = "aws-hogehoge"
}
}

Workspace 作成

  • Workspace stg 作成
1
$ terraform workspace new stg
  • workspace リスト一覧
1
2
3
$ terraform workspace list
default
* stg

tfstate を push

1
$ terraform state push -force .terraform/terraform.tfstate

これで S3 tfstate.bucketenv:/stg/ ディレクトリ以下に terraform.tfstate が push されました。
実際に S3 を見て確認してみてください。

env でなく env: なのが肝です。

実行計画確認

1
$ terraform plan

想定の実行計画通りか確認して問題なければ移行完了です。

おまけ

terraform を指定したバージョンで実行するには
one-off Container で実行できる様に Makefile でラップする、
が今の所自分の中のベストプラクティスです。

これによって local 環境に依存せず指定したバージョンの terraform 実行が可能となります。

one-off Container とは

one-off Container は Docker コンテナを run --rm で 1 度のコマンド実行の為だけに起動する手法です。

Makefile で Docker コマンドをラップしておくと
TERRAFORM_VERSION を変更するだけで
指定の terraform バージョンを利用できます。

以下は 0.11.1 の例です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
TERRAFORM_VERSION=0.11.1

DOCKER=docker run --rm -v ~/.ssh:/root/.ssh:ro -v ~/.aws:/root/.aws:ro -v ${PWD}:/work -w /work hashicorp/terraform:${TERRAFORM_VERSION}

$(eval ENV := $(shell ${DOCKER} workspace show))

ifeq (${ENV}, default)
$(error select workspace ! ex: make init ENV=<stg|prod>)
endif

default: init

init:
# tfstate ファイル初期化
${DOCKER} init
# workspace 作成. "; true" は既に作成済みエラーをスキップする為
${DOCKER} workspace new ${ENV}; true
# 作成した workspace を選択
${DOCKER} workspace select ${ENV}
# 作成した workspace の tfstate ファイルを同期
${DOCKER} init

plan:
${DOCKER} plan

apply
${DOCKER} apply -auto-approve
  • make init ENV=stg 実行で以下まとめてました
    • tfstate 初期化
    • workspace stg 作成
    • 選択した workspace の tfstate で初期化

きっとさらに素敵なベストプラクティスがあれば教えてください!

参考になれば幸いです。

Hubot で Git の Pull Request や Issue のコメントのメンション相手に Slack DM で通知

概要

Git での Pull Request や Issue コメントのメンションがメール通知で気づけず困った!
という声を多く聞き、メンション相手に Slack DM を通知する様な仕組みを作りました。

システム概要

今回は AWS 上に構築しました。

  • Git は GHE on EC2
    • github.com の場合だと、IP 定まらない問題があるかと思うので、動的に IP を取得して解放させる様な仕組みを入れる必要がありそう。
  • hubot は t2.nano と最小
    • 当初、IBM Bluemix で構築してみましたが、サポートから IP 制限はまだできていない、とのことなので on AWS にしました。
  • GHE からの hubot の受け口は ELB で EIP のみ許可させてます。
    • 今後、受け口を色々作る目的で ELB 立てました。
    • 元々は JIRA のメンションを Slack DM に送るだけの目的だったので 同一 Private Subnet に置いてました。

スクリプト

  • getSlackUsernameByGitUsername
    • 基本 git name と slack name は命名規則が統一されていたので正規表現で変換させる様に解決しています。
    • git name: kenzo-tanaka だったら slack name: kenzo.tanaka に変換
    • 命名規則に即していないユーザは以下の users リストに変換を任せます。
      • kimika.himura は DM 送られたくないと言う人を想定してます。
  • 依存ライブラリ
    • “hubot-slack”: “^4.4.0”
    • “hubot-slack-attachement”: “^1.0.1”
1
2
3
4
users = {
"kenzo-tanaka": "kenzo0107",
"kimika.himura": "no_send"
}

ソース全容は以下になります。

Git 設定

設定したい Organization or Owner > Settings > Hooks で hubot への URL を設定します。((Organization 跨いで一気に全部のリポジトリにHookかけるのは別途スクリプト組むなりしないと難しそう。GitHub社も Organization は 1つとすることを推奨とのことなので今回はこれで!))

その他設定

  • Content type: application/json
  • Let me select individual events:
    • Issues
    • Issue comment
    • Pull request
    • Pull request review
    • Pull request review comment

※ よりセキュアにする際には Secret 設定してください。

通知が来た!

早速 Pull Request でメンションしてみたら通知が来ました!
絵文字もしっかり!
URL も自動でリンクされている!

以上、参考になれば幸いです♪

Prometheus2.0 remote storage 検証

Prometheus2.0 remote storage 検証

いよいよ出ました Prometheus 2.0 !

Announcing Prometheus 2.0 | Prometheus

先日モニタリング勉強会でも Paul Taylor さんの LT を拝聴させて頂き
パフォーマンス向上とストレージフォーマット変更による圧縮・バックアップがしやすくなった等、
良い話がたくさん出ていました。

Operating Prometheus

中でも最も期待していた機能が Remote Long-Term Storage、
長期保存機能には歓喜しました ♪

1 系以下では、短期間用と長期間用の Prometheus を別途用意する等、対策が必要で
冗長な作りを余儀なくされたところがありましたが
2.0 リリースでついに!

早速試してみたく使用感をまとめました。

今回やりたかったことまとめ

  • Prometheus 2.0 リリースに際して期待の長期保存機能 (Remote long-term storage) を早速試す!
  • 実際にローカル環境で構築してみて 1 系からの変更箇所を確認
  • DB 側にどんなデータが入るのか確認

システム概要

あくまで使用感の検証をしたかったので docker-compose でお手軽に作れる環境にしました。

前提条件

以下を Vagrant にインストール

  • Ubuntu 16.04.3 LTS \n \l
  • Docker version 17.09.0-ce, build afdb6d4
  • docker-compose version 1.12.0, build b31ff33

起動する Docker Container

  • Prometheus 2.0.0
  • Node Exporter 0.15.1
  • AlertManager 0.9.1
  • cAdvisor 0.28.0
  • Prometheu Adapter
  • PostgreSQL 9.6.3
  • Grafana 4.6.1
  • Nginx 1.13.6
  • Adminer

使い方

以下手順通りです。

kenzo0107/vagrant-docker/tree/vagrant-docker-ubuntu16.04/docker/prometheus-grafana-on-ubuntu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
macOS%$ git clone https://github.com/kenzo0107/vagrant-docker
macOS%$ cd vagrant-docker
macOS%$ vagrant up

// Install docker, docker-compose
macOS%$ vagrant provision
macOS%$ vagrant ssh
vagrant%$ cd /vagrant/prometheus-grafana-on-ubuntu
vagrant%$ sudo docker-compose up -d

Name Command State Ports
-------------------------------------------------------------------------------------------------------------------------------------
adapter /prometheus-postgresql-ada ... Up
adminer entrypoint.sh docker-php-e ... Up 8080/tcp
alertmanager /bin/alertmanager -config. ... Up 9093/tcp
cadvisor /usr/bin/cadvisor -logtost ... Up 8080/tcp
grafana /run.sh Up 3000/tcp
nginx nginx -g daemon off; Up 0.0.0.0:18080->18080/tcp,
0.0.0.0:3000->3000/tcp, 80/tcp,
0.0.0.0:8080->8080/tcp,
0.0.0.0:9090->9090/tcp
node-exporter /bin/node_exporter Up 9100/tcp
pgsql docker-entrypoint.sh -csyn ... Up 5432/tcp
prometheus /bin/prometheus --config.f ... Up 9090/tcp

アクセスしてみる

Prometheus

Grafana

1
2
GF_SECURITY_ADMIN_USER=admin-user
GF_SECURITY_ADMIN_PASSWORD=admin-pass
  • Datasource 設定

Datasource 設定フォームに以下情報を入力し Add ボタンをクリックします。

Item Value
Name Prometheus
Type Prometheus
URL http://prometheus:9090
Access proxy
  • Dashboard.json インポート

グラフが表示されます。

cAdvisor

Adminer

ログインフォームに以下情報を入力します。

Item Value
Server pgsql
Username prometheus
Password password
Database postgres
  • PostgreSQL に保存されているメトリクス情報が確認できます。

PostgreSQL >> pgsql >> postgres >> prometheus >> Select: metrics

AlertManager でアラート通知してみる

例として node-exporter を停止

1
vagrant%$ sudo docker-compose stop node-exporter

./alertmanager/config.yml で設定した Slack Channel にちゃんと通知がきました。

所感

  • 2.0 になって設定の仕方が諸々変わり、公式サイトじっくり見る必要あります。

  • 今回は Prometheus ×1 台構成ですが、2 台以上で冗長化する構成も試してみたい。

余談

あとがき

Mackerel の様なマネージドな監視サービスで運用コストを削減する以上に
Prometheus をマネージドすれば、さらにトータルコストを抑えられる様になる、
と睨んでます。

ですが、Datadog は APM 付きプランも適度なコスト感で提供しておりマネージドサービスの魅力は尚大きいです。

モニタリングの棲み分けをできる様にするにも、
選択肢の一つにするにも Prometheus 挑戦しがいがあるのでは?
と思っています。

Prometheus、今後さらに広まることを期待しています。

参考

iftop でネットワーク接続状況をリアルタイム監視

iftop 概要

CLI上で利用できるネットワークの接続状況をリアルモニタリングするツールです。
→ ネットワークのボトルネックを特定する為に利用します。

単にネットワークのモニタリングであれば、モニタリングツールで良いですが

具体的にどこ(ドメイン・IP・ポート)にどれくらい(データ転送量)がわかります。

インストール方法

  • Ubuntu
1
$ sudo apt-get install -y iftop
  • CentOS
1
2
$ sudo yum -y install epel-release
$ sudo yum -y install iftop

使い方

よく利用するのはこんな形です。
※eth0 がない場合は -i eth0 を除いてください。

1
$ sudo iftop -i eth0 -B -P -n -N
  • -i インターフェース指定
  • -B 表示単位を Byte にする
  • -P プロトコル or ポート表示
  • -n ドメインでなく ip で表示
  • -N プロトコルサービス名でなくポート番号で表示

表示項目

=> が送信、
<= が受信です

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
                           24.4kB                      48.8kB                      73.2kB                      97.7kB                 122kB
+--------------------------+---------------------------+---------------------------+---------------------------+---------------------------
ip-10-13-1-101.ap-northeast-1.compute.internal:http => ip-10-13-100-41.ap-northeast-1.compute.internal:62635 559kB 121kB 67.1kB
<= 3.60kB 1.90kB 1.05kB
ip-10-13-1-101.ap-northeast-1.compute.internal:35244 => ip-10-13-102-56.ap-northeast-1.compute.internal:mysql 0B 2.18kB 1.21kB
<= 0B 23.1kB 12.8kB
ip-10-13-1-101.ap-northeast-1.compute.internal:35247 => ip-10-13-102-56.ap-northeast-1.compute.internal:mysql 0B 2.13kB 1.18kB
<= 0B 23.0kB 12.8kB
ip-10-13-1-101.ap-northeast-1.compute.internal:http => ip-10-13-0-231.ap-northeast-1.compute.internal:8239 0B 7.73kB 4.29kB
<= 0B 1.16kB 658B
ip-10-13-1-101.ap-northeast-1.compute.internal:ssh => ip-10-13-0-11.ap-northeast-1.compute.internal:56320 612B 576B 522B
<= 26B 26B 32B
ip-10-13-1-101.ap-northeast-1.compute.internal:http => ip-10-13-100-41.ap-northeast-1.compute.internal:62657 0B 49B 27B
<= 0B 92B 51B
ip-10-13-1-101.ap-northeast-1.compute.internal:40069 => ip-10-13-103-247.ap-northeast-1.compute.internal:6379 0B 99B 55B
<= 0B 34B 19B
ip-10-13-1-101.ap-northeast-1.compute.internal:40072 => ip-10-13-103-247.ap-northeast-1.compute.internal:6379 0B 99B 55B
<= 0B 34B 19B
ip-10-13-1-101.ap-northeast-1.compute.internal:http => ip-10-13-100-73.ap-northeast-1.compute.internal:27698 0B 44B 25B
<= 0B 33B 18B
ip-10-13-1-101.ap-northeast-1.compute.internal:53696 => ip-10-13-0-2.ap-northeast-1.compute.internal:domain 0B 21B 12B
<= 0B 31B 17B
ip-10-13-1-101.ap-northeast-1.compute.internal:41975 => ip-10-13-0-2.ap-northeast-1.compute.internal:domain 0B 21B 12B
<= 0B 31B 17B
-------------------------------------------------------------------------------------------------------------------------------------------
TX: cum: 1.31MB peak: 560kB rates: 560kB 134kB 74.7kB
RX: 505kB 117kB 3.69kB 49.8kB 28.1kB
TOTAL: 1.81MB 564kB 564kB 184kB 103kB
Item Value
TX (Transmitter) 送信量
RX (Receiver) 受信量
TOTAL iftop 起動からの総量
cum 総量
peak 最大
右端3列 (各トラフィック, rates含む) 2秒、10秒、40秒の転送量平均値

※TX,RX の 「X」 は省略しますよという意味

閲覧し続けると気になる処理があった時には
Shift + P で一旦停止させます。

もう一度開始したい場合は Shift + P です。

実際のCLI

以下見ていただくと白い帯グラフが左から伸びているのが見えるかと思います。
この横棒が一番上のバーの目盛りに相応してぱっと見でどの程度かわかるのが便利です。

DB への接続を確かめる

DB (MySQL) のデフォルトポート 3306 への送受信を調べたいとき

1
$ sudo iftop -B -P -n -N -f "port 3306"

当然ながら受信の方が大きいです。

補足

実際に負荷が高い時等、特定のインシデントがあった際に追記していこうと思います♪

Reference

toda-tocochan-bus flask on IBM Bluemix へ引っ越し

toda-tocochan-bus flask on IBM Bluemix へ引っ越し

GCP から IBM Bluemix へ引っ越しました!

toco ちゃんバス あと何分?

概要

さくら VPS から GCP、
そして今度は GCP から IBM Bluemix に引越ししました。

以前 GCP 運用時の話はコチラ

GCP は GKE に LB かましたら価格がバコッと上がってしまい
無料枠を逸脱してしまいました (>_<)

なんとか低価格で運用したいという目論見です。

何故 Heroku でなく IBM Bluemix ?

IBM Bluemix の良い所は機能が充実している所です。
無料・デフォルトで kibana が見れます。

その他 Git との連携も可です。

以下 Mac で作業することを前提に手順まとめました。

事前準備

  • IBM Bluemix に Sign Up しときます

Signup IBM Bluemix

  • clone
1
2
macOS%$ git clone https://github.com/kenzo0107/toda-tocochan-bus-on-ibmbluemix
macOS%$ cd toda-tocochan-bus-on-ibmbluemix
  • cloudfoundry の CLI インストール
1
2
macOS%$ brew tap cloudfoundry/tap
macOS%$ brew install cf-cli

デプロイ

1
2
3
macOS%$ cf api https://api.ng.bluemix.net
macOS%$ cf login
macOS%$ cf push <application name>
  • API は Region によって変わります。
Region API URL
米国南部 https://api.ng.bluemix.net
英国 https://api.eu-gb.bluemix.net

総評

Cloudfoundry の CLI のお陰で引っ越しも簡単でした ♪

セキュリティとして特定 IP やドメインからアクセスさせないとか出来たら
商用のメソッドとして利用出来そうかなと思いました。

その点質問してみましたが 2 週間ほど連絡がないので再度連絡してみます。
↑ 質問は英語限定でした!

サポートが強化されると有難いなと思いました。

以上
ご参考になれば幸いです。

CasperJS+PhantomJS で Github Organization 移行

CasperJS+PhantomJS で Github Organization 移行

概要

Github Enterprise の Organization 移行を実施した際に CasperJS と PhantomJS でヘッドレスブラウザより操作し移行しました。
Github 上で API がない(はず?)為、この様な対応をしました。

どちらかというと CasperJS+PhantomJS でブラウザ上の試験作りを楽しんでいたこともあり
試してみてすんなりできたので採用した経緯になります。

ヘッドレスブラウザとは?

GUI なしのブラウザを CLI で利用するというもの。
ページ描画や画像ロード、ログインしたりとフロントの試験で期待される機能を持っています。

三行まとめ

  • CasperJS + PhantomJS でログイン認証はじめブラウザ操作で Transfer する工程を実行
  • 移行後、元の URL が移行先にリダイレクトされるか確認
  • 期待する要素が時間内に取得できないときはページをキャプチャ

やってみる

Get Started ご参照ください。

以下にも手順まとめてます。

Clone

1
2
macOS%$ git clone https://github.com/kenzo0107/ghe-org-transfer
macOS%$ cd ghe-org-transfer

scripts/ghe-org-transfer.js の <your ****> 編集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var config = {
// site root.
siteRoot: 'https://<your github domain>',
// Github Login path
loginPath: '/login',
// Github Login Account
loginParams: {
email: '<your email>',
password: '<your password>',
},
viewportSize: {
width: 3000,
height: 2500,
},
paths: ['<your owner>/<your repository>'],
destOrganization: '<your destination of organization>',
reason: 'transfer this repository to new oragnization',
};

移行イメージ

  • ex) hoge/mywonderfulrepo —> moge/mywonderfulrepo
1
2
3
4
paths: [
"hoge/mywonderfulrepo"
],
destOrganization: 'moge',

paths は複数指定しても問題ありません。

移行実施

1
macOS%$ make run
  • 実行結果

※ 移行後に元の URL でリダイレクトされるか試験しています。

1
2
3
4
5
[url] https://github.aiueo.com/hoge/mywonderfulrepo/settings
[step] fill '#transfer_confirm > form'
[step] input checkbox #team-59
[step] click the button labeled "Transfer"
(^-^) redirect ok:https://github.aiueo.co.jp/hoge/mywonderfulrepo to https://github.aiueo.co.jp/moge/mywonderfulrepo

総評

以前 Grafana のグラフのスナップショットを撮る Grafana API がうまく動作しなかったのも
CasperJS+PhantomJS で取得する様にできました。

いやはや便利 ♪

全然別件ですが、サイトの認証に利用される reCAPTCHA の突破など挑戦してましたがうまく行かず…
うまくいったぜ!という方、是非教えてください m(_ _)m

WAF+CloudFront でリファラチェック (直リンク禁止)

WAF+CloudFront でリファラチェック (直リンク禁止)

概要

AWS WAF (Web Application Firewall) を利用し Cloudfront でのリファラ制御を実装しましたのでそのまとめです。

直リンク禁止対策として導入しました。

以下手順になります。

Go to AWS WAF ボタンクリック

サービス > WAF & Shield と辿り Go to AWS WAF クリック

Configure Web ACL ボタンクリック

ACL (Access Control List) を設定していきます。

概要確認

特にチェックせず Next ボタンクリック

web ACL 設定

以下、設定項目を設定し、Next ボタンクリック

Item Value
Web ACL name (任意) 例では Cloudfront の CNAME を設定しています。
CloudWatch metric name Web ACL name を入力すると自動で入力される。変更したい場合のみ変更
Region Global(CloudFront) 選択
AWS resource to associate 対象となる Cloudfront を選択する箇所。運用中の Cloudfront を対象とすると場合は後々設定。

条件作成

今回は文字列一致を条件とする為、 String match conditions にて Create condition ボタンクリック

string match condition 作成

以下設定し Add filter ボタンクリック。
複数 filter がある場合、Add filter を繰り返します。

Item Value
Name (任意)
Part of the request to filter on Header
Header Referer
Match type Contains
Transformation Convert to lowercase
Value to match 対象となるドメイン設定

Add filter 後、 Create ボタンクリック。

Next ボタンクリック

追加したもののすぐに反映されていない。
そのまま Next ボタンクリック

ルール作成

Create rule ボタンクリック。

ルールに条件を紐付け

Name, Cloudwatch metric name を設定し
Add conditions で条件を追加します。

その後、Create ボタンクリック。

ルール以外のリクエストのアクセス禁止

やはり Rule は反映されていない。ですが、続けて
Block all requests that don't match any rules をチェックし Review and create ボタンクリック。

※対象の Cloudfront に反映させたくない場合は、Cloudfront を選択したリソースを解除する必要があります。
※最後に関連付けができるのでここではするべきではないと思います。

確認ページで入力内容確認後作成

Confirm and create ボタンクリック。

対象の web ACL を編集

WEB ACLs より選択し Edit web ACL ボタンクリック

web ACL 編集

  1. 作成したルールを選択
  2. Add rule to web ACL ボタンクリック
  3. Allow 選択
  4. Update ボタンクリック

[f:id:kenzo0107:20171005183720p:plain]

Cloudfront 関連付け

Add association ボタンクリック

Web ACL に Cloudfront を関連付け

Resource で 対象の Cloudfront を選択し Add ボタンクリック

以上で数分後 WAF+CloudFront によるリファラチェックが確認できる状態になります。

アクセス確認

自環境では
ローカルの /etc/hosts 修正し対象ドメインから Cloudfront CNAME へのリンクを貼って確認しました。

Cloudfront CNAME ドメインでのリソースを直接アクセスすると
以下の様な エラーページが表示されることが確認できました。

もう少しユーザフレンドリーに

上記のエラーページは Cloudfront > Error Pages で Create Custom Error Response で S3 上のパスを指定することでカスタマイズが可能です。

是非サイトコンセプトに合ったエラーページをご用意されるとよりユーザフレンドリーな配信になるかと思います。

以上
ご参考になれば幸いです。

AWS Elasticsearch Service バージョンアップ 2.2 → 5.5

AWS Elasticsearch Service バージョンアップ 2.2 → 5.5

概要

AWS Elasticsearch Service (ES) 2.3 → 5.5 へバージョンアップを実施に際して
以下記事をまとめました。

大まかな流れ

  1. ES バージョン 2.3 のドメインから Snapshot 取得
  2. ES バージョン 5.5 のドメイン作成
  3. アプリの fluentd の向け先を ES バージョン 5.5 へ変更
  4. ES バージョン 5.5 のドメインにデータリストア
  5. ES バージョン 2.3 のドメイン削除

現状バージョン確認

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ curl https://<Elasticsearch 2.3 Endpoint Domain>

{
"name" : "Jackdaw",
"cluster_name" : "<Account ID>:xxxxxxxxxx",
"version" : {
"number" : "2.3.2",
"build_hash" : "72aa8010df1a4fc849da359c9c58acba6c4d9518",
"build_timestamp" : "2016-11-14T15:59:50Z",
"build_snapshot" : false,
"lucene_version" : "5.5.0"
},
"tagline" : "You Know, for Search"
}

その他、AWS console のクラスターの設定確認

その他クラスターへ設定している情報をメモ

  • インスタンスタイプ
  • アクセスポリシーの確認

AWS Elasticsearch Service スナップショットを S3 で管理する

IAM role 作成

  • ec2 タイプで作成
  • アクセス権限は特に設定せず 「次のステップ」ボタンクリック
  • ロール名を es-index-backups とし作成

ロール ARN arn:aws:iam:::role/es-index-backups で作成されていることが確認できる

  • 信頼関係の編集

Service を es.amazonaws.com に編集

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "es.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

IAM User 作成

  • ユーザ > ユーザ追加 選択


  • ユーザ名 es-index-backup-user とし プログラムによるアクセスにチェックを入れて 次のステップ クリック


  • 特にポリシーをアタッチせず 次のステップ クリック
  • es-index-backup-user を作成し独自ポリシーで先ほど作成した role へのアクセス許可設定をアタッチします。
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": "arn:aws:iam::<Account ID>:role/es-index-backups"
}
]
}

  • 発行されたアクセスキー、シークレットアクセスキーをメモしておきます。

S3 バケット作成

S3 > バケットを作成する でバケット作成してください。

Elasticsearch にてスナップショットリポジトリ作成

スナップショットを管理するリポジトリを Elasticsearch に作成する必要があります。

Elasticsearch へのアクセス可能なサーバにて以下スクリプト実行します。

  • register_es_repository.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from boto.connection import AWSAuthConnection

class ESConnection(AWSAuthConnection):

def __init__(self, region, **kwargs):
super(ESConnection, self).__init__(**kwargs)
self._set_auth_region_name(region)
self._set_auth_service_name("es")

def _required_auth_capability(self):
return ['hmac-v4']

if __name__ == "__main__":

client = ESConnection(
region='ap-northeast-1',
host='<Elasticsearch 2.3 Endpoint Domain>',
aws_access_key_id='<ACCESS KEY ID>',
aws_secret_access_key='<SECRET ACCESS KEY>', is_secure=False)

resp = client.make_request(
method='POST',
path='/_snapshot/index-backups',
data='{"type": "s3","settings": { "bucket": "<bucket name>","region": "ap-northeast-1","role_arn": "arn:aws:iam::<Account ID>:role/es-index-backups"}}'
)
body = resp.read()
print body
1
2
3
4
5
6
$ chmod +x register_es_repository.py

$ python register_es_repository.py

// 成功
{"acknowledged":true}

リポジトリ登録完了しました。

Snapshot 取得

snapshot 名を 20170926 とします。

1
2
3
4
$ curl -XPUT "https://<Elasticsearch 2.3 Endpoint Domain>/_snapshot/index-backups/20170926"

// 成功
{"accepted":true}

Snapshot 一覧

20170926 という snapshot 名で取得したことが確認できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ curl -s -XGET "https://<Elasticsearch 2.3 Endpoint Domain>/_snapshot/index-backups/_all" | jq .
{
"snapshots": [
{
"snapshot": "20170926",
"version_id": 2030299,
"version": "2.3.2",
"indices": [
"nginx-access-2017.09.09",
"nginx-access-2017.09.07",
"nginx-access-2017.09.08",
"nginx-error-2017.08.24",
"nginx-error-2017.08.23",
".kibana-4",
...
],
"state": "IN_PROGRESS",
"start_time": "2017-09-26T03:58:51.040Z",
"start_time_in_millis": 1506398331040,
"failures": [],
"shards": {
"total": 0,
"failed": 0,
"successful": 0
}
}
]
}

Snapshot 削除

スナップショット 20170926 を削除する場合、DELETE メソッドを実行します。

1
2
3
4
$ curl -XDELETE https://<Elasticsearch 2.3 Endpoint Domain>/_snapshot/index-backups/20170926

// 成功
{"acknowledged":true}

S3 確認

以下が作成されているのがわかります。

  • indices/*
  • meta-*
  • snap-*

はじめ meta-_ が作成できたら完了なのかなと思いきや
snap-_ も作られるまで待つ必要がありました。

  • CLI 上でスナップショット完了確認した方が確実です。
1
2
3
4
5
6
$ curl -s -GET https://<Elasticsearch 2.3 Endpoint Domain>/_snapshot/index-backups/20170926

...
"state": "SUCCESS",
...
...

Elasticsearch 5.5 Service 新規ドメイン作成

Elasticsearch 2.3 の設定に倣って作成します。

リポジトリ作成

  • register_es55_repository.py

register_es_repository.py の host 部分を新規ドメインに修正します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from boto.connection import AWSAuthConnection

class ESConnection(AWSAuthConnection):

def __init__(self, region, **kwargs):
super(ESConnection, self).__init__(**kwargs)
self._set_auth_region_name(region)
self._set_auth_service_name("es")

def _required_auth_capability(self):
return ['hmac-v4']

if __name__ == "__main__":

client = ESConnection(
region='ap-northeast-1',
host='<Elasticsearch 5.5 Endpoint Domain>',
aws_access_key_id='<ACCESS KEY ID>',
aws_secret_access_key='<SECRET ACCESS KEY>', is_secure=False)

print 'Registering Snapshot Repository'
resp = client.make_request(
method='POST',
path='/_snapshot/index-backups',
data='{"type": "s3","settings": { "bucket": "<bucket name>","region": "ap-northeast-1","role_arn": "arn:aws:iam::<Account ID>:role/es-index-backups"}}'
)
body = resp.read()
print body
1
2
3
4
5
6
$ chmod +x register_es55_repository.py

$ python register_es55_repository.py

// 成功
{"acknowledged":true}

スナップショットからリストア

20170926 のスナップショットをリストアします。

1
2
3
4
$ curl -XPOST "https://<Elasticsearch 5.5 Endpoint Domain>/_snapshot/index-backups/20170926/_restore"

// 成功
{"accepted": true}

リストア確認

1
$ curl -XGET "https://<Elasticsearch 5.5 Endpoint Domain>/_cat/indices"

スナップショットに失敗するケース

  • .kibana index が既に存在しており、リストアできない。
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"error":{
"root_cause":[
{
"type":"snapshot_restore_exception",
"reason":"[index-backups:20170926/Hc4rLIoARCWqpyJXeP7edw] cannot restore index [.kibana] because it's open"
}
],
"type":"snapshot_restore_exception",
"reason":"[index-backups:20170926/Hc4rLIoARCWqpyJXeP7edw] cannot restore index [.kibana] because it's open"
},
"status":500
}

対応策

1
2
3
curl -XPOST https://<Elasticsearch 5.5 Endpoint Domain>/_snapshot/index-backups/20170926/_restore -d '{
"indices": "nginx-*"
}' | jq .

indices を用い、スナップショット内のインデックスの中からマッチする正規表現のみをリストアできます。

自身ではこの様な解決法を実施し回避できました。
その他良い方法があれば御指南いただけますと幸いです。

ちなみに

Terraform で ES 2.2 → 5.5 バージョンアップを実施した所
1 時間以上経過してようやくアップデートが完了しました。

1
2
3
4
aws_elasticsearch_domain.elasticsearch: Still destroying... (ID: arn:aws:es:ap-northeast-1:xxxxxxxxxxxx:domain/***, 59m11s elapsed)
aws_elasticsearch_domain.elasticsearch: Still destroying... (ID: arn:aws:es:ap-northeast-1:xxxxxxxxxxxx:domain/***, 59m21s elapsed)
aws_elasticsearch_domain.elasticsearch: Still destroying... (ID: arn:aws:es:ap-northeast-1:xxxxxxxxxxxx:domain/***, 59m31s elapsed)
aws_elasticsearch_domain.elasticsearch: Destruction complete after 59m41s

これは辛い (>_<)

Terraform で管理している場合、
スナップショットを取得したら aws console 上でドメイン削除した方が早い。

ブルーグリーン的に ES 5.5 作成して ES 2.2 から乗り換えて
しばらく運用して問題なければ ES 2.2 を削除する方法が一番確実だなと思いました。

docker-comopse で Rails 5 (Puma) + Nginx + Mysql 構築 on Vagrant(Ubuntu)

docker-comopse で Rails 5 (Puma) + Nginx + Mysql 構築 on Vagrant(Ubuntu)

自身の Rails 開発環境の雛形として利用している docker-compose です。

以下設定手順です。

Vagrant 起動

1
2
3
4
macOS%$ git clone https://github.com/kenzo0107/vagrant-docker
macOS%$ cd ./vagrant-docker/
macOS%$ vagrant up
macOS%$ vagrant ssh

Rails プロジェクト作成

1
2
3
// on vagrant
vagrant%$ cd /vagrant/rails-puma-nginx-mysql
vagrant%$ docker-compose run --rm web rails new . --force --database=mysql --skip-bundle

puma 設定ファイルセット

1
vagrant%$ cp puma.rb ./rails/config/
  • ./rails/config/puma.rb
1
2
3
4
5
6
7
8
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

データベース設定ファイルセット

1
vagrant%$ cp database.yml ./rails/config/
  • ./rails/config/database.yml
1
2
3
4
5
6
7
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: <%= ENV['MYSQL_ROOT_PASSWORD'] %> # <--- MYSQL_ROOT_PASSWORD
host: db # <--- service name

データベース作成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vagrant%$ docker-compose run --rm web rails db:create
Created database 'app_development'
Created database 'app_test'

vagrant%$ docker-compose exec db mysql -u root -p -e'show databases;'
Enter password: (password)
+--------------------+
| Database |
+--------------------+
| information_schema |
| app_development | <--- add !
| app_test | <--- add !
| mysql |
| performance_schema |
| sys |
+--------------------+

以上で必要最低限の Rails プロジェクトの準備ができました!

Rails, Nginx, MySQL 全コンテナ起動

1
vagrant%$ docker-compose up -d

ブラウザより http://192.168.35.101 へアクセスし
Rails トップページが表示されることが確認できます。

総評

docker でコンテナ化しているので Nginx, MySQL 等、
バージョンアップしたい時でもコンテナを置き換えるだけで簡単に使用感を確認できたり
機能を確認できたりと便利です。

これに Elasticsearch + Kibana でログを可視化したり
Mailcatcher でメール送信を確認できるようにしたりと
開発するには十分な状況が用意できます。

是非開発の一助になれば幸いです。