このエントリーをはてなブックマークに追加

Amazon Web Services 実践入門を読んだ

遅くまきながら、大人の事情で献本していただけなかった(笑)「Amazon Web Services 実践入門」を買って読みました。

タイトルのとおり、まさに AWS を実際に業務で使う上で必須の本になります。

内容的には、アカウント開設から、EC2、Route53、VPC、S3、RDS、ELB、CloudWatch、IAM、といったウェブサービスを AWS 上で展開するにあたっての必須となるサービスについて、ウェブコンソール上での操作方法、コマンドラインでの操作方法、それぞれまとまっており、とても分かりやすい内容でした。出力画面数も、大変数が多く、解説もとても分かりやすい内容でした。

普段から AWS を使っている人も、きっと改めての再確認やもしかしたら知らないこと・忘れていることを再確認できる本になると思います。
ちなみに僕は、この本を読んで 次のことを改めて知りました。

  • CloudWatch で EC2 t2 シリーズの CPU クレジット状況を取得できることが分かった(というか忘れていました)ので、さっそく設定しました!
  • EC2 は、stop / start すると、物理的には違うサーバが起動する

おそらく、この時期ちょうどウェブコンソールが日本語化されて大変だっただろうと推測されます。執筆者の皆様、大変お疲れ様でした。今年、入門の次の続編が出ることを期待しています!!!

AWS の進化は、ご存じのとおりとても早いですが、この本の内容は今らでも買っておいても損はないと思います。

Amazon Web Services実践入門 (WEB+DB PRESS plus)
舘岡 守 今井 智明 永淵 恭子 間瀬 哲也 三浦 悟 柳瀬 任章
技術評論社
売り上げランキング: 7,067
このエントリーをはてなブックマークに追加

ある PHP 案件の振り返り

2015 年、僕がメインで担当したとある PHP 案件の振り返りを行ってみようと思います。おもに技術面から、設計前に想定したこと、実際に導入してうまくいったこと・いかなかったことを振り返ってみたいと思います。

技術的な環境は、次のとおりです。

  • インフラ環境: オンプレミス(だいだい6台くらい、このときのためにほぼハードウェアを新規に調達しました)
  • OS: CentOS 7.0
  • 言語: PHP 5.6
  • フレームワーク: FluelPHP 1.7.x
  • データベース: Postgresql 9.3
  • ミドルウェア
    • ロードバランサー冗長化: Keepalived(新規)
    • ウェブサーバ: Apache から Nginx に変更、PHP は FPM
    • キャッシュ: Redis(新規)
    • 検索: Elasticsearch(新規)
    • ログ: Fluentd(新規)
    • 監視: Nagios & Cacti & Munin(従来と同じですが、新規にセットアップしています)

案件内容的には、とある既存の Java だったサイトを PHP にてフルリニューアルするというものです。

初回の設計時の構成は、次のとおりです。僕の担当は、全体のアーキテクチャ設計と実装とインフラ構築と切り替え時の運用、そしてすべての引き継ぎです。

  • 納期がかなりタイトだったため、データベースは既存の構成のままとしました(テーブル構成・SQL チューニングなどは、フルリニューアル後にお任せすることにしました)
  • サイトのデザインは、そのままの形で移行しますが、あまりにも不要な機能名は移行しない(デザインは、PC、スマフォ、フィーチャーフォン版の3種類ありました)
  • リニューアル後、すぐに引き継ぐため、なるべく新しいミドルウェアは導入しない(あまり技術的な進化はしないですが、業務なので仕方がない面が多いのも事実です)

サーバ構成を図にすると、次のような感じになります。

構成図

まず、インフラ側から、各コンポーネントについて説明します。

OS

  • オンプレのため、OS をインストールが必要ですが、オンプレのホスティング先に設定内容をまとめて依頼することにしました(ネットワーク構成や IPMI になりますが、コストは多少かかりますが個人的にはかなり信頼できるホストティング先でこの面はとても安心できました)
  • ミドルウェア以上の導入は、すべてこちらの管轄として、責任分解点を明確にしました

