カテゴリー
サイト構築

GCEのインスタンスを自動再生成し終わったら電球光らせる

GCE! Hue!

 Google Compute Engineのオートスケールのテンプレート入れ替え後のインスタンス再生成をロードバランサーの状況を見ながら自動で行って、全部再生成終わったら、Philips Hueを光らせるスクリプトを作ったよ。

追記[2016-03-06]

 どうやらGoogleの方でAlpha版ですがローリングアップデートのAPIが出来ているようです。

RollingUpdates

 パラメーター見る限りでは似たようなことが出来そうなので、こっちを使った方がよいでしょうね。

インスタンスの再生成後にLBでHEALTHYになるのに時間がかかる

GCEのオートスケールのテンプレートをコマンドで楽に差し替える | A-tak-dot-com

 上の記事でも書いているように、なぜかGCEのHTTP負荷分散はインスタンスを再生成した後に、10分ぐらい経たないとヘルスチェックを飛ばしてくれません。そのため前回はgcloudコマンドでロードバランサーの状況をみつつ、手動で少しずつインスタンスを再生成し、全部のインスタンスが「UNHEALTY」の状態にならないようにしていました。

面倒だから自動化しよう

 面倒だし手動ではコマンドでやってるので自動化することにしました。

 bashスクリプトで途中まで組んでいましたが、今回ループを繰り返したりして面倒になってきたので、 PythonからGCEを操作しようと思います。ついでに処理が終わったらネットワークから操作できる電球「Hue」を光らせてお知らせするという「おまけ」付きです😁

Python3をMacにインストール

 Homebrewでインストールです。バージョンは3を使いました。

[bash]
brew update
brew install python3
[/bash]

API Client Library for Python をインストール

 PythonからGoogle Cloud Platformを操作するためのライブラリをインストールです。

Compute Engine API Client Library for Python

  以下のコマンド打つだけなので楽勝。私はpip3でインストールしました。

[bash]
pip3 install –upgrade google-api-python-client
[/bash]

Hueを操作するために requests をインストール

 Philips HueはRESTで操作するので、requestsをインストールしました。
[bash]
pip3 install requests
[/bash]

以上。

Google のAPIアクティベート

 「API Manager」の中から「Compute Engine API」を有効化します。私の場合、最初からAPIは有効になっていました。

インスタンスを再生成するスクリプト

 以下のようなスクリプトを作りました。とりあえず動けばいいや的な感じで作っているので、綺麗ではないです。メッセージもでたらめ英語です。雰囲気で感じ取っていただければ。importしているhueモジュールのソースは後で説明します。

 あと、先にgcloud initで認証をしておく必要があります。

[python title=”grecreate.py”]
# coding: UTF-8
#
# Python3
# Python命名規則
# http://works.surgo.jp/translation/pyguide.html

import sys
from oauth2client.client import GoogleCredentials
from googleapiclient.discovery import build
import json
import time
from datetime import datetime
from datetime import date
import hue

credentials = GoogleCredentials.get_application_default()
compute = build(‘compute’, ‘v1’, credentials=credentials)
project = ‘プロジェクト名’
zone = ‘ゾーン名’
ins_group_name = ‘インスタンスグループ名’
backend = ‘バックエンド名’
#最小のインスタンス数
minmum_ins_num = 1
#ウェイト(秒)
wait = 30
#インスタンス再生成後ウェイト(breakですぐに抜けるので)
recreate_wait = 30

def main():

#引数取得
param = sys.argv
if len(param)<=1:
print (‘ex. grecreate [target template-name]’)
print (‘InstanceTemplate List:’)
for tmpl in getTemplateList():
print (tmpl)
sys.exit()

target_template = param[1]
print (‘Target Template Name is ‘ + param[1])

start_time = datetime.now().isoformat()

all_clear = False

while all_clear == False:

#インスタンスリスト取得
instances = getInstanceList()

all_clear = True

for ins in instances:
#テンプレート名取得
tmpl = getInstanceTemplate(ins)
if tmpl is None:
#テンプレート名がとれなかった場合は次のインスタンスへスキップ
print (‘Can not get instance-template name’)
all_clear = False
continue

#古いテンプレートか?
if tmpl != target_template:
print (‘Found recreate target instance : ‘ + ins)
#古いテンプレートで動いているインスタンスが見つかったらフラグ立てる
all_clear = False
#有効なノードが複数あるか?
status = getHttpLoadBalance()
healthy_count = 0
print (‘Instance Health Stats:’)
for s in status:
print (‘ ‘ + s[‘instance’] + ‘ : ‘ + s[‘healthState’])
if s[‘healthState’] == ‘HEALTHY’:
healthy_count += 1

if healthy_count > minmum_ins_num:
#インスタンス再生成
print (‘Instance ‘ + ins + ‘ recreate!’)

recreateInstance(ins)

#ヘルスチェックの状態がすぐに変わらないことも考えられるためしばらく待つ
print (‘Instance recreate after wait ‘ + str(recreate_wait) + ‘ second’)
time.sleep(recreate_wait)
else:
print (‘A few HEALTHY instance : ‘ + str(healthy_count) + ‘ healthy instance. needs ‘ + str(minmum_ins_num + 1) + ‘ instance.’)
#HEALTYが少なければインスタンスのループ繰り返しても意味がないので抜ける
break
if all_clear == False:
#スリープ
print (‘wait ‘ + str(wait) + ‘ second.’)
time.sleep(wait)

