Terraform 0.12 Upgrade Battle!

こんにちは。
マネーフォワードでインフラエンジニアとして働いているgotoken(@kennygt51)です。
今回は、先日弊社のインフラチームで取り組んだ「Terraformのバージョンをチーム一丸となって一気にアップグレードする会」こと 『Terraform 0.12 Upgrade Battle!』 について紹介します。

 

『Terraform 0.12 Upgrade Battle!』開催の経緯

Terraform 0.12 リリースまで

マネーフォワードは、提供する各種サービスを動かすためのインフラとして、AWSを利用しています。そしてそのAWSリソースのほぼ全てを、Terraformを用いてコード管理しています。

遡ること半年ほど前でしょうか。実はインフラチームのメンバーは皆、事実上のメジャーバージョンアップであるTerraform 0.12のリリース を密かに待ちわびていました。Slackを見ると、何らかの情報解禁があるたびに盛り上がっていたのがうかがえます。

リリースが近づいてウズウズしています。

リリース日当日。待ちすぎて半信半疑。

そして2019年5月、待ち焦がれたTerraform 0.12がリリースされました。

リリースされたはいいものの…

Terraform 0.12がリリースされたのは大変喜ばしいことでした。ですが、AWS環境を管理しているコードを0.12にアップグレードするのは、以下に挙げるいくつかの理由が原因で簡単ではありませんでした。

  • コード量が膨大
    先程も述べたとおり、弊社では利用しているAWSリソースのほぼ全てをTerraformでコード管理しています。そのため修正対象のコード量はかなりの量でした。(16000行くらい)

  • アップグレード対応中はコードフリーズする必要がある
    日々舞い込んでくる依頼作業やインフラ改善対応に取り組む上で、Terraformのコードを修正する機会は毎日のようにあります。一方で、Terraformのアップグレード対応中は対象のコードの更新を一時的に止めておく必要があります。そのため更新頻度の多いworkspaceのアップグレードに取り組むのが難しい状況でした。

  • 普通に忙しい
    メンバー全員が目の前の業務に追われており、なかなか時間を作れないという現実がありました。

結果として、あんなにもリリースを心待ちにしていたのにも関わらず「早く0.12にアップグレードしたい。でもできない」という悶々とした時間を過ごすことになってしまいました。

そして3ヶ月経過した

インフラチームはスクラムを導入して日々の業務に取り組んでいます。スプリントの振り返り(レトロスペクティブ)でも「Terraform 0.12へのアップグレードが進まないこと」はたびたび課題としてあがっていました。

気がつけばTerraform 0.12のリリースから3ヶ月が経過 していました…
メンバー全員が「このままではまずい」と心の中で感じていたことでしょう。

そしてその瞬間は突然訪れました。Terraform 0.12のリリースから3ヶ月後のレトロスペクティブで「Terraform 0.12のアップグレードが進まない課題を、次のイテレーションで必ず解決しよう!」という意思決定をおこないました。チーム内で話し合った結果、次のイテレーションで 「既存Terraformコードのアップグレード作業 だけする一日を設定しメンバー全員で一気に片付ける」 ことに挑戦することにしました。

これが『Terraform 0.12 Upgrade Battle!』を開催するに至った経緯です。

『Terraform 0.12 Upgrade Battle』当日

当日は、毎朝開催している朝会終了後すぐに参加者全員で会議室に集まりました。8人のメンバが参加者となったため、2人ペアの4チームに分けて、workspace単位で作業の割り振りをおこないました。
作業の進め方としては「1人が修正作業を実施しもう1人が横から指示や気付いた事を言う」「作業者は時間で区切って交代する」というペアプロのようなスタイルで取り組むことにしました。
また僕のチームは、机の横の巨大なモニタにPCの画面を映しながら作業を進めました。

専用のSlackチャンネルを作ってワイワイガヤガヤしてました。

アップグレード作業の流れ

