AWS EC2で従量制VPNサーバの構築

はじめに

仕事柄海外のブログを閲覧しますが、中国在住だと悪名高い金盾によりネットワークが遮断されることがしばしば起こります。その場合はVPNを使いますが、閲覧したい時にだけ利用するのに月額サービスを利用するのはあまりにもったいない。そこで、自前で従量制VPNサーバを構築してみました。ただそれだけだとすぐに話が終わるので、専用のCLIツールについても紹介します。

VPNサーバについて

タイトルで既にネタばれですが、要はAWS EC2を利用するだけです。インスタンスの起動時間で実質的に料金が決まるので、ここへVPNサーバを構築すれば「従量制VPNサーバ」の出来上がりです。ちなみに、VPNサーバ構築手順についてはネットに出回っているため、ここでの説明は省略します。

ただし、インスタンス起動時には必ずVPNが接続できる状態にしておいてください。そのため、構築時には以下のコマンドでサービス自動起動するかどうか確認しておいてください。

$ systemctl status [service]

CLIツールについて

上記インスタンスをワンライナーで起動または停止するツールを作成しました。

aws-ec2-manager – the tools is managing AWS EC2 instance by python3.

このツールでいつでもどこでもVPNを利用できます。コマンドの実行にはインスタンスIDが必要なので、予め控えておいてください。ツールのインストール方法及び利用方法についてはReadmeをご覧ください。

コマンド実行時にはVPNサーバのパブリックIPアドレスを表示するようにしています。それをVPNクライアントソフトウェアに設定してVPN接続してください。

最後に

現在はVPNサーバのIPアドレスを取得するだけですが、更に発展させれば、コマンド一つでVPNクライアントへIPアドレスを設定しVPN接続することもできます。ただし、実行環境や利用するVPNクライアントソフトウェアへ依存するため、それは追々先の話でしょうか。

続きを読む

Security-JAWS#5レポート

こんにちは、ひろかずです。

5/22にトレンドマイクロさんで開催されたSecurity-JAWS#5に行ってきましたので、一筆書きます。

月曜に関わらず、盛況な集まりでした。

お品書き

Session1:トレンドマイクロ株式会社 姜 貴日さん「Deep SecurityとAWS WAF、SNS、Lambdaを使ってうまいこと自動防御する」
Session2:三井物産セキュアディレクション株式会社 大橋 和正さん「インシデント別対策から見るセキュリティ運用体制〜Alert Logicのことも少し〜」
Session3:エフセキュア株式会社 河野 真一郎さん「脆弱性について疑似クラッキング(デモ)をやってみる ~脆弱性対策はとても大切。でも多くの人はそれに気づかないんだ〜」
Session4:洲崎さん「AWS使って社内CTFを開催してみた」

Session1:「Deep SecurityとAWS WAF、SNS、Lambdaを使ってうまいこと自動防御する」

トレンドマイクロ株式会社 姜(かん) 貴日さん

DeepSecurityとは

サーバ向け総合セキュリティ対策製品
IPS/IDS, セキュリティログ監視, アンチマルウェア, 変更監視
単一の機能ではなく、多層防御の考え方でセキュリティを確保する製品。
AWSコンソールとDeepSecurityManagerが連携して、インスタンスの増減を自動で反映する機能もある。

自動防御の仕組み

今回の構成は、AWS上にALB(AWS WAF)-EC2(ECS)を配置した構成。
SNSとLambdaを用意
SecurityGroupは、隔離用のもの(Outbound,Inboudなし)を用意しておく

シナリオ1(自動ブロック)

  • Deep Security Agentの侵入防御(IPS/IDS)で検知する。
  • Deep Security ManagerからイベントがSNS送信される。
  • SNSは、Lambdaに通知する。
  • Lambdaは、送信元IPをAWS WAFのIP Condition(Blocked)に登録する。

シナリオ2(自動隔離)

  • DeepSecurityAgentのアンチマルウェアで検知
  • イベントがSNS送信
  • SNSはLambdaに通知
  • Lambdaは、検知したインスタンスIDのSGを隔離用のものに差し替え、Auto Scaling Groupから切り離す。
  • Auto Scaling Groupの設定により、差し替え用インスタンスが起動される

まとめ

より、リアルに近い環境でのPoCを実施したい。
共同PoC大募集中!声かけてください!

QA

IPは、トレンドマイクロのデータベースと突き合わせるのですか?

  • 今は検知ベースです。

テンプレートは公開されていますか?

  • まだです(公開予定)

IPはころころ変わると思いますが、リフレッシュとか考えてますか?

  • そういうフィードバック大歓迎です!

DSaaSは、どこまでの規模感に対応できますか?

  • DSaaSは、中規模向け。大規模ならDSMがいい。(中規模って、どれくらい?100大規模?)
  • 国内で1000超えはない。100大規模は実績ある。

DSaaSのバージョンアップってどうなんだろう?

  • Manager側は自動でバージョンアップされます。

Session2:「インシデント別対策から見るセキュリティ運用体制〜Alert Logicのことも少し〜」

三井物産セキュアディレクション株式会社 大橋 和正さん
もともとオンプレメインのセキュリティエンジニア出身。
三井物産セキュアディレクションは、セキュリティ診断、SOC、セキュリティコンサルティングをやっている。
Alert Logicの拡販はじめました。

セキュリティ脅威の動向

IPA発表の10大脅威では、標的型攻撃、ランサムウェアが多く、公開サーバではインジェクション系が多い。
VerizonでもWebサーバに対する攻撃がダントツ

ランサムによる被害

WannaCryの詳細は三井物産セキュアディレクションのサイトで公開中!

Apache Struts2脆弱性をついた情報流出の事例

Twitterで気づく等、対応の遅れがあった

セキュリティインシデントとは?

コンピュータセキュリティに関係する人為的事象で、意図的および偶発的なもの
意図的脅威

  • パスワードリスト攻撃
  • サービス拒否攻撃
  • 情報の持ち出し(内部犯行)
  • サイト改ざん(ハクティビズム、マルウェア配布)

偶発的脅威

  • 設定ミス
  • プログラムのバグ
  • メール誤送信(情報漏えい)
  • PC紛失(情報漏えい)

なぜ事前準備が必要?

100%攻撃を防ぐことはできない

  • 攻撃技術の進歩(いたちごっこ)
  • 人間が使う以上、100%はない(オペミスはない)

天災には備えるのに、セキュリティインシデントへの備えはしないの?

  • 対応の遅れが、ユーザーからの信頼失墜に結びつく。

どのようなインシデントがあって、どのような対応をするのか準備しておく。

AWSにおけるセキュリティインシデントについて

クラウドvsオンプレ

アジリティと自動化
高可用性

おなじみAWS責任共有モデル

コンピューティング、ネットワーク、ストレージ等、クラウド基盤はAWSの責任範囲
OSレイヤ以上は、利用者側の責任ではあるが、SIerやベンダーと協力して対応して行く必要がある。
インシデントの種類をマッピングして、対応すべきセキュリティインシデントを明確にすることが大事。

Alert Logicについて

セキュリティの専門家がSOCで監視している。
リクエストとレスポンスのペイロードがコンソールで見れる。

Session3:「脆弱性について疑似クラッキング(デモ)をやってみる ~脆弱性対策はとても大切。でも多くの人はそれに気づかないんだ〜」

エフセキュア株式会社 河野 真一郎さん

セキュリティ営業3年目(2月からエフセキュア所属)
本社はフィンランド
衣装はガルパンモチーフ

まずは質問

脆弱性をついたクラッキングデモを実際に見たことある方ー!(会場は半々)

今回のシナリオ

標的型メールの添付ファイル(履歴書)を送りつけて、開いてしまった。

前提条件

