MySQL Casual Talks Vol.3 で話してきた

4/19 にオラクルセンター青山で MySQL Casual Talks Vol.3 が開催されました。
今回は @myfinder さんにお声がけいただきトーク枠をいただけたので、挑戦させていただきました。

小さな話をいっぱい寄せ集めた内容だったので、うまくまとめきれずすみませんでした。また、時間を気にしてしまって少し早口だったり聞き取りにくかったりした所もあったかと思います。

伝えたかった思いはまとめに集約しています。
何よりも「MySQL すごいよ、ありがとう」と言いたかったのでした。MySQL 自体のソフトウェアとしての完成度や開発体制もさることながら、多くのユーザがいて様々な知見・ノウハウが共有されているこの環境自体も大きな資産であると感じています。ない分の動きを把握していて、様々手を加えられるのはとても理想的なことですし憧れます。ですが、そこまでのスキルセットがない人でも、調べたり試したりすることである程度がんばれる、というのが環境含めて完成されている証だと思っています。
また、これは MySQL に限った話ではないのですが、私自身の考えとして実際のサービス運用では様々な視点・知識・経験が必要だと思っています。「何でも屋になろう」ということではなく、「視点を変えてみるとうまくいくんじゃないかな」「そのためには薄くでも知識や経験があると取っつきやすいんじゃないかな」という考えです。サービスを運用していく以上は、使っていただいているユーザさんを「もてなす」心を意識して、そのためにどうすればいいかを考えてメンバーと協力して作り上げていきたいと思っています。

偉そうなことを言いながらも最近は直接のインフラ運用をしていないことも多いのですが、全体としては業務に限らない私自身の経験や思いをまとめたものとさせていただきました。
このような公の場でトークさせていただくのは初めてだったのですが、資料を作る時点で様々追って調べたりまとめたりと、かなり良い勉強となりました。アウトプット大事ですね。
いつもお世話になっているコミュニティに対して、今までは情報を集めて使うだけの立場だったのですが、少しずつ発信・共有する立場にもなりたいというのが今年の目標でした。まだ内容的にも未熟な点が多いと思いますが、人に説明できるまで理解をする(=自分の言葉でかみ砕く)を重ねていってコミュニティを支えたい・恩返しをしたいと強く思った一日でした。

快適な会場を提供いただいた Oracle のみなさま、とりまとめていただいた運営のみなさま、そしてお集まりいただいて素敵な時間を共有していただいた参加者のみなさま、ありがとうございました。

レプリケーションが追いつかないときに試すこと

MySQL Casual Advent Calendar 2011” 7 日目を担当させていただく、hatak (@hisashi) です。
普段はモバイルゲームのインフラをメインにみているのですが、今回はそんな業務で経験したことを基に記事を書かせていただきます。
カジュアルすぎる内容かもしれませんが、お付き合いいただければと思います。

MySQL のレプリケーション

MySQL のレプリケーションは、安定稼働やバックアップ、負荷分散などの目的に利用できる優れた機能です。
bin-log (バイナリログ) を利用して Master サーバから Slave サーバに更新を伝播させ、データの複製を行います。Slave サーバでは、2 つのスレッドが動作しています。

  • IO_THREAD
    • Master から送られてきたデータを受け取り、relay-log (リレーログ) として書き出す
  • SQL_THREAD
    • relay-log を読み出し、DB を更新する

遅延の調べ方

“SQL_THREAD” による遅延の場合は、Slave サーバで “SHOW SLAVE STATUS” コマンドを実行することで確認ができます。

hatak@dbslave> SHOW SLAVE STATUS \G
 *************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.12.2
                  Master_User: replicator
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.012863
          Read_Master_Log_Pos: 205295676
               Relay_Log_File: mysqld-relay-bin.026640
                Relay_Log_Pos: 75468325
        Relay_Master_Log_File: mysql-bin.012863
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 205295676
              Relay_Log_Space: 205296082
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 100
1 row in set (0.00 sec)

ここに示されている “Seconds_Behind_Master” の値が、「現在 SQL_THREAD が実行しているクエリの実行時刻」と「Slave サーバが保持しているリレーログの時刻」の差となり、遅延を表しています。

“IO_THREAD” による遅延の場合は、Master からのバイナリログが受信しきっていないため、Master における “SHOW MASTER STATUS” の結果も参考にする必要があります。