ロードバランサー

  • オンプレなので、いつもなら Linux のカーネルの変更(LVS ハッシュサイズ変更、TCP WAIT_TIME 変更)+ LVS (DSR) + Keepalived の構成をとりかったですが、引き継ぎ先のエンジニアのスキルセットとそれほどの規模ではなかったので、カーネルの変更は行いませんでした
  • Keepalived + Nginx の Active / Standby 構成としました
  • Keepalived は、VIP のみをもつ設定として、ローカルの Nginx へリクエストを行い、 Nginx をロードバランサーとして各ウェブサーバへプロキシする構成としました
  • L7 Nginx にしましたが、ページの静的ファイルなども、すべてウェブサーバへリクエストを振り分けました(※ロードバランサーにアプリケーションプログラムをデプロイするか、Nginx の静的ファイルキャッシュを使う選択肢も当時は考えましたが、静的ファイルのリクエストはそれほど多くなかったのでキャッシュは不要と判断しました)
  • なので、当然 DSR ではなく、NAT になります

ウェブ

  • ウェブサイトのページを表示するサーバです
  • 一般的な Nginx + PHP FPM の構成としました
  • ページを表示するために必要なデータは、すべてウェブサーバごとに Redis へキャッシュする方針としました(かなり当時の SQL が重かったので、別に PHP でクローラーとして実装してキャッシュしました)
  • ウェブサーバの Redis は、各ウェブサーバごとにキャッシュとしてデータをもっているため、レプリケーションは行っていません)
  • 今後の緊急サーバ増強にあわせて、AWS EC2 のハイブリッド構成も視野に入れていたので、awscli は最初から導入しておきました
  • PHP は、remi にある PHP 5.6 をそのまま導入しました
  • この他に、php-mecabphp_qr を PHP 5.6 で動くようにパッチをあてて使うことにしました

検索

  • 今まで SQL like 的な感じだったので、思い切って Elasticsearch を導入しました
  • 定期的にデータベースから Elasticsearch へデータを入れるプログラムを cron で動作させるようにしました

キャッシュ

  • ウェブサーバごとの Redis とは別に共通に必要なデータを Redis を、別のキャッシュサーバとして設けました
  • キャッシュ1の Redis をマスターとして、各ウェブサーバに別の Redis でスレーブとしました(ページを表示するために共通のキャッシュを取得するためにウェブサーバのローカルのスレーブ Redis から取得する構成としました)

データベース

  • 一般的な Postgresql 9.3 のマスター x スレーブ x 1 構成になります
  • リプレイス後、スレーブを 1 台追加、Keepalived を使って HA 構成としました

監視まわり

  • Sensu & Grafana & Kibana の構成で考えましたが、引き継ぎ先のエンジニアのスキルセットを考慮して見送りました
  • Munin は、別の非エンジニアチームの方が使っていたため、そのまま引き継いで導入しました
  • Mackrel を無料の範囲で導入しました
  • Cacti は、引き継ぎ先のエンジニアのスキルセットを考えて導入しましたが、個人的にはかなり久しぶりだったのでかなり設定に手間取りました

その他のツール

  • 構成管理ツールは、Ansible にしました(本当は Itamae にしたかったですが、Ruby だったので厳しかったです・・・)、Nagios の設定ファイルも自動生成したりしていました
  • Serverspec も導入したかったのですが、Ruby になってしまうため、こちらも引き継ぎ先のエンジニアのスキルセットを考慮して見送りました
  • 案件管理は Backlog、コード管理も Backlog の git、チャットは Slack(※もちろん有料!)、共通系は Qiita Team、を導入していただきました
  • コードのデプロイは、PHP 縛りということで Capistrano ではなく、同等の Rocketeer を使いました、このツールいろいろと罠があったので、別の機会にブログにまとめておきたいと思います

と、こんな感じです。

インフラのオンプレサーバは、サーバ選定からすべて僕一人で行いました。(※ラッキングと OS のインストールは、前述のとおりすべてホスティング先にお任せしました。)

サーバの選定にあたっては、台数が増えると運用コストが増えるため、できるかぎり予算の範囲でスペックの高いサーバとしていますが、次のようなスペックにしました。NIC は、安定の Intel 製にしました。

  • ロードバランサー: CPU 8 コア、メモリ 16GB、ディスク SAS 300GB RAID 1
  • ウェブ: CPU 32 コア、メモリ 64GB、ディスク SAS 300GB x 4、RAID10
  • キャッシュ: CPU 32 コア、メモリ 64GB、ディスク SSD 120GB x 4、RAID10
  • データベース: ioDrive サーバを導入できる見込みもありましたが、コストの問題、そこまでデータベースネックではない(SQL をちゃんとチューニングすれば問題ないはず)様子だったので、既存のサーバをそのまま転用しました、ただしディスク 4 台 SAS 300GB x 2 RAID1 + Spare 構成だったので RAID10 構成に統一しました

