Terraformでstate lockを触ってみた。

はじめに

初めてTerraformを触る折に
「v0.9以上だとstate lockっていう機能があるから使ったほうが良いよ」
というアドバイスをもらいました。
(そもそも、stateってなんですか?から始まるわけですが・・・)

初めてTerraformを触った時に、公式ドキュメントだけでは???な状態だったので
痕跡として残しておきます。

結果として、まだ使いこなせてません。というか僕の使い方は果たしてあっているのだろうか
なので情報としては不足しているかもしれませんが、とりあえず備忘録的に残します。
またわかったらUpdateします。

そもそもState Lockとは

完全に理解しないで書いてますが
Terraformは「自分が知っているリソース(EC2やS3)しか関与しないよ」というポリシーで
どのリソースを自分が扱ったか、というのをtfstateというJSONファイルで管理しているようです。

このtfstateファイルは、一人でTerraformを動かす場合は問題無いのですが
複数人でTerraformをいじる必要が出てきた時に問題で、それは「backend」モジュールを使うことで回避してきたようですが
同じタイミングでterraformを実施した場合、その部分までは制御しきれてなかったようです。

で、v0.9以上?から、「Plan/Applyをしているタイミングではロックする」機能が実装されたようで。
せっかくなので導入してみました。

公式サイト:https://www.terraform.io/docs/state/locking.html

準備

手動で準備が必要なものは
・terraformを実行するユーザのCredential情報
→ めんどくさかったので test-terraformとかいうユーザを別途作成しました。
・S3 Bucketの作成
→ terraform-s3-state とかいう名前で作りましょう
・DynamoDBの作成
→ 名前はなんでも良いです。terraform-state-lock とかで作りましょう。
  プライマリキーを「LockID」としてください。それ以外では動きません。
作り終えたら、読み込み・書き込み容量ユニットは最低の1にしておいたほうが良いと思います。

※ S3とDynamoDBをterraformで管理はしないほうが良さげです。
どのみち、初回実行時に「そんなリソース無いんだけど!」ってTerraformが怒ります。

動くまで

今回はState Lockの話がメインなので作るリソースはなんでも良いです。
リージョンが関係無いRoute53を題材にします。

route53.tf
# 適当に1ゾーン作ります。

resource "aws_route53_zone" "test_zone" {
    name    =   "test.lockstate.com"
}
settings.tf
# ネーミングよくないですが、providerとbackendの設定します

provider "aws" {
    access_key = "ACCESS_KEY"
    private_key = "PRIVATE_KEY"
}

terraform {
    backend "s3" {
        bucket     = "terraform-s3-state"
        key        = "terraform.tfstate"
        region     = "s3とdynamoがいるregion"
        lock_table = "terraform-state-lock"
    }
}

これで、「該当のディレクトリに移動して」$ terraform plan してください。(怒られます。)

Backend reinitialization required. Please run "terraform init".
Reason: Initial configuration of the requested backend "s3"

The "backend" is the interface that Terraform uses to store state,
perform operations, etc. If this message is showing up, it means that the
Terraform configuration you're using is using a custom configuration for
the Terraform backend.

とりあえず terraform init しろよと言われるので言われるがままに。

$ terraform init

ただし、以下の通り、認証情報がねーんだけど!って怒られる。なんやねん。

Initializing the backend...

Error configuring the backend "s3": No valid credential sources found for AWS Provider.
  Please see https://terraform.io/docs/providers/aws/index.html for more information on
  providing credentials for the AWS Provider

Please update the configuration in your Terraform files to fix this error
then run this command again.

ここらへんちゃんとよくわかってないですが、この問題の対処として2つありました。
A. settings.tfのbackendにaccess_key/secret_keyの2つを渡してCredential情報を平文で書く。
-> 変数でいいじゃん!と思ったんですが、変数で渡すと、Terraformがよしなにやる「前に」
  Backendが立ち上がるために違う方法で渡してくれ、と怒られました。
以下のようなメッセージ

% terraform init 
Initializing the backend...
Error loading backend config: 1 error(s) occurred:

* terraform.backend: configuration cannot contain interpolations

The backend configuration is loaded by Terraform extremely early, before
the core of Terraform can be initialized. This is necessary because the backend
dictates the behavior of that core. The core is what handles interpolation
processing. Because of this, interpolations cannot be used in backend
configuration.

If you'd like to parameterize backend configuration, we recommend using
partial configuration with the "-backend-config" flag to "terraform init".

B. 環境変数にaccess_key/secret_keyの2つを食わせる
  → 今はこちらを採用中。

init or plan実行時に、状況に応じて「ローカルのStateをS3にコピーするか / S3からStateファイルをローカルにコピーするか」聞かれるかもしれません。
初回(もしくはテスト)であればYes、すでに何回か実行しているような状況ではnoでいいんじゃないでしょうか。

これで、terraform plan (or apply)を実行すると
DynamoDBのLockDBに対して誰が使用しているか、のロック情報が書き込まれ
終わったタイミングでリリースされます。

ターミナルやシェルを複数立ち上げ、同じぐらいのタイミングで
terraform plan( or apply )を実行すると、ロックが成功できた奴以外はエラーで落とされます。
ただし、ロックがリリースされる前の実行中などにCtrl-Cなどで強制終了させるとロックが残り続けるので注意。
$ terraform force-unlock ID情報
で強制ロック解除できます。

今僕が解決できてない問題点

公式ドキュメントを見る限り、「terraform remote コマンドは terraform initコマンドに置き換えたよ!」と言っています。
また、backendを使用する、backendを書き換えた、などのタイミングに起因して terraform init を求められます。

が、terraform initは、「実行されたディレクトリに対して .terraform/terraform.tfstate」を吐き出します。
そしてそれが無いと怒ります。
まだ試せてはいませんが、以下のようなtfの配置をしていたりした場合
root配下でinitを実行しても、tfファイルがありませんと怒られる状況で
tfファイルがあるディレクトリまで移動しないとinitができない状態です。

$ terraform init ./route53/tf
とするとroot直下にtfファイルがコピーされる状況です。
なので、リソースごとに区切る、とか環境ごとに区切る、とかはうまくできていません。
別の見方をすれば、環境ごとに一つのディレクトリでリソース類は全部その中で管理しろよ!
というHashiCorpからのお達しなのか・・・?
これは、新しく実装されたEnvironmentを試せ、ということなんでしょうか。。。

と思ったらBackendConfigなるものがあるらしいのでそれを試してみよう。

root
├── settings
│   └── tf
│   └── settings.tf
├── route53
│   └── tf
│   └── route53.tf
└── ec2
└── tf
└── ec2.tf

Terraformとの付き合い方がよくわからない

続きを読む

AWS-SDK & nodejs メモ

はじめに

自分用のメモ代わりです。すみません。
nodejsでaws-sdkの各種機能を適当にメモっていきます。

共通処理

aws-sdkを読み込み、リージョンとプロファイルを設定

const aws = require('aws-sdk');

var credentials = new aws.SharedIniFileCredentials({
    profile: "<プロフィル名>"
});
aws.config.update({
    region: "<リージョン名>",
    credentials: credentials
});