hatak@dbmaster> SHOW MASTER STATUS \G
 *************************** 1. row ***************************
            File: mysql-bin.012863
        Position: 205295676
    Binlog_Do_DB:
Binlog_Ignore_DB:
1 row in set (0.00 sec)

この結果を基に、どれくらいずれているかを見なければなりません。

Master と Slave で “File” と “Master_Log_File”、”Position” と “Read_Master_Log_Pos” をそれぞれ比較し、どの程度転送が遅れているかをチェックします。
“IO_THREAD” に起因した遅延の場合は、サーバの処理というよりはネットワーク帯域の問題である可能性が高いと思います。

今回は “SQL_THREAD” による遅延を想定してまとめていきます。

Master の更新をブロックする

レプリケーションで送られてくるクエリの流量が多すぎる場合、つまり Master の更新が激しすぎて追いつかないケースでは、そもそも Master での更新を止めてしまうという方法があります。

これは、MASTER_POS_WAIT() 関数を利用することで実現できます。

手順については、MySQL5.1 リファレンスマニュアルの FAQ 項目内に「レプリケーションが追いつくまでマスタの更新をブロックする方法」として紹介されています。

この方法では同期化をコントロールすることで追いつかせることができますが、実際にサービス運用中のサーバではなかなか使いづらいところもあります。

Slave のパフォーマンスを調整する

このとき、レプリケーションで伝播するクエリは全て直列化されるため、更新が激しい場合はどうしても遅れてしまうことがあります。
DiskI/O への負荷が高いとき “innodb-flush-log-at-trx-commit” の値を変更することで、ディスクへのフラッシュを減らすことができます。このパラメータではログバッファからログファイルへの書き込み、およびディスクへのフラッシュをコントロールすることができます。

設定値 ログバッファのファイルへの書き込み ディスクへのフラッシュ 備考
0 毎秒 ログファイル上
1 コミット時 ログファイル上 デフォルト値
2 コミット時 毎秒

この設定値によるパフォーマンス向上度合いは、経験的には効果の大きい順に 0 > 2 > 1 の順と思っています。

my.cnf に記述し起動時に適用することもできますが、 mysqld の再起動をせずに変更・反映が可能です。

SELECT @@innodb_flush_log_at_trx_commit;
SET GLOBAL innodb_flush_log_at_trx_commit = 2;

この設定値はパフォーマンス向上の代わりに、信頼性を犠牲にします。プロセスが突然落ちた場合などにディスクにフラッシュされていないデータをロストする可能性がありますので、状況に応じて(あるいは追いつかせるまでの間だけなど)の使用に抑えることが良いかと思います。

まとめ

MySQL のレプリケーションが追いつかない場合、プロセスの再起動を行わずに簡単に試せて効果の期待できる方法をまとめてみました。

「実践ハイパフォーマンスMySQL」や「エキスパートのためのMySQLトラブルシューティングガイド」などの書籍でもわかりやすく紹介されていますので、ぜひご参照ください。

このほか、サーバ上で不要なデーモン(*1)が動いていないかチェックする、ionice(*2) で mysqld が優先的に DiskI/O を使えるようにするなどサーバ側でもできることはありそうです。

間違っているところや、他にもこんな方法がある、などございましたらぜひお聞かせください。

明日は @kamipo さんです!

*1: cpuspeed など

*2: I/O スケジューラを cfq にする必要があります

Hachioji.pm #11 に行ってきた

町田で行われた hachioji.pm #11 に参加してきました。
前回参加した hachioji.pm が 2 月実施の #2 だったので、9 回ぶり 2 回目の参加でした。振り返れば震災以降ドタバタといろんなことがあって参加できないままだったわけで、ちょっと勿体ないことしたなと反省したりもしてました。

町田も久しぶりすぎて変化に戸惑いが隠せない感じでした。資料作成しようと思ったカフェとかなくなってたし!

全体的な感想など

自己紹介代わりの LT はやっぱりおもしろいと思います。規模が大きくなると難しくなりますが、全員に向かって各々が話す hachioji.pm ならではの雰囲気は好きです。

かなり様々なお話を聞けたことも大きな収穫でした。同時に、モチベーションが上がるような刺激を受けることができました。

