AWSで構築した環境にありがちなシェルスクリプトたち まとめ
AWSでサーバを運用する際にはEC2からAWS CLIを使って他のAWSのサービスと連携したりすることがあると思いますが、AWS環境ならではのシェルスクリプトを集めてみました。AWS CLIのバージョンは1.7.13、Pythonのバージョンは2.6.9を使っています。私はAmazon Linuxで動作を確認しています。
目次
準備する
AWS CLIのインストール
Amazon Linux以外のOSの場合はAWS CLIをインストールする必要があります。AWS CLIはPythonで実装されているので、まずはPythonをインストールします。Pythonのインストール方法はOSによって違うと思いますので割愛させていただきます。Pythonをインストールしたら以下のコマンドでインストールできます。
$ sudo easy_install pip $ sudo pip install awscli
Macの場合は以下の記事が参考になると思います。
bashを使っている場合はaws_completerを使うとサブコマンドのオートコンプリートされるようになります。Amazon Linuxの場合はこのコマンドを実行しなくてもオートコンプリートが有効になっているようです。
$ complete -C aws_completer aws
AWS CLIのアップデート
AWS CLIをアップデートしたい場合は以下のコマンドを実行します。
$ sudo pip install -U awscli
aws configureでセットアップする
インストールができたらaws configureでアクセスキー、シークレットアクセスキー、リージョン、出力形式を対話形式で設定します。
アクセスキーとシークレットアクセスキーはIAM Roleを選択する場合は入力する必要はありません。リージョンは東京リージョン(ap-northeast-1)にしています。ここでセットアップすればコマンドのオプションで指定する必要はありません。出力形式(output format)はtext、json、table 3種類あります。ここで紹介しているシェルを実行するにはtextを選択する必要があります。
このコマンドを実行すると、~/.aws/ にconfigという名前のファイルが出力されます。
$ aws configure AWS Access Key ID [None]: XXXX AWS Secret Access Key [None]: XXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]: text
IAM roles for EC2 instancesに関して
EC2上でAWS CLIを実行する場合はEC2にIAM Roleを付与すればアクセスキー、シークレットアクセスキーを設定する必要がなくなります。
IAM roles for EC2 instancesを使ってみる
監視系
CloudWatchでカスタムメトリクスを設定する
CloudWatchにLoad Average、Stealなどの項目を追加したい場合はカスタムメトリクスを使います。
メモリ使用率やディスク使用量はAWSのサイトにスクリプトがありますので、そちらをご利用ください。
Amazon CloudWatch Monitoring Scripts for Linux
#!/bin/bash # instance id の取得 INSTANCE_ID=`wget -q -O - http://169.254.169.254/latest/meta-data/instance-id` LOAD_AVERAGE1=`uptime | tr -s ' ' | cut -d ' ' -f 9 | cut -d ',' -f 1` #LOAD_AVERAGE5=`uptime | tr -s ' ' | cut -d ' ' -f 9 | cut -d ',' -f 2` #LOAD_AVERAGE15=`uptime | tr -s ' ' | cut -d ' ' -f 9 | cut -d ',' -f 3` STEAL=`vmstat | tail -1 | tr -s ' ' | cut -d ' ' -f 18` aws cloudwatch put-metric-data --metric-name LoadAverage --namespace System/Linux --value ${LOAD_AVERAGE1} --unit Count --dimensions "InstanceId=${INSTANCE_ID}" aws cloudwatch put-metric-data --metric-name Steal --namespace System/Linux --value ${STEAL} --unit Percent --dimensions "InstanceId=${INSTANCE_ID}"
当ブログにはカスタムメトリクスの記事が他にもいくつかありますのでこちらもご覧ください。
CloudWatchのカスタムメトリクスでJavaVMのGC関連情報を取得
CloudWatchのカスタムメトリクスでFreeMemoryMBytes、UsedMemoryPercent、LoadAverage、Stealを取得
Tomcatの使用メモリ量をCloudWatchで表示する
ZabbixからCloudWatchの値を取得する
システムを監視するためにZabbixを利用することはあると思いますが、ZabbixにはCloudWatchの値を見る機能がありません。 EC2はエージェントを使って値をとることはできるのですが、RDSやELBなどフルマネージドなリソースを監視する場合は以下のようにCloudWatchの値を取得するスクリプトを書く必要があります。 こちらのスクリプトは5分間隔で実行される前提です。
cloudwatch.sh
#!/bin/sh while getopts n:r:d:m:s: OPT do case ${OPT} in n) NAMESPACE=${OPTARG} ;; d) DIMENSIONS=${OPTARG} ;; m) METRIC=${OPTARG} ;; s) STATISTICS=${OPTARG} ;; *) exit 1 ;; esac done aws cloudwatch get-metric-statistics \ --namespace ${NAMESPACE} \ --dimensions ${DIMENSIONS} \ --metric-name ${METRIC} \ --statistics ${STATISTICS} \ --start-time `date -u -d '5 minutes ago' +%Y-%m-%dT%TZ` \ --end-time `date -u +%Y-%m-%dT%TZ` \ --period 300 | sort -k 3,3 | tail -n 1 | awk '{print $2}'
実装したシェルはZabbixの外部チェックで呼び出すのですが、何を監視するかは引数で渡します。2つほど例を示します。値が標準出力に表示されればOKです。
RDSのCPU使用率の平均値を見たい場合
- 名前空間:AWS/RDS
- メトリクス名:CPUUtilization
- ディメンジョン名と値:Name=DBInstanceIdentifier,Value=[インスタンス名]
- 値の算出方法:Average
$ ./cloudwatch.sh -n AWS/RDS -d Name=DBInstanceIdentifier,Value=mydbinstance -m CPUUtilization -s Average
ELBのリクエスト数の合計を見たい場合
- 名前空間:AWS/ELB
- メトリクス名:RequestCount
- ディメンジョン名と値:Name=LoadBalancerName,Value=[ロードバランサー名]
- 値の算出方法:Sum
$ ./cloudwatch.sh -n AWS/ELB -d Name=LoadBalancerName,Value=[ロードバランサー名] -m RequestCount -s Sum
実装したスクリプトをZabbixに設定する方法などは以下のページが参考になると思います。
ZabbixでAWS/CloudWatchの値を取得してみた
名前空間と取得できるメトリクスの一覧などは以下をご覧ください。
Amazon CloudWatch の名前空間、ディメンション、メトリックスのリファレンス
プロセス監視する
CloudWatchではカスタムメトリクスを使うことにより、プロセス監視することも可能です。Apacheのプロセスを監視するスクリプトが以下になります。プロセスがない場合はProcessMonitoring が0になるので0の場合はアラームを送信するように設定します。
#!/bin/sh if ! (ps ax | grep -v grep | grep -q httpd); then VALUE=0 sudo service httpd start else VALUE=1 fi aws cloudwatch put-metric-data --metric-name ProcessMonitoring --namespace Processes --value ${VALUE} --dimensions "Processes=httpd"
プロセス監視の間隔を1分ごとにしたい場合、カスタムメトリクスを使うと有料になってしまいます。
Amazon CloudWatch 料金
コストの面でカスタムメトリクスを使いたくない場合はSNSを使って通知する方法もあります。
#!/bin/sh ACCOUNT="[アカウント]" TOPIC="[トピック]" MESSAGE="[メッセージ]" if ! (ps ax | grep -v grep | grep -q httpd); then sudo service httpd start aws sns publish --topic-arn arn:aws:sns:ap-northeast-1:${ACCOUNT}:${TOPIC} --message ${MESSAGE} fi
バックアップ系
次はバックアップ系になります。バックアップ処理の流れとしては以下のようになります。バックアップ対象の判定やバックアップの削除判定にはタグを利用しています。バックアップしたいEBSボリュームやEC2インスタンスにBackupという名前のタグを追加して値をtrueにして下さい。
- 1.バックアップの作成
- 1-1.バックアップを作成したいリソース一覧を取得する
- 1-2.タグを見てバックアップ対象かどうかチェックする
- 1-3.バックアップを作成する
- 1-4.検索に使うタグを作成する
- 2.古いバックアップの削除
- 2-1.タグを条件にして削除可能なバックアップを検索する
- 2-2.タグを見て削除対象か判定を行い、対象であればバックアップを削除する
AMIとEBSのバックアップを作成する
AMIのEBSとバックアップを作成するスクリプトです。バックアップを取得する目的としてはそれぞれ以下のような目的になります。
- AMIのバックアップ
- インスタンスの障害時やAZ障害時に別AZでEC2を起動したい場合に使います。複数EBSの構成情報も含まれます
- EBSのスナップショット
- データバックアップが目的の場合に使います。
AMIのバックアップの取得は以下になります。create-imageコマンドではno-rebootオプションをつけて再起動しないようにしていますが、no-rebootの場合は整合性は保証されませんので注意して下さい。
AMIのバックアップの取得
#!/bin/sh DATE_CURRENT=`date +%Y-%m-%d` TIME_CURRENT=`date +%Y%m%d%H%M%S` PURGE_AFTER_DAYS=10 PURGE_AFTER=`date -d +${PURGE_AFTER_DAYS}days -u +%Y-%m-%d` # 1-1.バックアップを作成したいリソース一覧を取得する INSTANCES=`aws ec2 describe-tags --filters "Name=resource-type,Values=instance" "Name=key,Values=Backup" | awk '{print $3}'` for INSTANCE in ${INSTANCES}; do BACKUP=`aws ec2 describe-tags --filters "Name=resource-type,Values=instance" "Name=resource-id,Values=${INSTANCE}" "Name=key,Values=Backup" | awk '{print $5}'` # 1-2.タグを見てバックアップ対象かどうかチェックする if [ "${BACKUP}" == "true" ]; then # 1-3.バックアップを作成する AMI_ID=`aws ec2 create-image --instance-id ${INSTANCE} --name "${INSTANCE}_${TIME_CURRENT}" --no-reboot` # 1-4.検索に使うタグを作成する aws ec2 create-tags --resources ${AMI_ID} --tags Key=PurgeAllow,Value=true Key=PurgeAfter,Value=$PURGE_AFTER fi done # 2-1.タグを条件にして削除可能なバックアップを検索する AMI_PURGE_ALLOWED=`aws ec2 describe-tags --filters "Name=resource-type,Values=image" "Name=key,Values=PurgeAllow" | awk '{print $3}'` for AMI_ID in ${AMI_PURGE_ALLOWED}; do PURGE_AFTER_DATE=`aws ec2 describe-tags --filters "Name=resource-type,Values=image" "Name=resource-id,Values=${AMI_ID}" "Name=key,Values=PurgeAfter" | awk '{print $5}'` if [ -n ${PURGE_AFTER_DATE} ]; then DATE_CURRENT_EPOCH=`date -d ${DATE_CURRENT} +%s` PURGE_AFTER_DATE_EPOCH=`date -d ${PURGE_AFTER_DATE} +%s` if [[ ${PURGE_AFTER_DATE_EPOCH} < ${DATE_CURRENT_EPOCH} ]]; then # 2-2.タグを見て削除対象か判定を行い、対象であればバックアップを削除する aws ec2 deregister-image --image-id ${AMI_ID} SNAPSHOT_ID=`aws ec2 describe-images --image-ids ${AMI_ID} | grep EBS | awk '{print $4}'` aws ec2 delete-snapshot --snapshot-id ${SNAPSHOT_ID} fi fi done [/shell] <h4>EBSのスナップショット</h4> <h3 id="rds-snapshot">RDSのスナップショットを作成する</h3> <p> 続いてRDSのスナップショットを取得するスクリプトです。RDSの場合は設定したバックアップ保持期間内であれば自動で取得してくれています。設定できる保持期限は最大で35日前のものになります。<br/> </p> <h3 id="s3-cleanup">S3のフォルダをクリーンアップする</h3> <p> S3のフォルダをクリーンアップするシェルスクリプトです。YYYY-MM-DDなど日付がフォルダ名になっている場合に使えます。 </p> #!/bin/sh BUCKET='[バケット名]' PURGE_AFTER_DAYS=10 PURGE_AFTER=`date +'%Y-%m-%d' --date "${PURGE_AFTER_DAYS} days ago"` TARGET="s3://${BUCKET}/${PURGE_AFTER}/" aws s3 rm ${TARGET} --recursive
この例では10日間でフォルダを削除していますが、一定の期間更新されなかったファイルを削除したり、Glacierに移動するだけでしたらS3のLifecycle設定でできますのでそちらをお勧めします。
Amazon S3でオブジェクトの有効期限を設定できるようになりました
Amazon S3のオブジェクト移行・削除タイミングを調べてみた
Amazon S3のGlacierアーカイブ機能を活用する
便利スクリプト系
Route53の自動登録
開発環境のEC2を利用していない時間に停止すると、起動した際にIPアドレスが変わってしまいます。その都度、IPアドレスを調べるのが面倒な場合は起動時にIPアドレスをRoute53に自動登録するスクリプトを実行すると常に同じドメイン名でアクセスすることができるようになります。
/etc/init.d/route53-register.sh
#!/bin/bash DOMAIN_NAME=“[ドメイン名]” HOST_NAME="[ホスト名]" IP_ADDRESS=`curl -s http://169.254.169.254/latest/meta-data/public-ipv4` HOSTED_ZONE_ID=`aws route53 list-hosted-zones | grep ${DOMAIN_NAME} | awk '{print $3}' | sed -e 's/\/hostedzone\///'` BATCH_JSON='{ "Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "'${HOST_NAME}'.'${DOMAIN_NAME}'", "Type": "A", "TTL" : 60, "ResourceRecords": [ { "Value": "${IP_ADDRESS}" } ] } } ] }' aws route53 change-resource-record-sets --hosted-zone-id ${HOSTED_ZONE_ID} --change-batch "${BATCH_JSON}"
実装できたら起動するスクリプトに登録します。
$ sudo chkconfig --add route53-register $ sudo chkconfig route53-register on
以下の記事を参考にしました。
Jenkinsを導入したEC2インスタンスに常に同じ名前でアクセスする
その他
EC2インスタンスを起動、停止する
バッチ処理などに利用されるEC2インスタンスを特定の曜日、時間帯のみ起動させたい、ということがあると思います。 起動と停止だけであればcrontabコマンドで以下のように指定すれば可能です。
0 7 * * 1-5 aws ec2 start-instances --instance-ids=i-xxxxxxxx 0 22 * * 1-5 aws ec2 stop-instances --instance-ids=i-xxxxxxxx
実装したらやること
作成したシェルスクリプトを cron で実行します。まずは実装したシェルに実行権限を付与します。
$ chmod +x test.sh
次にcrontabコマンドで登録します。
$ crontab -e 0 12 * * * /usr/local/bin/ebs-backup.sh 0 15 * * * /usr/local/bin/custom-metrics.sh
最後に
自分はシェルスクリプトがあまり得意ではないので文法から調べながら記事を書いていたので、書き方とかあまりうまくないところがあるかもしれませんが勉強してリファクタリングする予定です。私はまだAWSの全部のサービスを使ったことがないので、まだ”あるある”なスクリプトがあると思います。見つけ次第どんどん追加していきたいです!
リンク集
シェルスクリプトではないのですが、便利そうなRubyスクリプトがあったのでリンクをまとめておきます。
5秒でAmazon Linuxを起動するRubyスクリプト書いた
5秒でAmazon Linuxを起動して1日で殺すRubyスクリプトを書いた
新規アカウントでもこれ一発!CloudTrailを全リージョンで有効化するスクリプトを書いた