LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

Terraform & Packer での運用におけるサーバの構成変更

こんにちは、エンジニアの野本です。先日、door 賃貸をオンプレから AWS に移行した際、Terraform & Packer を中心に行ったという話を紹介しました。サーバの構成変更が必要な場合は Packer & Ansible で AMI を再作成、Terraform にて入れ替えるといういわゆる Immutable Infrastructure な運用をしているのですが、基本的には無停止でオンラインで入れ替える必要があり、それを行うのに工夫している部分をご紹介します。

Packer / Terraform による構成管理

Packer による AMI の作成

Packer の build にて、Ansible を利用して以下のようなものをプロビジョニングするようにします。

  • 各種ミドルウェアとその基本的な設定
  • 監視 / ログ収集 / デプロイのエージェント

この際、ステージング環境や本番環境に依存するような設定は、実際の起動時に cloud-init で設定を注入出来るように設定ファイルのテンプレートを配置しておくなどの対応をして、その環境の差分をなくすように構築するようにしておきます。

Terraform でのインスタンス起動時の user-data の利用

Packer にて作成した AMI を元にインスタンスを起動しますが、先の Packer のところで説明したように、環境が決まって初めて決定される設定項目は cloud-init を利用して設定を行います。 cloud-init で実行する基本的な user-data を template_file (0.7 以降であれば data として) で作成、Terraform の実行時に環境毎の vars で設定してある内容で user-data を生成するようにします。

このように Packer と Terraform の設定を行い、ステージングでも本番環境に対して行うのとほぼ同等のオペレーションを実施 / 検証出来るようになっています。

Terraform でのサーバの入れ替えの為の設定 / 作業

Auto Scaling グループに対する ELB 付け外しの利用

現在の Auto Scaling グループは ELB の付け外しが出来るようになっているので、これを利用します。Auto Scaling は blue / green の2系統を用意、ELB も本番用 / 確認用の2つを用意しています。

基本的な Terraform の設定はこちら。各設定は説明のために簡略化されてます。

autoscale.tf
resource "aws_launch_configuration" "blue" {
  image_id = "${var.blue_ami_id}"
  instance_type = "${var.blue_instance_type}"
  user_data = "${template_file.user_data_blue.rendered}"
  ...
}

resource "aws_autoscaling_group" "blue" {
  launch_configuration = "${aws_launch_configuration.blue.id}"
  min_size = "${var.blue_min_size}"
  max_size = "${var.blue_max_size}"
  load_balancers = ["${var.blue_elb_name}"]
  ...
}

resource "aws_launch_configuration" "green" {
  image_id = "${var.green_ami_id}"
  instance_type = "${var.green_instance_type}"
  user_data = "${template_file.user_data_green.rendered}"
  ...
}

resource "aws_autoscaling_group" "green" {
  launch_configuration = "${aws_launch_configuration.green.id}"
  min_size = "${var.green_min_size}"
  max_size = "${var.green_max_size}"
  load_balancers = ["${var.green_elb_name}"]
  ...
}
elb.tf
resource "aws_elb" "prd" {
    name = "elb_prd"
    ...
}

resource "aws_elb" "test" {
    name = "elb_test"
    ...
}
codedeploy.tf
resource "aws_codedeploy_app" "app" {
  name = "app"
}

resource "aws_codedeploy_deployment_group" "app" {
  app_name = "${aws_codedeploy_app.app.name}"
  deployment_group_name = "app"
  autoscaling_groups = [
    "${aws_autoscaling_group.blue.id}",
    "${aws_autoscaling_group.green.id}"
  ]
  ...
}
variables.tfvars
blue_ami_id = "ami-12345xyz"
blue_min_size = "2"
blue_max_size = "4"
blue_elb_name = "elb_prd"

green_ami_id = "ami-12345xyz"
green_min_size = "0"
green_max_size = "0"
green_elb_name = "elb_test"

参考にしたものはこちら

また、今回は blue green deploy の話ではないのですが、CodeDeploy を利用して blue / green のAuto Scaling ともに同じ deployment group を設定しておくと、新しく構築した系統のサーバにアプリケーションが自動的にデプロイされて便利です。

実際のオペレーションの手順