その他、つらつらと思ったことを。

  • ネタが意外とかぶっててなんかずるい
    • “退職しました”
    • “うれしいこと(内定・転職・おめでた)ありました”
    • “北海道”
  • 会場となったお店がよかった
    • 屋根裏っぽい雰囲気がよかった
    • 料理(とくに鍋)もおいしかった
    • オリオンビールが飲みやすかった
    • @ytnobody++
  • 業界狭い
    • いろんな方とのつながりはやっぱり大事

MySQL の preload

LT で話した資料を こちら においてあります (PDF 形式です)。
お題の「○○道」でネタを探していたのですが、preload のことで頭いっぱいだったこともありライトな感じにざっとまとめてみました。

周りの方にアドバイスいただいたり、調べたり、試したりしたものを組み合わせた結果、個人的な答えとして今はこのような手法がよいのかなと考えています。ただ、もっと 楽な 効率のよい方法など、実際に皆さんがどのようにされているかを知りたいなと思っているところです。

幹事の @ytnobody さん、主催の @uzulla さん、そして参加された皆さん、お疲れさま & ありがとうございました。
また次回も都合をつけて参加したいなー。

YAPC::Asia Tokyo 2011 (2日目) に行ってきた

昨日に引き続いて、 YAPC::Asia に参加してきました。

個人的な視点で、今回の YAPC から感じた Perl とそれを取り巻く Web サービス系の世界の現状をいくつかまとめると、

  • 前回参加した 2 年前に発表された PSGI はもはや標準
  • プラットフォーマーを中心に大規模サービスのノウハウが溜まってきている
  • アプリケーションの設計・実装でも、ミドルウェアやハードウェアも含めた視点がより大事になっている
  • 組織が大きくなった会社では、開発者と運用者の良い関係づくり (DevOps の考え方) に取り組んでいる

といったところかなと思います。
会社で標準的に使われている開発言語が違っても、根底の考え方や Web サービス系全般での動きは同じだなと痛感しました。

Read more »

YAPC::Asia Tokyo 2011 (1日目) に行ってきた

Perl のおまつり、 YAPC::Asia に参加しています。
昨年はプライベートでドタバタしていたので、2 年ぶりの参加でした。前回に比べて参加者も多く、それでいてスムーズなイベント進行と素敵なトークの数々でとても楽しい時間を過ごしています。
ひとまず 1 日目を振り返りつつ、聞いたトークをまとめておこうと思いました。

振り返ると、インフラ寄りな内容を選んでいたこともありますが、今日は Perl に限らない話も多かったように思います。それだけ広い知識と経験が必要で、いろんな所でいろんなひとが挑戦していることがわかってわくわくしましたが!

Read more »

github 上のプロジェクトの fork をプライベートな git サーバで管理

github で管理されている OSS を利用するとき、少しカスタムして使いたいという場合があったりします。もちろん、fork すれば github 上で自分が push できる状態にはなりますが、カスタムしたバージョンを利用する範囲が社内だったりすると github 以外で管理したくなったりもします。

そこで、こんな感じの構成を目指して、github 上のプロジェクトの fork をプライベートな git サーバで管理してみました。

プライベートなリモートリポジトリのメインブランチ origin/master
github のプロジェクトのメインブランチ github/upstream

事前準備として、プライベート git サーバには push できる空のリポジトリを作っておきます。
(gitosis の場合であれば、gitosis.conf にリポジトリ名を追加しておく感じでいけます)

まず、おおもとのツリーを github から clone して取得します。

$ git clone --origin github https://github.com/edavis10/redmine.git
$ cd redmine
$ git config -l
...
remote.github.fetch=+refs/heads/*:refs/remotes/github/*
remote.github.url=https://github.com/edavis10/redmine.git
branch.master.remote=github
branch.master.merge=refs/heads/master

ここでは master が github のメインブランチになっています。
今回はオリジナルの master を upstream という名前で扱うようにしたいので、ブランチ名を変更します。

$ git branch
 * master
$ git branch -m master upstream
$ git branch
 * upstream
$ git config -l
...
remote.github.fetch=+refs/heads/*:refs/remotes/github/*
remote.github.url=https://github.com/edavis10/redmine.git
branch.upstream.remote=github
branch.upstream.merge=refs/heads/master

これでおおもとの master は、ローカルでは github/upstream という名前で扱えるようになりました。
実際にこちらでカスタマイズするのは github/upstream のタグ “1.2.0″ をベースに使いたいので、ここから master ブランチを切ります。

$ git branch master 1.2.0
$ git branch
   master
 * upstream

そして、プライベートな git サーバをリモートリポジトリ “origin” として追加します。

$ git remote add origin ssh://gitosis@git.example.jp/redmine.git
$ git config branch.master.remote origin
$ git config branch.master.merge refs/heads/master
$ git config -l
...
remote.github.fetch=+refs/heads/*:refs/remotes/github/*
remote.github.url=https://github.com/edavis10/redmine.git
branch.upstream.remote=github
branch.upstream.merge=refs/heads/master
remote.origin.url=ssh://gitosis@git.example.jp/redmine.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master

あとはプライベートリポジトリに push すれば完了です。

$ git push origin master
$ git push origin upstream

これで、origin/master に push/pull し放題になります。

サイバーエージェント×クックパッド合同勉強会に行ってきた

クックパッドさんのオフィスで開催された「サイバーエージェント×クックパッド合同勉強会」に参加してきました。
両社で検証や本番導入しているクラウドサービスな話題がメインで、とても興味深いものでした。

OpenStackを検証してみた – オープンソースの仮想化技術 open stack の検証結果の報告

サイバーエージェントの坂本佳久 (@ton_katsu) さんの発表。プライベートクラウドを構築するプロジェクトのひとつ、OpenStack を検証した結果のまとめ。

OpenStack

Nova と Swift を組み合わせたプロジェクト。
OpenStack を検証対象として選んだ理由は次のとおり。

  • Eucalypusだとスケールしない (らしい)
  • ubuntu がサポートする (らしい)
  • 200 人体制で開発が行われている
  • 参加企業として 60 社
  • KVM, QEmu, Xen など広く対応している
  • Pythonベースで書かれている

AWS と比較すると、下記のような対比でだいたい同じことができる。

  • Amazon EC2
    • Nova
  • Amazon S3
    • Glance : OSイメージ登録
    • Swift : ストレージ

システム構成の例

インスタンスの起動や管理を行う “CloudController” と、この配下に複数の “Computenode” を配置する構成をとる (最小構成では両プロセスを同じマシンに同居させることも可能)。Computenode に hypervisor などをいれ、実際にインスタンスを動作させることとなる。その他、副次的に MySQL や RabbitMQ などのプロセスも利用する。

使ってみた

実際に検証で用いた環境は下記の通り。

  • OpenStack Cactus
  • Controller
    • ubuntu 10.04
  • Computenode
    • ubuntu 10.04 x3
  • virtual
    • KVM ベースで CentOS / ubuntu

これらの管理を行うために、 Django ベースの GUI が付属している。WebSocket を利用するため、Safari などで操作する必要がある (Chrome はうまく動作しなかったとのこと)。また、VNC コンソールは Cactus のバージョンでは未実装なので、利用する場合は trunk から取得する必要がある。
その他、API も用意されているので自前で管理ツールを作成することも可能。

感想

  • 良かった点
    • ubuntu ではパッケージで提供されているためインストールが簡単
    • シンプル
    • Python のためエラーが追いやすい
  • 苦労した点
    • インスタンスメタデータをどこに取り行けばいいかわからなかった。。
    • ハイブリッドクラウドがいいかも

来月より検証環境で運用するとのことで、今後のレポートにも注目したいところです。

AmebaPico を支える技術 – AWS上に構築された AmebaPico で使用されている技術・開発体制について

サイバーエージェントの森野耕平 (@kohei_april20) さんの発表。AmebaPico のアーキテクチャや体制に関して。

Ameba Pico

アメーバピグの海外版として位置づけられているサービス。

  • プラットフォーム
    • facebook
    • mochimedia
    • 独自 (自前)
  • 利用ユーザ
    • 390万UU、60万MAU
    • 10-20代が中心、男女比 3:7
    • インドネシア、フィリピン、アメリカが中心

アーキテクチャ

すべて AWS で運用されている。

  • application
    • Tomcat
  • socket server
    • ノンブロッキング I/O を使い、軽量データ処理を行う
    • 独自のバイナリプロトコルで細かい大量のコマンドを処理
    • コマンドとしてのデータをバイナリとしてシリアライズ
    • イベント駆動でリアルタイム性
  • static
    • CDN
    • Storage
  • ZooKeeper
    • システム全体でのロック管理を行う分散ロックシステム
  • memcached
  • MongoDB
    • 3台構成のレプリカセットを構築
      • うち1台は EBS
    • 6シャードで分散
  • ElasticMapReduce
    • ログ集計
  • pigg と共用の箇所
    • ID管理サーバ
    • ポイント (サービス内通貨) 管理サーバ

AWS の利用

  • S3
    • static な配信コンテンツを保持
    • ユーザ行動ログ
  • CDN
    • cloudfront を利用
  • ElasticMapReduce
    • S3 に保存されたログを解析
  • EC2
    • 汎用的なサーバインスタンスとして利用
    • 用途に応じて種別を使い分け
      • small : 開発環境
      • High-CPU : webサーバ
  • EBS
    • バックアップなど揮発性があって困る箇所にマウント

Flash の利用

クライアントサイドではまず main.swf のみをロードし、必要に応じてサブモジュールをロードする。

  • shop.swf
  • room.swf
  • profile.swf

delegate 実装でサーバ接続とモック用を切り分けることで、サーバサイドとクライアントサイド別々の開発が行えるようにしている。

運用してみて

EC2 上でサービスを運用してみての総括。

  • 良かった点は「手軽さ」
    • DC 借りる必要ないことで海外展開のハードルが下がる
    • インフラメンバーがいなくても進められた (スモールスタート)
  • 課題は「重い」こと
    • たまに落ちる
      • バージニアだからかもしれない?
      • バージニアで運用中のインスタンスが 60個くらい
        • 平均すると 2-3ヶ月に1つ落ちる
        • 最高で4週に4つ落ちたことがある
    • メンテナンスの告知が事前に来ることもある
      • こないこともある
    • インスタンスが落ちるとどうなるか
      • 応答がなくなる
        • 特定ポートだけの場合もある
        • 全体の場合は何も出来ない
      • リブートするしかない
    • 落ちなくても、一定時間応答なくなることもある
    • サービスとして利用する場合
      • 落ちやすいが冗長重視で組めば使える

そして、MongoDB をメインで利用してみての総括。

  • 実績
    • 処理クエリは 5,000 Read/s (Max)
  • 問題
    • コネクションプールが枯渇
      • MongoDB の I/O がボトルネックとなってしまう
      • シャードを 4→6 に増設することで対処
      • 合わせて I/O パフォーマンスの高いインスタンスを利用するように
    • オートバランシングの挙動
      • バランシングに偏りが生じてしまう
        • 新規シャード追加時などに特に顕著となる
      • 現在はオートバランシングを off にして手動で調整
    • レプリカセットのコンフィグ情報が未反映となるケース
      • 一見正常に動いてるので気付かなかった
        • プライマリ落ちたときにスレーブがうまく動かずに発覚
      • コンフィグの反映は全台再起動が必要

MongoDB を大規模に利用しているケースなので、とても興味深いものでした。海外展開の際に DC の場所を気にしなくても良い、というのは AWS ならではのメリットに感じました。

毎日の料理を楽しくする画像配信技術

クックパッドの成田一生 (@mirakui) さんの発表。レシピに必須の画像をリアルタイムでリサイズするための Apache モジュール “TOFU” について。

従来の画像アップロードの仕組み

  • アップロードされた画像はアプリケーションサーバがリサイズ
    • サムネイルを NFS に保存
  • いくつかの問題点
    • 新デザインのプロトタイプで様々な画像サイズを試すことが難しい
      • デザインに合わせて画像サイズを柔軟に変えたい
      • 試すたびに 800 万枚リサイズ
        • 昔はやってた (!)
      • デバイス展開の度にデザイン変わるためリサイズが必要
        • サービス出る速度が遅くなる
        • モチベーションが低下する

そこで新しい方法を模索

  • 現在の規模
    • 投稿画像の枚数 : 800万枚
    • 画像リクエスト : 7,000枚/sec
    • ストレージに NFS を利用
      • NFS が落ちると全サービスが落ちる
  • EC2 への移行を検討中
    • ストレージは S3 にしたい

TOFU

URL に処理内容をマッピングし、リクエストの度に画像をリサイズする Apache モジュール (mod_tofu.so)。

/recipes/{:recipe_id}/{:size}/{:hash}

  • アタック防止のために末尾に Hash キーを付加
  • 画像処理も可能
    • c : crop (指定したサイズに収める)
    • q : quality
  • 細かく指定すれば好きな部分だけ切り取る、もできる

画像処理には ImageMagick を利用している。

  • 構成
    • C1.XLARGE x6台 で処理
      • コア数単価が安い
      • mod_tofu.so は CPU 食うけどメモリ使わない
    • 前段で akamai のキャッシュ

苦労話など

  • ImageMagick と Imlib2
  • S3
    • s3::ListAllBuckets が正しい結果返さない障害発生
      • すべての画像が見えなくなった
    • データは消えないがアクセスできなくなることはある
  • akamai or cloudfront
    • cloudfront は元々 S3 のデータを返すものだった
      • 現在は EC2 を origin に使えるようになった
    • でもやはり akamai は圧倒的に早い
    • akamai キャッシュヒット率は高くない
      • 90% 程度
      • 試しに ELB 下に Varnish でキャッシュサーバを構築してみたところ 60% ほどヒット
        • TOFU サーバの数を 40% ほど減らせる
      • でも Varnish は大容量のメモリが必要なためインスタンスの単価が高い
        • キャッシュサーバ入れるか、TOFU サーバを増やすか、コスト的に微妙なところ
        • 現在はキャッシュサーバは外している

画像種類もリクエストも多いのにリアルタイムに変換するのは大変そうに思ったのですが、Apache モジュールと聞いて少し納得。キャッシュもうまく組み込んでいて、かなりコストを意識した工夫がされている印象を受けました。

AWS移行に向けたクックパッドの取り組み+α

クックパッドの菅原元気さんの発表。先日のアマゾンウェブサービスクラウドアドバンテージセミナーで発表された内容をベースに、AWS 移行に向けた全体的なお話。
プレゼン資料がとてもまとまっているので、そちらを直接見たほうが。。。

サーバ・ネットワーク構成

  • 現在
    • シンプルな3層構成
    • 別々のセグメントとすることでセキュリティを担保
    • すべてが同じセグメント
      • セキュリティグループでコントロール
    • ロール同士の通信は許可
    • 人的ミス対策としてすべてのサーバで iptables 起動

AWS でのサーバ

  • DNS
    • Active-Active 構成で EIP を VIP のように利用
    • 各サーバでは resolv.conf を cron で更新
  • AMI
    • 基本は CentOS 5.5
    • 各イメージはバージョンをつけて管理
    • Chef 導入も進めてる
  • Nagios + Munin
    • タグをもとに自動で監視項目を設定
    • 起動したインスタンスを cron でチェックして追加
  • 冗長化
    • ElasticIP を利用
      • Nagios
      • LDAP
    • cron で死活監視
    • heartbeat への移行
      • EIP を VIP として利用
      • マルチキャストが使えないのでユニキャストで
  • MySQL
    • EC2 上ではまだ Slave しか稼動していない

分散DNSについて

さすがに全台で resolv.conf を書き換えていくのは大変なのと、いくつかの問題点がクリア出来ない。

  • 内容がキャッシュされる
  • タイムアウト 1s 以下にできない
  • cron で監視は分単位

そのため、分散 DNS を開発。

$ ruby gem ddns

DNS がそれぞれノードとして機能する動きは、クラウドならではの問題点をクリアするためのひとつの解決策かと思います。現状のサービスを AWS に移行する際の参考になるような、総括的な話でした。

LT もあり、その後の懇親会ではおいしい料理もあり、でとても素敵な勉強会でした。スピーカー&スタッフの皆様、ありがとうございました!

git のバックアップ

分散 SCM とはいえ、バックアップはあるとうれいいものです。git のリモートリポジトリが破損した場合などに復元元を探すために、誰が持っているのが最新のリビジョンで、、というような作業が発生することは避けたいからです。

git のリモートリポジトリから別のサーバにバックアップを作成するのは、hooks を利用することで簡単に設定できますです。例えば、対象となるリポジトリの post-receive で下記のようなコマンドを設定しておくとできます。

  • バックアップ先のサーバ:ディレクトリは targethost.example.jp:/var/lib/git
  • バックアップのための SSH 接続で利用するユーザは syncuser
  • gitosis ユーザは syncuser 権限で git コマンドが利用出来るように visudo を設定
#!/bin/sh
#####
# hooks/post-receive
#####

MIRROR_HOST='targethost.example.jp'
REPO_NAME=`pwd | perl -e '$t=<stdin>;$t=~ s!^.*/!!;print $t'`