print (‘Recreate Complete!’)
print (‘Start Time :’ + start_time)
print (‘End Time : ‘ + datetime.now().isoformat())

hue.Hue.postAlert()

#インスタンス再生成
def recreateInstance(instance):
body = {
‘instances’ : [
‘https://content.googleapis.com/compute/v1/projects/’ + project+ ‘/zones/’ + zone + ‘/instances/’ + instance
]
}
res = compute.instanceGroupManagers().recreateInstances(project=project, zone=zone, instanceGroupManager=ins_group_name, body=body).execute()
print (json.dumps(res, sort_keys=True, indent=4))

#HTTP負荷分散の状況を取得
def getHttpLoadBalance():
body = {
‘group’ : ‘https://content.googleapis.com/compute/v1/projects/’ + project+ ‘/zones/’ + zone + ‘/instanceGroups/’ + ins_group_name
}
res = compute.backendServices().getHealth(project=project, backendService=backend, fields=’healthStatus’, body=body).execute()
status = []
for health in res[‘healthStatus’]:
#/で区切って11個目を取得。かなり無理矢理感が。
row = {‘instance’:health[‘instance’].split(‘/’)[10], ‘healthState’:health[‘healthState’]}
status.append(row)
return status

#インスタンスのリストを取得する
#インスタンスグループの名前から対象のインスタンスを絞り込んでいる
def getInstanceList():
# インスタンスリスト取得
res = compute.instances().list(project=project, zone=zone, filter=’name eq ‘ + ins_group_name + ‘-.*’).execute()
instances = []
for ins in res[‘items’]:
ins_name = ins[‘name’]
instances.append(ins_name)

return instances

#指定したインスタンスのインスタンスをテンプレート取得する
def getInstanceTemplate(ins_name):
# インスタンステンプレート取得
res = compute.instances().get(project=project, zone=zone, instance=ins_name).execute()
for tmpl_name in res[‘metadata’][‘items’]:
if tmpl_name[‘key’] == ‘instance-template’:
strs = tmpl_name[‘value’].split(‘/’)
return strs[4]
print (‘instace-template not foud : instance name is ‘ + ins_name)
return None

#定義済みのインスタンステンプレートリストを取得する
def getTemplateList():
res = compute.instanceTemplates().list(project=project).execute()
templates = []
for tmpl in res[‘items’]:
tmpl_name = tmpl[‘name’]
templates.append(tmpl_name)
return templates

#main呼び出し
if __name__ == "__main__":
main()
[/python]

 もっとどうにかしたい感じの複雑さですが動いているのでよしとします。

 最初の方の project や zone は環境に合わせて書き換えてください。minmum_ins_num は何個 HEALTHY なインスタンスを残しておくかの設定です。1を指定しておくと、再生成して HEALTHYなインスタンスが1個だけになると再生成をやめます。30秒待ってHEALTHYなインスタンスが2個以上になったら、再生成を再開するような動きをします。

 事前にインスタンスグループのインスタンステンプレートに新しいテンプレートを指定します。

 起動時の引数として、新しいインスタンステンプレート名を指定します。ここで指定したインスタンステンプレート以外を使っているインスタンスは再生成の対象になります。最終的にすべてのインスタンスが指定したテンプレートになったらスクリプトは終了します。

電球を光らせる

 Hueを光らせるスクリプトです。

[python title=”hue.py”]
# coding: UTF-8
#
# Python3
# Python命名規則
# http://works.surgo.jp/translation/pyguide.html

import requests
import json

hue_ip = ‘HueのIPアドレス’
user_id = ‘Hueから取得したユーザーID’
target_light = ‘ライトの番号’

def main():
Hue.postAlert()

class Hue(object):
def postAlert():
url = ‘http://’ + hue_ip + ‘/api/’ + user_id + ‘/lights/’ + target_light + ‘/state’
body = {‘alert’: ‘lselect’}
r = requests.put(url,data=json.dumps(body))
print (‘Response :’), r.text

#main呼び出し
if __name__ == "__main__":
main()
[/python]

 ソース内の「HueのIPアドレス」と「Hueから取得したユーザーID」と「ライトの番号」を書き換えてください。これらについてはHueのチュートリアルを試すと何を設定すればいいかわかります。10分ぐらいで光らせるところまでいけるぐらい簡単です。

Getting started

自動化できると楽になる

 やったー、これで一つめんどくさい手作業がなくなった。かねてよりやってみたかったHueの操作もできたし。Google Compute Engineでクラウドを使い、IoTも絡めてなんだか最先端っぽいじゃないか😀

この記事を書いた人: A-tak

A-tak.com(えいたっく どっとこむ)の管理人。
Apple野郎なおっさんでしたが、ちょっと最近のAppleには飽き気味。
A-tak.comは2002年2月から運営(前身のサイトは1999年3月から)。今年で18年目!

Twitter
Mastodon
Facebook

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください