GCE上でAnsibleのチュートリアル準備

GCE上でちょこっとAnsibleを試そうと思ったときに、思いのほか認証周りが面倒だったからメモっておきます。 Ansibleをそもそもまともに使ったことがないのが原因かも。

ansibleのインストール

GCE上のCentOS6でのインストール virtualenvでpython2.7環境で

workon ansibleenv
pip install ansible

External IPなしのインスタンス作成

こんなかんじで 節約のため --preemptible で一日インスタンス 外部IPなしは --no-address

gcloud compute \
  --project "shaped-infusion-632" instances create "test01" \
  --zone "asia-east1-a" \
  --machine-type "n1-standard-1" \
  --network "default" \
  --no-address \
  --no-restart-on-failure \
  --maintenance-policy "TERMINATE" \
  --preemptible \
  --scopes "https://www.googleapis.com/auth/devstorage.read_only" "https://www.googleapis.com/auth/logging.write" \
  --tags "http-server" "https-server" \
  --image "https://www.googleapis.com/compute/v1/projects/centos-cloud/global/images/centos-6-v20150710" \
  --boot-disk-type "pd-standard" \
  --boot-disk-device-name "test01"

ansibleの疎通確認

ふつうにやってみる

echo "test01" > hosts
ansible -i hosts test01 -m ping

うまくいかない

test01 | FAILED => SSH Error: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

鍵を指定する

echo "test01 ansible_ssh_private_key_file=~/.ssh/google_compute_engine" > hosts
ansible -i hosts test01 -m ping

結果はまだダメ

test01 | FAILED => SSH Error: Host key verification failed.
    while connecting to 相手のIP
    It is sometimes useful to re-run the command using -vvvv, which prints SSH debug output to help diagnose the issue.

Host Keyがダメっていうから、チェックを環境変数から外す

export ANSIBLE_HOST_KEY_CHECKING=False

これで

ansible -i hosts test01 -m ping

結果が無事帰ってくる

test01 | success >> {
    "changed": false,
    "ping": "pong"
}

認証周りがこれでクリアするので、あとは普通に使えるかと

AnsibleでのGCEインスタンス立ち上げをあきらめた

AnsibleでGCEインスタンスを立ち上げようと思ったけど、 Preemptibleという横取りという意味の一日以内に落ちるけど、安い、AWSのスポットみたいなの、を指定する設定がなかったので やめました。 遊ぶだけなので、コスパ重視

とは言っても、手元のMacからAPI叩けばいいんですが、 うちでは、Mac mini を使い、外では MacBookAirを使い、家ではMacBookAirは嫁専用機と貸していて タイミングが悪いと、Ubuntuのノートを持ち歩くことになるので GCE上に一番安いインスタンスを一個立ち上げっぱなしにして、そこに他のインスタンスを立ち上げるシェルスクリプトでもおいておくとします。

万人受けするから、CentOS6.X でいいやと思ったら、思ったよりハマりました。 けっきょく、Python2.7を入れて解決したので、手順をメモ

gceの中からインスタンス作成コマンド実行

開発ツール関連のインストール

sudo yum groupinstall "Development Tools"
sudo yum install python-devel

pipのインストール

cd /tmp
wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py
sudo python get-pip.py
sudo pip install virtualenv virtualenvwrapper

python2.7のインストール

sudo rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
sudo rpm -ivh http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/ius-release-1.0-11.ius.centos6.noarch.rpm

sudo yum install python27 python27-devel python27-setuptools

.bashrc 関連

  • .bashrc に追記
if [ -f /usr/bin/virtualenvwrapper.sh ]; then
    export WORKON_HOME=$HOME/.virtualenvs
    source /usr/bin/virtualenvwrapper.sh
fi
  • 読み込み
source ~/.

Virtualenvを作って入る

mkvirtualenv --python=/usr/bin/python2.7 py27
  • 抜けるのと入るのだけ