sudo -u syncuser -H git push --mirror syncuser@${MIRROR_HOST}:/var/lib/git/${REPO_NAME}

“–mirror” オプションを付けることで、バックアップ先にも bare のままディレクトリが作成されます。
リモートリポジトリとして利用するサーバと別のサーバで Redmine や Trac、あるいは gitweb などを動作させてリポジトリブラウザを利用する場合などでも bare を付けます。

hooks/post-receive は、リポジトリに加わる変更を受信したタイミングで実行される hook script です。cron などで仕込無者とは異なり、push されたタイミングで sync されるので無駄にコネクションが張られることがありません。
ただし、push するタイミングで別サーバへの push が実行されるため、ユーザからみると push 自体の時間が少し長くなるのが欠点です。

gitosis で作るプライベートな git サーバ

業務で使い始めた Git ですが、高機能過ぎて未だに使いこなせている自信がありません。
一方で、かつて利用していた Subversion はコマンドを忘れてしまって使うたびにググるほどに記憶が抜けつつあります。

そんな Git を複数メンバー・複数環境で利用する場合、マスターリポジトリを利用することがあります。これにより、Subversion のような中央集約型のソースコード管理をしつつも Git の恩恵を受ける開発スタイルを取ることができるます。
マスターリポジトリとして GitHub を利用するのが最も手っ取り早いですが、プライベート(= メンバーのみが閲覧できる)なリポジトリを作成するためには有料オプションにしなければなりません。しかも地味に高い。

