최신 스냅샷에서 루트 볼륨 복원 - AWS Systems Manager

최신 스냅샷에서 루트 볼륨 복원

루트 볼륨의 운영 체제는 여러 가지 이유로 손상될 수 있습니다. 예를 들어 패치 작업 후 손상된 커널 또는 레지스트리로 인해 인스턴스가 성공적으로 부팅되지 않을 수 있습니다. 패치 작업 전에 생성된 최신 스냅샷에서 루트 볼륨을 복원하는 것과 같은 일반적인 문제 해결 태스크를 자동화하면 가동 중지 시간을 줄이고 문제 해결 작업을 신속하게 수행할 수 있습니다. AWS Systems Manager 이러한 경우 Automation 작업이 도움이 될 수 있습니다. Automation은 AWS Systems Manager의 기능입니다.

다음 예제 AWS Systems Manager 런북은 다음과 같은 작업을 수행합니다.

  • aws:executeAwsApi 자동화 작업을 사용하여 인스턴스의 루트 볼륨에서 세부 정보를 검색합니다.

  • aws:executeScript 자동화 작업을 사용하여 루트 볼륨의 최신 스냅샷을 검색합니다.

  • 루트 볼륨에 대한 스냅샷이 발견되면 aws:branch 자동화 작업을 사용하여 자동화를 계속합니다.

