Skip to content

Instantly share code, notes, and snippets.

@yssk22
Created December 14, 2011 10:41
Show Gist options
  • Save yssk22/2476675e166184c541a1 to your computer and use it in GitHub Desktop.
Save yssk22/2476675e166184c541a1 to your computer and use it in GitHub Desktop.
purge_orphan について淡々と

purge_orphan について淡々と

これは何?

サービスの orphan(孤児) を purge (処分) してくれるツール

孤児って何?

まずは構成について再確認。

CloudController <-> NATS <-> Service Gateway <-> Service Node

Gateway は、自分自身がNodeにリクエストしたサービスのプロセス(mongod とか redis とか)を sqlite3 のデータベースで管理している。

$ ls -al
total 20
drwxr-xr-x 4 vcap vcap 4096 2011-12-13 21:58 .
drwxr-xr-x 4 vcap vcap 4096 2011-12-05 16:47 ..
drwxr-xr-x 3 vcap vcap 4096 2011-12-06 17:49 instances
drwxr-xr-x 3 vcap vcap 4096 2011-12-06 17:49 logs
-rw-r--r-- 1 vcap vcap 4096 2011-12-13 21:58 mongodb_node.db
$ ls -al instances/
total 12
drwxr-xr-x 3 vcap vcap 4096 2011-12-06 17:49 .
drwxr-xr-x 4 vcap vcap 4096 2011-12-13 21:58 ..
drwxr-xr-x 3 vcap vcap 4096 2011-12-13 21:58 8791ad25-f445-4769-977b-c60518a14788

$ sqlite3 mongodb_node.db
sqlite> .schema
CREATE TABLE "vcap_services_mongo_db_node_provisioned_services" ("name" VARCHAR(50) NOT NULL, "port" INTEGER, "password" VARCHAR(50) NOT NULL, "plan" INTEGER NOT NULL, "pid" INTEGER, "memory" INTEGER, "admin" VARCHAR(50) NOT NULL, "adminpass" VARCHAR(50) NOT NULL, "db" VARCHAR(50) NOT NULL, PRIMARY KEY("name"));
CREATE UNIQUE INDEX "unique_vcap_services_mongo_db_node_provisioned_services_port" ON "vcap_services_mongo_db_node_provisioned_services" ("port");
sqlite> select * from vcap_services_mongo_db_node_provisioned_services;
8791ad25-f445-4769-977b-c60518a14788|25001|eacb0e8c-7282-4e4c-8790-be29460703cd|1|8888|2048|admin|37b3cd6a-e291-4f36-af88-7d916d3feec4|db

Service Gateway は、sqlite3のDBに存在するけれども、インスタンスとしてとして起動していないprovisioned serviceに対しては、とにかくインスタンスの起動を試みる。このsqlite3のDBが、CloudControllerのDBと一致しない状態になったとき、具体的には、「sqlite3のDBには存在するけれど、CloudControllerのDBには存在しない」 provisioned service 情報が存在すると、"CloudControllerの管理対象化にないがインスタンスとしては存在する" = 孤児 のインスタンスが生まれ得る(ここでいうインスタンスは、プロセスであったりデータベースであったりとサービスによって様々)。

あるいはこの逆で、「sqlite3のDBには存在しないけれど、CloudControllerのDBには存在する」 = 孤児のバインディングが生まれ得る。

どんなときに孤児が生まれるの?

  • Service Gateway と NATS が切り離されたとき、(Cloud Controller はService Nodeの操作までを create-service, delete-service のトランザクションスコープにいれないので)
  • Redis (これはバグっぽい:後述)
  • ...etc

孤児を処分する

$ cd VCAP_HOME/services/tools/purge_orphan
$ ruby bin/purge_orphan.rb -h
Usage: purge_orphan.rb [options] <orphan_file>
-c, --config [ARG] Configuration File -k, --check Request to check orphan -h, --help Help