こんな時、gitosis を利用すると手軽にプライベートな Git サーバを構築することができます。もちろん、リポジトリを利用するメンバー全員が SSH での接続ができるサーバに bare リポジトリを作ることでも Git サーバとして機能しますが、それでも gitosis を使う優位性は以下のような点にあります。

  • 公開鍵認証を用い、通信にSSHを利用するため安全に利用できる
  • 公開鍵でユーザを識別し、リポジトリに対するアクセス制御が設定できる
  • サーバにアカウントとは切り離して、リポジトリへのアクセスアカウントの作成ができる

gitosis はマスターリポジトリとして利用するサーバのみインストールします。リモートから clone / pull / push をするクライアントには、通常通りの Git がインストールされていれば利用することができます。

パッケージ導入

gitosis は python で記述され、gitで管理されているプロジェクトです。
CentOS の場合、EPEL リポジトリに Git / gitosis 共にパッケージが存在するので、これを利用することで簡単にインストールできます。

$ sudo yum install git python-setuptools gitosis

この方法で導入した場合、gitosis ユーザが合わせて作成されます。

  • uid: gitosis
  • home dir: /var/lib/gitosis

初期設定

gitosis の設定は、 gitosis 管理リポジトリのファイルを変更し push することで反映されます。
設定を行うためには、管理ユーザとして最低一人の公開鍵をセットしなければなりません。ここでは、Git サーバ上の現在の操作ユーザの鍵を管理ユーザとしてセットする例を示します。

