oauth2_proxyとNginxのauth_requestを組み合わせると便利
oauth2_proxy、便利でググると導入手順も結構出てくるのだけど、今回紹介するのは(環境によっては)さらに便利に使えるナウい方法です。
なにをするのかというとREAMDEに書いてあります。
Configuring for use with the Nginx auth_request directive The Nginx auth_request directive allows Nginx to authenticate requests via the oauth2_proxy's /auth endpoint, which only returns a 202 Accepted response or a 401 Unauthorized response without proxying the request through. For example:
サンプル設定もあるので、なんとなく雰囲気は伝わるんだけど、はっきり言ってこれだけ見てすんなり出来る人はなかなかいないだろう(自分がそうだった)し、何が便利なのかもイマイチわからんので、もうちょい丁寧に説明してみようと思います。
まず以前からQuipperでもやっていた一般的な構成はこうです。
- nginxで特定locationやserver全体でproxy_passにoauth2_proxyサーバを指定する
- oauth2_proxyがGoogle Appsの認証ページにリダイレクトする
- 認証成功したら、oauth2_proxyのupstreamで設定しているbackendのアプリケーションにproxyされる
しかし、これだと下記のような面倒が発生することがあります。
- 認証を導入したいbackendのアプリケーションが複数ある場合、その分複数のoauth2_proxy設定を用意し、複数のプロセスを起動しなくてはいけない
- nginx側のproxy_passも変えないといけない
- nginx側の設定だけを見るとどこに最終的にproxyされるのかわからん
- 特定pathのみ認証をいれたいような場合に、設定が煩雑になる
それがこうなります。
- nginxのauth_requestディレクティブ使って、oauth2_proxyに対して認証(ログイン)済みかどうか確認する
- 未認証の場合はoauth2_proxyに自動的に認証を依頼する
- 認証がすんだらnginxのproxy_passに設定しているbackendアプリケーションにproxyされる
ポイントは「oauth2_proxyには認証だけしてもらってbackendへのproxyには一切ノータッチ」になるってとこです。これによって上述した問題は全て解決されます。oauth2_proxy沢山起動する必要もないし、nginxの設定も見通しが良くなります。
また、auth_requestはsatisfyに対応しているので、allowディレクティブと組み合わせることで、「特定のクライアントのみ認証不要にする」ということも簡単にできるようになります。
設定例
前置きが長くなった。ここからそれをやるための設定例です。尚、設定を作るにあたって、同じような仕組みで動作するクックパットさんのnginx_omniauth_adapterの紹介エントリが大変参考になりました。ありがとうございます。
oauth2_proxy側の設定についてはauth_requstを使わない場合とほぼ変わらないので省きますが、2点だけ注意点があります。
- まだauthエンドポイントがリリースされてない(releasesにあるバイナリでは未サポート)ので、masterからbuildすること
- oauth2_proxyでは一切proxyしないけど、configのupstreamsにはなんでもいいので値をsetしとくこと。しないと起動しない
です。
common/oauth2_proxy.conf
oauth2_proxyの各種エンドポイントへproxyするための設定。
# for checking login status. # only returns a 202 Accepted response or a 401 Unauthorized response; location = /oauth2/auth { internal; proxy_pass http://127.0.0.1:4180; proxy_set_header Host $host; proxy_pass_request_body off; proxy_set_header Content-Length ""; } # to start oauth cycle location = /oauth2/start { internal; proxy_pass http://127.0.0.1:4180; proxy_set_header Host $host; proxy_pass_request_body off; proxy_set_header Content-Length ""; } # to complete oauth cycle location = /oauth2/callback { auth_request off; proxy_pass http://127.0.0.1:4180; proxy_set_header Host $host; }
common/oauth2_enable.conf
locationコンテクストでincludeして、認証を有効化する設定。
satisfy any; # 認証済みかどうか確認 auth_request /oauth2/auth; # ^で401がかえってきたら認証を開始 # rdパラメータにpathをセットしないと、最終的に/にredirectされちゃうよ error_page 401 = /oauth2/start?rd=$uri; # オフィスなど認証が不要なクライアントIPをallow allow 119.172.242.131; deny all;
virtual host
上記の2つの設定をincludeして一部のpathのみ認証を有効にする設定例。
server { listen 80; server_name my.domain.com; include conf.d/common/oauth2_proxy.conf; location /secret/path { include conf.d/common/oauth2_enable.conf; # 認証こけたらproxyされないよ proxy_pass http://backend; } location / { proxy_pass http:/backend; } }
おわり。
Puppet人がAnsible界にきてみて
この記事はPuppet Advent Calendar 2015の14日目の記事です。 昨日はおっくんのPuppetの予約語 $name と $titleでした。
書いている人
- Puppetはペパボで6年間、複数のサービスインフラ用に書いてきた。社内で一番書いたかも
- 新規やら導入やら引き継ぎやらいろいろ
- オンプレもクラウドもある
- AnsibleはQuipperで2ヶ月ちょい。既ににあるのに書き足したりリファクタリングしたり
- 当然だけど2ヶ月ずっとそれだけやってたわけではないです
- EC2オンリー
経験値が全然違うけど、できるだけ公平に評価してみるつもり。 尚、Puppet、Ansible以外は語れるほど使ったこと無いので言及しません。
結論
- クラウド(IaaS)に
Configuration Management Tool
を導入したいならAnsibleがいいと思う - オンプレ、特に歴史ある複雑なシステムに対して使うならPuppet
- どんだけ慣れてたとしてもAnsibleを選択するのはドMだと思う
- Ansible超人なら大丈夫かも
普通の結論だと思う。
初期導入コストについて
これは、さんざん言われているけど、やはりエージェントレスなAnsibleの方が始めやすいと思います。Ansibleの方は最初から導入したことないけど、途中から触るようになった自分でもそう思います。
puppetは慣れててもマスターとエージェントのバージョン不整合とか認証まわりでハマることがしばしばあるので、その辺考えなくていいのはとても楽です。(あと巷で得れる情報量が…)
ただし、Ansibleの方もある程度初期の頃から、ベストプラクティスに従って設計しないと後々辛いことになるので、その辺のこととかトレンドを学習した上で始めた方がいいと思います。
学習コストについて
DSL(Ansibleのあれを単なるYAMLだと思ってはいけない)の学習コストは似たようなもんだと思います。
Ansibleの方がYAMLで簡潔に定義できていいよねぇ〜という意見を見ることが多いような気がしますが、自分はどっちかといえばAnsibleの方が癖があって馴染みにくいように感じています。
Puppetの方はまぁゆうても普通のLLぽいです。なので、多分こんか感じかなぁで書いても大体うまく行く感じがします。対してAnsibleの方は「え!マジで!それそう書くの?」みたいなことが多いです(短くは書けるので普通に書いててもGolfぽい)。
とはいえ、どちらも基本的な文法についてはすぐに慣れます。
表現力について
ここが全然違う。これについてはPuppetの圧勝だと思います。
上述したように基本的な文法で単純なリソース定義をする分には大差はないように思いますが、複雑な環境や構成を表現するにはAnsibleではきついと思います。
複雑というのは例えばこういうの
オンプレだと、こういったことは望む望まざるとにかかわらず発生します。
Ansibleにもroleの概念などは有りますが、変数のscopeがざっくりしてたり、条件分岐や依存関係の表現が分かりにくかったりして、上記のような複雑なものを綺麗にレイヤーをわけて適切に抽象化し、メンテナンス性の高い状態を維持するのは困難であろうと感じています(Puppetでも大変ではあるけど、うまく設計すれば可能)。
逆にいうと扱う対象がシンプルであればAnsibleで十分というか、コンパクトに記述できる分、全体の把握もしやすいので、Puppetより適していると思っています。
クラウドをクラウドらしくということ
上記のような複雑な状態をつくらないということです。
H/Wレイヤーの複雑性はそもそも発生しないので、いいとして、オンプレの時の感覚で単一ホストにどっちゃりいろいろつめ込んだりしないで、1ホストにつき1つのことだけをやるようにするのが肝要かと思います。
OSについては、クラウドでもいつかは切り替える日が来ますが、マシン調達のためのリードタイムが無いに等しいので、オンプレのように長い期間をかけてOSをリプレースせざるを得ないといった理由で複数のOSが混在するという状況は避けやすいと思います。
構成管理ツールの方では複数のOSに対応させるような書き方は最初から諦めて、切り替えのタイミングでそれように全部新たに書いてしまったほうが安上がりです。古い環境はすっぱり捨ててしまえばよい。
以上、文字ばかりのこのエントリをここまで読んでいただきありがとうございました。
お前のAnsible評価は間違っておるぞ。という方、突っ込お待ちしています。
明日はあの人です。
第3回ペパボテックカンファレンスでPrivate S3の作り方を発表してきた
第3回ペパボテックカンファレンス - connpass で発表してきました。
スライドはこちら。
www.slideshare.net
前後編に分かれていて、後半は @hiboma が発表しました。続けて見た方が良いです。
MogileFS をバックエンドとしたPrivate S3の作り方 【後半】API 編
残念ながらYAPC::Asiaの方では採択されなかったのですが、話すことは大分前からあれこれ考えていたので、アウトプットできてほっとしています。
内容は、細かいノウハウや工夫はあるものの、基本的には枯れたソフトウェアやセオリーの積み上げです。なので「ふーん割と普通だなぁ」と思うかもしれません。
でもその普通ぽいことをしっかりと積み上げることで、「国内最大級の写真共有サービス」のインフラ上に更に「国内最大級のショッピングカートASP」の画像をまるごと移行出来てしまう。という事例を示せたのは、結構意味のあることでは無いかと思ってます。
- 「事実を積み上げて判断する」
- 「古典的でも地道にやる」
ってことを、以前@myfinderさんが言ってて、これすごく好きなんですよね。今後もこの気持ちでよい物をつくっていきたいです。
ngx_mrubyを使って特定ホスト以外からのアクセスをメンテナンス画面にする
こんな感じ。(ほんとはもうちょいifが多い)
error_page 503 /maintenance.html; location = /maintenance.html { internal; } mruby_access_handler_code ' c = Nginx::Connection.new r = Nginx::Request.new Nginx.return -> do if File.exists?("/var/tmp/maint_ignore_office") && c.remote_ip == "192.168.1.3" return Nginx::DECLINED end if !File.exists?("/var/tmp/maint") return Nginx::DECLINED end # 503 return Nginx::HTTP_SERVICE_UNAVAILABLE end.call ';
これで、
/var/tmp/maint
が存在したらメンテナンス画面になって/var/tmp/maint_ignore_office
が存在して、且つアクセス元が192.168.1.3
であれば、そこは通常通りレスポンス
になる。
元々はひろせさんのこの方式で変数をsetしてコントロールしてたんだけど nginxで特定ホスト以外からのアクセスをメンテナンス画面にする方法 (2) - (ひ)メモ
その後ふじわらさんのフラグファイルみて切り替えるのがよいなぁと思って nginxで特定ホスト以外からのアクセスをメンテナンス画面にする方法 - 酒日記 はてな支店
でも特定ホスト(今回だとオフィス)からのみ開放するのもフラグファイルの有る無しでコントロールするとなると、またNginxの「複数条件できん」がネックで、うーんってなって、
あーそうだ!このNginxはngx_mrubyつかえるようになってるからそれでやろーっと。てなって上記のコードのようになった次第。
ちなみに、最初はこんな感じでハンドラのコードを書いたんだけど、これはうまくくいかなくて
if File.exists?("/var/tmp/maint_ignore_office") && c.remote_ip == "192.168.1.3" Nginx.return Nginx::DECLINED end if !File.exists?("/var/tmp/maint") Nginx.return Nginx::DECLINED end Nginx.return Nginx::HTTP_SERVICE_UNAVAILABLE
弊社は、同僚に @matsumotory がいるという便利な環境なので、聞いたら
Nginx.returnはコードからreturnするわけではないので 全部実行されちゃいます。
と教えてもらって、なるほどん!ってなって、次にこんな感じにしたら
if File.exists?("/var/tmp/maint_ignore_office") && c.remote_ip == "192.168.1.3" return Nginx.return Nginx::DECLINED end if !File.exists?("/var/tmp/maint") return Nginx.return Nginx::DECLINED end return Nginx.return Nginx::HTTP_SERVICE_UNAVAILABLE
こんどは、LocalJumpError
が出てしまって、あーなるほどmrubyのハンドラって関数なわけじゃないので途中で抜けれないのねぇってなって、lambdaにしてreturnする冒頭のコードに落ち着いたのでした。
褒めてもらった。
くろださんがngx_mrubyのNginx.returnをシャレオツな感じでreturnしてるのを見た
— MATSUMOTO, Ryosuke (@matsumotory) 2015, 7月 29
このラムダな書き方は、mruby_set
ディレクティブとかでも同様につかえて、なかなかシャレオツ便利です。
尚、今回は大したロジックでもないので、直接nginxの設定にコードを埋め込んでいるけど、複雑なのは、外部ファイルに切り出して、テストも書いた方がいいです。
テストのあたりは@hsbtさんのこれがわかりやすい。
構成管理(puppet)リポジトリでrake mtest
すると全ファイルのテストが実行されるようにしてます。
ngx_mruby大変便利なので、今後もどんどん使っていく所存です。
はてなブログに引っ越した
あまり更新してないのだけど、昨日中の人と話しててダイアリーで続ける理由も無いなぁと思って引っ越した。
3ポチくらいでブクマも全部引っ越し終わって楽ちんだった。
あとやっぱりずっと使いやすそうだ(それでも多分更新しないのだけど)。
実録、ほぼ無停止なMHAによるマスターフェイルオーバー(動画もあるよ
タイトルはこちらのインスパイアです。
これはPepabo Advent Calendar 2014 - Qiita18日目のエントリーです。 昨日は@nakajijapanの社内での活動履歴でした。なかじーーー。
1年半ぶりのblog更新ですが、そこそこ元気にやってます。もじゃもじゃは辞めました。 で、MHAですが、MHAってあれです。これです。
MHA for MySQL の概要 - Gosuke Miyashita
すごいです。すごいので今更ですが導入しようとおもいました。
ありがたいことにMHAはドキュメントが非常に充実しているので、大抵のことはそれを読めば把握できます。
が、賢明なインフラエンジニアのみなさんは次のように思うはずです。
- 実際に構築して動かして検証しないことには!
- 実運用を想定した意地悪を色々やってみたい!
- 色々やって環境をぶっ壊して、再現してまたぶっ壊したい!
- ip_failoverスクリプトを実装して、動作検証したい!
だがしかし、MHAは最低でも3台のホストが必要なのです。カジュアルに3台もオンプレサーバ調達できないし、同じような設定、構築を複数台で何度も行うのは辛いし、そろそろ四十路だ。
というわけで、つくったのがこちらでです。*1
lamanotrama/vagrant-mha · GitHub
デモ
細かいことは置いといて、このデモ動画でやってることはざっくりこんな感じです。
- muxを使ってtmuxのpaneを複数開き、vagrant(virtaulbox)のvm4台にログイ
- 左上: managerノード上でvipに対して接続テストするスクリプトを実行
- 左下: 同じくmanagerノード上でシェル操作
- 右側: mysqlノード3台でそれぞれmysqlのログをtail
- 一旦mhaマネージャデーモンを停止 -- 起動しているとオンラインmaster切り替えが実行できない + オンラインマスター切り替えを2回実行
- マネージャデーモンを起動 + 右上の現master(node001)のmysqldを停止
- マネージャによる自動マスター切り替え
左下のシェル操作以外はREADMEにある、setup手順とdemoの開始コマンドだけで勝手に始まります(多分)。 便利そうじゃないですか?
くわしく
というか想定問答集的な。
provisioning
vmのprovisioningにはpuppetを使っています。 MHA周りの定義はpuppetモジュールとして切り出してあるので、librarian-puppetを使って本番でもさくさくーと同様の環境を構築できるという寸法です。もうちょいブラッシュアップしたら、PuppetForgeにもupします。
lamanotrama/puppet-mha · GitHub
mysqlのバージョン(パッケージ名)はこの辺で定義しています。 https://github.com/lamanotrama/vagrant-mha/blob/master/roles/mha_node/manifests/init.pp#L15
ここを変更すれば別のバージョンで試したり、node毎に別々のバージョンにしたりってのも出来ます。vmにはmysql本家やpercona、epelといったyumリポジトリは登録済です。 OSは全部CentOS6にしていますが、それはVagrantfileで変更してください。多分el5、6とそのクローンOSでなら動くんじゃないかなと思います(試してない)。
mysql_online_switch ってなんぞ
オンラインでのmaster切り替えには /usr/bin/masterha_master_switch を使うことになっているんですが、オプションの指定が多くて面倒なので、よく指定する(であろう)オプションを面倒みてくれる簡単なラッパースクリプトを作りました。デモで実行しているのはそれです。
puppet-mha/mysql_online_switch at master · lamanotrama/puppet-mha · GitHub
接続でなくて更新のテストもしたい
そんなこともあろうかと、単純な更新テストできるスクリプトも置いておきました。
vagrant-mha/roles/mha_manager/templates/usr/local/bin at master · lamanotrama/vagrant-mha · GitHub
ip failover どうなってんの
MHAのリポジトリにサンプル実装があるので、それにちょちょいと手を加えて、単純なエイリアスIP付け替えを行うようにしました。 この辺に置いてます。 vagrant-mha/roles/mha_manager/files/usr/local/libexec at master · lamanotrama/vagrant-mha · GitHub
尚、初回vagrant up直後はnode001にvipを付けていますが、vmを停止して再度起動してもアタッチされないので、都度vagrant provisonを実行してvipをくっつけてください。手打ちでifconfigでもいいです。
環境のリセット
いろいろ試してレプリケーションをぶっ壊わした後に、全nodeでmysql_install_db実行したりするのは面倒臭かろう。ということでdbをresetするスクリプトを入れておきました。
vagrant-mha/reset_dbs.sh at master · lamanotrama/vagrant-mha · GitHub
ホストマシン(Mac)上で実行すると、各vmにvagrant sshしてあれこれやってくれます。その後レプリケーションとかMHA用にGRANT入れるのは、vagrant provison実行でよろ。
まとめ
明日は@kurotakyです。
*1:便利そうなプロジェクトを見つけたので試したんだけど、まず動かないし、色々微妙なとこがあったのでforkして変えていったら原型が全くなくなった。
WEB+DB PRESS Vol.75 に寄稿しました
自分を含む5人のペパボメンバー(@hsbt, @kentaro, @tnmt, @atakaP)で第一特集の「複雑性の増大と環境の変化に対応する継続的Webサービス改善ガイド」ってのをやらせて頂きました。その内自分が書いたのは4章の「インフラ構成管理の改善」の半分くらいです。
- 作者: 栗林健太郎,柴田博志,はまちや2,常松伸哉,黒田良,川添貴生,安宅啓,松下雅和,桑野章弘,Jxck,伊藤直也,佐藤鉄平,登尾徳誠,中川勝樹,奥野幹也,近藤宇智朗,堀江幸紀,後藤秀宣,渡邊恵太,中島聡,A-Listers,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2013/06/22
- メディア: 大型本
- この商品を含むブログ (8件) を見る
3月のある日のぽよん会(ってのが会社であるんです)で柴田さん、あんちぽさんに「お前も当然かくよな!」って言われた時は、正直「小学校以来、作文宿題の全てを友達に書いてもらってた俺に8ページも書けだとぉぉ!」とナーバスになったんですが、隣の席のつね様こと常松さんに「いっしょに書かない?」って誘ったら「もちろんさ!」と快諾してもらい、なんとか協力して書き上げることができました。やはり持つべきものはイケメンの同僚です。
内容としては、歴史あるサービスってインフラでも技術的負債が結構たまってくるよね。ヘテムルではこんな感じで運用を改善して、負債を返済していったよ。って感じです。
改善と題してはいますが、例えばこれからサービスを立ちあげていくってフェーズでも、後々負債を産まない為に気をつけることってなんだろ?って視点で読んでもらえたら、それはそれで幾つかヒントになる事が書いてあるんじゃないかなぁと思います。
んで、そのヘテムルでは現在、自分や、イケメン、あとターミナルマニアの@glidenoteとかとかと、一緒にインフラ周りのお仕事をしてくれる仲間を募集中です。https://js01.jposting.net/paperboy/u/recruit/job.phtml?job_code=66
ちょっとだけ興味あるんだけどって人はtwitterのDMとかでも全然OKです。お待ちしてまーす。