アンチウィルス、メールサーバ前段のFirewallでは阻害されない

  • 標的型攻撃なので事前調査している想定

AWS接続が簡単なのは、デモのため。

デモ

開いた時点で、Windows7の一般権限は取れている。
攻撃ツールを使用してコマンドプロンプトを操作できる。
1分に一回だけ使える脆弱性をついてAdministoratorに昇格。
PowerShellでDomain Adminを取るまで約6分

OSのパッチ適用してる?

Linuxでも脆弱性管理をしていなければ、ハッカーにかかれば危ない。
OSのパッチ適用だけでは不十分(脆弱性は全体の約12%)
ミドルウェア、アプリケーションの脆弱性が85%をを占める。

宣伝

エフセキュアでは、Rapid Detection Serviceというサービスがある。
デモのような脆弱性がないかを見て欲しいひとはコンタクトして!

Session4:「AWS使って社内CTFを開催してみたよ」

洲崎さん
謎の勉強会ssmjpの運営やってます。

某社で社内CTFを開催しました

会社のHP
30人規模から70人規模に参加者が増えた
セキュリティエンジニアに楽しんで貰える
集合研修方式
Jeopardy形式
CTF終了後には問題と解説を配布(希望者には環境も)

ガジェット

状況を表示するLED看板
ラズパイでスコアサーバのWebAPIに定期的に投げる
スコアサーバは自作
運用管理ダッシュボードを用意
競技PCはWorkspacesを使いたいなー(NGだった)

得られた知見

AWSでイベントやるには申請が必要。
日本語NG。英語で申請。
EC2のみ。WorkspacesはNG。
申請時にはかなり細かく聞かれる。終わるときにはイベント設計が終わってるレベル。
申請から承認まで7日と言われたが、割とすぐに承認がおりた。

Docker(ECS)を使いたかった

時間の関係でEC2に
使えたらECRでデプロイが超ラクになる

監視サーバ

Zabbix Docker Monitoringを使ってみた。
TCPとWebサービスについて

実際にかかった金額

準備期間を含めて$2555だった。
Workspacesの検証がなければもっと安くあがっただろう

QA

参加者の平均年齢は?

  • 若手が多かったが、ベテランまで

運営は何人?

  • 5人。問題は2人で作った。大変だった。他の人も巻き込んでいきたい。

参加者スキルのピンきり具合

  • 満足度は70%(研修としては悪め)
  • 事前にフルイにかけたけど、ミスマッチした方は残念だった。
  • トップは、CTF運営経験者だった

正答率は?

  • 71.38%。8割の問題が解かれた。
  • 2割の問題が解かれなかった。(作った人は凹んでた。作ったのに解かれないのは悲しい。)

最後に

質疑応答も活発でした!
次回も楽しみですね!

今日はここまでです。
お疲れ様でした。

続きを読む

aws周りのメモ2

postgresqlを使う

RDSへpostgresqlをいれて立ち上げ

認証と接続

import-key-pair — AWS CLI 1.11.87 Command Reference
http://docs.aws.amazon.com/cli/latest/reference/ec2/import-key-pair.html

cd $HOGE
openssl genrsa -out my-key.pem 2048
openssl rsa -in my-key.pem -pubout > my-key.pub
# IAMのコンパネで*.pubを入力
# 多分、権限があれば以下でもいける
# aws iam upload-ssh-public-key

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-minimum-vpc-server-environment/

便利

無料枠

無料のクラウドサービス | AWS 無料利用枠
https://aws.amazon.com/jp/free/

AMI

AWS Marketplace: Search Results
https://aws.amazon.com/marketplace/search/results?x=14&y=18&searchTerms=&page=1&ref_=nav_search_box

CFテンプレート

サンプルコード & テンプレート – AWS CloudFormation | AWS
https://aws.amazon.com/jp/cloudformation/aws-cloudformation-templates/

ec2 ami tool & ec2 api tool

Mac で Amazon EC2 API Toolsを設定する – サーバーワークスエンジニアブログ
http://blog.serverworks.co.jp/tech/2013/01/31/mac-amazon-ec2-api-tools-setup/

ec2 api toolは若干心配。

VPCを使う

接続の際に、sshを経由したい。sslでもいいけどなんかsshがいいなと。
パスワードよりkeyのほうがセキュアだからかな。

0から始めるAWS入門①:VPC編 – Qiita
http://qiita.com/hiroshik1985/items/9de2dd02c9c2f6911f3b

導入

Amazon VPC とは? – Amazon Virtual Private Cloud
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Introduction.html

公式のいろいろ

料金 – Amazon VPC | AWS
https://aws.amazon.com/jp/vpc/pricing/

基本は無料だけどNATとVPNは別課金。

【AWS 再入門】VPC 環境に踏み台サーバーを構築して SSH 接続してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-bastion-host-vpc/#i-3

ec2(Bastion)を配置する必要がありそう。

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-minimum-vpc-server-environment/

VPC に推奨されるネットワーク ACL ルール – Amazon Virtual Private Cloud
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Appendix_NACLs.html

vpcでのネットワークのポリシーの例

Default VPC

AWSのDefault VPCを削除して困った話 – MikeTOKYO Developers
http://blog.miketokyo.com/post/49939300091/aws-default-vpc

デフォルトvpcは削除したらダメか。使い分けがわからん。

Amazon EC2 と Amazon Virtual Private Cloud – Amazon Elastic Compute Cloud
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-vpc.html

基本の機能はデフォルトとそうじゃないvpcは同じだけど、
デフォルトvpcがないとちゃんと機能しない。
デフォルトの属性によって、指定がないとipを紐付けたりする。

VPCネットワーク設計

これだけ押さえておけば大丈夫!Webサービス向けVPCネットワークの設計指針 | eureka tech blog
https://developers.eure.jp/tech/vpc_networking/

ネットワークは一度稼働させると移行が大変なので、初期設計が非常に重要になります。

わかりやすい。図が特に。

  • Bastion
  • NAT
  • Security Group

ENI

インフラエンジニアに贈るAmazon VPC入門 | シリーズ | Developers.IO
http://dev.classmethod.jp/series/vpcfor-infra-engineer/

サブネットで指定したIPアドレスのうち、先頭4つと末尾の1つはVPCで予約されるため使用できません。

VPCでは常にDHCP有効とするのがポイントです。

また、DHCPサービスで伝えられる情報(DHCPオプション)は、変更することもできます。

仮想マシンにひもづくENIにより、DHCPサーバーから毎回同じMACアドレス、IPアドレスが付与されます。これは、仮想マシンの状態に依存しないため、仮想マシンを再起動しようと、一旦シャットダウンしてしばらくしてから起動した場合でも必ず同じアドレスが付与されます。

ENI(Elastic Network Interface)か。。なるほど。でも、使うことはなさそうだな。

NAT

IPマスカレードが使えないVPC
NATは、Static(静的・サーバー用途)とElastic(仮想・クライアント用途)がある。
個人的には、このNATインスタンスの実装は、あまり好きではありません。動きがややこしいですし、ユーザーが自分でNATインスタンスの管理をしなければならないのも煩雑な印象を受けます。VPCのネットワークサービスの一つとして提供される機能であれば、ユーザーからはなるべく抽象化され仮想マシンとして意識されないようにするべきと考えます。
ただ、ユーザーから仮想マシンとして見える分、機能・実装が具体的に把握できる点やカスタマイズ性が高い点は良いとも思っています。

NAT インスタンスと NAT ゲートウェイの比較 – Amazon Virtual Private Cloud
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/vpc-nat-comparison.html

なるほど。。

Bastion

AWSで最低限セキュアな構成を組む – Qiita
http://qiita.com/ausuited/items/09b626fa5264f0c650fd

