ジークレストでは、AWSを利用してアプリを開発しております。
今回は、AWSのAutoScalingで追加したインスタンスが不要になった際に、
監視対象から自動で除外させる方法をご紹介します。
監視対象の除外を自動化させることで誤検知や監視コストの無駄を減らします。
サーバ監視ツールは、mackarel(https://mackerel.io/ja/) を使っています。
Mackerelでは、一度登録されたホストやインスタンスを管理対象から除外することを
「退役」と呼んでいます。ここではその退役方法について説明します。
Mackerelには、OSシャットダウンが実行されると
自動退役されるオプションが標準で用意されています。
参考にされる方はこちら(https://mackerel.io/ja/docs/entry/howto/auto-scaling)
ただし、自動退役設定オプションをONにしても退役されない場合があります。
以下のOSシャットダウンが正常に実行されないままインスタンスが削除される場合です。
・「高負荷のためハングアップしたのでインスタンスを削除をした。」
・「AutoScalingのスケールアップ時にインスタンス追加が途中で失敗した。」
そこで、確実に退役させたいため、今回は独自のスクリプトとして、
python + AWS Lambdaによる方法で対応することにします。
# 新規設定謗法について
## 構成ファイル
└── mackerel-retire
├── mackerel_retire.py
└── lib/
## mackerel_retire.py
Lambda関数として実行されるスクリプトをmackerel_retire.pyとして記述します。
(MACKEREL_APIKEY)は、それぞれの環境によって異なります。
lambda_handlerで、Mackrelのホストと、ec2のインスタンスとで比較を行い
一致しなかった場合はMackrelから退役させる処理を行っています。
# -*- encoding: utf-8 -*-
import logging
import json
import requests
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
MACKEREL_APIKEY = '(MACKEREL_APIKEY)'
URL = 'https://mackerel.io/api/v0/hosts'
ec2 = boto3.client('ec2')
# mackerel_hostの取得
def get_mackerel_host():
response = requests.get(URL, headers={'X-Api-Key':MACKEREL_APIKEY})
mackerel = response.json()
mackerel_host = {}
for i, host in enumerate(mackerel['hosts']):
id_length = host['customIdentifier'].find('.')
instance_id = host['customIdentifier'][0:id_length]
mackerel_id = host['id']
mackerel_host[i] = {'instance_id': instance_id, 'mackerel_id':mackerel_id}
return mackerel_host
# ec2のインスタンスの取得
def get_ec2_instance():
ec2_instance = []
instances = ec2.describe_instances(Filters=[{'Name':'instance-state-name', 'Values':['running','stopped']}])
for instance in instances['Reservations']:
ec2_instance.append(instance['Instances'][0]['InstanceId'])
return ec2_instance
# mackerel_hostと、ec2のインスタンスの比較処理
def comparison(mackerel, ec2_instance):
for i in mackerel:
id = mackerel[i]['instance_id']
if not id in ec2_instance:
retire(mackerel[i]['mackerel_id'])
logger.info("retired host: " + mackerel[i]['mackerel_id'])
# 特定のhostのmackerelの退役を行う
def retire(host_id):
retire_url = URL + '/' + host_id + '/retire'
result = requests.post(retire_url, json.dumps({}), headers={'X-Api-Key':MACKEREL_APIKEY, 'Content-Type':
'application/json'})
# Lambda 関数を作成するときにハンドラーを指定する
# コードを実行する際に AWS Lambda が呼び出すコード内の関数を記述する
def lambda_handler(event, context):
comparison(get_mackerel_host(), get_ec2_instance())
## デプロイパッケージ作成: mackerel-retire.zip
### Libraryをインストールする
pip install requests -t .
pip install boto3 -t .
実際に、インストールされるパッケージは、以下の通りになります。
boto3-xyz.dist-info
botocore
botocore-xyz.dist-info
certifi
certifi-xyz.dist-info
chardet
chardet-xyz.dist-info
dateutil
docutils
docutils-xyz.dist-info
idna
idna-xyz.dist-info
jmespath
jmespath-xyz.dist-info
python_dateutil-xyz.dist-info
requests
requests-xyz.dist-info
s3transfer
s3transfer-xyz.dist-info
six-xyz.dist-info
urllib3
urllib3-xyz.dist-info
### デプロイパッケージ作成
Lambdaで関数として実行するためのデプロイパッケージを作成します。
zip -r /home/ec2-user/proj-tools/lambda-functions/mackerel-retire/mackerel-retire.zip *
cd /home/ec2-user/proj-tools/lambda-functions/mackerel-retire
zip -g mackerel-retire.zip mackerel_retire.py
## Lambdaの実行ロールポリシー作成:
Lambda関数を作成する場合、実行ロールとして1個のIAMロールを必ず指定します。
ここでは、AllowLambdaAssumeRole.json に以下のように記述しています。
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
## LambdaのIAMロール作成: MackerelRetireRole
Lambda関数の実行ロールとしてIAMロールを作成します。
IAMロール名:MackerelRetireRoleとして、先ほど作成したjsonを実行ロールポリシーとして指定します。
--role-name MackerelRetireRole \
--assume-role-policy-document file://AllowLambdaAssumeRole.json
## Lambda実行ログ出力のアクセスポリシーをアタッチ
Lambda関数の実行ログが出力されるように作成したロールに対して以下のように適用します。
--role-name MackerelRetireRole \
--policy-arn arn:aws:iam::aws:policy/AWSLambdaExecute
## ec2読み取りのアクセスポリシーをアタッチ
また作成したロールにec2読み取り用ののポリシーを付与します。
FullAccess権限を付与しないように注意が必要です。
--role-name MackerelRetireRole \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess
## デプロイバッケージのアップロード
デプロイパッケージをアップロードしてLambda関数として定義します。
--function-name mackerel_retire \
--runtime python3.6 \
--role arn:aws:iam::(AWS_ACCOUNT_ID):role/MackerelRetireRole \
--handler mackerel_retire.lambda_handler \
--zip-file fileb://mackerel-retire.zip
## lambda:mackerel_reitireのトリガーイベントを作成(時間はUTC)
定義したLambda関数が実行されるタイミングをトリガーとして起動するように指定します。
--name "mackerel-retire" \
--schedule-expression "cron(0 1 * * ? *)"
## Lambda:mackerel_retireに権限の設定
作成したLambda関数に対して、以下のように権限の設定を行います。
--function-name mackerel_retire \
--statement-id mackerel-retire \
--action 'lambda:InvokeFunction' \
--principal events.amazonaws.com \
--source-arn arn:aws:events:ap-northeast-1:(AWS_ACCOUNT_ID):rule/mackerel-retire
## rule:mackerel-retireのターゲットとしてlambda:mackerel_retireを指定
最後にルールとしてmackrel-retireを設定します。
これでトリガーイベントとしてmackerel-retireのLambda関数が実行されるようになります。
--rule mackerel-retire \
--targets "Id"="1","Arn"="arn:aws:lambda:ap-northeast-1:(AWS_ACCOUNT_ID):function:mackerel_retire"
# 更新方法について
既に設定されてあるLambda関数を更新したい場合は次の方法で更新することができます。
## デプロイパッケージ作成: mackerel-retire.zip
### デプロイパッケージ作成
更新したスクリプト(mackerel_retire.py)をzip形式に圧縮します。
zip -r /home/ec2-user/proj-tools/lambda-functions/mackerel-retire/mackerel-retire.zip *
cd /home/ec2-user/proj-tools/lambda-functions/mackerel-retire
zip -g mackerel-retire.zip mackerel_retire.py
## Lambdaファンクションアップデート
更新したzipファイルをupdate-function-codeを指定して、Lambda関数の更新を行います。
--function-name mackerel_retire \
--zip-file fileb://mackerel-retire.zip \
--publish
## lambda:mackerel_reitireのトリガーイベントを更新(時間はUTC)
トリガーイベントのスケジュールを次のように指定することでLambda関数の更新を行います。
--name "mackerel-retire" \
--schedule-expression "cron(0 2 * * ? *)"
# まとめ
今回は、python + AWS Lambda を利用した構成で運用効率化の事例を紹介しました。
自動化には、例外が発生してしまうケースも多く一部手動が残ったりすることも多いです。
今回は独自スクリプトで対応できましたが、自動化することに固執しすぎずに
「合理的な運用か」「効率がアップするか」で対応範囲を見極めると良いと思います。
ジークレストでは、このような運用の効率化を積極的に取り組むことで
アプリ開発により集中できるよう取り組んでいます。