感想:Site Reliability Engineering
Site Reliability Engineeringを読んだので、感想と個人的な各章のサマリ(備忘メモ)です。
shop.oreilly.com
Google - Site Reliability Engineering
なお私はソーシャルゲーム会社のインフラエンジニアで、CM対策などを含めた大規模インフラの経験はありますが、ビッグデータ的な分散処理基盤はあまり経験していません。
[感想]
SLO大事
SLO(Service Level Objectivesの略。稼働時間や遅延などのサービスの健全性を示す指標に対する目標値)の策定と自動化を進めて、インフラの雑務(toil)を削減することが大事だなと思いました。
「Error Budget」の概念に注目されている本ですが、「Error Budget」に基づいた運用をする/しないに関わらず、SLOを策定して自動化を進めることがSREチームのスタート地点になるのかなと思いました。
SLOを定めることで、サービスにとっての障害が何かを定義できて、作業スコープを明確にできます。その上で自動化を進めると、作業時間の短縮ができ、「Error Budget」に基づいたチーム運営もできるようになり、またインフラエンジニアの燃え尽きも防げるという主張は、その通りだなと思いました。
Error Budgetについて
私は賛否両方あると考えています。エンジニアの観点では賛成で、サービスの運用者としては悩ましいと感じました。
「Error Budget」としてクォーター毎にSLOを下回ってよい時間を定義しておき、「Error Budget」が残っているうちはアグレッシブなリリースをしたり、障害対応をon-call担当に任せて非on-call担当のエンジニアが開発に専念できるのは、エンジニアとしてはとても仕事がしやすいなと思いました。
一方でサービスの責任者から見ると、インフラは動いていてナンボなので、イノベーションのために多少のSLO違反は許容してというのは心理的に受け入れづらそう。極論すると、必死に盛り上げているサービスが、アグレッシブなリリースで水を差されたら、企画している人たちは嫌だろうなと。
そうしたデメリットをイノベーションが上回ると説得できればよいけど・・。「Error Budget」を低めに設定するとあまり意味がないので、まだいい落としどころが自分の中でついていないです。*1
[各章サマリ]
本を買わなくていいじゃん!とならない範囲で書きます。
「Chapter 1 : Introduction」もインパクトありましたが、「Chapter 21 : Handling Overload」と「Chapter 26 : Data Integrity: What You Read Is What You Wrote」が私のこれまでの業務・興味との重なりが大きく、特に面白かったです。
Chapter 1 : Introduction
SREとは?Error Budgetとは?という内容。
Chapter 2 : The Production Environment at Google, from the Viewpoint of an SRE
Googleのシステム構成について。Borgやストレージスタック、SpannerやChubbyなどの概要。
Chapter 3 : Embracing Risk
稼働率100%を目指すと超高コストになるので、コストやサービスの内容(売上や人命に関わるとかの特性など)を踏まえて、妥当なSLOを策定しようという話。
Chapter 4 : Service Level Objectives
SLOやSLIなどの用語説明と、いい感じのSLOの運用方針について。
Chapter 5 : Eliminating Toil
タイトルそのまま
Chapter 6 : Monitoring Distributed Systems
Googleの監視システムの思想について。
Chapter 8 : Release Engineering
Googleのコードのデプロイと、設定変更の本番反映についてちょっと具体的な話。
Chapter 9 : Simplicity
コードはシンプルなほうがいいとのこと。
Chapter 10 : Practical Alerting from Time-Series Data
Googleがどのように監視用のデータを時系列で作成・保持し、アラートを飛ばしていたかの詳細。
現監視システムでなく、過去に10年使っていたborgmonについての話。
Chapter 11 : Being On-Call
Googleが障害対応担当をどうローテーションしているかについて。
かなりエンジニアの燃え尽き(burn out)に気を配っている感じ。
Chapter 12 : Effective Troubleshooting
Googleの障害解析のアプローチについて。
全体的には普通のアプローチだけど、根本原因の追究が徹底的なのと、解決が自動化 or ツール化なのがGoogleらしい気がしました。
Chapter 14 : Managing Incidents
障害対応時の役割分担について。
Googleは簡単な障害は自動復旧の仕組みがあるので、人手が必要な大規模/複雑な障害の時の話。
Chapter 15 : Postmortem Culture: Learning from Failure
Postmortem(障害報告書)とは何か、どう運用しているかの話。
postmortem自体は、google以外の会社でも結構よく聞きます。
Chapter 16 : Tracking Outages
Googleが使っている障害管理システムのご紹介。
Chapter 17 : Testing for Reliability
Googleが実践しているテストの話。
Chapter 18 : Software Engineering in SRE
Auxonというキャパシティプランニングツールの構築を例にした、SREのソフトウェア開発について。
新ツールをどう社内で普及させるかという泥臭い話もあり。
Chapter 20 : Load Balancing in the Datacenter
GoogleのDC内のロードバランサについての話。
Googleはユーザのリクエストを、Chapter19のDNSで地理的に最適なDCに割り振ってから、本ChapterのとおりDC内でL7レイヤの振る舞いをするLBで負荷分散しているそうです。
Chapter 21 : Handling Overload
高負荷時の望ましい振る舞いと、その実装について。
個人的にあまり考えてこなかった内容なので、とても勉強になった章でした。
Chapter 22 : Addressing Cascading Failures
障害の連鎖をどう防ぐかという話。Chapter21から繋がっている内容で、具体的な対策もあり。
Chapter 23 : Managing Critical State: Distributed Consensus for Reliability
分散合意システムでどう信頼性を担保するかという話・・と思いますが、この分野の経験が足りなくてうまく読めなかった章です。
Chapter 24 : Distributed Periodic Scheduling with Cron
Googleの超大規模・分散システムでcron的な処理をどうやって実現しているかの話。
Chapter 25 : Data Processing Pipelines
GoogleはWorkFlowというツールを作って、いい具合にデータ処理を並列処理しているとのこと。あまりこの賞は深読みしなかったので違うかも・・
Chapter 26 : Data Integrity: What You Read Is What You Wrote
バックアップ/リストアの話と、データ不整合の対策について。
優秀なエンジニア達が、多くのコストをかけて、本気でデータ保全を検討して実装した内容が書いてある章で、すごく学びが多かったです。
Chapter 27 : Reliable Product Launches at Scale
Launch Coordination Engineering(LCE)という、サービスローンチを専門にしたエンジニアの紹介。
Chapter 28 : Accelerating SREs to On-Call and Beyond
新しくSREに加わったメンバーをOn-Callを担当できるまで育てるプロセス。
Chapter 29 : Dealing with Interrupts
SREチームはサービス横断的な組織で、また障害対応を受け持っているため、作業の割り込みが増えがちなので、それをどう処理するかという話。個人的には作業の割り込みは苦にならないけど、チームで苦手にしている人が多かったので、うまく配慮できるようになりたい。
Chapter 30 : Embedding an SRE to Recover from Operational Overload
運用負荷が高まっているチームにはSREを1名派遣して、根本原因を取り除いて立て直すhow to気味な章。
SRE的な仕事が根付いていないチームを、SRE文化に変えていくときにも参照できそうな内容。
Chapter 31 : Communication and Collaboration in SRE
SREとプロダクトチーム、または複数拠点に分散しているSREチーム同士が、どのように連携すると良いかのGoogleのケーススタディとベストプラクティス。
Chapter 32 : The Evolving SRE Engagement Model
SREがどうプロダクトに貢献するかという話。プロダクトのリリース後にチームに参加するパターン、開発中に参加するパターンを経て、フレームワークを提供するパターンに変遷してきたとのこと。
specファイルのRecommendsタグ
OpenSUSE用のタグ。CentOS等での扱い方は、OpenSUSEのガイドラインにあった。
openSUSE:Specfile guidelines - openSUSE
Recommends
SUSE's RPM supports the Recommends tag, but it was not available upstream until very recently. As a result, packages with Recommends will fail to build for OBS targets Fedora_18, RHEL_6 and CentOS_6, for example. A workaround is to exclude the Recommends tag from non-SUSE targets:
%if 0%{?suse_version}
Recommends: foo
%endif
Alternatively the Recommends tag could become a Requires tag so that a recommended package (foo) gets installed in any case on systems that do not support RPM Recommends:
%if 0%{?suse_version}
Recommends: foo
%else
Requires: foo
%endif
See also openSUSE:Build Service cross distribution howto
httpdをreloadしてログを切り替える際、ロギングに漏れが出るか
表題の内容を試したが、結果やはり漏れは出なそう。
そもそもは、logrotate時の動作が知りたかった。設定はデフォルトだと以下のような感じ。
$ less /etc/logrotate.d/httpd
/var/log/httpd/*log {
daily
rotate 30
missingok
notifempty
sharedscripts
postrotate
/sbin/service httpd reload > /dev/null 2>/dev/null || true
endscript
}
これを見て、reloadしたときにログに漏れが出ないのかがふと気になった。本当はapacheのreloadがどういった挙動をするか調べるべきだが、軽く調べてわからなかったため、手っ取り早く実証することにした。
手順としては連番の公開ファイルを作成し、順番にアクセスしている途中でアクセスログをリネーム(mv /var/log/httpd/access_log var/log/httpd/access_log.1)→httpdのリロード(sudo /etce/init.d/httpd reload)を行う。
アクセスするシェルは以下。
#!/bin/bash
for i in `seq 1 100000`
do
curl http://localhost/index$i.html
done
アクセスした結果は以下。
access_log.1(リネームした旧ファイル)
127.0.0.1 - - [19/Jun/2014:08:04:24 +0900] "GET /index10.html HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
127.0.0.1 - - [19/Jun/2014:08:04:24 +0900] "GET /index11.html HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
(中略)
127.0.0.1 - - [19/Jun/2014:08:05:42 +0900] "GET /index10972.html HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
127.0.0.1 - - [19/Jun/2014:08:05:42 +0900] "GET /index10973.html HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
access_log(reloadで生成された新ファイル)
127.0.0.1 - - [19/Jun/2014:08:05:42 +0900] "GET /index10974.html HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
127.0.0.1 - - [19/Jun/2014:08:05:42 +0900] "GET /index10975.html HTTP/1.1" 200 14 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
当然の結果だけど、安心した。
EC2(micro)上でのmysqlパフォーマンス設定と測定
最低限の設定のみで動かしていた、EC2にインストールしたmysqlですが、エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド(http://www.amazon.co.jp/dp/4774142948)を買ったので、チューニングしてみました。性能改善よりもmicroインスタンスでメモリが少ないため、メモリ消費量を抑える観点で設定を実施しています。学習用の個人サイトでアクセスがほぼなく、レスポンスが遅い訳ではないため。
[mysqld]
max_connections = 1024
table_open_cache = 2048
table_definition_cache = 1100
key_buffer_size = 16M
read_buffer_size = 128K
read_rnd_buffer_size = 256K
join_buffer_size = 128K
slow_query_log
long_query_time = 3
log_queries_not_using_indexes
open_files_limit = 5500
innodb_buffer_pool_size = 100M
innodb_additional_mem_pool_size = 1MB
innodb_autoextend_increment = 64M
innodb_log_files_in_group = 2
innodb_log_buffer_size = 1M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_thread_concurrency = 8
innodb_commit_concurrency = 5
書籍から変更しているパラメータと理由は下記のとおり。
・table_open_cache
→テーブルが2つのため
・read_buffer_size、read_rnd_buffer_size、join_buffer_size
→デフォルト値(未設定状態)でも性能が出ていたため
・innodb_buffer_pool_size
→扱うデータ量が少ないため(1ヶ月稼働で現在8MB程度。1年間もてばよい。)
・innodb_additional_mem_pool_size
→テーブルが2つで少なく、またデフォルト値(未設定状態)でも性能が出ていたため
・innodb_thread_concurrency 、innodb_commit_concurrency
→更新量も参照量も少ないため、書籍より数値を減らして様子見
設定前後の性能は、Chromeの開発者機能でサイト(http://ysfj.net/umakatter)のhtml(mysqlでの参照結果を含む)が戻ってくる時間を20回計って確かめました。キャッシュは無効化して、毎回別のページに遷移しています。
設定前→0.225秒
設定後→0.201秒
性能が落ちてなかったので、しばらくこれで様子見します。
mysqlslapも試したかったですが、実際のWebアクセスにおけるクエリで測定したほうが妥当と考え、この計測方法としました。
Webサービス即日開発の実践4(Perl)〜文字コードの扱い
この開発で文字コードを気にしたのは、以下の2箇所です。
・Perlのutf8フラグ
・MySQLの4バイト文字対応
【utf8フラグ】
Perlで文字を文字列として扱う場合(表現が微妙ですが)、コード内ではutf8フラグをOnにする必要があり、コードの外部(例えばDBや標準入力/出力など)へ文字列を渡す場合はutf8フラグをOffにする必要があります。
今回のプログラムでの文字情報は
1.TwitterAPI→Perl
2.Perl→MySQL
3.MySQL→Perl
4.PerI→html
の四ヶ所で移動します。
1はNet::Twitterでツイート情報を取得した場合、値はutf8フラグがOnされている状態でしたので、追加でエンコードはしていません。
2はMySQLに書き込む際、DBIx::Class::Schema::Loaderを使用しており、そこでUTF8Columnsを用いてutf8フラグをOffにしました。
3,4はMySQLからutf8フラグOffで取得した文字列をそのままhtml に出力しています。(のつもりです。)
【MySQLの4バイト文字対応】
ツイートやユーザ名に😄といった記号など、4バイト文字が含まれるケースがあります。こうした文字をMySQLで扱う場合、データベースの文字コードをutf8mb4にする必要があります。実装はこちらhttp://d.hatena.ne.jp/kaze-kaoru/touch/20110829/1314598374を参考にして
・データベースの文字コードをutf8mb4で作成
・Perlでデータベースに書き込む際、mysql_enable_utf8を指定しない
・my.cnfのclientでdefault-character-set=utf8mb4と設定(リンク先の通りにperlグループを作る方がよいですが、デフォルトではclientグループの値が参照されるので、clientに設定しても動きます。)
・DBIx::Class〜で接続する際に、mysql_read_default_file=でmy.cnfを指定
することで、無事に絵文字などもMySQLに格納出来ました。MySQLのバージョンが5.5以上でないといけないことも、要注意です。
Webサービス即日開発の実践3(Perl)〜Twitter API 1.1対応とhttps対応
しばらく更新が滞ってしまいました。
投稿する時間も惜しんでコーディングしていて、なんとか無事に動くコードが完成しました。
詰まった箇所は、主に以下の2つです。
①Twitter APIの仕様変更
②4バイト文字(絵文字や非常用漢字)の取り扱い
①は、サンプルコードが2010年のものでTwitter API 1.0を利用していたため、Twitter API 1.1に対応させる必要がありました。また開発中の1/14にhttpsも必須(http://news.mynavi.jp/news/2014/01/16/459/)になったため、その対応も実施し、以下のように書きました。
package Umakatter::CLI::FeedReader;
use Moose;
use Carp;
use URI;
use XML::Feed;
use Umakatter::CLI::FeedReader::Tweet;
use strict;
use Net::Twitter;
use utf8;
has 'consumer_key' => (
is => 'ro',
isa => 'Str',
default => 'xxxx'
);
has 'consumer_secret' => (
is => 'ro',
isa => 'Str',
default => 'xxxx'
);
has 'access_token' => (
is => 'ro',
isa => 'Str',
default => 'xxxx'
);
has 'access_token_secret' => (
is => 'ro',
isa => 'Str',
default => 'xxxx'
);
no Moose;
sub entries {
my $self = shift;
my $nt = Net::Twitter->new({
traits => [qw/API::RESTv1_1/],
ssl => 1,
consumer_key => $self->consumer_key,
consumer_secret => $self->consumer_secret,
access_token => $self->access_token,
access_token_secret => $self->access_token_secret,
});
my $r = $nt->search({q=>'美味い店', lang=>"ja", count=>100});
return @{$r->{statuses}};
}
sub tweets {
my $self = shift;
my @tweets;
my @entries = $self->entries;
for my $entry (@entries) {
push( @tweets,
Umakatter::CLI::FeedReader::Tweet->new($entry)
);
}
return \@tweets;
}
初期はssl => 1ではなく、api_urlにhttpsのURLを指定して実装していたのですが、https接続でエラーになったため、http://za.toypark.in/html/2009/10-21.htmlを参照して、IO::Socket::SSL と Mozilla::CAを導入しています。
②は、また次の記事にします。
Webサービス即日開発の実践2(Perl)〜テストスクリプト作成
Catalystが入ったので、Twitterに検索語を投げて検索結果を取得するテストスクリプトを作成し、フィジビリティを確認します。
こちらもまずは本の通りに書いてみましたが、エラーに。本が2010年のものであり、前提としているTwitterAPIが1.0のため、認証周りがだめでした。
そこで以下のサイトを参照にトライ&エラーを繰り返し、なんとか検索語に対する結果を取得できました。
Twitter API
https://dev.twitter.com/docs/api/1.1/get/search/tweets
Net::Twitter
http://search.cpan.org/~mmims/Net-Twitter-4.01000/lib/Net/Twitter.pod
ブログ類
http://www.fukudat.com/wiki/ja/PerlによるTwitter_botの作り方
http://ktz.sblo.jp/article/66724679.html
http://tech.voyagegroup.com/archives/465806.html
Perl(というかスクリプト言語)自体が初めてであり、コーディングの学習も兼ねた実践のため、文字コードにも苦戦しました。なので丁寧に解説してあった、Voyage Groupさんのブログ記事はとても参考となりました。
書いたコードは下記の通りで、実行時に引数で指定した文字列を検索し、結果を標準出力します。まだ「binmode STDOUT, ":utf8";」はおまじないで書いてしまっているので、調べて不要なら消したい。
=======================
#!/usr/bin/perl
use strict;
use Net::Twitter;
use utf8;
binmode STDOUT, ":utf8";
use Encode qw(decode_utf8);
my $consumer_key = "xxx";
my $consumer_secret = "xxx";
my $access_token = "xxx";
my $access_token_secret = "xxx";
my $nt = Net::Twitter->new({
traits => [qw/API::RESTv1_1/],
consumer_key => $consumer_key,
consumer_secret => $consumer_secret,
access_token => $access_token,
access_token_secret => $access_token_secret
});
do_search($_) foreach @ARGV;
sub do_search {
my $term = shift;
my $r = $nt->search({q=>decode_utf8($term), lang=>"ja", count=>100});
for my $status ( @{$r->{statuses}} ) {
print "$status->{text}\n";
}
}
=======================