S3

ローカルファイルをS3にアップロード

const s3 = new aws.S3();

const params = {
    Bucket: "<バケット名>",
    Key: "<キー名>",
    Body: fs.createReadStream("<ローカルファイルのパス>"),
};

s3.upload(params).promise()
.then(function(data) {
    console.log(`アップロード成功: s3://${data.Bucket}/${data.Key}`);
})
.catch(function(err){
    console.dir(err);
});

続きを読む

CRMのF-RevoをAWS Lightsailで構築したときにハマったこと(Apache,let’s encrypt)

はじめに

CRM(F-Revo)を導入したときにハマってしまったことを書いていきます。
F-Revoの環境についてはこちらです。
Lightsailどうこうではない部分もあります。

CRMをインストールしようとしたらページが真っ白になった件

F-Revoのインストール設定を入力したあと、こんなページまで行きます。

20150917093149.png

しかしこのあと次のページに遷移すると真っ白なページになって、戻るも進むなくなり、二進も三進もいかなくなりました。
原因はCRMがPHP7に対応していないからでした。
現場の開発はPHP7でやっていたため、できればPHP7を使用してF-Revoをインストールしたかったのですが、プライオリティは低いということでPHP5.6を再インストールしました。

参考
オープンソースなので、自己責任でソースを書き換えればPHP7でも動作するという書き込みもありましたのでご参考までに。
Vtiger with PHP7
https://discussions.vtiger.com/index.php?p=/discussion/183662/vtiger-with-php7/p1
PHP 7 compatibility
http://code.vtiger.com/vtiger/vtigercrm/issues/197

Let’s encryptがうまく実行されない件

オレオレ認証局ではなく、Let’s encryptを利用して証明書を作成しようと試みましたが失敗した話です。
結論から言うと、mod24_sslがインストールされていないからに落ち着きます。
「Let’s encryptはとにかく簡単。パッケージをインストールしてコマンドを叩けば自動で証明書の作成がされるよ」とみたいな話ではあるものの、凡ミスのせいで鮮やかにコケたというお話です。

ではコケるまでの手順を見ていきましょう。
1. インストールコマンドを叩く

$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
$ ./certbot-auto --agree-tos --webroot-path <RootDirectory> -d <DomainName> -m <E-Mail>
※--webroot-pathはhttpdを実行したまま証明書を発行できるオプションです。

はい! ここでコケました!
そして下記のエラーが出力されました。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for <DomainName>
No vhost exists with servername or alias of: <DomainName> (or it's in a file with multiple vhosts, which Certbot can't parse yet). No vhost was selected. Please specify ServerName or ServerAlias in the Apache config, or split vhosts into separate files.
Falling back to default vhost *:443...
Waiting for verification...
Cleaning up challenges
Failed authorization procedure. <DomainName> (tls-sni-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Failed to connect to <IPaddress>:443 for TLS-SNI-01 challenge

IMPORTANT NOTES:

翻訳すると、「DomainNameを持つ仮想ホストは存在しません」みたいなことが書いてあり、「Apacheの設定でServerNameまたはServerAliasを指定するか、仮想ホストを別々のファイルに分割してください」ともあります。
試しにApache設定にVirtualHostを追記しましたがエラーに変化はないです。
/etc/resolv.confの設定ミスというわけでもなかったです。
443が云々というエラーメッセージもあるのでssl.confを見に行ってみると、ssl.confがないことに気づきました。
というわけで、mod24_sslをyum installし、Let’s encryptにて証明書発行が実行できました。

常時https接続に設定変更したらCRMのWEBページにたどり着けなくなった件

少し話が長くなりますので、結論からいうとconfig.inc.phpに記載されたsite_URLがhttp://~になっていたことと、/etc/httpd/conf.d/le-redirect-DomainName.com.confが原因でした。

事の始まり

Let’s encryptのコマンドを無事実行し、いよいよ終了が近づくと「httpもhttpsもイケるようにする?それとも常時https接続にする?」みたいな二択が表示されます。

・・・で、もともと常時https接続にする予定だったこともあり、常時の方を選択しました。
「よーしこれで80ポートは閉めていいな?」ということで、LightsailのGUIコンソール側でセキュリティルールを変更しました。
そしてこのあとユーザさんから「ログインできない!」と続々お問い合わせが来てしまったのです。
見てみると、エラー内容はタイムアウトでした。

となると、まず疑うべきは

  • 「常時https接続する」を選択したときに、自動でどこかにその設定を読み込むコンフィグが作成された可能性

ということです。そのコンフィグが作成されたこと一点が原因でログイン画面に行けないのであれば、コンフィグ名を変更してやることで今までのようにhttp接続でログインページにたどり着けるようになるはずです。

はい、ありました。le-redirectと名前がついてるので決定的です。
中身を見てみると、httpsにRewriteしているようです。とりあえずこのファイル名を変更し、httpdを再起動します。

/etc/httpd/conf.d/le-redirect-DomainName.com.conf
<VirtualHost _default_:80>
ServerName <DomainName>:443
ServerAlias <DomainName>
ServerSignature Off

RewriteEngine On
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]

ErrorLog /var/log/httpd/redirect.error.log
LogLevel warn
</VirtualHost>

ただ、これだけだと80ポートを閉じているのでhttp通信もできない状態。再度Lightsailの80ポートを開きます。
これで一旦、http通信ではあるけれどログインページにはこれまで通りいけるはず!

  • 強制リダイレクトしてるファイルを読み込ませない
  • http通信を許可する

応急処置が終わったら次はhttpsで接続できるようにしていきます。

そもそもなんでHTTPS接続できなかったか

いわゆるリダイレクトループと言えばいいんでしょうか。

リクエスト→http→強制でhttpsに変換→config.inc.phpに記載されたsite_URLがhttp://~のためhttpになる→強制でhttpsに→→→というイメージです。

config.inc.phpを一部抜粋するとこんな感じで記述されています。

/var/www/html/config.inc.php
$site_URL = 'http://<IPaddress>';

つまりこれを、httpsと記述してやればいいわけです。もっというと、ドメイン名でURLを表記させたいので以下のように変更しました。

/var/www/html/config.inc.php
#$site_URL = 'http://<IPaddress>/~';
$site_URL = 'https://<DomainName>/~';

そして改めて、必要のない80ポートを閉じます。
これで私の環境ではhttps接続が可能になりました!

おわりに

長くなりましたが、大抵ネットワーク関連で死ぬときは設定漏れか把握してないファイルがいるみたいなときなんですかね。ともあれユーザが安全、安定して使用してもらえるよう運用していきたいです。

続きを読む

Shoryuken を導入しようとして諦めた話

Rails で非同期処理といえば、 Sidekiq, Resque, DelayedJob あたりが有名かと思います。
DelayedJob は RDS をジョブキューとして利用できる1ため、インフラの準備が不要で比較的ライトに導入できますが、数万件のジョブを登録しようと思うと RDS にかなりの負荷がかかりますし、Insert もそれほど早くないため、ジョブキューとしてはあまり適しているとは言えません。
Redis などのインメモリーデータベースを用いれば、RDS に負荷をかけず、Insert の高速化も見込めますが、万が一 Redis がクラッシュした際には登録されていたキューは全て無くなりますし、そういった障害に対応するためには幾らかの運用コストが発生します。

ところで、 AWS には SQS というキューイングサービスが提供されています。
安価で堅牢な造りになっており、運用コストも低そうだったのでジョブキューとしては最適なように感じましたが、SQS をジョブキューとして利用している非同期処理と言えば Shoryuken くらい2です。

今回、DelayedJob からの移行先として、Shoryuken が使えないか検討したのですが、いくつかの理由により結局諦めた、という話を書いてみます。

環境

  • Ruby 2.4.1
  • Ruby on Rails 5.0.2
  • Shoryuken 3.0.6

Shoryuken とは

SQS を使った Job queue worker。ActiveJob にも対応。
Sidekiq を意識して作られており、スレッドベース。

しょーりゅーけん!
GitHub Shoryuken

なぜ Shoryuken が良いと思ったのか?

  • SQS は安い

    • 100万 requests / month まで無料
    • 1億 requests / month で $39.60 (およそ 4,300円)
  • SQS だとインスタンスタイプの変更などの運用が発生しない
  • SQS 側でリトライをサポートしている
    • 一定時間内に処理が正常に完了しなかった場合、再度 Queue に入る
    • 一定回数失敗した場合、Dead letter queue に移動される
    • Redis の場合、 Sidekiq の worker プロセスが吹っ飛んだ場合にジョブが失われる恐れがある
      • Redis 本体が死んでもデータは吹っ飛ぶ
      • Sidekiq Pro ($950 / year) ならリカバリ可能らしい
  • 昇龍拳だから

SQS の仕組み

キューの種類

標準キュー

Amazon SQS のデフォルトのキューの種類は標準です。標準キューでは、1 秒あたりのトランザクション数はほぼ無制限です。標準キューではメッセージが少なくとも 1 回配信されることが保証されます。ただし (高いスループットを可能にする、徹底して分散化されたアーキテクチャであるために) ときとして、 メッセージのコピーが乱れた順序で複数配信されることがあります 。標準キューでは、メッセージが通常は送信されたのと同じ順序で配信されるようにする、ベストエフォート型の順序付けが行われます。
SQS よくある質問 – 標準キューと FIFO キューの違いは何ですか?

標準キュー

乱れた順序で複数配信されることがあります。

つまり、稀に複数の worker が同じ Job を実行してまう可能性があるということ。
また、Queue と言ってるけど、順番は保証しない(ベストエフォートなので一応は考慮してる)ということ。

複数回同じジョブが実行されてしまうのは困るけど、ジョブの処理の中で RDS などに処理の進行状態を保存して、複数回実行されないような実装をすれば回避可能。というか、 そういう実装が必須 だと思います。

一番の強みはトランザクション数がほぼ無制限なので、複数の worker から同時にアクセスしまくっても全然大丈夫です。

FIFO キュー

FIFO キューは、標準キューを補完するものです。このキュータイプの最も重要な特徴は、FIFO (先入れ先出し方式) 配信と 1 回限りの処理です。メッセージが送受信される順序は厳密に保たれます。メッセージは 1 回配信されると、消費者がそれを処理して削除するまでは使用できるままとなり、キューで重複が起きることはありません。FIFO キューはメッセージグループもサポートしているため、1 つのキュー内に複数の順序付けられたメッセージグループが可能です。FIFO キューは、1 秒あたり 300 トランザクション (TPS) に制限されていますが、標準キューの機能をすべて備えます。
SQS よくある質問 – 標準キューと FIFO キューの違いは何ですか?

FIFOキュー

キューの順序と1回限りであることを保証する代わりにトランザクション数が 300 TPS に制限されたバージョン。 EC サイトの決済処理とかで使う、みたいな例が書かれていました。

1 秒あたり 300 トランザクション = 1 分あたり 18000 トランザクション。
現在開発中のシステムでは要件として5分で 10000 件の処理を目標にしているジョブだったので、これでは物足りない感じ。

可視性タイムアウト (Visibility Timeout)

コンシューマーがキューからメッセージを受信して処理しても、そのメッセージはキューに残ったままです。Amazon SQS では、メッセージは自動的に削除されません。これは分散システムであるため、コンポーネントがそのメッセージを実際に受信するという保証がないからです (接続が切断されたり、コンポーネントでメッセージの受信に失敗する可能性があります)。そのため、コンシューマーはメッセージを受信して処理した後、キューからメッセージを削除する必要があります。
SQS 開発者ガイド – 可視性タイムアウト

可視性タイムアウト

引用の通りですが、SQS には可視性タイムアウトという設定値があって、Shoryuken の worker が SQS からメッセージを受け取ると、SQS 側はメッセージを一定時間、論理削除します。
worker はジョブが完了すると SQS 側にメッセージの削除要求を投げて、論理削除されていたメッセージを物理削除します。
もし、 worker 側で処理に失敗、または可視性タイムアウトの設定時間を経過しても処理が終わらなかった場合、SQS 側で論理削除を解除して、次のリクエストでメッセージを再配信します。
この仕組みがあるため、 Shoryuken 側でリトライを設定していなくとも、失敗した処理は再度実行されるようになります。
万が一 worker のプロセスが死んでしまっても、 SQS 上でメッセージはちゃんと生きている訳です。

worker の処理時間が長すぎて可視性タイムアウトの時間を過ぎてしまうのは問題なので、 2 分かかる処理なら可視性タイムアウトを4分に設定する、などがセオリーのようです。

なお、Shoryuken には auto_visibility_timeout というオプションがあって、これを true にしておくと、可視性タイムアウトに達する 5 秒前に可視性タイムアウトの延長をしてくれるようです。

遅延キュー

遅延キューを使用すると、キューにある新しいメッセージの配信を指定の秒数延期できます。遅延キューを作成した場合、そのキューに送信したすべてのメッセージが遅延期間の間コンシューマーに表示されなくなります。DelaySeconds 属性を 0 ~ 900 (15 分) の任意の値に設定することで、CreateQueue アクションを使用して遅延キューを作成できます。
(中略)
遅延キューは、メッセージを一定の時間コンシューマーが使用できなくするため、可視性タイムアウトと似ています。遅延キューと可視性タイムアウトの違いは、遅延キューの場合、メッセージが最初にキューに追加されたときに非表示になるのに対して、可視性タイムアウトの場合、メッセージがキューから取得された後のみ非表示になるという点です。
SQS 開発者ガイド – 遅延キュー

遅延キュー

ActiveJob ではオプションとして #set(wait: 1.minute) などが使用できますが、 Shoryuken でこの実現に用いられるのが遅延キューです。SQS 側で遅延時間に 0 ~ 15 分という制約があるので、 Shoryuken では 15 分以上の遅延実行はサポートされていません。

デッドレターキュー (Dead letter queue)