-k は orphan の情報をチェックする時につかう。Service Gateway のステータスサーバーが orphan 情報をステータスとして保持しているので、それを強制アップデートする際につかう。

-c は Service Gateway のHTTPサーバー(!= ステータスサーバー) の情報などを記述する設定ファイル。

余談: ステータスサーバー?

超重要。Cloud Foundry の各ノードで動く、ステータス情報をかえすHTTP Daemon(RubyのThinでかかれている) ステータスサーバーのBasic認証用のユーザー名、パスワード および ポートは通常ランダムに決められて、NATSを通じて知ることができる。この辺の詳細は VCAP::Component#register() のソース参照。

こんな感じのスクリプトで取得できる。

$ cat status_info.rb
require 'rubygems'
require 'nats/client'

NATS.start do
   NATS.request('vcap.component.discover') do |msg|
      puts msg
  end
end
{"type":"MongoaaS-Provisioner","index":169658655,"uuid":"169658655-2e3e584580abf6aa51c9e84acf49ff7d","host":"192.168.1.31:10007","credentials":["10f05b73741bc0bffaba02b68f43064e","passw0rd"],"start":"2011-12-13 21:58:44 +0900","uptime":"0d:21h:23m:28s"}

あるいは、Cloud Controller などであれば

status:
   user: thin
   password: thin
   port: 12345

のような設定をymlに書いておけば http://thin:thin@localhost:12345/varz とかで取得できる。

Service Gateway はそれできないの? → なぜかできないorz → パッチ: cloudfoundry-attic/vcap-services#13

ステータスサーバーから値をとる

$ curl http://{user}:{pass}@localhost:{port}/varz
{
  "type": "MongoaaS-Provisioner",
  "start": "2011-12-13 21:58:44 +0900",
  "num_cores": 2,
  "nodes": {
    "mongodb_node_169658655": 1323858367,
    "mongodb_node_169658656": 1323858370
  },
  "prov_svcs": {
    "8791ad25-f445-4769-977b-c60518a14788": {
      "service_id": "8791ad25-f445-4769-977b-c60518a14788"
    },
  }
  "orphan_instances": {
     // ここに 孤児のインスタンス情報がはいる
  },
  "orphan_bindings": {
     // ここに 孤児のバインディング情報がはいる
  },
}

purge_orphan -k

上記ステータス情報を更新するときにつかう。

purge_orphan varz.json

$ curl http://{user}:{pass}@localhost:{port}/varz > varz.json
$ purge_orphan varz.json

これで orphan_instances とか orphan_bindings を綺麗に掃除してくれる。

もう一歩進んで

_gateway.yml に check_orphan_interval という設定ができるので purge_orphan -k は自動でできる。

purge_orphan は自動でやる? 正常なトランザクションでも orphan_bindings が生まれ得るであろうから(ソース未確認)、インスタンスの生成速度と、purge_orphan の実行インターバルとの兼ね合い。Gatewayでは orphan のダブルチェックをしており、double_check_orphan_interval というパラメーターでダブルチェックの間隔も制御できる。

最後にRedis

Service Gateway は、sqlite3のDBに存在するけれども、インスタンスとしてとして起動していないprovisioned serviceに対しては、とにかくインスタンスの起動を試みる

「インスタンスとして起動しているかどうか」は、各Serviceの実装による。プロセスであれば ps だし、データベースであれば管理用テーブルへのSELECTだし.... ということで、 base/lib/base.rb には skeleton だけある。

# Subclass must overwrite this method to enable check orphan instance feature.
# Otherwise it will not check orphan instance
# The return value should be a list of instance name(handle["service_id"]).
def all_instances_list
   []
end

# Subclass must overwrite this method to enable check orphan binding feature.
# Otherwise it will not check orphan bindings
# The return value should be a list of binding credentials
# Binding credential will be the argument for unbind method
# And it should have at least username & name property for base code
# to find the orphans
def all_bindings_list
  []
end

さてRedis。

$ grep -R 'all_' .
$
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment