出力を入力へ

プログラミングに関する自分が考えた事を中心にまとめます

デスクトップ環境をUbuntu22.04にアップデートした

日頃利用するデスクトップ開発環境としてUbuntuを利用しているが、これをUbuntu 20.04→Ubuntu22.04にアップデートした。

旧環境の課題

原因不明の動作不具合がいくつかあり、これを解消したかった。

音声入出力の不安定

映像出力は問題ないが、音声入出力が不安定という問題を抱えていた。

音声入力はボリューム設定が度々リセットされて自分の設定より小さくなった。多くの場合はサスペンド&解除するとリセットされ、たまに画面ロックしただけでリセットされることもあった。このせいでwebミーティングのマイク音声が小さくて自分の声が届いていないことが多々あり、ミーティング開始時に毎回音声入力ボリュームをチェックする必要があり面倒だった。

また、音声出力も不安定だった。音楽をかけていると急にボリュームが小さくなったりノイズが載ったりしていた。断線等を疑ったが結局GPUがおかしいのでは?という結論で改善できていなかった。音声出力の問題はUbuntu 18.04のときから発生していたのでOS設定が原因ではなくハードウェア的な問題だろうと(症状からしてそう)、GPU交換待ちだった。ただし、GPUの高騰のせいでなかなか交換を試せないでいた。

さらに、firefoxなど一部アプリが指定のスピーカーを利用してくれないという問題があった。システム設定を無視して別のスピーカーを利用してしまう。これを回避する方法がわからなかったのでfirefoxではweb会議や動画再生ができないという制約があった。

起動時のNFSマウントエラー

デスクトップ起動時にNASをNFSマウントする設定だったが、これが上手くいかない問題があった。

起動処理で/etc/fstabの設定を反映してくれず、毎回手動で sudo mount -aする必要があり面倒だった。また、libvirtまわりでもNFSマウントしている設定があり、これが原因でデスクトップ起動時にVMも起動してくれないとうい問題を抱えていた。 こちらはOSをUbuntu20.04にアップデートしてから発生したのでOS設定まわり(特にsystemdのブート順序まわり)を疑っていたが結局解決できなかった。OSクリーンインストールすれば解決するだろう、くらいの感覚だった。

フルディスク暗号化

既存のシステムディスクは暗号化を適用していなかった。

デスクトップ環境であり自宅外に持ち出す機会はないものの、暗号化できていないという事実には気になっていた。主要なデータはクラウド管理・バックアップしているが、個人情報の塊なので。

ハードウェアアップデート

以下のハードウェアについて更新した。

GPU更新

音声入出力のトラブルのうち、少なくとも出力についてはGPUのトラブルだろうと推測できていたのでGPUを更新した。

今まではGTX1660Tiを利用していて、パフォーマンス的には特に困ってはいなかった(ゲームをするわけでも機械学習するわけでもないので)。ただし、blender等さわったりする機会はあるし、RTXレイトレーシングAPIを利用できる環境は欲しかった。

RTX4000シリーズの発売直前ではあるし、RTX3000シリーズのハイエンド帯は価格が下落傾向ではあるが、エントリー帯であれば価格改訂や値下げが進むことはないだろうと判断してRTX3050を購入した。 RTX3050であれば現行GPUとほぼ同程度のパフォーマンスを出すことができるので自分の用途で困ることはない。 将来的にはGPUのミドルエンド・ハイエンドへのアップデートも考慮しつつ、このGPUは別サーバ等でも利用することを想定する。

SSD更新

OS更新時にはシステムストレージごと交換して新規インストールすることにしている。このため現在システム用ストレージに利用しているSSD(NVMe M.2)も更新する。

従来はSamsung SSD 970 EVO 250GBを利用しており、パフォーマンス的には満足していたものの容量不足を感じていた。

新規ストレージとしてはWD Blue SN570 1TB を選択した。 細かなパフォーマンス比較はしていないが、WD BlueはHDDとしてはよく利用しているしNVMe M.2 SSDとしても利用してみたかった。また、SN570が比較的新しくリリースされた製品なので試してみようと考えた。価格も特に高価とうわけではなかった。

OSインストール

OSインストールまわりのトラブルについて。

インストールメディアの準備

USBメモリを利用してOSをインストールするため、インストールメディアを作成して利用した。

今回はddコマンドを利用して作成したのだが、手順ミスなのかたまたまデータ書き込みエラーが発生したのか、作成したインストールメディアでインストーラを起動できなかった。 結局そのときはWindows PCにRufusを使ってインストールメディアを作成して対処した。

ddでもよいが、Ubuntuヘルプ または Ubuntuチュートリアルの通り Startup Disk Creator を使って作成してもよかった。

また、できればPXEブート環境を構築してUSBメモリなしにOSセットアップを可能にしたい。ただし、これを実現するには開発用ネットワークを分離する必要がありハードルが高い。

フルディスク暗号化

OSの新規インストールと合わせてフルディスク暗号化オプションを有効にする。

手順は Ubuntu22.04フルディスク暗号化を有効にする などが参考になるが、特にLVMレイアウトを変更しないので考慮する点はない。

問題はリカバリーキーの扱いで、生成されたリカバリーキーはデフォルトでは永続化されない。このため、インストール直後の再起動前もしくはインストールセットアップ時にリカバリーキーを永続化する設定が必要だった。この対応をしないとリカバリーキーは失われてしまう。 リカバリーキーを保存することなく削除してしまったら、リカバリーキーの失効と再発行を行う必要がある。また、LUKSは今までまともに使ったことがないので理解を深める必要がある。

ソフトウェアインストール

OSインストール後のセットアップまりのトラブルについて。

Extension Manager および Sound Input & Output Device Chooser

音声まわりのトラブルに対処するために、入出力デバイスを細かく制御できる必要があった。 これを実現するために Sound Input & Output Device Chooser を、GNOME Shell Extension Manager経由でインストールする。詳細については以下の記事を参照。

gihyo.jp

これにより、音声入出力デバイスの制御が可能となった。ただし、初手で Sound Input & Output Device Chooser をセットアップしたのでUbuntu22.04環境で本当に必要だったのかわからない。OS更新やGPU更新で問題は解消できていた可能性もあったので、Sound Input & Output Device Chooser をインストールする前に各種音声まわりのトラブルが発生しないか確認しておくべきだった。 Sound Input & Output Device Chooser 自体は便利に利用できているので、これを継続利用すること自体は問題なし。あとはExtension Managerまわりもまだ理解できていないので理解を深めておきたい。

NFSマウントセットアップ

OSクリーンインストールにより、起動時にNFSマウント失敗する問題も解消される想定だったが、そもそもNFSマウントに失敗するようになった。

原因等不明だが、対策としてはマウントオプションとして mountvers=4 を付与することで解消した。これにより、mountコマンド経由でも、/etc/fstab 経由でもマウント/自動マウントするようになった。