Amazon SQS では、デッドレターキューがサポートされます。デッドレターキューは、正常に処理できないメッセージの送信先として他の (送信元) キューが使用できるキューです。これらのメッセージは、処理が成功しなかった理由を判断するためにデッドレターキューに分離できます。
SQS 開発者ガイド – デッドレターキュー

これも引用の通りですが、 SQS 側で設定しておけば一定回数失敗したメッセージが別のキューに自動的に移動されるようになります。失敗回数は 1~1000 で設定でき、これで失敗時のリトライ回数の上限が設けられるようです。

おそらく前述の可視性タイムアウトで失敗した回数だと思うのですが、 SQS のドキュメントでは記述が見つからなかったので未確認です。

結局使うのは諦めました

SQS の堅牢さには心惹かれるものがあったので、ぜひ利用したかったのですが、いくつかの理由により見送ることにしました。

Shoryuken での設定値の共有がイマイチ

config/shoryuken.yml に AWS の設定などを記述するのですが、どうやらこれは worker が参照するための設定らしく、Rails アプリ側ではこの値を読み込んではくれないようです。なのでジョブの登録時に AWS の設定が反映されておらず、エラーになりました。

Shoryuken の wiki によると、AWS クライアントのインスタンス作成処理を Shoryuken に任せずに自分で作るなどすれば良いようですが、せっかく config/shoryuken.yml が存在しているのだから、 Shoryuke 側で反映させて欲しいな、と思いました。
なお、参考先のサイト では、Rails の config/initializersShoryuken::EnvironmentLoader.load(config_file: "config/shoryuken.yml") を実行して Rails 側に設定を読み込ませる方法が紹介されていましたが、僕の環境ではキュー名の Prefix が二重に付いてしまったりして、うまく動作してくれませんでした。

また、 Rails.env によって設定を変える、といったことが Shoryuke 側ではサポートされていないので、 staging 環境では少し worker の thread 数減らそう、とかそういった設定がいちいち面倒なのも残念でした。

100万トランザクションは簡単に突破しそう

100万トランザクションまで無料、という SQS ですが、Queue が空でメッセージが配信されない場合もトランザクションとして計上されているようです(料金に計上されるかは未確認なので、もし違ったら教えてください。)
Shoryuken は 1 プロセスで標準 25 スレッド並列に処理が走るので、ローカルで1時間弱動かしただけでも 40000トランザクションを超えました。

ただし、Shoryuken 側のオプションで delay という項目があり、メッセージが空の場合は delay で指定した期間はリクエストをストップするので、この値を調整すればなんとかなりそうです。
しかし、あまり長い秒数 delay してしまうと Job の即時実行ができないので、要件に合わせた慎重な検討が必要です。

個人で開発された gem である

一番大きな点はここです。
現在も頻繁に開発されているのですが、割と作者本人が自分で PR 出して自分で merge という流れが多いようです。
あくまで作者が個人的に開発している gem であり、商用利用を考慮した慎重な開発はされていないような印象を持ちました。

ただ、ローカルで動かしてみただけですが、非常に軽快に動作しており、worker としての核はよくできている gem だと感じました。SQS についても Redis より堅牢なように感じたので、かなり本腰を入れて検討したのですが、ジョブで実行している処理は開発中のアプリの核になる機能なので、ちょっとここはリスクと考え、慎重になりました。

最後に

ジョブのキューに SQS を利用する、という観点は非常に良いように思いますし、他に SQS に対応した Job queue worker は存在しなさそうなので、個人的には Shoryuken を応援しています。
今回は商用利用のアプリ用だったので見送りましたが、個人で作成するアプリではぜひ使ってみたいと思います。

参考


  1. DelayedJob は RDS 以外のバックエンドも サポートしています 

  2. 他にもDelayedJobSQSがありますが、4年ほど開発が止まっているようです。  

続きを読む

knife zeroをawsで扱う際にsshが切断されてしまう問題と解決策

awsでknife zero convergeを使うと起きる問題

awsでknife zeroを使う場合、shellが長い間応答しないコマンドが走るとフリーズしてしまいます。具体的にはdokkuをインストールしようとしましたが、この辺でsshが応答しなくなり、Ctrl+Cでキルするしかなくなります。

Importing herokuish into docker (around 5 minutes)

原因

awsはsshで長い間通信されない場合にインフラ側で自動的にルーティングを切ってしまうようです。ssh-clientやsshdは生きていますが通信が通らないという状態に陥ります。

この状態を回避するために、~/.ssh/configに定期的に通信して切られないよう設定を追加するという方法が提案されています。
http://qiita.com/euno7/items/00ef56244b7b193d2455

しかし、knife zeroではこの方法が働きません。

解決策

knife zeroで~/.ssh/configが働かない課題には2つの要因があり、それらの解決策について解説します。

knife sshでコンソールのssh_configが反映されていない

chefのソースコード上(chef/lib/chef/knife/ssh.rb)で、~/.ssh/configを読み込む処理がありますが、user,forward_agent,portくらいしか使われていませんでした。そこでServerAliveIntervalを働かせるように修正します。

https://github.com/kackyt/chef/pull/1/commits/8b6bfd586774b754108b0a1d03f9db21e8543553

Net::SSH::Multiでkeepaliveパケットが流れない

これでうまくいったーと正直私も思いましたが上だけでは解決しませんでした。Net::SSHでは動作しますが、knife zero(knife ssh)ではNet::SSHから派生したNet::SSH:Multiというclassが使われています。これにはNet::SSHでは実装されているServerAliveIntervalに対応するkeepaliveパケットを送信する実装が入っていませんでした。そこでNet::SSH:MultiにNet::SSH同様の実装を追加しました。

https://github.com/kackyt/net-ssh-multi/commit/19c3efba675c61c50a6468f5200f7e52efdfcc04

これでようやくcookbookが長引いてもちゃんと処理が完了するようになりました。やったね。

TODO

もろもろ落ち着いたらpull request書きます

続きを読む

S3を使ってVagrantのBoxを管理する

背景

以前は3rd partyのVagrant Boxと一緒にAnsibleのplaybookを同梱して、開発環境を構築/配布していた。
しかし、プロビジョニングする物が増えてきたため、初回構築時のプロビジョニング時間が馬鹿にならなくなってきた。
時間節約のために、プロビジョニング済みのBoxをS3にUPして配布してしまうのが楽だろうと判断した。
※Ansibleのplaybookは変わらず同梱する。

問題

S3にアップロードしたBoxのURLをVagrantfileにそのまま記載しただけだと、Boxのバージョニングが行えない。
どうすっかなー。とりあえず公式ドキュメントを漁る。
https://www.vagrantup.com/docs/boxes/format.html

ここのBox Metadataが正にそれ。

どうやったか

Vagrantfile

Vagrantfileはこんな感じでS3にUPしたJSONファイルを指定しておく。

Vagrantfile
config.vm.box = "local-development"
config.vm.box_url = "https://s3-ap-northeast-1.amazonaws.com/local-development/box.json"
config.vm.box_check_update = true

この時、box_check_updateをtrueにしておくと、新しくBoxをS3にUPした際、勝手にアップデートが始まるのでおすすめ。

S3

