我在余生中学到了使用云信息的 6 个教训。

我开始与 云形成 4年前。从那时起,我破坏了很多基础设施,甚至是那些已经投入生产的基础设施。但每次我把事情搞砸时,我都会学到新的东西。通过这次经历,我将分享我学到的一些最重要的教训。

我在余生中学到了使用云信息的 6 个教训。

第 1 课:在部署更改之前对其进行测试

我开始工作后不久就学到了这个教训 云形成。我不记得当时我到底破坏了什么,但我肯定记得我使用了命令 aws云信息更新。此命令只是推出模板,而不对将部署的更改进行任何验证。我认为不需要任何解释为什么您应该在部署之前测试所有更改。

这次失败后我立刻改变了 部署管道,将更新命令替换为命令 创建更改集

# OPERATION is either "UPDATE" or "CREATE"
changeset_id=$(aws cloudformation create-change-set 
    --change-set-name "$CHANGE_SET_NAME" 
    --stack-name "$STACK_NAME" 
    --template-body "$TPL_PATH" 
    --change-set-type "$OPERATION" 
    --parameters "$PARAMETERS" 
    --output text 
    --query Id)

aws cloudformation wait 
    change-set-create-complete --change-set-name "$changeset_id"

创建变更集后,它对现有堆栈没有影响。与更新命令不同,变更集方法不会触发实际部署。相反,它会创建一个更改列表,您可以在部署之前查看这些更改。您可以在 aws 控制台界面中查看更改。但如果您希望自动化所有操作,请在 CLI 中检查它们:

# this command is presented only for demonstrational purposes.
# the real command should take pagination into account
aws cloudformation describe-change-set 
    --change-set-name "$changeset_id" 
    --query 'Changes[*].ResourceChange.{Action:Action,Resource:ResourceType,ResourceId:LogicalResourceId,ReplacementNeeded:Replacement}' 
    --output table

此命令应产生类似于以下内容的输出:

--------------------------------------------------------------------
|                         DescribeChangeSet                        |
+---------+--------------------+----------------------+------------+
| Action  | ReplacementNeeded  |      Resource        | ResourceId |
+---------+--------------------+----------------------+------------+
|  Modify | True               |  AWS::ECS::Cluster   |  MyCluster |
|  Replace| True               |  AWS::RDS::DBInstance|  MyDB      |
|  Add    | None               |  AWS::SNS::Topic     |  MyTopic   |
+---------+--------------------+----------------------+------------+

特别注意 Action 所在的变化 更换, 删除 或在哪里 需要更换 - True。这些是最危险的更改,通常会导致信息丢失。

一旦更改经过审核,就可以进行部署

aws cloudformation execute-change-set --change-set-name "$changeset_id"

operation_lowercase=$(echo "$OPERATION" | tr '[:upper:]' '[:lower:]')
aws cloudformation wait "stack-${operation_lowercase}-complete" 
    --stack-name "$STACK_NAME"

第 2 课:使用堆栈策略防止有状态资源被替换或删除

有时仅仅查看更改是不够的。我们都是人,都会犯错误。在我们开始使用变更集后不久,我的队友在不知不觉中执行了一次部署,导致数据库更新。没有发生什么不好的事情,因为这是一个测试环境。

尽管我们的脚本显示了更改列表并要求确认,但替换更改被跳过,因为更改列表太大以至于无法在屏幕上显示。而且由于这是测试环境中的正常更新,因此没有太多关注这些变化。

有些资源您永远不想替换或删除。这些是有状态服务,例如 RDS 数据库实例或 elasticsearch 集群等。如果正在执行的操作需要删除此类资源,如果 aws 能够自动拒绝部署,那就太好了。幸运的是,cloudformation 有一种内置的方法可以做到这一点。这称为堆栈策略,您可以在以下位置阅读更多相关信息: 文件资料:

STACK_NAME=$1
RESOURCE_ID=$2

POLICY_JSON=$(cat <<EOF
{
    "Statement" : [{
        "Effect" : "Deny",
        "Action" : [
            "Update:Replace",
            "Update:Delete"
        ],
        "Principal": "*",
        "Resource" : "LogicalResourceId/$RESOURCE_ID"
    }]
}
EOF
)

aws cloudformation set-stack-policy --stack-name "$STACK_NAME" 
    --stack-policy-body "$POLICY_JSON"

第 3 课:使用秘密参数更新堆栈时使用 UsePreviousValue

当您创建 RDS mysql 实体时,AWS 要求您提供 MasterUsername 和 MasterUserPassword。由于最好不要在源代码中保守秘密,并且我想完全自动化所有操作,因此我实现了一种“智能机制”,在部署之前将从 s3 获取凭证,如果找不到凭证,则会生成新凭证并存储在 s3 中。