パブリックSubnetにEC2インスタンス(踏み台サーバーとして)
NATインスタンスを作成した要領で、パブリックSubnetにEC2インスタンスを作成する。Security groupは新規に作成してSSHをAnywhereに。Key pairは厳重に管理。尚、踏み台サーバーは、使用する時以外はStoppedにしておく事で、さらにセキュアな状態とする。このデザインパターンをOn Demand Bastionパターンと呼ぶらしい。

詳しい。「On Demand Bastionパターン」か。なるほど。

vpcへの踏み台サーバー
ポートフォワーディング、トンネルなどと同じ意味。

Network ACL

インスタンス単位じゃなくサブネット単位でより制限してセキュアにしたい場合に使うのかな。

安全なVPC設計 — Commerce Hack
http://tech.degica.com/ja/2016/01/07/designing-vpc-and-subnets/


結局どうするのか、、ひとまずNATはつかわずに、Bistionをつくってみる感じかな。

アベイラビリティーゾーン

リージョンごとでの、障害などで全部やられないように物理的にセグメントされた範囲の単位かな。
RDSではセグメントグループに2つ以上のゾーンを含める。でも、一つしか使わなくていい。ということか。s

RDSのVPC間の移動

サブネットグループの関連付けを変えればいいらしい。間違って設定したので移動した。

【小ネタ】知っていましたか?RDSを別のVPCに移動できることを | Developers.IO
http://dev.classmethod.jp/cloud/aws/rds_can_move_to_another_vpc/

Bastion作成作業をしてみる

主に下記を参考。

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-minimum-vpc-server-environment/

  • サブネットってなんだっけとか復習。
  • ストレージはどうするのか。
    • とりあえずssdにしたけどマグネティックでよかったかなあ。

      • ssd:$0.12 : 1 か月にプロビジョニングされたストレージ 1 GB あたり
      • マグネティック: 0.05 USD/GB-月
  • public IPは設定必要だよね
  • market placeからamiを取得した方がいいの?
    • とりあえず公式のウィザードを使ったけど。
  • 認証にIAMが追加されていたので使ってみた
    • これとは別にキーペアは必要ってことかな。
  • CFnテンプレート(CloudFormationテンプレート)というのがあるらしい。。
    • これでつくりなおそうかな。。
  • サブネットとかいろいろネットワーク系の設定
    • なんだかんだいっていろいろあった
  • セキュリティグループ
    • エイリアスみたいなセキュリティグループにできたらいいのに。タグや名前で明示化かな。
    • bastionは22をあけて、rdsは5432をbastionからのみあける
  • ログイン
  • DNS
    • あれ、パブリックDNSがうまく割り振ってないな。。
      AWSでPublic DNS(パブリックDNS)が割り当てられない時の解決法 – Qiita
      http://qiita.com/sunadoridotnet/items/4ea689ce9f206e78a523
    • RDSのDNS
      • nslookupしたら内部ipがかえってくるのね。接続できないけどなんか気持ち悪いな。これかな。。
        外部からdnsを引けることを気にしている人は見かけなくて便利だからって話なのかね。
        【AWS】VPC内でPrivate DNSによる名前解決 – Qiita
        http://qiita.com/y_takeshita/items/2eb5e6abb5eb5516d1de

やってるうちはいいけど、しばらくやらないと設定の方法とか忘れそう。。こういうのは学習コストだけじゃないな。

PlantUMLで図にしておく

Kobito.hQIwJs.png

VPC内のRDSへLambdaから接続。。

しまった!アンチパターンだそうだ。。

Lambda+RDSはアンチパターン – Qiita
http://qiita.com/teradonburi/items/86400ea82a65699672ad

Lambda + RDS benchmark – Qiita
http://qiita.com/taruhachi/items/3f95ae3e84f56edb3787

新し目の記事でIAM認証でクリアできそうな。。

【全世界待望】Public AccessのRDSへIAM認証(+ SSL)で安全にLambda Pythonから接続する – サーバーワークスエンジニアブログ
https://blog.serverworks.co.jp/tech/2017/04/27/rds-iam-auth-lambda-python/


セキュアに接続するのと速度のトレードオフになっていたのが
IAM認証のおかげで両方可能になったということっぽい。
でも、ネットのスループット、コネクション数(料金・負荷)、など、、ほかにも気にすることが出て来そうで若干不安。
非同期でよければキューイングして一回投げっぱなしすればどうだろう。
もしくは、似てるけど、Lambdaから一回値を返してもらってからRDSへ投げ直す。
これでいっかなあ。。Lambdaの意味がなくなる?うーん。

今後

  • 疑問としてrdsなど内部向けのdnsを外から見れなくできないものか。
  • というか、rdsのエンドポイントって再起動したら変わったりしないかね。ipは固定されるのか。
    • たぶん、サブネット内でdhcpになるのでipは変動するけどエンドポイントは固定。。じゃないかしら。

posgresqlをつかうための情報

Amazon RDS 上の PostgreSQL – Amazon Relational Database Service
http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.SSL

続きを読む

Amazon ECSを用いたDocker本番運用の実現

はじめに

現在お手伝いしているアカウンティング・サース・ジャパンにて、ECSを使ったDockerの本番運用を始めたので、その一連の流れについてまとめました。

税理士向け会計システムを扱うアカウンティング・サース・ジャパンでは最近Scalaでの新規プロジェクトが立ち上がってきており、既存のプロジェクトはJavaであったり、Erlangであったりと様々な言語が用いられていますが、インフラ人員が少ないということもあり、なるべくシンプルなインフラ構成を実現する必要がありました。

そういった中、各アプリケーションをDocker化することでインフラとしては共通基盤としてのDockerクラスタのみの管理になり、運用コストが下がるのではないかという仮説からDocker化を進めることになりました。クラスタを実現するに辺りKubenatesなどの選択肢もありましたが、今回はECSを選択し、下記のようにAWSのマネージドサービスを最大限に活用しています。

  • オーケストレーションツール: Amazon EC2 Container Service (ECS)
  • サービスディスカバリ: Application Load Balancer (ALB)
  • Dockerレジストリ: Amazon ECR
  • ログ、メトリクス収集: CloudWatch, CloudWatch Logs
  • 監視: CloudWatch Alarms
  • Infrastructure as Code: CloudFormation
  • CIツール: Jenkins

各技術の選定理由

今回Docker化を行うに辺り、下記を優先的に技術選定を行いました。

  • 運用が楽であること
  • 構成がシンプルで、技術の学習コストが低いこと

まずは、オーケストレーションツールの選定です。候補に上がったのは、Docker Swarm、Kubernetes、ECSです。

DockerのSwarm modeは本番での運用例が技術選定時点であまり見当たらなかったので候補から落としました。次にKubernetesとECSですが、海外の事例などではどちらも多く使われているようです。

今回は多機能さよりも運用に手間がかからない方が良いと考え、マネージドサービスであるECSが第一候補にあがりました。ここは詳細に調査したというよりも、ある種勢いで決めています。その上でやりたいことが実現できるかどうか一つ一つ技術検証を行った上で導入判断を行いました。

同じようにマネージドサービスを優先的に使ったほうが良いという考えで、ログなどでもCloudWatchを使っています。

AWSインフラをコードで記述するものとしてはTerraformが良く取り上げられている気がしますが、個人的にはいくつかの理由でCloudFormationを推しているのでこちらを使っています。

CIツールですが、社内の標準であるJenkinsをそのまま使うことにしました。

全体構成

下記のような構成になっています。

スクリーンショット 2017-05-21 12.46.39.png

ざっくりと説明すると、developmentブランチにプッシュするとGithub HookでJenkinsがDockerイメージをビルドして、ECRにPushします。ユーザはJenkinsでDeployジョブを実行(あるいはBuildの後続ジョブとして自動実行)し、CloudFormationにyamlファイルを適用することでTask, Service, ALB, Route53設定, CloudWatch設定を一通り実行します。またECSのClusterはあらかじめCloudFormationテンプレートを作成して作っておきます。

