Raccoon Tech Blog [株式会社ラクーン 技術戦略部ブログ]

株式会社ラクーン 技術戦略部より、tipsやノウハウなど技術的な話題を発信いたします。

インフラ

CORECを支える技術 ~ログ監視編~ fluentd-AmazonSNS連携

こんにちは。なべです。
CORECの開発を担当しています。
CORECを支える技術 ~チケット駆動編~』に続き、『CORECを支える技術』シリーズの第2弾としてCORECのログ監視について書きたいと思います。

旧ログ監視の問題と対策

当初、CORECのログ監視はログ集約にくっつける形で始まりました。
ログ集約はログファイルを一定時間ごとにバックアップする仕組みで、各サーバーのログをfluentdで集め、1時間ごとに圧縮しS3に格納しています。
ログ監視は、その集約されたログを社内の監視システムで取得し、エラーログがあった場合にメールとIRCで通知するというものです。

この仕組には大きく以下の2つの問題がありました。

  • タイムラグ
    ログの監視をできるタイミングがS3に圧縮ファイルがアップされた後なので、検知するまで1時間のタイムラグが発生する可能性がある。
  • サービスレベル
    ログ監視を社内のサーバーに配置していて、消防点検や停電または、サーバーメンテナンス時に監視できないなどサービスレベルにも問題がある。

これらの問題に対応するためfluentdに集めたタイミングでエラーログを抽出し、AmazonSNS経由でメール通知することにしました。
これにより、社内のサーバーも経由しなくなり、タイムラグの問題・サービスレベルの問題を同時に解消できました。

CORECログ監視概要図

実施内容

この対応で実施したことは以下の4つです

  1. fluentdにプラグイン追加
  2. AmazonSNSに通知用Topic作成
  3. メールBody整形用のERB追加
  4. fluentdの設定ファイル記述

1. fluentdにプラグイン追加

以下の2つのプラグインを追加しました。

fluent-plugin-sns
Amazon SNS通知用
fluent-plugin-rewrite-tag-filter
エラーのフィルタ・レベルごとの分岐処理用
fluentdはrubyで動いているのでRubyistならお馴染みのgemコマンドでインストールできます。
この時fluentdが使っているrubyの環境を指定する必要があります。
特にfluentdにバンドルされたrubyを使用している場合は注意してください。
sudo /usr/lib/fluent/ruby/bin/gem install fluent-plugin-sns
sudo /usr/lib/fluent/ruby/bin/gem install fluent-plugin-rewrite-tag-filter

2. AmazonSNSに通知用Topic作成

Amazonの管理コンソールからSNSサービスを選択し、画面に従ってTopic及びSubscritionを作成します。 特に迷うことはないかと思いますが、注意するポイントは以下の2点です。

  • メールアドレスを登録するとメールがくるのでクリックして認証すること
  • 作成したTopicの権限設定を確認する
    権限設定とは作成したTopicに対して、通知に利用するAWSアカウントがこのトピックに投稿する権限のことです。もし権限がない場合には、[ポリシーの編集]から許可を与えてください。

3. メールBody整形用のERB追加

CORECの場合、fluentdが受け取るレコードは改行が\nにエスケープされていて読みづらいため、テンプレートで改行コードに変換しました。
以下のコードをerbファイルとして保存しておきます。
これだけのコードですが受け取った側の負担がだいぶ違います。特にスタックトレースで威力を発揮します。

<%record.each do |k,v|%>
<%="#{k}:#{v.to_s.gsub(/\\n/,"\n")}" %>
<%end%>

4. fluentdの設定ファイル記述

それでは最後にメインとなるfluentdの設定ファイルの編集をします。
変更/追加のポイントは以下の3点です。

4-a. copyプラグインで処理を2系統に分岐する
4-b. rewrite_tag_filterプラグインでエラーレベルごとにフィルタリングして、新しいタグを設定する
4-c. snsプラグインで通知するためのmatchディレクティブを追加する

それぞれ簡単に説明します。

4-a. copyプラグインで処理を2系統に分岐する

fluentdは最初にマッチしたmatchディレクティブだけを実行しますので、同じレコードに対して複数の処理をしたい場合にはcopyプラグインを使用します。
※copyプラグインは標準のプラグインなのでインストールをする必要はありません。

matchディレクティブと同等の内容をstoreタグに書くことができ、同じレコードに対して処理を複数記述できるようになります。
今回の対応では従来のS3への送信する処理をcopyで分岐し、SNSに送信する処理を追加します。

<match tag.log>
  type copy
  <store>
        従来の処理
  </store>
  <store>
        追加したい処理
  </store>
</match>

4-b. rewrite_tag_filterプラグインでエラーレベルごとにフィルタリングして、新しいタグを設定する

ただ単に前項の「追加したい処理」にSNS送信のディレクティブの内容を書いてしまうと、アプリケーションが出力したログすべて(エラーでないものも含めて)通知されてしまいます。
そこでログの内容によって処理を振り分けるにはrewrite_tag_filterプラグインを使用します。
レコードのキーを指定して、検索する文字列または正規表現を指定し、それにマッチした場合に新しいタグを指定する仕組みです。
今回はログレベルがFATALもしくはERRORの場合にシステム通知がしたかったので、ログレベルが格納されているキー(lebel)に対して、FATAL・ERRORそれぞれルールを作成しました。
複数のルールを設定するにはrewriteruleの後の数字をインクリメントして記述します。

  <store>
    type rewrite_tag_filter
    rewriterule1 level FATAL corec.rails.sns_fatal
    rewriterule2 level ERROR corec.rails.sns_error
  </store>

4-c. snsプラグインで通知するためのmatchディレクティブを追加する

typeにsnsを指定して、内容にはAWSユーザーにはお馴染みのパラメータ群を設定していきます。 sns_body_templateのパスはtd-agentが参照可能な場所にERBを配置して指定してください。

<match corec.rails.sns_fatal>
  type sns
  sns_topic_name for_system_notice_topic
  aws_key_id xxxxxxxxxx
  aws_sec_key xxxxxxxxxx
  sns_endpoint sns.ap-northeast-1.amazonaws.com
  sns_subject [COREC ALERT]corec/rails
  sns_body_template /etc/td-agent/erb/sns_body_template.erb
</match>

fluentd設定ファイルイメージ(全体)

変更前後の設定ファイルは以下のとおりです。
行はだいぶ増えていますが、それぞれの設定内容に難しいところはありませんでした。

  • 設定ファイル:修正前
<match corec.rails>
    S3に送信する設定(省略)
</match>
  • 設定ファイル:修正後
<match corec.rails>
  type copy
  <store>
        従来のS3に送信する設定(省略)
  </store>
  <store>
    type rewrite_tag_filter
    rewriterule1 level FATAL corec.rails.sns_fatal
    rewriterule2 level ERROR corec.rails.sns_error
  </store>
</match>
<match corec.rails.sns_fatal>
  type sns
  sns_topic_name for_system_notice_topic
  aws_key_id xxxxxxxxxx
  aws_sec_key xxxxxxxxxx
  sns_endpoint sns.ap-northeast-1.amazonaws.com
  sns_subject [COREC ALERT]corec/rails
  sns_body_template /etc/td-agent/erb/sns_body_template.erb
</match>
<match corec.rails.sns_error>
 (省略)
</match>

設定ファイル修正後、fluentdを再起動すれば反映されます。
※fluentdの連携をしている場合には送信元のfluentdも再起動することをおすすめします。
 私が設定した時には送信側が受信側を見失ってログの送信が止まってしまいました。

新たな問題

前項までの対応により、旧ログ監視の問題は解消し、 リアルタイムで、かつサービスレベルの高い仕組みができました。
が、新たな問題がいくつか発生しました。

  1. フィルタリングが無くなったのでERROR・FATALのレベルのログが全部飛んでくる
  2. 異常が発生するとガンガンメールがくる
  3. IRCに通知されない

新たな問題 1. ERROR・FATALのレベルのログが全部飛んでくる
→ 対策: 随時チケット化してエラーログの根本原因を潰す!

実はログを検索してメールを飛ばす処理の中でホワイトリストでフィルタリングして、通知するものを絞っていました。
その仕組みが無くなったのでERROR・FATALのレベルのログが全部飛んでくるようになってしまいました。 fluentdでフィルタリングするという選択肢もありましたが、対応の必要のないFATALやERRORを出すシステム側を修正すべきということで、チケット化してエラーの根本対応を実施。

新たな問題 2. 異常が発生するとガンガンメールがくる
→ 対策: 覚悟しておく!