YAML
--- description: Custom Automation Troubleshooting Example schemaVersion: '0.3' assumeRole: "{{ AutomationAssumeRole }}" parameters: AutomationAssumeRole: type: String description: "(Required) The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to use this runbook." default: '' InstanceId: type: String description: "(Required) The Instance Id whose root EBS volume you want to restore the latest Snapshot." default: '' mainSteps: - name: getInstanceDetails action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: DescribeInstances InstanceIds: - "{{ InstanceId }}" outputs: - Name: availabilityZone Selector: "$.Reservations[0].Instances[0].Placement.AvailabilityZone" Type: String - Name: rootDeviceName Selector: "$.Reservations[0].Instances[0].RootDeviceName" Type: String nextStep: getRootVolumeId - name: getRootVolumeId action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: DescribeVolumes Filters: - Name: attachment.device Values: ["{{ getInstanceDetails.rootDeviceName }}"] - Name: attachment.instance-id Values: ["{{ InstanceId }}"] outputs: - Name: rootVolumeId Selector: "$.Volumes[0].VolumeId" Type: String nextStep: getSnapshotsByStartTime - name: getSnapshotsByStartTime action: aws:executeScript timeoutSeconds: 45 onFailure: Abort inputs: Runtime: python3.8 Handler: getSnapshotsByStartTime InputPayload: rootVolumeId : "{{ getRootVolumeId.rootVolumeId }}" Script: |- def getSnapshotsByStartTime(events,context): import boto3 #Initialize client ec2 = boto3.client('ec2') rootVolumeId = events['rootVolumeId'] snapshotsQuery = ec2.describe_snapshots( Filters=[ { "Name": "volume-id", "Values": [rootVolumeId] } ] ) if not snapshotsQuery['Snapshots']: noSnapshotFoundString = "NoSnapshotFound" return { 'noSnapshotFound' : noSnapshotFoundString } else: jsonSnapshots = snapshotsQuery['Snapshots'] sortedSnapshots = sorted(jsonSnapshots, key=lambda k: k['StartTime'], reverse=True) latestSortedSnapshotId = sortedSnapshots[0]['SnapshotId'] return { 'latestSnapshotId' : latestSortedSnapshotId } outputs: - Name: Payload Selector: $.Payload Type: StringMap - Name: latestSnapshotId Selector: $.Payload.latestSnapshotId Type: String - Name: noSnapshotFound Selector: $.Payload.noSnapshotFound Type: String nextStep: branchFromResults - name: branchFromResults action: aws:branch onFailure: Abort inputs: Choices: - NextStep: createNewRootVolumeFromSnapshot Not: Variable: "{{ getSnapshotsByStartTime.noSnapshotFound }}" StringEquals: "NoSnapshotFound" isEnd: true - name: createNewRootVolumeFromSnapshot action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateVolume AvailabilityZone: "{{ getInstanceDetails.availabilityZone }}" SnapshotId: "{{ getSnapshotsByStartTime.latestSnapshotId }}" outputs: - Name: newRootVolumeId Selector: "$.VolumeId" Type: String nextStep: stopInstance - name: stopInstance action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: StopInstances InstanceIds: - "{{ InstanceId }}" nextStep: verifyVolumeAvailability - name: verifyVolumeAvailability action: aws:waitForAwsResourceProperty timeoutSeconds: 120 inputs: Service: ec2 Api: DescribeVolumes VolumeIds: - "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}" PropertySelector: "$.Volumes[0].State" DesiredValues: - "available" nextStep: verifyInstanceStopped - name: verifyInstanceStopped action: aws:waitForAwsResourceProperty timeoutSeconds: 120 inputs: Service: ec2 Api: DescribeInstances InstanceIds: - "{{ InstanceId }}" PropertySelector: "$.Reservations[0].Instances[0].State.Name" DesiredValues: - "stopped" nextStep: detachRootVolume - name: detachRootVolume action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: DetachVolume VolumeId: "{{ getRootVolumeId.rootVolumeId }}" nextStep: verifyRootVolumeDetached - name: verifyRootVolumeDetached action: aws:waitForAwsResourceProperty timeoutSeconds: 30 inputs: Service: ec2 Api: DescribeVolumes VolumeIds: - "{{ getRootVolumeId.rootVolumeId }}" PropertySelector: "$.Volumes[0].State" DesiredValues: - "available" nextStep: attachNewRootVolume - name: attachNewRootVolume action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: AttachVolume Device: "{{ getInstanceDetails.rootDeviceName }}" InstanceId: "{{ InstanceId }}" VolumeId: "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}" nextStep: verifyNewRootVolumeAttached - name: verifyNewRootVolumeAttached action: aws:waitForAwsResourceProperty timeoutSeconds: 30 inputs: Service: ec2 Api: DescribeVolumes VolumeIds: - "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}" PropertySelector: "$.Volumes[0].Attachments[0].State" DesiredValues: - "attached" nextStep: startInstance - name: startInstance action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: StartInstances InstanceIds: - "{{ InstanceId }}"
JSON
{ "description": "Custom Automation Troubleshooting Example", "schemaVersion": "0.3", "assumeRole": "{{ AutomationAssumeRole }}", "parameters": { "AutomationAssumeRole": { "type": "String", "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to run this runbook.", "default": "" }, "InstanceId": { "type": "String", "description": "(Required) The Instance Id whose root EBS volume you want to restore the latest Snapshot.", "default": "" } }, "mainSteps": [ { "name": "getInstanceDetails", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "DescribeInstances", "InstanceIds": [ "{{ InstanceId }}" ] }, "outputs": [ { "Name": "availabilityZone", "Selector": "$.Reservations[0].Instances[0].Placement.AvailabilityZone", "Type": "String" }, { "Name": "rootDeviceName", "Selector": "$.Reservations[0].Instances[0].RootDeviceName", "Type": "String" } ], "nextStep": "getRootVolumeId" }, { "name": "getRootVolumeId", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "DescribeVolumes", "Filters": [ { "Name": "attachment.device", "Values": [ "{{ getInstanceDetails.rootDeviceName }}" ] }, { "Name": "attachment.instance-id", "Values": [ "{{ InstanceId }}" ] } ] }, "outputs": [ { "Name": "rootVolumeId", "Selector": "$.Volumes[0].VolumeId", "Type": "String" } ], "nextStep": "getSnapshotsByStartTime" }, { "name": "getSnapshotsByStartTime", "action": "aws:executeScript", "timeoutSeconds": 45, "onFailure": "Continue", "inputs": { "Runtime": "python3.8", "Handler": "getSnapshotsByStartTime", "InputPayload": { "rootVolumeId": "{{ getRootVolumeId.rootVolumeId }}" }, "Attachment": "getSnapshotsByStartTime.py" }, "outputs": [ { "Name": "Payload", "Selector": "$.Payload", "Type": "StringMap" }, { "Name": "latestSnapshotId", "Selector": "$.Payload.latestSnapshotId", "Type": "String" }, { "Name": "noSnapshotFound", "Selector": "$.Payload.noSnapshotFound", "Type": "String" } ], "nextStep": "branchFromResults" }, { "name": "branchFromResults", "action": "aws:branch", "onFailure": "Abort", "inputs": { "Choices": [ { "NextStep": "createNewRootVolumeFromSnapshot", "Not": { "Variable": "{{ getSnapshotsByStartTime.noSnapshotFound }}", "StringEquals": "NoSnapshotFound" } } ] }, "isEnd": true }, { "name": "createNewRootVolumeFromSnapshot", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateVolume", "AvailabilityZone": "{{ getInstanceDetails.availabilityZone }}", "SnapshotId": "{{ getSnapshotsByStartTime.latestSnapshotId }}" }, "outputs": [ { "Name": "newRootVolumeId", "Selector": "$.VolumeId", "Type": "String" } ], "nextStep": "stopInstance" }, { "name": "stopInstance", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "StopInstances", "InstanceIds": [ "{{ InstanceId }}" ] }, "nextStep": "verifyVolumeAvailability" }, { "name": "verifyVolumeAvailability", "action": "aws:waitForAwsResourceProperty", "timeoutSeconds": 120, "inputs": { "Service": "ec2", "Api": "DescribeVolumes", "VolumeIds": [ "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}" ], "PropertySelector": "$.Volumes[0].State", "DesiredValues": [ "available" ] }, "nextStep": "verifyInstanceStopped" }, { "name": "verifyInstanceStopped", "action": "aws:waitForAwsResourceProperty", "timeoutSeconds": 120, "inputs": { "Service": "ec2", "Api": "DescribeInstances", "InstanceIds": [ "{{ InstanceId }}" ], "PropertySelector": "$.Reservations[0].Instances[0].State.Name", "DesiredValues": [ "stopped" ] }, "nextStep": "detachRootVolume" }, { "name": "detachRootVolume", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "DetachVolume", "VolumeId": "{{ getRootVolumeId.rootVolumeId }}" }, "nextStep": "verifyRootVolumeDetached" }, { "name": "verifyRootVolumeDetached", "action": "aws:waitForAwsResourceProperty", "timeoutSeconds": 30, "inputs": { "Service": "ec2", "Api": "DescribeVolumes", "VolumeIds": [ "{{ getRootVolumeId.rootVolumeId }}" ], "PropertySelector": "$.Volumes[0].State", "DesiredValues": [ "available" ] }, "nextStep": "attachNewRootVolume" }, { "name": "attachNewRootVolume", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "AttachVolume", "Device": "{{ getInstanceDetails.rootDeviceName }}", "InstanceId": "{{ InstanceId }}", "VolumeId": "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}" }, "nextStep": "verifyNewRootVolumeAttached" }, { "name": "verifyNewRootVolumeAttached", "action": "aws:waitForAwsResourceProperty", "timeoutSeconds": 30, "inputs": { "Service": "ec2", "Api": "DescribeVolumes", "VolumeIds": [ "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}" ], "PropertySelector": "$.Volumes[0].Attachments[0].State", "DesiredValues": [ "attached" ] }, "nextStep": "startInstance" }, { "name": "startInstance", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "StartInstances", "InstanceIds": [ "{{ InstanceId }}" ] } } ], "files": { "getSnapshotsByStartTime.py": { "checksums": { "sha256": "sampleETagValue" } } } }