めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Aeolus(アイオロス)でRHELonEC2を利用する(その3)

はじめに

前回から間があきましたが、いよいよ複数インスタンスの起動/連携にチャレンジしてみます。例として、前回の最後にデモを紹介したGlusterFSクラスタを構築します。

具体的な手順としては、1つのアプリケーション・ブループリントに複数インスタンスの構成情報を記述すればOKです。ただし、前回のようにアプリケーションの導入手順をスクリプトでベタ書きするのは、あまりスマートではありません。そこで、今回は、「アプリケーション環境構築の自動化をまじめに考えてみる(3)」で編み出した、Puppetによるアプリケーション導入・設定を併用することにします。

つまり、GlusterFSの導入・初期設定を実施するPuppetマニフェストをGithubに上げておき、アプリケーション・ブループリントには、これをダウンロードして適用するテンプレート処理を記載しておきます。ただし、Puppetでは個々のインスタンスの構成しかできませんので、Puppetにより個々のインスタンスでglusterdの起動が完了した後に、GlusterFSのクラスタを構成して、ボリュームを作成する処理を別途、アプリケーション・ブループリントに仕込みます。

Puppetマニフェストの準備

実際に作成したマニフェストはここにあります。ファイルの構成は下記の通りで、dist以下には、iptablesとI/Oチューニングの設定ファイルが入っています。

$ tree gluster_puppet/
gluster_puppet/
|-- dist
|   |-- iptables
|   `-- rc.gluster
`-- main.pp

折角なので、マニフェストの中身も晒しておきます。

class glusterfs {

  exec { 'mkfs_data':
    path => '/sbin',
    command => "mkfs.ext4 -I 512 -E lazy_itable_init=1 $data_device",
    unless => "tune2fs -l $data_device",
    logoutput => true,
    before => Mount['data_dir'],
  }

  file { '/data':
    owner => 'root',
    group => 'root',
    mode => '0755',
    ensure => directory,
    before => Mount['data_dir'],
  }

  mount { 'data_dir':
    name => '/data',
    options => 'defaults',
    device => $data_device,
    fstype => 'ext4',
    ensure => 'mounted',
  }

  file { '/etc/sysconfig/iptables':
    owner => 'root',
    group => 'root',
    mode => '0600',
    source => "$manifest_dir/dist/iptables",
    notify => Service['iptables'],
  }

  service { 'iptables':
    name => 'iptables',
    ensure => 'running',
    enable => true,
    hasstatus => true,
  }

  yumrepo { 'glusterfs_repo':
    name => 'glusterfs',
    descr => 'Repository for GlusterFS 3.3',
    baseurl => 'http://download.gluster.org/pub/gluster/glusterfs/3.3/LATEST/EPEL.repo/epel-6Server/x86_64/',
    enabled => '1',
    gpgcheck => '0',
    before => Package['glusterfs_server'],
  }

  service { 'glusterd':
    name => 'glusterd',
    ensure => 'running',
    enable => true,
    hasstatus => true,
    subscribe => Package['glusterfs_server'],
  }

  $server_packages = [ 'glusterfs-server',
                       'glusterfs-geo-replication',
                       'glusterfs-fuse' ]
  package { 'glusterfs_server':
    name => $server_packages,
    ensure => installed,
  }

  file { '/etc/rc.gluster':
    owner => 'root',
    group => 'root',
    mode => '0755',
    source => "$manifest_dir/dist/rc.gluster",
    notify => Exec['rc_gluster'],
  }

  exec { 'rc_gluster':
    path => [ '/sbin', '/bin', '/usr/bin' ],
    command => 'echo "/etc/rc.gluster" >> /etc/rc.local; /etc/rc.local',
    unless => 'grep "/etc/rc.gluster" /etc/rc.local',
    logoutput => true,
    refreshonly => true,
  }
}

include 'glusterfs'

$data_deviceで指定されるデバイスをフォーマットして/dataにマウントした後に、GlusterFSのRPMをインストールして、glusterdを起動する処理を行います。$data_deviceは、環境変数$FACTER_data_device経由で与えます。