従来のログ監視は1時間に1回ログを検索してメールを飛ばす処理だったため過去1時間に何件エラーが発生していても1通の通知にまとまっていました。
それがリアルタイムでエラー1件毎に通知されるため、異常事態にはメールが大量に送信されてしまいます。
実際に外部ベンダー側にトラブルがあり、復旧までに4時間かかった際に断続的に300通送信されてきました。orz
しかし、異常が発生しているのだから通知はあるべきでそれを止めるのは本末転倒ということで受け入れることにしました。

新たな問題 3. IRCに通知されない
→ 対策:社内で導入を進めている新監視システムへ

メールと同時にIRCに通知して、業務時間中はメールよりも気づきやすいように対策していましたが、IRCは社内サーバーで稼働しているため利用できませんでした。
現状は通知のために旧監視システムと並行稼動をしていますが、実際にはリアルタイムに飛んでくるメールに十分に気づけています。
今後は社内で進めているSlackのサービスを利用した障害対応フローに移行する予定です。
AmazonSNSに通知先を増やすだけで対応できるはずです。

まとめ

  • fluentdにプラグインを追加することで簡単にAmazonSNS経由の通知を実現できました。
    こういったプラグインが充実しているのもfluentdの魅力だと思います。(fluentdの導入については『fluentd(td-agent) の導入』を参照してください。)

  • ログ監視も即時性・対障害性の高いシステムを構築して確実で迅速な検知が必要です!
    ログ監視は最後の砦であり将来発生する障害の芽を発見するアンテナだと思っています。これからもCORECとともにCORECを支える運用システムもブラッシュアップしていきます。

  • 無駄なログは技術負債。根本から除去しましょう!
    無駄なログがたくさん通知されてくると、本来拾わなければいけないログを見落としてしまう可能性があります。そのために通知するログを絞ることは大切ですが、安易にフィルタリングをするのではなく、一つ一つ原因を突き止めて対応すべきところはプログラムで対応し、技術負債を残さないようにしたいですね。

iptablesを利用してNATサーバを構築

こんにちは、えのもとです。

弊社では本番リリース直前検証環境として、ステージング環境を利用しています。
この環境が古くなってしまったため、環境再構築を実施することになりました。

再構築する上で、今後のこともあるので再構築しやすい環境に切り替えることにしました。
構築上でポイントとなったのは以下の2つです。
  • 本番環境はVMで稼働しているので、そのバックアップを活用する
  • サーバ設定(ネットワーク設定など)はできる限り変更したくない
これらを達成するため、閉鎖されたネットワークを構築しその中に環境を構築することにしました。そこで問題となるのは閉鎖された環境内外との通信なのですが、iptablesを利用してnatサーバを構築することで解決しました。今回はこのiptablesの検証の様子を紹介します。

NATとiptablesについて

今回環境を構築するに当たり、以下を参考にしました。

@IT:第10回 IPパケットの構造とIPフラグメンテーション
@IT:natテーブルを利用したLinuxルータの作成

要点だけ羅列すると以下のような内容になります。
  • パケット内IPヘッダに送信元アドレスと宛先アドレスが記載されている。
  • 送信元アドレスを変換するNATのことをSNAT
    宛先アドレスを書き換えるNATのことをDNATと呼ぶ。
  • iptablesのSNATはPOSTROUTINGチェインで、DNATはPREROUTINGチェインで行う。


検証内容と環境の紹介


存在しないIPアドレス宛てのパケットをiptablesでnatし、別のIPアドレスで受けとることができるか検証しました。検証には以下のような環境を利用しました。

セグメント
  1. A(192.168.100.0/24)
  2. B(192.168.200.0/24)
仮想マシン
  1. vm01           eth0:【 192.168.100.1 】
  2. vm02           eth0:【 192.168.100.2 】 eth1:【 192.168.200.2 】
  3. vm03           eth0:【 192.168.200.3 】
  4. gateway_A   eth0:【 192.168.100.254 】
  5. gateway_B   eth0:【 192.168.200.254 】

eno_2015120901

vm01から存在しないIP【 192.168.100.3 】 宛にパケットを送り、
vm02でNATさせてvm03のIP【 192.168.200.3 】へ届けます。
vm01からするとIP【 192.168.100.3 】 が存在するように見えます。(赤線)
実際はvm02を経由してセグメントBにいるvm03へ届きます。(青線)

eno_2015120902


事前確認

iptablesに余計な設定があると誤作動の原因になるので、一旦どのサーバもクリアしておきます。

# iptables クリア
iptables -F
iptables -F -t nat
# iptables 確認
iptables -L
iptables -L -t nat
# iptables 保存
service iptables save