NFSバージョン詳細やマウントオプション詳細はあまり理解できていないので、詳細確認が必要。

deb版 firefox

snap版firefoxは不具合もあるのであまり使わない方がよいと事前から聞いてはいたが、実際に上手く動作しない問題があった。1passwordのアドオン拡張で Ctrl+. のショートカットが動作せず、パスワード補完にマウス操作が必要になるという問題があった。 これはさすがに許容できないと、以下の記事を参考にdeb版のfirefoxを導入した。

yuzu441.hateblo.jp

これにより無事1passwordアドオンも動作するようになった。ただし、カーソルが入力欄にあたっているとまだ動作しないという問題は残っており、完全解決には至っていない。

また、firefox以外も基本的にはsnap版を利用しないという方針をとっている。slackなどのアプリも上手く動作しないという事象に遭遇しており(Ubuntu20.04時、詳細は忘れた)、そういったトラブルの可能性を抱えてまでsnap版を利用するメリットを感じていない。

vim error

vi/view/lessなどのコマンドを利用するとき、 Failed to source defaults.vim のエラーが発生するというトラブルに遭遇した。 この不具合は既知のもので、いくつかのフォーラムにて報告されている。

bugs.launchpad.net

bugzilla.redhat.com

対策としては、 touch ~/.exrc にて .exrcファイルを作成した。これによりエラーは発生せずに動作するようになった。

SRE NEXT 2022に参加しました

2022-05-14 - 2022-05-15の2日間で開催されていた SRE NEXT 2022に参加しました。 各社のさまざまな工夫や取組みが共有され、学びのある2日間でした。 運営、発表者、スポンサーのみなさま、ありがとうございました。

以下視聴したセッションのうち特に気になった発表の備忘録です。

How We Foster Reliability in Diversity

speakerdeck.com

1日目の基調講演で、SREの難しさやどのように取組むとよいかの発表。

「SREの実践における重要な5つのステップ」において、「いきなりプラクティスを実践するのではなく、状況把握からはじめる」というのはまさに今の自分にあてはまる言葉だった。以前SREのプラクティス導入に失敗した身として、プラクティス導入のモチベーションが伝わっていなかったのでデザインドキュメントのような形で整理が必要と思っていたけど、それ以前に品質やプロダクティビティの現状が十分に共有されておらず、先に計測を始めるべきと取り組んでいる最中だった。「計測なくして改善なし」など、計測が重要なことは十分把握しているつもりだったけど、言語的な・定性的な現状把握だけでなく、定量的な把握が何よりも重要だと思っている(「定量的な」現状把握までは発表では触れられていなかったけど)。

また、組織の氷山モデルについて、単なる行動(Level1)を変えるだけでなく価値観(Level3)を変える必要があり、価値観を変えるためのアプローチが重要というのはその通り。行動を変えるだけならすぐにでもできるけど、それだとすぐに元に戻ってしまうので価値観から変える必要があると感じている。一方で、いきなりMVVの策定などに取り組んでも上手くいかないので、Level1から小さく始めて繰り返し、成功体験を積む必要があると感じており、まさに上からと下からの両面のアプローチが大切だと感じた。

1,000万人以上が利用する「家族アルバム みてね」のSRE組織は4年間でどのように作られてきたのか

speakerdeck.com

ミクシィさんにおけるSRE組織の誕生とこれまで・これから取り組んでいくこと。

SREチーム設立直後の施策として、IaC整備や開発環境整備、ログ分析基盤の構築、S3最適化や各種セキュリティ施策の実施などは、まさに自分が今までやってきたことなので、今まで自分が取り組んできたことは大きく間違っていないと安心することができた。

一方で、自分たちと違うこととして見積りを含むスクラムをベースに開発に取り組んでいる点。障害対応などの割り込みやチームとして開発コストの見積りが難しいことから、自分たちはスプリントを切ってスクラムで開発することは諦め、カンバン方式で開発のフローに集中することにした。このあたりスプリントを切ることの工夫やメリットは気になるところ。

1年間のポストモーテム運用と、そこから生まれたツールsre-advisor

speakerdeck.com

面白法人カヤックさんにおけるポストモーテムの取組み。

障害対応の知見をチーム間でも共有したいという目的の元でポストモーテムのフォーマット共通化およびそれを社内展開・ナレッジ蓄積するための取組みはすばらしい。自分はまずはナレッジの蓄積ができるようにポストモーテムのドキュメントを作成するところまではやっていたけど、それを組織に広めるために興味を引く一言コメント付きで展開するというルールまで策定しているのはよい取組みだと思った。ポストモーテムを書くかどうかの判断基準にエラーバジェットの消費量で判断するというのもわかりやすくよさそう。

また、ポストモーテムから導かれる再発防止をチェックリストにしたくないので、ツールで自動検知できるようにしたというのはフットワーク軽くすごい取組み。IaC側のコードでチェックするのが正道ではないかな、と思ったけどタスク定義のようにIaCされない部分をチェックしたいとか、社内固有のルールをチェックしたいなどの理由もあるようでなるほど。確かにtfsecやtflintで独自ルールを実装・管理するのは結構大変そうなので、このあたりさくっと対応できるのはすばらしい。

SRE チーム立ち上げから1年。気づいたら SRE っぽくない仕事まで貢献しちゃってる説

speakerdeck.com

ビットキーさんにおけるSREとしてやってきたこと、SREっぽくないけどやってきたこと。

プロダクトチームをスケールさせるためにSREチームを立ち上げ、SLI/SLOやセキュリティ,テストなどを整備してきた上に、プロダクト開発における文化醸成やコーポレートセキュリティ、コスト削減などの相談がくるなど、プロダクト開発以外の相談事項がSREにくるのはあるあるなのかなと思った。コスト削減についても、具体的な閾値があるわけではなく無駄使いを止めたい、現在のコストが適切なのか判断できるようにしたい(コストの透明性を確保したい)という本来の目的からコストの可視化に取り組んだ点はまさに自分も同じことをしたので納得感が強かった。

SREの守備範囲が広過ぎてDevOpsの文脈以外の取組みもあるというのはその通りだし、コストやセキュリティなどは無関係ではいられなくても片手間でどうにかなる話ではないので、きちんと責務を分離して取組めるようになるとわかりやすい。現状だと Embedded SRE / Enabling SRE / Platform SREのようなわけ方はあるが個人的にはしっくりこないので、品質にコミットするSRE、セキュリティにコミットするSRE、コストにコミットするSREのような分類方法があっても面白いと思う。

Who owns the Service Level?

speakerdeck.com

リクルートさんにおけるSREの実現について2020年の発表を踏まえたふりかえり。

いつもながら、自己完結を重視しこれを実現するのはすばらしいなと思いつつ、エラーバジェットを元にした行動には至れていないというのは少し驚いた。ただし、SLO違反したら機能リリースはストップするというのは難しいと思っていたので、これを幻想と言うのはすごく納得した。SLI/SLOはエンジニア以外との共通言語の役割で、認識共有やディスカッションにつながっていればそれで十分役目を果しているというのであれば、自分ももう少し安心して社内にSLI/SLOを導入できる気がする。

街じゅうを"駅前化"する電動マイクロモビリティのシェアサービス「LUUP」のIoTとSRE

speakerdeck.com

LuupさんにおけるIoTシステムにおけるSREの取組み。

IoTなシステムにおける信頼性の計測に、CUJからSLI/SLOを策定するだけでは不十分なのでM2M通信の可用性を計測する必要があり、これをCMC(Critical Machine Communication)と名付けて電文解析・ログ管理して計測できるようにするという取組み。このあたりはまあ難しいよねと思いつつ、ハードウェアでは故障を検知した時点で手遅れなことが多い(修正に時間が掛かる)ので、故障予測などが必要ではないかと思ったら当然並行して取り組んでいるとのこと。 IoTシステムの場合、特に製品が安価になるほど監視や故障予測がコストに見合わずに、障害時にさっさと代替品を提供するという判断になりがちなので、CUJを重視してこのあたりを取り組んでいく取組みは非常に興味深い。

SRE NEXT全体について

動画視聴やQ&Aなどは特に問題なく、2日間通して気持ちよく参加することができた。これだけで運営のみなさまには大感謝。今後カンファレンスには発表する側か、せめてプロポーザルリジェクトな状態で参加したいところ。

ただ正直なところ、EventbriteやZoom Event Lobbyはあまり使いやすいとはいえず、このあたりもう少しどうにかならないかなとは思った。自分の普段利用の環境であるLinux Desktop(Ubuntu)はZoom Event Lobbyに対応しておらず、1日目開始直前の動作環境で利用できないことに気付いてあわててmacbook環境を準備した。Linux Desktopがこういった機能未対応なことはよくあるのでしょうがないけど、ちょっと悲しい。

書評: Jetpack ComposeによるAndroid MVVMアーキテクチャ入門

久しぶりにAndoridアプリ開発がしたくて始めたけれど、 Jetpack Composeがよくわからなかった。 いろいろ調べているうちにこの本を見付けて、よさそうだったので読んだ。

概要

Android アプリ開発においてMVVMアーキテクチャを採用する方法として、Jetpack Composeを用いる方法の解説。 MVVMの解説・シンプルなアプリ例・実践的なアプリ例の3構成となっている。

自分はJetpack Composeというところに惹かれて購入したけど、どちらかというと主題はMVVMであり、 Jetpack Composeに含まれているViewModelライブラリの方に重点が置かれている。 そのため、Viewに関するところの説明でデザインに相当する解説はほとんどない。 また、MVVMを採用する上で重要となるDIについても扱われている。

よかったところ

目的であったJetpack Composeについて知れたのはよかった。 UIデザインの目的で購入したが、それよりもUIを宣言的に実装できること、ViewModelを導入してライフサイクルの考慮が軽減できることが知れてよかった。 ただし、本書で「Androidアプリに特有のライフサイクルの問題を吸収してくれる機能がある」と記載しておきながら、 その具体的なライフサイクルとは何か説明されていないことは気になった。 いろいろ調べて試してみたところ、自前実装のライフサイクルを考慮していないViewModelだと 画面の回転でリソースが破棄されて画面の表示がリセットされるが、 JetpackライブラリのViewModelを利用すると表示が引き継がれることがわかり感動した。 おそらくAndroidアプリ開発経験者にはあたりまえの事なので省略されていたのだと思うが これを理解していると実感がまったく異なるのでこれに気付けてよかった。

また、DIについて知れてよかった。 AndroidにおけるDIはまったく把握していなかったので、このとっかかりにすることができた。 残念ながらDIの解説書ではないので説明が十分とはいえないが、予めDIとは何か簡単に知っていると本書の内容としては理解できるし、 知らなくても実際に動くコードを見ながら理解を深めることができる。

よくなかったところ

一番残念なのは4章の実践的なアプリとして登場する、GitHubのWeb APIを使ったアプリについて説明が少ないこと。 分量としては約40ページ割かれているが、その大半がコードの掲載およびそのコードの説明で、 その背景にあるMVVMアーキテクチャの説明としては不十分に感じた。

例えば、ViewがViewModelに依存しておらず状態変更を購読しているだけだという2章における説明が、 4章のアプリの説明では一切出てきていない。 これを実現するためにDIが上手くやってくれていると一言あるだけでMVVMとの対比が鮮明になると思う。

それ以外にも4.2節 Remote Data Sourceについて、大きくRemoteDataSource、ApiClient、GitHubUserと登場する中で、 それぞれの立ち位置が説明されておらずきちんと理解できているかよくわからない。 RemoteDataSourceがRepositoryに対するインタフェース、ApiClientはRemoteDataSourceに対するインタフェース、 GitHubUserは各インタフェースおよびRepositoryに対するDTOと理解したが正しいか自信がない。 どのインタフェースが何に対して抽象化しているのかといった説明は明記して欲しいと思った。

まとめ

AndroidにおけるMVVMアーキテクチャを理解する上で、またJetpack Composeを利用する上で非常に参考になった。 後半若干説明不足に感じるが、精読したり周辺技術を追加調査したりすると、MVVMやJetpack Composeについて理解を深めることができる。 そのとっかかりとして本書は非常に有益に感じた。

2021年のふりかえりと2022年の抱負

2021年のふりかえり

2021年は2020年と比べて失敗経験を積めた年だった。 2020年は新しい環境で、今までにない経験を積むことができたのに比べて、2021年は今までの取り組みをより良くするために試行錯誤してきた。結果、上手くいったものもあれば、上手くいかないものもあった。 残念ながら課題の多い1年だったけど、これを糧に2022年をより良い1年にしたい。

できたこと

成果のアウトプット

アウトプットは一定の成果を出せたが満足行くものではなかった。及第点という感じか。

  • 登壇: 1件
  • 個人ブログ: 11件
  • Qiita+Zenn: 5件
  • 会社ブログ: 1件

一番よかったのは目標であったカンファレンスでの発表を実践できたこと。カンファレンスでの発表はエンジニアキャリアの中でも目標の1つだったので、ひとまずそれが達成できたのがよかった。 とはいえ、久しぶりのプレゼンで話をするのが下手になっていたので、発表には満足できておらずそれは残念だった。 もう少し気軽に5分LTから経験してと考えていただけに、いきなり&久しぶり&初の社外向けの発表で15分はやり過ぎだったか。15分くらいは大したことないと思っていたので、今後どのように発表していくかは考えどころ。

tech.spacely.co.jp

OSSへのコントリビュートも、実装レベルでプルリクを作成することはできなかったが、不具合のissue報告などは取組むことはできた。 しかし件数がたった2件と圧倒的に足りていない。もちろん数の問題ではないが、普段利用しているOSSの数や利用量から考えるとコントリビュートするという目標に対して少ない。 github actionを作成・公開することはできたが、それも1件かつ作成後ほとんどメンテできていない。

メインとなるはずのブログやQiita/Zennなどの記事執筆も量的には満足できないものだった。 2021年前半はある程度順調にアウトプットできていたと思うが、後半には社外へのアウトプットが途絶えてしまった。 社内でのアウトプットは継続できており、社内Qiita、GitHub Wiki、Google Docsとさまざまな形で実施できていた。 業務で得た知見はすべてが社外に公開できないものではないので、これを社外に公開できないのは反省点。 個人での学びも2021年後半は減りはしたものの止まったわけではなかったのでもっとアウトプットに時間を割くべきだった。

CI/CDの強化

CI/CDはいろいろと取り組むことができたのでよかった。

  • GitHub Actionの作成と公開
  • コンテナアプリのCI/CD環境としてGitHub Actions (CIサービス)の利用
  • lambda関数のCD活用
  • railsアプリのCodePipeline適用
  • インフラまわりのCI/CD活用(ansible-lint, Terraform Cloud活用, ImageBuilder活用)

CI/CDサービスとしてはGitHub ActionsやAWS Codeシリーズなどを本格的に活用できるようになった。 対象アプリとしても、コンテナアプリ、lambda関数アプリ、その他のアプリ、インフラと幅広く扱うことができた。 社内のCI/CDインフラもだいぶ改善したし自分の知見もいろいろと蓄積することができた。

もちろん課題も多くある。 1つは、CircleCIまわりのメンテ・サポートがほとんどできなかった。 Webエンジニア側にまかせてもいいかと判断した点はあったけど、せっかくCI/CDに注力するとしていたのだからもう少し手を出してもよかった。 2つ目がDevSecOpsの導入で、具体的にはSonarCloudを検証・導入したが、残念ながらあまり上手くいっているとは言えない。 このあたりは導入モチベーションをきちんと共有できないことが失敗の原因だった。どちらかというとマネジメントの問題。 最後にインフラまわりのCI/CD。Terraform Cloudの活用がどんどん進んだことはよかったが、 ansibleはlint止まりでテストは導入できず、terraformもtrivyを検証まで行ったが導入までいかなかった。 AWS Image Builderもなんとか動作するようにはなったがまだまだ課題が多い。

運用・監視の深化

運用監視はまだまだ課題はありつつも、大きく進歩した1年だった。

具体的にはWebエンジニアを巻き込んだダッシュボードのふりかえり定例会議が開催されるようになった。 ここでの確認を通してダッシュボードや監視の仕組みの継続的な改善が行われるようになった。 これを通してダッシュボードの修正や監視メトリクスの追加・修正なども行われるようになった。 加えてWebアプリの修正にも反映されるようになったのでよかった。

一方で課題としては、SLI/SLOの導入失敗が挙げられる。 これはできなかったことにも挙げているがマネジメントの失敗で、十分にSLI/SLOのメリットや導入目的を共有しないまま進めてしまったことが失敗の原因にある。 そりゃSLI/SLOやエラーバジェットなんて知らない人の方が多いのだから、もっと導入意義を納得してもらってから進めるべきだった。

セキュリティ対策強化

苦手だと思ってあまり深追いしてこなかったセキュリティ分野は、必要に迫られる形でいろいろ取組むことができた。

Webアプリの脆弱性診断を主導することができたし、CIの中でセキュリティを意識し始めることができた。 DevSecOpsはtrivyを通してインフラに対しても適用検討することができた。 こういった取組みを開始して形として残り始めたのはよかった。

一方で、まだまだ対応については満足できていない。 本来は対応すべき内容を優先度の観点から保留しているケースもあり、 2022年はセキュリティ面でも安心できるようにしたい。

できなかったこと

マネジメント

正直なところ、2021年で一番失敗したと思うのがマネジメント、というよりリードエンジニアとしてエンジニア組織を引っ張るところ。 具体的には、エンジニア全体で取組むべきと自分が考えた事について十分に理解を得ずに進めようとした結果、空回りしてしまった。

やりたい理由・モチベーションを十分に共有してから進めるべきだった。 また、上手くいかない状況に焦ってより悪い振舞いをしてしまったのよくなかった。 モチベーション共有にはデザインドキュメントを採用してみたが、これも上手くいっているかは判断できない状況。 デザインドキュメントの導入自体も押し付けであるとも考えられるので、自分1人で試しているが上手く運用に乗せることができるかは2022年次第。 振舞いの改善はどうすればよいかはまだ悩み中。自分で気付いたときには振舞いを改めるようにしているが、大抵の場合は議論の最中には気付けない。

逆にタスクマネジメントについてはある程度上手くいったのでこちらは継続していきたい。

スケーラブルなインフラ

インフラのスケーラビリティについてもビジネス的な必要性がないことから対応できなかった。

自分たちでリソースを管理する必要のないマネージドなサービスを選択できている一方で、 リソース管理が必要な部分ではまだまだ無駄が生じている。 コストまわりはやっと課題として認識されてきたので、2022年こそは取組めるはず。 2021年にもいろいろと検討できているので、その検討結果をリリースまで取組みたい。

DB知見の深化

DBまわりも結局詳細な検討にまで取組めなかった。

RDBはパフォーマンスモニタリングまでは取組めたものの、改善には着手できなかった。 スロークエリやDB負荷については認識を共有できたと思うので、着実に改善に繋げていきたい。 また、Redisは未対応だしMongoDBまわりも結局置き換えてしまった (これはむしろ良い判断だった。ただし、MongoDBまわりを検証する機会は失った)。

DBまわりは今後も課題がたくさん残されているので、継続して取組みたい。 RDB負荷については負荷試験のための取組みを取組みたい。 Rails6の複数データベースまわりの知見は間違いなく必要になるのできちんと検証しておきたい。 Redisの対応は必要になるだろうがMongoDBは必要になるだろうか?その分Elasticsearchのような全文検索の仕組みは必要になりそう。 いろいろなデータレイク・DWHを試して知見を蓄積したい。

フロントエンド技術入門

結局フロントエンドまわりはほとんど取組むことができなかった。

できたのは node.jsでバックエンドを構築するくらいで全然フロントエンドではない。 Vue.jsやTypeScriptを学ばなければ、と思いつつも本当に触っただけに終わった。 Railsのアセットパイプラインについては学ぶことはできたが全然フロントエンドではない。

ここは自分の興味であるVRに範囲を絞って取組むべきだった。 それ以外のフロントエンドまで含めてしまうと自分の興味から外れてしまうので取組むモチベーションにならなかった。 もしくは、アプリケーションの実装という中でフロントエンドにも触れる、くらいを目指すべきだった。

