この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS Step Functionsが連携できるサービスが爆増しました
こんにちは、のんピ(@non____97)です。
AWS Step Functionsの連携可能サービス数が200個以上に増える神アップデートが来ました!!
これにより、AWS Step Functionsから直接実行できるAPIの数は9,000以上となりました。
今まで、例えばEC2インスタンスを停止したい場合は、EC2インスタンスを停止させるLambda関数を用意して、そのLambda関数をAWS Step Functionsから実行する必要がありました。 この1ステップが削減されただけですが、以下の様なメリットがあると考えます。
- 大量のLambda関数の作成/メンテナンスが不要
- タスク(API)を実行する際に必須なパラメーターを表示してくれるので、ステートマシンの作成の準備や手間を削減
- Workflow Studio上では、タスクごとに実行するAPIのAWSサービスのアイコンも表示されているので、ステートマシンでどのようなサービスを使用しているのかを簡単に把握可能
このアップデートにより、Step Functionsの使いやすさがグッと増したと思います。
加えて、AWS Step Functionsは個人的2021年上半期最高アップデートのWorkflow Studioにより、GUI上で簡単にワークフローを設計できるようになっています。Workflow Studioの説明は以下記事をご参照ください。
今まで、AWS Step Functionsに苦手意識を持っていた方や、触ったことがなかった方は、是非触ってみてください! 世界が変わります。
そんな私はこのアップデートで興奮しすぎて心臓がドキドキしています。
AWS Step FunctionsのWorkflow StudioでWebシステムの停止のステートマシンを組んでみる
作業内容の確認
私は以前以下記事で紹介している通り、ジョブ管理システムから抜け出すために、AWS Step FunctionsとSSM RunCommandでステートマシンを組みました。
こちらの記事で作成したWebシステムの停止のステートマシンは以下のように、複雑怪奇な状態です。
今回は、こちらとほぼ同様なステートマシンを作成してみたいと思います。(Mapを使った複数のEC2インスタンスへの処理は省略しました)
事前準備
まず事前準備です。
事前準備として、以下の作業を行います。
- AWS CDKでWebシステムをデプロイ
- EC2インスタンスにCloudWatch Agentをインストール・設定
- EC2インスタンス上のhttpd(Apache)の起動
実行するAWS CDKはこちらの記事で紹介したコードを使ってデプロイします。
デプロイ後、上述した記事のEC2インスタンスへのCloudWatchの設定を参考にEC2インスタンスにCloudWatch Agentをインストール・設定も行います。
最後にUser Dataで作成したスクリプトを実行して、以下のようにhttpdを起動します。
# 起動前のhttpdのステータスの確認
$ sudo bash /usr/local/sbin/checkHttpd.sh
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Drop-In: /usr/lib/systemd/system/httpd.service.d
└─php-fpm.conf
Active: inactive (dead)
Docs: man:httpd.service(8)
# httpdの起動
$ sudo bash /usr/local/sbin/startHttpd.sh
# 起動後のhttpdのステータスの確認
$ sudo bash /usr/local/sbin/checkHttpd.sh
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Drop-In: /usr/lib/systemd/system/httpd.service.d
└─php-fpm.conf
Active: active (running) since Fri 2021-10-01 09:07:50 UTC; 2s ago
Docs: man:httpd.service(8)
Main PID: 2404 (httpd)
Status: "Processing requests..."
CGroup: /system.slice/httpd.service
├─2404 /usr/sbin/httpd -DFOREGROUND
├─2414 /usr/sbin/httpd -DFOREGROUND
├─2416 /usr/sbin/httpd -DFOREGROUND
├─2421 /usr/sbin/httpd -DFOREGROUND
├─2423 /usr/sbin/httpd -DFOREGROUND
└─2428 /usr/sbin/httpd -DFOREGROUND
Oct 01 09:07:50 ip-10-0-2-247.ec2.internal systemd[1]: Starting The Apache HTTP Server...
Oct 01 09:07:50 ip-10-0-2-247.ec2.internal systemd[1]: Started The Apache HTTP Server.
CloudWatch Logsからもこちらのスクリプトが実行されていることを確認します。
実際にブラウザでWebサーバーにアクセスできるかも確認しましょう。
ALBのDNS名をコピーします。
コピーしたDNS名にブラウザでアクセスすると、Apache HTTP Serverのテストページが表示されることが確認できます。
また、ステートマシンでWebシステムを停止する前にEC2インスタンスとDBクラスターが起動していることも確認します。
ステートマシンの作成
それでは、AWS Step FunctionsのWorkflow Studioを使って、Webシステムの停止ステートマシンを作成します。
まず、AWS Step Functionsのコンソールを開きます。そして、ステートマシン
からステートマシンの作成
をクリックします。
次にステートマシンの作成方法を指定します。
今回は、Workflow Studioで作成するので、ワークフローを視覚的に設計
を選択します。また、タイプは標準
を選択して、次へ
をクリックします。
そして、Workflow Studioでステートマシンのワークフローを設計します。
見てください。この未来感。興奮を隠せません。APIを検索して、ドラッグ&ドロップするだけで簡単にタスク同士を接続することができます。また、APIパラメーターで必須のものは表示されているので、最低限どのような情報を渡せば良いのかが分かりやすくなっています。
最終的なワークフローは以下のようになりました。「結構細かそうだな...」と思われる方も多いかもしれませんが、JSONで設定するよりか直感的に操作できるため、そこまでストレスなく設計できました。
右上のExport
- Export PNG Image
をクリックすると、以下のようにワークフローをPNG形式でダウンロードできます。エクスポートするとアイコンが表示されなくなるのが少し残念ですね。
次に生成されたコードの確認です。
Workflow Studioで設計したのワークフローから生成されたコードを確認することができます。確認が完了したら次へ
をクリックします。
参考までに生成されたコードは以下の通りです。
生成されたコード
{
"Comment": "This is your state machine",
"StartAt": "CreateRule for maintenance mode",
"States": {
"CreateRule for maintenance mode": {
"Type": "Task",
"Parameters": {
"Actions": [
{
"Type": "fixed-response",
"FixedResponseConfig": {
"StatusCode": "503",
"ContentType": "text/html",
"MessageBody": "<!doctype html><html lang=\"ja\"><head><meta charset=\"UTF-8\"><!-- Required meta tags --><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><!-- Bootstrap CSS --><link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css\" rel=\"stylesheet\"integrity=\"sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1\" crossorigin=\"anonymous\"><title>ただいまメンテナンス中です</title></head><body class=\"m-5\"><div class=\"mx-auto\"><h1 class=\"text-center text-danger font-bold\">ただいまメンテナンス中です</h1><dl class=\"text-center text-lg font-bold\"><dt>【メンテナンス日時】</dt><dd class=\"\">2021年5月21日 0:00〜0:30</dd></dl><p class=\"text-center\">メンテナンス終了までしばらくお待ちください。</p></div></body></html>"
}
}
],
"Conditions": [
{
"Field": "source-ip",
"SourceIpConfig": {
"Values": [
"0.0.0.0/0"
]
}
}
],
"ListenerArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:listener/app/WebAp-Alb16-1PFUCP6F9WBO4/5388462c9625591a/b1301fb282842d15",
"Priority": 1
},
"Resource": "arn:aws:states:::aws-sdk:elasticloadbalancingv2:createRule",
"Next": "SendCommand - stop httpd",
"ResultPath": "$.Output"
},
"SendCommand - stop httpd": {
"Type": "Task",
"Parameters": {
"InstanceIds": [
"i-09daf1c434dd8d7ca"
],
"DocumentName": "AWS-RunShellScript",
"MaxConcurrency": "1",
"Parameters": {
"commands": [
"sudo bash /usr/local/sbin/stopHttpd.sh"
]
},
"TimeoutSeconds": 60
},
"Resource": "arn:aws:states:::aws-sdk:ssm:sendCommand",
"Next": "Wait 10 sec for \"SendCommand - stop httpd\""
},
"Wait 10 sec for \"SendCommand - stop httpd\"": {
"Type": "Wait",
"Seconds": 10,
"Next": "ListCommandInvocations - \"stop httpd\""
},
"ListCommandInvocations - \"stop httpd\"": {
"Type": "Task",
"Parameters": {
"CommandId.$": "$.Command.CommandId",
"Details": "true"
},
"Resource": "arn:aws:states:::aws-sdk:ssm:listCommandInvocations",
"Next": "Success or failure \"SendCommand - stop httpd\""
},
"Success or failure \"SendCommand - stop httpd\"": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.CommandInvocations[0].Status",
"StringEquals": "Success",
"Next": "SendCommand - describe httpd status"
},
{
"Or": [
{
"Variable": "$.CommandInvocations[0].Status",
"StringEquals": "Pending"
},
{
"Variable": "$.CommandInvocations[0].Status",
"StringEquals": "InProgress"
}
],
"Next": "Wait 10 sec for \"SendCommand - stop httpd\""
}
],
"Default": "Faild stop httpd"
},
"SendCommand - describe httpd status": {
"Type": "Task",
"Parameters": {
"InstanceIds": [
"i-09daf1c434dd8d7ca"
],
"DocumentName": "AWS-RunShellScript",
"MaxConcurrency": "1",
"Parameters": {
"commands": [
"sudo bash /usr/local/sbin/checkHttpd.sh"
]
},
"TimeoutSeconds": 60
},
"Resource": "arn:aws:states:::aws-sdk:ssm:sendCommand",
"Next": "Wait 10 sec for \"SendCommand - describe httpd status\""
},
"Wait 10 sec for \"SendCommand - describe httpd status\"": {
"Type": "Wait",
"Seconds": 10,
"Next": "ListCommandInvocations - describe httpd status"
},
"ListCommandInvocations - describe httpd status": {
"Type": "Task",
"Parameters": {
"CommandId.$": "$.Command.CommandId",
"Details": "true"
},
"Resource": "arn:aws:states:::aws-sdk:ssm:listCommandInvocations",
"Next": "Success or failure \"SendCommand - descibe httpd status\""
},
"Success or failure \"SendCommand - descibe httpd status\"": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.CommandInvocations[0].CommandPlugins[0].ResponseCode",
"NumericEquals": 3,
"Next": "StopInstances"
},
{
"Or": [
{
"Variable": "$.CommandInvocations[0].Status",
"StringEquals": "Pending"
},
{
"Variable": "$.CommandInvocations[0].Status",
"StringEquals": "InProgress"
}
],
"Next": "Wait 10 sec for \"SendCommand - describe httpd status\""
}
],
"Default": "Failed describe httpd status"
},
"StopInstances": {
"Type": "Task",
"Parameters": {
"InstanceIds": [
"i-09daf1c434dd8d7ca"
]
},
"Resource": "arn:aws:states:::aws-sdk:ec2:stopInstances",
"Next": "Wait 30 sec for \"StopInstances\""
},
"Wait 30 sec for \"StopInstances\"": {
"Type": "Wait",
"Seconds": 30,
"Next": "DescribeInstances"
},
"DescribeInstances": {
"Type": "Task",
"Next": "Is stopped EC2 instances",
"Parameters": {
"InstanceIds": [
"i-09daf1c434dd8d7ca"
]
},
"Resource": "arn:aws:states:::aws-sdk:ec2:describeInstances"
},
"Is stopped EC2 instances": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.Reservations[0].Instances[0].State.Code",
"NumericEquals": 80,
"Next": "StartBackupJob - EC2 Instances"
}
],
"Default": "Wait 30 sec for \"StopInstances\""
},
"StartBackupJob - EC2 Instances": {
"Type": "Task",
"Parameters": {
"BackupVaultName": "Default",
"IamRoleArn": "arn:aws:iam::<AWSアカウントID>:role/service-role/AWSBackupDefaultServiceRole",
"ResourceArn": "arn:aws:ec2:us-east-1:<AWSアカウントID>:instance/i-09daf1c434dd8d7ca"
},
"Resource": "arn:aws:states:::aws-sdk:backup:startBackupJob",
"Next": "StartBackupJob - DB Cluster"
},
"StartBackupJob - DB Cluster": {
"Type": "Task",
"Parameters": {
"BackupVaultName": "Default",
"IamRoleArn": "arn:aws:iam::<AWSアカウントID>:role/service-role/AWSBackupDefaultServiceRole",
"ResourceArn": "arn:aws:rds:us-east-1:<AWSアカウントID>:cluster:webappstack-dbcluster15af587f-1cq946f88dm5z"
},
"Resource": "arn:aws:states:::aws-sdk:backup:startBackupJob",
"Next": "Wait 60 sec for \"DescribeDBClusters for available backup\""
},
"Wait 60 sec for \"DescribeDBClusters for available backup\"": {
"Type": "Wait",
"Seconds": 60,
"Next": "DescribeDBClusters for available backup"
},
"DescribeDBClusters for available backup": {
"Type": "Task",
"Parameters": {
"DbClusterIdentifier": "webappstack-dbcluster15af587f-1cq946f88dm5z"
},
"Resource": "arn:aws:states:::aws-sdk:rds:describeDBClusters",
"Next": "Is available backup"
},
"Is available backup": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.DbClusters[0].Status",
"StringEquals": "available",
"Next": "StopDBCluster"
},
{
"Or": [
{
"Variable": "$.DbClusters[0].Status",
"StringEquals": "backing-up"
},
{
"Variable": "$.DbClusters[0].Status",
"StringEquals": "backtracking"
},
{
"Variable": "$.DbClusters[0].Status",
"StringEquals": "maintenance"
}
],
"Next": "Wait 60 sec for \"DescribeDBClusters for available backup\""
}
],
"Default": "Failed start backup job for db cluster"
},
"StopDBCluster": {
"Type": "Task",
"Parameters": {
"DbClusterIdentifier": "webappstack-dbcluster15af587f-1cq946f88dm5z"
},
"Resource": "arn:aws:states:::aws-sdk:rds:stopDBCluster",
"Next": "Wait 60 sec for \"StopDBCluster\""
},
"Wait 60 sec for \"StopDBCluster\"": {
"Type": "Wait",
"Seconds": 60,
"Next": "DescribeDBClusters for \"StopDBCluster\""
},
"DescribeDBClusters for \"StopDBCluster\"": {
"Type": "Task",
"Parameters": {
"DbClusterIdentifier": "webappstack-dbcluster15af587f-1cq946f88dm5z"
},
"Resource": "arn:aws:states:::aws-sdk:rds:describeDBClusters",
"Next": "Is stopped DB cluster"
},
"Is stopped DB cluster": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.DbClusters[0].Status",
"StringEquals": "stopped",
"Next": "Success"
},
{
"Variable": "$.DbClusters[0].Status",
"StringEquals": "stopping",
"Next": "Wait 60 sec for \"StopDBCluster\""
}
],
"Default": "Failed stop DB cluster"
},
"Failed start backup job for db cluster": {
"Type": "Fail"
},
"Success": {
"Type": "Succeed"
},
"Failed stop DB cluster": {
"Type": "Fail"
},
"Faild stop httpd": {
"Type": "Fail"
},
"Failed describe httpd status": {
"Type": "Fail"
}
}
}
最後にステートマシンの設定です。
ステートマシン名と、事前準備で作成したIAMロールを指定して、ステートマシンの作成
をクリックします。
以上でステートマシンの作成は完了です。
ステートマシンの実行
それでは、作成したステートマシンを実行します。
作成したステートマシンを選択し、実行の開始
をクリックします。
今回は特にステートマシンに入力するJSONは変更しません。そのまま実行の開始
をクリックします。
すると、ステートマシンが実行されます。
20分ほど待つと、処理が完了しており、実行ステータスも成功
となっていました。
それでは、正しくWebシステムが停止できているのかを確認します。
ALBのDNS名で再度ブラウザでアクセスすると、ただいまメンテナンス中です
と正常にメンテナンスページが表示されるようになっています。
EC2インスタンスとDBクラスターが停止していることも確認できます。
最後に、AWS Backupで正常にAMI及び、スナップショットが作成されていることも確認します。
ステートマシンが意図した通り、動作していることが確認できました。
大 AWS Step Functions 時代が来る予感
AWS Step Functionsの連携可能なAWSサービスが大幅増加したアップデートをお伝えしました。これは大 AWS Step Functions 時代が来る予感を感じずにはいられません。
Lambda関数で何でもかんでもタスクを定義しなくて済むのがかなり嬉しいポイントですね。
個人的にはAWS Step Functionsに、さらに以下の機能が追加されるとかなり大興奮します。
- Workflow Studio上で編集途中のワークフローの保存
- 設計したワークフローで使用されているAWSサービスからIAMロールを自動生成
- ステートマシンの途中からの再実行
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!