こんにちは。@jedipunkz です。

以前、“Swift HA 構成を Chef でデプロイ” というタイトルで記事を書きました。

http://jedipunkz.github.io/blog/2013/07/26/swift-ha-chef-deploy/

こちらですが、Swift-Proxy, MySQL, Keystone をそれぞれ haproxy, keepalived で HA 組みました。ですが、これは実用的なのかどうか自分でずっと考えていました。

MySQL と KeepAlived はできればシングル構成にしたいのと、Swift-Proxy は HA で組 みたい。MySQL は Master/Master レプリケーション構成になり、どちらかのノードが 障害を起こし万が一復旧が難しくなった時、構築し直しがしんどくなります。かと言っ て Swift-Proxy をシングル構成にすると今度はノード追加・削除の作業時にサービス 断が発生します。Swift-Proxy を再起動書ける必要があるからです。なので Swift-Proxy は引き続き HA 構成にしたい。

もう一点、見直したいと思っていました。

日経コンピュータから出版されている “仮想化大全 2014” の記事を読んでいて 気がついたのですが。Swift には下記の通りそれぞれのサーバがあります。

  • swift-proxy-server
  • swift-account-server
  • swift-container-server
  • swift-object-server

Swift には下記のような特徴がある事がわかりました。

  • swift-object

swift-object は swift-accout, swift-container とは物理リソースの扱いに全く異な る特性を持っています。swift-account, swift-container はクライアントからのリクエ ストに対して “アカウントの存在を確認”, “ACL 情報の確認” 等を行うサーバであるの に対して swift-object はストレージ上のオブジェクトをクライアントに提供、または 逆に格納するサーバです。よって、Disk I/O の利用特性として swift-account, container は SSD 等、高スループットの Disk を利用するケースが推奨されるのに対 して swift-object はオブジェクトの実体を格納する必要があるため Disk 容量の大き なストレージを要する。

よって今回は下記の構成変更をしてより実用的な構成を Chef でデプロイする方法を記 そうと思います。

  • swift-object を swift-account, swift-container とノードを分離する
  • MySQL/Keystone の HA を行わず Swift-Proxy のみ HA 化する

参考資料

構成

+-----------------+
|  load balancer  |
+-----------------+
|
+-------------------+-------------------+-------------------+-------------------+---------------------- proxy network
|                   |                   |                   |                   |                   
+-----------------+ +-----------------+ +-----------------+ +-----------------+ +-----------------+
|   chef server   | | chef workstation| |   swift-mange   | |  swift-proxy01  | |  swift-proxy02  | 
+-----------------+ +-----------------+ +-----------------+ +-----------------+ +-----------------+ ...> scaling
|                   |                   |                   |                   |                   
+-------------------+-------------------+-------------------+-------------------+-------------------+-- storage network
|                   |                   |                   |                   |                   |
+-----------------+ +-----------------+ +-----------------+ +-----------------+ +-----------------+ +-----------------+ 
| swift-storage01 | | swift-storage02 | | swift-storage03 | | swift-account01 | | swift-account02 | | swift-account03 |
+-----------------+ +-----------------+ +-----------------+ +-----------------+ +-----------------+ +-----------------+ ..> scaling

特徴

  • load balancer は一般的なハードウェア、ソフトウェアで用意 (今回は割愛)
  • swift-proxy は同等の機能のモノを並列に
  • swift-storage と ‘swift-account, container’ はそれぞれの zone 毎に並べるため同数
  • swift-account は swift-account, swift-container が稼働
  • swift-manage は mysql, keystone, git server を搭載
  • chef server は外部に配置しても構わないが到達出来る箇所に
  • chef workstation は全ての操作を行う端末。全てのノードに到達出来る箇所に

それぞれのノードでの disk の準備

object, account, container 共に OS 領域とは別の disk を必要とするため /dev/sdb 等のディスクを追加し下記の通り gdisk でパーティションを切る。