アプリケーション・ブループリントの中から次のスクリプトを実行すると、Githubからマニフェストをダウンロードして適用することができます。

# Setting up glusterfs using Puppet
yum -y install http://mirror.us.leaseweb.net/epel/6/i386/epel-release-6-7.noarch.rpm
yum -y install git
yum -y install puppet
mkdir -p /tmp/gittmp
cd /tmp/gittmp
git clone https://github.com/enakai00/gluster_puppet.git
cd gluster_puppet
git checkout $AUDREY_VAR_glusterd_gittag
export FACTER_manifest_dir="/tmp/gittmp/gluster_puppet"
export FACTER_data_device="/dev/xvdj"
puppet main.pp

「$AUDREY_VAR_glusterd_gittag」には、Gitの適用するタグを指定します。これにより、複数バージョンの設定ファイルから、希望のバージョンを適用することが可能になります。

アプリケーション・ブループリントの作成

まずは、前回記事の「自動設定機能のテスト」の冒頭部分の手順に従います。イメージ「RHEL6.3 with audrey-agent」に対して、「イメージから展開可能な新規アプリを作成」を押して、新しいアプリケーション「GlusterFS 2nodes cluster」を「EC2 Test Catalogue」に作成します。その後、「XMLの編集」を押して、XML(アプリケーション・ブループリント)の編集画面に移ります。

そして、おもむろに下記の内容を書き込みます。

<?xml version="1.0"?>
<deployable version="1.0" name="GlusterFS 2nodes cluster with Puppet">
  <description/>
  <assemblies>
    <assembly name="node01" hwp="small-x86_64">
      <image id="c0d6f20f-6e7a-4835-b41e-7b16a8cdbe35"/>
      <services>
        <service name="glusterd">
          <executable>
            <contents><![CDATA[#!/bin/bash -x
{
# Setting up glusterfs using Puppet
yum -y install http://mirror.us.leaseweb.net/epel/6/i386/epel-release-6-7.noarch.rpm
yum -y install git
yum -y install puppet
mkdir -p /tmp/gittmp
cd /tmp/gittmp
git clone https://github.com/enakai00/gluster_puppet.git
cd gluster_puppet
git checkout $AUDREY_VAR_glusterd_gittag
export FACTER_manifest_dir="/tmp/gittmp/gluster_puppet"
export FACTER_data_device="/dev/xvdj"
puppet main.pp

# Create volume
sleep 1
gluster peer probe $AUDREY_VAR_glusterd_node02_host
gluster vol create vol01 \
  $HOSTNAME:/data/brick01 \
  $AUDREY_VAR_glusterd_node02_host:/data/brick01
gluster vol start vol01
} 2>&1 | logger -t "audrey_volume"
]]>
            </contents>
          </executable>
          <parameters>
            <parameter name="gittag">
              <value>ec2-v1_0</value>
            </parameter>
            <parameter name="node02_host">
              <reference assembly="node02" parameter="hostname"/>
            </parameter>
            <parameter name="node02_up">
              <reference assembly="node02" parameter="glusterup"/>
            </parameter>
          </parameters>
        </service>
      </services>
    </assembly>

    <assembly name="node02" hwp="small-x86_64">
      <image id="c0d6f20f-6e7a-4835-b41e-7b16a8cdbe35"/>
      <services>
        <service name="glusterd">
          <executable>
            <contents><![CDATA[#!/bin/bash -x
{
# Setting up glusterfs using Puppet
yum -y install http://mirror.us.leaseweb.net/epel/6/i386/epel-release-6-7.noarch.rpm
yum -y install git
yum -y install puppet
mkdir -p /tmp/gittmp
cd /tmp/gittmp
git clone https://github.com/enakai00/gluster_puppet.git
cd gluster_puppet
git checkout $AUDREY_VAR_glusterd_gittag
export FACTER_manifest_dir="/tmp/gittmp/gluster_puppet"
export FACTER_data_device="/dev/xvdj"
puppet main.pp

# Notifying this node is up
cat <<EOF >/usr/lib/ruby/site_ruby/1.8/facter/glusterup.rb
Facter.add(:glusterup) do
  setcode do
    true
  end
end
EOF
} 2>&1 | logger -t "audrey_glusterd"
]]>
            </contents>
          </executable>
          <parameters>
            <parameter name="gittag">
              <value>ec2-v1_0</value>
            </parameter>
          </parameters>
        </service>
      </services>
      <returns>
        <return name="hostname"/>
        <return name="glusterup"/>
      </returns>
    </assembly>
  </assemblies>
