こちらは、 “ゆるWeb勉強会@札幌 Advent Calendar 2019” の 14日目の記事です。
(本当は作成中の自分のサイトに載せたかったのですが、ヒジョーに見づらかったのでこちらに載せておきます...)
何を調べたか
今回は、「AWSでオペミスせずに(汗)想定のサーバーを停止する」方法を調べてみました。
背景
AWS マネジメントコンソールから検証サーバーを停止しようとして、間違えて本番サーバーを停止してしまいました。 (操作対象のチェックが複数入っているのに気づかず、停止してしまいました)
気づいたときには遅く、検証サーバーと本番サーバーが仲良く停止するのをボーゼンと見守るしかありませんでした…
できれば、停止前には「こちらのサーバーを停止しますが宜しいですか?」と確認するワンクッションが欲しい。 ということで探してみたくなりました。
結論
- (2019/12/13現在では)AWS マネジメントコンソールの操作にワンクッション挟むことは出来ない
- AWS コマンドラインインターフェイス(CLI)でバッチを作れば可能
※監視であれば「CloudWatch」、処理フローなら「Simple Workflow Service(SWF)」などが使えそうですが、イベントをトリガーにするため、イベント前に何かするのは難しいようです。
オペミスは想定されていないっ(ううう…)
作成したもの
WindowsのバッチファイルからAWS CLIのサーバー停止コマンドを呼び出すようにしました。
手順概要
AWS CLIは未使用で、インストールから行う場合です。
使用OS:Windows10
使用エディタ:Visual Studio Code
- IAMユーザーの作成
- CLI用IAMユーザーを作成
- IAMユーザーに権限付与
- AWS CLI操作用アクセスキーの取得
- AWS EC2で各サーバにタグを付ける
- AWS CLIのインストール
- AWS CLIの初期設定
- AWS CLIの実行確認
- AWSに接続
- EC2(サーバー)一覧の取得
- EC2停止コマンドの実行
- EC2起動コマンドの実行
- バッチの作成
詳細手順
IAMユーザーの作成
※もし、EC2インスタンス停止などに使用するユーザーが作成済でしたら、新規作成しなくても良いと思います。 rootアカウントしか無い!という人は、操作用のIAMユーザーを作りましょう。
IAMユーザーの作り方は公式サイトを参照してください。
アクセス権限のポリシーは、「AmazonEC2FullAccess」を付けておきます。(今回だけならフルには不要なのですが)
IAMユーザーのアクセスキー、シークレットアクセスキーは後ほど設定で使用するため、メモしておきます。
AWS EC2で各サーバにタグを付ける
本番サーバー、検証サーバーが識別出来るようにタグを付けます。
詳しくは「Amazon EC2 リソースにタグを付ける」をご確認ください。
私は以下のようなタグを付けました。これで区別しようと思います。
対象 | タグ名 | 値 |
---|---|---|
本番サーバー | Application | Production |
検証サーバー | Application | Staging |
AWS CLIのインストール
AWS CLIにはバージョン1と2(評価版)があります。今回はバージョン1をインストールします。
Windowsの方はこちら
その他の方はAWS CLI バージョン 1 のインストールをどうぞ。
インストーラで実行した場合、パス設定がされる場合とされない場合があるようです。環境変数の設定方法はこちらを参照してください。
AWS CLIがインストールされたか確認します。コマンドプロンプトで以下を実行します。バージョンが表示されればインストールされています。
Visual Studio Codeの方は、ctrl+shift+@でターミナルが開き、そこでコマンドの実行が出来て便利です。
> aws --version
aws-cli/1.16.302 Python/3.6.0 Windows/10 botocore/1.13.38
AWS CLIの初期設定
先程メモしておいたアクセスキー、シークレットアクセスキーを用意します。
シャットダウンしたいEC2インスタンスが存在するリージョンが分からない方は、一旦マネジメントコンソールにログイン→EC2ダッシュボードに移動→右上のリージョンを確認してみてください。
リージョンが「東京」の場合は「ap-northeast-1」です。
その他のリージョンの確認はこちら
> aws configure
AWS Access Key ID [None]: ここにIAMユーザーのアクセスキー
AWS Secret Access Key [None]: ここにシークレットアクセスキー
Default region name [None]: (東京の場合ap-northeast-1)
Default output format [None]:出力するデータフォーマット(json/text/tableから選択。デフォルトはjson)
設定されたか確認するには、aws configure list
を入力します。
> aws configure list
Name Value Type Location
---- ----- ---- --------
profile None None
access_key ****************PPUK shared-credentials-file
secret_key ****************h5n8 shared-credentials-file
region ap-northeast-1 config-file ~/.aws/config
AWS CLIの実行確認
コマンド構造としては以下になっているようです。EC2の操作の場合は、aws ec2 操作 オプション
のようになります。
aws command subcommand [options and parameters]
ヘルプが見たい場合は、サービスやコマンド毎に確認できます。
aws ec2 help
aws ec2 describe-instances help
EC2(サーバー)一覧の取得
まずは設定した内容でAWSに接続できるか確認します。
aws ec2 describe-instances
> aws ec2 describe-instances
{
"Reservations": [
{
"Groups": [],
"Instances": [
{
"AmiLaunchIndex": 0,
"ImageId": "ami-3a5a415d",
"InstanceId": "i-09cc5014c75905b8f",
"InstanceType": "t2.micro",
"KeyName": "XXX",
"LaunchTime": "2019-12-10T13:54:36.000Z",
"Monitoring": {
"State": "disabled"
},
"Placement": {
"AvailabilityZone": "ap-northeast-1c",
"GroupName": "",
"Tenancy": "default"
},
"PrivateDnsName": "ip-XXX-XXX-XXX-XXX.ap-northeast-1.compute.internal",
"PrivateIpAddress": "XXX.XXX.XXX.XXX",
"ProductCodes": [
{
"ProductCodeId": "f18wc0igqjhsxwoxouogwqb8m",
"ProductCodeType": "marketplace"
}
],
"PublicDnsName": "",
"PublicIpAddress": "XXX.XXX.XXX.XXX",
"State": {
"Code": 16,
"Name": "running"
},
"StateTransitionReason": "",
"SubnetId": "subnet-01db2ef783448de05",
"VpcId": "vpc-0e58a4cff354769c7",
"Architecture": "x86_64",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/sda1",
"Ebs": {
"AttachTime": "2019-11-24T10:20:43.000Z",
"DeleteOnTermination": true,
"Status": "attached",
"VolumeId": "vol-0e8ea73e857cc1c26"
}
}
],
"ClientToken": "157459081408640974",
"EbsOptimized": false,
"EnaSupport": false,
"Hypervisor": "xen",
"NetworkInterfaces": [
{
"Association": {
"IpOwnerId": "688747735680",
"PublicDnsName": "",
"PublicIp": "XXX.XXX.XXX.XXX"
},
"Attachment": {
"AttachTime": "2019-11-24T10:20:42.000Z",
"AttachmentId": "eni-attach-0e83f3e818c31d244",
"DeleteOnTermination": true,
"DeviceIndex": 0,
"Status": "attached"
},
"Description": "Primary network interface",
"Groups": [
{
"GroupName": "wp-sg",
"GroupId": "sg-0cab6793bf2ff78b9"
}
],
"Ipv6Addresses": [],
"MacAddress": "0a:a3:df:b1:34:28",
"NetworkInterfaceId": "eni-01f4f73c21aab0a27",
"OwnerId": "688747735680",
"PrivateIpAddress": "XXX.XXX.XXX.XXX",
"PrivateIpAddresses": [
{
"Association": {
"IpOwnerId": "688747735680",
"PublicDnsName": "",
"PublicIp": "XXX.XXX.XXX.XXX"
},
"Primary": true,
"PrivateIpAddress": "XXX.XXX.XXX.XXX"
}
],
"SourceDestCheck": true,
"Status": "in-use",
"SubnetId": "subnet-01db2ef783448de05",
"VpcId": "vpc-0e58a4cff354769c7",
"InterfaceType": "interface"
}
],
"RootDeviceName": "/dev/sda1",
"RootDeviceType": "ebs",
"SecurityGroups": [
{
"GroupName": "wp-sg",
"GroupId": "sg-0cab6793bf2ff78b9"
}
],
"SourceDestCheck": true,
"Tags": [
{
"Key": "Application",
"Value": "Production"
},
{
"Key": "Name",
"Value": "wp-test"
}
],
"VirtualizationType": "hvm",
"CpuOptions": {
"CoreCount": 1,
"ThreadsPerCore": 1
},
"CapacityReservationSpecification": {
"CapacityReservationPreference": "open"
},
"HibernationOptions": {
"Configured": false
},
"MetadataOptions": {
"State": "applied",
"HttpTokens": "optional",
"HttpPutResponseHopLimit": 1,
"HttpEndpoint": "enabled"
}
}
],
"OwnerId": "688747735680",
"RequesterId": "086189789714",
"ReservationId": "r-05b6d9b7554d0a378"
},
{
"Groups": [],
"Instances": [
{
(2台目以降省略)
}
}
]
}
たくさん情報が出てきて見づらいです…必要な情報だけ表示したいので、絞り込みをします。
私はIPやNameタグで判断する事が多いので、後ほど使用するインスタンスID、IP、Nameタグを取得してみました。
> aws ec2 describe-instances --filters 'Name=tag:Application,Values=Staging' --query 'Reservations[].Instances[].{PublicIp:PublicIpAddress,InstanceId:InstanceId,Name:Tags[?Key==`Name`].Value}'
[
{
"PublicIp": "XXX.XXX.XXX.XXX",
"InstanceId": "i-0c65cca2d2fbfb1db",
"Name": [
"wp-site-stg"
]
}
]
EC2停止コマンドの実行
停止する際のコマンドを試してみます。先程確認したインスタンスIDの情報が必要になります。
コマンドリファレンス:stop-instances
aws ec2 stop-instances --instance-ids インスタンスID
実行結果のイメージはこちらです。
> aws ec2 stop-instances --instance-ids i-0c65cca2d2fbfb1db
{
"StoppingInstances": [
{
"CurrentState": {
"Code": 64,
"Name": "stopping"
},
"InstanceId": "i-0c65cca2d2fbfb1db",
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
画面上でも停止されていました。
![result_img2-min.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177998/f768acfa-a3f3-d0d4-0d84-b91d93f7b162.png)コマンドでも確認できるようです。以下コマンドでInstanceStatusesの値が空なら停止しています。
aws ec2 describe-instance-status --instance-ids インスタンスID
//停止したインスタンス
> aws ec2 describe-instance-status --instance-ids i-0c65cca2d2fbfb1db
{
"InstanceStatuses": []
}
//実行中のインスタンス
> aws ec2 describe-instance-status --instance-ids i-09cc5014c75905b8f
{
"InstanceStatuses": [
{
"AvailabilityZone": "ap-northeast-1c",
"InstanceId": "i-09cc5014c75905b8f",
"InstanceState": {
"Code": 16,
"Name": "running"
},
"InstanceStatus": {
"Details": [
{
"Name": "reachability",
"Status": "passed"
}
],
"Status": "ok"
},
"SystemStatus": {
"Details": [
{
"Name": "reachability",
"Status": "passed"
}
],
"Status": "ok"
}
}
]
}
EC2起動コマンドの実行
ついでに起動コマンドも確認しておきます。こちらもインスタンスIDが必要です。
コマンドリファレンス:start-instances
aws ec2 start-instances --instance-ids インスタンスID
> aws ec2 start-instances --instance-ids i-0c65cca2d2fbfb1db
{
"StartingInstances": [
{
"CurrentState": {
"Code": 0,
"Name": "pending"
},
"InstanceId": "i-0c65cca2d2fbfb1db",
"PreviousState": {
"Code": 80,
"Name": "stopped"
}
}
]
}
コマンド実行直後にステータスを確認してみました。
> aws ec2 describe-instance-status --instance-ids i-0c65cca2d2fbfb1db
{
"InstanceStatuses": [
{
"AvailabilityZone": "ap-northeast-1c",
"InstanceId": "i-0c65cca2d2fbfb1db",
"InstanceState": {
"Code": 16,
"Name": "running"
},
"InstanceStatus": {
"Details": [
{
"Name": "reachability",
"Status": "initializing"
}
],
"Status": "initializing"
},
"SystemStatus": {
"Details": [
{
"Name": "reachability",
"Status": "initializing"
}
],
"Status": "initializing"
}
}
]
}
起動後のステータスはこちらです。
> aws ec2 describe-instance-status --instance-ids i-0c65cca2d2fbfb1db
{
"InstanceStatuses": [
{
"AvailabilityZone": "ap-northeast-1c",
"InstanceId": "i-0c65cca2d2fbfb1db",
"InstanceState": {
"Code": 16,
"Name": "running"
},
"InstanceStatus": {
"Details": [
{
"Name": "reachability",
"Status": "passed"
}
],
"Status": "ok"
},
"SystemStatus": {
"Details": [
{
"Name": "reachability",
"Status": "passed"
}
],
"Status": "ok"
}
}
]
}
バッチの作成
これで一通りコマンドの確認が出来たので、あとはバッチを作成していきます。
stop_stg.batなど適当な名前で、実行するPCに保存しておきます。
バッチでは、検証のインスタンス情報を表示し、停止するか確認メッセージを表示します。
はい、と答えたあと、コレジャナイ!!を予防するため、再確認もします(笑)。
@echo off
@SET AWS_ACCESS_KEY_ID=アクセスキー
@SET AWS_SECRET_ACCESS_KEY=シークレットアクセスキー
@SET AWS_DEFAULT_REGION=リージョン(東京はap-northeast-1)
@SET TARGET_VM=画面に表示するサーバー名
@SET TARGET_VM_ID=インスタンスID
echo 停止するサーバ情報です。
echo.
aws ec2 describe-instances --filters Name=tag:Application,Values=Staging --query Reservations[].Instances[].{PublicIp:PublicIpAddress,InstanceId:InstanceId,Name:Tags[?Key==`Name`].Value}
echo.
echo %TARGET_VM% サーバを停止しますか?
choice
if %errorlevel% equ 1 goto okexe
echo.
echo 実行しません…。
pause
exit
:okexe
echo.
echo 本当に実行しますか?
choice /c yn /t 5 /d n
if %errorlevel% equ 1 goto okok
echo.
echo 実行をキャンセルしました。
pause
exit
:okok
echo.
echo 実行します!
rem ここにサーバ停止コマンド
echo aws ec2 stop-instances --instance-ids %TARGET_VM_ID%
aws ec2 stop-instances --instance-ids %TARGET_VM_ID%
echo.
pause
exit
作成中の失敗1:確認したコマンドがバッチでは動かない?
Visual Studio Codeではターミナルを「PowerShell」にしており、コマンドプロンプトで実行するとエラーになりました。
以下を修正すると動作するようになりました…
#PowerShellで実行可能
aws ec2 describe-instances --filters 'Name=tag:Application,Values=Staging' --query 'Reservations[].Instances[].{PublicIp:PublicIpAddress,InstanceId:InstanceId,Name:Tags[?Key==`Name`].Value}'
#コマンドプロンプトで実行可能
aws ec2 describe-instances --filters Name=tag:Application,Values=Staging --query Reservations[].Instances[].{PublicIp:PublicIpAddress,InstanceId:InstanceId,Name:Tags[?Key==`Name`].Value}
</code>
作成中の失敗2:認証エラーが出る
バッチを試す際、以下のエラーが出ました…
An error occurred (AuthFailure) when calling the DescribeInstances operation: AWS was not able to validate the provided access credentials
原因は、変数の値を""で囲っていたためでした。""を外すと動作しました。
#NG
@SET AWS_ACCESS_KEY_ID="アクセスキー"
@SET AWS_SECRET_ACCESS_KEY="シークレットアクセスキー"
#OK
@SET AWS_ACCESS_KEY_ID=アクセスキー
@SET AWS_SECRET_ACCESS_KEY=o3ZvV3uAGi4WxDOs7kkYG2P45EfMgaH0nXMpRFyZ
実行結果
いろいろ試行錯誤しつつ、無事検証サーバーを停止することが出来ました!
![result_img.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177998/a0d91d45-d5ca-1e1f-ace5-8688125d3183.png)ちゃんと停止しています…!
![result_img2-min.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177998/60ca2e92-8e05-e608-64ff-c58c029d1eb5.png)次のステップ(野望)
次は以下も試してみたいです。
- アクセスキーなどはベタ書きせず、ファイル読み込みにする(お恥ずかしい…)
- バッチの中で、停止サーバーの指定が出来るようにしたい
- AWSの別アカウントで管理されている場合の制御(AWS CLIに複数設定)も試したい
所感
いずれ使ってみたいと思っていたAWS CLI、オペミスにより使う機会が出来ました。(怪我の功名?)
色々操作が出来て良いですね。マウスなどを動かす手間が無くて便利です。
停止サーバーの選択が出来るようにすると、自由度は上がるものの、またオペミスするかもしれませんね…。どうしたらオペミスを無くせるのかしら。
もしオペミスを予防出来る素敵な案をお持ちの方はぜひ教えてください。お待ちしています !!!!!
P.S.明日はgishi_yamaさんの「ゆるくWeb開発環境(物理)について話します」です。楽しみで震えますね!(物理)