OpenStack Swiftを使ってクラウドストレージサービスを構築する

OpenStack Swift

クラウドインフラストラクチャ構築ソフトウェアであるOpenStackには、クラウドストレージシステムを提供する「Swift」というコンポーネントが含まれている。Swiftを利用することで、自動レプリケーションや分散化といった機能を持つクラウドストレージサービスを構築することが可能だ。本記事ではこのSwiftが持つ機能を紹介するとともに、Swiftによるストレージサービスの構築手順を解説する。

クラウドストレージを自前で構築できる「Swift」

「クラウド」という言葉は幅広いジャンルで使われているが、この名前を冠したものの1つに「クラウドストレージ」がある。クラウドストレージというのは、その名前が示すとおり、インターネット経由でアクセスできるデータの保存領域を提供するものだ。クラウドストレージでは保存するデータを「オブジェクト」と呼ぶことから、「オブジェクトストレージ(Object Storage)」などと呼ばれることもある。

クラウドストレージが一般的なオンラインストレージサービスやNAS/SANと異なるのは、HTTPベースのREST方式でデータがやり取りされるという点と、格納したデータは複製されて複数のストレージサーバーに配置され、たとえストレージサーバーの一部が停止したとしてもデータが保護されるという点だ。高価な専用ハードウェアが不要で、一般的なサーバーとストレージの組み合わせだけで大容量かつ安全性の高いストレージサービスを構築できるというメリットもある。

このようなクラウドストレージでもっとも有名なのが、Amazon.comが提供する「Amazon Web Services」の1つである「Amazon Simple Storage Service(Amazon S3)」だろう。Amazon S3は後続のサービスに大きな影響を与え、Amazon S3互換のAPIを提供しているクラウドストレージサービスは多い。また、Googleの「Google Cloud Storage」やMicrosoftの「Windows Azure」といったサービスも同様のクラウドストレージサービスを提供している。

このようなクラウドストレージサービスを独自に構築するためのソフトウェアが、今回紹介する「Swift」である。Swiftは現在クラウドインフラストラクチャを構築するためのソフトウェア開発プロジェクト「OpenStack」の一部として開発が進められている。このようなストレージシステムについては安定性が重要であるが、Swiftは既存技術を組み合わせた構造になっており、また米国のホスティングサービス事業者Rackspaceによる商用サービス「Cloud Files」で使われていた技術をベースとしている。大規模なトラブルなどは今のところ報告されておらず、すでに十分実用段階にあるソフトウェアであると言える。

Swiftの構成

Swiftではストレージや認証、管理といった機能がそれぞれ異なるサービスとして実装されている(表1)。また、これに加えてユーザー認証を行うための認証サービスが必要だ。

表1 Swiftを構成するサービス
サービス名 説明
Proxy ストレージにアクセスするためのAPIの提供や各サービスの管理を行う
Object オブジェクトの管理を行う
Container コンテナの管理を行う
Account アカウントの管理を行う

SwiftはOpenStackプロジェクトの一部として開発が進められているものの、ほかのコンポーネントとの関連性は低い。認証にはユーザー認証機能を提供するOpenStackの「Keystone」コンポーネントを使用できるが、基本的にそれ以外のコンポーネントへの依存性はなく、単独での環境構築が可能だ。

また、これらのサービスを実行するホストは「ノード」と呼ばれる。一般的な構成としては、Proxyサービスを実行させる「プロクシノード」と、ObjectサービスおよびContainerサービス、Accountサービスを実行させる「ストレージノード」、そしてkeystoneを実行させる「認証ノード」を複数用意することが多い(図1)。

図1 Swiftの一般的な構成例
図1 Swiftの一般的な構成例

 すべてのサービスを1つのノード上で稼働させたり、認証ノードおよびプロクシノードとストレージノードそれぞれ1台のみを利用するといったシンプルな構成でもサービスの実現は可能だが、この場合オブジェクトの分散化は行われず、冗長性が確保されない。そのため、実運用環境においては最低でも5台のストレージノードを利用するのが推奨される。また、ストレージノードではオブジェクトの管理にファイルシステムが持つメタデータ機能を利用する。そのため、通常オブジェクト保存用のストレージには単独のパーティションもしくはストレージを用意し、そのファイルシステムにはXFSを使用する。Linuxで一般的なext3やext4はメタデータ機能をサポートしていないため、オブジェクト格納用のストレージとしては利用できない点には注意したい。

なお、サービスを利用するクライアントはProxyサービスに向けてリクエストを送信し、ストレージノードに対するデータの読み書きはProxyサービスが実行する。そのため、ストレージノードは通常プロクシノードからのみアクセスできるプライベートネットワークに接続される。

Swiftを使ったクラウドストレージサービスを構築する

それでは、Swiftを使って実際にクラウドストレージサービスを構築する手順について解説していこう。以下ではサーバーとして「さくらの専用サーバ」サービスで提供される専用サーバーを使用する。これらのネットワーク構成は図2のとおりとなっている。

図2 今回使用するネットワーク構成
図2 今回使用するネットワーク構成

 OS環境としてはRet Hat Enterprise Linux(EPEL) 6.3互換のCentOS 6.3(64ビット版)を使用している。CentOS 6.3のパッケージリポジトリにはOpenStack関連のパッケージが含まれていないため、SwiftのインストールにはFedoraプロジェクトが公開しているパッケージリポジトリ「EPEL(Extra Packages for Enterprise Linux)」に含まれるパッケージを利用する。

また、今回は認証サービスとしてOpenStackのKeystoneを使用する。Keystoneのインストールや設定については「さくらの専用サーバとOpenStackで作るプライベートクラウド」記事で詳しく解説しているので、そちらを参照してほしい。

keystoneへの登録

SwiftではKeystoneを使ってユーザー認証を行うため、KeystoneのデータベースにSwift関連の設定を追加しておく必要がある。まずはKeystoneサービスを稼働させているホスト上で以下のスクリプトを実行してアカウントを追加しておく。

export SERVICE_TOKEN=<keystoneで設定したアクセストークン>
export SERVICE_ENDPOINT=http://localhost:35357/v2.0/

SWIFT_PROXY_HOST_PUB=<プロクシノードのパブリックネットワーク側ホスト名>
SWIFT_PROXY_HOST_INT=<プロクシノードのプライベートネットワーク側ホスト名>
TENANT_NAME=<サービス用のテナント名>
PASSWORD=<swift用ユーザーのパスワード>

# 管理者ロールとテナントIDを取得
ADMIN_ROLE=$(keystone role-list | awk '/admin/ {print $2}')
TENANT_ID=$(keystone tenant-list | awk "/$TENANT_NAME/ {print \$2}")

# ユーザーの作成
keystone user-create --tenant-id $TENANT_ID --name swift --pass $PASSWORD
SWIFT_USER=$(keystone user-list | awk '{/swift/ print $2}')
keystone user-role-add --user-id $SWIFT_USER --tenant-id $TENANT_ID --role-id $ADMIN_ROLE

# サービスの登録
keystone service-create --name=swift --type=object-store --description="Swift Object Storage"
SWIFT_ID=$(keystone service-list | grep swift | awk '{print $2}')
keystone endpoint-create --region RegionOne --service_id $SWIFT_ID --publicurl "http://$SWIFT_PROXY_HOST_PUB:8080/v1/AUTH_%(tenant_id)s" --adminurl "http://$SWIFT_PROXY_HOST_INT:8080/v1" --internalurl "http://$SWIFT_PROXY_HOST_INT:8080/v1/AUTH_%(tenant_id)s"

このスクリプトではSwift用のユーザーとして「swift」という名前のユーザーを作成し、パスワードとしてPASSWORD変数で指定したものを設定する。また、サービスやエンドポイントの登録も実行している。

プロクシノードの準備