</deployable>

なかなか複雑ですが、ポイントをしぼって解説します。まず、大きくは、次の2つの「assembly」があります。これにより、このアプリケーション・ブループリントからインスタンスが2個起動します。「image id」は環境によって変わるので注意してください。デフォルトで用意されたXMLに記載されていた値を使用します。

<assembly name="node01" hwp="small-x86_64">
      <image id="c0d6f20f-6e7a-4835-b41e-7b16a8cdbe35"/>
・・・

<assembly name="node02" hwp="small-x86_64">
      <image id="c0d6f20f-6e7a-4835-b41e-7b16a8cdbe35"/>
・・・

次に、node02の「executable」タグに記載されたスクリプトを見てみます。前半は先に示した、PuppetでGlusterFSを構成する部分です。その後に続く下記の処理に注目します。

# Notifying this node is up
cat <<EOF >/usr/lib/ruby/site_ruby/1.8/facter/glusterup.rb
Facter.add(:glusterup) do
  setcode do
    true
  end
end
EOF

これは、実は、node02の構成が完了したことをnode01に通知するためのトリックです。各ノードは、Rubyのfacterモジュールで取得可能な情報をConfigServerに通知しますが、これが実行されると、新しく「glusterup」というパラメータが用意されて通知されるようになります。この後で説明するように、node01では、node02のこのパラメータをConfigServerから取得した後に設定を開始するように仕込んであります。したがって、node02の構成が完了したタイミングで、glusterupパラメータが取得できて、ようやくnode01の設定処理が開始します。

具体的には、node01のassemblyタグの下にある下記の部分で、node02のパラメータを取得しています。

            <parameter name="node02_host">
              <reference assembly="node02" parameter="hostname"/>
            </parameter>
            <parameter name="node02_up">
              <reference assembly="node02" parameter="glusterup"/>
            </parameter>

ここでは、node02のhostnameも合わせて取得しています。これは、「$AUDREY_VAR_glusterd_node02_host」という変数で参照できるようになります。

そして、node01の「executable」タグに記載されたスクリプトを見ると、Puppetで構成する処理の後に、glusterコマンドで、クラスタの構成とボリュームの作成処理を行なっていることが分かります。先のトリックがあるので、node01の設定処理が走る時は、node02の構成は完了しているので、クラスタの構成処理に安心して進むことができます。

最後に、それぞれのノードに下記のパラメータ指定があります。

            <parameter name="gittag">
              <value>ec2-v1_0</value>
            </parameter>

これは、Aeolusからアプリケーションを展開する際にユーザが指定するパラメータで、使用する設定のGitのタグを指定しています。次の画面で、任意のタグを指定できるようになります。


アプリケーションを展開してみる

以上の設定を元に、実際にアプリケーションを展開します。無事に展開に成功したところで、node01のAudreyのログを見ると次のようになっています。

[root@ip-10-150-125-231 ~]# cat /var/log/audrey.log 
2012-10-21 02:57:34,935 - INFO    : audrey:1351 Invoked audrey_script_main
2012-10-21 02:57:35,638 - INFO    : audrey:1380 
<Instance of: CSClient
	Version: 1
	Config Server Endpoint: https://ec2-175-41-218-247.ap-northeast-1.compute.amazonaws.com
	Config Server oAuth Key: xxxxxxxxxx
	Config Server oAuth Secret: xxxxxxxxxxx
	Config Server Params: 
	Config Server Configs: 
	Temporary Directory: 
	Tarball Name: 