Task/Serviceの更新についてはCloudFormationを経由しない方がシンプルかとは思いまいしたが、Service毎に管理するRoute53やCloudWatchと合わせて一つのテンプレートにしてしまうのが良いと判断しました。

ここまでやるなら専用のデプロイ管理ツールを作った方がとも思ったのですが、業務委託という立場で自分しかメンテができないものを残すものは躊躇されたため、あくまでAWSとJenkinsの標準的な機能を組み合わせて実現しています。

CloudFormationテンプレートの解説

上記の流れが全てなので理解は難しくないと思いますが、一連の処理で重要なポイントとなるのはCloudFormationテンプレートなのでこれについてだけ触れておきます。長いテンプレートなのでざっくりとだけ雰囲気を掴んでもらえればと思います。

ECSクラスタのテンプレート

cluster作成用のCloudFormationテンプレートは下記のようになっています。

gist:cluster.yaml

一見複雑に見えますが、Amazon EC2 Container Service テンプレートスニペットを参考に作ると簡単に作成できると思います。

(あまりそのまま書くと会社に怒られそうなため)省略していますが、実際にはここにECSクラスタの監視を行うCloudWatch Alarmなどを設定することで、監視設定までこのテンプレートだけで完了します。

ECSクラスタはインフラチーム側であらかじめ用意しておき、リソースが足りなくなったときなどには適宜インスタンス数を変更したりクラスタ自体を別途作ったりしていきます。オートスケーリングを導入すればそれすら必要なくなります(今回はDocker運用が初めてだったので知見がたまるまで手動での対応にしています)。

インフラ側としての責務はここまでで、下記のテンプレートで定義される個別のサービスについてはアプリ開発者側の責務として明確に責任境界を分けました。(もちろん実際にはサポートはかなりの部分でしています。)

これにより全員が今までよりインフラに近い領域まで意識するように個人の意識が変わっていくことを期待しています。

個別サービス用テンプレート

開発環境、ステージング環境、プロダクション環境などそれぞれで同一のテンプレートを使うようにし、パラメータを使用します。そのパラメータをJenkinsのジョブ内で注入することで実現します。VPCなどの環境で決まる値はJenkinsジョブで実行するスクリプト内で定義し、アプリケーションごとの値は environment.yaml というファイルを用意してスクリプトから読み込みます。

environment.yamlは例えば下記のようになっています。アプリケーション開発者は、特殊なことをしない限りは service.yaml をインフラチームが用意したservice.yamlをコピーして、environment.yamlだけ編集すれば良い形になっています。DSLですら無いのでアプリ側のメンバーも心理的な抵抗が少ないようで良かったです。

environment.yaml
images:
- xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-image
parameters:
  default:
    TaskMemory: 512
    TaskMaxMemory: 990
    ImageRepositoryUrl: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-image
    ServiceDesiredCount: 1
  dev:
    ClusterName: dev-default
    JavaOpts: "-Xmx256MB"
  stg:
    ClusterName: stg-default
    JavaOpts: "-Xmx256MB"
  prod:
    ClusterName: default
    JavaOpts: "-Xmx1500MB -Xms1500MB"
    TaskMemory: 1990
    TaskMaxMemory: 1990
    ServiceDesiredCount: 2

そして service.yaml は下記のようなファイルです。

gist:service.yaml

これもAmazon EC2 Container Service テンプレートスニペットから作ればすぐにできるのではないかと思います。(もちろん全てのパラメータは一つ一つ値を検討します。)

こちらもCloudWatch周りや重要でないところは削除しています。色々と手で削ってるのでコピペだと動かない可能性大ですが雰囲気だけ掴んで貰えればと思います。

このファイルは全アプリケーションで同一ファイルを使うのではなく、アプリケーションごとにコピー/編集して利用します。全体の変更を行うときには全プロジェクトのファイルを更新しなければいけませんが、共通基盤がアプリケーション側を制約しないように、プロジェクト毎のyamlファイル管理としています。ファイルの配置場所は各Gitリポジトリに配置するのが理想ですが、現状ではDocker運用になれてくるまで全てのyamlファイルを管理するリポジトリを作成してインフラチーム側が主に編集する形を取っています。

デプロイ

あとは、このservice.yamlとenvironment.yamlを組み合わせてデプロイするRubyスクリプトでもJenkinsのPipelineのコードでも適当に書いてJenkinsのJobを登録すれば完了です。(environment.yamlファイルを読み込んで aws cloudformation create-stack でservice.yamlと共にパラメータとして渡すだけなので簡単です!)

新規アプリ開発時も社内標準のservice.yamlとenvironment.yamlをファイルを持ってきて、environment.yamlを修正した上で、Jenkinsにジョブを登録すればすぐにDockerクラスタへのデプロイ準備が整います。しかも、上記のテンプレート例では割愛していますが、テンプレートには監視項目/通知設定まで書かれているので、インフラ側で設定を行う必要もなく監視が開始されます。CloudFormation最高ですね。

おわりに

実際の運用ではミッションクリティカルなアプリケーションならではの品質管理のために、JenkinsのPipeline機能を利用して開発→検証→リリースまでのデプロイメントパイプラインを実現しています。

アプリケーションのSECRETなどコミットしない情報をどう管理するかも検討する必要がありますが、これは管理の仕方はチームによって異なると思ったため割愛しています。

また、ログ解析としてはS3に出されたALBのログをRedash+Amazon Athenaでエラー率やアクセス数を分析できるようにし、CPU使用率やメモリ使用率などのパフォーマンス状況をCloudWatchの内容をGrafanaで可視化しています。これによりログ収集の基盤などを作らずに必要な可視化を実現することができました。ベンチャーでは分析基盤の運用も大きなコストになってしまうため、こういった工夫も必要です。(もちろん重要なKPIについては別途分析する仕組みが整っています。)

今回の構成が最高とは思いませんが、ある程度満足行くところまではできたかなと思います。もっとよくできるよ!とか一緒にやりたいな!とかもっと詳細聞きたいな!いう方はぜひ @miyasakura_ までご一報ください。

続きを読む

AWS IoTとは

AWS IoTに関する基本的な内容をまとめてみたものです。AWS IoTに関する、Web上にすでにある解説コンテンツをまとめたサイトの抜粋です。
AWS IoTとは

AWS IoTとは

AWS IoSサービスにより、さまざまなデバイスを AWS の各種 Services や他のデバイスに接続し、データと通信を保護し、デバイスデータに対する処理やアクションを実行することが可能になります。

AWS IoTデバイスSDK
AWS IoT には、ハードウェアデバイスやモバイルアプリケーションを簡単に、すばやく接続できるようサポートする SDK が準備されています。AWS IoT デバイス SDK を使用すれば、AWS IoT との間で MQTT、HTTP、または WebSockets プロトコルを介した接続、認証、メッセージ交換が可能になります。このデバイス SDK では C、JavaScript および Arduino がサポートされており、クライアントライブラリ、開発者ガイドおよびメーカー向けの移植ガイドが付属しています。

認証と認可
AWS IoT では、接続するすべてのポイントでの相互認証と暗号化が提供されており、デバイスと AWS IoT 間では身元が証明されたデータのみが交換されます。

コミュニケーションプロトコル
AWS IoT とデバイスの間では MQTT、HTTP、または WebSockets プロトコルを介した接続、認証、メッセージ交換が可能です。MQTTは、M2M/IoTでよく使用されるOASISスタンダードのプロトコル。ライトウェイトでリソースや回線帯域が限られているデバイスでよく利用されます。MQTTはhttpsと比較して、スループットで93倍、メッセージ送信の消費電力で1/12、メッセージ受信の消費電力で1/180となります。