deactivate py27
workon py27

pyopensslをインストールするのに必要なパッケージを入れる

sudo yum install openssl-devel libffi-devel

pyopensslのインストール(py27のvirtualenv環境で)

pip install pyopenssl

GCEの管理コンソール周り

  • google cloud の管理コンソールのcredentialあたりからp12ファイルを取ってくる 置く

  • おてもとのPCからの転送コマンド

gcloud compute copy-files hogehogekeyfile.p12 インスタンス名:~/
chmod 600 hogehogekeyfile.p12

export CLOUDSDK_PYTHON_SITEPACKAGES=1
gcloud auth activate-service-account --key-file my_key_file.p12 my_service_account@developer.gserviceaccount.com
gcloud config set プロジェクト名

ここまでやると、 google cloud の管理コンソールのコマンドがCentOS6から実行できる

こんなかんじのも打てるようになるよ

gcloud compute --project "プロジェクト名" \
instances create "test01" \
--zone "asia-east1-a" \
--machine-type "n1-standard-1" \
--network "default" \
--no-restart-on-failure --maintenance-policy "TERMINATE" \
--preemptible --scopes "https://www.googleapis.com/auth/devstorage.read_only" "https://www.googleapis.com/auth/logging.write" \
--tags "http-server" "https-server" \
--image "https://www.googleapis.com/compute/v1/projects/centos-cloud/global/images/centos-6-v20150710" \
--boot-disk-type "pd-standard" \
--boot-disk-device-name "test01"

Ansibleに手を出してみる (インストールだけ)

ひさびさに Google Cloud Platform を使おうと思ったら、すべて忘れてしまっていました。 また、手作業するのが面倒なので、Ansible を覚えようと思います。

Chefはなんかそれ自体でめんどくさそうに感じたのでやめました。

前提条件として、VPC まで GCP 上に作った状態でやります。 VPC は、作りっぱでも課金されないし、一回作ったら終わりなので、Ansible でやらなくてもいいかなーと思います。 (お仕事ならやります、たぶん)

インストール

$ git clone git://github.com/ansible/ansible.git --recursive
$ cd ./ansible
$ source ./hacking/env-setup

pip は入ってるよね

$ sudo pip install paramiko PyYAML Jinja2 httplib2

デフォルトの設定ファイルは /etc/ansible/hosts だけど、ANSIBLE_INVENTORY 環境変数の方が強いみたい。 インベントリーってなんじゃらほい

$ echo "127.0.0.1" > ~/ansible_hosts
$ export ANSIBLE_INVENTORY=~/ansible_hosts

でも、とりあえずやっとく

って、ここまでやって、気づいた。 マニュアルの下の方に Mac は sudo pip install ansible って書いてある

さらに OSX Maveriks ならこれ

$ sudo CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install ansible

環境変数で読んでるだけっぽいから、あとで pip で入れなおす

ここまで書いてて、日本語のすごくわかりやすいブログがあったので、もういいかなってなってきてしまいました。

www.curious-eyes.com

Solrのjoinとblock joinについて

MySQLの場合

regions

id name
1 US
2 ASIA

products

id name brand_id_s
1 iPhone 1
2 iPad 1
3 Galaxy S3 2
4 Galaxy Note 2
5 one X 3

brands

id name region_id_s
1 Apple 1
2 Samsung 2
3 HTC 2

products.name が iPad の brands を検索

JOINでこんなかんじ

mysql> SELECT brands.* FROM brands
    ->        INNER JOIN products ON brands.id=products.brand_id_s
    ->        WHERE products.name="iPad";
+------+-------+-------------+
| id   | name  | region_id_s |
+------+-------+-------------+
|    1 | Apple |           1 |
+------+-------+-------------+
1 row in set (0.00 sec)

mysql>

products.name が iPad の brands がある region を検索

もうちょい複雑なJOINでこんなかんじ

mysql> SELECT regions.* FROM regions
    ->       INNER JOIN brands ON regions.id=brands.region_id_s
    ->       INNER JOIN products ON brands.id=products.brand_id_s
    ->   WHERE products.name="iPad";
+------+------+
| id   | name |
+------+------+
|    1 | US   |
+------+------+
1 row in set (0.00 sec)

mysql>

ここまでは普通です。とは言ってもSQLを普段書かないので、すぐにJOINでこんがらがります。

Solr の JOIN(block joinではない)の場合

brands

id name
1 Apple
2 Samsung
3 HTC

※ brands に resion_id_s がないのは使わないので付け忘れてました

products

id name brand_id_s
1 iPhone 1
2 iPad 1
3 Galaxy S3 2
4 Galaxy Note 2
5 one X 3

ただし、それぞれのフィールドごとに_versionが付く

products.name が iPad の brands を検索

/solr/brands/select?q=:&fq={!join from=brand_id_s to=id fromIndex=products}name:iPad

<?xml version="1.0" encoding="UTF-8"?>
<response>

<lst name="responseHeader">
  <int name="status">0</int>
  <int name="QTime">1</int>
  <lst name="params">
    <str name="indent">true</str>
    <str name="q">*:*</str>
    <str name="_">1404925101638</str>
    <str name="wt">xml</str>
    <str name="fq">{!join from=brand_id_s to=id fromIndex=products}name:iPad</str>
  </lst>
</lst>
<result name="response" numFound="1" start="0">
  <doc>
    <str name="id">1</str>
    <str name="name">Apple</str>
    <long name="_version_">1473164065209581568</long></doc>
</result>
</response>

products.name が iPad の brands がある region を検索

3つ以上のjoinはできない

Solr の BLOCK JOIN の場合

blocks (brandsとproductsをJOINすることを前提として作成)

parents を持つものにのみ _version が付く (区切りがブロック単位)

id name type_s
1 Apple parents
1 iPhone
2 iPad
id name type_s
2 Samsung parents
3 Galaxy S3
4 Galaxy Note
id name type_s
3 HTC parents
5 one X

products.name が iPad の brands を検索

(iPadのparentsを検索)

/solr/blocks/select?q=:&fq={!parent which=type_s:parent}+name:iPad

<?xml version="1.0" encoding="UTF-8"?>
<response>

<lst name="responseHeader">
  <int name="status">0</int>
  <int name="QTime">1</int>
  <lst name="params">
    <str name="indent">true</str>
    <str name="q">*:*</str>
    <str name="_">1404923918712</str>
    <str name="wt">xml</str>
    <str name="fq">{!parent which=type_s:parent}+name:iPad</str>
  </lst>
</lst>
<result name="response" numFound="1" start="0">
  <doc>
    <str name="id">1</str>
    <str name="type_s">parent</str>
    <str name="name">Apple</str>
    <long name="_version_">1473164315527741440</long></doc>
</result>
</response>

wblocks (brandsとproductsとregionsをJOINすることを前提として作成)

grand_parents を持つものにのみ _version が付く (区切りがブロック単位)

id name type_s
1 US grand_parents
1 Apple parents
1 iPhone
2 iPad
id name type_s
2 Asia grand_parents
2 Samsung parents
3 Galaxy S3
4 Galaxy Note
3 HTC parents
5 one X

products.name が iPad の brands を検索

(iPadのparentsを検索) /solr/blocks/select?q=:&fq={!parent which=type_s:parent}+name:iPad

<?xml version="1.0" encoding="UTF-8"?>
<response>

<lst name="responseHeader">
  <int name="status">0</int>
  <int name="QTime">0</int>
  <lst name="params">
    <str name="indent">true</str>
    <str name="q">*:*</str>
    <str name="_">1404924377347</str>
    <str name="wt">xml</str>
    <str name="fq">{!parent which=type_s:parent}+name:iPad</str>
  </lst>