では、ストレージノードの構築手順について説明していこう。

パッケージのインストール

まず、Proxyサービスを提供するopenstack-swift-proxyパッケージと、Proxyサービスが利用するmemcachedパッケージ、そしてKeystoneにアクセスするためのライブラリであるpython-keystoneパッケージをyumコマンドでインストールする。

# yum --enablerepo=epel install openstack-swift-proxy memcached python-keystone

Memcachedの設定

続いて、一時的なデータの保存に利用するMemcachedの設定を行っておく。Memcachedの設定は/etc/sysconfig/memcachedファイルに記述する。基本的にはデフォルトのままでOKだが、アクセスを許可する接続元を指定する「-l」オプションの追加だけ行っておこう。

PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS="-l <プロクシノード自身のIPアドレス>"

ファイルの編集後、以下のようにしてMemcachedを起動しておく。

# service memcached start

Proxyサービスの設定

Swiftでは各サービスごとに設定ファイルが分けられており、また各サービスで共通する設定項目は/etc/swift/swift.confというファイルに記述されている。今回/etc/swift/swift.confで設定が必要なのは、「swift_hash_path_suffix」項目だけだ。ここには適当な文字列を指定すれば良い。ただし、すべてのSwiftノードで同じ値が指定されている必要がある。たとえば「SourceForge20130401」という文字列を設定する場合は以下のようになる。

[swift-hash]
swift_hash_path_suffix = SourceForge20130401

また、Proxyサービスの設定は/etc/swift/proxy-server.confファイルに記述されている。基本的にはデフォルトのままで大丈夫だが、Memcachedが稼働しているホストを指定する「memcache_server」や、keystoneの認証に使用する「admin_tenant_name」および「admin_user」、「admin_password」、「auth_host」項目については以下のように適切なものに変更しておく。

[DEFAULT]
bind_port = 8080
workers = 8
user = swift
cert_file=/etc/swift/cert.crt
key_file=/etc/swift/cert.key

[pipeline:main]
pipeline = healthcheck cache authtoken keystone proxy-server

[app:proxy-server]
use = egg:swift#proxy
allow_account_management = true
account_autocreate = true

[filter:cache]
use = egg:swift#memcache
memcache_servers = <プロクシサーバーのIPアドレス>:11211

[filter:catch_errors]
use = egg:swift#catch_errors

[filter:healthcheck]
use = egg:swift#healthcheck

[filter:keystone]
paste.filter_factory = keystone.middleware.swift_auth:filter_factory
operator_roles = admin, SwiftOperator
is_admin = true
cache = swift.cache

[filter:authtoken]
paste.filter_factory = keystone.middleware.auth_token:filter_factory
admin_tenant_name = <管理用テナント名>
admin_user = <swiftユーザー名>
admin_password = <swiftユーザーのパスワード>
auth_host = <keystoneが稼働しているホスト名>
auth_port = 35357
auth_protocol = http
signing_dir = /tmp/keystone-signing-swift

ringの作成

Swiftでは、どのホストがどのサービスを提供しているのか、またオブジェクトをどのように分散配置するのか、といった設定情報を「ring」と呼ぶ。ringはAccountサービスおよびContainerサービス、Objectサービスのそれぞれに必要で、swift-ring-builderコマンドでその作成や設定を行う。

まず、「swift-ring-builder <ビルダー名> create」コマンドでring情報が含まれたファイルを作成する。<ビルダー名>には「account.builder」および「container.builder」、「object.builder」のうち対応するものを指定する。

swift-ring-builder <ビルダー名> create <part_power> <replicas> <min_part_hours>

ここでpart_powerは利用するパーティション数を決定するためのパラメータ(2のpart_power乗個のパーティションが利用される)を、replicasは1つのオブジェクトに対し保持する複製の数を、min_part_hoursはパーティションの複数回の移動を制限する時間を指定する。

