Deploy ツール Stretcher で Rollback する
Stretcher を使うと、Consul と連携して、所謂 Pull 型の Deploy ができるようになります。
Consul と連携させる場合は、
$ consul event -name deploy s3://xxx-stretcher-files/deploy-20151112-193139.yml
のように、consul event
で Manifest の path を指定してイベントを送ると、
YAML に書いてある path(s3, http, file) から tar.gz を取得して展開してくれます。
Stretcher は Rollback するのも簡単で、
Rollback したいバージョンの Manifest の path を指定して consul event
を実行するだけです。
S3 に置いている場合、都度 aws s3 ls
などして Manifest の path を調べて consul event
を実行するのは面倒です。
そこで、以下の様なスクリプトを用意しておくと Rollback が楽になります。
aws s3 ls
の結果から最新の Manifest を除いて、
bash の select で番号で選択できるようにしています。
実行すると以下のようになります。
$ ./rollback_stretcher.sh 1) deploy-20151112-191452.yml 14) deploy-20151029-215027.yml 2) deploy-20151112-191236.yml 15) deploy-20151028-182546.yml 3) deploy-20151112-190934.yml 16) deploy-20151028-175655.yml 4) deploy-20151112-190820.yml 17) deploy-20151028-175201.yml 5) deploy-20151112-190421.yml 18) deploy-20151028-174909.yml 6) deploy-20151112-190254.yml 19) deploy-20151028-174155.yml 7) deploy-20151112-190127.yml 20) deploy-20151028-171838.yml 8) deploy-20151112-190018.yml 21) deploy-20151022-191616.yml 9) deploy-20151112-185832.yml 22) deploy-20151022-143736.yml 10) deploy-20151112-185441.yml 23) deploy-20151022-143011.yml 11) deploy-20151112-185341.yml 24) deploy-20151022-142645.yml 12) deploy-20151112-185242.yml 25) deploy-20151022-141728.yml 13) deploy-20151110-162450.yml 26) deploy-20151022-140614.yml Select manifest (quit q or Ctrl-C) : 1 Event ID: 7a5e80c5-27aa-973e-3328-b7093a91439f
Rollback するスクリプトを用意しておくと、
障害発生時にも落ち着いて作業ができるので良いですね。
※前提条件として、Manifest のファイル名に timestamp を含めて sort できるようにしておく必要があります。
AWS Lambda のスケジュールイベントで定期的に RDS の Snapshot を作成する
AWS Lambda でスケジュールイベントの設定ができるようになりました。
- Amazon Web Services ブログ: 【AWS発表】AWS Lambdaのアップデート – Python, VPC, 実行時間の延長, スケジュールなど
- http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/getting-started-scheduled-events.html
これにより cron で実行していたスクリプトを Lambda に移行できます。
cron の冗長化は面倒ですし、cron のためだけに EC2 を起動しなくて良いので便利ですね。
そこで(?)、Lambda から AWS の API を叩いて定期的に RDS の Snapshot を作成してみます。
Create Lambda Function
- 「Get started now」か 「Create Lambda Function」を押下
Select blueprint
Lambda のテンプレートを選択します。
- Python 2.7 を選択
- 「lambda-canary」か「hello-world-python」を選択
- (一通り設定した後にスケジュールイベントの設定をしたかったので「hello-world-python」を選択)
Configure function
Lambda の設定をします。
- Name, Description を入力
Lambda function code
Lambda のコードを作成します。
- 以下のコードを入力する
lambda_handler
の以下の変数を適宜修正delete_days
: 指定日経過したら削除する- 削除する必要がなければ
delete_snapshots
ごと削除
- 削除する必要がなければ
snapshot_prefix
: Snapshot 名の prefixinstances
: RDS の Instance 名を配列で指定
- Snapshot 名の時刻は、JST に変換する必要がなければ
tz=JST()
を削除
追記 2017-01-11 14:09
Aurora の場合は以下のコードで snapshot を取ることができます。
Lambda function handler and role
Lambda function handler はソースコード中の関数を指定します。
handler を変更すれば lambda_handler
以外の関数を実行することも可能です。
また、Lambda 実行に必要な IAM role も設定します。
こちらもテンプレートがありますが、
RDS の Snapshot を操作するものは用意されていないので、
IAM policy を修正します。
- 「Basic execution role」を押下
- IAM role 設定画面に遷移する
- ポップアップブロックしていると設定できないようです
- IAM role 設定画面に遷移する
- 「編集」を押下
- (ドキュメントを読んで)「OK」を押下
- 以下の json を入力する
Advanced settings
メモリやタイムアウトの時間を設定します。
数台分の Snapshot を作るだけの想定ですので、
タイムアウトは 10 秒で設定します。
Review
- 確認して「Create function」を押下
スケジュールイベントの設定
スケジュールイベントの設定を行います。
cron の書式が若干違うので、
詳しくは以下のドキュメントを参照してください。
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/getting-started-scheduled-events.html
(執筆時は、)設定する時間は UTC ですので、
日本時間で設定しないように注意してください。
- 「Event sources」タブの「Add event source」を押下
- 「Scheduled Event」を押下
- Name, Description を入力
- Schedule expression を入力
- (cron(0 4 * ? * *))
- (例は日本時間で毎13:00 に実行)
- (cron(0 4 * ? * *))
これで、スケジュールイベントの設定完了です。
あとは、時間になれば対象 Instance の Snapshot が作成されます。
CloudWatch
CloudWatch に実行ログが出力されます。
- ログを選択
print などで出力した結果やエラーログが出力されます。
まとめ
AWS Lambda で RDS の Snapshot を作成する方法を紹介しました。
EC2を使わないようにできるので、
可能な範囲で Lambda で実行させるようにすると運用が楽になりそうです。
また、紹介したコードでは省略しましたが、
実際に使う場合は Snapshot 作成に成功・失敗したかを、
チャットなどに通知するようにしたほうが良いですね。
Nginx で query string を見て動的にファイルを配信する
server { listen 80; server_name localhost; rewrite_log on; error_log /var/log/nginx/rewrite.log notice; location ~ ^/weather+\.json { rewrite ^ /weather/$arg_date.json; } location ~ ^/weather/.*\.json { default_type application/json; root /var/www/html; try_files $uri =404; error_page 404 = @info; } location @info { more_set_headers -s 404 "Cache-Control: no-cache, no-store"; return 404 " "; } }
という設定ファイルで、
$ cat /var/www/html/weather/20141001.json {"weather":"sunny"}
を置いている状態で、http request を送ると、
$ curl -s "http://localhost/weather.json?date=20141001" {"weather":"sunny"} $ curl -I "http://localhost/weather.json?date=20141001" HTTP/1.1 200 OK Server: ngx_openresty/1.4.3.6 Date: Fri, 10 Oct 2014 07:25:05 GMT Content-Type: application/json Content-Length: 20 Last-Modified: Fri, 10 Oct 2014 07:02:02 GMT Connection: keep-alive ETag: "5437846a-14" Accept-Ranges: bytes
のようになります。
以下は、rewrite log です。
2014/10/10 16:08:03 [notice] 21659#0: *1 "^" matches "/weather.json", client: 127.0.0.1, server: localhost, request: "GET /weather.json?date=20141001 HTTP/1.1", host: "localhost" 2014/10/10 16:08:03 [notice] 21659#0: *1 rewritten data: "/weather/20141001.json", args: "date=20141001", client: 127.0.0.1, server: localhost, request: "GET /weather.json?date=20141001 HTTP/1.1", host: "localhost"
ファイルが存在しない場合は以下のように 404 を返すことが確認できます。
$ curl -I "htp://localhost/weather.json?date=20141002" HTTP/1.1 404 Not Found Server: ngx_openresty/1.4.3.6 Date: Fri, 10 Oct 2014 07:25:32 GMT Content-Type: application/octet-stream Content-Length: 1 Connection: keep-alive Cache-Control: no-cache, no-store
application で認証した後に nginx で static file を配信する
application で認証している場合のみファイルをダウンロードさせたい場合があると思いますが、
そんなときの設定例です。
以下に簡単に試す方法を記載しています。
tkuchiki/nginx-direct-data-transfer-sample · GitHub
例では Basic 認証ですが、実際はちゃんとした認証がいいでしょう。
application 側で http headr に、
X-Accel-Redirect としてファイルのパスを追加すると、
nginx から static file を配信することができます。
X-Accel-Redirect を使用しなくても、
nginx の proxy cache がききますが、
大容量ファイルの配信を行う場合、
proxy cache の作成に Disk I/O、CPU リソースを大量に使ってしまう可能性があります。
sinatra の場合、以下のようになります。
content_type "image/png" headers "X-Accel-Redirect" => "/img/black.png" status 200
これで負荷を低減することができます。
Bats を使って Travis CI で bash(シェルスクリプト) のCI を回す
Bats で bash のテストを書く - tkuchikiの日記
の続きです。
bash のテストができるようになると CI を回したくなりますよね。
ということで、Travis CI でテストを回すまでの方法を説明します。
https://github.com/tkuchiki/bats-travis-ci
にすべてのコードを置いています。
.travis.yml
before_install で bats をインストールし、script で実行します。
language: bash before_install: - sudo add-apt-repository ppa:duggan/bats --yes - sudo apt-get update -qq - sudo apt-get install -qq bats script: - bats /path/to/test.bats
これで Travis CI で bats が使えるようになります。
あとは、テストを書いて push すれば bats を実行できます。
以下は、実行結果の一部です。
$ git clone --depth=50 --branch=master https://github.com/tkuchiki/bats-travis-ci.git tkuchiki/bats-travis-ci $ cd tkuchiki/bats-travis-ci $ git checkout -qf 56e66e566c89c7e891862bb2f3128cb8c9014dcf $ sudo add-apt-repository ppa:duggan/bats --yes $ sudo apt-get update -qq $ sudo apt-get install -qq bats $ sudo apt-get install -qq bc $ bats ./test.bats 1..7 ok 1 addition using bc ok 2 status code ok 3 output ok 4 lines ok 5 run command ok 6 load ok 7 # skip (skipped) skip The command "bats ./test.bats" exited with 0. Done. Your build exited with 0.
実行結果の詳細は以下のページにアクセスすると確認できます。
Bats で bash(シェルスクリプト) のテストを書く
※記事を書いて公開するまで1年くらい経っているので情報が古い可能性があります。
Bats(Bash Automated Testing System)は、bash のテスティングフレームワークです。sstephenson/bats · GitHub
Test Kitchen(Bussser) の Bats plugin で使うことができるのでご存知の方も多いと思います。
簡単に使い方を説明すると、
@test "テストの説明" { command ... }
という記法でテストを記述し、コマンドの返り値が 0 ならテストをパスします。
以下のようにスクリプトを実行してテストをすることができます。
$ bats test.bats
インストール
インストール方法は、以下のとおりです。
sudo add-apt-repository ppa:duggan/bats --yes sudo apt-get update -qq sudo apt-get install -qq bats
brew install bats
- その他
git clone https://github.com/sstephenson/bats.git cd bats ./install.sh /usr/local
テストの書き方
テストコードと実行結果は、以下のようになります。
- テストコード
- 実行結果
テストコードを元に上から順に説明していきます。
setup, teardown
- setup
- 各テストの実行前に実行される処理を関数として定義
- teardown
- 各テストの実行後に実行される処理を関数として定義
となります。
コード内で、BATS_ から始まる変数が使われていますが、
これらは Bats の特殊変数です。
詳細は後述します。
run, $status, $output, $lines
run FILEPATH で外部スクリプトを実行すると、
$status, $output, $lines に値がセットされます。
各変数は以下のとおりです。
- $status
- $output
- 標準出力
- $lines
- 標準出力を行ごとに配列に格納
以下のスクリプトだと、
status=1 output=foobar lines[0]=foobar
になります。
lines については配列なので、出力が 2 行の場合は lines[1] に 2 行目の出力結果が格納されます。
skip
skip は、文字通りテストをスキップします。
load
load は、外部スクリプトを読み込みます。
load の引数となるファイルパスを書く場合は 2 つの注意点があります。
特殊変数
以下の実行結果から、それぞれの変数の値を見ていくと、
- BATS_TEST_NAME
- テストの説明部分を test_ prefix と、半角スペースを _ に変換したもの
- BATS_TEST_FILENAME
- 実行している Bats ファイルのフルパス
- BATS_TEST_DIRNAME
- Bats ファイルを配置しているディレクトリ
- BATS_TEST_NAMES
- BATS_TEST_NAME を 0 から始まる配列に格納
- BATS_TEST_DESCRIPTION
- テストの説明部分
- BATS_TEST_NUMBER
- テストの番号
となっていることがわかります。
また、実行結果を見ると、
setup -> テスト -> teardown と実行されていることが確認できます。
テスト外のコードについて
@test, setup, teardown 関数の外で標準出力をするコードを書いた場合、
>&2 で標準エラー出力にリダイレクトするようにしないと、
TAP の標準出力を汚染してテストが失敗する可能性があるようです。
linux で TZ の offset を出力する
date +%z
を使います。
$ TZ=UTC date +%z +0000 $ TZ=Asia/Tokyo date +%z +0900 $ TZ=EST date +%z -0500
全ての TZ と offset を出力する場合は、以下のシェルスクリプトを実行します。
実行結果
$ ./tz.sh EST5EDT -0500 MST7MDT -0700 Portugal +0000 Jamaica -0500 Poland +0100 Zulu +0000 GMT-0 +0000 ROK +0900 Chile/EasterIsland -0500 Chile/Continental -0300 ...