$ cd /var/lib/gitosis
$ sudo -H -u gitosis gitosis-init < ~/.ssh/id_rsa.pub

これで、gitosis のホームに "gitosis" ,"repositories" という2つのディレクトリが生成されます。

$ sudo -H -u gitosis tree
.
|-- gitosis
|   `-- projects.list
`-- repositories
    `-- gitosis-admin.git
        |-- HEAD
        |-- branches
        |-- config
        |-- description
        |-- gitosis-export
        |   `-- keydir
        |       `-- hatak@example.jp.pub
        |-- gitosis.conf
        |-- hooks
        |   |-- applypatch-msg.sample
        |   |-- commit-msg.sample
        |   |-- post-commit.sample
        |   |-- post-receive.sample
        |   |-- post-update
        |   |-- post-update.sample
        |   |-- pre-applypatch.sample
        |   |-- pre-commit.sample
        |   |-- pre-rebase.sample
        |   |-- prepare-commit-msg.sample
        |   `-- update.sample
        |-- index
        |-- info
        |   `-- exclude
        |-- objects
        |   |-- info
        |   `-- pack
        |       |-- pack-e0194c80b63eb10d17a54a4a4551efc7fea1076d.idx
        |       `-- pack-e0194c80b63eb10d17a54a4a4551efc7fea1076d.pack
        `-- refs
            |-- heads
            |   `-- master
            `-- tags

14 directories, 22 files

この gitosis 管理リポジトリを、登録した鍵を持つクライアント(ここでは同一ホスト)からcloneし、設定します。

$ cd ~/work
$ git clone gitosis@localhost:gitosis-admin.git