ルールエンジンによって、AWS Lambda、Amazon Kinesis、Amazon S3、Amazon Machine Learning、Amazon DynamoDB、Amazon CloudWatch、および Amazon Elasticsearch Service (組み込みの Kibana と統合されている) などの AWS エンドポイントへのメッセージのルーティングも行えます。

AWS IoTのコンポーネント

デバイスゲートウェイ
接続されたデバイスから、指定されたトピックについて複数の受信者にデータをブロードキャストすることができます。デバイスゲートウェイでは MQTT、WebSocket、および HTTP 1.1 プロトコルがサポートされており、専用プロトコルやレガシープロトコルのサポート実装も容易に行えます。デバイスゲートウェイは、インフラストラクチャのプロビジョニングが不要でありながら、10 億台以上のデバイスにも対応できるよう自動的にスケールされます。

レジストリ
レジストリによって、デバイスの ID が確定され、デバイスの属性や機能といったメタデータが追跡されます。各デバイスには、デバイスのタイプや接続方法にかかわらず、一貫した形式の一意の ID がレジストリによって割り当てられます。

シャドウ
AWS IoT では、それぞれのデバイスについて「シャドウ」を作成できます。シャドウにはデバイスの最新の状態が保存されるため、アプリケーションや他のデバイスからのメッセージの読み出し、およびデバイスとの通信が実行できます。デバイスのシャドウには、デバイスがオフライン状態のときでも、各デバイスについて最後に報告された状態と、希望する今後の状態が保持されます。最後に報告された時点の状態の取得や、希望する今後の状態の設定は、API またはルールエンジンによって実行できます。

ルールエンジン
ルールエンジンによって、接続されたデバイスによって生成されるデータを収集し、処理し、分析し、データに基づいたアクションを実行するアプリケーションを構築することが可能になります。ルールエンジンでは、お客様が定義したビジネスルールに基づいて、AWS IoT に向けて発行された入力メッセージが評価、変換され、別のデバイスやクラウドサービスへと配信されます。1 つのデバイスからのデータにも、多数のデバイスからのデータにも同じルールを適用でき、アクションを単独で実行することも、多数のアクションを並行して実行することも可能です。

ルールエンジンによって、AWS Lambda、Amazon Kinesis、Amazon S3、Amazon Machine Learning、Amazon DynamoDB、などの AWS エンドポイントへのメッセージのルーティングも行えます。AWS Lambda、Amazon Kinesis および Amazon Simple Notification Service (SNS) を使用して外部のエンドポイントに届けることも可能です。

KinesisとIoTの違い

Kinesisを使ってIoTのシステムを実現している例もありますが、IoTサービスはより包括的にデバイスとクラウドを接続するためのサービスを用意していますので、まずはデバイスとの入り口をIoTサービスで対応して、Kinesisサービスにつなぐという構成が、AWSでのIoTシステムの構成かと思います。
具体的なKinesisとIoTの違いとして一番明確なのは、デバイスとの間のコミュニケーションプロトコルです。IoTはMQQTに対応していますが、Kinesisはhttpsのみとなります。IoTはシャドウのような仕組みも持っており、IoTシステムを構築するためのトータルなシステムを組み上げる仕組みを包括的に用意しています。

デバイスデータに対するさまざまな処理を簡単に実現する仕組み

AWS IoTでは、「ルールエンジン」に定義したビジネスルールに基づいて、デバイスデータを迅速にフィルタリング、変換、活用することができます。各種処理は、AWS Lambda、Amazon Kinesis、Amazon S3、Amazon Machine Learning、Amazon DynamoDB、Amazon CloudWatch、および Amazon Elasticsearch Service といった AWS の各種サービスを呼び出すことにより実行させます。

各種AWSのサービスには、「ルールエンジン」がAWS IoTにPublishされたメッセージを評価し、ルールに基づいて配信されます。

また、Amazon Kinesis、AWS Lambda、Amazon S3などのサービスを経由して、外部のエンドポイントを呼び出すことも可能です。

(例) センサデータをKinesisを経由してエンタプライズアプリケーションへ

・各種デバイスからのセンサデータをIoTにて受信
・ルールエンジンにて、受信したセンサデータへのフィルタリングおよびデータ変換を実施
・AWS Kinesisにストリームデータとしてパブリッシュ
・Kinesisで収集されたデータをデータベースやBIなどの分析アプリケーションに転送

例) デバイスデータをIoTからAmazon SNSを経由してスマホにプッシュ通知

・各種デバイスからのデータやイベント通知をIoTで受信
・スマホに通知すべき条件判定をIoTのルールエンジンにて実行
・通知条件に合致する場合には、Amazon SNSを経由して、アップル社APNS、Google社のGCMに
・配信リクエストを送信することにより、スマホへのプッシュ通知として、デバイスのデータやイベントの通知を実現

続きを読む

AWS ECSにてカスタムしたredmineのdockerイメージを動かすまでのメモ(その1)

redmineの構築、プラグインの導入をふつーにやると面倒くさい。
あと、一旦構築した後redmineのバージョンをあげるのもやっぱり面倒くさい。

→ので、dockerにてプラグインのインストールやらなにやらを手順をコード化して簡単にRedmineの導入が
できるようにしました。
なお動作環境はAWSのECS(Elastic Container Service)を使います。

大きな流れ

1.Dockerfileを用意
2.AWSにてElastic Container Service(ECS)のタスクを定義
3.ECSのインスタンスを用意

今回はまず1を用意します。

1.Dockerfileを用意

redmineの公式イメージがdockerhubにあるのでこれをもとにpluginを導入する手順を
dockerfile化していきます。

ポイントは2つです。
1.ベースのイメージはredmine:x.x.x-passengerを利用する
2.DBのマイグレーションが必要ないものはpluginsフォルダに配置し、マイグレーションが必要なものは別フォルダ(install_plugins)に配置。
→コンテナ起動時にマイグレーションを行うようdocker-entrypoint.shに記載しておきます。

インストールするプラグイン一覧

独断と偏見で入れたプラグインです。

No プラグインの名前 概要
1 gitmike githubの雰囲気のデザインテーマ
2 backlogs スクラム開発でおなじみ。ストーリーボード
3 redmine_github_hook redmineとgitを連動
4 redmine Information Plugin redmineの情報を表示可能
5 redmine Good Job plugin チケット完了したら「Good Job」が表示
6 redmine_local_avatars アイコンのアバター
7 redmine_startpage plugin 初期ページをカスタマイズ
8 clipboard_image_paste クリップボードから画像を添付できる 
9 Google Analytics Plugin 閲覧PV測定用
10 redmine_absolute_dates Plugin 日付を「XX日前」 ではなくyyyy/mm/ddで表示してくれる
11 sidebar_hide Plugin サイドバーを隠せる
12 redmine_pivot_table ピボットテーブルできる画面追加
13 redmine-slack 指定したslack channnelに通知可能
14 redmine Issue Templates チケットのテンプレート
15 redmine Default Custom Query 一覧表示時のデフォルト絞り込みが可能に
16 redmine Lightbox Plugin 2 添付画像をプレビューできる
17 redmine_banner Plugin 画面上にお知らせを出せます
18 redmine_dmsf Plugin フォルダで文書管理できる
19 redmine_omniauth_google Plugin googleアカウントで認証可能
20 redmine view customize Plugin 画面カスタマイズがコードで可能

Dockerfile

上記のプラグインをインストール済みとするためのDockerfileです。
なお、ベースイメージは最新(2017/05/19時点)の3.3.3を使用しています。

Dockerfile
FROM redmine:3.3.3-passenger
MAINTAINER xxxxxxxxxxxxxxxxx