また、vm02はパケットを転送する必要があるので以下の設定を加えます。
vi /etc/sysctl.conf
#net.ipv4.ip_forward=0
net.ipv4.ip_forward=1

sysctl -p



ステップ 1 : vm01にルーティング追加

手始めにvm01から【 192.168.100.3 】宛のpingを実行し、その様子をvm01とvm02の両方でtcpdumpを使いパケットをキャプチャして確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.1 icmp_seq=2 Destination Host Unreachable
From 192.168.100.1 icmp_seq=3 Destination Host Unreachable
From 192.168.100.1 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:28:24.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:25.003469 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:26.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:28.004484 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46

vm01から【 192.168.100.3 】へパケットを送る場合、同一セグメント内なのでゲートウェイなどは経由せず直接通信することになります。直接通信するためには相手のMACアドレスを知る必要があります。ARPリクエストをブロードキャストへ送信しMACアドレスを探している様子が確認できました。

これを無理やりvm02へ送るようにvm01にスタティックルーティングを追加します。

# vm01
[root@vm01 ~]# ip route add 192.168.100.3 via 192.168.100.2 dev eth0
[root@vm01 ~]# ip route show
192.168.100.3 via 192.168.100.2 dev eth0
192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.1
169.254.0.0/16 dev eth0  scope link  metric 1002
default via 192.168.100.254 dev eth0

再度vm01から【 192.168.100.3 】へpingを実行し確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.2: icmp_seq=2 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=3 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=4 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2 icmp_seq=2 Destination Host Unreachable
From 192.168.100.2 icmp_seq=3 Destination Host Unreachable
From 192.168.100.2 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:48:19.136230 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 1, length 64
13:48:20.135528 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 2, length 64
13:48:21.135501 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 3, length 64
13:48:22.135497 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 4, length 64

【 192.168.100.3 】宛のパケットがvm02へ届くようになりました。



ステップ2 : vm02にDNAT設定を追加

次に存在しない宛先IPアドレス【 192.168.100.3 】をvm03【 192.168.200.3 】変換するため、vm02のiptablesにDNATの設定を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A PREROUTING -d 192.168.100.3 -i eth0 -j DNAT --to-destination 192.168.200.3
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination


vm01から【 192.168.100.3 】へpingを実行し、vm02とvm03で確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.318556 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 1, length 64
13:55:28.318586 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.318498 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 2, length 64
13:55:29.318513 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.318483 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 3, length 64
13:55:30.318496 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.318515 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 4, length 64
13:55:31.318529 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.381794 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.381711 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.381702 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.381730 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

vm03にvm01のPINGが届いていることが確認できました。



ステップ3 : vm02にSNAT(NAPT)設定を追加

vm01から送るpingは無事vm03へ届けることができたのですが、vm03がセグメントAの情報を持っていないため、送信元であるvm01に返信することができません。そのため、vm02へSNAT(NAPT)を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A POSTROUTING -o eth1 -s 192.168.100.0/24  -j MASQUERADE
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  192.168.100.0/24     0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
64 bytes from 192.168.100.3: icmp_seq=1 ttl=63 time=0.235 ms
64 bytes from 192.168.100.3: icmp_seq=2 ttl=63 time=0.164 ms
64 bytes from 192.168.100.3: icmp_seq=3 ttl=63 time=0.164 ms

# vm02
[root@vm02 ~]# tcpdump -i eth0 src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915680 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915507 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915500 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915525 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 4, length 64

# vm02
[root@vm02 ~]# tcpdump -i eth1 src host 192.168.200.2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915714 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915522 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915513 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915546 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.200.2 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
14:04:40.978924 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.978721 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.978718 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.978754 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

無事にpingが返ってくることが確認できました。



まとめ

作成した環境を利用して、どんなサービスが利用できるのかも検証しました。

動作OK : ssh http https dns mysql
動作NG : ftp oracle

ipヘッダ以外でipのやり取りをするものについては正常に動作しないのですが、動作したものについては、特に問題もなく使えることが分かりました。

以上です。何かの役に立てばと思います。

CORECを支える技術 ~チケット駆動編~

こんにちは。なべです。
CORECの開発を担当しています。
これから何回かに分けて、『CORECを支える技術』と題してCORECの開発について書いてみようかと思います。
今回はチケット管理と構成管理との連携についてCORECでの取り組みをご紹介したいと思います。
 
 
corec