クラウドネイティブな技術のキャッチアップ

これは元々目標ではなかったけれど。 年々業務に必要な知見にのみ注力してしまい、Kubernetesやその周辺知識のキャッチアップが疎かになってしまった。

AWSの新規プロダクトを通して知見を増やすことはよいが、もっと上流(と言えるかは不明だが)のOSSまわりや他社含めた動向にも目を通すべきだった。 CNCFもそうだが、USENIXなどの学会系も含めた最新技術の動向や、他社のアウトップトを通しての実践例を学び、 自社にない技術についても知見を深めていきたい。

2022年の抱負

2022は継続と完結をテーマにしたい。

一時的な興味だけに終わらず、継続して取組むことで目標達成まで到達すること、完結させることを大切にしたい。 調子の良いときに成果を出すのはもちろんだが、調子が悪いときに無理なく取組めるか、継続できるかを意識して頑張りたい。 やるべきこと、やらなくてよいことの取捨選択を行い、いつかやるで保留せずにやらない判断を下して物事を完結できるようにしたい。

個人成果の追求

良い意味でもっと自己中心的に、自分個人の取組みに注力していきたい。 組織をリードする点も止めるわけではないけど、それが理由で個人の成果を犠牲にはしないようにしたい。

組織のリードについては、会社組織が拡大してきたこともあり、自分が何でもリードする必要はなくなってきた。 言いたい事ややりたい事を少し我慢してでも、特に別チームの事はそのチームに任せ、 エンジニア全体として取組むべきでも合意を得ることが難しいようであれば諦めることも選択肢に入れたい。 エンジニア全体の改善よりは、多少局所最適になっても自分のことに注力したい。

また、業務にばかり時間を掛けずにもっと個人的な興味に時間を使いたい。 自分の興味や好みにリソースを割いて、自分が楽しむことを優先していきたい。

成果のアウトプット

2021年はもの足りなくても2020年よりは上手くいった。 2022年は2021年よりさらに意識して取組みたい。

カンファレンスでの発表ももっと増やしていきたい。発表するという目標から、自分が満足できる質で発表できることを目指したいし、発表回数も増やしていきたい。 OSSへのコントリビュートや自作OSSの公開などは特に注力したい。 できれば実装修正のプルリクを投げられるようにしたいが、そうでなくても不具合再現のためのissue起票などをできるようにしたい。 自分が利用しているOSSをしっかりと使い込んでいきたい。 ブログ記事等の執筆は2021年前半のペースでよいので継続していきたい。 どちらかと言うと、ブログ記事は内容にこだわって書けなくなるよりは、小ネタでも継続して書けるようにしたい。 その際はQiita/Zennなども活用してアウトプットしていきたい。

xR技術

せっかくVRの会社で働いているのだからxRの技術について理解を深めたい。 VRはもちろんだけど、個人的にはARにも興味はあるのでARにも取組みたい。

VR技術としては、three.jsなどのフロントエンド技術もそうだし、blenderやOpenCV、Unityなどを通して3DCGモデリングについても理解を深めたい。 またVR動画(というか360度動画)まわりにチャレンジしたい。正直なところ、動画まわりは配信技術としてインフラまわりに特に興味があるのでそのあたりも含めて検証したい。 できれば、Androidアプリなどを通してARにも挑戦したいし、いろんなデバイスについても試してみたい。

テスト

テストまわりの知見強化と実践に取り組んでいきたい。 2021年はCIという形でテストに触れてきたけど、よりテストにフォーカスしたい。

アプリケーションのテストとしては静的解析/動的解析やセキュリティおよびリファクタリングまわりも含めて取り組んでいきたい。 このあたりはインフラエンジニアとしてではなく、SETやQAなどのロールとしても動ける程に知見を深めていきたい。 また、その流れとしてシステムテストとその自動化まわりにも取り組んでいきたい。

アプリ以外にもインフラのテストについて取り組んでいきたい。 インフラのテストをどう実践するかはまだ具体化できていないが、2021年の課題に感じていたところなのでいろいろと実践していきたい。 IaC自体のテストはもちろんだが、システムテストにまでいかない、上手いテストのやり方を模索していきたい。

低レイヤ技術

業務ではOSに近い低レイヤ技術にあまり触れる機会がないので、個人的な形ででも取組んでいきたい。 HWに近いいわゆる低レイヤ技術だけでなく、オーケストレーションツールやGPUまわりなど、今より1つ低いレイヤに取組みたい。

Amazon Linux2022の検証という形でFedoraディストリビューションは試していきたいし、その他ディストリビューションやlinux kernelレベルの知見を深めていきたい。 途中で止まっている自作OSの取組みもできれば再開していきたい。 自宅サーバの運用もほとんど動きが止まってしまったのでアップデートしていきたい。 xR技術に取組む中でGPUやIoTデバイスなどをいろいろ扱っていきたい。

GitHub PRにRedmine issueへのリンクを記載したらコメント通知するactionを書いた

初めてのGitHub Actionsの開発。

github.com

Actions開発の背景

タスク管理にGitHub issueではなくRedmine issueを利用しているのだけれど、プルリクを作成したときにRedmine issueへのリンクを記載することがよくある。 git コミットにissue番号を記載して連携させれる方法もあるけど、コミットレベルではなくプルリクレベルで連携させたかった。

あまりこういった取り組みは行われていないようなので、GitHub Actionsでプルリクの内容を解析してリンク先のRedmine issueにコメントを残すActionを開発した。 これによってGitHub側の開発状況がRedmineにコメントとして自動反映されるので、わざわざRedmine issueを手動で更新しなくて済むようにした。

開発の取り組み

GitHub Actionsについて

基本的には公式ドキュメントの アクションの開発を読み進めていった。

最初は環境変数を通して必要な情報が渡されないかなと期待したけど、欲しい情報はなかったのでしっかりと作り込むことにした。 Actionを開発する上で、JavaScriptに慣れておらずDockerに慣れていることから、Dockerアクションで実装しようかと思っていた。ただし、RedmineのAPI叩くためだけにDockerイメージのプルするのはやり過ぎかなと思ったのと、DockerアクションはJavaScriptアクションより遅いとうことだったので、JavaScriptアクションで実装してみることにした。JavaScriptを使う良い機会とも思ったので。 ただし、プルリクを作成したタイミングでしか実行されず、コメント通知が少し速くてもまったく嬉しくないので、JavaScriptアクションを選択した意味はあまりなかったかもしれない。

Action開発する上では、どのようにGitHubから必要な情報を取得するかが悩みどころだった。パッケージとしてはtoolkitが公開されているけど、細分化されておりどのパッケージが必要なものかわからなかった。 結局はGitHub Actionsに関する情報取得は @actions/coreを、プルリクに関する情報は @actions/githubを利用した。ただし、@actions/github を使う上で欲しい情報がどのように取得できるかは最後までよくわからなかった。一応 ドキュメントはしっかりあるけど、各APIを叩くと具体的にどのような情報が取得できるのか判断できなかった。結局は実際にAPIをいくつか叩いてみたりしてあたりをつけた。