たとえば、2の18乗のパーティションを利用し、1つのデータに対し3つの複製を作成、データがパーティションを移動した場合にその後1時間はパーティションの移動を制限する、といった設定の場合、以下のようにコマンドを実行すればよい。

# swift-ring-builder account.builder create 18 3 1
# swift-ring-builder container.builder create 18 3 1
# swift-ring-builder object.builder create 18 3 1

この場合、1つのデータは3つの異なるストレージノードに複製されて格納される。そのため、3台以上のストレージノードが必要となる。

続いて、「swift-ring-builder <ビルダー名> add」コマンドでringにサービス情報を追加する。

swift-ring-builder <ビルダー名> add z<zone>-<ip>:<port>/<device_name>_<meta> <weight>

ここでzoneにはゾーン番号、ipにはIPアドレス、portにはポート番号、device_nameにはデバイス名、metaにはメタデータ名、weightには重みを指定する。メタデータ名は省略可能だ。

たとえばゾーン番号が0でストレージノードのIPアドレスが「192.168.100.21」、使用するストレージデバイス名がsda5、重みを100とする場合、以下のようになる。

# swift-ring-builder account.builder add z0-192.168.100.21:6002/sda5 100
# swift-ring-builder container.builder add z0-192.168.100.21:6001/sda5 100
# swift-ring-builder object.builder add z0-192.168.100.21:6000/sda5 100

使用するストレージノードごとにこれらのコマンドを実行し、ringにノードを登録しておこう。なお、登録された情報は「swift-ring-builder <ビルダー名>」コマンドで確認できる。

# swift-ring-builder account.builder
account.builder, build version 1
262144 partitions, 3 replicas, 1 zones, 1 devices, 100.00 balance
The minimum number of hours before a partition can be reassigned is 1
Devices: id zone ip address port name weight partitions balance meta
0 0 192.168.100.21 6002 sda5 100.00 0 -100.00
:
:
:

もしここで誤った値が登録されていた場合、再度swift-ring-builder craeteコマンドを実行してringを初期化するか、swift-ring-builder removeコマンドで指定したデバイスを削除して再度登録を行う。

ノードの登録が完了したら、最後に「swift-ring-builder <ビルダー名> rebalance」コマンドを実行して設定を反映させる。

# swift-ring-builder account.builder rebalance
# swift-ring-builder container.builder rebalance
# swift-ring-builder object.builder rebalance

すると、/etc/swift以下にring情報を格納した「container.ring.gz」および「object.ring.gz」、「account.ring.gz」というファイルが作成される。

Proxyサービスの起動

以上の設定が完了したら、swift-initコマンドでProxyサービスを起動させる。swift-initコマンドは次のような引数を取る。

swift-init <サーバー名> <操作>

<操作>には「start」や「stop」、「restart」、「status」などが指定可能だ。たとえばproxyサービスを起動するには、以下のようにする。

# swift-init proxy start

なお、Swiftではエラーメッセージやログなどをsyslogを使って出力する。もし問題が発生した場合は、/var/log/messagesファイルを確認してみよう。

ストレージノードの準備

続いて、ストレージノードの構築手順について説明していこう。まず、次のようにyumコマンドを実行してストレージノードで稼働させるObjectサービスおよびContainerサービス、Accountサービスが含まれるパッケージをインストールしておく。

yum install --enablerepo=epel openstack-swift-account openstack-swift-container openstack-swift-object

XFS形式のファイルシステム作成とマウント

前述のようにオブジェクトなどを保存するストレージのファイルシステムにはXFSを使用するため、XFS関連のツールが含まれたxfsprogsパッケージをインストールし、ストレージ用パーティションをXFS形式でフォーマットしておく。

# yum install xfsprogs

XFS形式でのフォーマットは、mkfs.xfsコマンドで行える。たとえば/dev/sda5パーションをフォーマットするには以下のようにする。

# mkfs.xfs -i size=1024 /dev/sda5

