システムモニタのグラフ生成にMuninを使用する
Monitでシステムモニタ、プロセス監視も行うのですが、プラスでMuninも導入します。 Muninでは各種システムモニタ、グラフ生成、しきい値超え検知時の通知など行います。
導入するパッケージは各インスタンスに導入するmunin-nodeとmunin-nodeから情報を収集してグラフ生成(RRDtool)、アラート通知を行うmuninの2つで構成されます。 Muninはwatchと名づけたサーバに導入します。Muninでグラフ生成するノードの対象はEC2のNameタグに名前がついているものだけとします。 appサーバ、resqueサーバの中でオートスケールによって増えた分は対象としません。
対象ノードが増えてくるとMunin側でのグラフ生成に時間がかかるようで、色々対策されるかたもいるみたいです。この案件でもノード数が増えてきた場合は何かしらの対策をしたいと思いますが、とりあえずはそのまま使っていきます。
インストール
毎度のyumでいれるだけです。
# 全ec2インスタンス $sudo yum install munin-node # watchサーバ(グラフを生成する) $sudo yum install munin
munin-nodeの設定
ACL
Muninが動いているサーバが各インスタンスのmunin-nodeに接続してくるので、munin-nodeの設定でmuninのIPを接続許可する設定が必要です。 今回はVPC内サブネットのネットワークアドレス全体を許可にしました。
/etc/munin/munin-node.conf
# A list of addresses that are allowed to connect. This must be a # regular expression, since Net::Server does not understand CIDR-style # network notation unless the perl module Net::CIDR is installed. You # may repeat the allow line as many times as you'd like allow ^127\.0\.0.1$ allow ^172\.31\.[0-9]+\.[0-9]+$ # Which address to bind to; host * # host 127.0.0.1 # And which port port 4949
またmunin-node自体はtcp:4949でListenしているので、全セキュリティグループに対して下記のポリシーを追加します。
SecurityGroup=NETからのtcp:4949を受け入れる
プラグインを有効にする
色んなサービスのプラグインがデフォルトで用意されているので、サーバの中にそのサービスが動いていればmuninインストール後からすぐにモニタリングができるようになります。
どのプラグインが有効になっているかはmunin-node-configure
で確認できるようです。
参考になった記事
試した所munin_statsというのが有効になっていないようなので有効にしてみました。シンボリックリンクを貼った後、munin-nodeのrestartをします。
$sudo munin-node-configure --shell ln -s '/usr/share/munin/plugins/munin_stats' '/etc/munin/plugins/munin_stats' $sudo ln -s '/usr/share/munin/plugins/munin_stats' '/etc/munin/plugins/munin_stats' $sudo /etc/init.d/munin-node restart
また、WEBサイトのヘルスチェックをMuninで行いたいと考えていたのですが、どんぴしゃなプラグインがありましたのでこれをそのままwatchサーバのmunin-pluginに導入します。 httpingとmuninでWebサーバのレスポンスをグラフ化した
munin側の設定
監視対象のノードを追加する
/etc/munin/munin.confで収集するノードを下記のように追記していきます。ノード毎にしきい値をデフォルト以外に設定することも可能です。
[example;] contacts systemalert [example;watch-1] #watch-1で名前解決できないとだめ address 127.0.0.1 use_node_name yes httping_staging.contacts httping load.load.warning 10 cpu.user.warning 500 load.load.critical 20 cpu.user.critical 700
アラート通知設定
また、しきい値超の場合のアラートメール設定は、下記のようなものを任意の別ファイルにして設定しました。 アラートの内容、深刻度によってメールの送信先を分けてるようにします。
- /etc/munin/munin-conf.d/munin-alert
contact.httping.always_send critical contact.httping.max_messages 5 contact.httping.command mail -s "${var:worst} ${var:cfields} Munin http healthcheck" systemservicedown@watch.example.com contact.systemalert.always_send critical contact.systemalert.max_messages 3 contact.systemalert.command mail -s "${var:worst} ${var:cfields} ${var:group} ${var:host}" systemalert@watch.example.com
計画メンテナンス時などでサービスダウンしている場合に毎回ご丁寧にアラートメールを送ら無くても良いので、アラート通知を気軽にオン、オフにできるようにします。 上記のmunin-alertが/etc/munin/munin-conf.d/にあると通知オン、なければオフとなるので、このファイルを置いたり消したりするデプロイタスクを後ほど用意します。
グラフを見る
グラフはhttp://muinが動いているサーバ/munin
で閲覧できます。
Muninを入れたサーバでApacheが動いているものとします。Muninを入れた時点で/etc/httpd/conf.d/munin.conf
が用意されます。
社内からのアクセスだったらパスワード認証なしでグラフを見れるようにして、社外であればベーシック認証というように変更します。
- /etc/httpd/conf.d/munin.conf
AuthUserFile /etc/munin/munin-htpasswd AuthName "Munin" AuthType Basic require valid-user Satisfy Any Order deny,allow Deny from all Allow from 127.0.0.1 172.31. 会社のIP
Monitでインスタンス内のシステムモニタ、プロセス死活監視する
各EC2インスタンスにMonitを導入して、CPU使用率、メモリ空き容量、Disk容量といった数値の監視とプロセスの死活監視を行います。例えば、Disk容量が90%超えを検知したら指定のメールアドレスに送信することもできます。 モニタをグラフで見る機能はないので、それについてはあとで記事にする予定のMuninを導入します。
インストール
$sudo yum install monit
で入ります。
設定は/etc/monit.d/
以下に.confを配置します。サーバの役割毎に監視したいプロセスなども変わってくるので、役割毎にファイルを分けて用意します。
base.confは全サーバ共通の監視項目、web.confはWEBサーバとしての監視項目、app.confはAPPサーバとしての監視項目・・・と言った具合です。
インスタンスがAPPサーバとして立ち上がった時は、base.confとapp.confを/etc/monit.d/下に配置されるようにします。
設定内容
confの例を書いておきます。こんなことを設定しています。
set alert systemalert@watch.example.com but not on { INSTANCE pid } with mail-format { from: monit@$HOST subject: $SERVICE $EVENT $HOST message: Monit $ACTION $SERVICE at $DATE on $HOST: $DESCRIPTION. } set mailserver localhost port 25 # System check system system group system if loadavg (1min) > 12 then alert if loadavg (5min) > 8 then alert if memory usage > 90% then alert if swap usage > 25% then alert if cpu usage (user) > 95% then alert if cpu usage (system) > 75% then alert if cpu usage (wait) > 50% then alert check filesystem rootfs with path / if space usage > 80% then alert # syslogd check process syslogd with pidfile /var/run/syslogd.pid start program = "/etc/init.d/rsyslog start" stop program = "/etc/init.d/rsyslog stop" if 5 restarts within 5 cycles then timeout check file syslogd_file with path /var/log/messages #eye check process eye matching "eye monitoring" start program = "/etc/init.d/eye start" stop program = "/etc/init.d/eye stop" if 5 restarts within 5 cycles then timeout
HAProxyでMySQL slaveの通信を分散する
初めにだらだらと概要を書く
MySQLの負荷分散として更新系のmaster、参照系のslaveでインスタンスを複数台用意することにします。slaveについては負荷に応じて台数を増減する予定です。
アプリからみてslaveサーバが何台あるか意識しなくてもいいようにHAProxyを用意します。 HAProxyがアプリとMySQLサーバの通信の中継役を行ってくれるので、アプリからは常にHAProxyだけ見えていればOKとなります。 HAProxy側で複数のslaveサーバに接続を分散してくれます。もしslaveサーバのどれかがダウンしてるときはそのサーバには接続を振らないようにもしてくれます。
また、「負荷に備えてslaveを増設したけど暖気が終わるまで接続を振りたくない」という場合に、設定を一時的に変更することでそのサーバに接続を振らないようにもできます。 接続数の重みづけの調整もできるので、じわじわと接続数を増やしていくということもできそうです。
当初MySQLだけでなくRedisの中継も行おうと考えていたのですが、RedisはElastiCache側でうまく分散してくれるようなので、MySQLのSlaveだけに使用することにします。
インストールと設定
インストールはyumですんなり入ります。
またコマンド経由でhaproxyを制御できるようにhaproxyctlを入れます。
こちらはgemでして、$gem install haproxyctl ; rbenv rehash
でインストールします。
設定内容は以下の感じです
/etc/haproxy.cfg
global maxconn 4096 user haproxy group haproxy daemon stats socket /tmp/haproxy level admin quiet nbproc 1 log 127.0.0.1 local0 info defaults log global mode tcp retries 3 option redispatch option tcplog option dontlognull maxconn 4096 timeout connect 10s timeout client 10s timeout server 10s grace 1000 listen mysql-slave bind :3307 mode tcp balance roundrobin option mysql-check user haproxy server slave1 staging-slave-db-1.example.internal:3306 weight 50 check port 3306 inter 5s rise 2 fall 2 server slave2 staging-slave-db-2.example.internal:3306 weight 50 check port 3306 inter 5s rise 2 fall 2 server slave3 staging-slave-db-3.example.internal:3306 weight 50 check port 3306 inter 5s rise 2 fall 2 server slave4 staging-slave-db-4.example.internal:3306 weight 50 check port 3306 inter 5s rise 2 fall 2 server master1 staging-master-db-1.example.internal:3306 check port 3306 inter 5s rise 2 fall 2 backup
MySQLのヘルスチェック
option mysql-check user haproxy
という設定で、HAProxyがhaproxyユーザを使ってMySQLサーバに接続を試みてヘルスチェックを行います。
MySQLサーバ側で、haproxyユーザを作っておく必要があります。
grant usage on *.* to 'haproxy'@'%'; flush privileges;
運用中にやりそうな事
例えば、staging-slave-1を何らかの理由で使用したくないという場合は、sudo haproxyctl disable server mysql-slave/slave1
で使用されなくなります。
復活させたい場合はsudo haproxyctl enable server mysql-slave/slave1
です。
slaveを立ち上げたばかりなので少ない接続数から慣らしていきたいという場合は、他のslaveの重み(weight)より低い数値に変更します。
コマンドとしてはこんな感じです。
sudo haproxyctl set weight mysql-slave/slave1 1
weightの数値は0~256のようで、数が大きいほど接続数も多くなります。
なお、stats socket /tmp/haproxy level admin
としておかないとsetコマンドはpermission deniedで拒否されます。
クライアント側の名前解決を設定する
各インスタンスのリゾルバを設定する
/etc/hosts
見る/etc/resolv.conf
に書かれたDNSサーバに問い合わせる
/etc/resolv.conf
は下記のように設定します。
options single-request-reopen search example.internal nameserver 127.0.0.1 nameserver 自前のBINDのIP nameserver AWSの用意したDNSのIP
ローカルにdnsmasqをいれてDNSキャッシュする
127.0.0.1が何かというと、dnsmasqというDNSサーバで、各インスタンスのローカルで起動させておきます。毎回DNSサーバに問い合わせしないようにDNSキャッシュ機能をするためです。dnsmasqにキャッシュがない場合は/etc/resolv.conf
に書かかれたIPにフォワードしてくれます。
dnsmasqの導入は$sudo yum install dnsmasq
ですんなり入ります。
ネガティブキャッシュ(名前解決できなかったということをキャッシュする)はしないようにします。
- /etc/dnsmasq.conf
# If you want to disable negative caching, uncomment this. no-negcache
キャッシュを全クリアしたい場合は$sudo /etc/init.d/dnsmasq force-reload
します。
インスタンス起動時に/etc/resolv.confが初期化されてしまう
最初/etc/resolv.conf
に上記の内容を直接ベタ書きしていたのですが、インスタンス再起動時に/etc/resolv.conf
が初期化?されてしまい、AWSの用意したDNSサーバのIPのみに書き換わっていたりしました。
DHCPクライアントなので/etc/resolv.conf
も設定されるのでしょう。
調べてたところ/etc/dhcp/dhclient-enter-hooks
のファイルを用意していると、DHCP更新時の/etc/resolv.conf
の書き換え処理を上書きできるようです。
make_resolv_conf() { rscf=`mktemp /tmp/XXXXXX`; echo 'options single-request-reopen' > $rscf echo 'search example.internal' > $rscf echo 'nameserver 127.0.0.1' >> $rscf echo 'nameserver 172.31.4.75' >> $rscf echo 'nameserver 172.31.0.2' >> $rscf change_resolv_conf $rscf rm -f $rscf }
今のところこれで正常に動作しているようです。
最初のec2インスタンスを作る
AWS management consoleのEC2 Dashboardから早速EC2インスタンスを一つ作り、これをテンプレートマシンと呼ぶことにします。 このテンプレートマシンに一通りのソフトウェア、設定を手でごりごり入れていきます。 アプリケーションサーバが1台ほしいという時になったら、このテンプレートマシンのAMIをベースに「アプリケーションサーバ」という環境変数的なものを与えてマシンを起動するとアプリケーションサーバとして立ち上がってくるというようにします。
色々調べているとChefを使って構成管理をする記事をよく見かけるので、「自分もやりたい!」と思いつつもすぐ「時間足りないかなぁ」となって、使うのは見送っていました。最近になってまた勉強する時間もできそうなのでChefにトライしたいところです。
インスタンス起動後は以下を進めます。手順詳細はここに書かずに、参考になった記事をリンクさせて頂きます。
yum updateする
/etcをgitで管理する
etckeeperを導入します。 手順
dateコマンド打った時に日本時間で表示されるようにする
cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
ruby環境準備
rbenv、ruby-build、ruby2.1(当時は2.0)を入れる 手順
シェルからAWSのコマンドを使えるようにする
このインスタンスのシェル上からAWSのAPIを叩けるようにします。 AWS-CLIパッケージはインストール済みだったので、与えたれたアクセスキー、シークレットアクセスキー等を設定します。 手順
$aws configure AWS Access Key ID [****************]: *** AWS Secret Access Key [****************]: *** Default region name [ap-northeast-1]: ap-northeast-1 Default output format [text]: text
すると~/.aws/configが生成されます。
[default] aws_access_key_id = *** aws_secret_access_key=*** output = text region = ap-northeast-1
awsからはじまるコマンドが使えるようになります。 as-、ec2-、rds-から始まるコマンドは昔のもので今はawsに一新されたようです。
AWS SDK for rubyはRuby2.1を入れた後gem installで入れます。
このインスタンスのAMIを定期的に作成できるようにする
任意のインスタンスのAMIを作成するスクリプトを用意します。 AWS manegement consoleからAMI削除、AMI作成をするのが手間だったので、スクリプトにしました。 インスタンスの改造作業の前にスクリプトを実行したり、cronで定時バックアップするようにしておきます。
Ruby環境な会社なので、私も見習ってRuby使っていきます!
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require "aws-sdk" AWS.config(YAML.load(File.read("config.yml"))) machine_names = [ "template", ] machine_names.each {|name| AWS::EC2.new.images.filter("name", name).each{|ami| p "deregister ami name: #{name}, ami_id: #{ami.id}" if ami.deregister p "success" else p "fail" end } AWS::EC2.new.instances.tagged("Name").tagged_values(name).each{|ins| p "create ami name: #{name}, ec2_id: #{ins.id}" new_ami = ins.create_image(name, {:description => Time.now.to_s, :no_reboot => true}) if new_ami && new_ami.tag("template", :value => name) p "success" p "ami.id: #{new_ami.id}" else p "fail" end } }
AWS内の全体構成を図にした
構成図
今現在ステージング環境をこの構成で動かしています。 最初この構成内のサービスの機能や役割についてだらだらと書いていたら、やたらかったるい長文になってしまったので書き直しました。
思う事がある所だけ書き留めておきます。
VPC環境下に構築する
用意されたAWSのアカウントでログインするとすでにVPCが設定されていたのでそのままVPC内に作ります。 サブネットもアベイラビリティゾーンA(AZ-A),AZ-Bでそれぞれ1つずつ作られてたのでそのまま使用します。 WEB、DBという用途役割でのサブネットは作りません。(細かいネットワークアドレスでの制御等はしないと思うので)
セキュリティーグループ(SG)は用途役割に応じて作成する
WEB,DB,ADMINといった大体の用途役割別にSGをつくりインスタンスを属させます。インスタンスは複数のSGに属することも可能ですが、この案件ではそうしません。 下記のような簡単なパケットフィルタリングでセキュリティーグループ間の通信を管理します。
appsグループはproxyグループからくるtcp80(nginx)のパケットは受け入れる。
dbグループはappsとbackgroundグループからくるtcp3306(mysql),tcp6379(redis)のパケットは受け入れる。
AZ-Aにインスタンスを寄せる
インスタンスをA,B両方に配置して耐障害性を高めるのが推奨かと思いますが、あえてAZ-Aに寄せました。 どうもアプリサーバとDBサーバがA,Bでまたぐ場合、レスポンスが遅くなる問題が頻発するのです。 解決もできないし一旦あきらめました。AZ-Aが崩壊しないことを祈りながら運用することにします。 AZ-Aが全く使えなくなった場合は、その時点からAZ-Bにリストアすることを検討します。
インスタンスのマシンイメージ,DBのバックアップはS3に確保されてるから大丈夫として、mongodb,redisのデータはAZ-Aにしか存在しないので、 これらはAZ-A以外のところにバックアップを退避させておきます。
やること、要件など
下記が私の作業分担と認識してまして、このブログのネタになるものであります。
やること
- インフラ周りの新規構築
- Railsで動くオープンソーシャルなWEBサイト一式
- デプロイ周り
- リリース後の運用保守
- 障害時対応
- 負荷に応じてのインスタンス等リソース調整
- ソフトウェアアップデート
- テープカートリッジのマウント、デッキのクリーニング
- プリンタの用紙補給、ジャム時の対応、クリーニング
- 社内環境の整備
- 障害アラート通知
前提、要件
- AWSクラウドを使う
- ピーク時のアクセスは500req/secまで
- WEBサーバは5秒以内に応答する
- 負荷に応じてリソースを増減できるようにして、無駄な費用をかけないようにする
- 障害時になるべくはやく対応できるようにする
「どんなことがあってもサービスダウンはダメ!」という要件はないので、故障発生率、故障した場合の損失、運営収益なども考えて、妥当なインフラ構成が維持できるようにしたいと思います。
はじめに
はじめまして
とあるWEB屋で働いています。専任のインフラ担当者が日経先物225で大損ぶっこいて逃亡したらしく、新規案件の構築運用を急遽私がすることになりました。
インフラ周りが全く未経験というわけではないのですが、AWSクラウドを使うことや、オープンソーシャルなサイトを構築運用するのは初めてなります。
担当になってからリリースまで1ヶ月の期間程しかなく、必死になってAWSのサービスやソフトウェア構成などを試していたのですが、仕様変更などでリリースがのびてしまったので、少し余裕ができました。作業中は課題や作業メモをRedmineのチケットに残していたのですが、ドキュメントらしいものは全く残していませんでした。とはいってもきっちりしたドキュメントを書く能力もないので、どうせならブログにでもしてみるか思い、今この記事を書いてるところです。
サーバ周りの構築はある程度できてまして、過去のRedmineのチケットをもとにブログの記事にしていこうと思います。