次に PHP の部分です、PHP の実装は、引き継ぎ先のエンジニアにも最初から担当していただきました。

  • フレームワークは、CodeIgniter か Laravel かどうか迷いましたが、けっこうシンプルな FuelPHP にしました(どうも現在 FuelPHP は、作者の人が病気?のため、かなり開発が停滞してしまっているようなので、もしかしたら選定に失敗したかもしれません)
  • FuelPHP は、基本的に MySQL での用途となっていたので、Model 部分を頑張って Postgresql に対応しました(今回の案件は画像のデータはそのまま Postgresql に格納されていて、その部分の Model 対応が一番大変でした)
  • テストも同梱されていることが魅力的で、僕の実装ではほぼ PHPunit によるユニットテストを書きましたが、他のエンジニアの方までテストを書く体制がとれなかったです
  • なので、非エンジニアが行うブラックボックステスト QA にみの品質管理体制となっていました(エンジニア側での動作確認は、すべてのブラウザを F5 リロードになっていました)
  • Redis へキャッシュするプログラムは、バッチ処理のように記述してデータベースの変更にあわせてほぼリアルタイムに更新できるようしました(そのため、FuelPHP の tasks + Supervisord の組み合わせにしました)

本番リリース前には、wrk を使って、それぞれの UserAgent で簡易ベンチマークを行いました。そのといは結果的にギリギリのパフォーマンスでした。

リリース後、いくつか問題が発生しました。。。

まずロードバランサー側で iptables の ip_conntack 設定ミス問題が発生しました。これ完全に僕の設定ミスだったので、すぐに設定を変更しました。Nagios での監視も漏れてもれていたことが原因でした。やはり、Serverspec を導入しておけよかったと思います。

Ansible 的には、次の設定を追加しました。

- sysctl: name="{{ item.name }}" value="{{ item.value }}" sysctl_file=/etc/sysctl.d/10-lb.conf
  with_items:
  - { name: net.nf_conntrack_max, value: 2097152 }

次に、それほどアクセスが多くないページの部分的な、キャッシュを使っていない部分でデータベースのボトルネックが発生しました。すぐにすべてキャッシュに入れる構成に変更して再リリースしました。納期的な問題から、あとで対応しようと考えていたのですが、やはりかなり甘い考えだったようです。

さらに PHP でキャッシュデータを生成していたクローラーデーモンがメモリリークして、OOM Killer を抑制していたため、サーバに SSH できない問題が発生しました・・・。調べてみると、どうも FuelPHP の Model まわりで発生しているようで、かなり根本解決が難しかったため、一定時間おきに PHP クローラーデーモンの内部で実際にキャッシングしている処理を別の PHP で実行する方式に変更しました。
クローラーデーモンは、Supervisor 経由で FuelPHP の task を、次のような設定に変更しました。次の設定では、FuelPHP tasks の crawler::run_fork を実行して、foo_cache という crawler に定義されている関数を別 PHP で 10 秒おきに実行するという意味になります。


[program:crawler]
command= /opt/remi/php56/root/usr/bin/php oil refine crawler:run_fork foo_cache --wait=10

そして、キャッシュの方のパフォーマンス問題がピーク時間帯やピークを少し越えたときに発生してしまいました。原因とすると、キャッシュ構造性の設計が起因していました。具体的には、ページのリクエストおきに ZSET なキーを取得して、そのキーをもとにすべての HASH を取得してキャッシュデータとデータベースから取得したデータを作成していましたが、この構成だとパフォーマンス的な限界があったようです。
この修正はかなり時間がかなりそうだったので、取り急ぎ PHP から Redis へアクセスする部分を Redis_db から phpredis に変更しましたが、大きくパフォーマンスは改善しませんでした。

この改善は、キャッシュデータを構造を大幅に見直して、PHP Serialize したデータのみにアクセスする構成にしましたが、結局時間がかかってしまい、引き継ぎもあってこの変更は受け入れられませんでした。。。

結局時間がかかったこともあって、ロードバランサー側にすべての動的なページを 10 分間キャッシュするように設定変更することになかったようです。パフォーマンス問題があったとはいえ、個人的にはちょっとありえない施策だったと思います。