作成したファイルシステムは、「/srv/node/<ストレージデバイス名>」というディレクトリにマウントしておく。まず、マウントを行うために/etc/fstabファイルに以下の行を追加する。

/dev/sda5 /srv/node/sda5 xfs noatime,nodiratime,nobarrier,logbufs=8 0 0

続いてマウント先ディレクトリを作成してファイルシステムをマウントし、Swiftがアクセスできるようにパーミッションを設定しておく。

# mkdir -p /srv/node/sda5
# mount /srv/node/sda5
# chown -R swift:swift /srv/node

データの同期に使用するrsyncdを設定する

Swiftでは障害などでファイルシステムが破損した場合、復旧後にrsyncを使ってデータの同期を行う。そのため、rsyncdが利用できるようになっている必要がある。

まず、CentOS 6.3ではrsyncdがxinetd経由で起動されるため、xinetdのインストールを行っておく。

# yum install xinetd

続いて/etc/rsyncd.confを作成し、以下の内容を記述しておく。

uid = swift
gid = swift
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
address = <ストレージノード自身のIPアドレス>

[account]
max connections = 2
path = /srv/node/
read only = false
lock file = /var/lock/account.lock

[container]
max connections = 2
path = /srv/node/
read only = false
lock file = /var/lock/container.lock

[object]
max connections = 2
path = /srv/node/
read only = false
lock file = /var/lock/object.lock

最後に、chkconfigコマンドでrsyncを有効にし、xinetdを起動させる。

# chkconfig rsync on
# service xinetd start

各種ストレージサービスの設定

続いてストレージサービスの設定を行っていく。編集が必要な設定ファイルは以下の4つだ。

  • /etc/swift/swift.conf
  • /etc/swift/account-server.conf
  • /etc/swift/container-server.conf
  • /etc/swift/object-server.conf

まず/etc/swift/swift.confだが、プロクシノードの場合と同様に「swift_hash_path_suffix」項目を編集しておく。たとえばプロクシノードで「SourceForge20130401」という文字列を設定していた場合、こちらでも同じ値を指定する。

[swift-hash]
swift_hash_path_suffix = SourceForge20130401

それ以外の設定ファイルについては、サービスを稼働させるIPアドレスの指定のみが必要だ。まずアカウントサービスの設定ファイルである/etc/swift/account-server.confファイルは以下のようになる。

[DEFAULT]
bind_ip = <ストレージノード自身のIPアドレス>
bind_port = 6002
workers = 2

[pipeline:main]
pipeline = account-server

[app:account-server]
use = egg:swift#account

[account-replicator]

[account-auditor]

[account-reaper]

Containerサービスの設定ファイルである/etc/swift/container-server.confでも、同様に「bind_ip」にストレージノードのIPアドレスを指定しておく。

[DEFAULT]
bind_ip = <ストレージノード自身のIPアドレス>
bind_port = 6001
workers = 2

[pipeline:main]
pipeline = container-server

[app:container-server]
use = egg:swift#container

[container-replicator]

[container-updater]

[container-auditor]

[container-sync]

Objectサービスの設定ファイルは/etc/swift/object-server.confだ。こちらも「bind_ip」の指定を行っておく。

[DEFAULT]
bind_ip = <ストレージノード自身のIPアドレス>
bind_port = 6000
workers = 3

[pipeline:main]
pipeline = object-server

[app:object-server]
use = egg:swift#object

[object-replicator]

[object-updater]

[object-auditor]

ringファイルの配置

続いて、プロクシサーバーで作成したring情報が含まれたファイルをscpなどでコピーし、/etc/swiftディレクトリにコピーしておく。以下の例は、プロクシサーバーからコピーを行う例だ。

# scp /etc/swift/*.gz <ストレージノードのホスト名>:/etc/swift/

サービスの起動

以上でストレージノードの設定は完了だ。swift-initコマンドでサービスを起動させよう。なお、swift-initの第一引数に「main」を指定することで、Swiftを構成している主サービスすべてをまとめて指定できる。これで問題が無ければ、続いて「rest」を指定してそのほかに必要となるサービスを起動させる。