ここから、我々も取り組んだ具体的なアップグレード作業の流れについて簡単に説明します。基本的にはHashiCorp社の公式ドキュメントである『Upgrading to Terraform v0.12』に従って作業をすすめます。

Terraform 0.11 を最新版にする

もしローカルの端末のTerraformのバージョンが0.11の最新版でないのであれば、まずアップグレードして0.11.14にしておきます。作業中は複数バージョンのTerraformを切り替えて使うことが多いので、バージョンを簡単に切り替えることが出来るtfenvというツールを利用するのがオススメです。

事前に変更差分がない状態にしておく

アップグレード作業を開始する前に terraform apply を実行してコードとリソースを一致させておきましょう。その後terraform planを実行して、差分が出力されないことを確認します。

Terraform 0.11 でアップグレード事前確認をする

Terraform 0.11.14 には、 0.12 へのアップグレード時に問題となりそうな箇所をチェックする 0.12checklist サブコマンドが用意されています。

$ terraform init
$ terraform 0.12checklist

よほど問題のある書き方をしていない限り、ここでのチェックは Looks good! という出力とともに正常終了します。ただし、ここでチェックが通ったとしても、後ほど大きな変更が必要になることが多々あります。(ここでチェックが通ったからといって簡単にアップグレードできるわけではありません)この時点で問題があれば修正しておきましょう。

Terraform 0.12に切り替えてアップグレードする

Terraform を0.12に切り替えた上で0.12upgradeサブコマンドを実行します。

$ tfenv use 0.12.7
[INFO] Switching to v0.12.7
[INFO] Switching completed

$ terraform 0.12upgrade

このサブコマンドは、既存のコードを機械的にTerraform 0.12の構文で置換してくれます。ただし置換に失敗するケースもあるため、手作業での修正が必要になります。

差分がなくなるまでtfファイルを修正する

ここから泥臭い修正作業が始まります。具体的にはterraform validate及びterraform planを繰り返します。

terraform validate を実行することで、基本的なシンタックスエラーの有無を確認できます。まずは terraform validate が通るところを目指すのがよいでしょう。

terraform planを実行した結果差分が出力されない状態になれば、既存のコードを0.12にアップグレードできた、ということになります。
差分が出力されなくなったら、最後の段階に進みます。

terraform applyを実行して変更がないことを確認する

ここまでくれば、あとはterraform applyを実行するだけです。コマンドが正しく終了し、差分が出力されなければ、アップグレード作業は完了です。
terraform plan実行時はエラーとして出力されなかったのにこのタイミングでエラーが出力された、というケースも多々あるので、その時は出力されたエラーを元に修正をおこないます。

 

『Terraform 0.12 Upgrade Battle!』の結果

10:30くらいから作業を初めて、色々とハマりながらも、17:30頃には全てのworkspaceでのアップグレード作業が完了しました。

結果的に修正したコード量は5000行弱でした。

全ての修正が完了したブランチをmasterに反映するPR。

LGTM祭り。

 

取り組みを通じて得られたこと

今回の取り組みを通じて 「技術的な知見」「チームとしての成長」 が得られました。

技術的な知見

Terraform 0.12へのアップグレードに関する技術的な知見をたくさん得ることができました。
代表的なものを、以下にまとめます。

式エラー

validateで簡単に見つかります。

Error: Invalid expression

型の不一致エラー

Error: Incorrect attribute value type

このケースは非常に多く書き方によって修正方法も違うので、よく読んで修正する必要があります。

output の名前に一部記号が使えなくなった

output の名前に一部記号を使うことができなくなりました。
例えば.を使った以下のコードは、Error: Invalid output nameというエラーが出力されてしまいます。

output "aws_iam_policy.arn" {
    value = "foo"
}

output "aws_iam_policy.name" {
    value = "bar"
}

以前と同じようなアクセスを期待する場合は、 value に map を設定する書き方に変更する必要がありました。

output "aws_iam_policy" {
    value = {
        arn = "foo"
        name = "bar"
    }
}

属性が複数回定義されているエラー

Error: Attribute redefined

「属性が複数回定義されている」とのことですが、使い方を誤っている場合にも発生します。
例えば data source での filter 指定に誤って = を記述すると起こります。

data "foo" "bar" {
    filter = {
        name = "Name"
        value = "bar"
    }

    filter = {
        name = "Foo"
        value = "Bar"
    }
}

filter は block なので =を使用しません。

data "foo" "bar" {
    filter {
        name = "Name"
        value = "bar"
    }

    filter {
        name = "Foo"
        value = "Bar"
    }
}

類似の例として arguments のものを block のように書いてしまっている例もあります。例えば tagsarguments です。

tags {
  name = "foo"
}

正しい例はこちらです。

tags = {
  name = "foo"
}

module source の相対パス指定

以下の場合はカレントディレクトリからのパス指定であることを明示するため、必ず ./ をつける必要があります。

module "foo" {
  source = "modules/foo"
}

正しい例はこちらです。

module "foo" {
  source = "./modules/foo"
}

list argument の厳格化変更

list を要求するargumentの場合、 [list1, list2] は実質的に concat と同じ動作をしていました。
Terraform 0.12 では明示的に flattenconcat を使って list を正しく定義する必要があります。

今までは以下の書き方で問題ありませんでした。

foo = [list1, list2]

Terraform 0.12では、以下のように定義する必要があります。

foo = concat(list1, list2)

また以前は、以下のように異なる型を同じリストに定義できましたが、同様に不可になりました。

foo = [list1, var1]

Terraform 0.12では、以下のように定義する必要があります。

foo = concat(list1, [var1])

list ではない値を返す DataSource

aws_subnet_ids.*.ids などを以前と同じ振る舞いにするには tolist を追加します。

ids = aws_subnet_ids.foo.ids

Terraform 0.12では、以下のように定義する必要があります。

ids = tolist(aws_subnet_ids.foo.ids)

ヒアドキュメントのインデントが trim される

例えば、 user_data の指定などでヒアドキュメントを使って記述しているとハッシュ値が代わり再実行されてしまうことがあります。

配列やマップの中に記述されているコメントが消える場合がある

0.12upgradeサブコマンドを実行した結果、配列やマップの中に記述されているコメントが消えるケースに遭遇しました。 terraform apply実行前に、ちゃんとコードレビューするようにしましょう。

チームとしての成長

このような技術的な知見を得られたこと以外にも、チームとして仕事をすすめる為の方法論を獲得できたことが大きな収穫だったと考えています。
タスクを「緊急度」と「重要度」で分割した時に、「大事だけど緊急でないこと」はつい延期されがちです。そんなタスクを一気に終わらせる手段として、今回取り組んだやり方(モブプロに近い形で時間を取って一気に終わらせる)は、大変良い手段だということがわかりました。
具体的なメリットを挙げると、以下のとおりです。

  • 一人でコツコツやるとひたすら辛いけど、皆でやればお祭り感覚で楽しい
  • 詰まった時にすぐ聞けるので意外と効率が良い(画面に写したりしてすぐ共有できる)
  • 本筋とは違う思いもよらぬ知見が得られる(ペアプロを通じて他の人が使っていた便利なツールを知ることができたりした)

チームとしてパフォーマンスを出す方法論を1つ獲得できたことで、我々インフラチームはまた1つ強くなれた気がします。

 

最後に

マネーフォワードでは、チーム一丸となって様々な課題に立ち向かうエンジニアを募集しています。
ご応募お待ちしています。

【採用サイト】
マネーフォワード採用サイト
Wantedly

【マネーフォワードのプロダクト】
お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android

ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』

おつり貯金アプリ 『しらたま』

おトクが飛び出すクーポンサービス 『tock pop』

金融商品の比較・申し込みサイト 『Money Forward Mall』

くらしの経済メディア 『MONEY PLUS』

本業に集中できる新しいオンライン融資サービス 『Money Forward BizAccel』

Pocket