Vagrantfileが参照する、JSONファイルはこんな感じ。
BoxもS3のバケットの中に入れておく。

box.json
{
  "description": "Centos7 x86_64",
  "short_description": "CentOS7",
  "name": "local-development",
  "versions": [{
    "version": "1.0.0",
    "status": "active",
    "description_html": "<p>CentOS7 x86_64</p>",
    "description_markdown": "CentOS7 x86_64",
    "providers": [{
      "name": "virtualbox",
      "url": "https://s3-ap-northeast-1.amazonaws.com/local-development/box-1.0.0.box"
    }]
  }]
}

BoxのバージョンをUPする際は、versionurlを変えてやれば良い。
これでくそ長いプロビジョニングに付き合わせることなく、Boxのバージョニングもよろしくやれて楽チンダネー。

続きを読む

純粋なシェルスクリプトだけでAWSを操りたい!〜POSIX原理主義でクラウドも侵略〜

この記事は、POSIX原理主義とシェルショッカー日本支部に感化されちゃって、なんとか実践しようとしている、なんちゃって雑魚戦闘員がお送りします。POSIX原理主義でもクラウドを侵略できますよ、というお話です。これで、POSIX原理主義がますます強力になれるんじゃないかと勝手に思っています。

まずコマンドの紹介から、

axs - UNIX哲学を守ったつもりのsimpleなawsコマンド

A simple ‘aws’ command ‘axs’ written in POSIX sh. axs(access) to aws(amazon web services) with posixism.

このコマンドは、AmazonWebServicesにアクセスし、数々のwebサービスを利用したり、アプリを構築したりするために作られた、POSIX原理主義に基づく、なんちゃってawsコマンドです。

https://github.com/BRAVEMAN-L-BRID/axs

作った経緯

AWS APIツールは「いつでも、どこでも、すぐ」使えるわけではない。

バージョン依存や環境依存の大きい、AWS APIツールはいつでもどこでもすぐに使えるわけではありません。それに加えて、SDKやCLIも、それらをインストールできない環境だと、そもそも役に立ちません。意外なこと使えない環境を目にする機会も多いです。

しかしながら、環境やバージョンアップに悩まされずにAWSのサービスを利用したい、というニーズも時にはあるのではないでしょうか?
浅くネットサーフィンした限りでは、そのように認識しています。

このaxsコマンドは、そのニーズに応えるために作りました。

POSIX shに準拠しているつもりなので、Windows, Mac, Linuxなどで、それこそコピーしてくるだけで「いつでも、どこでも、すぐ」動きます。

大量のオプション、引数からの解放

作った経緯2です。awsコマンドの大量の引数やオプション、サービスごとに異なる数々のサブコマンドにヘキヘキした覚えはありませんか?私はあります。
あれはUNIX哲学を守っていません。コマンドとクラウドへ渡すパラメーターがぐちゃぐちゃに混ざったシェルスクリプトを書くのは、もう、こりごりです。

このコマンドでは、その煩わしさから解放されます(嘘、かもしれません)。また、UNIX哲学を守ったつもりで、RESTfulの概念も大事にしています。直感的な操作が可能かもしれません。

スクリプトを使った自動化も楽になる可能性があります。

使い方

ダウンロードしたら、このリポジトリの/binディレクトリにPATHを通してください。

0. 準備

~/.aws/credentialsファイルに以下のようにアクセスキーIDとシークレットアクセスキーを記述してください。パーミッションには気を付けてください。

[default]
aws_access_key_id = hogehoge
aws_secret_access_key = mogemoge

1. 基本

使い方は簡単です。設定ファイル(データ形式は後述)をaxsコマンドで読み込むだけです。

例えば、catコマンドを使って

$cat config_file | axs

とできますし、もしくは、引数に設定ファイルを置くだけです

$axs config_file

2. 設定ファイルのデータ形式について

AWSはREST APIを用いています。そこで、今回は、正しいのかどうかは横に置いておいて、HTTPにちなんだデータ形式の設定ファイルを記述します。

以下のような形式をとることにしました。

METHOD URI                    (←リクエストライン)
Key Value                   (←キーバリュー形式に分解したクエリ)
key Value                   (←キーバリュー形式に分解したクエリ)
Key Value                     (←キーバリュー形式に分解したクエリ)
Host: ec2.ap-northeast-1.amazonaws.com  (←必須ヘッダー)
Content-Type: application/hoghoge     (←場合によっては必須のヘッダー)
X-Amz-moge: hogehogehoge                (←設定などの追加のヘッダー)

(bodyの部分コンテンツの中身)
xmlとかjsonとかバイナリデータ         (←コンテンツの中身)

基本的に、Host,Content-Typeヘッダのみが必須だと考えてもらっていいです。

AWS APIを利用するので、body部には、基本的にxmlやjsonを記述します。

ただし、場合によっては画像ファイルや音声ファイルなどのバイナリデータをアップロードしなければならない時もあります。また、xml,jsonが長く煩雑な時は、リクエストライン、クエリ、ヘッダまでの部分とbody部を分離したいと思う時もあるでしょう。

そのような時に、axsコマンドの-fオプションを使います。

3. -fオプション

body部を分離して別ファイル(body.txtなど)にしてaxsコマンドを使う場合には以下のようにします。
※ここではヒアドキュメントを使用しています。

$cat<<END | axs -f moon.jpg
PUT /image.jpg
Host: Bucket.s3.amazonaws.com (東京リージョンの場合はbucket.s3-ap-northeast-1.amazonaws.com)
Content-Type: image/jpeg
END

*この例では、ローカルのmoon.jpgという画像ファイルをs3のBucketバケットにimage.jpgという名前で保存しています。

4. -qオプション

 これはAPIアクセス後に返ってくる、レスポンスのレスポンスヘッダーを表示するかいなかを決定するオプションです。
 デフォルトではレスポンスヘッダを表示します。AWSのREST APIがヘッダ情報をよく扱うので、汎用性を高めるためにそうのようにしてあります。

例えば、以下のような違いになります。

$cat config_file | axs 

HTTP 200  OK
hoge: hogehoge
hage: hagehoge
hoga: hogレスポンスヘッダー

<xml ......>
<DescribeInstances>...........</...>
.......

-qオプションを利用した場合は、レスポンスヘッダが省略されxmlやjsonやバイナリが直に帰ってきます。ですので、パイプでつないで加工したりするのに便利です。


cat config_file | axs -q > polly.mp3
               (pollyに喋ってもらう)


cat config_file | axs -q | parsrx.sh(POSIX原理主義製xmlパーサー)
               (xmlが返ってくる)


cat config_file | axs -q | parsrj.sh(POSIX原理主義製jsonパーサー)
               (jsonが返ってくる)

TIPS

  • 設定ファイルの書き方は、AWS API referenceなどを参照してください。設定ファイルの記述はクエリ部分以外はHTTPと同じです。
    深く悩まずに記述できることでしょう。

  • Content-Lengthヘッダ,x-amz-content-shaナンチャラヘッダ,Autorizationヘッダは自動生成されるので、考慮する必要はありません。

  • 私も仲間に加えてもらった秘密結社シェルショッカー日本支部のPOSIX原理主義製の他コマンドと相性がいいです。
    これを機に秘密結社シェルショッカー日本支部よりダウンロードしてくることをお勧めします。
    中でもmojihameコマンドとの相性は抜群です。https://github.com/ShellShoccar-jpn/installer