#必要コマンドのインストール
RUN apt-get update -y 
 && apt-get install -y curl unzip ruby ruby-dev cpp gcc libxml2 libxml2-dev 
  libxslt1-dev g++ git make xz-utils xapian-omega libxapian-dev xpdf 
  xpdf-utils antiword catdoc libwpd-tools libwps-tools gzip unrtf 
  catdvi djview djview3 uuid uuid-dev 
 && apt-get clean

#timezoneの変更(日本時間)
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
ENV RAILS_ENV production

#gitmakeテーマのインストール
RUN cd /usr/src/redmine/public/themes 
 && git clone https://github.com/makotokw/redmine-theme-gitmike.git gitmike 
 && chown -R redmine.redmine /usr/src/redmine/public/themes/gitmike



#redmine_github_hookのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/koppen/redmine_github_hook.git

#Redmine Information Pluginのインストール
RUN curl http://iij.dl.osdn.jp/rp-information/57155/rp-information-1.0.2.zip > /usr/src/redmine/plugins/rp-information-1.0.2.zip 
 && unzip /usr/src/redmine/plugins/rp-information-1.0.2.zip -d /usr/src/redmine/plugins/ 
 && rm -f /usr/src/redmine/plugins/rp-information-1.0.2.zip

#Redmine Good Job pluginのインストール
RUN curl -L https://bitbucket.org/changeworld/redmine_good_job/downloads/redmine_good_job-0.0.1.1.zip > /usr/src/redmine/plugins/redmine_good_job-0.0.1.1.zip 
 && unzip /usr/src/redmine/plugins/redmine_good_job-0.0.1.1.zip -d /usr/src/redmine/plugins/redmine_good_job 
 && rm -rf /usr/src/redmine/plugins/redmine_good_job-0.0.1.1.zip

#redmine_startpage pluginのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/txinto/redmine_startpage.git

#Redmine Lightbox Plugin 2 Pluginのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/peclik/clipboard_image_paste.git

#Google Analytics Pluginのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/paginagmbh/redmine-google-analytics-plugin.git google_analytics_plugin

#redmine_absolute_dates Pluginのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/suer/redmine_absolute_dates

#sidebar_hide Pluginのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/bdemirkir/sidebar_hide.git

#redmine_pivot_tableのインストール
RUN cd /usr/src/redmine/plugins 
 && git clone https://github.com/deecay/redmine_pivot_table.git

#redmine-slackのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/sciyoshi/redmine-slack.git redmine_slack

#Redmine Issue Templates Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/akiko-pusu/redmine_issue_templates.git redmine_issue_templates

#Redmine Default Custom Query Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/hidakatsuya/redmine_default_custom_query.git redmine_default_custom_query

#Redmine Lightbox Plugin 2 Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/paginagmbh/redmine_lightbox2.git redmine_lightbox2

#redmine_banner Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/akiko-pusu/redmine_banner.git redmine_banner

#redmine_dmsf Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/danmunn/redmine_dmsf.git redmine_dmsf

#redmine_omniauth_google Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/yamamanx/redmine_omniauth_google.git redmine_omniauth_google

#redmine_omniauth_google Pluginのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/onozaty/redmine-view-customize.git view_customize

#redmine_local_avatars用モジュールを用意(インストールはredmine起動時に実施)
RUN cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/ncoders/redmine_local_avatars.git

#backlogsのインストール用モジュールを用意(インストールはredmine起動時に実施)
RUN mkdir /usr/src/redmine/install_plugins 
 && cd /usr/src/redmine/install_plugins 
 && git clone https://github.com/AlexDAlexeev/redmine_backlogs.git 
 && cd /usr/src/redmine/install_plugins/redmine_backlogs/ 
 && sed -i -e '11,17d' Gemfile

#database.ymlファイルを置くフォルダを用意
RUN mkdir /config
COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]

EXPOSE 3000
CMD ["passenger", "start"]

docker-entrypoint.sh

次にdocker-entrypoint.shファイルです。
githubに公開されているファイル
(https://github.com/docker-library/redmine/blob/41c44367d9c1996a587e2bcc9462e4794f533c15/3.3/docker-entrypoint.sh)
を元にプラグインのインストールを行うコードを記載していきます。

docker-entrypoint.sh
#!/bin/bash
set -e

case "$1" in
    rails|rake|passenger)
        if [ -e '/config/database.yml' ]; then
                    if [ ! -f './config/database.yml' ]; then
                echo "use external database.uml file"
                ln -s /config/database.yml /usr/src/redmine/config/database.yml
            fi
        fi
                if [ -e '/config/configuration.yml' ]; then
                        if [ ! -f './config/configuration.yml' ]; then
                                echo "use external configuration.uml file"
                                ln -s /config/configuration.yml /usr/src/redmine/config/configuration.yml
                        fi
                fi
        if [ ! -f './config/database.yml' ]; then
            if [ "$MYSQL_PORT_3306_TCP" ]; then
                adapter='mysql2'
                host='mysql'
                port="${MYSQL_PORT_3306_TCP_PORT:-3306}"
                username="${MYSQL_ENV_MYSQL_USER:-root}"
                password="${MYSQL_ENV_MYSQL_PASSWORD:-$MYSQL_ENV_MYSQL_ROOT_PASSWORD}"
                database="${MYSQL_ENV_MYSQL_DATABASE:-${MYSQL_ENV_MYSQL_USER:-redmine}}"
                encoding=
            elif [ "$POSTGRES_PORT_5432_TCP" ]; then
                adapter='postgresql'
                host='postgres'
                port="${POSTGRES_PORT_5432_TCP_PORT:-5432}"
                username="${POSTGRES_ENV_POSTGRES_USER:-postgres}"
                password="${POSTGRES_ENV_POSTGRES_PASSWORD}"
                database="${POSTGRES_ENV_POSTGRES_DB:-$username}"
                encoding=utf8
            else
                echo >&2 'warning: missing MYSQL_PORT_3306_TCP or POSTGRES_PORT_5432_TCP environment variables'
                echo >&2 '  Did you forget to --link some_mysql_container:mysql or some-postgres:postgres?'
                echo >&2
                echo >&2 '*** Using sqlite3 as fallback. ***'

                adapter='sqlite3'
                host='localhost'
                username='redmine'
                database='sqlite/redmine.db'
                encoding=utf8

                mkdir -p "$(dirname "$database")"
                chown -R redmine:redmine "$(dirname "$database")"
            fi

            cat > './config/database.yml' <<-YML
                $RAILS_ENV:
                  adapter: $adapter
                  database: $database
                  host: $host
                  username: $username
                  password: "$password"
                  encoding: $encoding
                  port: $port
            YML
        fi

        # ensure the right database adapter is active in the Gemfile.lock
        bundle install --without development test
        if [ ! -s config/secrets.yml ]; then
            if [ "$REDMINE_SECRET_KEY_BASE" ]; then
                cat > 'config/secrets.yml' <<-YML
                    $RAILS_ENV:
                      secret_key_base: "$REDMINE_SECRET_KEY_BASE"
                YML
            elif [ ! -f /usr/src/redmine/config/initializers/secret_token.rb ]; then
                rake generate_secret_token
            fi
        fi
        if [ "$1" != 'rake' -a -z "$REDMINE_NO_DB_MIGRATE" ]; then
            gosu redmine rake db:migrate
        fi

        chown -R redmine:redmine files log public/plugin_assets

        if [ "$1" = 'passenger' ]; then
            # Don't fear the reaper.
            set -- tini -- "$@"
        fi
                if [ -e /usr/src/redmine/install_plugins/redmine_backlogs ]; then
                        mv -f /usr/src/redmine/install_plugins/redmine_backlogs /usr/src/redmine/plugins/
                        bundle update nokogiri
                        bundle install
                        bundle exec rake db:migrate
                        bundle exec rake tmp:cache:clear
                        bundle exec rake tmp:sessions:clear
            set +e
                        bundle exec rake redmine:backlogs:install RAILS_ENV="production"
                        if [ $? -eq 0 ]; then
                echo "installed backlogs"
                                touch /usr/src/redmine/plugins/redmine_backlogs/installed
            else
                echo "can't install backlogs"
                        fi
            set -e
            touch /usr/src/redmine/plugins/redmine_backlogs/installed
        fi
                if [ -e /usr/src/redmine/install_plugins/redmine_local_avatars ]; then
                        mv -f /usr/src/redmine/install_plugins/redmine_local_avatars /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
                fi
        if [ -e /usr/src/redmine/install_plugins/redmine_slack ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_slack /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/redmine_issue_templates ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_issue_templates /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/redmine_default_custom_query ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_default_custom_query /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/redmine_lightbox2 ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_lightbox2 /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/redmine_banner ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_banner /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/redmine_dmsf ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_dmsf /usr/src/redmine/plugins/
            bundle install --without development test xapian
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/redmine_omniauth_google ]; then
            mv -f /usr/src/redmine/install_plugins/redmine_omniauth_google /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ -e /usr/src/redmine/install_plugins/view_customize ]; then
            mv -f /usr/src/redmine/install_plugins/view_customize /usr/src/redmine/plugins/
            bundle install --without development test
            bundle exec rake redmine:plugins:migrate RAILS_ENV=production
        fi
        if [ ! -f '/usr/src/redmine/plugins/redmine_backlogs/installed' ]; then
            set +e
            bundle exec rake redmine:backlogs:install RAILS_ENV="production"
            if [ $? -eq 0 ]; then
                echo "installed backlogs"
                touch /usr/src/redmine/plugins/redmine_backlogs/installed
            else
                echo "can't install backlogs"
            fi
            set -e
        fi
        set -- gosu redmine "$@"
                ;;