毎週リリースのサイクルを維持するためのポイントは自動化

CORECは2014年3月17日に初回リリースをしてから、80回を超えるリリースを行っています。(2015年5月時点)
その内容は、機能追加からデザイン変更やバグフィックス、文言変更まで大小さまざまですが、ほぼ週1回のペースを守っています。それは、より早くフィードバックサイクルを回し、お客様に喜んでいただける機能の追加や改修をより早く届けたいと考えているからです。

このペースを維持するために、いくつかのツールを利用したりそれらを連携する仕組みを構築しています。
できるだけ自動化することによって生産性の向上ヒューマンエラーの極小化を目指します。
 

その一端を担うのがチケット管理と構成管理との連携です。
この仕組みがCORECの開発を飛躍的に安心・安全にしてくれます。

なぜ、チケット駆動開発と構成管理?

なぜ、チケット管理と構成管理が連携すると安心・安全になるのでしょうか?

「やった(ような気がする)」のヒューマンエラー

チケット駆動開発は、チケットが正しく完了しそれがリリースされる必要があります。
開発者にチケットを渡し、そのチケットの完了を各担当者に任せると、やはり人間ですからミスが発生することがあります。 たとえば、実装したつもりが実装していなかった、実装したがコミットせずにチケットを完了してしまった、といったことがあげられます。

チケットの対応確認は手間

