Docker with ECS × Railsを実現させるために考えたこと(デプロイ編)
こんにちは。エンジニアの志村です。
の続きとなります。 今回はタイトル通り、デプロイ辺りを執筆していければと思います。
Blue-Green Deployment
Blue-Green Deploymentはマーチン・ファウラー氏が提唱したデプロイ方式です。 blue, greenとほぼ同じ本番環境を2系統用意してデプロイを行います。このデプロイ方式ですが、
- ダウンタイムの極小化
- ロールバックが容易
という大きな2つの利点があります。
Dockerizeへのモチベーションとして前回の記事にも書いたのですが、徐々にドッグフーディング環境・本番環境で差異が出てきました。
そのような状況だと、ドッグフーディングでは正しく動作したのに本番環境では意図した挙動とは違うという場面も出てきます。
上記のようなトラブルがあった際に今までのIn place型のデプロイ方式だとロールバックに時間がかかり、ユーザーに迷惑をかけてしまう時間が長くなるという問題がありました。
Blue−Green Deploymentでは特に2.の「ロールバックが容易」という点が非常に魅力でした。
何かトラブルがあったとしてもELB設定を変えてしまえば瞬時に旧環境に戻せるという点で、デプロイに対する心理的ハードルが社内全体で下がりました。
また、ECSの仕組みにより今までのようにCapistranoによるSSHを用いたデプロイが難しかったという理由も相俟って、弊社でもBlue−Green Deploymentを用いております。
ECSによるBlue-Green Deployment
前回の記事にも書いた通り、ECSを使用した際のBlue−Green Deploymentにはいくつかやり方が存在します。
- ECSのMaximum healthy percent, Minimum healthy percentを調整し、徐々にタスクが切り替わるように調整する。
- CLB(Classic Load Balancer)× Autoscaling Groupを使用して切り替える
- Routes53を使用し、Standby, ActiveのELBを切り替える
- ALBに紐付いているTarget groupの優先順位を切り替える
ECSのMaximum healthy percent, Minimum healthy percentを調整し、徐々にタスクが切り替わるように調整する。
利点
- ECSデフォルトの機能を用いるので、他に余計な操作をする必要が一切ない
- タスクの増減を元にデプロイを行うのでインスタンス料金を低く抑えることが出来る
欠点
- デプロイがまずまず遅い?→ロールバックが遅い
ECSの特徴が最大限発揮されたデプロイ方式だと考えます。
ECSには「タスク」という概念があります。タスクはDocker imageを起動させる際の設定、またそのリビジョンです。
タスクは
- 使用するdocker image
- コンテナ間のリンク
- コンテナのコマンド
- データボリューム
等が設定できます。
リビジョンが用意されたdocker-composeだと考えると分かりやすいかと思います。
タスクに関しては公式のドキュメントが分かりやすいです。
Amazon ECS タスク定義
そのタスクを管理するのがサービスという概念です。
サービスはざっくり言うと、ロードバランサーとの連携、上記のタスクをインスタンスにどう配置するか、最大・最小・維持タスク数をいくつにするかの取り決めです。
ECSのサービスのデプロイメントオプションに、maximum helathy percent(最大率), minimum healthy percent(最小ヘルス率)という項目があります。
この値を調整することによって、Blue-Green Deploymentを実現することが出来ます。
例えば
* maximum healthy percent: 200%
* minimum healthy percent: 100%
と設定すれば、
デプロイ時に通常のタスク数の2倍、デプロイ終了後にタスク数1倍という形になります。
仮に1インスタンス1タスクという戦略であれば、デプロイ時に2倍のインスタンス数が必要ということになります。
* maximum healthy percent: 50%
* minimum healthy percent: 100%
個人的な感触ですが、既存のタスクをkillして新しいタスクに入れ替えるのに結構な時間がかかっている印象でした。
つまりは、ロールバック時にも同じ作業をしなければならない≒ロールバックに時間がかかると予想される為、今回は見送りました。
CLB(Classic Load Balancer)× Autoscaling Groupを使用して切り替える
利点
- 設定がほとんど必要ない。実装が楽
- ロールバックが早い
- ダウンタイム無し
欠点
- SpotFleetが使用できない
- Blue, Greenが重なるタイミングがある
恐らく現時点で最もポピュラーなBG Deploymentの方式かと思います。 実装もAutoscaling Groupを作成すればOKという簡便さです。
ALBを今回使用したいと考えていましたが、コンソールを見てもALBが使用できない雰囲気ではありました。 しかしながら、現在では下記のように使用できるようです。
これで課題は1つクリアしましたが、SpotFleetがAutoscaling Groupに登録出来ない為、今回この方式は断念しました。
ロールバックを容易かつスピーディにするにはインスタンスの数が単純に倍必要となります。SpotFleetの使用は予算の都合上、必要でした。
またこの方式だとヘルスチェックの観点から、Blue, Greenが重なるタイミングがどうしても出てきます。
マイグレーション・モデルの変更をした際に、旧稼働系・新稼働系が混ざるとエラーを引き起こし兼ねません。この観点からも今回は見送りました。
Route53を使用し、Standby, ActiveのELBを切り替える
利点
- ダウンタイムほぼ無し
- シンプル
欠点
- キャッシュを考慮するのが面倒
- ロールバックに時間がかかる
一番古典的かつ確実方式です。
Route53のWeightを利用し、Blue→GreenのELBに徐々に切り替える方式です。
実装がシンプルではありますが、一番の問題点はDNSキャッシュです。
キャッシュサーバによってはTTLを無視して一定期間キャッシュするものもあるらしいので、実際にデプロイが完全に完了したかを確認するのが難しくなります。
上記のようなキャッシュのコントロールの難しさから、今回この方式は見送りました。
ALBに紐付いているTarget groupの優先順位を切り替える
利点
- ダウンタイムほぼ無し
- ロールバックが早い
- メンテナンスにすぐ切り替えられる
欠点
- 実装が面倒
結論から言うとこの方式を採用しました。
いくらか利点はありますが、特にロールバックが容易な点が大きいかと思います。
ALBにはパスベースルーティング、ホストベースルーティングという機能があります。
パスベースルーティングは下記にある通り、パスによってどのターゲットグループにトラフィックを流すかを決定できます。
例えば、
IF: /target1/*, THEN: target1 IF: /target2/*, THEN: target2
のように設定すると、
host/target1/の際にはtarget1に紐付いたインスタンスに、
host/target2/の際にはtarget2に紐付いたインスタンスにトラフィックを分岐することが出来ます。
また、このルーティングでは優先順位というものがあります。
同じパスであっても優先順位が高い(数字が小さい)方にトラフィックが流すということが可能になります。この特徴を活かし、
優先順位 | パス | ターゲットグループ |
---|---|---|
1 | * | target-blue |
2 | * | target-green |
last | Requests otherwise not routed | target-maintenance |
上記のように設定しました。
こうすると通常、blueにトラフィックが流れます。
デプロイ時にこの優先順位を切り替えると、green系にトラフィックが変わります。
ロールバック時も同じように優先順位を変更するだけで、一瞬で切り替わります。
メンテナンスページを出したい場合、他のルーティングルールを削除すればOKです。
上述の通り、ALBの特徴を使用すれば一瞬でロールバックが可能になり、さらにメンテナンスページまで容易に出すことが出来ます。
欠点としてはこの部分の実装が面倒だという点です。
今回、harmonikというデプロイツールを用意しました。
harmonik
事情によりGithub等に公開はしておりません…(早くしたい)
harmonikは今回のBG Deploymentを使用するにあたって新しく作成したデプロイツール(CLI)です。
主に下記のような機能があります
- デプロイ
- 単発のタスク起動
- ロールバック
- ステータス表示
1.は他のECS向けデプロイツールとほぼ同じです。
ALBの優先順位からデプロイ先を決定 ↓ task definitionを更新 ↓ serviceを更新 ↓ taskがRUNNING状態かどうか判別 ↓ target groupのヘルスチェックがhealthyかどうか判別 ↓ ALBの優先順位切り替え
上記のようにしてBG Deploymentを実現しております。
2.はマイグレーション等の単発のタスクに使用します。
CIでは、単発のタスク→Blue or Greenにデプロイ
という流れをとっております。
3.は単純で、ALBの優先順位を変更してロールバックさせているだけです
4.は稼働系のステータス(クラスタ名・インスタンス数・デプロイ履歴等)を表示させるものです。
以上になります。
この実装により、Capistranoとほぼ同じような感覚で簡単にデプロイをすることが可能になりました。
Dockerの運用に切り替えてからサーバのメンテナンス等に時間を割かなくて済むようになったので非常に運用が楽になりました。
※まだ運用から日が浅く、間違えている箇所があるかもしれません。
その際はお手数ですがコメント頂けますと幸いです。
Ruby on Railsにwebpackを使ってPostCSSを導入してみた【ローカル環境編】
こんにちは、エンジニアの井戸田です。
今回はモダンなCSS環境を構築するために流行っていると言われているPostCSS×webpackをRailsに導入してみました。
今回は開発環境だけですが、次回本番運用に関して書いていきたいと思います。
- 環境
- PostCSSとは
- Railsプロジェクトを作る
- webpack環境のためのディレクトリを作成
- npmで必要なライブラリをインストール
- webpack.config.jsにビルドの設定を書く
- Rails側の設定
- テスト
- PostCSSのプラグインの導入方法
- PostCSSのプラグインの紹介
- We’re hiring!!
環境
PostCSSとは
PostCSSはJSプラグインでスタイルを変換するためのツールです。特徴として下記のことが挙げられます。
- PostCSS自体が変換するのではなくCSSパーサーとASTを操作するためのAPIを提供しているだけで、実際にCSSに変換するのはPostCSSのプラグインが行います。
- Sassでよく使う
mixin
extend
変数($)
などの機能がそれぞれ1つ1つのプラグインになっており、それらを入れることで使用が可能になります。 - メタ言語の中でも特にPostCSSは速度が速いです。
ref) https://github.com/postcss/benchmark
Railsプロジェクトを作る
$ rails new sample-postcss -BJ $ bundle install --path vendor/bundle
とりあえず $ bundle exec rails s -b 0.0.0.0 -p 3000
webpack環境のためのディレクトリを作成
プロジェクトルートに client
というディレクトリを作成。ここがwebpack環境のためのディレクトリになっています。
clientディレクトリの構成については下記のようになります。
client |-- webpack.config.js |-- node_modules |-- package.json └── src |-- images |-- javascripts └── stylesheets
npmで必要なライブラリをインストール
client
ディレクトリに移動し、下記のコマンドを入力して package.json
を生成します。
$ npm init -y
webpackをインストール。webpackでは1系を使用したかった為、バージョンの指定をしています。
$ npm install -D webpack@1.14.0
ローカル環境では下記ことを可能にする webpack-dev-server
が、便利なので使っていきます。 webpack-dev-server
はポート8080を使うNode.jsのexpressサーバーです。
- ファイルの変更を検知して自動でビルドをしたのち、ブラウザを自動でリロードする
- HMR(
Hot Module Replacement
)という編集したモジュールを自動で更新する
$ npm install -D webpack-dev-servser
cssやpostcssに必要な style-loader
css-loader
postcss-loader
モジュールと、CSSをJavaScriptでロードせずにlinkタグでロードさせるため、 extract-text-webpack-text
をインストール。
$ npm install -D style-loader css-loader postcss-loader extract-text-webpack-plugin@1.0.1
webpack.config.jsにビルドの設定を書く
- デバッグ用のSourceMapの設定
- entryポイントの設定
- 出力先を設定
- トランスパイルの設定
exstract-text-webpack-plugin
の設定webpack-dev-server
でファイル変更した際に検知し、自動でビルド、自動でブラウザをリロードしてくれるように設定
client/webpack.config.js
/** * Require basic plugins */ const path = require('path'); const webpack = require('webpack'); /** * webpack plugins */ const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { devtool: 'inline-source-map', context: path.join(__dirname + '/../client/src'), entry: { application: [ 'webpack-dev-server/client?http://localhost:8080', 'webpack/hot/dev-server', './javascripts/application.js' ] }, output: { filename: '[name].js', publicPath: 'http://localhost:8080/assets/' }, module: { loaders: [ { test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css!postcss') } ] }, plugins: [ new ExtractTextPlugin('[name].css'), new webpack.HotModuleReplacementPlugin() ], devServer: { headers: { 'Access-Control-Allow-Origin': 'http://localhost:3000', 'Access-Control-Allow-Credentials': 'true' } } };
webpack-dev-serverの起動用にpackage.jsonに追記
client/package.json
{ ... "scripts": { "dev": " ./node_modules/.bin/webpack-dev-server --debug --hot --inline --devtool --public --host 0.0.0.0 --port 8080 --config ./webpack.config.js", ... }, ... }
webpack.config.js
のentryは client/src/javascripts/application.js
なので、 application.js
に client/src/stylesheets/application.css
をrequireさせます。
画像などもwebpackで配信したい場合は、同様に application.js
にrequireさせます。
client/src/javascripts/application.js
/** * Require stylesheets */ require('../stylesheets/application.css');
Rails側の設定
headタグ内のjavascript_include_tag, stylesheet_link_tagを変更
assets部分は webpack-dev-server
から取得するため、 app/views/layouts/application.html.erb
のheadタグの stylesheet_link_tag
と javascript_include_tag
部分を変更。
app/views/layouts/application.html.erb
<!DOCTYPE html> <html> <head> <title>SamplePostcss</title> <%= csrf_meta_tags %> <%= stylesheet_link_tag 'http://localhost:8080/application.css', media: 'all' %> <%= javascript_include_tag 'http://localhost:8080/application.js' %> </head> <body> <%= yield %> </body> </html>
Railsの起動設定
rails s
と npm run dev
の2つのコマンドを打てば起動できるのですが、面倒くさいので foreman
というgemを使っていきます。
group :development do gem 'foreman' end
ルートディクトリに Procfile
という名前でファイルを作成し編集していきます。
rails: bundle exec rails server -b 0.0.0.0 -p 3000 webpack: npm --prefix ./client run dev
$ bundle exec foreman start
のコマンドで立ち上がります。
テスト
以上でローカル環境の構築は完了したので、テストしていきましょう。
config/routes.rb
Rails.application.routes.draw do root 'static_pages#about' end
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController # GET / def about end end
app/views/static_pages/about.html.erb
<h1>about</h1>
client/src/stylesheets/application.css
h1 { color: red; }
http://localhost:3000
にアクセスした結果
h1
タグのcolorが red
になっているのと、 gray
に変更した時に自動でブラウザの更新を行い色が変わりました。
PostCSSのプラグインの導入方法
autoprefixer
というprefixをつけてくれるプラグインを入れていきます。
まず client
ディレクトリに移りnpmで autoprefixer
をインストールします。
$ npm install -D autoprefixer
webpack.config.js
を編集していきます。下記の記述でPostCSSのプラグインを導入することができます。
client/webpack.config.js
... /** * postcss plugins */ const autoprefixer = require('autoprefixer'); module.exports = { ... postcss: [autoprefixer], ... }
それではcssファイルの方に flex
を書いてみます。
client/src/stylesheets/application.css
div { display: flex; }
webpackのビルド後は以下のようになります。
dev { display: -webkit-box; display: -ms-flexbox; display: flex; }
PostCSSのプラグインの紹介
postcss-import
ref) https://github.com/postcss/postcss-import
@import
が使用できるプラグインになっています。postcss-simple-vars
など他のプラグインを使用したファイルをimportした時にエラーになる可能性があるので、config.webpack.js
の postcss
の 方でこちらのプラグインを最初に読み込ませておくようにしましょう。
@import 'componsents/button.css'; @import 'base.css';
postcss-simple-vars
ref) https://github.com/postcss/postcss-simple-vars
SCSSのような $
を使って変数が定義できます。また弊社では変数( $
)はcolorの色を定義時に使用しています。
before
$red-color: #ff0000; $column: 200px; h1 { color: $red-color; } .column-400px { width: calc(2 * column); }
after
h1 { color: #ff0000; } .column-400px { width: calc(2 * 200px); }
postcss-nested
ref) https://github.com/postcss/postcss-nested
SCSSの様にネストを使用できるpluginです。
before
.item { &_title { width: 500px; @media (max-width: 500px) { width: auto; } body.is_dark & { color: white; } } img { display: block; } }
after
.item_title { width: 500px; } @media (max-width: 500px) { .item_title { width: auto; } } body.is_dark .item_title { color: white; } .item img { display: block; }
postcss-mixins
ref) https://github.com/postcss/postcss-mixins
SCSSの様に @mixin
で定義し、 @include
で呼び出す形ではないので注意が必要です。
before
@define-mixin margin-10px { margin: 10px; } .column { @mixin margin-10px; }
after
.column { margin: 10px }
また引数を指定することも可能です。
@define-mixin
引数で 0
を指定しています。 @mixin
の方で $px
の値を指定しなければ 0
になり、指定すればその指定した数値になります。
今回の場合は margin: 10px
となります。
@define-mixin margin $px: 0 { margin: $px } .column { @mixin margin 10px; }
PostCSS Sassy Mixinsと言うプラグインもあり、こちらだとSCSSの時と同じ記法で書けますので検討してみてください。
postcss-extend
ref) https://github.com/travco/postcss-extend
SCSSで使っていたextendと同じ記法で書けます。
before
%padding { padding: 10px; } .margin { margin: 10px; } .item { @extend %padding; @extend .margin; }
after
.item { padding: 10px; margin: 10px; }
いかがでしたでしょうか?
PostCSSのプラグインの種類は豊富で、 mixin
の機能を使いたい思った時には上記で書いた様に、 postcss-mixins
, postCSS-sassy-mixins
など複数ブラグインが存在するので、記法やコードを読んで選んでいく必要がありそうですね。
また拡張しすぎると本来のCSSの記法とかけ離れてしまうため使うプラグインは最小限に抑えるべきだと考えます。
We’re hiring!!
Cluexではエンジニアサイド、ビジネスサイド共にメンバーを募集しています! お気軽にご連絡下さいませ! エンジニアの方、ぜひ情報交換しましょう!
Docker with ECS × Railsを実現させるために考えたこと(導入編)
こんにちは。エンジニアの志村です。
今回から私は「Docker on Rails with ECSを実現させるために考えたこと」と題して、実際にDockerをProductionで運用する際にハマったポイント、また考慮すべき点に関して、数記事に渡って執筆していこうかなと考えております。
弊社では開発環境はDocker + compose、その他はItamae × EC2の構成でしたが、現在ドッグフーディング・本番環境をDocker with ECSに移行しております。
ProductionをDockerで運用しようと思った背景
今回インフラ環境を見直した背景として、
- プロビジョニングツールの管理つらい
→緊急で直接サーバ内で作業をした際にプロビジョニングツールとサーバ側の差異が発生。 - OSにインストールしているライブラリのアップデートが完全手動になっている。
→自動化したい - Productionとドッグフーディング環境の差異
→ドッグフーディング環境で新規ミドルウェアや設定の実験を繰り返していたため
といった理由が挙げられます。
プロビジョニングツール、本番環境とドッグフーディング環境の差異に関してはルールを決めて厳密に管理、またライブラリのアップデートも定期的に新規インスタンスを自動起動するスクリプトを作成すれば問題ないとは思います。
しかし、規模が大きくなったタイミングで導入するのはかなりのコストがかかる、またなるべく上記のようなことを考えずに実装に集中したいという思いから今回のインフラ刷新に踏み切りました。
ProductionをDockerで運用する際に考慮した点
開発環境は既にDockerizeしておりましたが、実際にProductionをDockerにて運用する為には様々な障壁にぶつかりました。
デプロイ
Blue-Green Deployment
恐らく、ECSを使用する際に最も悩むのがデプロイの方式をどのようにするかだと思います。
今まででの環境であればCapistranoを叩いてgitをpullして〜のようにデプロイ出来ましたが、コンテナの場合そのようなデプロイ方法は難しいです。
ECSのデプロイ方法として真っ先に挙げられるのがBlue-Green Deploymentです。
BG Deploymentについては割愛しますが、
- ロールバックが容易
- デプロイ時のダウンタイムを極力抑えることが出来る
のような特徴があげられます。 ロールバックの容易さ、ダウンタイム極小化の観点から弊社でもBG Deployment方式にしました。
BG Deploymentは良いことずくめに見えますが、デプロイの戦略を立てる上では中々苦労した部分です。
ECSを使用した際のBG Deploymentには以下の3種類が考えられるかと思います。
- ECSのMaximum healthy percent, Minimum healthy percentを調整し、徐々にタスクが切り替わるように調整する。
- CLB(Classic Load Balancer)× Autoscaling Groupを使用して切り替える
- ALBに紐付いているTarget groupの優先順位を切り替える
- Routes53を使用し、Standby, ActiveのELBを切り替える
結論から言うと弊社は3を選択しました。 選んだ理由やそれぞれのメリット・デメリットに関しては別の記事で書きたいと思います。
スクリプト
今までの環境であれば、Capistranoという素晴らしいツールが存在するのでそこまでデプロイ環境で悩むことも無かったですが、DockerになるとCapistrano単体でのデプロイ環境の構築は中々難しいと感じました。
デプロイの手順としては下記の通りになります。
- Task definitionの更新
- ECSのServiceをupdate
- TaskがRUNNINGになっているか確認
- インスタンスの状況を見て、ALBのTarget groupの優先順位を切り替える
現在では様々なECS向けのデプロイツールが出ておりますが、上述している通りALBのTarget groupの優先順位を切り替えるという作業が発生する為、社内専用のデプロイツールを作成する方針にしました。
この辺りも詳しくは別の記事で書いていきたいと思います。
CI
現在、CircleCIをはじめとして様々なCIサービスが出ていますね。
弊社でもCircleCIを使用し、Rubocop -> scss-lint -> RSpec -> capistranoという流れでデプロイを行っておりました。
今回の移行にあたって、Jenkinsへシフトさせました。
Jenkins移行の理由として、カスタマイズ性が一番の理由です。
弊社では、ドッグフーディング環境においてはテスト有りデプロイ、テスト無しデプロイの2種類が存在します。
cap dogfooding deploy 〜
のように手動デプロイをし、テストを通す前からディレクターに動作を確認して貰う手順をとっております。
勿論テストを通して完璧な状態で見てもらうのが一番ですが、確認を早め早めにすることによって、実装の最終段階でディレクターの意図と違う!というような不一致を極力減らすことが出来ます。
テストを通してからデプロイだとその回数が減ってしまい、リリーススピードが結果的に落ちてしまうのでテスト無しデプロイを可能にする環境を構築したいという背景がありました。
CircleCIでこのような環境を作成するのは結構な手間だったため、今回はJenkinsのパラメータ付きビルドで処理することにしました。
マイグレーション
RailsではDBスキーマの変更、データのクレンジングにActiveRecord::Migrationを使用します。
一定規模になると ActiveRecord::Migrationを使わない例が多いですが、弊社ではロールバックが容易なのとRubyで記述出来るという点で未だに使用しております。
以前の環境であればcapistranoがよしなにやってくれたのですが、今回の環境の場合、稼働用のTask definitionで実行するのは難しいと判断しました。
そこでbatch, migration専用のインスタンス・クラスタを作成し、デプロイスクリプトを走らせた際にまずmigrationを当該クラスタで実行させるようにしました。
こちらはデプロイスクリプトにバッチ専用のコマンドを実装しました。
バッチ
上記のマイグレーションに使用しているインスタンス・クラスタと併用しております。
cronによるバッチ処理+sidekiqをこのインスタンスで管理しています。
環境変数の扱い
環境変数はEC2 Systems Manager・EC2のインスタンスメタデータ・S3と様々な方法があるかと思います。
今回は管理の手軽さ、実装の容易さというメリットから、S3を選択しました。
具体的にはビルド前にS3からenvファイルをダウンロード→ビルド→デプロイ
という流れになります。
また環境によってnginxのconfやfluentdのconfの中身を変えたいという場合がありますが、
これに関してはenvsubstコマンドを使用し、ビルド時にconfの中身を動的に変えるようにしています。
assets周り
弊社ではwebpackを使用し、assetsをビルドしております。
webpackにてビルド→asset_syncを使用し、S3にアップロード→CloudfrontのInvalidationを走らせる
というフローでデプロイを行っておりました。
デプロイフローが複雑になる + Herokuも推奨していないという点から今回はこのasset_syncを使用するのをやめ、Webサーバから直接配信→サイト前段にCloudfrontを張ってキャッシュをさせるようにしました。
ログ周り
fluentdコンテナを立て、Kinesis, BigQueryにログを流すように変更しました。
厳密なログの記録が必要ないサービスに関しては、Cloudwatchにそのまま流して記録しております。
監視
zabbix-agentコンテナを立て、そこからzabbixにデータを流すようにしています。
勿論zabbix, grafana自体もDockerizeしております。
https://hub.docker.com/r/monitoringartist/dockbix-agent-xxl-limited/
非常に使い勝手が良いので、上記のイメージを使用しております。
インスタンス起動時に勝手に登録してくれてとても使い勝手が良いのですが、インスタンス消えた時にホスト情報をどう削除するか未だに悩んでおります…
以上になります。
次回からは項目ごとに具体的な事例と共に執筆していきたいと考えています。
エンジニアが暗号理論を学んでみた。〜暗号の基礎編〜
こんにちは、神山です。
今まで週3日運動してたのですが、今冬、寒さのあまり週1日のみになってしまいました。極端に寒さに弱いです。
今回は暗号理論についてブログを書かせて頂きました。
暗号理論とは
暗号理論は暗号の仕組みを研究する分野になります。Wikipediaを引用させて頂きました。
暗号理論では主に次の二つを扱う。
- 暗号系 (cryptosystem) の構成方法や性能・安全性などに関する研究
- 暗号や電子署名といった守秘 (confidentiality) や完全性 (integrity) を実現する、 暗号アルゴリズムや暗号プロトコルの研究
暗号理論では、主として、アルゴリズムやプロトコルによってセキュリティ機能を実現する研究(情報セキュリティ)がなされており、 OSやネットワークの特徴を生かしてセキュリティ機能を実現する研究(コンピュータセキュリティやネットワークセキュリティ)と区 別される。
暗号理論には、情報理論、符号理論、計算複雑性理論といった計算機科学、数論や代数幾何、離散数学といった数学、時には 力学系などの物理学の知見が用いられることもある。
引用元: https://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E7%90%86%E8%AB%96
要するに、暗号のアルゴリズムの仕組みと安全性を学ぶ学問ということです。ただ、学ぶには大学レベルの数学(主に代数分野)の知識が必要になります。
自分は数学科出身の為、大学時代に少しだけかじりました(専門外だったので習熟度は低いですが…)。最近になって、HTTPSの仕組みを学ぶのに必要になり再度勉強しております。
暗号の代表例
例えば、シーザー暗号とかRSA暗号とか聞いたことある人も多いのではないでしょうか。また合言葉も暗号の一種と言えます。
そこまで詳しくない人もいると思うのでシーザー暗号について説明しようと思います。 シーザー暗号はもとのメッセージ(アルファベットの文字列)を適当に決めた数字の数だけシフトさせて暗号化する方法です。
シフトさせる文字数を3文字としたとき。 a -> d k -> n のように、3文字シフトさせて暗号化する。 「THE ALFEE」 をシーザー暗号(文字数3)で暗号化すると、 「WKH DOIHH」 となる。
メッセージを送りたい人に事前に「3文字シフトで」と伝えておけば、暗号を送り合うことができます。
ちなみにシーザー暗号の由来は、ジュリアス・シーザーから来ております。 ジュリアス・シーザーは紀元前に活躍したとされる政治家、軍人です。「賽は投げられた」という有名な言葉を残しております。
馴染み深い例として、シェイクスピアの戯曲「ジュリアス・シーザー」はご存知でしょうか?
「ブルータス、お前もか」というセリフが出て来る有名なものです。
その「ブルータス、お前もか」のセリフを放った人こそ、ジュリアス・シーザーです。
用語
基本的な知っておくべき用語を挙げました。先程のシーザー暗号を例に取り説明します。
平文
暗号化したいメッセージのことです。「THE ALFEE」が平文です。暗号化
平文を暗号化することです。「THE ALFEE」を「WKH DOIHH」とすることです。復号
暗号文を正規の方法で平文に戻すことです。シーザー暗号で「3文字シフトした」と共有されていた場合、「WKH DOIHH」から「THE ALFEE」という平文を取り出すことを指しております。
ちなみに復号化という言葉が使われることもありますが同じ意味になります。ただ復号という言葉に「暗号を平文に戻す」という意味があるので、復号「化」とすると意味が2重になる為、本当は間違いらしいです。暗号化方式
暗号の名称です。シーザー暗号やRSA暗号などにあたります。鍵
平文を暗号化するのに使うもの、また暗号文を復号するのに使うものです。暗号化方式がシーザー暗号の場合、シフトさせる文字数が鍵です。上の例だと「3」が鍵になります。解読
これは復号と似て非なるものなので注意してください。暗号文から平文を取り出すことではあるのですが、鍵を知らない状態で取り出すことです。
例えば、「探偵が暗号を解読した」というセリフがドラマ等であると思います。これは探偵が鍵を知らない状態で暗号を読み取るからです。基本的に探偵がするのは「解読」です。
もし「復号」としたら、探偵自身が重要参考人ですね(暗号を平文に戻す方法を知っているということなので)。
そもそも暗号とは?
最後に暗号を数学的に説明しようと思います。
暗号化方式とは。 P: 平文の集合(plaintext) C: 暗号化文の集合(ciphertext) K: 鍵の集合(key) E: 暗号化関数の集まり(encryption function) (暗号化関数とは、平文を暗号化するもの。シーザー暗号(文字数3)で暗号化することだと考えればよい。) D: 復号関数の集まり (decryption function) (復号関数とは、暗号文を復号するもの。暗号文をシーザー暗号(文字数3)で復号することだと考えればよい。) この5つからなるものを暗号化方式という。また下記を満たさなくてはいけない。 ∀k ∈ K, ∀p ∈ P, ∃e ∈ E => ∃d ∈ D, d(k, e(k, p)) = p (直訳すると、適当な鍵を用いて平文を暗号化したとき、必ず復号できる方法があるということ。)
数学用語で書いてみましたが、基本的に最後の直訳を理解して頂ければ大丈夫です。 数学こそ、簡単な言葉をただ難しくする暗号ですね。
他にも、秘密鍵暗号(共通鍵暗号)とか公開鍵暗号とか書きたいことがあるのですが、内容が重くなりすぎるのでまた別の機会で書こうと思います。
We’re hiring!!
Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています!
お気軽にご連絡下さいませ! エンジニアの方、ぜひ情報交換しましょう!
GrafanaでZabbix/CloudWatchのメトリクス監視を一元化する
Grafanaかっこいいですよね。いつまでも見ていられそうです。
今回は統合監視ツールZabbixとAWSのCloudWatchメトリクス群を Grafanaで一元的に可視化して見れるようにしてみます。
今回の背景
CloudWatchやEC2のメトリクスを見るのに、 AWSにコンソールで入っていちいち確認するの面倒じゃないですか。 加えてサービスごとにAWSアカウントが分かれているとなると更に不便じゃないですか。
・よりリアルタイムなデータを見たいものはZabbixのメトリクスを使用したい
・ELBにアタッチされてるホストの数も見たい
・オートスケーリングどんな感じか見たい
・インスタンス毎のメトリクスも見たい
・ついでにサービス全体でどれくらいの負荷がかかってるかも見たい
・なんなら複数のAWSのアカウントを跨いで見れると尚良い
・・・ということで全部grafanaに突っ込みました。
構成
今回のメインはzabbix-server、Grafana及びCloudWatchあたりになります。
構成はこんな感じです。
Grafanaは標準でCloudWatchをサポートしていて、 Zabbixもプラグインを入れることでデータソースとして使用することが可能です。
ちなみにGrafanaで標準サポートされているデータソースがこちら
・CloudWatch
・Elasticsearch
・Graphite
・InfluxDB
・OpenTSDB
・Prometheus
Zabbixも標準でサポートしてくれると嬉しいです。
grafanaをインストールする
インストールはOSはUbuntuベースで話を進めていきます。
Ubuntu以外に関してのGrafanaのインストールは、詳しくはこちらをご覧ください。
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_4.1.1-1484211277_amd64.deb $ sudo dpkg -i grafana_4.1.1-1484211277_amd64.deb $ sudo apt-get install grafana
grafanaにZabbixのプラグインをインストール
$ sudo grafana-cli plugins install alexanderzobnin-zabbix-app
GitHub - alexanderzobnin/grafana-zabbix: Zabbix plugin for Grafana dashboard
grafanaを起動
$ sudo service grafana-server start
Grafana自体にHTTPサーバーの機能も内臓されているので、これでGrafanaが立ち上がります。 デフォルトでは3000番ポートを使用するので、適宜リバースプロキシでnginxを前にかませるといいかと思います。そのままでも問題なく使えます。
では次にZabbixのデータを取り込んでいこうと思います。
Zabbix側でgrafana用のユーザーを作成
Zabbix側の手順としては
・grafana用のユーザーを作成する。
・grafana用ユーザーにREAD権限を付与。
といった流れになります。
ちなみにZabbix側でgrafanaユーザーに適切なパーミッションを与えていなかった結果、grafanaからAPI叩いてもデータが取れずに2時間ばかり無駄にしたのは私です。 パーミッションを与えてください。
Zabbixのパーミッションなどは本家にドキュメントがあります。
grafanaでZabbixデータソースの設定
左上のロゴから、[Data Sources] → [Add data source]と辿っていきます。
設定はこんな感じです。
Zabbixには標準でAPIが用意されているので
http://エンドポイント/zabbix/api_jsonrpc.php
をURLのところに設定してあげます。
Zabbix API Detailsには先ほどZabbix側で作成したgrafana用ユーザーのUsernameとPasswordを設定してください。
今回は便宜上、Zabbix Serverが入っているEC2インスタンスにGrafanaも入れているのでVPCやSGなどは特に気にせず、GrafanaをプロキシとしてlocalhostでZabbixのエンドポイントを叩いています。
Zabbix用のダッシュボードを作成
ダッシュボードの作り方などはServerworksさんのこちらのブログが詳しいです。
個人的な要件:EC2のメトリクス表示をオートスケーリングに対応させたい
Grafanaでダッシュボード作ったのはいいものの、 オートスケーリングでインスタンスが増減するたびにグラフの設定し直すのとかめちゃめちゃ面倒ですよね。
そもそもオートスケーリングしなくてもインスタンス多いとグラフ作るのが面倒。
でも1つのパネルで全インスタンスのCPU利用率とか表示させたい。
解決策:正規表現でフィルタリングができる。
文字が黄色くなっている箇所ですが、Host名でフィルタをかけて、ホスト名がマッチするサーバー群のメトリクスを一発で表示することができます。
上の設定だけでこんなグラフがすぐに出来上がります。
これだけでもパネル設定がかなりラクになりました。
そのほかの色々なグラフなどの作り方はgrafana-zabbix.orgが出しているデモの設定が参考になります。
http://play.grafana-zabbix.org/dashboard/db/grafana-zabbix-demo
GrafanaでCloudWatchをデータソースに設定する
続いてGrafanaにCloudWatchのデータを取り込んで、グラフを表示させていきます。
AWS IAMでgrafana用のユーザーを作成
CloudWarchReadOnlyAccess
ポリシーをアタッチしたユーザーをIAMで作成します。
Grafanaの方で使用する認証情報はアクセスキー及びシークレットキーになります。
GrafanaでCloudWatchデータソースの設定
本家Grafanaのドキュメントではaws-cliのクレデンシャルファイルを使用した設定方法となっていますが、 設定画面から直接IAMユーザーのアクセスキーとシークレットキーを入れることも可能です。
個人的にはサーバーの中に credential
を置く必要ないので、GUIで設定しちゃうとがラクかなと思います。
GUIで設定しても、再度設定を開くとアクセスキーなどは見れないようになっています。
これでCloudWatchの方からもメトリクスを表示が可能となりました。
複数のAWSアカウントを設定する際も同じ要領でIAMユーザーを作成し、データソースを追加していくことでGrafanaからデータを取り出せます。
ELBのHealthyHostCountをパネルに表示する
試しにCloudWatchからのメトリクスとしてELBにアタッチされているHealthyHostCountのメトリクスを表示させてみます。
設定としてはこんな感じです。
これでZabbix及びCloudWatchからデータが取れるようになったので、あとはパネルを一通り作れば、完成です!
【番外編】 AWSの請求情報をgrafanaで確認する
AWSの請求情報、ついでにGrafanaで見たくないですか? 私は見たいです。
例えばこんな感じ。
Grafanaの公式サイトにあるテンプレートを使えばすぐに表示させることができます。
上記のものだとインポートした後にカスタマイズができないので、 インポートした後にカスタマイズしたいという方には、カスタマイズできるやつ置いておきます。
よかったら使ってください。
GitHub - yoshi42662/Grafana-AWS-Billing-Dashboard
We’re hiring!!
Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さいませ! エンジニアの方、ぜひ情報交換しましょう!
男もすなるsshといふものを、我もしてみむとて解説するなり
こんにちは。エンジニアでインターンをしている村田です。 最近寒くてベッドから起き上がるのがつらいです。
最近デスクトップ環境をxubuntuにしたのですが使いやすくて感動してます。 軽くて、Ubuntuらしい便利な機能も多くありますので他の方にもぜひおすすめです!
話は変わりますが、コンピュータの用語で新しく知ったものを調べると なんでこんなにわかりづらい説明しかないんだ!! と思ったことってありませんか?
自分は結構多いです、この現象(笑)。 学習していくとそれが当たり前になってしまうので「このサイトにわかりやすく書いてあるじゃん」と思うのですが、最初は難しく感じてしまうものなんですよね。
今回は以前自分が概念を理解するまでに苦労してしまった「sshのしくみ」についてまとめていこうと思います。 (「わかりやすい」というのは個人的な感想です。)
「ssh」略さずに言うと?
「ssh」はsecure shellの略です。 日本語にすると安全なシェルということです。
「ssh」っていつ使うの?
「sshとは安全なシェルのことです!」と言われてもそれではなんのことかわからないと思いますので、もう少しかみ砕いて説明していきます。 このページをご覧になっている方は、例えば「サーバーにアクセスしようとしたけどsshってどういうことなんだ?」とか「GitHubを使ってみたいけどなんか登録してsshを使わなくちゃいけないらしい」といった方が多いかと思います。
それではsshとはどういう場面で必要なのでしょうか?
簡単な言葉で説明すると「他のコンピュータに対してアクセスして、何かしらの操作を加えたいとき」にsshを使います。 例えば「サーバーに対してコマンドを実行して環境を作りたい」とか「GitHubなどのリモートリポジトリに対してブランチをマージさせたい」といったところで、sshというコマンドを使用したいわけです。
基本的にはssh user@host_name
で使います。
これは色々なところで書いてあるのでわかりやすいと思います。
そもそも、「安全なシェル」の「シェル」とは人間とコンピュータが気軽におしゃべりするための言語です。 ディレクトリを変えたり、 ファイルを削除したい!というときの「cd」とか「rm」はそれに当たりますよね。
遠くのコンピュータに命令したいときも当然同じようにおしゃべりできたら便利なわけですから、シェルを使いたいですよね。 ただし、暗号化なしで通信するとほかの人に見られる危険などがあるため安全なシェルすなわちsshを使いたいということなのです。 (実は以前は暗号化されていないtelnetというものを使っていました。基本的な機能しかないことを利用して、現在もサーバーを作る際にときどき用いることがあります。)
暗号化の仕組み
sshで公開鍵暗号方式を使用しています。(実は違うやり方でパスワード認証方式というのもありますが、あまり使われないように思われます。) 公開鍵暗号方式の通信とはサーバーに公開鍵を持たせて文書等を暗号化させて、受信する際には自分の持っている秘密鍵を使用して複合する通信のことです。
これを例えるとすると、 公開鍵を錠(lock)、秘密鍵はその錠の鍵(key)だと考えてください。
南京錠などを想像すればわかるように、lockのやり方がわかっても、keyが用意できなければ実際に中身にたどり着くことはできません。 リアルの世界とは違って暗号化の世界では錠の鍵穴を見て鍵を作り出すことができない(らしい)ので、自分の秘密鍵をばらさなければこれで安全に通信することが可能なわけです。
また、sshではより安全性を高めるために秘密鍵に対して鍵をかけるのが一般的です。 鍵に鍵をかけるというのは面白いですよね。
この鍵は暗号化も複合化も同じ共通鍵暗号方式を使用しています。
実際に使用していくコマンド
ここでは実践で使っていくコマンドを簡単に紹介していきます。 紹介できなかったオプションやコマンドがたくさんあると思うので、不足している部分に関しては最後の参照URL等をご覧ください。
また事前知識としてsshに関するファイルは~/.ssh
というディレクトリに入るのですが、そこのファイルの役割を紹介しておきます。
authorized_keys 公開鍵を登録しておくファイル。 config SSH接続に関する設定を書いておくファイル。 known_hosts 過去に接続したことがあるサーバーを書いておくファイル。
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
これはgithubで推奨されている鍵の作り方です。
-t rsa
の部分はRSAという暗号化方式を使って作成するという意味です。
他にもDSAやECDSAといった種類がありますが一般的にはRSAを用いるのではないかと思います。
また-b 4096
では鍵のビット数をしてしています。デフォルトでは2048になのでより強固に作成していると言えますね。
-C "your_email@example.com"
の部分はコメントなので必要なければ省いて構いません。
途中でパスフレーズを聞かれると思いますが、これが先ほど書いたとおり秘密鍵に対するパスワードになっているので忘れないものを使用してください。
このコマンドを実行するとid_rsa
とid_rsa.pub
が作成されます。もちろん前者が秘密鍵、後者が公開鍵です。
余談ですが、GitHubユーザーのSSH鍵6万個を調べてみたでGitHubのssh鍵に関して面白い考察がありました。
ssh-copy-id -i ~/.ssh/id_rsa.pub user@host_name
これはsshの公開鍵を登録したいサーバーに送るコマンドです。
i
オプションで任意のファイルを指定して送ることができます。ここでは先程作成した~/.ssh/id_rsa.pub
を指定しています。
ssh-agentとssh-add ~/.ssh/id_rsa
ssh-agent
は認証エージェントとよばれるもので、sshをするときに必要なパスワードの入力をショートカットできるようにするコマンドです。
(ubuntuやMacの人は自動的に起動しているのでこのコマンドは忘れてしまっても問題ありません。ただ、他のOSではssh-agent bash
などを打たないといけないかもしれません。)
ssh-add
はssh-agent
に秘密鍵を追加するメソッドです。
引数にとった秘密鍵を追加します。
参照URL
http://qiita.com/tag1216/items/5d06bad7468f731f590e http://qiita.com/suthio/items/2760e4cff0e185fe2db9 http://webos-goodies.jp/archives/50672669.html
またこちらは書籍ですが、私がsshの仕組みを少し理解できるようになったきっかけになった本です。 はじめてUNIXで仕事をする人が読む本
HTMLテンプレートエンジン「slim」の解析ツールslim-lintを導入してみた!
こんにちは、エンジニアの井戸田です。
弊社ではmamanokoという子育てをするママのためのメディアをRuby on Railsで運営しており、viewではHTMLテンプレートエンジンであるslimを使用しています。今までrubyの解析ツール rubocop
や、scssの解析ツール scss-lint
は導入しましたが、slimの解析ツールを導入していなかったということで、今回導入してみました。
インストール
$ gem install slim_lint
又は Gemfile
に下記を記入して bundle install
をすることでインストールが可能です。
gem 'slim_lint', require: false
require: false
を指定することで、 autorequire
をしないようにしています。
実行方法
スキャンするディレクトリ、又は複数のディレクトリを指定することによって、コマンドラインから slim-lint
を実行することが可能です。
$ slim-lint app/views $ slim-lint app/**/*.slim
又 slim-lint -v/--version
を指定するとバージョンを確認ができます。他にもいろいろなオプションがあるので、下記のURLから御覧ください。
ref) https://github.com/sds/slim-lint#usage
実装
slim-lintはデフォルトで下記のような設定になっています。
ref) https://github.com/sds/slim-lint/blob/master/config/default.yml
# Default application configuration that all configurations inherit from. # # This is an opinionated list of which hooks are valuable to run and what their # out of the box settings should be. # Whether to ignore frontmatter at the beginning of Slim documents for # frameworks such as Jekyll/Middleman skip_frontmatter: false linters: CommentControlStatement: enabled: true ConsecutiveControlStatements: enabled: true max_consecutive: 2 EmptyControlStatement: enabled: true RedundantDiv: enabled: true LineLength: enabled: true max: 80 RuboCop: enabled: true # These cops are incredibly noisy since the Ruby we extract from Slim # templates isn't well-formatted, so we ignore them. # WARNING: If you define this list in your own .slim-lint.yml file, you'll # be overriding the list defined here. ignored_cops: - Lint/BlockAlignment - Lint/EndAlignment - Lint/Void - Metrics/LineLength - Style/AlignHash - Style/AlignParameters - Style/BlockNesting - Style/FileName - Style/FirstParameterIndentation - Style/FrozenStringLiteralComment - Style/IfUnlessModifier - Style/IndentationConsistency - Style/IndentationWidth - Style/MultilineArrayBraceLayout - Style/MultilineAssignmentLayout - Style/MultilineHashBraceLayout - Style/MultilineMethodCallBraceLayout - Style/MultilineMethodDefinitionBraceLayout - Style/MultilineMethodCallIndentation - Style/MultilineOperationIndentation - Style/Next - Style/TrailingBlankLines - Style/TrailingWhitespace - Style/WhileUntilModifier TagCase: enabled: true TrailingWhitespace: enabled: true
この設定の中で、不要・変更したい設定に対して、ホームディレクトリに .slim-lint.yml
というファイルを作成し、記述することで無効化・変更します。
.slim-lint.yml
の記述例
exclude: - 'repository_name/app/views/**/*.slim' linters: CommentControlStatement: enabled: false LineLength: include: 'repository_name/app/views/**/*.slim' max: 80 EmptyControlStatement: exclude: 'repository_name/app/views/**/*.slim'
オプション | 説明 |
---|---|
exclude | slim-lintや各linterから除外したいファイルがある時に使用します。 |
include | 指定したファイルで絞り込みを行います。 |
enable | false とした時には、そのlinterが走ることはありません。そのlinterが不要な時に使用します。 |
linterの説明
CommentControlStatement
コメントの書き方についての制御するlinterです。
- Bad
# commentout
- Good
/ commentout
ConsecutiveControlStatements
連続の制御文に対して制御するlinterです。
ConsecutiveControlStatements
には下記ようなオプションがあります。
制御構文が同じインデントで2行以上続くのでしたら、見やすさからviewに書かずに helper
にその制御を移動させた方がいいと思います。
オプション | 説明 |
---|---|
max_consecutive | 行に指定できる制御文の最大数 |
- Bad
- code1 - code2 - code3
- Better
ruby: code1 code2 code3
- Good
- helper_method_name
EmptyControlStatement
空行に対して制御するlinterです。
こちらのlinterを true
にすると空行がなくなり、場合によっては見づらくなる可能性があるので、注意する必要がありそうですね。
- Bad
p pタグ - p pタグ
- Good
p pタグ p pタグ
RedundantDiv
div
タグに対して制御するlinterです。
slimでは div.class_name
と書かずに .class_name
と記述するだけで divタグ
になります。不要な場合に div
と記述しないというのが、このlinterの役割です。
- Bad
div.class_name
- Good
class・idを指定せず、ただの改行などの場合に使用する場合にはdiv
と記述する必要があります。
div
- Good
.class_name
RuboCop
このlinterはテンプレート内に存在する Ruby
コードをチェックする為のものです。
ホームディレクトリに作成された .rubocop.yml
の内容が反映されるようです。
ref) https://github.com/bbatsov/rubocop
オプション | 説明 |
---|---|
ignored_cops | 無視したいRubocopの設定を記述 |
TagCase
大文字のタグ名を報告するlinterです。
HTMLの標準では、小文字のタグ名を限定するということはなく、大文字でも対応できるのですが、XHTMLでは小文字の記述が必須なので、一貫性としてこのlinterが作られたようです。
個人的に大文字・小文字が混ざっているいると見づらい点や、小文字にすることで大文字より見やすくなる点があるので、小文字で揃えた方がいいと思います。
- Bad
BODY p pタグ
- Good
body p pタグ
いかがでしたでしょうか?
slim-lint
を導入することで、気をつけて同じフォーマットにしようとしていても、どうしても見逃してしまう部分や、行数を制限することで、1つのファイルだけで処理するのではなく、partial化させることにより見やすくなると思います。
最後まで読んでいただきありがとうございます。
We’re hiring!
フロントエンジニアの方で、サーバーサイドを触ってみたい方、ぜひ一度Cluexに遊びに来てください!!