

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Step Functions のステートマシンバージョンの段階的なデプロイの実行
<a name="version-rolling-deployment"></a>

ローリングデプロイとは、アプリケーションの以前のバージョンを新しいバージョンのアプリケーションに徐々に置き換えるデプロイ戦略です。ステートマシンバージョンのローリングデプロイを行うには、徐々に増加する実行トラフィックを新しいバージョンに送信します。トラフィックの量と増加率は、設定するパラメータです。

次のいずれかのオプションを使用して、バージョンのローリングデプロイを実行できます。
+ [Step Functions コンソール](https://console.aws.amazon.com/states/home?region=us-east-1#/) - 同じステートマシンの 2 つのバージョンを指すエイリアスを作成します。このエイリアスでは、2 つのバージョン間でトラフィックをシフトするようルーティングを設定します。コンソールを使用したバージョンのロールアウトに関する詳細については、「[バージョン](concepts-state-machine-version.md)」および「[Aliases](concepts-state-machine-alias.md)」を参照してください。
+ **AWS CLI と SDK のスクリプト** - AWS CLI または AWS SDK を使用してシェルスクリプトを作成します。詳細については、AWS CLI および AWS SDK の使用に関する以下のセクションを参照してください。
+ **AWS CloudFormation テンプレート** - `[AWS::StepFunctions::StateMachineVersion](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html)` と `[AWS::StepFunctions::StateMachineAlias](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html)` のリソースを使用して複数のステートマシンバージョンを公開し、そのうちの 1 つまたは 2 つのバージョンを指すエイリアスを作成します。

## AWS CLI を使用してステートマシンの新しいバージョンをデプロイします。
<a name="version-canary-deploy-cli"></a>

このセクションのスクリプト例は、AWS CLI を使用してトラフィックを以前のステートマシンバージョンから新しいステートマシンバージョンに徐々に移行する方法を示しています。このサンプルスクリプトを使用することも、必要に応じて更新することもできます。

このスクリプトは、エイリアスを使用して新しいステートマシンバージョンをデプロイするための Canary デプロイを示しています。以下の手順は、このスクリプトが実行するタスクの概要を示しています。

1. `publish_revision` パラメータが true に設定されている場合は、最新の [revision](concepts-cd-aliasing-versioning.md#statemachinerev) を、ステートマシンの次のバージョンとして公開します。デプロイが成功すると、このバージョンが新しいライブバージョンになります。

   `publish_revision` パラメータを false に設定すると、スクリプトは、最後に公開されたバージョンのステートマシンをデプロイします。

1. エイリアスがまだ存在しない場合は作成します。エイリアスが存在しない場合は、このエイリアスへのトラフィックの 100% を新しいバージョンにルーティングして、スクリプトを終了します。

1. エイリアスのルーティング設定を更新して、トラフィックのごく一部を以前のバージョンから新しいバージョンにシフトします。canary のこの割合は `canary_percentage` パラメータで設定します。

1. デフォルトでは、設定可能な CloudWatch アラームを 60 秒ごとにモニタリングします。これらのアラームのいずれかが発せられたら、トラフィックの 100% を以前のバージョンにルーティングし、すぐにデプロイをロールバックしてください。

   `alarm_polling_interval` で定義した時間間隔 (秒単位) が経過したら、引き続きアラームをモニタリングします。`canary_interval_seconds` で定義した時間間隔が経過するまでモニタリングを続けます。

1. `canary_interval_seconds` の間にアラームが鳴らなかった場合は、トラフィックの 100% を新しいバージョンにシフトします。

1. 新しいバージョンが正常にデプロイされたら、`history_max` パラメータで指定された番号より古いバージョンをすべて削除します。



```
#!/bin/bash
# 
# AWS StepFunctions example showing how to create a canary deployment with a
# State Machine Alias and versions.
# 
# Requirements: AWS CLI installed and credentials configured.
# 
# A canary deployment deploys the new version alongside the old version, while
# routing only a small fraction of the overall traffic to the new version to
# see if there are any errors. Only once the new version has cleared a testing
# period will it start receiving 100% of traffic.
# 
# For a Blue/Green or All at Once style deployment, you can set the
# canary_percentage to 100. The script will immediately shift 100% of traffic
# to the new version, but keep on monitoring the alarms (if any) during the
# canary_interval_seconds time interval. If any alarms raise during this period,
# the script will automatically rollback to the previous version.
# 
# Step Functions allows you to keep a maximum of 1000 versions in version history
# for a state machine. This script has a version history deletion mechanism at
# the end, where it will delete any versions older than the limit specified.
# 
# For an example that also demonstrates linear (or rolling) deployments, see the following: 
# https://github.com/aws-samples/aws-stepfunctions-examples/blob/main/gradual-deploy/sfndeploy.py

set -euo pipefail

# ******************************************************************************
# you can safely change the variables in this block to your values
state_machine_name="my-state-machine"
alias_name="alias-1"
region="us-east-1"

# array of cloudwatch alarms to poll during the test period.
# to disable alarm checking, set alarm_names=()
alarm_names=("alarm1" "alarm name with a space")

# true to publish the current revision as the next version before deploy.
# false to deploy the latest version from the state machine's version history.
publish_revision=true

# true to force routing configuration update even if the current routing
# for the alias does not have a 100% routing config.
# false will abandon deploy attempt if current routing config not 100% to a
# single version.
# Be careful when you combine this flag with publish_revision - if you just
# rerun the script you might deploy the newly published revision from the
# previous run.
force=false

# percentage of traffic to route to the new version during the test period
canary_percentage=10

# how many seconds the canary deployment lasts before full deploy to 100%
canary_interval_seconds=300

# how often to poll the alarms
alarm_polling_interval=60

# how many versions to keep in history. delete versions prior to this.
# set to 0 to disable old version history deletion.
history_max=0
# ******************************************************************************

#######################################
# Update alias routing configuration.
# 
# If you don't specify version 2 details, will only create 1 routing entry. In
# this case the routing entry weight must be 100.
# 
# Globals:
#   alias_arn
# Arguments:
#   1. version 1 arn
#   2. version 1 weight
#   3. version 2 arn (optional)
#   4. version 2 weight (optional)
#######################################
function update_routing() {
  if [[ $# -eq 2 ]]; then
    local routing_config="[{\"stateMachineVersionArn\": \"$1\", \"weight\":$2}]"
  elif [[ $# -eq 4 ]]; then
    local routing_config="[{\"stateMachineVersionArn\": \"$1\", \"weight\":$2}, {\"stateMachineVersionArn\": \"$3\", \"weight\":$4}]"
  else
    echo "You have to call update_routing with either 2 or 4 input arguments." >&2
    exit 1
  fi
  
  ${aws} update-state-machine-alias --state-machine-alias-arn ${alias_arn} --routing-configuration "${routing_config}"
}

# ******************************************************************************
# pre-run validation
if [[ (("${#alarm_names[@]}" -gt 0)) ]]; then
  alarm_exists_count=$(aws cloudwatch describe-alarms --alarm-names "${alarm_names[@]}" --alarm-types "CompositeAlarm" "MetricAlarm" --query "length([MetricAlarms, CompositeAlarms][])" --output text)

  if [[ (("${#alarm_names[@]}" -ne "${alarm_exists_count}")) ]]; then
    echo All of the alarms to monitor do not exist in CloudWatch: $(IFS=,; echo "${alarm_names[*]}") >&2
    echo Only the following alarm names exist in CloudWatch:
    aws cloudwatch describe-alarms --alarm-names "${alarm_names[@]}" --alarm-types "CompositeAlarm" "MetricAlarm" --query "join(', ', [MetricAlarms, CompositeAlarms][].AlarmName)" --output text
    exit 1
  fi
fi

if [[ (("${history_max}" -gt 0)) && (("${history_max}" -lt 2)) ]]; then
  echo The minimum value for history_max is 2. This is the minimum number of older state machine versions to be able to rollback in the future. >&2
  exit 1
fi
# ******************************************************************************
# main block follows

account_id=$(aws sts get-caller-identity --query Account --output text)

sm_arn="arn:aws:states:${region}:${account_id}:stateMachine:${state_machine_name}"

# the aws command we'll be invoking a lot throughout.
aws="aws stepfunctions"

# promote the latest revision to the next version
if [[ "${publish_revision}" = true ]]; then
  new_version=$(${aws} publish-state-machine-version --state-machine-arn=$sm_arn --query stateMachineVersionArn --output text)
  echo Published the current revision of state machine as the next version with arn: ${new_version}
else
  new_version=$(${aws} list-state-machine-versions --state-machine-arn ${sm_arn} --max-results 1 --query "stateMachineVersions[0].stateMachineVersionArn" --output text)
  echo "Since publish_revision is false, using the latest version from the state machine's version history: ${new_version}"
fi

# find the alias if it exists
alias_arn_expected="${sm_arn}:${alias_name}"
alias_arn=$(${aws} list-state-machine-aliases --state-machine-arn ${sm_arn} --query "stateMachineAliases[?stateMachineAliasArn==\`${alias_arn_expected}\`].stateMachineAliasArn" --output text)

if [[ "${alias_arn_expected}" == "${alias_arn}" ]]; then
  echo Found alias ${alias_arn}

  echo Current routing configuration is:
  ${aws} describe-state-machine-alias --state-machine-alias-arn "${alias_arn}" --query routingConfiguration
else
  echo Alias does not exist. Creating alias ${alias_arn_expected} and routing 100% traffic to new version ${new_version}
  
  ${aws} create-state-machine-alias --name "${alias_name}" --routing-configuration "[{\"stateMachineVersionArn\": \"${new_version}\", \"weight\":100}]"

  echo Done!
  exit 0
fi

# find the version to which the alias currently points (the current live version)
old_version=$(${aws} describe-state-machine-alias --state-machine-alias-arn $alias_arn --query "routingConfiguration[?weight==\`100\`].stateMachineVersionArn" --output text)

if [[ -z "${old_version}" ]]; then
  if [[ "${force}" = true ]]; then
    echo Force setting is true. Will force update to routing config for alias to point 100% to new version.
    update_routing "${new_version}" 100
    
    echo Alias ${alias_arn} now pointing 100% to ${new_version}.
    echo Done!
    exit 0
  else
    echo Alias ${alias_arn} does not have a routing config entry with 100% of the traffic. This means there might be a deploy in progress, so not starting another deploy at this time. >&2
    exit 1
  fi
fi

if [[ "${old_version}" == "${new_version}" ]]; then
  echo The alias already points to this version. No update necessary.
  exit 0
fi

echo Switching ${canary_percentage}% to new version ${new_version}
(( old_weight = 100 - ${canary_percentage} ))
update_routing "${new_version}" ${canary_percentage} "${old_version}" ${old_weight}

echo New version receiving ${canary_percentage}% of traffic.
echo Old version ${old_version} is still receiving ${old_weight}%.

if [[ ${#alarm_names[@]} -eq 0 ]]; then
  echo No alarm_names set. Skipping cloudwatch monitoring.
  echo Will sleep for ${canary_interval_seconds} seconds before routing 100% to new version.
  sleep ${canary_interval_seconds}
  echo Canary period complete. Switching 100% of traffic to new version...
else
  echo Checking if alarms fire for the next ${canary_interval_seconds} seconds.

  (( total_wait = canary_interval_seconds + $(date +%s) ))

  now=$(date +%s)
  while [[ ((${now} -lt ${total_wait})) ]]; do
    alarm_result=$(aws cloudwatch describe-alarms --alarm-names "${alarm_names[@]}" --state-value ALARM --alarm-types "CompositeAlarm" "MetricAlarm" --query "join(', ', [MetricAlarms, CompositeAlarms][].AlarmName)" --output text)

    if [[ ! -z "${alarm_result}" ]]; then
      echo The following alarms are in ALARM state: ${alarm_result}. Rolling back deploy. >&2
      update_routing "${old_version}" 100

      echo Rolled back to ${old_version}
      exit 1
    fi
  
    echo Monitoring alarms...no alarms have triggered.
    sleep ${alarm_polling_interval}
    now=$(date +%s)
  done

  echo No alarms detected during canary period. Switching 100% of traffic to new version...
fi

update_routing "${new_version}" 100

echo Version ${new_version} is now receiving 100% of traffic.

if [[ (("${history_max}" -eq 0 ))]]; then
  echo Version History deletion is disabled. Remember to prune your history, the default limit is 1000 versions.
  echo Done!
  exit 0
fi

echo Keep the last ${history_max} versions. Deleting any versions older than that...

# the results are sorted in descending order of the version creation time
version_history=$(${aws} list-state-machine-versions --state-machine-arn ${sm_arn} --max-results 1000 --query "join(\`\"\\n\"\`, stateMachineVersions[].stateMachineVersionArn)" --output text)

counter=0

while read line; do
  ((counter=${counter} + 1))

  if [[ (( ${counter} -gt ${history_max})) ]]; then
    echo Deleting old version ${line}
    ${aws} delete-state-machine-version --state-machine-version-arn ${line}
  fi
done <<< "${version_history}"

echo Done!
```

## AWS SDK を使用してステートマシンの新しいバージョンをデプロイします。
<a name="version-deploy-sdk"></a>

[aws-stepfunctions-examples](https://github.com/aws-samples/aws-stepfunctions-examples/tree/main/gradual-deploy) にあるサンプルスクリプトは、AWS SDK for Python を使用して、トラフィックを以前のバージョンから新しいバージョンのステートマシンに徐々に移行する方法を示しています。このサンプルスクリプトを使用することも、必要に応じて更新することもできます。

このスクリプトは、以下のデプロイ戦略を示しています。
+ **Canary** - トラフィックは 2 つの増分で移行されます。

  1 回目の増分では、トラフィックのごく一部、例えば 10% が新しいバージョンにシフトされます。2 回目の増分では、指定した秒単位の時間間隔が経過する前に、残りのトラフィックが新しいバージョンにシフトされます。残りのトラフィックの新しいバージョンへの切り替えは、指定された時間間隔内に CloudWatch アラームがオフになっていない場合にのみ行われます。
+  **リニアまたはローリング** - トラフィックを同じ間隔で、各増分の間隔を同じ秒数空けて新しいバージョンにシフトします。

  例えば、増加率を **600** 秒のうちの `--interval` の **20** として指定すると、このデプロイでは、新しいバージョンがトラフィックの 100% を受信するまで、600 秒ごとに 20% ずつトラフィックが増加します。

  このデプロイでは、いずれかの CloudWatch アラームがオフになると、すぐに新しいバージョンがロールバックされます。
+ **All at Once または Blue/Green** - トラフィックの 100% をただちに新しいバージョンに移行します。このデプロイでは新しいバージョンをモニタリングし、CloudWatch アラームがオフになっている場合は、以前のバージョンに自動的にロールバックします。

## AWS CloudFormation を使用して、新しいステートマシンバージョンをデプロイします。
<a name="version-deploy-cfn"></a>

次の CloudFormation テンプレートの例では、`MyStateMachine` というステートマシンの 2 つのバージョンを公開しています。両方のバージョンを指す `PROD` というエイリアスを作成して、バージョン `2` をデプロイします。

この例では、このバージョンがトラフィックの 100% を受信するまで、5 分ごとにトラフィックの 10% がバージョン `2` に移行されます。この例は、CloudWatch アラームを設定する方法も示しています。設定したアラームのいずれかが `ALARM` 状態になると、デプロイは失敗し、すぐにロールバックされます。

```
MyStateMachine:
  Type: AWS::StepFunctions::StateMachine
  Properties:
    Type: STANDARD
    StateMachineName: MyStateMachine
    RoleArn: arn:aws:iam::account-id:role/myIamRole
    Definition:
      StartAt: PassState
      States:
        PassState:
          Type: Pass
          Result: Result
          End: true

MyStateMachineVersionA:
  Type: AWS::StepFunctions::StateMachineVersion
  Properties:
    Description: Version 1
    StateMachineArn: !Ref MyStateMachine

MyStateMachineVersionB:
  Type: AWS::StepFunctions::StateMachineVersion
  Properties:
    Description: Version 2
    StateMachineArn: !Ref MyStateMachine

PROD:
  Type: AWS::StepFunctions::StateMachineAlias
  Properties:
    Name: PROD
    Description: The PROD state machine alias taking production traffic.
    DeploymentPreference:
      StateMachineVersionArn: !Ref MyStateMachineVersionB
      Type: LINEAR
      Percentage: 10
      Interval: 5
      Alarms:
        # A list of alarms that you want to monitor. If any of these alarms trigger, rollback the deployment immediately by pointing 100 percent of traffic to the previous version.
        - !Ref CloudWatchAlarm1
        - !Ref CloudWatchAlarm2
```