こうしていくつかの問題があったのですが、引き継ぎもすべて完了して、この案件は僕としては終了しました。個人的な反省は、次のとおりです。

  • PHP を常時起動しているクローラーとして実装しない方がよい: やはり Apache の MaxRequestsPerChild や PHP FPM の pm.max_requests にみるように PHP は一定量処理したあとは再起動するのが鉄板だったようです、Supervisor を使っていたので、わざとデーモンプログラムを終了するようにすれば一定時間おきに起動することができました
  • キャッシュ構造の設計ミス: 今回二段階のキャッシュの構造でパフォーマンス問題がありました、二段階にした理由はキャッシュデータ量を減らすためそうしたのですが、キャッシュする量が対象増えても一段階に最初からするべきでした

あわせていくつか課題が残りました。

  • PHP ユニットテスト: 僕が実装した範囲はほぼテストコードを書いていましたが、その他のところはまったくテストコードがない状態となってしまいました、今回の案件の場合すべてのテストを書く必要はないと思いますが、最低限 Model と Controller の部分はテストコードを書いておけば QA するときの工数がかなり下がるため、総合的にみると工数は減るはずです、ヒアリングしてみるとテストコードを書いた経験がないため難しいと言っておられましたが、誰も最初は初めてなのでテストコードを書いてほしかったと思います
  • インフラの運用: オンプレなので、日々の運用で PHP や Nginx やセキュリティパッチをあてていく運用が必要となってきます、必要なものはすべて Qita Team にまとめておきましたが、PHP のバージョンが 3 つくらい古い様子なので、ちゃんと引き継ぎできているのか心配です

ということで、貴重な体験を振り返ってみました。
2016 年も始まっているので、引き続きウェブサービスを開発していきたいと思います。

このエントリーをはてなブックマークに追加

Homebrew cask の仕様がすこし変わった話

OS X 使いの皆さん定番の homebrew-cask ですが、おそらくクリスマス頃に仕様が少し変わったようです。

詳細のチケットは、こちらですが、要約すると、次のような感じになります。

  • 今まで ~/Applications へはすべてリンクだったが、移動するようになった
  • アプリケーションディレクトリは、すべて ~/Applications ではなく、/Applications になる
  • :target はしばらく動作するが、そのうち link に変更になる
  • Caskroom は、デフォルトで /opt/homebrew-cask/Caskroom となる
  • 上記のような理由で Caskroom 以下のバージョン番号のディレクトリは存在しなくなる

これはとても嬉しい変更です、例えば Google Chrome を homebrew-cask 経由でインストールした場合、今までだと 1Password が symlink のため動作しなかった問題があったので、これを根本的に解決できることになります。今までは、個人的にはまだ boxen を使っているので自動的に移動させるようなマニフェストを書いていましたが、これをしなくても済みます。

また、こちらのチケットにあるとおり、tap コマンドを使ったもののみに変更するようです。今までは、brew-cask というパッケージが必要でこれを更新するようになっていましたが、これから brew update コマンドのみで homebrew + homebrew-cask どちらもまとめて更新できるようになります。これはうれいしですね。

新しいシステムに切り替えるには、次のコマンドを実行するだけです。


$ brew uninstall --force brew-cask; brew update

簡単ですね、少し既存のもので試してみましたが、例えば google-chome をみると、次のようになっていますね。


$ brew cask info google-chrome
google-chrome: latest
Google Chrome
https://www.google.com/chrome/
/opt/boxen/homebrew-cask/Caskroom/google-chrome/latest (208 files, 184M)
https://github.com/caskroom/homebrew-cask/blob/master/Casks/google-chrome.rb
==> Contents
Google Chrome.app (app)
==> Caveats
The Mac App Store version of 1Password won't work with a Homebrew-Cask-linked Google Chrome. To bypass this limitation, you need to either:

+ Move Google Chrome to your /Applications directory (the app itself, not a symlink).
+ Install 1Password from outside the Mac App Store (licenses should transfer automatically, but you should contact AgileBits about it).

すべて同じようになっているのかなと思い、他のアプリでも確認してみましたが、なっていないようですね。


$ brew cask info flux
flux: 36-5
f.lux
https://justgetflux.com/
/opt/boxen/homebrew-cask/Caskroom/flux/36-5 (105 files, 2.7M)
https://github.com/caskroom/homebrew-cask/blob/master/Casks/flux.rb
==> Contents
Flux.app (app)