例えば、クエリAPIとmojihameコマンド

テンプレファイルを用意します、template

GET /
QUERY
%1 %2
QUERY
Version 2016-11-15
Host: ec2.ap-northeast-1.amazonaws.com
Content-Type: application/x-www-form-urlencoded

設定の用意、config.txt

Action Hogehoge
AAAAA dededed
hogemoge ahahaha

いざアクセス

cat config.txt | mojihame -lQUERY template - | axs -q | parsrx.sh | 加工

素晴らしいparsrsのコマンドを用いれば、無駄に多くのコマンドを用いずにレスポンスの解析もできます

ちなみに

cat config.txt | mojihame -lQUERY template -

までの結果だけ抜き出すと以下のようになっています。

GET /
Action Hogehoge
AAAAA dededed
hogemoge ahahaha
Version 2016-11-15
Host: ec2.ap-northeast-1.amazonaws.com
Content-Type: application/x-www-form-urlencoded

実際にAWSにaxs(アクセス)してみよう!

以下の内容ではシェルショッカー日本支部の開発しているコマンドを多く使うかもしれません。mojihameコマンドとParsrsは必須です。また、記述の簡略化のために、ヒアドキュメントを多用します。

S3にアクセスして、東京リージョンで静的webサイトのホスティングをしてみよう!

0.好きな名前のbucketをおく(PUTする)

cat <<END | axs
PUT /
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: application/xml

<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 
  <LocationConstraint>ap-northeast-1</LocationConstraint> 
</CreateBucketConfiguration >
END

1.bucketの中に公開するオブジェクトindex.htmlをおく(PUTする)

公開するものなので、誰でも読み込み可能にするために、設定として追加のヘッダx-amz-acl: public-readを付与しています。

cat <<-END | axs -f index.html     (←ローカルファイル)
PUT /index.html
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: text/html
x-amz-acl: public-read
END

2.bucketの中に公開するオブジェクトerror.htmlをおく(PUTする)

これは、エラーページ用のhtmlです。

cat <<END | axs -f error.html
PUT /Error.html 
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: text/html
x-amz-acl: public-read
END

3.bucketの中に公開するオブジェクトimage.jpgをおいて(PUTする)、やっぱり消す(DELETE)

index.htmlに載せる画像をbucketにputしましたが、気に入らなかったのでやっぱりDELETEすることにしました。

cat <<END | axs -f moon.jpg
PUT /image.jpg
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: text/html
x-amz-acl: public-read
END

やっぱり気に入らないので消す

cat <<END | axs
DELETE /image.jpg
Host: BucketName.s3-ap-northeast-1.amazonaws.com
END

4.バケットをウェブサイト用に修正する(PUTする)

s3を静的webサイトとして活用します。

s3のwebサイトホスティングでは、ルーティングルールを使用して、特定のHTTPエラーコードをチェックする条件を指定できます。ルールを追加すれば、リクエストエラーが発生した場合、リクエストを再ルーティングしたりできます。つまりwebサイトの機能を持たせることができるのです。

たとえば、リクエストエラーを処理し、別のホストにルーティングしてみましょう。次のルーティングルールは、HTTPエラー404のイベントでEC2インスタンスにリダイレクトします。

ExamplePage.htmlページをリクエストしてHTTP 404エラーが発生したとします。すると、そのリクエストは、指定されたEC2インスタンスのreport-404/testPage.htmlに再ルーティングされます。ルーティングルールが存在しない状態で、HTTPエラーが発生した場合には、Error.htmlを返します。

cat <<END | axs
PUT /
website
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: application/xml

<WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
  <IndexDocument>
    <Suffix>index.html</Suffix>
  </IndexDocument>
  <ErrorDocument>
    <Key>Error.html</Key>
  </ErrorDocument>

  <RoutingRules>
    <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals >
    </Condition>
    <Redirect>
      <HostName>ec2-11-22-333-44.compute-1.amazonaws.com</HostName>
      <ReplaceKeyPrefixWith>report-404/</ReplaceKeyPrefixWith>
    </Redirect>
    </RoutingRule>
  </RoutingRules>
</WebsiteConfiguration>

5.TIPS:独自ドメインで静的webサイトを公開する場合

上記までの設定を用いて、s3で静的webサイトのホスティングを行うと、webサイトの公開アドレスは、AWS側で決定されたendpoitとなります。例えばs3-website.s3.amazonaws.comのようなものです。

しかし、自分のドメインでwebサイトを公開したいと思う時もあるでしょう。

その場合は、バケット名を独自ドメイン名にして、Route53で設定してあげれば可能です。

例えば、あなたの所得したドメインがwebsite.comだった場合。バケット名をwebsite.comにします。あとは、Route53で設定してあげると、website.comでwebサイトを公開できます。

6.TIPS:独自ドメインのバケットを用いるときの注意

独自ドメインでサイトを公開しない通常の時は、以下のようにホストヘッダにバケットの名前を含めることができます。そしてこちらの方がレファレンス的には正しいです。

cat <<END | axs
PUT /
website
Host: BucketName.s3-ap-northeast-1.amazonaws.com
END

なおこの場合のwebサイトのアドレス以下のようになります。

http://BucketName.s3-website-ap-northeast-1.amazonaws.com

しかし、独自ドメインをバケット名に使用し、ホストヘッダに含める場合は、axsコマンドでは、エンドポイントの名前解決ができません。したがってドメイン名のバケットを作る場合には、ターゲットuriにバケット名を含めます。

cat <<END | axs -f index.html
PUT /website.com/index.html
Host: s3.amazonaws.com
Contetnt-Type: text/html
END

この操作では、us-east-1リージョンにあるwebsite.comバケットに、index.htmlをPUTしています。なおAWS APIで用いるendpointについては、レファレンスを参照してください。サービスとリージョンごとに形式が異なる場合が多いです。
http://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html

7.TIPS:ヒアドキュメントで記述した内容を設定ファイルに残したい

そんな時はteeコマンドを使います 。ヒアドキュメントに記述した内容がconfig_fileに保存されます。

cat <<END | tee config_file | axs
GET /
Host: s3.amazonaws.com
END

Amazon Pollyにアクセスして音読させてみよう!

Amazon Pollyはテキストの音声変換サービスです。誰でも非常に簡単に利用できるサービスで、AWSの中でも敷居は低いです。とりあえずやってみましょう。何かのアプリ開発に役に立つかもしれません。

また、AWSのフルマネージドサービスなので利用者がインフラ構成やコーディングについて心配する必要は微塵もありません。東京リージョンでは利用できないので注意してください。

なお、ここではmojihameコマンドを使います

1.テンプレートファイル speech.templateを用意します。