esac

exec "$@"

次はこのイメージをAWSのECSにて動作させます。
(次回に続く)

続きを読む

Amazon Elasticsearch Service へ Embulk を使ってデータロード

はじめに

AWS ElasticSearch Serviceはフルマネージドで運用要らず、今のところ(2017/5/18)他の追随を許さ無い魅力があります(筆者はElastic社のElastic Cloudは使った事がありません)
Azureの場合、MarketPlaceからElastic-Stackが提供されて完全占有クラスターが自動構築可能です。GCPもblogにある通り、Azureとほぼ同じ。完全占有クラスターのメリット?がありそうな反面、ElasticClusterの運用/管理がtrade-offとしてつきまといます。

当初はAzureで個別クラスターを使っていましたがトラブルが続き、運用などはしたく無いのでAWSに浮気してみましたのでその記録とTipsをまとめます

環境

  • 既存の物を使い回しでAzureのVMにembulkインストール、データロード等の制御に使います
  • データは Azure Blobに保存
  • AWS ElasticSearch Service (ver5.1が選択できたのでこれ)
  • 図解
    Load

セットアップ

実行

以下の手順で実施

  • BlobにElasticSearchに投入したいログをUploadしておく
  • embulkから吸い出し、ElasticSearch Serviceへ投げ込み用のconfig.yml作成
in:
  type: azure_blob_storage
  account_name: <BLOB NAME>
  account_key: <BLOB KEY>
  container: <CONTAINER NAME>
  path_prefix: <PREFIX as you like>
  decoders:
    - {type: gzip}
  parser:
    charset: UTF-8
    newline: CRLF
    type: jsonl
    schema:

out:
  type: elasticsearch_using_url
  mode: normal
  nodes:
    - url: "<YOUR ElasticSearch Domain>.us-east-2.es.amazonaws.com:80"
  index: "sample"
  index_type: "sample"
  • 実行
$ embulk preview config.yml
$ embulk run config.yml -l warn -r resume_state_aws.yml &>> embulk_awses.log

確認

  • AWS consoleで確認
    Load OK

まとめ

  • ElasticSearchへの大量のデータロードにはembulkは最適な手段
  • AWS ElasticSearch Serviceでは 9200 portが使えない為、embulk pluginの選択に注意、http経由で実施するのがおすすめ

余談

  • およその費用比較
AWS Azure GCP
費用 \$250 – $300 程度 \$530 未確認
  • VirtulMachineのサイズ、データ量は1TB程度

    • AWS

      • EC2: t2.midium x3 , \$150
      • EBS: 1TB, \$100
      • データ通信料
    • Azure
      • VM: D2v2 , \$84.82 x6 = $505.44
      • Elastic-Stack構築時、VMサイズを選択可能だが、D1v2(default)では使い物にならなかった
      • Blob: 1TB , \$24
      • データ通信料
    • GCP
      • 未検証

続きを読む

AWS Elastic Beanstalkでmonit監視するための注意点

最近、インフラのお仕事をしています

Ruby on railsのアプリケーションでよしなにキューを使いたいという用件があり、shoryukenを使っておりました
sidekiqでいいんじゃね、というツッコミはなしにして、インフラ担当の自分はデプロイ時下記の要件を満たすインフラを作る必要がありました

  • shoryukenのジョブが立ち上がっていなかったら立ち上げる
  • shoryukenのジョブが落ちたら復旧する

元々Elastic Beanstalkで構成していたのですが、上記の用件を満たすために .ebextensions で実現しようとしましたがなかなかハマったのでメモを残しておきます

shoryukenを立ち上げるために

shoryukenを立ち上げるために下記のファイルを配置しました

 .ebextensions/shoryuken.config
files:
  "/opt/elasticbeanstalk/hooks/appdeploy/post/50_restart_shoryuken":
    mode: "000777"
    content: |
      . /opt/elasticbeanstalk/support/envvars
      . /opt/elasticbeanstalk/support/scripts/use-app-ruby.sh

      # undefined $HOME when execute this script, so abort `bundle exec shoryuken`
      HOME=/root; export HOME

      mkdir -p /var/app/support/pids
      mkdir -p /var/app/support/logs

      cd /var/app/current

      if [ -f /var/app/support/pids/shoryuken.pid ] && \
         ps aux | grep `cat /var/app/support/pids/shoryuken.pid` | grep 'shoryuken'
      then
        kill -TERM `cat /var/app/support/pids/shoryuken.pid`
        rm -rf /var/app/support/pids/shoryuken.pid
      fi

      sleep 10

      bundle exec shoryuken \
        -R \
        -P /var/app/support/pids/shoryuken.pid \
        -L /var/app/support/logs/shoryuken.log \
        -d

  "/opt/elasticbeanstalk/hooks/appdeploy/pre/03_mute_shoryuken":
    mode: "000777"
    content: |
      if [ -f /var/app/support/pids/shoryuken.pid ]
      then
        kill -USR1 `cat /var/app/support/pids/shoryuken.pid`
      fi

HOMEが定義されているのが渋いところなのですが、shoryukenを実行するコードの中にデフォルトで ~ にアクセスしているコード(ライブラリ側)があり、monitから叩かれる際には root なので、HOMEがない状態で、タスク自体がabortしてしまいます。ちょっと他に回避策が思いつかなかったので、緊急回避的にこうなっています。

monit管理下におく

 .ebextensions/monit.config
packages:
  yum:
    monit: []
commands:
  00_copy_monit_config:
    command: "cp /var/app/support/monit.d/shoryuken.conf /etc/monit.d/shoryuken.conf"
  01_monit_chkconfig:
    command: "/sbin/chkconfig monit on"
  02_monit_restart:
    command: "/sbin/service monit restart"
    ignoreErrors: true