ですので、管理する側はチケットの内容がリリースされるかどうかを確認する必要があります。
CORECでは構成管理からリリース対象を抽出してリリースするフローになっているので、 管理する側から見てリリースされるかどうかを確認する一番確実な方法は構成管理を確認することです。 しかし、手作業でチケットの一覧と構成管理のログを突き合わせて確認するのは、根気と注意力のいる作業です。 少なくとも私には無理です(^^;

だから、自動化!

そこで、構成管理から自動的に連携します。
構成管理にコミットされたことをチケットに反映することによって、チケット管理システムで対応状況を正確に把握できるようになります。 副次的な効果として、開発者側も対応してコミットしたものが自動的に”対応済み”に変わっていくとちょっとした達成感が得られるという心理的効果もあり、 、それが、コミットコメントにチケット番号を含める動機にもなるので、ログで対応内容が把握しやすくなりました。


使用しているツール

まずはどのようなツールを使用しているかを説明します。

  • Backlog(チケット管理)
  • git
  • jenkins

Backlog

CORECではすべてのタスクをチケット化します。
そのチケットの管理にはBacklogというWebサービスを利用しています。

このチケットに割り振られたチケット番号がすべてのキーになります。

git

構成管理はgitを利用しています。
社内で管理しているサーバー上にあります。
CORECでは単純にgitを使うのではなく、git-flow()というブランチモデルを利用しています。

git-flowブランチモデル

このgit-flowはCORECのスピードを保つ一つの大きな役割を果たしています。
本番(master)・開発(develop)のブランチをキレイに保ちつつ、本番で発生した問題に対応するブランチ(hotfix)、 日々の機能開発をするブランチ(feature)が適正に管理できる素晴らしい仕組みです。
この仕組とgitの的確なマージのおかげで開発者は同時に走っている他の開発のことをほとんど意識することなく自分のペースで開発を進めていくことができます。

※参考:『いまさら聞けない、成功するブランチモデルとgit-flowの基礎知識』(http://www.atmarkit.co.jp/ait/articles/1311/18/news017.html)

gitのルール

git-flowを利用することでgitの使い方はおのずと決まってきますので、gitを使う上で決めているルールは以下の5点ほどです。

  • 開発作業はfeature/hotfixブランチで行うこと
  • feature/hotfixのブランチ名にはチケット番号を使用すること
  • コミットコメントにはチケット番号を含めること
  • finishは第三者が行うこと
  • developにマージするコードはテストが通るものであること

jenkins

皆さんご存知のCIツールです。 gitとBacklogの連携部分を担います。


連携の設定

corec-2015-06-006

featureがfinishされ、gitサーバーのdevelopにpushされると、git-hookからjenkinsのjobをキックしてビルドが開始します。
ビルドでは自動テストやメトリクスの取得など実施し、結果をIRCに通知します。
ビルドが成功した場合には、開発評価環境に自動的にデプロイします。
逆に失敗した場合にはBacklogに課題チケットが作成されるので他の開発者に冷ややかな目で見られることになります。

それでは実際の連携の方法を解説していきたいと思います。
すべて同じツールを利用していなくても、部分的にも利用できるところはあるかと思いますので、参考にしていただければと思います。

構成管理(git/git-hook)とジョブ管理(jenkins)

まずは構成管理が更新されたことをジョブ管理に通知します。

git(git-hook)側の設定

gitにはgit-hookというイベントトリガの仕組みが用意されています。
いろいろありますが、詳細はこちらを参照してください。
今回はpush後に呼ばれるpost-updateというトリガを利用します。
トリガを利用するには、gitリポジトリのルートパス以下のgithookフォルダにトリガの名称のファイルを置きます。
同じフォルダに.sampleファイルがあるのでリネームしてファイルを作成します。

[gitリポジトリのルートパス]/githook/post-update

例えば以下のようになります。この例ではブランチ名を取得してdevelopだったらjenkinsの”corec”jobをキックしています。

#!/bin/bash
BRANCH=$(git rev-parse --symbolic --abbrev-ref $1)
if [ "${BRANCH}" = "develop" ]; then
  curl http://[jenkins-host]/job/corec/build
fi

jenkinsのjobの実行にアクセス制限をしていない環境では、こんな簡単なcurlコマンド1行でjobを実行できます。

jenkins側の設定と認証付きの連携の設定

jenkinsのjobの実行にアクセス制限を設定する場合は以下の2つの方法があります。

  1. jenkinsそのものにユーザー認証をつける
  2. jobの実行にパスワードをかける

jenkinsそのものにユーザー認証をつけるには、jenkinsの Jenkinsの管理>グローバルセキュリティの設定から「セキュリティを有効化」して認証方法を選択・設定する必要があります。 その方法については、ここでは省略します。

以下、上記2つのアクセス制限を設定した場合のgit-hookとjenkinsの連携について説明します。

ユーザー認証情報(APIトークン)の取得方法

jenkinsそのものにユーザー認証をつけた場合、APIトークンが必要になります。
具体的には、APIトークンをパスワードとしてベーシック認証を行います。

  1. 実際にgitサーバーからアクセスするユーザでjenkinsにログインします。
  2. ヘッダーの右側にあるユーザ名のリンクをクリックすると、開発者というメニューが表示されます。
  3. 設定メニューをクリックします。
     
    corec-2015-06-002
     
  4. 右のペインに「APIトークン」のカテゴリが表示されます。
  5. APIトークンの表示をクリックすると、APIトークンが表示されるのでそれを控えておきます。
     
    corec-2015-06-003  
     
job実行の認証情報(認証トークン)の設定方法

jobの実行にパスワードをかける場合、認証トークンが必要になります。 実行パラメータに認証トークンを渡します。

  1. jobの設定画面を開きます。
  2. ビルド・トリガのリモートからビルドのチェックボックスをチェックし、表示された認証トークンのボックスに任意の文字列を設定します。
    ここに入力したものがそのままパスワードになります。
    ※ 認証トークンをjobに設定した時点で先ほどサンプルに書いたcurlコマンドではアクセスできなくなりますので、稼働中の仕組みを触る場合はご注意ください。
     
    corec-2015-06-001

認証付きでjenkinsのjobをリモートから実行する方法

最後に取得した情報を元に[repos]/githook/post-updateを編集します。
curlでベーシック認証&POSTするように以下のように書き換えます。

#!/bin/bash
BRANCH=$(git rev-parse --symbolic --abbrev-ref $1)
if [ "${BRANCH}" = "develop" ]; then
  # 認証トークンをパラメータとして送るのでPOSTにして、できればhttpsにしておいたほうが良い  
  curl -u corec_jenkins:[APIトークン] -X POST \
  http://[jenkins-host]/job/corec/build?delay=0sec&token=[認証トークン]
fi

ジョブ管理(jenkins)-チケット管理(Backlog)

次にジョブ管理から、構成管理の更新をBacklogに通知します。
仕組みとしては、ジョブ管理の中でシェル(プログラム)を起動し、Backlogの提供しているAPIを叩きます。
以下の2つのステップで実現しています。

  1. 更新する対象のチケットの取得
  2. Backlogへ通知

それではそれぞれ説明します

1.更新する対象のチケットの取得

更新する対象のチケット番号はjenkinsの変更履歴を解析して取得します。
gitのlogから取得しても良かったのですが、jenkinsは場合によっては複数のリビジョンをまとめて対象にする場合があるで、jenkinsのjobで管理されている変更履歴から取得する方法を採りました。
jenkinsの変更履歴の取得にはjenkinsのリモートAPIを利用します。
 
http://[jenkins-host]/job/corec/[build-no]/api/json
 
このURLのレスポンスでビルドの情報がJSONで取得できます。
取得したjsonのchangeSet以下が変更履歴のリストになるのでその中からチケット番号を拾い出しています。
 

2.Backlogへ通知

Backlogへの通知はコメント登録をすることによって実現しています。
Backlogへの接続にはスペースIDAPIキーが必要になりますが、取得方法は以下のとおりです。

  • スペースID
    Backlogのサブドメインの部分です。
    https://[スペースID].backlog.jp
     
  • APIキー
    Backlogの個人設定のAPIタブを選択して登録ボタンをクリックすると生成されます。
     
    corec-2015-06-004

BacklogのAPI

BacklogのAPIライブラリはこちらに各種言語のものが紹介されています。
CORECではBacklog4Jを利用しています。
Javaを選んだ理由は当時私が扱いやすいものを選びました(^^;

Backlog4Jでの接続とコメント登録は非常に簡単で数行で実装可能です。
その他、いろいろな操作ができますので、詳細はgithub - backlog4jを参照してください。

// Backlogへ接続
BacklogConfigure configure = new BacklogJpConfigure("[スペースID]").apiKey("[APIキー]");
BacklogClient backlogClient = new BacklogClientFactory(configure).newClient();

// ステータスを変更し、コメントを登録
UpdateIssueParams params = new UpdateIssueParams(issueKey);
params.status(Issue.StatusType.Resolved);  //ステータスを処理済みにする
params.assigneeId([担当者のID]); //担当者を変更する
params.comment(comment); //コメントを付加する
backlogClient.updateIssue(params);

ちなみにコメントは以下のように登録しています。
Backlogに登録するコメントにビルドURLを含めておくことで同時になにがコミットされたのか参照しやすくなるのでお勧めです。

変更がdevelopにコミットされました.

commiter:corec taro
commitId:xxxxxxxxxx9c6bc1adf5d3319f8cb49598
comment :COREC-999 jenkins自動登録機能

refer :http://[jenkins-host]/job/corec/[build-no]/changes

jenkins Backlogプラグイン(おまけ)

jenkinsにはBacklogプラグインがあります。

いくつか便利だと思った機能をご紹介します。お試しください!

  • jenkinsへのログインをBacklogユーザーで認証する。
  • 変更履歴にチケット番号があると自動的にBacklogへのリンクにしてくれる。
    先ほどのコメントのサンプルにあるようにBacklogに登録するコメントにビルドURLを含めておくと、Backlogとjenkinsの変更履歴が相互リンクできるようになります。
  • 失敗ビルドの場合Backlogにチケットを登録してくれる。
  • jobのメニューにBacklogへのリンクが表示される。
    クリックするとBacklogのスペースに飛んでくれます。  
    corec-2015-06-005

まとめ

CORECを支える仕組みとして、今回はチケット管理(Backlog)と構成管理(git)をjenkinsでつなぐ仕組みと その設定方法をご紹介しました。
それぞれの設定やコードは簡単なものだということがわかっていただけたかと思います。
しかし、これにより得られる生産性や安心感は絶大です。
もし、まだこのような連携をしていなければ、ぜひ試していただいて、得られる生産性や安心感を実感していただきたいと思います。
少しの手間を惜しんで多くの時間を無駄にしないように、自分の仕事の環境にこそITを導入しましょう!

次回の『CORECを支える技術』もお楽しみに!

AWS社内勉強会

AWSの社内勉強会を技術戦略部の松尾を講師として開催しました。

AWSは、2014年3月にサービスインしたCOREC(コレック)で使われ始めてから
社内で勢いを増してきています。


今回の勉強会は

1. VPC (Virtual Private Cloud)
2. EC2 (Elastic Compute Cloud)
3. RDS (Relational Database Service)
4. Route 53
5. SES (Simple Email Service)
6. S3 (Simple Storage Service)
7. IAM (Identity and Access Management)
8. SQS (Simple Queue Service)

の概要から説明していただき、
各自AWS マネジメントコンソールからEC2インスタンスの起動と終了を操作したり、
AWS SDK for Rubyを使ってS3へファイルをアップロードしたりしました。

IMG_0547

次回の社内勉強会は・・・まだ未定です。

サーバサイドでWebページのキャプチャ画像を作成する

開発の松尾です。
とある事情よりサーバサイドで任意のWebページのキャプチャ画像を保存したいという要件が湧いてきたので、比較的楽に実現できる方法について考えてみました。

CutyCapt

CutyCaptはAppleのSafariやGoogleのChromブラウザーなどのレンダリングエンジンのベースとなっているWebkitというライブラリを利用してWebページを画像やその他のフォーマットに変換してくれる非常に便利なツールです。
わたしが開発環境として利用しているUbuntu 14.04であれば下記のように簡単に導入できます。
$ sudo apt-get install cutycapt
使い方も簡単。
$ cutycapt --url=http://www.superdelivery.com/ --out=top.png
オプションの「--url」でキャプチャしたいページのURLを指定しつつ、「--out」で出力するファイルを指定するだけで問題なく使用できます。出力先のファイルの拡張子部分を見てフォーマットを推測しているようで、PNG形式に限らずPDF形式で出力することも可能です。

サーバサイドで動かすには?

たまたまわたしがUbuntu Desktopを手元で利用しているので簡単に導入と実行ができたわけですがサーバサイドでCutyCaptを動かすにはどうしたらいいでしょうか?
通常はサーバサイドでX Window Systemに類するサービスは動いていませんし、動かしたくもありません。
そこで、X Serverの仮想的フレームバッファを作るためにxvfbを利用します。

これまたUbuntuであれば導入も簡単。
$ sudo apt-get install xvfb
少しだけややこしいのですが、xvfbとCutyCaptを組み合わせて下記のように利用します。
$ xvfb-run --server-args="-screen 0, 1024x768x24" \
/usr/bin/cutycapt --url=http://www.superdelivery.com/
xvfb-runは「--server-args」で指定された内容の仮想フレームバッファを用意し、その環境下で任意のコマンドを実行します。
これでサーバサイドでも上手く動きそうです。実際にAWS上のインスタンスで試してみましょう。

AWSインスタンス上でCutyCapt

仮想的なX環境とCutyCaptを利用してサーバサイドでも簡単にキャプチャは実行できそうですが、ひとつだけ厄介な問題があります。
「普通のLinuxサーバには通常プロポーショナルな日本語フォントなぞインストールされていない」という問題です。つまり、日本語も含めてWebページをそれなりにレンダリングするためにはOSのパッケージを取捨選択して必要なものを揃える必要があります。
Ubuntuの膨大なパッケージ情報の中からCutyCaptが日本語も含めて綺麗にレンダリングできる環境の設定…。
「無理!」

ということで大雑把にいきます。

AWSのインスタンスをUbuntu 14.04 LTSから作成して下記のようにえいやっと。
$ sudo apt-get install ubuntu-desktop xvfb cutycapt
ubuntu-desktopパッケージはUbuntuのデスクトップ環境であるUnityであるとか、そのベースであるgnomeであるとかとにかく大量の関連パッケージを含んでおり、累計1.7GBものパッケージがインストールされるため実行中はコーヒーでも飲みに行って休憩するといいかもしれません。
途中でFirefoxやThunderbirdのような明らかにサーバに不要なものもずんずんインストールされて怖くなってきますが我慢します。あまり労力を払わずに整った日本語環境を手に入れるための試練です。明らかに不要なのにデカいものはあとで個別にremoveすりゃなんとかなりますって(汗

さて、実験。
標準では横幅800ピクセルでレンダリングされるので、ちょっと贅沢に「--min-width=1280」で試してみます。
$ xvfb-run --server-args="-screen 0, 1024x768x24" \
/usr/bin/cutycapt --url=http://www.superdelivery.com/ \
--out=superdelivery.png --min-width=1280
処理の内容を考えれば当然のことなんですが数秒の時間がかかります。

superdelivery

かなり綺麗なレンダリング結果が得られました。Ubuntuの整った多言語環境がサーバサイドでも力を発揮した瞬間です。
このクオリティで画像化できることがわかり十分に要件を満たせそうだと立証できました。

まとめ

  • サーバサイドでもUbuntuの他言語環境+CutyCaptを使って綺麗なWebページのキャプチャができる。
  • JavaScriptも動作するし必要が無ければOFFにできる。今のところ何故Flashコンテンツまでレンダリングされるのかが謎。
  • 反動で大量のパッケージがインストールされてしまったけど、えーっともう少し片付けます…

記事検索