前述のように、同一の AMI でステージングでその AMI の検証と Terraform でのオペレーションの確認を行えるようになっています。なので、以降の手順は基本的にはステージングで全く同じ手順で確認作業を行い、問題がなければまた本番で同じ手順で作業を行うという運用を行っています。 ここでは、blue が稼動中の以下のような状態からどのような動きになるのか説明していきます。

各手順においての主な作業は、各手順の下部に記載している variables.tfvars の変数を変更し terraform apply で適応するというものになります。

1. green の設定変更 / 起動

green の台数を指定して、green サーバを起動します。この際、Auto Scaling と Code Deploy の連携により前回成功時のアプリケーションが自動的に green にデプロイされます。

この時はまだ本番の ELB ではなく確認用の ELB にぶら下がっています。起動とデプロイノ完了を確認したら確認用の ELB に対して動作確認を行います。

# variables.tfvars

blue_ami_id = "ami-12345xyz"
blue_min_size = "2"
blue_max_size = "4"
blue_elb_name = "elb_prd"

green_ami_id = "ami-98765abc"       # 新しい AMI を設定
green_min_size = "2"                # AutoScale の台数を設定し EC2 を起動
green_max_size = "4"                # 同上
green_elb_name = "elb_test"         # 確認用の ELB を設定
2. green サーバ群を本番 ELB に設定

正常に動作することが確認出来たら、green サーバ群を本番 ELB に設定します。ここでも Terraform の変数化した ELB の設定箇所を変更し apply します。完了したら green サーバ群でエラーが起っていないか確認を行います。

# variables.tfvars

blue_ami_id = "ami-12345xyz"
blue_min_size = "2"
blue_max_size = "4"
blue_elb_name = "elb_prd"

green_ami_id = "ami-98765abc"
green_min_size = "2"
green_max_size = "4"
green_elb_name = "elb_prd"       # ELB を本番に
3. blue サーバを本番 ELB から切り離す

エラーもなく問題がなければ blue サーバ群を本番 ELB から切り離します。green サーバのみになった後もエラーが起きていないかを確認。問題があれば blue をまた ELB に戻すよう設定を行います。

# variables.tfvars

blue_ami_id = "ami-12345xyz"
blue_min_size = "2"
blue_max_size = "4"
blue_elb_name = ""              # blue の ELB を無効に

green_ami_id = "ami-98765abc"
green_min_size = "2"
green_max_size = "4"
green_elb_name = "elb_prd"
4. blue の台数を 0 に

green サーバ群のみで問題がなければ移行は無事完了ということで blue サーバ群を退役させます。

以降は green サーバ群が稼働中となり、次回入れ替え時は blue を green に置き替えて 1. からまた作業を実施します。

# variables.tfvars
blue_ami_id = "ami-12345xyz"
blue_min_size = "0"             # EC2 を起動しないように
blue_max_size = "0"             # 同上
blue_elb_name = ""

green_ami_id = "ami-98765abc"
green_min_size = "2"
green_max_size = "4"
green_elb_name = "elb_prd"

実際に運用してみて

  • Terraform の変数と AWS の各種機能の連携で導入はさほど難しくない
  • ステージング / 本番で全く同様の手順で操作出来る環境を作っておくことで、作成した AMI のテストや Terraform の挙動などの確認も行える
  • 段階を踏んで入れ替えることでより安全に作業を行える

課題や今後

  • terraform apply する回数が多い / 自動化しづらい

    • 各手順において確認を行いたいので今のところ無理に自動化はしていないがやはり若干の手間は感じている
  • blue green 'deployment'

    • 本サイトのデプロイ頻度などの要因を顧み、平常時のデプロイにおいて blue / green のデプロイは現在行っていない
    • blue / green のデプロイを行える基盤は出来ているので、大幅な改修のデプロイ時には活用出来る
  • Packer build の時間がかかる

まとめ

この仕組みで移行後しばらく運用し、実際に入れ替え作業も何度も行っていますが、問題なく行いたい作業は実現出来ています。カジュアルにサーバを捨てたり入れ替えたり出来る仕組みがあるのは、何か問題があった際は準備してあるサーバを入れ替えてしまえば良いという運用が出来るので、心理的に大分楽になったと思います。

また、クラスメソッドさんのこちらの記事でも同様のオペレーションを実現しており、大変参考になると思います。

以上 Terraform & Packer を使った運用の一部を紹介させていただきました。何かのご参考になればと思います。