お馴染みのHTTP形式の設定ファイルのテンプレです。使い回しが可能なので、メンテナンス効率は上がるかもしれません。aws cliのようにパラメータとコマンドをごちゃまぜにしたスクリプトを書く必要がなくなります。

POST /v1/speech
Host: polly.us-east-1.amazonaws.com
Content-Type: application/json

{
  "OutputFormat": "mp3",
  "Text": "%1",
  "VoiceId": "Mizuki"
}

2.pollyにアクセスしておしゃべりしよう!

ここまできたら簡単、あとはAmazon Pollyにアクセスしておしゃべりするだけです。

$echo "こんにちはQiita" | mojihame polly.template - | axs -q > result.mp3

ちなみにaxsコマンドの前までを抜き出すと以下のようになっています。

POST /v1/speech
Host: polly.us-east-1.amazonaws.com
Content-Type: application/json

{
  "OutputFormat": "mp3",
  "Text": "こんにちはQiita",
  "VoiceId": "Mizuki"
}

3.長文の小説もPollyに喋ってもらうことができます。

「小説家になろう」というサイトから小説をもらってきて、pollyに音読させましょう。pollyには文字数制限がありますので、何文字かに区切ってリクエストを出し、返ってきたレスポンスを対象ファイルに上書きしていきましょう。

下記のcurl先のurlのncode下の番号は小説とページによって異なります。

$curl http://ncode.syosetu.com/txtdownload/dlstart/ncode/XXXXXXXX/ | 
tr -d 'n' | 
sed 's/.{250}/&@/g' | 
tr '@' 'n' | 
while read LINE; do 
echo $LINE | mojihame polly - | axs -q ; 
done >> polly.mp3

EC2にアクセスしてインスタンスを立ててみよう!

このセクションではmojihameコマンドとparsrx.shを使います。

0.テンプレートファイルec2.templateの用意

以下のファイルはmojihameコマンドで使います。

GET /
QUERY
%1 %2
QUERY
Version 2016-11-15
Host: ec2.ap-northeast-1.amazonaws.com
Content-Type: x-www-form-urlencoded

1.VPCを作ろう

cat <<END                       | 
Action CreateVpc
CidrBlock 10.0.0.0/16
END
mojihame -lQUERY ec2.template - | 
axs -q                          |
parsrx.sh                       | 
grep 'vpcId'                    | 
awk '{print $2}'

(結果としてvpcIdが表示されます)

ヒアドキュメントではなく、config_file1に設定項目であるAction CreateVpcとCidrBlock 10.0.0.0/16を記述したとしたら以下のようになワンライナーになります。

 cat config_file1 | mojihame -lQUERY ec2.template - | axs -q | parser.sh | grep 'vpcId' | awk '{print $2}'

2.puclic-subnetを作ろう

作成したvpcにinternet gatewayをアタッチし、パブリックサブネットを作成します。以下省略

3.ec2インスタンスを立てよう

作成したpublic-subnet内にec2インスタンスを作成します。以下省略

今回は面倒臭かったので色々省略しましたが、1、2、3の流れは全て簡潔で似通っています。次回があれば、AWS上に3-tierアーキテクチャを構成する完全自動化スクリプトを組んでみたいと思います。もちろんaxsコマンドを使って。

Amazon Rekognitionにアクセスして画像解析してみよう!

Amazon Rekognitionは、深層学習により、画像解析を行なってくれるAWSのフルマネージドサービスです。ユーザーがコーディングの心配をすることなく、簡単にすぐに、どこでも利用することができます。非常に利便性が高く、将来必要不可欠になるであろうサービスの一つです。

1.S3にバケットを配置して、顔を含んだjpeg画像を置いてみる

リージョンはusを選択してください。他リージョンではまだサービスを開始していません。

  • バケットを作る
cat <<END | axs
PUT /
Host: s3-to-rekognition.s3.amazonaws.com
  • 画像ファイルを配置する
cat <<END | axs -f my-face.jpg
PUT /my-face.jpg
Host: s3-to-rekognition.s3.amazonaws.com
Content-Type: image/jpeg

2.Rekognitionで画像解析して、要素を抽出してみる

これにはDetectLabels APIを使用します。

DetectLabelsは、入力として提供される画像(JPEGまたはPNG)内における実世界の要素を検出して、ラベルを付与します。花、木、テーブルなどのオブジェクトは当然含まれ、さらには、結婚式、卒業、誕生日パーティーのようなイベントシーン、風景、夕方、自然などの概念までが含まれます。

結果は整形されていないjsonで帰ってきます。これでは読み取りづらいので、ヘッダ情報を排して、jsonパーサーにかけてしまいます。もちろん、POSIX原理主義jsonパーサーparsrj.shを使って。

cat <<END | axs -q | parsj.sh
POST /
Host: rekognition.us-east-1.amazonaws.com
X-Amz-Target: RekognitionService.DetectLabels
Content-Type: application/x-amz-json-1.1

{
   "Image":{
      "S3Object":{
         "Bucket":"s3-to-rekognition",
         "Name":"my-face.jpg"
      }
   }
}
END

私の顔画像をアップロードして解析した結果は以下のようになりました。

$.Labels[0].Confidence 99.2739486694336
$.Labels[0].Name Human
$.Labels[1].Confidence 99.27888488769531
$.Labels[1].Name People
$.Labels[2].Confidence 99.27890014648438
$.Labels[2].Name Person
$.Labels[3].Confidence 82.6961669921875
$.Labels[3].Name Dimples
$.Labels[4].Confidence 82.6961669921875
$.Labels[4].Name Face
$.Labels[5].Confidence 82.6961669921875
$.Labels[5].Name Smile
$.Labels[6].Confidence 54.255836486816406
$.Labels[6].Name Selfie
$.Labels[7].Confidence 50.784881591796875
$.Labels[7].Name Female
$.Labels[8].Confidence 50.784881591796875
$.Labels[8].Name Girl
$.Labels[9].Confidence 50.784881591796875
$.Labels[9].Name Woman
$.Labels[10].Confidence 50.74814987182617
$.Labels[10].Name Glasses
$.Labels[11].Confidence 50.74814987182617
$.Labels[11].Name Goggles

多分、私は二重で目が大きいので上のような結果になってしまったのだと思います。サングラスとか眼鏡の可能性が指摘されてます。ちなみに顔アップの自撮り写真をあげたのですが、それも認識されているようですね。えくぼとスマイルまで認識されています。rekognitionには女顔として認識されたようでした。

以上のようにかなり詳しく要素を検出してくれます。別の例ではテーブルクロスなども検出していました。他にもAmazon Rekognitionでは顔の判別や比較、リストができます。ですので、例えば、普段見ない顔を検出した時にアラートを送信するようなアプリケーションを作れば、自作警備システムなんかも作成できるかもしれませんね!東京オリンピックに備えて笑

まとめ

さて、これでクラウドの力を借りれば、POSIX原理主義でも、様々なアプリケーションを開発できることがわかりましたね!さらには、機械学習や深層学習の力を借りた強力なものまでも、比較的簡単にできちゃうことがわかったと思います!しかも環境やバージョンに悩まされることなしで!

わーい!POSIX原理主義シェルスクリプトは楽しいですね!