おそらく、順次変更しているのか、いつか内部的にまとめて切り替えるのかもしれません。

 

homebrew-cask を使っている人は、この機会に新しいアップデートに対応してみてはいかがでしょうか?

このエントリーをはてなブックマークに追加

CentOS 7 で起動時と終了時にそれぞれ1回だけ実行するスクリプトを作成する

CentOS 7 で起動時と終了時にそれぞれ1回だけ実行するスクリプトを作成する方法を紹介したと思います。
次のような systemd スクリプトを作成します。


cat << EOF > /usr/lib/systemd/system/my-oneshot.service

[Unit]
Description=my-oneshot
After=network.service
Before=shutdown.target
Requires=network.service

[Service]
Type=oneshot
ExecStart=/bin/bash -v ‘/usr/local/bin/my-up.sh’
ExecStop=/bin/bash -v ‘/usr/local/bin/my-down.sh’
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
EOF

次に systemd をリロードして、サービスを有効・開始します。


$ sudo systemctl daemon-reload
$ sudo systemctl enable my-oneshot
$ sudo systemctl start my-oneshot

これで、起動時には /usr/local/bin/my-up.sh が実行されて、終了時には /usr/local/bin/my-down.sh が実行されます。
もちろん、EC2 上でも利用可能ので、AutoScaling 時などに起動あるいは終了時に実行したいコマンドがあるときに設定すると便利ですよね。

このエントリーをはてなブックマークに追加

CircleCI 上で、最初から定義されている環境変数について

CircleCI 便利ですが、CircleCI 上の EC2 ホスト上で、いくつか CircleCI 上の環境変数が定義されているので、一部まとめておきます。
公式ドキュメントは、こちらですね。

  • CI: 値 true、CI かどうか
  • CIRLECI: 値 true、CircleCI かどうか
  • CIRCLE_ARTIFACTS: 値 /tmp/circle-artifacts.XXXX
  • CIRCLE_BRANCH: 値 ブランチ名、対象のブランチ名
  • CIRCLE_BUILD_NUM: 値 整数、CircleCI 上でのビルド回数番号です
  • CIRCLE_COMPARE_URL: 値 URL、前回のビルドからの差分をあらわす URL、GitHub の場合 https://github.com/[project]/compare/xxx…yyy となります
  • CIRCLE_NODE_INDEX: 値 整数、CircleCI で並列で使っている場合 0 から始まるインデックス番号です
  • CIRCLE_NODE_TOTAL: 値 整数、CircleCI で並列で使っている場合合計のコンテナの数です
  • CIRCLE_PREVIOUS_BUILD_NUM: 値 整数、前回のビルド回数番号です、CIRCLE_BUILD_NUM -1 になります
  • CIRCLE_PROJECT_REPONAME: 値 リポジトリ名、対象のリポジトリ名です
  • CIRCLE_PROJECT_USENAME: 値 ユーザ名、対象のリポジトリを操作しているユーザ名です、GitHub の場合関連づけたオーガニゼーション名となるようです
  • CIRCLE_SHA1: 値 SHA1、コミットをあらわすハッシュ値です
  • CIRCLE_USERNAME: 値 ユーザ名、GitHub の場合自身の GitHub ユーザ名です
  • CI_TEST_REPOSTS: 値 パス、Junit のレポートパス名です
  • CI_PULL_REQUEST: プルリクエストです
  • CI_PULL_REQUESTS: プリクエストの URL です

これらの環境変数をうまく使い分けることで、CI の操作ができて便利です。
特に、個人的に CIRCLECI、CIRCLE_NODE_INDEX、CIRCLE_NODE_TOTAL、あたりはインフラ CI で駆使して使っていますので、別の機会に紹介したいと思います。

このエントリーをはてなブックマークに追加

Fluentd s3 + ローカルファイルに出力する設定の紹介

このエントリは、Fluentd Advent Calendar 2015 – Qiita の 4 日目のエントリです。当日、勝手にエントリしています。

今日は、最近実施した非常によくある構成 Fluend 経由で s3 + ローカルファイルに出力する設定を本番環境(※ただし、正式本番稼働前)で設定したので、どこにもある情報ですが、一緒に設定された設定ファイルの例がなかなかなかったので紹介してみます。

