はじめに
表題の通り、コンピューティング環境のインスタンスタイプを変更しようとしてちょっと手こずりましたので対応を共有します。
賢明な方はお気づきかと思いますが、インスタンスタイプは後から変更できないので、厳密には再作成してサッと切り替えるという話になります。
なお記事の最後に注意点も書きましたので、重要な環境で実行する前によければご確認ください。
TL;DR
- コンピューティング環境のインスタンスタイプは
Update requires: Replacementなパラメータなので、変更する場合はコンピューティング環境の再作成が必要になる - しかしジョブキューが元のコンピューティング環境に依存しているため、そのままでは再作成できない
- 対策として、
instance_typeとあわせてcompute_environment_nameを変更するとともに、lifecycleにcreate_before_destroy = trueを明示的に指定する
雰囲気でやるとどうなるか
- 以下のサンプルコードをapplyすると、最低限AWS Batchに必要なリソースが作成されます
resource "aws_iam_role" "ecs_instance_role" {
name = "ecs_instance_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
}
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "ecs_instance_role" {
role = "${aws_iam_role.ecs_instance_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}
resource "aws_iam_instance_profile" "ecs_instance_role" {
name = "ecs_instance_role"
role = "${aws_iam_role.ecs_instance_role.name}"
}
resource "aws_iam_role" "aws_batch_service_role" {
name = "aws_batch_service_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "batch.amazonaws.com"
}
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "aws_batch_service_role" {
role = "${aws_iam_role.aws_batch_service_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole"
}
resource "aws_security_group" "sample" {
name = "aws_batch_compute_environment_security_group"
}
resource "aws_vpc" "sample" {
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "sample" {
vpc_id = "${aws_vpc.sample.id}"
cidr_block = "10.1.1.0/24"
}
resource "aws_batch_compute_environment" "sample" {
compute_environment_name = "sample"
compute_resources {
instance_role = "${aws_iam_instance_profile.ecs_instance_role.arn}"
instance_type = [
"c5",
]
max_vcpus = 4
min_vcpus = 0
security_group_ids = [
"${aws_security_group.sample.id}",
]
subnets = [
"${aws_subnet.sample.id}",
]
type = "EC2"
}
service_role = "${aws_iam_role.aws_batch_service_role.arn}"
type = "MANAGED"
depends_on = ["aws_iam_role_policy_attachment.aws_batch_service_role"]
}
resource "aws_batch_job_queue" "test_queue" {
name = "test_queue"
state = "ENABLED"
priority = 1
compute_environments = ["${aws_batch_compute_environment.sample.arn}"]
}
- インスタンスタイプをc5->m5に書き換えてみます
- planを確認すると、コンピューティング環境を再作成しようとしています
-/+ aws_batch_compute_environment.sample (new resource required)
id: "sample" => <computed> (forces new resource)
...
compute_resources.0.instance_type.2800388089: "c5" => "" (forces new resource)
compute_resources.0.instance_type.2893327091: "" => "m5" (forces new resource)
...
~ aws_batch_job_queue.test_queue
compute_environments.#: "1" => <computed>
- applyすると、ジョブキューが再作成前のコンピューティング環境に依存しているという理由で死亡します
aws_batch_compute_environment.sample: Destroying... (ID: sample)
Error: Error applying plan:
1 error(s) occurred:
* aws_batch_compute_environment.sample (destroy): 1 error(s) occurred:
* aws_batch_compute_environment.sample: error deleting Batch Compute Environment (sample): : Cannot delete, found existing JobQueue relationship
対策
- Terraformでリソースが再作成される場合、デフォルトでは削除→作成の順序で実施されます。ですので
compute_environment_nameだけ変更してapplyすると、一時的にジョブキューが依存するコンピューティング環境が存在しない状態になるので、下記のように死亡します。
Error: Error applying plan:
1 error(s) occurred:
* aws_batch_compute_environment.sample (destroy): 1 error(s) occurred:
* aws_batch_compute_environment.sample: error deleting Batch Compute Environment (sample): : Cannot delete, found existing JobQueue relationship
- そのため、
compute_environment_nameを変更するとともに、lifecycleにcreate_before_destroy = trueを明示的に指定します
resource "aws_batch_compute_environment" "sample" {
compute_environment_name = "sample-v2"
...
instance_type = [
"m5",
]
...
lifecycle {
create_before_destroy = true
}
}
まとめと注意
以上の方法でコンピューティング環境名が変わることは不本意ながら、意図した結果になります。
これは Update requires: Replacement なパラメータ全般に言える話で、他にはBidPercentageあたり変更したいケースがあるかなと思いました。
ここで取り上げたインスタンスタイプはコストを予測するためにある程度固定したい場合が多いと思いますが、optimal(ジョブキューの需要に見合ったインスタンスタイプをオンザフライで使用する)を選択できる場合はそうしたほうが楽かもしれません。
最後にここまで書いておいてそれかい!という注意です。
今回書いたように一撃でコンピューティング環境を切り替えるためには、元のコンピューティング環境でジョブが実行されていない状態にする必要があります。
CloudWatch等から定期的にsubmitJobするようなケースではタイミングを見計らって一時的にルールを無効にする等で対応可能ですが、アプリケーションから任意のタイミングでsubmitJobするような場合、そういう状況を作るのはなかなか難しいでしょう。
ですので実際には、
- 新コンピューティング環境を作成する
- ジョブキューに「優先度1: 新環境、優先度2: 旧環境」として割り当てる
- 旧環境で実行されているジョブがなくなったら、旧環境削除
みたいな段取りを踏むことになる場合が多いかなと。
そうなると、じゃあそういうのどこまで Infrastructure as Code みたいな枠組みでやったほうがいいの、的な議論に発展する機運が高まりますがそれはそれとして、今回のお話は限定的な状況でやってみたことの共有ということで、ここまでとさせていただきます。