社で、徐々にConsulへの依存度が上がってきたので、Consulパーソンをより増やすべくワークショップを開催した。
gistを公開します。
サンプルプロジェクトは以下。Vagrantのプロビジョナでポンポンとステップを進められます。
Consul、機能が多いけど、クールなこともできるので、知っていることはどんどんドキュメントにしていこうと思いました。
社で、徐々にConsulへの依存度が上がってきたので、Consulパーソンをより増やすべくワークショップを開催した。
gistを公開します。
サンプルプロジェクトは以下。Vagrantのプロビジョナでポンポンとステップを進められます。
Consul、機能が多いけど、クールなこともできるので、知っていることはどんどんドキュメントにしていこうと思いました。
という状態。起動しないので mysqldump
のようなコマンドは使えないが、以下のように復旧した。
Puppetなどでサーバ構成管理していればなんちゃないですね。していない場合は... ......
いわゆる、compute nodeというもの。
拡張の種類にもよるが、 nova show
などで確認できるはず。また、一緒に instance_name
というものも確認できる、控えておく。
$ nova show test001 +--------------------------------------+------------------------------------------------------------------+ | Property | Value | +--------------------------------------+------------------------------------------------------------------+ ... | OS-EXT-SRV-ATTR:hypervisor_hostname | comp-node0014.example.cloud | | OS-EXT-SRV-ATTR:instance_name | foocloud01-0000063b | ...
入ったら、 guestmount
というすごい便利そうなコマンドがあって、それでサーバAのブートディスクをマウントできる。
root@comp-node0014:/# instance_name=foocloud01-0000063b ## /var/lib/nova/instances/${instance_name}/disk にあるはず root@comp-node0014:/# mkdir /tmp/${instance_name}-work root@comp-node0014:/# guestmount --ro -a /var/lib/nova/instances/${instance_name}/disk -m /dev/ubuntu-vg/root /tmp/${instance_name}-work
マウントできたら、 chroot
なりなんなりでサーバAのファイルシステムがどうなっていたか確認できる。
ここで、mysqlの既存のデータを持ってくる。
# chroot /tmp/${instance_name}-work root@comp-node0014:/# cd /var/lib root@comp-node0014:/var/lib# tar czvf mysql.tgz mysql/ mysql/ mysql/ibdata1 mysql/ib_logfile1 mysql/auto.cnf ... root@comp-node0014:/var/lib# exit ## 見える箇所にコピー # cp /tmp/${instance_name}-work/var/lib/mysql.tgz ./ ## umount忘れない # umount /tmp/${instance_name}-work
このデータをアップロードした前提で、以下は新サーバでの操作。
## mysqlを止める ubuntu@serverB:~$ sudo systemctl stop mysql ## 持って行ったら /var/lib/mysql を差し替え ubuntu@serverB:~$ sudo mv /var/lib/mysql /var/lib/mysql.bak ubuntu@serverB:~$ tar xzf mysql.tgz ubuntu@serverB:~$ sudo mv ./mysql/ /var/lib/
ただ置き換えただけでは認識しないので、 mysql_upgrade
の実行。
ubuntu@serverB:~$ sudo mysql_upgrade -u root -p Enter password: Looking for 'mysql' as: mysql Looking for 'mysqlcheck' as: mysqlcheck Running 'mysqlcheck with default connection arguments Warning: Using a password on the command line interface can be insecure. Running 'mysqlcheck with default connection arguments Warning: Using a password on the command line interface can be insecure. mysql.columns_priv OK mysql.db OK mysql.event OK ...
sudo
しているのは、 /var/lib/mysql/mysql_upgrade_info
というファイルの読み書きができるユーザでこのコマンドを実施する必要があるため。mysql的なユーザは関係ない。
終わったら、mysqlを立ち上げ、mysqlクライアントからSQLなどでデータの復旧を確認する。
ubuntu@serverB:~$ mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 23 Server version: 5.6.25-0ubuntu0.15.04.1 (Ubuntu) Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show tables; ERROR 1046 (3D000): No database selected mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | foobar | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec)
お疲れ様でした。
話しました
www.slideshare.net
最近とにかくPackerとConsulを触っていたのでそのお話をし、その後、なぜ僕がこのようにYak刈りに突っ込んでいくかのお話をしました、が、当日は50分を若干オーバーし、後半(後半が一番大事なのに)は駆け足となってしまいました......すいません。
50分で180枚目のところぐらいまでは話せたので、今後はこれをベロシティの基準にしたいと思います...(?
正直発表前はそわそわして集中できず、発表後は放心演義といった趣きだったのですが、松本さん、ばりかた勉強会の花田さんのお話などを聞いていました。
懇親会では、おひさしぶりに id:repeatedly さんとお話できたり、 id:kyon_mm さん、 id:daiksy さんといった以前よりお話したいと思っていた方々と初めて話せたり、非常に楽しく過ごせました!
運営の皆様ありがとうございました。福岡でもこんな感じのキレキレなイベントをもっと増やしたいですね。
以前、Qiitaでこういう記事を書いたんですが:
これと同じコンセプトで、最低限の設定で作成後のイメージにServerspecを走らせるためのプラグインを書きました。
インストールは、今のところ自分でビルドする必要があります(もうちょいちゃんとしたらバイナリを配りたい)。
# Needs Go >= 1.3 $ git clone https://github.com/udzura/packer-provisioner-serverspec.git $ cd packer-provisioner-serverspec $ make install
設定はこんな感じで。
{ "builders" : [ { "type": "amazon-ebs", "ssh_username": "centos", "ssh_pty": true, "...": "..." } ], "provisioners": [ { "type": "shell", "script": "/path/to/your-provisoner.sh" }, { "type": "serverspec", "source_path": "/path/to/your/serverspec-root" } ] }
今はα版ということ(にしておく)で、いろいろな条件が固定です。
"ssh_pty": true
でないといけない(Packer 0.8からだそうです。参考)rake spec
でないといけない/path/to/your/serverspec-root ├── Gemfile # Gemfileとlock、必ずしも必要なし ├── Gemfile.lock ├── Rakefile └── spec ├── localhost │ ├── cloud_init_spec.rb │ ├── hosts_spec.rb │ ├── packages_spec.rb │ └── users_spec.rb # and so on... └── spec_helper.rb
Serverspec自体のインストールは omnibus を使っています。 @sawanoboly さんは神です。おかげで、CentOSやUbuntuであればひとまず動きそうです。あ、Ubuntuはちゃんと検証していません......
rake spec
が終わったらserverspecパッケージとアップしたserverspecのディレクトリは綺麗に消します。
そんな感じで、機能はまだ全然足りないですが、お試しください。
こういうPackerの深い話、具体的にはprovisionerだけでなくbuilderまで自作した事例を、 HackerTackleというイベントで話します。ついでに、 Consulの話とかなんか抽象的な話もしちゃいます。
なんと! 明日 なんですが、会場かなり余裕があるので、今から登録しても参加できると思います!
九州に住んでいない人も、今から飛行機を取れば全然間に合います!
という感じで、深い話ができ、なおかつ最前線(自分を棚にあげる)のエンジニアに質問できたり、がっつり議論できる貴重な場だと思います。博多でお待ちしております!
今日はFaradayの話をする。
yao の開発の基本方針は、「必要のない依存をしない」だけれど、Faradayは数少ない依存gemに含まれている。
理由は、
と考えたから。
具体的には、FaradayはRackよろしくMiddlewareで機能追加をするパターンを採用しているため、ちょっとした仕事をさせるためのコードが、短く、わかりやすいものにできる。以下は具体例。
OpenStackのAPI利用の流れは、
という感じになっている。 OpenStack API Quick Start の通りである。
トークンは、 "ac4e1c05084ba4365c1c38bfb1350000"
みたいなMD5ハッシュ風の値で、これをヘッダに X-Auth-Token: "ac4e1c05084ba4365c1c38bfb1350000"
と含めばOK。なお、この認証方式、微妙にOAuth2と違うので既存のMiddlewareを使いまわせない...
今回は以下のようなMiddlewareを作った。
class Faraday::Request::OSToken def initialize(app, token) @app = app @token = token end def call(env) if @token.expired? @token.reflesh(Yao.default_client.default) end env[:request_headers]['X-Auth-Token'] = @token.to_s @app.call(env) end end Faraday::Request.register_middleware os_token: -> { Faraday::Request::OSToken }
ご覧の通り、これはなんというRack Middleware...という感じである。
ポイントは、 @token
自体は普通にTokenオブジェクトなので、expireしたかどうかの情報も所持している。なので、「Tokenがexpireしていたら、ハンドラーの中で勝手に再発行する」といった実装が可能になっているところ。
OpenStackのドキュメントなどに想定されるレスポンスはあるんだけど、実際のものを見た方が理解が早い場合が多い。
こういうレスポンスを保存するやつとしては vcr/vcr · GitHub が有名だけど、リクエストも保存したりなど重量級なのでこんぐらいのコードで対応する。
class Faraday::Response::OSResponseRecorder < Faraday::Response::Middleware def on_complete(env) require 'pathname' root = Pathname.new(File.expand_path('../../../tmp', __FILE__)) path = [env.method.to_s.upcase, env.url.path.gsub('/', '-')].join("-") + ".json" puts root.join(path) File.open(root.join(path), 'w') do |f| f.write env.body end end end Faraday::Response.register_middleware os_response_recorder: -> { Faraday::Response::OSResponseRecorder }
レスポンスのMiddlewareの場合、Rackっぽくなくて、 on_complete(env)
が順次呼ばれ続けるというイメージ。その中でenvに副作用を起こせばいい。
このMiddlewareを有効にすると、通常通りAPIアクセスをしつつ、 PROJECT_ROOT/tmp
以下にそのままレスポンスのJSONが残る。
$ jq . < tmp/POST--v2.0-tokens.json { "access": { "token": { "issued_at": "2015-08-31T03:58:36.073232", "expires": "2015-09-01T03:58:36Z", "id": "31a5166533fd49f3b11b1cdce2000000", "tenant": { "description": "development environment", "enabled": true, "id": "b598bf98671c47e1b955f8c9660e0000", "name": "dev" } }, "serviceCatalog": [ { "endpoints": [ ....
OpenStack APIのエラーの扱いは、原則として 200..299
の範囲外のものであれば、ステータスコードに対応したエラーになる(300系はない。ないよね?)。
なので、アダプター側で共通化できる。
class Faraday::Response::OSErrorDetector < Faraday::Response::Middleware # TODO: Better handling, respecting official doc def on_complete(env) # Faraday::Env の組み込みメソッド # 本来、APIごとに正常なステータスコードの指定があるのだが、後でやる return if env.success? raise Yao::ServerError.detect(env) end end Faraday::Response.register_middleware os_error_detector: -> { Faraday::Response::OSErrorDetector }
module Yao class ServerError < ::StandardError def self.detect(env) case env.status when 400 if env.body && env.body["computeFault"] ComputeFault.new(extract_message(env.body), env) elsif env.body && env.body.to_a.join.include?('NetworkNotFound') NetworkNotFound.new(extract_message(env.body), env) else BadRequest.new(extract_message(env.body), env) end when 401 Unauthorized.new(extract_message(env.body), env) when 404 if env.body && env.body["itemNotFound"] ItemNotFound.new(extract_message(env.body), env) else NotFound.new("The resource could not be found.", env) end when 405 BadMethod.new(extract_message(env.body), env) # ... こんな感じで判定を頑張る end end end end
エラー種類の判定は、まあ頑張ってメタプログラミングしなくていいかと思ってこうしてる。この辺多分最近Go言語を書いてることが強く影響してそう。。
エラーについては、より注意深くハンドリングしたい場合もあるだろうが、その場合はRequest pathとか(というかenv自体)そういう情報も入れてるので、rescueしてそういうのを見るという設計方針で良さそう。
こんな感じでMiddlewareは簡単に書けて助かる。という話でした。
ノリで 0.0.2 をリリースしました。
yao | RubyGems.org | your community gem host
ConoHaのAPI でも動くんじゃないかという気がする。試してないけど。