コード化中
- AWS CDKの導入
- CodePipelineのコード化
- EventBridge Schedulerのコード化(この記事)
- ECS clusterとserviceのコード化
- ElastiCacheのコード化、本番環境構築
ECS task definistion
EventBridge Schedulerの中でタスク定義を使用するので合わせてコード化します
特筆することは特に無いです
先輩が作成した環境と同じになるように実装しました
var taskExecutionRole: Role = Role.Builder
.create(this, "developmentMyappBatchCdkTaskExecutionRole")
.roleName("developmentMyappBatchCdkTaskExecutionRole")
.assumedBy(ServicePrincipal.Builder
.create("ecs-tasks.amazonaws.com")
.build()
)
.managedPolicies(listOf(
ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy")
))
.build()
var taskDefinition: TaskDefinition = TaskDefinition.Builder
.create(this, "developmentMyappBatchCdkEcsTaskDefinition")
.family("developmentMyappBatchCdkEcsTaskDefinition")
.compatibility(Compatibility.FARGATE)
.cpu("1024")
.memoryMiB("2048")
.executionRole(taskExecutionRole)
.taskRole(taskExecutionRole)
.runtimePlatform(RuntimePlatform.builder()
.operatingSystemFamily(OperatingSystemFamily.LINUX)
.cpuArchitecture(CpuArchitecture.X86_64)
.build())
.build()
val logGroup: LogGroup = LogGroup.Builder
.create(this, "developmentMyappBatchCdkEcsContainerLogGroup")
.logGroupName("/ecs/" + "developmentMyappBatchCdkEcsContainer")
.retention(RetentionDays.INFINITE)
.build()
val ecsEnvironment = context["ecsEnvironment"] as Map<String, String>
val dbSettings = context["dbSettings"] as Map<String, String>
taskDefinition.addContainer(
"developmentMyappBatchCdkTaskContainerDefinition",
ContainerDefinitionOptions.builder()
.containerName("batch")
.image(ContainerImage.fromEcrRepository(ecrRepository))
.cpu(1024)
.memoryLimitMiB(2048)
.essential(true)
.environment(ecsEnvironment + dbSettings)
.logging(LogDriver.awsLogs(AwsLogDriverProps.builder()
.logGroup(logGroup)
.streamPrefix("ecs")
.build()))
.build()
)
EventBridge Scheduler
EventBridge SchedulerはL2のライブラリが無いのでL1のCfnScheduleを使用します
EventBridge Schedulerの設定については先輩の記事をどうぞ
複数のバッチを定義したいのでListにしてループさせています
val awsAccountId = this.getNode().tryGetContext("awsAccountId").toString()
val batchTaskDefinitionArn = ecsTaskDefinition.getTaskDefinitionArn()
val batchSecurityGroup = context["batchSecurityGroup"].toString()
val batchSubnet = context["batchSubnet"].toString()
// バッチを定義
// listOf("batch name", "cron式", "ENABLED or DISABLED", "command")
val scheduleArray = listOf(
listOf(target + "MyappBatch1", "cron(0 0 * * ? *)", "ENABLED", "--batch.execute=batch1"),
listOf(target + "MyappBatch2", "cron(0 0 * * ? *)", "ENABLED", "--batch.execute=batch2")
)
val group: CfnScheduleGroup = CfnScheduleGroup.Builder
.create(this, target + "MyappBatchCdkScheduleGroup")
.name(target + "MyappBatchCdkScheduleGroup")
.build()
val runTaskPolicy: PolicyDocument = PolicyDocument.Builder
.create()
.statements(listOf(
PolicyStatement.Builder
.create()
.actions(listOf("ecs:RunTask"))
.resources(listOf(batchTaskDefinitionArn))
.conditions(mapOf(
"ArnLike" to mapOf("ecs:cluster" to ecsClusterArn)
))
.build()
))
.build()
val passRolePolicy: PolicyDocument = PolicyDocument.Builder
.create()
.statements(listOf(
PolicyStatement.Builder
.create()
.actions(listOf("iam:PassRole"))
.resources(listOf(ecsTaskExecutionRole.getRoleArn()))
.build()
))
.build()
val role: Role = Role.Builder
.create(this, target + "MyappBatchCdkSchedulerRole")
.roleName(target + "MyappBatchCdkSchedulerRole")
.assumedBy(ServicePrincipal.Builder
.create("scheduler.amazonaws.com")
.conditions(mapOf(
"StringEquals" to mapOf("aws:SourceAccount" to awsAccountId)
))
.build()
)
.managedPolicies(listOf(
ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy")
))
.inlinePolicies(mapOf(
"RunTaskPolicy" to runTaskPolicy,
"PassRolePolicy" to passRolePolicy
))
.build()
scheduleArray.forEach {
val targetProperty: TargetProperty = TargetProperty.builder()
.arn(ecsClusterArn)
.ecsParameters(EcsParametersProperty.builder()
.taskDefinitionArn(batchTaskDefinitionArn)
.taskCount(1)
.launchType("FARGATE")
.networkConfiguration(NetworkConfigurationProperty.builder()
.awsvpcConfiguration(AwsVpcConfigurationProperty.builder()
.subnets(listOf(batchSubnet))
.securityGroups(listOf(batchSecurityGroup))
.assignPublicIp("ENABLED")
.build())
.build())
.platformVersion("LATEST")
.enableEcsManagedTags(true)
.enableExecuteCommand(true)
.build())
.input("""{
"containerOverrides": [
{
"name": "batch",
"command": ["${it[3]}"]
}
]
}""")
.roleArn(role.getRoleArn())
.retryPolicy(RetryPolicyProperty.builder()
.maximumEventAgeInSeconds(3600) // 1h = 60 * 60
.maximumRetryAttempts(2)
.build())
.build()
CfnSchedule.Builder
.create(this, it[0])
.flexibleTimeWindow(FlexibleTimeWindowProperty.builder()
.mode("OFF")
.build())
.name(it[0])
.groupName(group.getName())
.scheduleExpression(it[1])
.scheduleExpressionTimezone("Asia/Tokyo")
.state(it[2])
.target(targetProperty)
.build()
}
ファイル分割
これを全部 BatchStack.kt
にまとめてしまうのは読みにくいのでファイル分割を行いました
BatchStack.kt
class BatchStack(
parent: Construct,
name: String,
stackProps: StackProps,
ecsClusterArn: String
): Stack(parent, name, stackProps) {
init {
val context = this.getNode().tryGetContext("development") as Map<String, Any>
val pipeline = Pipeline(this, context)
pipeline.createPipeline()
val ecs = Ecs(this, context)
ecs.createTaskDefinition(pipeline.ecrRepository)
val eventBridge = EventBridge(this, context)
eventBridge.createScheduler(
ecs.taskDefinition,
ecs.taskExecutionRole,
ecsClusterArn)
}
}
recources/batch/Pipeline.kt
class Pipeline(val scope: Construct, val context: Map<String, Any>) {
lateinit var ecrRepository: Repository
fun createPipeline() {
...
}
}
resources/batch/Ecs.kt
class Ecs(val scope: Construct, val context: Map<String, Any>) {
lateinit var taskDefinition: TaskDefinition
lateinit var taskExecutionRole: Role
fun createTaskDefinition(ecrRepository: Repository) {
...
}
}
resources/batch/EventBridge.kt
class EventBridge(val scope: Construct, val context: Map<String, Any>) {
fun createScheduler(
ecsTaskDefinition: TaskDefinition,
ecsTaskExecutionRole: Role,
ecsClusterArn: String
) {
...
}
}
次回
ここまででバッチのリポジトリに関連するコード化が終了したので、次はDeployStage内のstackを増やしてAPIと管理画面のリポジトリに関するコード化を行います