Node.js アプリ開発

Node.jsアプリ開発はほとんど経験がなかったので、結構基本的なところから試行錯誤していたので思ったより時間を掛けてしまった。 基本的には javascript-action-templateの通り。 迷ったのは以下の通り。

パッケージマネージャとしてのnpmについて。 てっきりyarnを利用するのかと思ったけど、サンプルにはnpmしかなかったので、そのままnpmを利用し続けた。yarnの方が良いらしいくらいにしか理解していないのと、npmで困っていないので現状もnpmのまま。

パッケージの配置について。 動作検証として開発中のActionsをGitHub上で動かしてみたけど、node_modulesが見付からないというエラーで動作しなかった。原因は全部ドキュメントに書いてあるのだけれど node_modulesごとコミットする必要があるとこのと。 ただし、他のプロジェクトを見てもnode_modulesをコミットしているプロジェクトはほとんど見あたらず、テンプレートすらnode_modulesをコミットする方針は採用していなかった。また、コミットするにしてもmainブランチにはコミットせずリリースブランチ/タグでのみコミットしないと開発がやりずらくなる、みたいな情報もあった。node_modulesの中身もきちんと理解していないのでコミットしたくないという思いもあり、テンプレートは他のActionsと同じように@vercel/nccを利用する方針を採用した。 ただし、nccをどのように実行すればよいか解決していない。現状はアプリを修正したときやパッケージを更新したときに手動でコマンドを叩いている。リリースプロセスの中で自動実行する仕組みにしないと間違えそうだなという不安が残っている。

テストについて。 node.jsのテストフレームワークとしてjestは前々から使ってみたいなという思いはあったので喜んで採用した。単体テスト自体は慣れているし、違和感なくテストを書くことができた。 ただしテストを書くためには対象関数を別ファイルに分割する必要があったり、テストのためだけに? module.exportsしたりと、本当にこのやり方でいいのかなという違和感が残っている。 Redmine APIもよくわからない事が多いので、できればRedmine APIをモックにしてテストを書きたい気持ちもあったけど、そこは面倒そうなので諦め。

Redmine API用のライブラリについて。 探すと node-redmineがあったので特に悩まず採用した。実際問題なく開発を進めることはできた。 ただし、後から確認してみるとまったくメンテされておらず、開発者もその気がなさそう。今後の事を考えると別のライブラリ(例えば axios-redmineなど)を採用しておけばよかったかもしれない。

TypeScriptについて。 TypeScriptでの開発はまったく検討していなかったけど、開発中の調査等でTypeScriptという選択肢があることに気が付いた。また、TypeScriptのテンプレートも用意されている。 TypeScriptもどこかで使ってみたいという思いはあるけれど、まあ別の機会ということでスルーした。

アプリの実装について。 あまり複雑な処理はないが、唯一困ったのがプルリクの本文からRedmine issueへのリンクを取得する部分。特にissue番号の一覧だけが欲しいのをどうするかが悩みどころで結構時間を使ってしまった。 結局は RegExp.exec を使えばシンプルに実装できるとわかり無事解決。

リリース

リリースをどうするかはまだ解決していない。 特に利用において、現状では @main のようにメインブランチを指定する利用方法を想定しているが、できればよくある@v1のようなバージョン指定をサーポートしたい。しかし、これを実現するにはv1タグをリリースの度に打ち直す必要がある。できればタグは一度打ったら別のコミットに打ち直したくないので、このリリース方法には違和感がある。 解決策としてv1ブランチを作成することにした。これで@v1.0.0 指定と@v1指定の両方がサポートされ、v1タグをリリースの度に打ち直す必要はない。ただし、v1.0のようなマイナーバージョンまでの指定はサポートできない。最新機能に追従するv1指定か、バージョンを固定するv1.0.0指定の2つがサポートされていればまずはOKだろうか。

今後の展望

まだまだやりたい事はたくさんあるがどこまで対応できるか不明。しばらく使ってみてから考える。

機能としては、Redmineのissueステータス更新やメッセージのカスタマイズなんかはやりたい。 非機能としても、リリースプロセスは整備したいし、テストまわりももう少しきちんと書きたい。あとはREADMEもえいやで書いたので英文を見直したい。

それ以外にも、だいたい実装が完了した時点で、GitHub Actions実践入門の書籍にActions開発についても触れられていることを知った。てっきりActions利用のみだと勘違いしていた。Actions開発については分量はそこまで多くないし、Actions側のアップデートもあるので全部そのままは利用できないかもしれないが、今回自分がActionsまわりで悩んだ点くらいは楽に解決できたかもしれない。

lambda関数のコンテナイメージサポートをterraformで構築する

AWS Lambda関数をコンテナイメージでデプロイする環境をterraformで構築したい。 特にterraformでは対象のコンテナイメージを管理から外したい場合における 初期構築方法について検証する。

検証コードはこちら。

github.com

lambda関数によるイメージ指定

Lambda関数を package_type = Image で作成する場合、 コンテナイメージを指定する必要があり、ECRだけでは作成できない。 イメージをpushせずに構築しようとすると、以下のようなエラーになる。