# swift-init main start
# swift-init rest start

Swiftの稼働テスト

最後に、swiftコマンドを使って実際にSwiftにデータを格納する操作を行い、正しくSwiftが稼働しているかどうかを確認しておこう。まず、「ST_AUTH」および「ST_USER」、「ST_KEY」環境変数に認証のためのURLや使用するテナント名およびユーザー名、パスワードを設定しておく。

export ST_AUTH=http://<keystoneが稼働しているホスト名>:5000/v2.0
export ST_USER=<テナント名>:<ユーザー名>
export ST_KEY=<パスワード>

続いて、swiftの利用状況を確認する「swift stat」コマンドを実行する。正しくSwiftが稼働していれば、以下のように利用状況が表示されるはずだ。

# swift stat
Account: AUTH_********************************
Containers: 0
Objects: 0
Bytes: 0
Accept-Ranges: bytes
X-Timestamp: 1361870000.09871

それでは、実際にファイルをSwiftに格納してみよう。まず、「swift post」コマンドでファイルを格納するコンテナを作成する。

# swift post container01

この状態で「swift list」コマンドを実行すると、作成されたコンテナが確認できるはずだ。

# swift list
container01

続いて、「swift upload」コマンドで作成したコンテナに適当なファイルをアップロードする。ここでは「container01」というコンテナに、ローカルにあるtest.txtというファイルをアップロードしている。

[root@fusion ~]# swift upload container01 test.txt
test.txt

「swift list <コンテナ名>」コマンドを実行すると、ファイルが格納されていることが確認できる。

[root@fusion ~]# swift list container01
test.txt

このとき、「swift stat」コマンドを再度実行するとその出力が変化していることが分かる。

# swift stat
Account: AUTH_********************************
Containers: 1
Objects: 1
Bytes: 18
Accept-Ranges: bytes
X-Timestamp: 1361870000.09871

ファイルのダウンロードは、「swift dwonload」コマンドで実行できる。

# cd /tmp
# swift download container01 test.txt
test.txt [headers 0.145s, total 0.145s, 0.000s MB/s]

コンテナやファイルの削除は「swift delete」コマンドで行える。

# cd /tmp
# swift delete container01

OpenStack Dashboard(Horizon)からコンテナにアクセスする

OpenStackのGUI管理コンソールであるOpenStack Dashboard(Horizon)を利用している場合、ここからもSwiftにアクセスできる。「プロジェクト」で対象とするテナントを選択し、左メニューの「コンテナー」を選択すると、アクセス権限があるコンテナが一覧表示される。ここでコンテナの作成や削除が行えるほか、コンテナ名をクリックするとそのコンテナ内に含まれるオブジェクト(ファイル)の確認やコンテナへのアップロード、オブジェクトの削除といった操作が行える(図3)。

図3 OpenStack Dashboard(Horizon)にはコンテナへのアクセス機能が搭載されている
図3 OpenStack Dashboard(Horizon)にはコンテナへのアクセス機能が搭載されている

独自のクラウドストレージを容易に構築可能、プライベートクラウド以外にもさまざまな利用できる

以上のように、Swiftを利用することで容易に独自のクラウドストレージを構築できる。SwiftではRAIDではなく、データを複製して複数のノードに分散配置することで冗長性を確保しているため、低コストで信頼性の高いストレージサービスを構築できるのが特徴だ。

もちろん、AmazonやGoogleなどのサービスを利用する場合と比べると、自前でこのようなストレージサーバーを構築・管理する手間は少なくない。しかし、セキュリティポリシなどの関係でこれらのクラウドサービスを利用できないケースもあるだろう。必ずしもクラウドストレージを利用することが正解というわけではないが、たとえば大量の画像データなどを扱うようなWebサイトなど、目的によってはクラウドストレージは非常に有用だ。目的と構成に応じて利用を検討してみてはいかだろうか。