eot>
2012-10-21 02:57:35,641 - INFO    : audrey:952 Invoked CSClient.get_cs_tooling()
2012-10-21 02:57:36,070 - INFO    : audrey:684 Invoked unpack_tooling()
2012-10-21 02:57:36,077 - INFO    : audrey:909 Invoked CSClient.get_cs_configs()
2012-10-21 02:57:36,105 - INFO    : audrey:1415 No configuration parameters provided. status: 202
2012-10-21 02:57:36,105 - INFO    : audrey:924 Invoked CSClient.get_cs_params()
2012-10-21 02:57:36,132 - INFO    : audrey:522 Invoked generate_provides()
2012-10-21 02:57:39,763 - INFO    : audrey:939 Invoked CSClient.put_cs_params_values()
2012-10-21 02:57:49,806 - INFO    : audrey:909 Invoked CSClient.get_cs_configs()
2012-10-21 02:57:49,836 - INFO    : audrey:1415 No configuration parameters provided. status: 202
(中略)
2012-10-21 03:00:00,373 - INFO    : audrey:909 Invoked CSClient.get_cs_configs()
2012-10-21 03:00:00,403 - INFO    : audrey:1415 No configuration parameters provided. status: 202
2012-10-21 03:00:10,413 - INFO    : audrey:909 Invoked CSClient.get_cs_configs()
2012-10-21 03:02:54,544 - INFO    : audrey:614 Execute Tooling command: /var/audrey/tooling/user/glusterd/start
2012-10-21 03:02:54,545 - INFO    : audrey:614 return code: 0
2012-10-21 03:02:54,545 - INFO    : audrey:614 
	Start Output of: /var/audrey/tooling/user/glusterd/start >>>

	<<< End Output

「(中略)」の部分は、しばらくConfigServerからの202の応答が続いていますが、これは、node02の「glusterup」パラメータを取得しようとして失敗している部分です。node02の構成が完了して、glusterupパラメータが取得できた段階で、設定スクリプトの実行が始まっています。

念のためGlusterFSのボリュームが構成できていることも確認しておきます。

# gluster peer status
Number of Peers: 1

Hostname: ip-10-152-142-94
Uuid: 95dfc36f-fc99-446d-a9b3-c6b68a37db95
State: Peer in Cluster (Connected)

# gluster vol info
 
Volume Name: vol01
Type: Distribute
Volume ID: dc561ad6-903e-4e3b-b62b-ee022b3823a5
Status: Started
Number of Bricks: 2
Transport-type: tcp
Bricks:
Brick1: ip-10-160-78-73:/data/brick01
Brick2: ip-10-152-142-94:/data/brick01

# mount -t glusterfs localhost:/vol01 /mnt
# df -h
Filesystem            Size  Used Avail Use% マウント位置
/dev/xvde1            5.7G  1.8G  3.8G  33% /
none                  828M     0  828M   0% /dev/shm
/dev/xvdj             147G  188M  140G   1% /data
localhost:/vol01      294G  376M  279G   1% /mnt

まとめ

今回は、2ノードの構成を行いましたが、原理的には、ノード数が増えても同じです。node01では、その他の各ノードから「glusterup」パラメータを取得するように仕込んでおけば、他のノードの構成がすべて終わってから、node01の構成が始まります。

そして、賢明な読者はお気づきかと思いますが、この手法にはいくつか不便な点があります。

まず、使用するノード数が決め打ちになります。アプリケーション展開時にユーザが使用するノード数を指定することができません。また、ノード間の依存関係を「ワークフロー」的に定義することができません。つまり、「node01でgluterdの起動まで行なって、そこで、node02の構成完了を待って、それからクラスタを定義する」などの複雑な処理はできません。

このあたりは、今後のバージョンアップに期待することにしましょう。。。