files:
  "/var/app/support/monit.d/shoryuken.conf" :
    mode: "000644"
    owner: root
    group: root
    content: |
      check process shoryuken-proc with pidfile /var/app/support/pids/shoryuken.pid
      start program = "/usr/bin/sudo /opt/elasticbeanstalk/hooks/appdeploy/post/50_restart_shoryuken"
      stop  program = "/usr/bin/sudo /bin/kill -QUIT kill -TERM `cat /var/app/support/pids/shoryuken.pid` && rm -rf /var/app/support/pids/shoryuken.pid"

注目すべきは一番最初のcommandのcopyなのですが、これは直接 /etc/monit.d/shoryuken.conf を配置するとEBが気を利かせて過去ファイルを /etc/monit.d/shoryuken.conf.bak を配置してしまい monit が立ち上がらないという状態になってしまいます
これはデフォルトの monit.confが /etc/monit.d/* のファイルすべてをIncludeしており、過去ファイルまでIncludeするとプロセス名が被ってしまうためです

回避策として /etc/monit.d/ 外にファイルを一旦配置して、それをコピーすることにより
*.bak ファイルの影響を受けないようにしました

まとめ

下記の二点の気持ち悪い機転を聞かせた対応により、shoryukenを起動しプロセス監視できるようになりました

  • ダミーの環境変数定義
  • confファイルの一次退避

EBを使っていると遭遇する問題かと思いますので、何かの参考になりましたら幸いです

続きを読む

〇FLAGの中の人に憧れてMastodon×AWSでモンストドン作ってみた

Mastodon立ち上げたらいい会社に入れると聞いて、邪な気持ちで。。。いや、Mastodonとモンストって相性よさそうだなぁと思いたち、少し乗り遅れた感をかもしだしながら、フルにAWSを使って規模拡大しても大丈夫な構成で作ってみた。

モンストドン (https://monstdn.com)

構成

monstdn.png

最小構成のざっくり料金($1=113円、1ヶ月30日計算)

サービス 単価 月額料金
ALB 1台 × $0.0243/1H + データ転送的なの  約2000円 + α
EC2 2台(t2.nano) × $0.008/1H + データ転送的なの 約1300円 + α
RDS 1台(db.t2.micro シングルAZ) $0.028/1H + データ転送的なの 約2300円 + α
ElasticCache 1台(cache.t2.micro) $0.026/1H + データ転送的なの 約2100円 + α
S3Bucket $0.025/GB + リクエスト数的なの + α
SES $0.10/1,000通あたり + データ転送的なの + α
合計     ( 約7700円 + α なので ) ざっくり1万ぐらい

※無料枠があるので1年目はもう少しやすくできそう

やったこと

  • AWSのアカウント作成
  • IAMの作成とアカウントの初期設定(二段階認証とか、パスワードポリシーとか)
  • Route53でドメインを買う
  • SESでメール設定と制限解除申請
  • ACMの取得(無料でHTTPS通信)
  • S3バケット作成(画像とかのアップロードファイルの配信用)
  • VPCとセキュリティグループの作成
  • SES、S3へアクセスする為のIAMユーザの作成
  • ElasticCacheでRedisの作成
  • RDSでPostgreSQLの作成
  • EC2でCentOSを使ってMastodonの構築(下に詳細)とイメージ(AMI)の作成
  • AutoScallingの設定
  • ALB(ApplicationLoadBalancer)の作成(ACMをつける)
  • Route53でHostZoneのレコード設定

CentOSでのMastdon構築(20170517現在)

sudo su -
yum -y update
yum -y install vim

localectl set-locale LANG=ja_JP.utf8
localectl set-keymap jp106
localectl status

timedatectl set-timezone Asia/Tokyo
timedatectl status

dd if=/dev/zero of=/mnt/swapfile bs=1M count=2560
mkswap /mnt/swapfile
swapon /mnt/swapfile
chmod 0644 /mnt/swapfile
echo "/mnt/swapfile                             swap                    swap    defaults                0 0" >> /etc/fstab
free

vim /etc/sysconfig/selinux
 SELINUX=enforcing
 ↓
 SELINUX=disabled

systemctl disable postfix
systemctl disable auditd.service

yum -y install libxml2-devel ImageMagick libxslt-devel git curl nodejs file
yum -y install epel-release
rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
yum -y install ffmpeg ffmpeg-devel

yum -y group install "Development tools"
curl -sL https://rpm.nodesource.com/setup_4.x | sudo bash -

yum -y install nodejs
npm -g install yarn

yum -y install postgresql postgresql-contrib postgresql-devel
yum install -y openssl-devel readline-devel

useradd mastodon
passwd mastodon
su - mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src && cd ~
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile && source ~/.bash_profile
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install 2.4.1 && rbenv global $_ && rbenv rehash

# 確認
ruby -v

cd ~
git clone https://github.com/tootsuite/mastodon.git live
cd live
git checkout $(git tag | tail -n 1)

gem install bundler
bundle install --deployment --without development test
yarn install --pure-lockfile

cp .env.production.sample .env.production
sed -i "/^PAPERCLIP_SECRET=$/ s/$/`rake secret`/" .env.production
sed -i "/^SECRET_KEY_BASE=$/ s/$/`rake secret`/" .env.production
sed -i "/^OTP_SECRET=$/ s/$/`rake secret`/" .env.production

vim .env.production
#Redis,Postgresql,言語,SMTP,S3の設定

RAILS_ENV=production bundle exec rails db:setup
RAILS_ENV=production bundle exec rails assets:precompile

exit

cat << "_EOF_" > /etc/systemd/system/mastodon-web.service
[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_


cat << "_EOF_" > /etc/systemd/system/mastodon-sidekiq.service
[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

cat << "_EOF_" > /etc/systemd/system/mastodon-streaming.service
[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

systemctl enable mastodon-{web,sidekiq,streaming}
systemctl start mastodon-{web,sidekiq,streaming}

cat << "_EOF_" | crontab -
RAILS_ENV=production
@daily cd /home/mastodon/live && /home/mastodon/.rbenv/shims/bundle exec rake mastodon:daily > /dev/null
_EOF_

yum -y install nginx

cat << "_EOF_" > /etc/nginx/conf.d/mastodon.conf
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name {domainName};

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /home/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://localhost:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}
_EOF_

systemctl enable nginx
systemctl start nginx

# ユーザ登録後 admin設定
RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME={UserName}

メモ

EC2のDiskはSSDで(swapで使う)
ロードバランサーはApplicationの方じゃないとwebSocketがうまくいかない
コミュニティベースのシステムだからCloudFrontはあまり必要性感じなかったので使わなかった
(日本向けだしS3のバケット東京リージョンにあるし、S3もかなり性能いいし)
もしCloudFrontを使うなら、websocketできないからS3の前に置く感じ
今回CloudFrontの利点があるとすれば”ドメイン”が自分の使えることぐらいかな
CentOSじゃなくてAmazonLinux使いたかったけど、ffmpeg入れるのにやたら時間かかったからやめた。一応動いたけど(純正AWSが。。。)
DockerはDeployまで楽そうだけど、効率よくなさそうだったのでやめた
AWSでDocker使うならECSでやってみたいけど、Mastodonはすんなりできるのかなー
セキュリティ的にはロードバランサーからの80番ポートしか受け付けないように制御してるから大丈夫かな。
sshでのログインは同じVPC内に踏み台サーバ立ててと。

最後に

ここまで読んで頂きありがとうございます。
技術的なことを投稿するのはこれが初めてですが、だれかのお役にたてれたら嬉しいです。
普段はPHPとAWS少しいじる程度なのでいい勉強になりました。
ほとんど公開されている文献をもとにプラモデル感覚で作りましたので、ご指摘等あればコメント頂ければと思います。
個人でのサイト運用となりますので、落ちたらごめんなさい。

続きを読む