resource "aws_lambda_function" "sample" {
  function_name = "sample-container"
  role          = aws_iam_role.lambda.arn

  package_type = "Image"
  image_uri    = "${aws_ecr_repository.sample_prepared.repository_url}:latest"
}
aws_lambda_function.sample_prepared: Creating...
╷
│ Error: error creating Lambda Function (1): InvalidParameterValueException: Source image xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/sample-prepared:latest does not exist. Provide a valid source image.
│ {
│   RespMetadata: {
│     StatusCode: 400,
│     RequestID: "4c6a36d1-1ed8-4c2a-959a-50f8b066d21d"
│   },
│   Message_: "Source image xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/sample-prepared:latest does not exist. Provide a valid source image.",
│   Type: "User"
│ }
│
│   with aws_lambda_function.sample_prepared,
│   on external_registry.tf line 3, in resource "aws_lambda_function" "sample_prepared":
│    3: resource "aws_lambda_function" "sample_prepared" {
│

このため、有効なコンテナイメージを準備しておきLambda関数を構築可能とする必要がある。 このコンテナイメージにはAWSが提供してくれるイメージなどは利用できず、 対象のAWSアカウントに自分で準備する必要がある。

docs.aws.amazon.com

Lambda 関数の作成は、Amazon ECR のコンテナレジストリと同じアカウントから実行する必要があることにご注意ください

以上から、対策方針としては以下の3つがある。

  • ダミーECRリポジトリおよびコンテナイメージを準備しておく
  • local-exec でECRレジストリ作成と同時にイメージを作成する
  • docker providerでイメージまで作成する

方針1. 外部のダミーECRレジストリおよびコンテナイメージを準備しておく

一番シンプルなのは事前に専用のダミーコンテナイメージを準備しておく方法。 ダミーイメージをデプロイ時に合わせて構築することは難しく、 lambdaの仕様上別AWSアカウントのイメージ等を利用することはできない。 このため自分で事前にイメージを構築しておく。

resource "aws_lambda_function" "sample_prepared" {
  function_name = "sample-container-prepared"
  role          = aws_iam_role.lambda.arn

  package_type = "Image"
  # 初期構築時はダミーイメージを指定しておく
  image_uri = "${data.aws_ecr_repository.prepared.repository_url}:latest"

  lifecycle {
    ignore_changes = [image_uri]
  }
}

# 実際にlambda関数で実行するコンテナイメージを格納するレジストリ
resource "aws_ecr_repository" "sample_prepared" {
  name                 = "sample-prepared"
  image_tag_mutability = "MUTABLE"
}

# 以下は事前に手動で構築しておいたECRレジストリおよびコンテナイメージ
data "aws_ecr_repository" "prepared" {
  name = "prepared"
}

手作業でECRレジストリを作成し、適当なイメージをlatestタグでプッシュしておく。 初回構築時のみLambda関数がこのコンテナイメージをロードするが、 以降はlifecycleで指定している通り無視されるので、 別のイメージタグを指定しようが異なるリポジトリのイメージを指定しようが terraform上では差分は発生しない。

この方法はシンプルで手軽に対応可能な一方で、 手動でECRリポジトリの作成とイメージのpushが必要であること、 初回構築のためだけに別のECRリポジトリへの依存を明記する必要があるといった気持ち悪さがある。 実際に利用するECRリポジトリが対象lambda関数から指定されないことは誤解を生じやすいのでできれば避けたい。

方針2. local-exec でECRレジストリ作成と同時にイメージを作成する

初回構築のためだけに別ECRリポジトリを準備・利用するのは気持ち悪いので 初回構築時にECRリポトリにダミーイメージを格納する。 こういった操作にはlocal-execが便利なのでこれで実現する。

resource "aws_lambda_function" "sample_localexec" {
  function_name = "sample-container-localexec"
  role          = aws_iam_role.lambda.arn

  package_type = "Image"
  image_uri    = "${aws_ecr_repository.sample_localexec.repository_url}:latest"

  depends_on = [null_resource.generate_dummy_image]
}

# 実際にlambda関数で実行するコンテナイメージを格納するレジストリ
resource "aws_ecr_repository" "sample_localexec" {
  name                 = "sample-localexec"
  image_tag_mutability = "MUTABLE"
}

# ダミーコンテナイメージの保存
resource "null_resource" "generate_dummy_image" {
  provisioner "local-exec" {
    command = "aws ecr get-login-password | docker login --username AWS --password-stdin ${data.aws_caller_identity.current.account_id}.dkr.ecr.ap-northeast-1.amazonaws.com"
  }

  # ダミーイメージとしてalpineを利用する
  provisioner "local-exec" {
    command = "docker pull alpine:latest"
  }

  provisioner "local-exec" {
    command = "docker tag alpine:latest ${aws_ecr_repository.sample_localexec.repository_url}"
  }

  provisioner "local-exec" {
    command = "docker push ${aws_ecr_repository.sample_localexec.repository_url}"
  }
}

この方法を利用することで lambda関数のイメージ指定先として実際に利用するECRリポジトリを指定できる。 事前の操作も不要であり構築も容易である。

課題として、動作環境にawscliおよびdocker cliが必要なことが挙げられる。 通常の開発環境であれば存在を仮定しても問題なさそうだが、これが問題になるのがTerraform Cloud環境である。 Terraform Cloud への追加ソフトウェアインストールについてはドキュメントにまとめられているが、 基本的に非推奨でありさまざまな制約も存在する。 やはりAWS CLIが欲しいという意見は挙がっているようだが、 Terraform Cloud環境でaws cli や docker cli を利用する方針は避けた方がよさそう。

※別途確認したところ、Terarform Cloud環境にaws cliは存在したがdocker cliは存在しなかった。

方針3. docker providerでイメージまで作成する

local-execを利用すればLambda関数の構築に必要なダミーdockerイメージを生成・格納できるが Terraformの実行環境に追加ソフトウェアの依存が生まれてしまう。 これを解決する方法として、terraformでdockerイメージを制御する terraform-provider-docker を利用する。 docker providerを利用することで、追加でのawscliやdocker cliなしにdockerイメージのpushが可能になる。

resource "aws_lambda_function" "sample_dockerprovider" {
  function_name = "sample-container-dockerprovider"
  role          = aws_iam_role.lambda.arn

  package_type = "Image"
  image_uri = "${aws_ecr_repository.sample_dockerprovider.repository_url}:latest"
}

resource "aws_ecr_repository" "sample_dockerprovider" {
  name                 = "sample-dockerprovider"
  image_tag_mutability = "MUTABLE"
}

# Dockerレジストリにダミーイメージを格納しておく
resource "docker_registry_image" "sample_dockerprovider" {
  name = "${aws_ecr_repository.sample_dockerprovider.repository_url}:latest"

  build {
    context = "dummy"
  }
}

# docker-providerでECRを利用するための認証設定
data "aws_ecr_authorization_token" "token" {
}

provider "docker" {
  registry_auth {
    address  = "${data.aws_caller_identity.current.account_id}.dkr.ecr.ap-northeast-1.amazonaws.com"
    username = data.aws_ecr_authorization_token.token.user_name
    password = data.aws_ecr_authorization_token.token.password
  }
}

ここで一番実現したいことは、DockerHub上のalpineイメージを対象のECRリポジトリにコピーすること。 しかし、docker providerではこのような機能は議論されているが実装さていない。 このため、dummyディレクトリにあるダミー用のDockerfileにてコンテナイメージビルドした上で、そのイメージをECRリポジトリにpushすることで実現する。

FROM alpine

この方法であれば追加でaws cliや docker cliは不要でdocker providerを追加すれば解決できる。 ただし、hashicorp管理ではない別のproviderへ依存することになるのでこれを許容できるかが鍵となる。

まとめ

  • lambda関数でコンテナイメージサポートを利用する場合、初期構築時からコンテナイメージが必要となる
  • Terraformで上記lambda関数を構築する場合は構築時用のコンテナイメージをどのように準備するかが課題になる。
  • 別ECRリポジトリを参照する方法、local-execで構築する方法、docker providerで構築する方法が考えられる。
  • どの方針も一長一短なのでどれを選択するかは状況次第

参考

Terraform Cloudでリソースインポート時の変数参照

TerraformのバックエンドにTerraform Cloudを利用しているときに リソースインポートでエラーが発生して困っていた。 これについて調べていたら、リソースインポート時の変数参照についていろいろわかったのでそのまとめ。

Terraform 0.15.5で検証。

発生したエラー

再現手順

まずは発生したエラーについて。 特定の条件でリソースのインポートを行うとエラーが発生するというもので、再現手順は以下の通り。

  1. サンプルリポジトリの通りリソースを構築(terraform apply)する
  2. リソースをstateファイルから削除する
  3. Terraformをリモート実行モードに切り替える
  4. Terraform Cloud上で未定義の変数(例: undefined_variable = "test")を設定する
  5. Terraformをローカル実行モードに切り替える
  6. 2で削除したリソースをインポートする

以上の手順で実行したとき、リソースが正常通りインポートされることを期待するが、 実際には以下の通りエラーになる。

$ terraform import aws_s3_bucket.sample sample-import-tf
Acquiring state lock. This may take a few moments...
╷
│ Error: Value for undeclared variable
│ 
│ A variable named "undefined_variable" was assigned a value, but the root module does not declare a variable of that name. To use this value, add a "variable" block to
│ the configuration.
╵

エラー原因

当然ながら、原因は手順4で未定義変数に値を設定しようとしたこと。 リソースインポート時に未定義変数への値の割り当てが実行できずにエラーとなった。

未定義変数への値の代入があると、terraform planでも警告はあるがエラーにはならないので インポート時はエラー扱いになるという挙動の違いに混乱した。

$ terraform plan
...
╷
│ Warning: Value for undeclared variable
│ 
│ The root module does not declare a variable named "undefined_variable" but a value was found in file "terraform.tfvars". If you meant to use this value, add a
│ "variable" block to the configuration.
│ 
│ To silence these warnings, use TF_VAR_... environment variables to provide certain "global" settings to all configurations in your organization. To reduce the
│ verbosity of these warnings, use the -compact-warnings option.

また、一番驚いたのはローカル実行モードなのにTerraform Cloud上の変数を参照したところ。 ローカル実行モードでは変数のタブも参照できなくなるので、利用されないだけでなく、利用できないという認識だった。 ドキュメントを確認したが、インポートの章でもremoteの章でも変数を参照するという記載は見付からなかった。 リソースインポート作業が発生する場合は、過去に設定した変数はもう使わないからと放置せず、予め削除しておいた方がよさそう。

追加検証

この挙動について理解するために、以下のような追加検証を行った。

ローカル変数定義ファイルの参照

Terraform Cloud上の変数定義で、未定義変数に値を設定しているとエラーになることはわかった。 ではローカルの変数定義ファイル(terraform.tfvars)で未定義変数に値を設定しているとどうなるか検証した。

手順は以下の通り

  1. サンプルリポジトリの通りリソースを構築(terraform apply)する
  2. リソースをstateファイルから削除する
  3. ローカルのterraform変数定義ファイル(terraform.tfvars)に未定義変数(例: undefined_variable = "value")を設定する
  4. 2で削除したリソースをインポートする

以上の検証の結果、リソースインポートはエラーにはならず警告だけ出力してインポートに成功した。 ローカルの変数定義ファイルも参照はされるものの、Terraform Cloud上の変数定義とは異なりエラー扱いにならないというのは挙動の違いに驚かされる。

$ terraform import aws_s3_bucket.sample sample-import-tf
Acquiring state lock. This may take a few moments...

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

╷
│ Warning: Value for undeclared variable
│ 
│ The root module does not declare a variable named "undefined_variable" but a value was found in file "terraform.tfvars". If you meant to use this value, add a
│ "variable" block to the configuration.
│ 
│ To silence these warnings, use TF_VAR_... environment variables to provide certain "global" settings to all configurations in your organization. To reduce the
│ verbosity of these warnings, use the -compact-warnings option.
╵

ローカルとリモート両方での未定義変数設定

リソースインポート時にローカルとリモート両方の変数定義が参照されていることがわかった。 では、ローカルとリモート両方で変数定義が行われているときの挙動を確認した。

手順は以下の通り。

  1. サンプルリポジトリの通りリソースを構築(terraform apply)する
  2. リソースをstateファイルから削除する
  3. Terraformをリモート実行モードに切り替える
  4. Terraform Cloud上で未定義の変数(例: undefined_variable = "test")を設定する
  5. ローカルのterraform変数定義ファイル(terraform.tfvars)に、4とは別の未定義変数(例: another_undefined_variable = "value")を設定する
  6. Terraformをローカル実行モードに切り替える
  7. 2で削除したリソースをインポートする

以上の検証の結果、2つの未定義変数の設定で警告とエラーが出力されることを確認した。 このため、どちらかで変数定義を行っていればもう1方の変数定義が無視されるということはなく、両方の変数定義が参照される様子。

$ terraform import aws_s3_bucket.sample sample-import-tf
Acquiring state lock. This may take a few moments...
Releasing state lock. This may take a few moments...
╷
│ Warning: Value for undeclared variable
│ 
│ The root module does not declare a variable named "another_undefined_variable" but a value was found in file "terraform.tfvars". If you meant to use this value, add a
│ "variable" block to the configuration.
│ 
│ To silence these warnings, use TF_VAR_... environment variables to provide certain "global" settings to all configurations in your organization. To reduce the
│ verbosity of these warnings, use the -compact-warnings option.
╵

╷
│ Error: Value for undeclared variable
│ 
│ A variable named "undefined_variable" was assigned a value, but the root module does not declare a variable of that name. To use this value, add a "variable" block to
│ the configuration.
╵

リソース削除時の挙動

リソースインポート時にエラーは発生することは理解したが、 逆にリソースをstateから除外するときにどうなるか検証した。

手順は以下の通り

  1. サンプルリポジトリの通りリソースを構築(terraform apply)する
  2. Terraformをリモート実行モードに切り替える
  3. Terraform Cloud上で未定義の変数(例: undefined_variable = "test")を設定する
  4. ローカルのterraform変数(terraform.tfvars)に、3とは別の未定義変数(例: undef = "value")を設定する
  5. Terraformをローカル実行モードに切り替える
  6. リソースをstateファイルから削除する

結果は以下の通り、警告もエラーも生じずにリソースを除外することができた。 state操作において変数を参照するのはインポート時だけの挙動の様子。

$ terraform state rm aws_s3_bucket.sample
Acquiring state lock. This may take a few moments...
Removed aws_s3_bucket.sample
Successfully removed 1 resource instance(s).

まとめ

Terraform Cloud利用時におけるリソースインポート時には ローカル実行モードであってもTerraform Cloudおよびローカルで設定した変数定義が参照される。 特にローカルでは警告止まりだった未定義変数の警告が、Terraform Cloudだとエラー扱いになる。 ローカル実行モードで利用する場合はTerraform Cloud上の変数定義はすべて削除しておいた方がよい。