検証・本番環境を考慮した EC2インスタンスへのパッチ適用環境を作ってみる【SSMメンテナンスウィンドウ編】

2021.06.22

はじめに

img

上図のように インスタンスの タグを使って パッチを当てる/当てないパッチ内容 をコントロールできるようなパッチ当て環境を実装してみます。

前回のブログでは Patch Manager と State Manager を活用しました。

今回は Patch Manager と Maintenance Windows を使います。

前提条件

今回は Amazon Linux 2 のインスタンス群からなる検証/本番環境を前提として話を進めます。

また、検証環境/本番環境がアカウント分離されているかどうかは問いません。

実現したい環境

  • 時間的制約があるインスタンス※ へ重要パッチを適用させたい
  • 定期的にパッチが当たるようにしたい
  • 事前にどのパッチが当たるかを判断するために、「スキャンのみ」も実施できるようにしたい
  • 検証環境/本番環境でパッチ内容を変えたい(例: タイミングをずらす)

※ここでいう 「時間的制約があるインスタンス」 は、例えば以下のような事情を持つモノです。

  • どうしてもパッチ当て作業の負荷/影響を受けたくない時間帯があるモノ
  • 厳密にメンテナンス期間を指定しているモノ

このようなインスタンスには Maintenance Windows が活かせます。 制約のない場合は Maintenance Windows でも良いですし、 前回ブログ のように State Manager でも良いでしょう。

設計

時間的制約はインスタンスごとに異なります。 その 時間的制約単位でメンテナンスウィンドウを作る 必要があります。

以降の設計は 毎日 AM2:00 – AM3:00 をメンテナンス期間としてパッチ当て可能 な検証/本番インスタンスを対象にした 設計とします。

インスタンスの タグ を使って パッチを当てる/当てないパッチ内容 をコントロールします。 具体的には以下の 2種類のタグを使って、インスタンスを分類してみます。

Patch Group タグ PatchWindowTaskAM2 タグ 分類
STG SCAN 検証環境のインスタンス(AM2:00 -- AM3:00 パッチ当て可)。パッチスキャンのみ
STG INSTALL 検証環境のインスタンス(AM2:00 -- AM3:00 パッチ当て可)。パッチ当てを行う
PRD SCAN 本番環境のインスタンス(AM2:00 -- AM3:00 パッチ当て可)。パッチスキャンのみ
PRD INSTALL 本番環境のインスタンス(AM2:00 -- AM3:00 パッチ当て可)。パッチ当てを行う

この分類に応じた 「Patch Manager ベースライン」「Maintenance Window」設計を行います。

Patch Manager ベースライン

(前回のブログ と同じ内容です。詳細はこちら御覧ください)

以下 2種類のベースラインを作成します。 検証環境インスタンス、本番環境インスタンスでパッチ内容を分けるように設定します。

STG用 ベースライン PRD用 ベースライン
名前 STG-AL2Baseline PRD-AL2Baseline
パッチグループ Patch Group=STG Patch Group=PRD
承認ルール#1 Security/Critical/リリース後 1日 Security/Critical/リリース後 31日
承認ルール#2 Security/Important/リリース後 1日 Security/Important/リリース後 31日

Maintenance Window タスク

メンテナンスウィンドウの設計です。 ざっくりと説明すると、以下のパラメータを定めます。

  • スケジュール
    • タスクを実行できる期間
  • ターゲット
    • タスクを実行するインスタンス
    • インスタンスIDやタグで指定 (今回は タグを利用)
    • 複数作成可
  • タスク
    • ターゲットとSSMドキュメントの組み合わせ
    • 複数作成可

スケジュールは 毎日 AM2:00 -- AM3:00 です。 この時間帯にタスクを実行できるメンテナンスウィンドウを作成します。

パッチ適用を行う SSMドキュメントは AWS-RunPatchBaseline です。 AWS-RunPatchBaseline の主なパラメータは以下 2つ。

  • RebootOption=Reboot/NoReboot … パッチ適用後に再起動するか、しないか
  • Operation=Scan/Install … スキャンのみ行うか、インストールまで行うか

よって、以下のように スキャン用のウィンドウタスクと インストール用のウィンドウタスクを 作成すれば良さそうです。

スキャン用 ウィンドウタスク インストール用 ウィンドウタスク
名前 patch-windowtask-scan patch-windowtask-install
ターゲット PatchWindowTaskAM2=SCAN PatchWindowTaskAM2=INSTALL
Operationパラメータ Scan Install

設計まとめ

img

改めて 実現したい環境と、その解決案をまとめます

  • 時間的制約があるインスタンス へ重要パッチを適用させたい
    • → Maintenance Window を活用
  • 定期的にパッチが当たるようにしたい
    • → Maintenance Window を活用
  • 事前にどのパッチが当たるかを判断するために、「スキャンのみ」も実施できるようにしたい
    • → SCAN用ターゲット/タスク、INSTALL用ターゲット/タスクを作成。 PatchWindowTaskAM2 タグで分類できるようにする
  • 検証環境/本番環境でパッチ内容を変えたい(例: タイミングをずらす)
    • → STG用ベースライン、PRD用ベースラインを作成。 Patch Group タグで分類できるようにする

構築

Patch Manager ベースライン

前回ブログ と変わりません。 以下のような CloudFormation テンプレートを作成して、展開しています。 Patch Manager ベースラインを 2つ展開します。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  # --- PatchBaseline(STG)
  BaseLineNameSTG:
    Type: String
    Default: STG-AL2Baseline
  PatchGroupSTG:
    Type: String
    Default: STG
  ApproveAfterDaysSTG:
    Type: Number
    Default: 1

  # --- PatchBaseline(PRD)
  BaseLineNamePRD:
    Type: String
    Default: PRD-AL2Baseline
  PatchGroupPRD:
    Type: String
    Default: PRD
  ApproveAfterDaysPRD:
    Type: Number
    Default: 31

Resources:
  # --- PatchBaseline(STG)
  STGBaseline:
    Type: AWS::SSM::PatchBaseline
    Properties: 
      Name: !Ref BaseLineNameSTG
      OperatingSystem: AMAZON_LINUX_2
      PatchGroups: 
        - !Ref PatchGroupSTG
      ApprovalRules:
        PatchRules:
          - ApproveAfterDays: !Ref ApproveAfterDaysSTG
            ComplianceLevel: CRITICAL
            EnableNonSecurity: false
            PatchFilterGroup: 
              PatchFilters:
                - Key: CLASSIFICATION
                  Values: [Security]
                - Key: SEVERITY
                  Values: [Critical]
          - ApproveAfterDays: !Ref ApproveAfterDaysSTG
            ComplianceLevel: HIGH
            EnableNonSecurity: false
            PatchFilterGroup: 
              PatchFilters:
                - Key: CLASSIFICATION
                  Values: [Security]
                - Key: SEVERITY
                  Values: [Important]

  # --- PatchBaseline(PRD)
  PRDBaseline:
    Type: AWS::SSM::PatchBaseline
    Properties: 
      Name: !Ref BaseLineNamePRD
      OperatingSystem: AMAZON_LINUX_2
      PatchGroups: 
        - !Ref PatchGroupPRD
      ApprovalRules:
        PatchRules:
          - ApproveAfterDays: !Ref ApproveAfterDaysPRD
            ComplianceLevel: CRITICAL
            EnableNonSecurity: false
            PatchFilterGroup: 
              PatchFilters:
                - Key: CLASSIFICATION
                  Values: [Security]
                - Key: SEVERITY
                  Values: [Critical]
          - ApproveAfterDays: !Ref ApproveAfterDaysPRD
            ComplianceLevel: HIGH
            EnableNonSecurity: false
            PatchFilterGroup: 
              PatchFilters:
                - Key: CLASSIFICATION
                  Values: [Security]
                - Key: SEVERITY
                  Values: [Important]

▼ (参考) STG用ベースライン

img

▼ (参考) PRD用ベースライン

img

Maintenance Window

以下 CloudFormation テンプレートを作成して、展開しました。 メンテナンスウィンドウ、およびウィンドウターゲット/タスクを作成しています。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  WindowName:
    Type: String
    Default: patch-window-am2
  ScheduleExpression:
    Type: String
    Default: "cron(00 17 * * ? *)"
  DurationHours: 
    Type: Number
    Default: 1
  Cutoff:
    Type: Number
    Default: 0
  TargetKey:
    Type: String
    Default: PatchWindowTaskAM2
  TimeoutSeconds:
    Type: Number
    Default: 1800

  # --- Window Target(Scan)
  TargetValueScan:
    Type: String
    Default: SCAN

  # --- Window Target(Install)
  TargetValueInstall:
    Type: String
    Default: INSTALL

  # --- Logging
  OutputS3BucketName:
    Type: String
  OutputS3KeyPrefixScan:
    Type: String
    Default: "patch/window/scan/"
  OutputS3KeyPrefixInstall:
    Type: String
    Default: "patch/window/install/"

