boto3を使ってEC2のインスタンス を立ち上げるところまではできるようになった。次は、立ち上げたインスタンス に対してコマンドを発行して、EC2インスタンス に任意の操作を実行できるようにする。
boto3経由でEC2インスタンス を操作するためには、SSMという機能を使用する必要があるようだ。
SSMというのはAmazon Systems Manager Agentの略称らしく、AWS のAPI を使用してEC2インスタンス 内の制御を行うための環境らしい。ちなみに、Amazon Linux , Amazon Linux 2, Ubuntu 18.04 LTS, Ubuntu 16.04 LTSのAMIならば標準でインストールされている。
ここではAmazon Linux2を使うのでSSMは標準でインストールされているのだが、これを使うためにはSSMのRoleを有効にする必要があるようだ。
つまり、単純にboto3で、
instances = ec2_resouce.create_instances(ImageId='ami-0c3fd0f5d33134a76' ,
MaxCount=1 , MinCount=1 ,
InstanceType='t2.micro' ,
)
としてAWS を立ち上げてコマンドを発行しても動作する訳ではないようだ。実際、何度もSendCommandを実行してエラーが帰ってきてしまい、なぜなのか原因が分からずしばらく悩んでしまった。
AWS のIAMのページで新しいロールの追加設定を行い、SSMに対するFull Access を行うためのロールを追加した。
AmazonSSMFullAccessのためのロールであるSSMAccessを追加する
そして、boto3でEC2インスタンス を作成する際にこのロールを追加する。
instances = ec2_resouce.create_instances(ImageId='ami-0c3fd0f5d33134a76' ,
MaxCount=1 , MinCount=1 ,
InstanceType='t2.micro' ,
IamInstanceProfile={'Name' : 'SSM_Access' },
InstanceMarketOptions=instance_market_options
)
これでSSMアクセス付きのインスタンス の作成が確認できた。SSMで制御できるインスタンス が存在しているかどうかは、AWS のCLI コマンドで以下のようにして確認できる。
aws --region ap-northeast-1 ssm describe-instance-information --output text
INSTANCEINFORMATIONLIST 2.3.372.0 ip-172-31-14-230.ap-northeast-1.compute.internal 172.31.14.230 i-063a1f747b3dc1c74 False 1566119111.05 Online Amazon Linux Linux 2 EC2Instance
aws ssm describe-instance-information --instance-information-filter-list key =PingStatus,valueSet =Online
{
"InstanceInformationList": [
{
"IsLatestVersion": false,
"ComputerName": "ip-172-31-14-230.ap-northeast-1.compute.internal",
"PingStatus": "Online",
"InstanceId": "i-063a1f747b3dc1c74",
"IPAddress": "172.31.14.230",
"ResourceType": "EC2Instance",
"AgentVersion": "2.3.372.0",
"PlatformVersion": "2",
"PlatformName": "Amazon Linux",
"PlatformType": "Linux",
"LastPingDateTime": 1566119111.049
}
]
}
これでコマンドを流し込むことができるようになる。
ssm_client = boto3.client('ssm' )
response = ssm_client.send_command(
DocumentName="AWS-RunShellScript" ,
Parameters={'commands' : ['ls -lt /' , 'df' ]},
InstanceIds=[instance_id],
)
time.sleep(5.0 )
command_id = response['Command' ]['CommandId' ]
output = ssm_client.get_command_invocation(
CommandId=command_id,
InstanceId=instance_id,
)
print ("Output = \n {} \n " .format(output['StandardOutputContent' ]))
print ("Error = \n {} \n " .format(output['StandardErrorContent' ]))
time.sleep(5.0)
としているのは、コマンドを実行してからし ばらく待った方が安定するため。
結果として以下のような出力が得られた。コマンドはちゃんと実行されているらしい。
Launching EC2..
Instance ID = i-00d91f32fea43875f
Waiting EC2 Launch ...
EC2 Launch Finished ...
Output =
total 16
drwxr-xr-x 27 root root 960 Aug 18 09:15 run
drwxr-xr-x 80 root root 8192 Aug 18 09:14 etc
drwxrwxrwt 9 root root 301 Aug 18 09:14 tmp
drwxr-xr-x 4 root root 38 Aug 18 09:14 home
dr-xr-x--- 3 root root 103 Aug 18 09:14 root
drwxr-xr-x 15 root root 2820 Aug 18 09:14 dev
drwxr-xr-x 19 root root 269 Aug 18 09:14 var
dr-xr-xr-x 13 root root 0 Aug 18 09:14 sys
dr-xr-xr-x 109 root root 0 Aug 18 09:14 proc
dr-xr-xr-x 4 root root 4096 Jun 18 22:24 boot
drwxr-xr-x 4 root root 27 Jun 18 22:24 opt
drwxr-xr-x 13 root root 155 Jun 18 22:23 usr
lrwxrwxrwx 1 root root 7 Jun 18 22:23 bin -> usr/bin
lrwxrwxrwx 1 root root 7 Jun 18 22:23 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Jun 18 22:23 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 8 Jun 18 22:23 sbin -> usr/sbin
drwxr-xr-x 2 root root 6 Jun 18 22:23 local
drwxr-xr-x 2 root root 6 Apr 9 19:57 media
drwxr-xr-x 2 root root 6 Apr 9 19:57 mnt
drwxr-xr-x 2 root root 6 Apr 9 19:57 srv
Filesystem 1K-blocks Used Available Use% Mounted on
devtmpfs 485712 0 485712 0% /dev
tmpfs 503664 0 503664 0% /dev/shm
tmpfs 503664 348 503316 1% /run
tmpfs 503664 0 503664 0% /sys/fs/cgroup
/dev/xvda1 8376300 1244484 7131816 15% /
Error =
Waiting EC2 Terminate ...
EC2 Terminate Finished ...
付録:ここまでのソースコード 。
import time
import boto3
print ("Launching EC2.." )
ec2_resouce = boto3.resource('ec2' )
instance_market_options={
'MarketType' : 'spot' ,
'SpotOptions' : {
'MaxPrice' : '0.27' ,
'SpotInstanceType' : 'one-time' ,
}
}
instances = ec2_resouce.create_instances(ImageId='ami-0c3fd0f5d33134a76' ,
MaxCount=1 , MinCount=1 ,
InstanceType='t2.micro' ,
IamInstanceProfile={'Name' : 'SSM_Access' },
InstanceMarketOptions=instance_market_options,
KeyName='msyksphinz_test' ,
)
instance = instances[0 ]
instance_id = instance.instance_id
print ("Instance ID = {}" .format(instance_id))
print ("Waiting EC2 Launch ..." )
instance.wait_until_running()
print ("EC2 Launch Finished ..." )
time.sleep(5.0 )
ssm_client = boto3.client('ssm' )
response = ssm_client.send_command(
DocumentName="AWS-RunShellScript" ,
Parameters={'commands' : ['ls -lt /' , 'df' ]},
InstanceIds=[instance_id],
)
time.sleep(5.0 )
command_id = response['Command' ]['CommandId' ]
output = ssm_client.get_command_invocation(
CommandId=command_id,
InstanceId=instance_id,
)
print ("Output = \n {} \n " .format(output['StandardOutputContent' ]))
print ("Error = \n {} \n " .format(output['StandardErrorContent' ]))
instance.terminate()
print ("Waiting EC2 Terminate ..." )
print ("EC2 Terminate Finished ..." )