サポート終了するAmazon Linuxのバージョンアップ移行対応(Terraform利用)


目的

もう間もなくとなりますが、2020年中にAmazon Linuxのサポート期間が切れてメンテナンスサポート期間に移行します。
https://aws.amazon.com/jp/blogs/news/update-on-amazon-linux-ami-end-of-life/

メンテナンスサポート
2020 年 12 月 31 日を過ぎると Amazon Linux AMI には新たにメンテナンスサポート期間が設定されます (2023 年 6 月 30 日まで継続)。
この新たなメンテナンスサポート期間中は、Amazon Linux AMI には重要、重大なセキュリティアップデートだけが縮小パッケージセットとして提供されます。
ただし、新しい EC2 プラットフォームキャパシティーや AWS の新機能に対するサポートは保証されません。

本記事では、Amazon LinuxからAmazon Linux2に移行する時の手順と注意点をまとめます。

環境

今回の例は、以下のようなAmazon Linuxで稼働するEC2のバージョンアップになります。

  • AMIはAmazon Linux AMI 2018.03.0.20180508 x86_64 HVM GP2
  • AWSリソースは全体をterraformで管理
  • 複数メンバーの公開鍵を登録し、sshでアクセス可能
  • 外部からsshするためのEIPを持つ

移行

Amazon Linux2への移行は、インプレースアップグレード(稼働中のシステムを直接アップグレードする手法)はサポートされていないので、リプレースアップグレード(稼働中のシステムと別にアップグレード済みシステムを用意して切り替える手法)する必要があります。

よって、以下のような手順で移行します。

  1. 現行のインスタンスと別にAmazon Linux2のAMIを使って新インスタンスを作成。
  2. 一時的に2台を並行稼働した状態で、現行から公開鍵などの必要なデータを新インスタンスに移行。
  3. IPを切り替える。
  4. 現行インスタンスを削除し、新インスタンスのみ稼働。

データコピーが必要なため、現行と同じVPC、subnet、AZに新インスタンスを作成します。

事前確認

Amazon LinuxとAmazon Linux2ではインストール可能なパッケージが違うので、移行が出来るか事前に確認が必要です。
まずは現行のAmazon Linux上で、Amazon Linux2に移行する場合の影響を確認するプレアップグレードアシスタントを実行して影響を確認しておきます。

# プレアップグレードアシスタントをインストール
sudo yum install -y preupgrade-assistant preupgrade-assistant-al1toal2
# アシスタント実行
sudo preupg

詳細な実行結果は以下のようなHTMLファイルで出力されるので、内容を確認します。

6項目のルールのチェック結果とスコアが表示されています。
ルールチェックについてはPythonに関して一件検査が必要だという結果が出ていますが、特に支障はないので移行可能と判断しました。

その他、公式ドキュメントで詳細な違いについて確認しておきます。
* Amazon Linux 2 に関するよくある質問

新インスタンスの作成

terraformでAWSリソースを管理しているので、現行インスタンスのmoduleであるec2をコピーし、nameAMIだけ変更して新インスタンス用moduleとしてec2_newを作成します。
backend.tfにAMI用のssm_parameterのdataを作成して、Amazon Linux 2の最新版を取得します。

backend.tf
data "aws_ssm_parameter" "this" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
main.tf
module "ec2_new" {
  source = "../../../../../../modules/external/github/terraform-aws-modules/terraform-aws-ec2-instance"

  name          = "ec2_new_name"
  ami           = data.aws_ssm_parameter.this.value
  instance_type = "t3.nano"
  key_name      = var.ec2_key_name
  monitoring    = true

  vpc_security_group_ids = [
    data.terraform_remote_state.security_group.outputs.sg_internal_id,
    module.sg.this_security_group_id,
  ]

  subnet_id                   = element(data.terraform_remote_state.vpc.outputs.public_subnets, 0)
  disable_api_termination     = true
  associate_public_ip_address = true
  user_data                   = data.template_cloudinit_config.config.rendered
  iam_instance_profile        = "${title(var.app_name)}ec2_new_name"

terraform apply後、正しく新インスタンスが作成されていることを確認します。

データコピー

新インスタンスに割り当てられたPrivateIPを確認し、現行インスタンスから以下のデータをコピーします。

  • /etc/sudoers:sudo可能ユーザや可能コマンドを管理
  • /etc/passwd:ユーザとパスワードを管理
  • /etc/shadow:各ユーザの暗号化されたパスワードを管理
  • /etc/group:権限グル―プを管理
  • /etc/gshadow:権限グループの暗号化パスワードを管理
  • /home:ユーザごとの公開鍵を含むローカルディレクトリ

データコピー時の注意点

データコピーに際しては、作業するユーザが権限を持たない、rootや他のユーザの所有しているディレクトリに対しても移行する必要があります。
単純にscpでコピー元として/homeを指定しても、パーミッションエラーとなってコピーが出来ません。
そこで、以下の通りssh時にリモート先の/homeをtarで固めてローカルにコピーしてくるという方法を取りました。

# 新インスタンス側で実行する場合
ssh user@現行インスタンス "tar zxvf -C /home -" < /tmp
cp -a /tmp/home/user /home

/homeデータコピーが済むと、AWSが動的に割り当てたGrobalIPを指定して新インスタンスにssh出来るようになります。
この時に、パーミッション相違や移行漏れファイルがないか確認します。
各ユーザは既存のローカル秘密鍵を使い、現行インスタンスから新インスタンスにコピーした公開鍵を使って認証してsshする形になります。

EIPの切り替え

既存のEIPを新インスタンスに付け替えます。
EIPのresorceで指定しているmoduleをec2からec2_newに変更して切り替えます。

main.tf
# 変更後
resource "aws_eip" "this" {
  vpc      = true
  instance = element(module.ec2_new.id, 0)
}

terraform planでEIPの割り当てインスタンスが切り替わることを確認しapplyします。

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place
Terraform will perform the following actions:
  # aws_eip.this will be updated in-place
  ~ resource "aws_eip" "this" {
        association_id    = "eipassoc-0e291430b051514f9"
        domain            = "vpc"
        id                = "eipalloc-060bc8d30f11a933f"
      ~ instance          = "i-039fe23cd242331a7" -> "i-05c1fa53ee28cd971"
        network_interface = "eni-03180ae1d6eaeeeb9"
        private_dns       = "ip-xx-xx-xx-xx.ap-northeast-1.compute.internal"
        private_ip        = "xx.xx.xx.xx"
        public_dns        = "ec2-yy-yy-yy-yy.ap-northeast-1.compute.amazonaws.com"
        public_ip         = "zz.zz.zz.zz"
        public_ipv4_pool  = "amazon"
        tags              = {
            "Name" = "ec2"
        }
        vpc               = true
        timeouts {}
    }
Plan: 0 to add, 1 to change, 0 to destroy.

確認

EIPの切り替えが完了し、改めてsshしてみると以下のような警告メッセージが表示されます。


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:.
Please contact your system administrator.
Add correct host key in /Users/username/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /Users/username/.ssh/known_hosts:6
ECDSA host key for xx.xx.xx.xx has changed and you have requested strict checking.
Host key verification failed.

EIPが変わったことによる警告なので、.ssh/known_hostsの対象インスタンスの行を一度削除すればssh出来るようになります。

現行インスタンス削除

新インスタンスの作成時にコピー元にしたmoduleであるec2を削除します。
terraform planで現行インスタンスのみが削除されることを確認してください。

現行インスタンス削除後、新インスタンス用につけたmodule名をec2_newからec2に戻して移行完了です。

参考