</lst>
<result name="response" numFound="1" start="0">
  <doc>
    <str name="id">1</str>
    <str name="type_s">parent</str>
    <str name="name">Apple</str></doc>
</result>
</response>

products.name が iPad の brands がある region を検索

(iPadのgrand_parentsを検索)

/solr/wblocks/select?q=:&fq={!parent which=type_s:grand_parent}+name:iPad

<?xml version="1.0" encoding="UTF-8"?>
<response>

<lst name="responseHeader">
  <int name="status">0</int>
  <int name="QTime">1</int>
  <lst name="params">
    <str name="indent">true</str>
    <str name="q">*:*</str>
    <str name="_">1404924542233</str>
    <str name="wt">xml</str>
    <str name="fq">{!parent which=type_s:grand_parent}+name:iPad</str>
  </lst>
</lst>
<result name="response" numFound="1" start="0">
  <doc>
    <str name="id">1</str>
    <str name="type_s">grand_parent</str>
    <str name="region_s">US</str>
    <long name="_version_">1473164347482046464</long></doc>
</result>
</response>

いがいとJOINってできるもんですね(白目)

この blockjoin って cloud datastore でできないかなあというのが本題です。 ちょっとだけ、joinについてまとめてみようと思ったらえらいことになってしまった。

cloud datastoreを使う前にSolrのおさらいをするためにMySQLとSolrを立てる(今回はSolr)

それでは、同様のことをSolrでしてみます。

Solrですが、たぶん、create table 的なことである、add core とかが あまりなじみがなさそうなので、順番に書きます。

コアの作成

cd solr-4.9.0/example/solr
cp -pR collection1 products
cp -pR collection1 brands
cp -pR collection1 blocks
cp -pR collection1 wblocks

先ほど、Solrを起動させたので起動状態を表すファイルの削除

rm products/core.properties
rm brands/core.properties
rm blocks/core.properties
rm wblocks/core.properties

コアを有効にする

curl "http://localhost:8983/solr/admin/cores?action=CREATE&name=products&instanceDir=products"
curl "http://localhost:8983/solr/admin/cores?action=CREATE&name=brands&instanceDir=brands"
curl "http://localhost:8983/solr/admin/cores?action=CREATE&name=blocks&instanceDir=blocks"
curl "http://localhost:8983/solr/admin/cores?action=CREATE&name=wblocks&instanceDir=wblocks"

ここから、gistを貼付けていこうを思いましたが、コマンド書いたりすると、(このブログが)長くなるのでxmlを落として Solrの管理画面 の Documents から更新してください。

apt-get install git
git clone https://gist.github.com/yuhiwa/d4731586c1cef2716945
git clone https://gist.github.com/yuhiwa/7e1efdbc094cda199a5f
git clone https://gist.github.com/yuhiwa/98ddfa45e1de36e1db57
git clone https://gist.github.com/yuhiwa/4da8e124925008a90eea

(nested_blocks.xml は wblocksコアです、まぎらしくなってしまった)

(あ、brands_data.xml は brands コアです、そのうち直さなきゃ)

join

wget -q 'http://localhost:8983/solr/brands/select?q=*:*&fq={!join from=brand_id_s to=id fromIndex=products}name:iPad' -O - | head

block join

wget -q 'http://localhost:8983/solr/blocks/select?q=*:*&fq={!parent which=type_s:parent}+name:iPhone' -O - | head

wblock join

wget -q 'http://localhost:8983/solr/wblocks/select?q=*:*&fq={!parent which=type_s:parent}+name:iPhone' -O - | head
wget -q 'http://localhost:8983/solr/wblocks/select?q=*:*&fq={!parent which=type_s:grand_parent}+name:iPhone' -O - | head

curlコマンドだと、{}が展開されてしまうので、wget コマンドで書いてます。 Solrの管理画面のfqにfq=の先を入れてしまうのが見やすいです。

解説で次回です。