Resources:
  # --- Window
  Window:
    Type: AWS::SSM::MaintenanceWindow
    Properties: 
      Name: !Ref WindowName
      AllowUnassociatedTargets: true
      Cutoff: !Ref Cutoff
      Duration: !Ref DurationHours
      Schedule: !Ref ScheduleExpression
  
  # --- Window Target/Task(Scan)
  WindowTargetScan:
    Type: AWS::SSM::MaintenanceWindowTarget
    Properties:
      Name: PatchingTargetScan
      ResourceType: INSTANCE
      Targets:
        - Key: !Sub "tag:${TargetKey}"
          Values:
            - !Ref TargetValueScan
      WindowId: !Ref Window
  WindowTaskScan:
    Type: AWS::SSM::MaintenanceWindowTask
    Properties:
      MaxConcurrency: "50%"
      MaxErrors: "50%"
      Name: PatchingTaskScan
      Priority: 1
      Targets:
        - Key: WindowTargetIds
          Values:
            - !Ref WindowTargetScan
      TaskArn: AWS-RunPatchBaseline
      TaskType: RUN_COMMAND
      WindowId: !Ref Window
      TaskInvocationParameters:
        MaintenanceWindowRunCommandParameters:
          TimeoutSeconds: !Ref TimeoutSeconds
          Parameters:
            Operation: ["Scan"]
            RebootOption: ["NoReboot"]
          OutputS3BucketName: !Ref OutputS3BucketName
          OutputS3KeyPrefix: !Ref OutputS3KeyPrefixScan
  
  # --- Window Target/Task(Install)
  WindowTargetInstall:
    Type: AWS::SSM::MaintenanceWindowTarget
    Properties:
      Name: PatchingTargetInstall
      ResourceType: INSTANCE
      Targets:
        - Key: !Sub "tag:${TargetKey}"
          Values:
            - !Ref TargetValueInstall
      WindowId: !Ref Window
  WindowTaskInstall:
    Type: AWS::SSM::MaintenanceWindowTask
    Properties:
      MaxConcurrency: "50%"
      MaxErrors: "50%"
      Name: PatchingTaskInstall
      Priority: 1
      Targets:
        - Key: WindowTargetIds
          Values:
            - !Ref WindowTargetInstall
      TaskArn: AWS-RunPatchBaseline
      TaskType: RUN_COMMAND
      WindowId: !Ref Window
      TaskInvocationParameters:
        MaintenanceWindowRunCommandParameters:
          TimeoutSeconds: !Ref TimeoutSeconds
          Parameters:
            Operation: ["Install"]
            RebootOption: ["NoReboot"]
          OutputS3BucketName: !Ref OutputS3BucketName
          OutputS3KeyPrefix: !Ref OutputS3KeyPrefixInstall

▼ ざっくりリソース解説

大枠として patch-window-am2 というメンテナンスウィンドウを作成します。 毎日 AM2:00 -- AM3:00 の間をメンテナンス期間とします。 このウィンドウ配下にターゲット、およびタスクを登録していきます。

次に、それぞれターゲット(WindowTarget[Scan|Install])を作成します。 タグ指定です。 PatchWindowTaskAM2 タグによって、どちらのターゲットかを指定します。

そして、それぞれタスク(WindowTask[Scan|Install])を作成しています。 タスクは「ターゲット」と「実行するSSMドキュメント」の組み合わせです。 SCAN用ターゲットには 「Operation=Scan」パラメータを渡した AWS-RunPatchBaseline を、 INSTALL用ターゲットには 「Operation=Install」パラメータを渡した AWS-RunPatchBaseline をタスクとして登録します。

また、SSMドキュメントの実行ログを S3バケットへ格納する設定を入れています。 S3バケットへ格納しない場合は、 Run Command のコンソールから実行ログ(24,000文字まで)を確認できます。 AWS-RunPatchBaseline の実行ログは多いため、ほぼ 24,000文字以降のログが省略されます。 証跡を残すためにも S3へ保存しましょう。

▼ (参考) メンテナンスウィンドウ

img

▼ (参考) メンテナンスウィンドウ ターゲット

img

img

img

▼ (参考) メンテナンスウィンドウ タスク

img

img

img

img

img

確認

STG環境インスタンスへ「スキャン」を行う

以下のようなタグ情報を付与したEC2インスタンスを作成しました。

img

1日経った後にメンテナンスウィンドウを確認します。履歴からパッチスキャン実行を確認できました。

img

Run Command の実行ログも コンソールで確認。さらに S3バケットへ格納されていることを確認できました。

img

PRD環境インスタンスへ「パッチ当て」を行う

次に以下 タグ情報を付与したEC2インスタンスを作成してみます。

img

1日経った後にメンテナンスウィンドウを確認します。履歴からパッチインストール実行を確認できました。

img

Run Command の実行ログも コンソールで確認。さらに S3バケットへ格納されていることを確認できました。

img

おわりに

以上、メンテナンスウィンドウを使ったパッチ適用環境の一例でした。

前回ブログ の State Managerとの使い分けを簡単にまとめます

  • 定期的なパッチ当てをしたい → どちらでもできます
  • Auto Scaling から起動されるインスタンスも等しくパッチ適用されている状態にしたい → State Manager がおすすめです
  • 時間的制約があるインスタンスへパッチ適用したい → Maintenance Windows がおすすめです

Patch Manager 活用の際の参考になれば幸いです。

参考