然后,这些凭据将作为参数传递给 cloudformation create-change-set 命令。在试验该脚本时,与 s3 的连接丢失了,我的“智能机制”将其视为生成新凭证的信号。

如果我开始在生产中使用此脚本并且连接问题再次发生,它将使用新的凭据更新堆栈。在这种特殊情况下,不会发生任何不好的事情。但是,我放弃了这种方法并开始使用另一种方法,仅在创建堆栈时提供一次凭据。后来,当堆栈需要更新时,我不会指定参数的秘密值,而是简单地使用 使用上一个值=true:

aws cloudformation create-change-set 
    --change-set-name "$CHANGE_SET_NAME" 
    --stack-name "$STACK_NAME" 
    --template-body "$TPL_PATH" 
    --change-set-type "UPDATE" 
    --parameters "ParameterKey=MasterUserPassword,UsePreviousValue=true"

第 4 课:使用回滚配置

我合作的另一个团队使用了该功能 云形成,称为 回滚配置。我以前从未遇到过它,但很快意识到它会让我的堆栈部署变得更加酷。现在,每次使用 cloudformation 将代码部署到 lambda 或 ECS 时,我都会使用它。

工作原理:您指定 云监控报警 在参数中 --回滚配置当您创建变更集时。稍后,当您执行一组更改时,aws 会监控警报至少一分钟。如果警报在此期间将状态更改为“警报”,它将回滚部署。

以下是模板摘录的示例 云形成我在其中创建 云表报警,它跟踪云用户指标作为云日志中的错误数量(该指标是通过 度量过滤器):

Resources:
  # this metric tracks number of errors in the cloudwatch logs. In this
  # particular case it's assumed logs are in json format and the error logs are
  # identified by level "error". See FilterPattern
  ErrorMetricFilter:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !Ref LogGroup
      FilterPattern: !Sub '{$.level = "error"}'
      MetricTransformations:
      - MetricNamespace: !Sub "${AWS::StackName}-log-errors"
        MetricName: Errors
        MetricValue: 1
        DefaultValue: 0

  ErrorAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub "${AWS::StackName}-errors"
      Namespace: !Sub "${AWS::StackName}-log-errors"
      MetricName: Errors
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      Period: 1 # 1 minute
      EvaluationPeriods: 1
      Threshold: 0
      TreatMissingData: notBreaching
      ActionsEnabled: yes

现在 报警 可以用作 回滚 执行工具箱时触发:

ALARM_ARN=$1

ROLLBACK_TRIGGER=$(cat <<EOF
{
  "RollbackTriggers": [
    {
      "Arn": "$ALARM_ARN",
      "Type": "AWS::CloudWatch::Alarm"
    }
  ],
  "MonitoringTimeInMinutes": 1
}
EOF
)

aws cloudformation create-change-set 
    --change-set-name "$CHANGE_SET_NAME" 
    --stack-name "$STACK_NAME" 
    --template-body "$TPL_PATH" 
    --change-set-type "UPDATE" 
    --rollback-configuration "$ROLLBACK_TRIGGER"

第 5 课:确保部署最新版本的模板

部署低于最新版本的 cloudformation 模板很容易,但这样做会造成很大的损害。我们曾经遇到过这样的情况:开发人员没有从 Git 推送最新的更改,并且在不知不觉中部署了堆栈的先前版本。这导致使用该堆栈的应用程序停机。

像在提交之前添加检查以查看分支是否是最新的这样简单的事情就可以了(假设 git 是您的版本控制工具):

git fetch
HEADHASH=$(git rev-parse HEAD)
UPSTREAMHASH=$(git rev-parse master@{upstream})

if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
   echo "Branch is not up to date with origin. Aborting"
   exit 1
fi

第六课:不要重新发明轮子

它可能看起来像部署 云形成 - 这很容易。您只需要一堆执行 aws cli 命令的 bash 脚本。

4 年前,我开始使用名为 aws cloudformation create-stack 命令的简单脚本。很快剧本就不再简单了。每一次的教训都让剧本变得越来越复杂。这不仅困难,而且还充满了错误。

我目前在一个小型 IT 部门工作。经验表明,每个团队都有自己的部署 cloudformation 堆栈的方式。这很糟糕。如果每个人都采取相同的方法,那就更好了。幸运的是,有许多工具可帮助您部署和配置 cloudformation 堆栈。

这些课程将帮助您避免错误。

来源: habr.com

添加评论