はじめに
分散処理環境を扱うエンジニアにとって、時々の検証の必要性に応じたクラスターを構築するために、クラウドを活用することができます。
本来の目的である検証自体に集中するためにも、クラスター環境の構築は、再現性があり、かつ容易に構成変更できるものとしたいところです。
世の中には、様々な構成管理ツールが存在しており、それらは本番環境運用のように、構成管理そのものの信頼性や運用の標準化を求める場合には非常に有用なものですが、ここでの、検証環境の構築という目的のためには、最低限必ず必要になる技術要素のみで、目的を達成したいと思います。
ここでは、クラウドとしてAWSを利用します。スクリプトの実行環境としては、Macを用い、CLIのインストールなどは完了しているものとします。スクリプト化に際して、SSHを利用していますが、SSHについての説明も割愛します。
ここで紹介する手順は、Hadoopなど、様々な分散処理環境の準備に応用できるものですが、ここではCouchbase Serverのインストールを実例として説明します。
EC2インスタンス作成
VMを構築するスクリプトの本体(create_aws_instances.sh
)は以下のようなものです。
#!/bin/bash
. ./my_aws.config
echo "KEY_NAME:" $KEY_NAME
echo "TAG_PROJECT:" $TAG_PROJECT
echo "TAG_OWNER:" $TAG_OWNER
echo "INSTANCE_NAME:" $INSTANCE_NAME
echo "OS_USER:" $OS_USER
LANG=c date +%Y%m%d
CLI_INPUT_JSON=file://my_cli_input.json
TAG_DATE=`LANG=c date +%Y%m%d`
TAG_DATE_TIME=`LANG=c date +%Y%m%d_%H%M`
TAG_ENDDATE=`LANG=c date -v +7d +%m%d%Y`
INSTANCE_FULLNAME="${INSTANCE_NAME}_${TAG_DATE_TIME}"
TAG_SPECS="ResourceType=instance,Tags=[\
{Key=Name,Value=${INSTANCE_FULLNAME}},\
{Key=owner,Value=${TAG_OWNER}},\
{Key=enddate,Value=${TAG_ENDDATE}},\
{Key=project,Value=${TAG_PROJECT}}]"
MSG_DRYRUN_EXPECTED="Request would have succeeded, but DryRun flag is set"
MSG_DRYRUN=$(aws ec2 run-instances --dry-run --region $REGION \
--instance-type $INSTANCE_TYPE \
--tag-specifications $TAG_SPECS \
--cli-input-json $CLI_INPUT_JSON 2>&1 )
echo $MSG_DRYRUN
if [[ $MSG_DRYRUN = *$MSG_DRYRUN_EXPECTED* ]]; then
echo "Dry Run Succeeded."
else
echo "Dry Run Failed."
exit
fi
FILENAME_PASSWORDLESS_SHELL="passwordless_${INSTANCE_NAME}_${TAG_DATE_TIME}.sh"
function create_instance() {
if [ $# -ne 0 ]; then
NODE_TYPE=$1
INSTANCE_FULLNAME="${INSTANCE_NAME}_${TAG_DATE_TIME}_${NODE_TYPE}"
fi
echo $INSTANCE_FULLNAME
TAG_SPECS="ResourceType=instance,Tags=[\
{Key=Name,Value=${INSTANCE_FULLNAME}},\
{Key=owner,Value=${TAG_OWNER}},\
{Key=enddate,Value=${TAG_ENDDATE}},\
{Key=project,Value=${TAG_PROJECT}}]"
echo $TAG_SPECS
INSTANCE_ID=$(aws ec2 run-instances --region $REGION \
--instance-type $INSTANCE_TYPE \
--tag-specifications $TAG_SPECS \
--query 'Instances[].InstanceId' \
--output text \
--user-data=file://"init.sh" \
--cli-input-json $CLI_INPUT_JSON )
echo $?
echo Instance ID: $INSTANCE_ID
aws ec2 wait instance-running --instance-ids $INSTANCE_ID; echo 'Instance is prepared.'
aws ec2 modify-instance-attribute \
--instance-id $INSTANCE_ID \
--no-disable-api-termination
PUBLIC_IP=$(aws ec2 describe-instances --instance-id $INSTANCE_ID --query 'Reservations[].Instances[].PublicIpAddress' --output text)
echo Public IP: $PUBLIC_IP
cat <<EOF >> $FILENAME_PASSWORDLESS_SHELL
cat ~/.ssh/id_rsa.pub | ssh -o "StrictHostKeyChecking=no" -i $KEY_NAME.pem $OS_USER@$PUBLIC_IP 'cat >> .ssh/authorized_keys'
ssh -o "StrictHostKeyChecking=no" -i $KEY_NAME.pem $OS_USER@$PUBLIC_IP 'chmod 700 .ssh; chmod 640 .ssh/authorized_keys'
EOF
}
for node in ${NODES[@]}
do
create_instance $node
done
chmod +x $FILENAME_PASSWORDLESS_SHELL
設定の定義
上記スクリプトの初めの部分で、読みこまれているmy_aws.config
は、以下のようなものです。
<YOUR_...>
の部分やINSTANCE_TYPE
、OS_USER
の値は、適宜置き換えてください。
KEY_NAME=~/<YOUR_KEY_NAME>
INSTANCE_NAME=<YOUR_INSTANCE_NAME>
REGION=<YOUR_REGION>
TAG_PROJECT=<YOUR_TAG_PROJECT>
TAG_OWNER=<YOUR_TAG_OWNER>
INSTANCE_TYPE=t2.medium
OS_USER=ec2-user
NODES=(CB01 CB02 CB03 CB04 CB05 CB06)
最後の行は、6ノードからなるCouchbase Serverクラスターを構築するために、6つのインスタンスを作成することを意味します。それぞれのインスタンスには、指定した名前が付されます。
Hadoopの場合は、(MASTER1 MASTER2 WORKER1 WORKER2 WORKER3)
のようにしても良いでしょう。
上記スクリプト実行により、ドライラン実行後、NODES
変数に指定した数のEC2インスタンスを作成します。
また、passwordless_${INSTANCE_NAME}_${TAG_DATE_TIME}.sh
という名前のシェルスクリプトファイルを生成します。
注:上記のスクリプト中に、EC2インスタンスにタグを付与している箇所があります。多くは(Key=Name
以外は)任意のものですが、今回記事にするに当たって、あえて取り除かず残してあります。特にKey=enddate
は、混乱を招くかもしれないため注釈すると、ここで指定している日付(作成時点の7日後)も、あくまで任意のものです。このようなスクリプトによるタグ内容の生成は、組織から提供されたクラウドのアカウントを使っている際に、特定のタグが付与されていない場合には自動的にインスタンスが削除される運用が行われている場合に、活用することができます。
初期実行スクリプト
上記スクリプトの下記抜粋にあるように、aws ec2 run-instances
コマンドに--user-data=file://"init.sh
というオプションを指定しています。
INSTANCE_ID=$(aws ec2 run-instances --region $REGION \
--instance-type $INSTANCE_TYPE \
--tag-specifications $TAG_SPECS \
--query 'Instances[].InstanceId' \
--output text \
--user-data=file://"init.sh" \
--cli-input-json $CLI_INPUT_JSON )
init.sh
というファイルが実行しているディレクトリに配置されていることを想定しています。このスクリプトは、インスタンス作成後にインスタンス上で実行されます。
内容は任意ですが、例として、下記のように設定した時刻に自動的にシャットダウンする設定を加えることなどができます。
#! /bin/bash
echo "0 22 * * * root /sbin/shutdown -h now" >> /etc/crontab
インスタンス定義
上記の抜粋の--cli-input-json
オプションに指定されている変数($CLI_INPUT_JSON
)は、下記のように定義されています。このファイル(my_cli_input.json
)も、同じディレクトリに配置されていることを前提としています。
CLI_INPUT_JSON=file://my_cli_input.json
内容は、下記のようなものです。
<YOUR_...>
の部分やInstanceType
、その他の値は、適宜置き換えてください。
{
"ImageId": "<YOUR_IMAGE_ID>",
"KeyName": "<YOUR_KEY_NAME>",
"SecurityGroupIds": [
"<YOUR_SECURITY_GROUP_IP>"
],
"InstanceType": "t2.medium",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/sda1",
"Ebs": {
"VolumeSize": 20,
"DeleteOnTermination": true,
"VolumeType": "standard"
},
"NoDevice": ""
}
],
"Monitoring": {
"Enabled": false
},
"SubnetId": "<YOUR_SUBNET_ID>",
"DisableApiTermination": true,
"NetworkInterfaces": [
{
"DeviceIndex": 0,
"AssociatePublicIpAddress": true
}
]
}
リモートログイン設定
パスワードを用いずに、EC2へのリモートログインを実行できるようにするために、生成されたシェルスクリプトを実行します。
このスクリプトの中身は、上記スクリプトの以下の部分が対応します。
cat ~/.ssh/id_rsa.pub | ssh -o "StrictHostKeyChecking=no" -i $KEY_NAME.pem $OS_USER@$PUBLIC_IP 'cat >> .ssh/authorized_keys'
ssh -o "StrictHostKeyChecking=no" -i $KEY_NAME.pem $OS_USER@$PUBLIC_IP 'chmod 700 .ssh; chmod 640 .ssh/authorized_keys'
さらに、下記のスクリプト(renew_ssh_config.sh
)を実行します。
このスクリプトは、現在起動されているEC2インスタンスを対象として、SSH設定ファイルを生成します。
上掲のインスタンス作成用スクリプトでは、何度かインスタンスを作成した場合にも、EC2インスタンスの名前が重複しないよう年月日時分を使ってフルネームを生成していますが、ここではNODES
配列に利用した名前を使うため、同じ名前(上記フルネーム中の最後に_
区切りを用いサフィックスとして追加)が付されたインスタンスが同時に起動していないことが前提となります。先に作ったインスタンスを停止せずに、付加的に、インスタンス作成用スクリプトを複数回実行する場合には、都度NODES
配列に含まれる名前を変更する必要があります。
#!/bin/bash +x
DATE=`LANG=c date +%y%m%d_%H%M`
PATH_CONFIG=~/.ssh/
FILE_CONFIG=config
mv ${PATH_CONFIG}${FILE_CONFIG}{,.$DATE.bak}
aws ec2 describe-instances --output=text --query 'Reservations[].Instances[].{InstanceId: InstanceId, GlobalIP: join(`, `, NetworkInterfaces[].Association.PublicIp), State: State.Name, Name: Tags[?Key==`Name`].Value|[0]}' | grep running | awk '{printf "Host %s\n HostName %s\n User ec2-user\n Port 22\n ServerAliveInterval 60\n", substr($3,match($3,"_[^_]*$")+1), $1}' > ${PATH_CONFIG}${FILE_CONFIG}
注:実はこのSSH設定ファイル作成スクリプトには、OSのユーザ (ec2-user
)を、ハードコードしています(最後のコマンド)。
このファイルも、初めのスクリプトで生成するよう修正することが考えられますが、差し当たり、このスクリプトを利用する場合には、適宜利用するOSに合わせて、編集してください。
ここまで実行すると、my_aws.config
の定義を用いて、下記のようにVMにログインすることができるようになります。
$ ssh ec2-user@CB1
リモートシェル実行(Couchbase Serverインストール)
上記のリモートログイン設定により、下記のスクリプト(exec_on_hosts.sh
)を用いて、ローカル端末からVM上でのシェルのリモート実行が可能になります。
#!/bin/sh +x
. ./my_aws.config
echo "OS_USER:" $OS_USER
script=$1
LOG_BASE=$(basename ${1%.*})_`LANG=c date +%y%m%d_%H%M`
echo $LOG_BASE
for node in ${NODES[@]}
do
ssh $OS_USER@$node 'LC_ALL=C sudo bash -s -x 2>&1' < $script > ${LOG_BASE}_${node}.log
done
上記のスクリプトは、下記のように、リモートで実行したいスクリプトを引数にとります。
$ ./exec_on_hosts.sh cb_install.sh
Couchbase Serverインストール用のスクリプト(cb_install.sh
)は、以下のようなものです。
実際に、リモートOS上で実行できるスクリプトです(ローカルから実行するための特別な要素ははありません)。
#!/bin/bash -x
if [ ! -e ./couchbase-release-1.0-x86_64.rpm ]; then
curl -O https://packages.couchbase.com/releases/couchbase-release/couchbase-release-1.0-x86_64.rpm
fi
sudo rpm -i ./couchbase-release-1.0-x86_64.rpm
sudo yum -y install couchbase-server
echo 'PATH="$HOME/.local/bin:$HOME/bin:$PATH:/opt/couchbase/bin"' >> .bashrc
echo "export PATH" >> .bashrc
echo 'MANPATH=$MANPATH:/opt/Couchbase/share/man' >> .bashrc
echo "export MANPATH" >> .bashrc
sudo systemctl status couchbase-server
上記スクリプト実行によるログが、ローカルのファイルに出力されます。
ここまでが、一般的にクラスター環境準備のために活用できる部分です。
Couchbase Serverクラスターの構成
ここからは、Couchbase Serverをクラスターとして構成する方法を説明します。
http://(Public IP/DNS):8091
にアクセスします。
インストール直後の状態では、下記のような画面が表示されます。「Setup New Cluster」を選択します。
「Host Name / IP Address」として、アクセスしているホストの情報を入力します。「Save & Finish」を押下します。
さらに必要な設定を行います。
1ノードからなるクラスターの構築は、以上で完了です。適宜ノードの追加を行います。
最後に
今回、目的を達成するために必要な手順の概観を伝えることを旨とし、内容の解説は最低限に留めました。引用したスクリプトは、必要な修正を加えた上で実行できるよう、ファイル全体を掲載しています。具体的な詳細は、スクリプト自体を参照してください。