設定を変更後、push することで Hooks のスクリプトが実行されることで key が gitosis ユーザの authorized_keys に追記されていきます。
この authorized_keys には command が併記されているため、git のリモートリポジトリを操作する以外のコマンドが実行できないようになっているのです。

新規ユーザの追加

新規ユーザの追加は、*.pub ファイルを作成して該当するグループに追記、push するだけです。
gitosis では、グループ毎に権限をコントロールする形式を取ります。このため、ユーザが複数のグループに所属しているとうまく動作しないことがあります。

.pub は単なる authorized_keys なので、1行にひとつの公開鍵を書く形式になっていれば複数設定可能です。

新規リポジトリの追加

新規リポジトリの追加は、gitosis.conf に変更を加えて push するだけです。空のまま pull すると init されるだけですが、追加して push すれば大丈夫です。

設定のコツ

keydir 以下に公開鍵ごとにファイルを作成し、 gitosis.conf に公開鍵のファイル名 (.pub を除く) を設定していきます。

[gitosis]

[group gitosis-admin]
writable = gitosis-admin \
members = admin

[group development]
writable = misc/sandbox \
           project test
members = hatak \
           hoge \
           fuga

サブディレクトリ

リポジトリはサブディレクトリに入れることができます。
上記 sandbox の場合は次のようになります。

  • Clone 用 URL : ssh://gitosis@localhost/misc/sandbox.git
  • サーバ内ファイルシステム: /var/lib/gitosis/repositories/misc/sandbox.git

サブディレクトリは予め作成しておく必要がありますが、こうすることで乱立するリポジトリを階層分けして整理することができます。

$ sudo -H -u gitosis mkdir /var/lib/gitosis/repositories/misc

config の改行

gitosis.conf ではユーザが多いときなどバックスラッシュを入れることで、設定ファイル内でも改行することができます。
ユーザが多いときなどに可読性を上げることができ、とても便利です。

関連するページ

Hosting Git repositories, The Easy (and Secure) Way
多人数開発で Git を使う場合の環境構築 | GREE Engineers' Blog

MacOSX のローカルの DNS キャッシュ

DNS の設定を触るとき、ローカルのキャッシュが変わっていないためにハマることが多々あります。Mac の場合、ターミナルで dig して変更を確認してもブラウザで開けなかったりするわけで。

これは、MacOSX 内部で DNS 解決の結果をキャッシュする機能が働いているためです。
キャッシュをコントロールするためには dscacheutil コマンドを使います。

$ dscacheutil
Usage: dscacheutil -h
       dscacheutil -q category [-a key value]
       dscacheutil -cachedump [-buckets] [-entries [category]]
       dscacheutil -configuration
       dscacheutil -flushcache
       dscacheutil -statistics

キャッシュをクリアするのは “-flushcache” オプションをつけます。

$ dscacheutil -flushcache

sudo しなくてもユーザ権限でできるので安心。
実際にキャッシュされてるデータを調べるにはこんな感じ。

$ dscacheutil -q host -a name blog.hatak.net
name: vps02.sakura.dwmp.jp
alias: blog.hatak.net
ip_address: 49.212.41.49
$ dig blog.hatak.net
...
;; ANSWER SECTION:
blog.hatak.net.		600	IN	CNAME	vps02.sakura.dwmp.jp.
vps02.sakura.dwmp.jp.	600	IN	A	49.212.41.49

ちゃんと dig の結果と同じものがキャッシュされてました。CNAME の場合は alias として表示されるようですね。
IP からの逆引きも調べてみました。

$ dscacheutil -q host -a ip_address 49.212.41.49
name: www30255u.sakura.ne.jp
alias: 49.41.212.49.in-addr.arpa
ip_address: 49.212.41.49
$ dig -x 49.212.41.49
...
;; ANSWER SECTION:
49.41.212.49.in-addr.arpa. 3600	IN	PTR	www30255u.sakura.ne.jp.

逆引きの委譲をしていないので管理用のドメインが返ってきてしまいますが、これも dig の結果と同じものになってます。
“ds” というコマンド名のとおり、もともと DirectoryService のキャッシュを操作するためのもののようで、DNS に限ったコマンドではなさそうです。

$ dscacheutil -q user -a name hatak
name: hatak
password: ********
uid: 501
gid: 20
dir: /Users/hatak
shell: /bin/bash
gecos: hatak