我在餘生中學到了使用雲端資訊的 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 堆疊。

這些課程將幫助您避免錯誤。

來源: www.habr.com

添加評論