※ここまでの流れで、AWS自体がPOSIX原理主義と対極じゃんて声が聞こえてきそうですが、そこはあまり関係ありません。AWSは道具というよりサービスです。ほらAmazon Web Servicesって言います。それにフルマネージドなサービスを多く活用するようにすれば、バージョンアップによる不具合とは比較的無縁です。

ということで、私は、実は、POSIX原理主義は、クラウドととても相性がいいのではないかと勝手に思っています(素人考え)。

「必要なもの」POSIX準拠コマンドと交換可能性を担保したコマンド

  • openssl ver1.0.0以上
  • utconv (同梱、秘密結社シェルショッカー日本支部)
  • urlencode (同梱、秘密結社シェルショッカー日本支部)
  • cat
  • awk
  • sed
  • printf
  • echo
  • grep
  • curl
  • wget
  • eval

感想

意味がるのか知りません。あるかもしれないし、ないかもしれません。ただ楽しかったですとだけ付け加えておきます。

「シェルかっこいい、クラウドかっこいい」
→「なんか作りたい」
→「POSIX原理主義すげー」
→「POSIX原理主義でもクラウドwebサービスの力を借りたい!」
→「一番サービスが充実していそうなAWSを操りたい」
→「awsコマンド的なものをPOSIX原理主義で開発しよう!」
という動機で始めました。作者は初Qiita,教育学部卒業したてのずぶの素人で数ヶ月前までIT系とは無縁でした。技術的な誤りを多分に含んでいる可能性があります。その時は指摘してくださると助かります。

続きを読む

EC2インスタンスの自動起動停止

自動起動

var AWS = require('aws-sdk'); 
AWS.config.region = 'ap-northeast-1';

function ec2Start(cb){
    var ec2 = new AWS.EC2();
    var params = {
        InstanceIds: [
            "i-0da881bbe333d6fbb"
        ]
    };

    ec2.startInstances(params, function(err, data) {
        if (!!err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            cb();
        }
    });
}

exports.handler = (event, context, callback) => {
    // TODO implement
    // callback(null, 'Hello from Lambda');
    ec2Start(function() {
        context.done(null, 'Started Instance');
    });
};

自動停止

var AWS = require('aws-sdk'); 
AWS.config.region = 'ap-northeast-1';

function ec2Stop(cb){
    var ec2 = new AWS.EC2();
    var params = {
        InstanceIds: [
            "i-0da881bbe333d6fbb"
        ]
    };

    ec2.stopInstances(params, function(err, data) {
        if (!!err) {
            console.log(err, err.stack);
        } else {
            console.log(data);
            cb();
        }
    });
}

exports.handler = (event, context, callback) => {
    // TODO implement
    // callback(null, 'Hello from Lambda');
    ec2Stop(function() {
        context.done(null, 'Stoped Instance');
    });
};

ロール

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ec2:StopInstances",
                "ec2:StartInstances"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*",
                "arn:aws:ec2:*"
            ]
        }
    ]
}

続きを読む

AWS CodeDeploy 導入調査 アプリ側編

AWS CodeDeploy 導入調査 AWS 設定編 の続きになります

前回のあらすじ

  • EC2 インスタンスを作った
  • コードアップロード用の S3 Bucket を作った
  • デプロイ用の IAM ロールを作った
  • CodeDeploy アプリケーションを作った

今回はアプリ側の作業です.

デプロイ先アプリ構成

Nginx – uWsgi を sudpervisord でプロセス監視
Nginx とか supervisord とかは割とどうでもいいのですが
サーバー構成はあらかじめ一通り作成しておきます.

さらに CodeDeloy-Agent をインストールしておきます.
http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install.html

とりあえずデプロイしてみる

ローカル -> S3 のアップロードは AWS-CLI を使用します.
ローカルプロジェクトのトップディレクトリで,

$ aws deploy push --application-name <<app-name>> --s3-location s3://<<s3bucket-name>>/<<app-name>>.zip --source ./ --region <<region-name>>

を実行します.
<<app-name>> は CodeDeploy で作成したアップケーション名です.

  • –ignore-hidden-files オプションで隠しファイルを無視します.
  • S3 にアップロードするファイル名は本当はなんでもいいです.
  • 圧縮時の拡張子は tar.gz でもいけるはず.

アップロードに成功すると以下のように出力されます.

To deploy with this revision, run:
aws deploy create-deployment --application-name <<app-name>> --s3-location bucket=<<s3bucket-name>>,key=<<app-name>>.zip,bundleType=zip,eTag=<<eTag>>--deployment-group-name <deployment-group-name> --deployment-config-name <deployment-config-name> --description <description>

ただし今回は成功しないと思います.

appspec.yml の作成

appspec.yml は CodeDeploy によるデプロイ作業を記述したものになります.
まずローカルプロジェクトのトップディレクトリに appspec.yml ファイルを作成し,

version: 0.0
os: linux

まで記述します. ここで,
– version は appspec のシステムバージョンで2017/04現在 0.0 のみ有効です
– os はデプロイ先 EC2 インスタンスの OS 種類で windows か linux かを設定します. linux ディストリビューションまで指定する必要はありません.

次に,

files:
   - source: /
     destination: /usr/local/src

でファイル配置を設定します.
source はコードパスで appspec.yml があるディレクリの相対パスになります.
なので source: / とした場合はディレクリ構成そのままで EC2 インスタンスの
destination に配置されます.
特定のディレクリ/ファイルのみ配置先を変えたい場合は – source: destination の記述を追加することができます.

さらに, 今回は以下の設定を追加しました.

hooks:
  AfterInstall:
    - location: copy_settings.sh
      timeout: 180
      runas: root
    - location: pip_install.sh
      timeout: 180
      runas: root
  ApplicationStart:
    - location: reload_uwsgi.sh
      timeout: 180
      runas: root

hooks 以下では CodeDeploy ライフサイクルの各処理にフックしてスクリプトを実行させることができます.
http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html

CodeDeploy-Agent は Ruby で記述されており, Ruby コード上で Shell スクリプトを実行します.
今回追加した処理は

  • コード配置後に EC2インスタンス内に仕込んでおいた追加設定をコピー
  • プロジェクト内の requirements.txt の内容で pip install
  • アプリスタート時に uwsgi のリスタート

です. runas は各スクリプトの実行ユーザーです.
今回は面倒だったので全て root にしました.

デプロイに再度チャレンジ

ふたたび ローカル -> S3 のアップロード をやってみると今度は成功するはずです.
アップロードが成功したら次は AWS コンソールの CodeDeploy に移動します.

  1. ダッシュボードでアプリケーションを選択して “アクション” -> “新しいリビジョンのデプロイ”
  2. “アプリケーション”, “デプロイグループ” はそのまま, “リビジョンタイプ” は Amazon S3
  3. “リビジョンの場所” でアップロードしたソースコードを指定します.
  4. その他はそのままで “デプロイ” でデプロイ開始します.

フックスクリプトでエラーにならなければデプロイは成功するはずです.

フックスクリプトでエラーになったので解決した内容は次回

続きを読む