% sudo apt-get update; sudo apt-get -y install gdisk
% sudo gdisk /dev/sdb # デバイス名は例

対象ノード : swift-object0[1-3], swift-account0[1-3]

デプロイ準備

下記は全て chef-workstation での操作

rcpops 管理の chef-repo を取得する。

% git clone https://github.com/rcbops/chef-cookbooks.git
% cd chef-cookbooks

現時点で v4.1.2 がリリースされているので tag を利用して checkout する。

% git tag -l
1.0.0
release-2011.3-d5
release-v2.0
v2.9.0
v2.9.1
v2.9.2
v2.9.3
v2.9.4
v2.9.5
v2.9.6
v2.9.7
v2.9.8
v3.0.0
v3.0.1
v3.1.0
v4.1.0
v4.1.1
v4.1.2
% git checkout -b v4.1.2 refs/tags/v4.1.2

git submodule 化されている cookbooks を取得する。

% git submodule init
% git submodule sync
% git submodule upate

cookbooks, roles の chef-server へのアップロードを行う。

% knife cookbook upload -o cookbook -a
% knife role from file role/*.rb

今回の構成一式を収容する chef environment を作成する。

{
  "name": "swift",
  "description": "",
  "cookbook_versions": {
  },
  "json_class": "Chef::Environment",
  "chef_type": "environment",
  "default_attributes": {
  },
  "override_attributes": {
    "package_component": "grizzly",
    "osops_networks": {
      "management": "10.200.9.0/24",
      "public": "10.200.9.0/24",
      "nova": "10.200.9.0/24",
      "swift": "10.200.9.0/24"
    },
    "keystone": {
      "admin_user": "admin",
      "tenants": [
        "admin",
        "service"
      ],
      "users": {
        "admin": {
          "password": "secrete",
          "roles": {
            "admin": [
              "admin"
            ]
          }
        },
        "demo": {
          "password": "demo",
          "default_tenant" : "service",
          "roles": {
            "admin": [ "admin" ]
          }
        }
      },
      "db": {
        "password": "keystone"
      }
    },
    "mysql": {
      "root_network_acl": "%",
      "allow_remote_root": true,
      "server_root_password": "secrete",
      "server_repl_password": "secrete",
      "server_debian_password": "secrete"
    },
    "monitoring": {
      "procmon_provider": "monit",
      "metric_provider": "collectd"
    },
    "vips": {
      "keystone-admin-api": "10.200.9.112",
      "keystone-service-api": "10.200.9.112",
      "keystone-internal-api": "10.200.9.112",
      "swift-proxy": "10.200.9.112",
      "config": {
        "10.200.9.112": {
          "vrid": 12,
          "network": "management"
        }
      }
    },
    "developer_mode": false,
    "swift": {
      "swift_hash": "107c0568ea84",
      "authmode": "keystone",
      "authkey": "3f281b71-ce89-4b27-a2ad-ad873d3f2760"
    }
  }
}

作成した environment ファイル environments/swift.json を chef-server へアップ ロードする。

% knife environment from file environments/swift.json

デプロイ

かきのとおり knife bootstrap する。

% knife bootstrap <manage_ip_addr> -N swift-manage -r 'role[base]','role[mysql-master]','role[keystone]','role[swift-management-server]' -E swift --sudo -x thirai
% knife bootstrap <proxy01_ip_addr> -N swift-proxy01 -r "role[base]","role[swift-proxy-server]",'role[swift-setup]','role[openstack-ha]' -E swift --sudo -x thirai
% knife bootstrap <proxy02_ip_addr> -N swift-proxy02 -r "role[base]","role[swift-proxy-server]",'role[openstack-ha]' -E swift --sudo -x thirai
% knife bootstrap <storage01_ip_addr> -N swift-storage01 -r 'role[base]','role[swift-object-server]' -E swift --sudo -x thirai
% knife bootstrap <storage02_ip_addr> -N swift-storage02 -r 'role[base]','role[swift-object-server]' -E swift --sudo -x thirai
% knife bootstrap <storage03_ip_addr> -N swift-storage03 -r 'role[base]','role[swift-object-server]' -E swift --sudo -x thirai
% knife bootstrap <account01_ip_addr> -N swift-account01 -r 'role[base]','role[swift-account-server]','role[swift-container-server]' -E swift --sudo -x thirai
% knife bootstrap <account02_ip_addr> -N swift-account02 -r 'role[base]','role[swift-account-server]','role[swift-container-server]' -E swift --sudo -x thirai
% knife bootstrap <account03_ip_addr> -N swift-account03 -r 'role[base]','role[swift-account-server]','role[swift-container-server]' -E swift --sudo -x thirai

zone 番号を付与する。

% knife exec -E "nodes.find(:name => 'swift-storage01') {|n| n.set['swift']['zone'] = '1'; n.save }"
% knife exec -E "nodes.find(:name => 'swift-account01') {|n| n.set['swift']['zone'] = '1'; n.save }"
% knife exec -E "nodes.find(:name => 'swift-storage02') {|n| n.set['swift']['zone'] = '2'; n.save }"
% knife exec -E "nodes.find(:name => 'swift-account02') {|n| n.set['swift']['zone'] = '2'; n.save }"
% knife exec -E "nodes.find(:name => 'swift-storage03') {|n| n.set['swift']['zone'] = '3'; n.save }"
% knife exec -E "nodes.find(:name => 'swift-account03') {|n| n.set['swift']['zone'] = '3'; n.save }"

zone 番号が付与されたことを確認する。

account-server の確認

% knife exec -E 'search(:node,"role:swift-account-server") \
  { |n| z=n[:swift][:zone]||"not defined"; puts "#{n.name} has the role \
  [swift-account-server] and is in swift zone #{z}"; }'
swift-account01 has the role       [swift-account-server] and is in swift zone 1
swift-account02 has the role       [swift-account-server] and is in swift zone 2
swift-account03 has the role       [swift-account-server] and is in swift zone 3

container-server の確認

% knife exec -E 'search(:node,"role:swift-container-server") \
  { |n| z=n[:swift][:zone]||"not defined"; puts "#{n.name} has the role \
  [swift-container-server] and is in swift zone #{z}"; }'
swift-account01 has the role       [swift-container-server] and is in swift zone 1
swift-account02 has the role       [swift-container-server] and is in swift zone 2
swift-account03 has the role       [swift-container-server] and is in swift zone 3

object-server の確認

% knife exec -E 'search(:node,"role:swift-object-server") \
  { |n| z=n[:swift][:zone]||"not defined"; puts "#{n.name} has the role \
  [swift-object-server] and is in swift zone #{z}"; }'
swift-storage01 has the role   [swift-object-server] and is in swift zone 1
swift-storage02 has the role   [swift-object-server] and is in swift zone 2
swift-storage03 has the role   [swift-object-server] and is in swift zone 3

Chef が各々のノードに搭載された Disk を検知出来るか否かを確認する。

% knife exec -E \
  'search(:node,"role:swift-object-server OR \
  role:swift-account-server \
  OR role:swift-container-server") \
  { |n| puts "#{n.name}"; \
  begin; n[:swift][:state][:devs].each do |d| \
  puts "\tdevice #{d[1]["device"]}"; \
  end; rescue; puts \
  "no candidate drives found"; end; }'
    swift-storage02
            device sdb1
    swift-storage03
            device sdb1
    swift-account01
            device sdb1
    swift-account02
            device sdb1
    swift-account03
            device sdb1
    swift-storage01
            device sdb1

swift-manage ノードにて chef-client を実行し /etc/swift/ring-workspace/generate-rings.sh を更新する。

swift-manage% sudo chef-client

generate-rings.sh の ‘exit 0’ 行をコメントアウトし実行

swift-manage% sudo ${EDITOR} /etc/swift/ring-workspace/generage-rings.sh
swift-manage% sudo /etc/swift/ring-workspace/generate-rings.sh

この操作で /etc/swift/ring-workspace/rings 配下に account, container, object 用の Rings ファイル群が生成されたことを確認出来るはずである。これらを swift-manage 上で既に稼働している git サーバに push し管理する。

swift-manage# cd /etc/swift/ring-workspace/rings
swift-manage# git add account.builder container.builder object.builder
swift-manage# git add account.ring.gz container.ring.gz object.ring.gz
swift-manage# git commit -m "initial commit"
swift-manage# git push

各々のノードにて chef-client を実行することで git サーバ上の Rings ファイル群 を取得し、swift プロセスを稼働させる。

swift-proxy01# chef-client
swift-proxy02# chef-client
swift-storage01# chef-client
swift-storage02# chef-client
swift-storage03# chef-client
swift-account01# chef-client
swift-account02# chef-client
swift-account03# chef-client

3台のノードが登録されたかどうかを下記の通り確認行う。

swift-proxy01% sudo swift-recon --md5
[sudo] password for thirai:
===============================================================================
--> Starting reconnaissance on 3 hosts
===============================================================================
[2013-10-18 11:14:43] Checking ring md5sums
3/3 hosts matched, 0 error[s] while checking hosts.
===============================================================================

動作確認

swift-storage01# source swift-openrc
swift-storage01# swift post container01
swift-storage01# echo "test" > test
swift-storage01# swift upload container01 test
swift-storage01# swift list
swift-storage01# swift list container01

ノードの追加方法

次にノードの追加方法を記します。

gdisk にて /dev/sdb1 等のパーティションを作成する。

下記の通り knife bootstrap する。

% knife bootstrap <storage04_ip_addr> -N swift-storage04 -r 'role[base]','role[swift-object-server]' -E swift --sudo -x thirai
% knife bootstrap <account04_ip_addr> -N swift-account04 -r 'role[base]','role[swift-account-server]','role[swift-container-server]' -E swift --sudo -x thirai

zone 番号を付与する。

% knife exec -E "nodes.find(:name => 'swift-storage04') {|n| n.set['swift']['zone'] = '4'; n.save }"
% knife exec -E "nodes.find(:name => 'swift-account04') {|n| n.set['swift']['zone'] = '4'; n.save }"

zone 番号が付与されたことを確認する。

account-server の確認

% knife exec -E 'search(:node,"role:swift-account-server") \
  { |n| z=n[:swift][:zone]||"not defined"; puts "#{n.name} has the role \
  [swift-account-server] and is in swift zone #{z}"; }'
swift-account01 has the role       [swift-account-server] and is in swift zone 1
swift-account02 has the role       [swift-account-server] and is in swift zone 2
swift-account03 has the role       [swift-account-server] and is in swift zone 3
swift-account04 has the role       [swift-account-server] and is in swift zone 4

container-server の確認

% knife exec -E 'search(:node,"role:swift-container-server") \
  { |n| z=n[:swift][:zone]||"not defined"; puts "#{n.name} has the role \
  [swift-container-server] and is in swift zone #{z}"; }'
swift-account01 has the role       [swift-container-server] and is in swift zone 1
swift-account02 has the role       [swift-container-server] and is in swift zone 2
swift-account03 has the role       [swift-container-server] and is in swift zone 3
swift-account04 has the role       [swift-container-server] and is in swift zone 4

object-server の確認

% knife exec -E 'search(:node,"role:swift-object-server") \
  { |n| z=n[:swift][:zone]||"not defined"; puts "#{n.name} has the role \
  [swift-object-server] and is in swift zone #{z}"; }'
swift-storage01 has the role   [swift-object-server] and is in swift zone 1
swift-storage02 has the role   [swift-object-server] and is in swift zone 2
swift-storage03 has the role   [swift-object-server] and is in swift zone 3
swift-storage04 has the role   [swift-object-server] and is in swift zone 4

swift-manage ノードにて chef-client を実行し /etc/swift/ring-workspace/generate-rings.sh を更新する。

swift-manage% sudo chef-client

generate-rings.sh の ‘exit 0’ 行をコメントアウトし実行

swift-manage% sudo ${EDITOR} /etc/swift/ring-workspace/generage-rings.sh
swift-manage% sudo /etc/swift/ring-workspace/generate-rings.sh

この操作で /etc/swift/ring-workspace/rings 配下に account, container, object 用の Rings ファイル群が生成されたことを確認出来るはずである。これらを swift-manage 上で既に稼働している git サーバに push し管理する。

swift-manage# cd /etc/swift/ring-workspace/rings
swift-manage# git add account.builder container.builder object.builder
swift-manage# git add account.ring.gz container.ring.gz object.ring.gz
swift-manage# git commit -m "added zone 4 nodes"
swift-manage# git push

各々のノードにて chef-client を実行することで git サーバ上の Rings ファイル群 を取得し、swift プロセスを稼働させる。

swift-proxy01# chef-client
swift-proxy02# chef-client
swift-storage01# chef-client
swift-storage02# chef-client
swift-storage03# chef-client
swift-storage04# chef-client
swift-account01# chef-client
swift-account02# chef-client
swift-account03# chef-client
swift-account04# chef-client

4台のノードが登録されたかどうかを下記の通り確認行う。

swift-proxy01% sudo swift-recon --md5
[sudo] password for thirai:
===============================================================================
--> Starting reconnaissance on 4 hosts
===============================================================================
[2013-10-18 11:14:43] Checking ring md5sums
4/4 hosts matched, 0 error[s] while checking hosts.
===============================================================================

proxy ノードの swift プロセスを停止・起動する

swift-proxy01# swift-init all stop
swift-proxy01# swift-init all start
swift-proxy02# swift-init all stop
swift-proxy02# swift-init all start

ノード削除方法

次にノードの削除方法を記します。

disk 障害等のzone4 のノード swift-storage04, swift-account04 を削除する前提で記す。

swift-manage# swift-ring-builder /etc/swift/ring-workspace/rings/account.builder remove 10.200.9.109
swift-manage# swift-ring-builder /etc/swift/ring-workspace/rings/container.builder remove 10.200.9.109
swift-manage# swift-ring-builder /etc/swift/ring-workspace/rings/object.builder remove 10.200.9.110

swift-manage 上で既に稼働している git サーバに push し管理する。

swift-manage# cd /etc/swift/ring-workspace/rings
swift-manage# git add account.builder container.builder object.builder
swift-manage# git add account.ring.gz container.ring.gz object.ring.gz
swift-manage# git commit -m "added zone 4 nodes"
swift-manage# git push

全てのノードで chef-client を実行する。この操作で rings ファイルの配布が行える。

swift-proxy01# chef-client
swift-proxy02# chef-client
swift-storage01# chef-client
swift-storage02# chef-client
swift-storage03# chef-client
swift-storage04# chef-client
swift-account01# chef-client
swift-account02# chef-client
swift-account03# chef-client
swift-account04# chef-client

proxy ノードの swift プロセスを停止・起動する

swift-proxy01# swift-init all stop
swift-proxy01# swift-init all start
swift-proxy02# swift-init all stop
swift-proxy02# swift-init all start

まとめと考察

MySQL のリカバリは単純にダンプからのリストアが良かったのでシングル構成に出来て よかった。また、通常のシングルスター・マルチスレーブの構成に変えても OK かと思 いますが、Cookbooks を修正する必要がありそう。まぁ Keystone のデータのみを管理 している MySQL なので負荷は無いですしシングル構成が良いと個人的には思います。 また Swift-Proxy は 2 台構成の HA になります。VRRP プロトコルの仕組み、また haproxy の構成上 3 台以上でも OK でありますが、Cookbooks 的に NG でした。これ は修正する価値が大いにありそう。

またこの構成を Chef で管理し始めると大量のノードで Chef-Client を実行すること になるので Gnu Prallel や pssh を使った方が良さそう…。