OS は、CentOS 7 になります。td-agent は、公式の方でインストール します。

やりたいことしては、S3 のバケット上に、「YYYY/MM/DD/HH/タグ1_タグ2」ファイルとして出力して、ローカルファイルにも同じディレクトリ構成のファイルで、同時出力することになります。

まず、設定ファイルですが、次のようになりました。

<source>
  type forward
  bind 127.0.0.1
  port 24224
</source>

<match hoge.**>
  type copy

  <store>
    type forest
    subtype s3
    <template>
      id hoge-s3-${tag_parts[1]}
      path ${tag_parts[1]}/

      aws_key_id <AWS_ACCESS_KEY_ID>
      aws_sec_key <AWS_SECRET_ACCESS_KEY>
      s3_bucket hoge-bucket
      s3_region <AWS_DEFAULT_REGION>
      format json
      s3_object_key_format %{path}%{time_slice}_%{index}.log
      store_as json

      time_slice_format %Y/%m/%d/%H/${tag_parts[1]}_%Y%m%d%H%M_${tag_parts[2]}
      flush_interval 60s
      flush_at_shutdown true

      buffer_type file
      buffer_path /var/log/td-agent/.s3-${tag_parts[1]}_%Y%m%d%H%M_${tag_parts[2]}.log
      buffer_chunk_limit 64m
      buffer_queue_limit 32

      num_threads 1
    </template>
  </store>

  <store>
    type forest
    subtype file_alternative
    <template>
      id hoge-file-${tag_parts[1]}
      path /var/log/td-agent/${tag_parts[1]}/%Y/%m/%d/%H/${tag_parts[1]}_%Y%m%d%H%M_${tag_parts[2]}.log
      symlink_path /var/log/td-agent.log
      dir_mode 0750

      time_slice_format %Y%m%d%H%M
      flush_interval 0s
      flush_at_shutdown true

      buffer_type file
      buffer_path /var/log/td-agent/.${tag_parts[1]}_%Y%m%d%H%M_${tag_parts[2]}.log
      buffer_chunk_limit 64m
      buffer_queue_limit 32

      num_threads 1
    </template>
  </store>
</match>

プラグインは、設定ファイルから分かるとおり、fluentd-plugin-forestfluent-plugin-file-alternative、が必要なので、td-agent-gem 経由でインストールしておきます。


$ sudo /opt/td-agent/usr/sbin/td-agent-gem install --no-ri --no-rdoc fluentd-plugin-forest fluent-plugin-file-alternative

ここでちょっとハマったのは、ローカルファイルへ出力する file_alternative の実際の出力ファイルが「/var/log/td-agent/.YYYY/MM/DD/HH/ファイル名」となってしまって、/var/log/td-agent 以下とみるとドットディレクトリとしてしまったところでした。上の設定で、正しくファイルが出力されています。

これから、この環境で本番稼働前のベンチマークを実施して、buffer まわりのパラメータ、thread の調整などを行ってみるつもりです。

今回、監視まわりは Datadog を使っているので、別のエントリーで紹介したいと思います。

このエントリーをはてなブックマークに追加

アウトプットするということ

やはり、どんな小さなことでも、大きいことでも、アウトプットするというのは大事なことだと思ったので、心機一転ブログのデザインをリニューアルしてみました。
とある案件で、てんてこ舞いだったということもありますが・・・。

いまだずっと WordPress を使い続けているのですが、かなり進化していきますね。
今だとほとんど直接デザインのテンプレートの HTML を修正する必要はないですね。

さて、昨今 AWS 全盛期として、VPS との料金比較表が Qiita に掲載されていました。
AWS – VPS料金比較表 (ssh可のみ) – Qiita

特に転送量制限があるものがあるものもあれば、特に記載はない無制限?なものをあって、とても興味深いですね。
僕は、今のところ、個人的には、さくら VPS をメインで使っていましたが、DigitalOcean を使ってもいいかもしれないと思いました。
あと、さくらは今あの石狩リージョンの初期費用が無料キャンペーンもやっているそうです。

このエントリーをはてなブックマークに追加

Slack のチーム移動を簡単にするキーボードショートカットを設定した

なんだか、久しぶりにブログを書きます。

Slack にチーム移動するためのキーボードショートカットキーは、Slack keyboard shortcuts にあるとおり、% + Shift + [ or ] か、% + 数字、なのですが、僕の環境の場合後者は別の OS X のワークスペースの切り替えで使っているため、前者のキーボードだとちょっと使いに食いにくので、Slackのチャンネル移動をキーボードで簡単に出来るようKarabinerのprivate.xmlを書いてみました。

次のように private.xml に追記することで、Command + P or N で、チームの移動をすることができるようになりました!


...
__KeyToKey__ KeyCode::P, ModifierFlag::COMMAND_L, KeyCode::BRACKET_LEFT, ModifierFlag::COMMAND_L | Mod
ifierFlag::SHIFT_L

__KeyToKey__ KeyCode::N, ModifierFlag::COMMAND_L, KeyCode::BRACKET_RIGHT, ModifierFlag::COMMAND_L | Mo
difierFlag::SHIFT_L

...

参考リンク

このエントリーをはてなブックマークに追加

Ganglia で MySQL をモニタリングする

このエントリは、MySQL Casual Advent Calendar 22日目のエントリです。

多数の MySQL サーバをもモニタリングするとき Ganglia を使うと便利です。

現在の Ganglia は、バージョン 3.6.0 なのですが、MySQL をモニタリングしたい場合には、前に hirose31 さんが作ったプラグインがありますが、現在は本体に取り込まれているプラグインを使うといいです。

まず、インストール方法は、CentOS を例にとって説明しますが、Ganglia の RPM パッケージを作成しています。本体に SPEC ファイルのもとがあるので、手元で ./configure したあと ganglia.spec を使うと、次の RPM パッケージが出来上がります。

  • ganglia-web: Ganglia の Web GUI
  • gangala-gmetad: Ganglia サーバの収集デーモン
  • ganglia-gmond: Ganglia クライアント
  • ganglia-gmond-modules-python: Ganglia クライアントの Python プラグイン
  • ganglia-devel: Ganglia 開発用のライブラリ

Ganglia のクライアントに必要なのは、ganglia-gmond, ganglia-gmond-modules-python パッケージをインストールする必要があります。ただし、依存関係で libganglia というパッケージもあわせてインストール必要があります。あと、Python の MySQL クライアント MySQL-python が必要ですが、これは使っている MySQL のバージョンによって動作しないことがあるため、別途あわせて作成してインストールしておくとよいでしょう。

Ganglia で MySQL をモニタリングしているのは、mysql.pyDBUtil.py で、内部的には show innodb status の出力結果を頑張って解析して値を取得しています。

しかし、最新版では、次の問題ありました。

      DBUtil.py で、UNIX Domain Socket がサポートされていない(mysql.py 側ではサポートされているが、3.6.0 には取り込まれていない…)
      MySQL 5.1 以降の show innodb status の出力に対応していない(こちらのパッチで直ります)

それぞれのパッチは、次のようなっています。


そして、本家にもその他の変更も含めて、すこし大きい pull-request を送っておいたのですが、無視され続けていました…。まさにすこし大きい pull-request は無視されてしまう法則にあたってしまったようです…。

内部的な変更は、これで動作するので、あとは設定するだけですが、MySQL サーバのユーザにユーザ権限 SUPER, PROCESS をもったユーザが必要になります。
これを作成した状態で、/etc/ganglia/conf.d/mysql.pyconf をこのような感じで設定します(この設定ではスレーブでの設定例です)

あとは、gmond を起動して、/var/log/messages にエラーが出ていなければ、次のようなグラフが出力されてるはずです。

ganglia-mysql-slave

このエントリーをはてなブックマークに追加

あなたの OS X で、はじめてみよう! “h2o”

この記事は、h2o Advent Calendar 第四日目の記事です。

まず、h2o を実際にサーバ上で使うために、h2o は OS X に対応しているので、OS X 上で h2o をはじめてみましょう。

最初にインストール。これは、homebrew-headonly を使うと、次のように簡単にインストールすることができます。なお、Homebrew がインストールされていることが前提です。


$ brew tap homebrew/headonly
$ brew install homebrew/headonly/h2o

そうすると、最新の h2o が $HOMEBREW_ROOT/Cellar/h2o/HEAD にインストールされます。$HOMEBREW_ROOT/bin/h2o に symlink をはるので、まずはサンプルの設定ファイルで起動してみましょう。


$ cd [Homebrew のキャッシュパス]/cache/homebrew/h2o--git/
$ h2o -c examples/h2o/h2o.conf

さっそく、別のターミナルあるいはブラウザから、動作確認してみましょう。
サンプルの設定ファイルでは、HTTP は 8080 と HTTPS は 8081 ポートで起動する内容になっています。


$ curl localhost:8080

<!DOCTYPE html>
<html>
<header>
<title>Welcome to H2O</title>
</header>
<body>
<p>Welcome to H2O – an optimized HTTP server</p>
<p>It works!</p>
<body>
</html>

ちゃんと HTTP 接続できましたね!
h2o を停止するには、Ctrl-C コマンドを入力します。

サンプルの設定ファイルでは、次のようになっています。

# to find out the configuration commands, run: h2o --help

listen: 8080
listen:
  port: 8081
  ssl:
    certificate-file: examples/h2o/server.crt
    key-file: examples/h2o/server.key
  hosts:
    default:
    paths:
      /:
      file.dir: examples/doc_root
    access-log: /dev/stdout

YAML 形式の設定ファイルで、とてもシンプルな設定ファイルになっていますね。

h2o –help をすると、設定ファイルで指定できるパラメータが分かりますので、ここから設定できるパラメータを勝手に日本語訳してみました。

listen: リクエストを受け付けポートの設定(listen:  あるいは、次のようにマッピングによる設定ができます)
  port: 待ち構えるポート番号あるいはサービス名
  host: 待ち構える IP アドレス
  ssl: SSL を使用するかどうか(デフォルト: none)、使用する場合は certificate-file と key-file を指定する

hosts: ホストごとのマッピングを設定します

paths: URL パスのマッピングを設定します

request-timeout: リクエストを受け付けたときのタイムアウト秒です(デフォルト 10 * 1000 秒)

limit-rquest-body: リクエストボディの最大サイズ(デフォルト: 無制限)

http1-upgrade-to-http2: HTTP/2 へのアップグレードを可能にするかフラグ(ON あるいは OFF、デフォルト ON)
http2-max-concurrent-requests-per-connection: HTTP/2 ストリームの内部で処理される最大リクエスト数(デフォルト: 16)

max-connections: 最大接続数(デフォルト: 1024)
num-threads: ワーカーのスレッド数(デフォルト: 1)

access-log: アクセスログのファイル名、マッピング情報の場合はフォーマットとの組み合わせで設定することができます、| から始まる場合はパイプの設定をすることができます、デフォルトのアクセスログフォーマットは。、「"%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""」です

file.dir: 公開するディレクトリのパスです
file.index: インデックスを示すファイル名です
file.mime.settypes:  MIME タイプの設定です
file.mime.addtypes: 追加する MIME タイプの設定です
file.mime.removetypes: 削除する MIME タイプの設定です
file.mime.setdefaulttype: MIME タイプのデフォルト設定です

proxy.reverse.url: アップストリームの URL です(HTTP のみサポートされています)
proxy.keepalive:: Keepalive を有効にするかどうかです(デフォルト:OFF)
proxy.timeout.io: アップストリームの I/O タイムアウトミリ秒です(デフォルト: 5000 ミリ秒)
proxy.timeout.keepalive: アイドル接続のタイムアウトミリ秒です(デフォルト: 2000 ミリ秒)

例えば、実際設定例は、次のようになるようでしょう。file.index だけ設定をすると「in command file.index, argument cannot be a scalar」エラーがでてしまい、設定することができませんでした。

listen:
  port: 8080
  host: 127.0.0.1

max-connections: 1024
num-threads: 1

request-timeout: 1
hosts:
  default:
    paths:
      /:
        file.dir: /var/www/default
    access-log: 
      path: "|/usr/sbin/rotatelogs /var/log/h2o/access_log-%Y-%m-%d 3600"
      format: "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""
  localhost:
    paths:
      /:
        file.dir: /var/www/localhost

h2o は、日々進化しているので、今後の進化に目が離せませんね。
現時点では、パフォーマンスが必要な静的ファイルの処理や、中間プロキシとして使うといいのかなと考えています。

あと、h2o の実装方法については、HTTP2 カンファレンスの動画、約3時間1分くらいから作者の @kazuho さんの講演があるので、実際にどうやって高速なコードを書くのか、その技術を詳しく解説しているので、興味のある人は要チェックだと思います。僕も拝見しましたが、とても勉強になりました。