본문으로 건너뛰기

Karpenter Spot Event

SQS 생성

import * as aws from "@pulumi/aws";
import * as variable from "@src/variable";

const karpenterInterruptionQueueName = "karpenter-interruption-queue";
export const karpenterInterruptionQueue = new aws.sqs.Queue(karpenterInterruptionQueueName, {
namePrefix: `${karpenterInterruptionQueueName}-`,
messageRetentionSeconds: 300,
sqsManagedSseEnabled: true,
tags: {
Name: karpenterInterruptionQueueName,
"loliot.net/stack": variable.stackName,
},
});

const karpenterInterruptionQueuePolicyName = "karpenter-interruption-queue-policy";
const karpenterInterruptionQueuePolicy = new aws.sqs.QueuePolicy(karpenterInterruptionQueuePolicyName, {
queueUrl: karpenterInterruptionQueue.id,
policy: {
Id: "EC2InterruptionPolicy",
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: {
Service: ["sqs.amazonaws.com", "events.amazonaws.com"],
},
Action: "sqs:SendMessage",
Resource: [karpenterInterruptionQueue.arn],
},
],
},
});

EventBridge 추가

const targetId = "KarpenterInterruptionQueueTarget";

const scheduledChangeRuleName = "scheduled-change-rule";
const scheduledChangeRule = new aws.cloudwatch.EventRule(scheduledChangeRuleName, {
namePrefix: `${scheduledChangeRuleName}-`,
eventPattern: JSON.stringify({
source: ["aws.health"],
"detail-type": ["AWS Health Event"],
}),
tags: {
Name: scheduledChangeRuleName,
"loliot.net/stack": variable.stackName,
},
});

new aws.cloudwatch.EventTarget(`${scheduledChangeRuleName}-target`, {
rule: scheduledChangeRule.name,
arn: karpenterInterruptionQueue.arn,
targetId: targetId,
});

const spotInterruptionRuleName = "spot-interruption-rule";
const spotInterruptionRule = new aws.cloudwatch.EventRule(spotInterruptionRuleName, {
namePrefix: `${spotInterruptionRuleName}-`,
eventPattern: JSON.stringify({
source: ["aws.ec2"],
"detail-type": ["EC2 Spot Instance Interruption Warning"],
}),
tags: {
Name: spotInterruptionRuleName,
"loliot.net/stack": variable.stackName,
},
});

new aws.cloudwatch.EventTarget(`${spotInterruptionRuleName}-target`, {
rule: spotInterruptionRule.name,
arn: karpenterInterruptionQueue.arn,
targetId: targetId,
});

const rebalanceRuleName = "rebalance-rule";
const rebalanceRule = new aws.cloudwatch.EventRule(rebalanceRuleName, {
namePrefix: `${rebalanceRuleName}-`,
eventPattern: JSON.stringify({
source: ["aws.ec2"],
"detail-type": ["EC2 Instance Rebalance Recommendation"],
}),
tags: {
Name: rebalanceRuleName,
"loliot.net/stack": variable.stackName,
},
});

new aws.cloudwatch.EventTarget(`${rebalanceRuleName}-target`, {
rule: rebalanceRule.name,
arn: karpenterInterruptionQueue.arn,
targetId: targetId,
});

const instanceStateChangeRuleName = "instance-state-change-rule";
const instanceStateChangeRule = new aws.cloudwatch.EventRule(instanceStateChangeRuleName, {
namePrefix: `${instanceStateChangeRuleName}-`,
eventPattern: JSON.stringify({
source: ["aws.ec2"],
"detail-type": ["EC2 Instance State-change Notification"],
}),
tags: {
Name: instanceStateChangeRuleName,
"loliot.net/stack": variable.stackName,
},
});

new aws.cloudwatch.EventTarget(`${instanceStateChangeRuleName}-target`, {
rule: instanceStateChangeRule.name,
arn: karpenterInterruptionQueue.arn,
targetId: targetId,
});

Client

현재 v0.28.0 버전에서는 EC2 Instance Rebalance Recommendation 이벤트를 처리하지 않고 있습니다. (https://github.com/aws/karpenter/blob/bf484b63ae500578ac2843f28ba6ba6ab849d83f/pkg/controllers/interruption/controller.go#L288-L295)

Karpenter 설정

const controllerPolicyName = "karpenter-controller-policy";
const controllerPolicy = new aws.iam.Policy(controllerPolicyName, {
namePrefix: `${controllerPolicyName}-`,
policy: {
Version: "2012-10-17",
Statement: [
// ...
{
Sid: "AllowInterruptionQueueActions",
Effect: "Allow",
Resource: karpenterInterruptionQueue.arn,
Action: ["sqs:DeleteMessage", "sqs:GetQueueAttributes", "sqs:GetQueueUrl", "sqs:ReceiveMessage"],
},
],
},
// ...
});
karpenter-values.yaml
settings:
interruptionQueue: <sqsName>

AWS Node Termination Handler