tech.guitarrapc.cóm

Technical updates

リモートワークで使えるイヤホン

以前の記事で、リモートワークでイヤホンをいろいろ試したことを書きましたが、実際使えるイヤホン、使えないイヤホンの個人的なポイントを書いておきます。 なお、イヤホンといいつつヘッドホンやヘッドセットも含まれます。

なお、イヤホンの接続先は MacBook Air M1 です。

tl;dr;

  • 優先/無線なら、無線を選ぶと便利かつ今なら音質な問題もない
  • スピーカーは、ノイズキャンセリングがあると周りの音よりリモートの音に集中できる
  • 最重要はマイク。ヘッドセットのような口元マイク(ブームマイク) + ノイズキャンセリングがついているのがいい
  • 骨伝導ヘッドセットだと耳を覆わないので、長時間の着用がつらくない、家で周りの音も聞こえつつリモートの音に集中できてよい

(よかった) Sony MDR-1000X

以前買っていた古いヘッドホンを引っ張り出して来たら、マイク、スピーカーともに優秀でした。(2017年2月購入) ワイヤレスノイズキャンセリングステレオヘッドセットとかいうだけあって、マイクの音もしっかり伝わり、スピーカーも問題ありません。 ノイズキャンセリング、大事。

www.sony.jp

今なら、正当進化したWH-1000Xシリーズでいいかと思います。

www.sony.jp

MDR-1000Xは好きだったんですが、筐体が弱く、イヤーパッドの上の本体部分の樹脂が割れてお蔵入りしていました。

壊れやすい箇所

飛行機でも音が気にならないので、本当にいいイヤホンです。(CES で気になって買ったやつでした)

(よかった) AfterShockz OPENCOMM

前回も書いた骨伝導イヤホンです。詳しくはそっちを見ていただければ。

同ブランドにAEROPEX というのもありますが、比較動画でわかる通りマイク性能が違います。

AEROPEXaftershokz.jp

リモートワークで気になるのが、話している相手の声、じゃなくてキーボード音なのですね。 OPENCOMM は、AEROPEX に比べて、スピーカー直と違和感ない程度には声が曇らず、キーボードのタイプ音も小さくなります。 OPENCOMM + Krisp のソフトノイズキャンセリングが組み合わさると、キーボード音がほとんど消えるのでこの組み合わせが気に入っています。 Aeroとの違いはマイク部分なので、なるほどブームマイク + ノイキャン効果ある。

www.youtube.com

ということで、キーボードタイプ音がうるさいか気になっている人は OPENCOMM 使うといいです。Krisp も組み合わせてください。 今一押しはこれですね。

ちなみに、マイク部分を上に上げると、外部の音がマイクから伝わりますが、マイクが口元にあるときは周辺の音がマイクから先に伝わりません。 普通にBGM鳴らしていても伝わらないので結構すごい。

(だめだった) WH-CH710N

いろいろ試そうと思って、MDR-1000Xよかったし、今の下位モデルでもいいのではと思って試しました。

www.sony.jp

スピーカーはいいのですが、マイクが壊滅的で、リモートワークでZoom/Meets/Teams ともに声が小さく、ぶつぶつ切れるという状況になりました。(krisp オフでも変わらず) マイクがほぼ機能しないので致命的でダメです。

(だめだった) HyperX Cloud Stinger

有線で、口元にマイクがあってよさそうじゃないですか。 無線より有線がいいという言説をよく聞くのでどうかなぁと思って試しました。 が、マイクがだめで声が大きくならずぶつぶつ切れるという状況になりました。(krisp オフでも変わらず)

ゲーミングヘッドセットwww.hyperxgaming.com

スピーカーもノイキャン付きの Sony に比べて蒸れて熱くなりやすく、好みではなかったです。

まとめ

いまならオープンエアーかつノイキャン付きのAfterShockz OPENCOMM がいいですね。 部屋で閉じこもって、外の音を気にしたくないなら、WH-1000X でもいいかと思います。

AirPods も知人と試した感じだと問題ありませんでしたが、私は使ってないので記載していません。

そういえば、一時 Bose や Plantronics がリモートワークで使えると話題になったのできになるところですが、OPENCOMMベースで考えると満足できるのかな。 片耳はなんとなくずっと避けているのですが。

リモートワークでの切り替え

リモートワークが続いて1年余り経ちました。 ここ半年余りの課題は、仕事中の集中、切れ目の2つなのですが、解消するために何をしているか残しておきます。

tl;dr;

オフィスで集中で来ているなら、その状況を作れるように努力する、ということでいろいろやっています。

  • 机の上をオフィスと同じようにする
  • 仕事前に身支度を整えて恰好から入る
  • 一日の目標は意図的に細かく書き出して達成目指す
  • Twitter は開かない
  • 骨伝導ヘッドセットを付けて過ごすことにしたところ、以前オフィスで仕事をしていた時のように意識を切り替えるのが楽になった
    • 骨伝導で 16時間使い続けられるのは本当に楽なのでよいです
  • 昇降デスクや昼寝など、自分の癖にあった対策はする
  • ごはんはサラダから (冗談のようで本気)

課題

結果は出す、けど自分が納得いく集中状態に入っていたかというと疑いがあるので、「リモートワークでの切り替え」でいかに集中状態にもっていくかが課題です。

もともと私はずっと仕事をしていても気にならないほうです。 これは、仕事と楽しいことが一致しているからなのですが、夢中になって気づいたら時間が過ぎていることがほとんどです。

ただ、リモートワークだと今までオフィスや深夜の集中で得ていたなにかと別の能力が求められるようです。 オフィスでの仕事だと簡単に集中に入るのが、リモートワークだとあと一歩足りないような。(語彙力) 時間は過ぎているけど、自分が自分に期待するだけの納得いく進捗を感じないような。(表現力)

深夜作業や緊急メンテなどで容易に得ている集中を継続的に再現したいです。

切り替えを促すためにやっていること

意識の切り替えは自分への暗示だと思っているので、いくつかやり方が思いつきます。 太字にしたものは、私が今行っていることです。

  • 机の上をオフィスと同じようにさっぱりと片付ける
  • カメラでずっと映るようにする
  • 常時オンライン雑談部屋に参加
  • 一日、あるいは単位時間の目標を常に示して細かく達成していく
  • 仕事と私生活の場を切り離す
  • スマホを見えないところに置いておく
  • Twitter を開かない
  • 仕事のトリガーを作る
  • 集中切れた、眠くなった対策

知人は、仕事用の部屋に移ってやることで完全に意識を切り替えているということで、これはいいなぁと思います。 ただ、あいにくと私はそういう部屋を設けているわけではないのでなしです。

やっていることを簡単に振り返っておきます。

(集中) 机の上をオフィスと同じようにさっぱりと片付ける

私の普段のデスクの状態です。 仕事場でもそうなのですが、私は極力ものを減らすのが好きなので机の上はキーボード、マウス、切り替え、お茶、スマホのみです。

オフィスと家で、同じように余計なものを置かないので代わり映えしないとも言います。

デスクの上

オフィスだとスマホはカバンにいれているので、スマホはノイズですが、後述します。

(集中/切れ目) 常時オンライン雑談部屋に参加

常時オンライン雑談部屋に参加していると、いつ声がかかってもいいようになります。という意味で、結構切り替えを促してくれています。 一緒の部屋で仕事をしている、というほどではないのですが、一緒にやっているという空気感はあるようです。

もちろん、必ずしも雑談しないといけないわけでもなく、必要な時に相談を行ったりという自由があるぐらいが気楽に感じます。 仕事終わるときに抜けると切れ目にもなります。

経緯

リモートワークが始まって3か月ぐらい経ったころの2020年6月までは朝ミーティングのみオンラインでやって、以降は必要に応じてオンラインにしますが基本オフラインでいました。が、限界を感じたので、常時オンラインをやるようにしてもらいました。(フットワークの軽いチームですごくいい)

リモートワークになることの最大のつらみは、信頼を貯める方法が減る、雑談機会の減少と思っています。

オフィスでの作業が別に最高とは言わないのですが、顔を合わせる以外に人となりを知っていることによる信頼はあったようです。さりげない会話や雑談、相談時のレスポンス、応答の様子をオフラインで行うと、比較的に好意的に受ける情報が想定していたのより大きいのだなぁと思います。1

リモートワークでの作業は、チャットやコード、レビューといったテキストベースのコミュニケーションに偏重していくように思います。 これは、たとえ朝・常時オンラインで話していても、観測範囲では例外なくその傾向があります。 オフラインで得られていた相手を信頼する機会/信頼される機会、オフィスでは自然と周りから聞こえてくる雑談を得る機会は意識してても減りました。失ってみて雑談が仕事上で大きかった、と3か月の間にたびたび感じることがあり危機感を覚えていました。

当時はそういう意味で「オフィスでの信頼貯金を崩しながらリモートワークをしている」と感じており、そろそろ限界を感じで常時オンラインミーティングに参加するというスタイルに切り替えたのでした。

切り替えてしばらくはお互いに遠慮するような「ぎこちなさ」がありましたが、一年もやっていると自然になってきているようで、雑談をする機会も増え、カジュアルなやりとりから相手を信頼をする機会も増えてきているのでいい傾向だと思います。 それでもオフラインのほうが楽なのは、やはり難しい。

よく言われることですが、人間は声だけでは冷たい印象を受けるので、顔や素振りを含めたコミュニケーションが圧倒的に有利です。つまり、オンラインでのコミュニケーションで白熱するとハラハラします。

課題

オフィスでも起こるものの、オンライン雑談のみになるとより起こりやすい課題がいくつかあります。

1つ目は、声が思ったより響いて集中が切れる問題です。 オフィスでもあった? そうですね、ではオンラインミーティングではどうやりましょう問題があります。 今のところ邪魔にならない程度までボリュームを落とすことで、意識に入ってきにくいようにしていますが、気づきにくくなるのが怖いところ。(でもボリューム落とす)

2つ目は、コミュニケーションの乏しい相手ができることです。 これはチャットでも同じ課題があるのですが、やり取りをしない相手とは関わる機会が極端に減ります。2 オンライン雑談でも、何もやりとりの機会がなければずっと同じでしょう。リモートワークでは常時オンライン雑談にいても喋ること自体のハードが高く、難しいものがあります。(伝われ) 別にそれでもいいのかもしれないとも思いますが、どうなんだろう。

3つ目の課題は、雑談の総量の減少です。リモートオンラインって、頑張っても一つしか聞こえないんですよね。オフラインだと複数チャンネルきこえて、たとえ自分がかかわっていなくてもやり取りが聞こえる、というのがありましたが。

4つめの課題は、オンラインで喋った内容をチャットやドキュメントに残す癖がないと取り残される人がどうしても発生することです。これは会話する人ほど気づきにくい問題で、取り残される人は未来の自分も会話を忘れれば該当するのですが、当事者になるまで自覚できないのが厄介です。 オフィスでも起こるんですけど、ほかの人が意外と覚えてて救われた経験ありませんか? 私はあります。 リモートワークだからこそ、チャットなどに会話の内容を要約して書く癖はあるといいと思います。(やる人を尊敬しています) この問題はあるいは、自転車置き場問題とも近いものがあるので、永遠に難しいですね。

2021年になっても、こういう基本的な課題が解決があると感じます。

(集中) 一日、あるいは単位時間の目標を常に示して細かく達成していく

始業時に、今日これやります、とチャットに書いておく、その程度でも効果はあります。 自分自身の予定というメリットもありますが、リモートワークでチームみんながやることを書いているのは、チームの状態共有や相談のきっかけになったりしてて良いと感じます。

コツ

やることを1つ二つ書くよりも、意図的に内訳を細かく書いて、自分で自分の達成を意識するのが好みです。 やりすぎはよくないけど、やることが目の前にはっきりしていると集中できるタイプの人間です。 「これが終わった、次はこれ」、たぶんこれが私が集中するルーティンで一番効果があります。

NOTE

終業時にやったことを書くのは、振り返りという意味でありなきもするけど、私は朝やることをどんどん追記しているのでどっちでも。

Disclaimer

ここではあえて、アジャイルなどの開発手法とは混ぜて書きません。

(集中) Twitter を開かない

これはTwitter に限らず、自分が頻度を高めにをやっているSNSと置き換えてもいいかもしれません。

私は、PC上ではTwitterを「ブラウザで特定のプロファイルを使わないとみれない」ようにしています。 仕事用のブラウザプロファイルでは見れないようにしてあるので、Twitterを見れるプロファイルでブラウザを起動しないルールを自分に課すことで集中の分散を防いでいます。

経緯

よく見かける気がしますが3、Twitter を見るというのは時間が消えます。 自分にはない知見を得られる場なので、おもしろく当然だと思います。 ただ、面白いということは他で使っていた集中が途切れているとも思うので、私は見ないようにすることを仕事との切り替えに利用しています。

(集中/切れ目) 仕事のトリガーを作る

今から仕事、仕事おわり。を示す方法として、物理的に身に着けるものがあるのは有効だと思っています。 出社のように、顔を洗って家仕事用の服に着替えたりといった身支度はしていましたが、それは休日でもあまり変わりません。

ということで、骨伝導ヘッドセットのAfterShokz OPENCOMMを仕事の時に身に着けるようにしました。

OPENCOMMaftershokz.jp

いくつか使ってて良いことを書いておきます、やりたいことができて、細かい不安がないのがいいです。

  • Zoom / Google Meets / Teams でもマイク、スピーカーともに問題なく、ノイズキャンセリングヘッドセットなのもあり、相手からも声がきっちりとどいているといわれており安心して使えます。(私は MacBook Air M1 とつないでいます)
  • イヤホン、ヘッドホンと違って一日つけるのを続けていても痛くなったりしないのは骨伝導のメリットと当時も今も思います
  • 骨伝導は5年前に使っていた頃は2時間あまりしか持たず微妙でしたが、これは16時間つなぎっぱなしもできるので、バッテリーで困ったことがありません。(充電がマグネットなのもよい)
  • もし充電をずっと忘れても急速充電が可能(5分充電するだけで2時間使える)なので適当になんとかなります

ここ半年、特に集中状態にもっていくのに結構悩んでいたのが、これを付けるようにしてオフィスの感覚により近づいているので維持したいところです。

ヘッドセットを外すと仕事終わり、つけると仕事で集中、という自己暗示に便利。

経緯

オフィスにいたときから、集中するためにイヤホン、ヘッドセット、骨伝導イヤホンといろいろ使っていました。 リモートワークになってみると、オフィスと違ってマイク問題が結構課題で、ヘッドセットのマイクだとリモートミーティング時に音が切れたりしたのでつけるのをやめていました。 あと、家だからこそ、周りの音が聞こえないのは主に宅急便などで困る。

リモートワークになってから、このヘッドセットを見つけるまで試行錯誤を繰り返しました。(たびたび試した4品全部だめだった)

(集中) 集中切れた、眠くなった対策

集中が切れたり、眠くなることもあります。(寝ないでいれても、集中が底辺になるのがまずい)

  • 軽く集中が切れたときとかは、昇降デスクなので立って仕事しています。気づくと深夜までたってます
  • 完全に眠い時は、昼ごはん後に15分寝ています。あきらめて寝ると、眠気飛んで集中がすごいので割り切っています

幸い目覚ましを無限にかけるので昼寝で寝過ごしたことはないのですが、夜しっかり寝るのがいいでしょう。4

あ、ご飯はなるべく食べましょう。食べたら眠くなる?これはお得情報ですが、サラダ山盛り食べてからほかのものを食べ始めると、冗談抜きで食後眠くならないのでオススメです。セブンの大根サラダを食べています。

7premium.jp

経緯

オフィスにいるときだと、少し外に出てコンビニ行ったりするやつですね。 リモートワークだと本当に外でないですよね、私のリモートワーク中の徒歩は平均300歩でした。 ということで、試行錯誤してモノで解消できるならする、など打てる手は打つようにしています。

今後やるか検討していること

今はデメリットも感じるのでやっていないものの、間違いなく効果があるので検討しているものです。

(集中) スマホを見えないところに置いておく

オフィスと同様に、目に写らないように片付けようかと思っています。 集中して存在が目に入らないことも多いのですが、チャットの通知など気にしたいことがあり、悩んで一年経ちました。

ただ、通知に気づかないとそれはそれで困るので、果たしてどうするのがいいのかまだ悩んでいます。 オフィスだと、誰かに甘えてたんですね。(どうしても気づかないときは声をかけられていた)

まとめ

いろいろ書いていますが集中は今でも課題で、やっぱりリモートワーク難しいなぁと思っています。 深夜に一人で仕事して、しっかり集中もしていたので、ここまで悩むことになると思ってなかったのですが、どうやらリモートワークはそれとは別の何かが必要なようです。

リモートワークでもできる、けど、よりよい方法を見つけられていないと常に感じます。 ほかの人のやっていることも、見つけると試すのですがなかなかしっくりこない。

もっと細かく自分で分析していかないと改善するのは難しいのかもしれない。

そして、お前は本当にオフィスで日中集中できていたのか?


  1. カメラだとそこまで感じないのはなぜだろう
  2. 私が times チャンネルを嫌う理由は、この問題を加速させるためです
  3. この時点でだめ
  4. それはそう

GitHub Actions の setup-dotnet と ホストランナーの .NET SDK

GitHub Actions で .NET Core ビルドを行いたいときに利用するものといえば、setup-dotnet があります。

github.com

今回は Ubuntu-latest な環境で setup-dotnet がいる場合と、そうでない場合について考えてみます。

tl;dr;

  • GitHub Actions ホストランナーのデフォルト環境で .NET はビルドでき、最新が1-2週で入ってくるのでOSS はsetup-dotnet なしで十分
  • プロダクトなど .NET SDK バージョンを厳密に制御する場合は、setup-dotnet を使うとよい
  • setup-dotnet は global.json を読んでくれるので、バージョン固定するなら global.json 便利

GitHub Actions と ホストランナー

GitHub Actions のホストランナーは環境ごとに特定のツールがインストールされた状態になっています。 例えば、ubuntu-latest である Ubuntu 20.04 LTS 環境 なら、以下のツールが入っています。

https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.mdgithub.com

2021年5月13日時点で .NET SDK は次のバージョンが入っており、かなり網羅されていることがわかります。 2.1 / 3.1 / 5.0 に関してはおおむね困らないでしょう。

2.1.300 2.1.301 2.1.302 2.1.401 2.1.402 2.1.403 2.1.500 2.1.502 2.1.503 2.1.504 2.1.505 2.1.506 2.1.507 2.1.508 2.1.509 2.1.510 2.1.511 2.1.512 2.1.513 2.1.514 2.1.515 2.1.516 2.1.517 2.1.518 2.1.519 2.1.520 2.1.521 2.1.522 2.1.523 2.1.602 2.1.603 2.1.604 2.1.605 2.1.606 2.1.607 2.1.608 2.1.609 2.1.610 2.1.611 2.1.612 2.1.613 2.1.614 2.1.615 2.1.616 2.1.617 2.1.700 2.1.701 2.1.801 2.1.802 2.1.803 2.1.804 2.1.805 2.1.806 2.1.807 2.1.808 2.1.809 2.1.810 2.1.811 2.1.812 2.1.813 2.1.814 2.1.815 3.1.100 3.1.101 3.1.102 3.1.103 3.1.104 3.1.105 3.1.106 3.1.107 3.1.108 3.1.109 3.1.110 3.1.111 3.1.112 3.1.113 3.1.114 3.1.200 3.1.201 3.1.202 3.1.300 3.1.301 3.1.302 3.1.401 3.1.402 3.1.403 3.1.404 3.1.405 3.1.406 3.1.407 3.1.408 5.0.100 5.0.101 5.0.102 5.0.103 5.0.104 5.0.200 5.0.201 5.0.202

このため、わざわざ setup-dotnet を使わずともdotnet cli は参照できますし、ビルドも可能です。 つまり、setup-dotnet を使わない場合、GitHub Actions のホストランナーに事前インストールされた複数の .NET SDK から必要なバージョンが参照されます。

setup-dotnet

setup-dotnet がやっていることは、「特定のバージョンの .NET SDK を azure feed からダウンロード、展開して参照できるようにする」ということです。

GitHub ホストランナーは Prerelease の .NET SDK には未対応で、ホストランナーの環境更新も 1週間に一度程度なので待ってられないというケースが稀にあります。 そんなときsetup-dotnet は Prerelease にも対応しており、まだfeed に乗っていないリリース直後の .NET SDK バージョンを利用するときに便利です。

GitHub Actions ホストランナーUbuntu 20.04 LTS の環境更新頻度

また、setup-dotnet はバージョン省略時にglobal.json があればそのバージョンを読む機能もあります。global.json と CI のバージョン固定なら global.json でいいというのはあるので、これはこれで便利です。(global.json の配置場所がリポジトリルートではない場合は少しアレってなりますが)

    if (!version) {
      // Try to fall back to global.json
      core.debug('No version found, trying to find version from global.json');
      const globalJsonPath = path.join(process.cwd(), 'global.json');
      if (fs.existsSync(globalJsonPath)) {
        const globalJson = JSON.parse(
          // .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649
          fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim()
        );
        if (globalJson.sdk && globalJson.sdk.version) {
          version = globalJson.sdk.version;
        }
      }

setup-dotnet/setup-dotnet.ts at bf3c3eb1fdba530a22805f082a2dcebc125d6ce4 · actions/setup-dotnet · GitHub

setup-dotnet の副作用

さて、この記事をここまで読んでいる人は「setup-dotnet を使うとインストールした以外の SDK が使えなくなる」という状況になったことがあるのではないでしょうか? ホストランナーの環境には、複数の .NET SDK が入っているにも関わらず、です。

これは setup-dotnet が、SDKのインストール時に .NET SDK の参照ルートパスを ホストランナーのデフォルトパス /usr/bin/dotnetから setup-dotnet で入れたSDK のパス $HOME/.dotnetに切り替えているために起こります。 結果、 setup-dotnet でインストールした以外のSDK バージョンは参照できなくなります。 .NET SDK のルートパスは、環境変数 DOTNET_ROOT に任意のパスを設定することでサクッと切り替えることができます。というのが気付きでした。便利。

setup-dotnet は installer.ts でこれを設定しています。

        // 184 行目
        core.exportVariable(
          'DOTNET_ROOT',
          path.join(process.env['HOME'] + '', '.dotnet')
        );

setup-dotnet/installer.ts at bf3c3eb1fdba530a22805f082a2dcebc125d6ce4 · actions/setup-dotnet · GitHub

実際に GitHub Actions で .NET SDK の参照パスと利用可能バージョン一覧を見てみましょう。 setup-dotnet を利用する前は、標準の/usr/bin/dotnetが利用されています。

GitHub Actions ホストランナーデフォルトの .NET SDK 状態

setup-dotnet を利用すると、パスが /home/runner/.dotnet/dotnet になり、利用可能な SDK も限定されていることがわかります。

setup-dotnet利用後の.NET SDK の参照パスと利用可能バージョン一覧

まとめ

一見すると、setup-dotnetを使うとめんどくささが増えてメリットがスポイルされているように見えます。 しかし、プロダクトなど厳密に .NET SDK のバージョンを管理したい場合は、setup-dotnet を使うのは合理的でしょう。 一方で、基本的にホストランナーには最新バージョンの.NET SDKが入っているので、OSS では 脆弱性情報などの対応を除き ホストランナーのデフォルトで十分というのは言えそうです。

  • setup-dotnet を使うと、.NET SDK preview や任意の.NET SDK をインストールができる
  • setup-dotnet を使うと、デフォルトの .NET SDK のパスから、setup-dotnet でインストールした .NET SDK のパスに変更されるため、ホストランナーのデフォルト .NET SDK 達はパス省略で使えなくなる

今日の学びでした。

Terraform の構成

ベストプラクティスといいつつ、どういう風にやりたいかで変わるというのは往々にしてあります。 ベストプラクティスは求めても意味ないのでどうでもいいとして、いろんなパターンのメリット/デメリットを把握して現状に即しているのかどうかは考え続ける必要があります。

ということで、長年頭を悩ませて納得感があまりない代表が Terraform です。 今回は以下の記事を読んでて、普段やっている Terraform の構成について書いてなかったので記録として残しておきます。

blog.serverworks.co.jp

TL;DR;

  • ディレクトリ分離 + モジュールでやっているけど、基本的にローカルモジュールに寄せている
  • locals.tf で差分を完全に抑えて、モジュール内は分岐なしの型で環境の違いを表現している

実は Workspace で分離してもさほど問題ないといえば問題ないことに読み直して、ふと考えが変わってきました。 ただし、Workspace の state後戻りができないのが厳しく、この環境だけしれっと追加みたいなカジュアルさは欠けるというのはあります。 そういう意味で、やはり Workspace は手放しで使う気にはなれないものがあり、微妙だなぁと感じます。

HashiCorp社の Terraform ベストプラクティス

ベストプラクティス自体よりも、開発元がどのようなコンセプトを持っているかは注目に値します。

Terraform開発元のHashiCorp 社(以降、公式)はたびたびBest Practice 的なことを謳いますが、具体的な構成を示していた昔のリポジトリは archive されて久しいものがあります。だめじゃん。

github.com

Nebulaworks の例

公式はさまざまなリポジトリ構成があるといいつつ、具体例をほぼ示していない。と感じつつも、Nebulaworks の例を2020/Jan/18 にブログで紹介しプレゼン動画で構成を見ることができます。(0.11.11 の頃なので0.14の今ではproviderやversions などは古く感じます)

www.hashicorp.com

youtu.be

youtu.be

この中で以下のモジュール構成例を示しています。(いわゆるよくあるモジュール構成) プレゼンの内容はいうほど特筆することはないです。

.
├── env
│   ├── dev
│   │   ├── main.tf
│   │   ├── module.tf
│   │   └── reseources.tf
│   ├── prod
│   │   ├── main.tf
│   │   ├── module.tf
│   │   └── reseources.tf
│   └── staging
│       ├── main.tf
│       ├── module.tf
│       └── reseources.tf
└── modules
    └── resources.tf

Nebulaworks Module Directory Structure

公式では構成は示されないので、仕方ないのでコミュニティに学びにいく旅に出ることになります。(2021年になっても変わってない)

なぜ Terraform の構成は難しいのか

改めて、Terraformの構成が難しいのはなぜか考えてみると、terraform はパスベース + .tf 拡張子で参照ファイルや変数のスコープ1が決まります。 スコープが驚くほど広いわりに、言語機能としてそのスコープ内でファイル限定といった表現力は持たないため、ファイル構造でカバーすることになります。 これが難しい原因の多くを生み出しているように感じます。 具体的にあげると、

  1. 言語としてはシンプル、だけどスコープも広いために収拾がつきにくい
  2. インフラ構成はただでさえブラストラディウスが広いにも関わらず、言語による「影響を限定させるサポート」に乏しい。2
  3. 同じコードを何度も書くのは事故の原因なので、なるべくDRYにいきたいが運用しやすさとのトレードオフはしないようにするサポートが乏しい。3

言語機能が貧弱というほうが適切かもしれないですね。

ということで、今現在よく使っている構成と、きっかけとなったブログのセクションについて考えます。

よく使っている構成

私は実行環境をTerraform Cloud に一本化しています。(以前はatlantis を用いていました)

今現在よく使うディレクトリ構成は次のものです。 EKS は特に悩みやすいのでサンプルを置いておきます。

├── common (VPC分離の場合のみ)
├── dev
│   ├── compute.tf
│   ├── data.tf
│   ├── iam.tf
│   ├── locals.tf
│   ├── modules
│   │   ├── compute
│   │   │   ├── ecr
│   │   │   │   ├── main.tf
│   │   │   │   ├── outputs.tf
│   │   │   │   └── variables.tf
│   │   │   ├── eks
│   │   │   │   ├── iam
│   │   │   │   │   ├── eks_cluster_role
│   │   │   │   │   │   ├── main.tf
│   │   │   │   │   │   ├── outputs.tf
│   │   │   │   │   │   └── variables.tf
│   │   │   │   │   ├── eks_node_role
│   │   │   │   │   │   ├── main.tf
│   │   │   │   │   │   ├── outputs.tf
│   │   │   │   │   │   └── variables.tf
│   │   │   │   │   └── eks_pod_role
│   │   │   │   │       ├── data.tf
│   │   │   │   │       ├── main.tf
│   │   │   │   │       ├── outputs.tf
│   │   │   │   │       └── variables.tf
│   │   │   │   ├── main.tf
│   │   │   │   ├── oidc
│   │   │   │   │   ├── bin
│   │   │   │   │   │   └── oidc_thumbprint.sh
│   │   │   │   │   ├── main.tf
│   │   │   │   │   ├── outputs.tf
│   │   │   │   │   └── variables.tf
│   │   │   │   ├── outputs.tf
│   │   │   │   └── variables.tf
│   │   │   └── kubernetes
│   │   │       ├── main.tf
│   │   │       └── variables.tf
│   │   └── iam
│   │       ├── account
│   │       │   ├── main.tf
│   │       │   ├── outputs.tf
│   │       │   └── variables.tf
│   │       ├── role
│   │       │   ├── main.tf
│   │       │   ├── outputs.tf
│   │       │   └── variables.tf
│   │       └── user
│   │           ├── main.tf
│   │           ├── outputs.tf
│   │           └── variables.tf
│   ├── outputs.tf
│   ├── providers.tf
│   ├── variables.tf
│   └── versions.tf
├── production
├── staging
└── modules
    ├── iam
    │   └── iam_role
    │       ├── main.tf
    │       ├── outputs.tf
    │       └── variable.tf
    └── xxxxx

原則

この構成は、運用においてモジュール構成に分岐や入れ替えなどを起こさないことを目的に組んでいます。 そのため、次の原則に基づいています。

  • Workspace を使った dev/staging/production の環境切り替えは用いない
  • 環境ごとの差分は local変数で表現し、モジュールやdataを含むコードは、構成が全く同じなら同じとする。(./locals.tf)
  • ルートの共通Module には普遍的で必ず環境差分が起こらない共通化するモジュールのみ配置する。(./modules)
  • モジュールには、アプリの事情を込みで記述し、ルートのlocals.tf に記述した環境ごとのパラメーターを variables.tf で受け取る。(./dev/modules など)
  • 環境の違いがある場合、その環境のローカルモジュールのみ変更を加える。(基本的に locals.tf のパラメーターのみで対応できるようにモジュールに改修を入れる)

この原則だけわかっていると、ただのシンプルなModule構成です。 誰が見ても初見ですぐにわかり、影響を分離できるようにするのがゴールです。

要はモジュール構成ではモジュールを共通化するのが前提ですが、初めからローカルモジュールに展開するように割り切った構成です。

terraform ファイルの記載内容

モジュール内部の構成は見ればわかると思うので、ざっくりとdevフォルダ配下のファイル説明だけ見てみます。

  • data.tf: data リソースの定義を行っています
  • locals.tf: local変数定義を行います。環境ごとの定義はここですべて表現されます。環境差分はここだけ発生します
  • modules: ロジックはすべてモジュールに閉じ込めます。compute.tf などにresourceを直接書くことはなしです
  • outputs.tf: よくある出力定義です
  • providers.tf: プロバイダー一覧
    • 0.13 から provider がいい感じになったので、main.tf をやめて providers.tf と versions.tf にきっちり分離するようになりました
  • variables.tf 実行時の variables.tf 差し込みはterraform cloud でのクラウドへの認証差し込みなど必須な情報以外入れない
  • versions.tf: terraform ブロックとプロバイダーバージョン、backend 指定

残りのcompute.tfやiam.tfは、ただのモジュール参照です。 main.tf に羅列していると使いにくいので、いつでもリソースをパージできるように分けています。 tfstate の分離はもちろんしますが、するしないはプロジェクト規模でも変わって来たりするので、ここでは簡単のため同一stateとしました。

NOTE: terraform を使って upgrade とかしていると、providerとversionsはこういう形になる気がします。(0.12で自動生成されたversions.tf にはじめ戸惑いましたが、0.13で納得いく変更がきた)

www.terraform.io

この構成で最も設計しないといけないこと

ローカルモジュールの設計が大事になります。

  • 異なる環境でも同じlocal変数の型でモジュールが受けるようにできるようにすること
  • ローカルモジュールの凝集
  • module のoutput

環境差分は locals.tf にのみ許容することはやってみると全然できます。 しかし、いざやると dev だけ 2つリソース作って、stagingやproduction では 1つだけ、などといった差に出会ったりするでしょう。 そういったものは型でうまく解決するようにします。

例えば、1つ、2つといった数の差異がモジュールで閉じ込められるように mapを使ったり map は型表現が弱いので set(object) を使ったりといった工夫は必要です。 共通モジュールでも、分岐ではなく、こういう型での解決をするほうが望ましいことは多いので別に違いはないといわれるとそうですね。

元記事の迷子

私もいつも迷子なので今時点の考えを書いておきます。

workspace 使うのか使わないのか問題

使いません。

とはいっても、元記事の「今どの workspace にいるのか」という問題が発生しません。 これは、Terraform Cloud の remote apply を必須にしているため、Workspaceを使わずとも環境ごとに Terraform Cloud の Workspaceは分離されているからです。

使わない理由は別にあります。 workspace 機能を用いた場合、環境ごとに差分が生じた場合に、差分を吸収するために分岐を用いたりするのがいやだったからです。 workspace の効果で得られる共通リソースで完結するメリットは、「分岐を見逃した場合など発生することが当然あるであろう状況で環境を破壊するリスク」に比べて割が合わないと思っているからです。

私は人間はミスをすると思っているので、ミスが起こっても影響が抑えられる構成を好みます。

環境の分割方法問題

環境の分割は、見ての通り「環境ごとにディレクトリを作る」です。 シンプルな反面めんどくさくなるのが「漏れがあったら」という問題ですが、それは locals.tf への環境差分の限定とモジュール設計で対応します。

module 設計問題

  • prod/stg それぞれで構築する: ローカルモジュール
    • 将来にわたって完全に同一で差分がない場合のみ、共通モジュールで定義します
  • 環境別に構築したりはしないが両方で利用する: ローカルモジュール
    • 将来にわたって環境ごとの破壊的な変更が起こらず、完全に共通利用でき環境差分がない場合は、共通モジュールへ配置
  • 片方だけ構築する: ローカルモジュール

今行っている構成では、まったく同じ構成なら、diff を行っても locals.tf 以外に差分が出ません。 そのため、devで検証が終わりstagingやprodに適用する場合も、モジュールは丸っとディレクトリごとコピーで構わないので変更もれリスクが抑えられます。(localst.tf だけ残して残り丸っとコピーでいい)

共通Modules + 分岐が必要なものを、ローカルモジュールに振っているので、そういう悩みが起こらない設計にしています。 同じものを複数書くのではなくコピーにしているのは、まったくスマートではないですが、悩むことより事故は起こらないものです。

自作 module か、公式 module か問題

私は自作module が多いようです。

公式module を一時期に優先して使っていたのですが、awsモジュールなどで結構破壊的変更があったので苦しくなってやめました。 とはいえ、vpcなどは公式モジュールでも十分に柔軟で、破壊的変更も起こされた記憶がないので使ってもいいです。(この見極めが難しいのが公式モジュールの問題だと思います)

variable の配置方法問題

これはベストプラクティスに乗らない理由がないので遵守します。

  • ファイル分割: 分割する
  • 格納データの型: 厳密に指定する。map(string) よりは object(型) で定義して、差し込むときは map で指定するのがいいです
  • デフォルトの値有無: 基本的にデフォルト値はなし
    • デフォルト値があってもいいのは、入らなくても動作にまずい影響が起こらない場合のみです
    • デフォルト値をないようにすると、値を意味を持って指定することになるため意図のない設計が発生しないためです

NOTE: 事故があってからデフォルト値なしに考えを変えました。デフォルト値、意図がない限り基本的に避けたほうがいいです。

過去に参考にした構成

コミュニティから学ぶことは多くあります。 今の構成はこれらの構成を自分でやってみて、納得がいかない部分を変えています。

terraform-aws-providerコミッターが公開している 以下の構成はよく見かけるものです。

github.com

small

.
├── README.md
├── main.tf
├── outputs.tf
├── terraform.tfvars
└── variables.tf

medium (large は未完成で medium と同じ)

.
├── README.md
├── modules
│   └── network
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── prod
│   ├── main.tf
│   ├── outputs.tf
│   ├── terraform.tfvars
│   └── variables.tf
└── stage
    ├── main.tf
    ├── outputs.tf
    ├── terraform.tfvars
    └── variables.tf

他にもありますが、コンセプト似てますね。

kenzo0107.github.io

dev.classmethod.jp

future-architect.github.io

他にもレイアード構成とかもやってた時期がありました。

書籍としては、「実践Terraform AWSにおけるシステム設計とベストプラクティス 」が良書です。module構成から、ローカルモジュールにもう少し振ってもいいと思ったきっかけは、この本でした。

https://www.amazon.co.jp/dp/B07XT7LJLCwww.amazon.co.jp

他に、「Google Cloud Platformで学ぶTerraform 〜実践編〜」などもテストが手集めに書かれています。(一方で構成はあまり触れていません)

techbookfest.org

おわり

terraformを使い始めて、もう使って5年たつような気がしますが、ディレクトリ構成には今も納得がいかないです。とはいえ、過去に試した構成よりも納得度と、変更の楽さ、影響度の限定ができているので今はこれで。

CDK や Pulumi のような、プログラミングの一般的なルールで構成できるものはこういうところが圧倒的に楽です。 とはいえ、Pulumi や CDK を書いてから terraform に戻ると、型を意識せずに依存関係が解決されて楽極まりないのもあり、なんとも難しいものだと感じます。

どっちもいいし、どっちもまだまだ改善の余地があるので、引き続き迷子を楽しんでいきたいものです。

ベストプラクティスがないのはいいですが、公式でそろそろモジュールの典例はしめしてもいいのではと思いますが、HashiCorp社なのでしないでしょう。残念ですが。

蛇足

リソース名のsuffix

いろんな経験を積んだ結果、アカウント分離だろうと、リソース分離だろうと、リソース名のsuffix には _dev などの環境名を含んで設計するといいと思っています。 あと、CDK や Pulumi のようなランダムリソースsuffix も好ましいですが、文字長制限で怒られたりするので 2021年にもなって各クラウドはいい加減にしてくれと引っかかる度に思っています。

文字種 4もそうだけど、文字長制限5が許されるのは201x年で終わってほしかったけど、現実はそんなに甘くない。

Terraform公式で示しているベストプラクティス

公式は、主に次のようにTerraform をどのように運用に乗せていくかのベストプラクティスを示しています。 とはいえ、内容はどのように現状を把握して使える状態にもっていくかがメインです。

www.terraform.io

具体的なTerraform 構成は触れていないものの、Terraform Cloudの文脈でプラクティスをいくつか示しています。 3.3.4 を要約します。(要約は気になる方はぜひ元文章もどうぞ)

  • VCS リポジトリ/ブランチと Terraform Cloud のWorkspace をマッピングさせて
  • AppやServiceの各環境は、同じリポジトリ/ブランチのTerraformコードで管理し
  • 異なる環境は variables で表現しつつ Workspace 事に適切に設定する

NOTE: 加えて、環境ごとにブランチを分けるのではなく、1つの正規ブランチをもとに全環境に適用することにも触れています。

Part 3.3.4 Create Workspaces

また、この中で自作モジュールの作成基準についてダイアグラムを示しています。(同時にナレッジシェアしようといったことも言っています)

Part 3.2.3 Create Your First Module

また、Terraform のチュートリアルにおいて、モジュールをプログラミング言語によくあるライブラリ、パッケージなどと似たものと触れつつ、ベストプラクティスを示しています。

learn.hashicorp.com

We recommend that every Terraform practitioner use modules by following these best practices:

  1. Start writing your configuration with modules in mind. Even for modestly complex Terraform configurations managed by a single person, you'll find the benefits of using modules outweigh the time it takes to use them properly
  2. Use local modules to organize and encapsulate your code. Even if you aren't using or publishing remote modules, organizing your configuration in terms of modules from the beginning will significantly reduce the burden of maintaining and updating your configuration as your infrastructure grows in complexity
  3. Use the public Terraform Registry to find useful modules. This way you can more quickly and confidently implement your configuration by relying on the work of others to implement common infrastructure scenarios
  4. Publish and share modules with your team. Most infrastructure is managed by a team of people, and modules are important way that teams can work together to create and maintain infrastructure. As mentioned earlier, you can publish modules either publicly or privately. We will see how to do this in a future tutorial in this series

しかし私は構成の例が欲しいのであった。

モジュールのベストプラクティス

モジュールを書く時の注意がいくつかあります。 とはいえ、別に大したことはいってないので普通にやればいいのではないでしょう。

  • Dependency Inversion
  • Multi-cloud Abstractions
  • Data-only Modules

www.terraform.io


  1. せめて local 変数がファイル内部でスコープがとどまるならまだしも
  2. 影響を限定させる手段としてモジュールやstate分割を使っていくことになる
  3. Workspace やモジュール参照で同一ソースを使うなどを使っていくことなる。
  4. - とか _ とか使える文字種がずれたりするのはとても悪い文化
  5. 64文字制限もあれだけど、32文字制限とか許せない

WSL2 環境でのローカル Kubernetes クラスタ構築を検討した話

私はローカル環境の Kubernetes にDocker Desktop for Windows を用いています。

Minikube や kubeadm、k3s、kind、microk8s など各種クラスタ構成がある中で、WSL2にローカルのクラスタ環境を他で組んだ場合の違いを改めてみておくことにしました。

今回は ローカル Kubernetes クラスタの構成にMinikubeを用いて構築してみて、最終的にどれがいいかの所感です。

目次

TL;DR

Windows/WSL で共通のコードベースを Windows にファイルを置きつつ、Windows と WSL でファイルマウントして、docker-compose も使って、Kubernetesも使いたい、という環境だとDocker Desktop (Hyper-V) + WSL1 が現時点ではおすすめ。 Docker Desktop での Kubernetes Cluster は、展開も極めて容易で local docker registry も共用され、Windows/WSLの両方からアクセスが容易なのでとてもいいです。Minikubeやkindと比べても何気に構成も奇をてらっていないのもいいです。今後のdockershimsに対する同行が気になります。

  • Docker Desktop (Hyper-V) + WSL1 がストレスなく快適。WSL1 Ubuntu での docker volume マウントだけ注意
  • Docker Desktop (WSL2) + WSL2 は、Kubernetes は快適だが docker-compose でファイルマウント、dockerアプリのWebレスポンスが絶望なので無理筋
  • Minikube を docker-driver でWSL2 に建てる場合、Windows からのアクセスは minikube tunnel で Load Balancerをトンネルする以外は手がない、悲しい
  • Docker Desktop (Hyper-V) + WSL2 は ホスト docker イメージの共有ができず、大変に厳しいものがある

半年余り WSL2 で組んでいましたが、Minikube の結果も受けたうえで、Hyper-V + WSL1 に戻ることにしました。 2020年は WSL2 で喜び、WSL2 に泣いた。 2021年がどうなるのか... WSL2 の 9P が劇的改善、vmmem/COM Surrogate の CPUあげあげも解消したら最高ですね、WSL2に戻ります。

なお、Linux 上でファイル操作が完結するなら WSL2 が圧倒的におすすめです。WSL と違って妙な制約一切ないので WSL2 最高。Windows と Linux でファイル操作をまたぐ頻度、ファイル量がほぼないなら WSL2 でいいでしょう。

はじめに

この記事の視点は利用用途に強く依存しています。 私と同じ使い方をしたい方でないとまったく違う検討結果になると予想されます。

環境と利用方法が合致するときにだけ参考になるかもしれませんし、あるいは参考にもならないかもしれません。

環境

  • Windows 10 Pro 20H2
  • Docker for Windows 3.0.0
  • Ubuntu 20.04 on WSL2

期待する利用方法

  • Windows から WSL にたてたKubernetes の Ingress/LoadBalancer へのアクセスを行う
  • Kubernetes より簡便に動作確認を行う環境として docker-compose も同様の構成で動かす

NOTE: WSL内部からの Kubernetes アクセスのしやすさは評価対象ではない。

Kubernetes のローカルクラスター構成

ローカルのKubernetes Cluster構成には選択肢がいくつかあります。 この記事ではMinikube on WSL2 で見ていきますがちらっと見ておきましょう。

NOTE: おおむね今のローカルKubernetesクラスタの選択肢はなんとなくここをみていただくとして。

  • kind
  • microk8s
  • k3s
  • k3d

それぞれ WSL2 と組み合わせて動作させる方法があるので興味ある人はみるといいでしょう。

NOTE: kind は LoadBalancer に対応していないので、こういう用途では使いにくいのを付記しておきます。インストールは本当に簡単なのですが。

kind.sigs.k8s.io

wsl.dev

k3s on WSL2 · GitHub

wsl.dev

WSL2 ではなく、ホストOS に Kubernetes クラスターを組むのも十分に有用な方法です。 Windows/macOS で最も簡便、かつ構成の組みなおしも用意なのは Docker Desktop の Kubernetes クラスターでしょう。

docs.docker.com

VMに建てるという意味では、Minikube を Hyper-V/Virtual Box で入れるという手もあります。(よくやられているやつ) Minikube に限らず、WSL2 で建てるのとVMで建てるのはおおむね概念は同じなので、どれもそういった方法を提供しています。

kubernetes.io

Minikube on WSL2 のインストール

WSL2 で起動した Ubuntu 20.04 に Minikube を入れて、Minikubeの自動起動をsystemd にさせることろまで構築します。 おおむね Kubernetesブログ に沿ってみていきましょう。

kubernetes.io

記事では Docker Desktop の WSL2 Integration を有効にしていますが、docker のファイルマウントの遅さを排除するため Hyper-V のまま動作させます。

Hyper-Vバックエンドの場合、WSL2 Ubuntu には docker /docker-compose / kubectl は入らないので、Ubuntuには自分で入れましょう。

systemd の構成

Minikube は --vm-driver=none の場合 systemd が必要ですが、docker の場合不要です。

--vm-driver=docker でも、WSL2ログインごとに minikube 起動をやっていられない + SysV Init を用意するのもつらいという場合、WSL2でSystemdを動かして minikube.service を作って調整してもいいでしょう。

NOTE: もちろん、.bashrc で適当に開始させてもいいでしょう。

systemd を WSL2 に入れる方法は、Kubernetes のminikube ブログやいい記事が紹介しています。

qiita.com

これで systemd の起動が確認できます。

systemd で自動起動するためのminikube.service は適当にこんな感じで。

[Unit]
Description=Runs minikube on startup
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/minikube start --vm-driver=docker --addons ingress --kubernetes-version v1.18.0
ExecStop=/usr/local/bin/minikube stop
User={{ ansible_user_id }}
Group={{ ansible_user_id }}

[Install]
WantedBy=multi-user.target

systemd を構成する場合、genie ではなく enter-systemd-namespace スクリプトの方がカスタマイズしやすいのでおすすめです。 例えば、systemd にすると Windows からの WSL2 ログイン時に bash loginをするため /mnt/Windowsパス が維持されず ~/ になってしまいますが、enter-systemd-namespace で bash login 前に今のパスを保持しておいて、ログイン後に自動的に cd するなどの対処が取れます。

minikube の動作確認

minikube を docker driver で起動させてみます。

minikube start --vm-driver=docker

minikube の起動が確認できます。

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
timeToStop: Nonexistent
$ kubectl get node
NAME       STATUS   ROLES    AGE   VERSION
minikube   Ready    master   21h   v1.18.0

$ k get pod -A
NAMESPACE     NAME                                        READY   STATUS      RESTARTS   AGE
kube-system   coredns-66bff467f8-tmqw6                    1/1     Running     1          21h
kube-system   etcd-minikube                               1/1     Running     1          21h
kube-system   ingress-nginx-admission-create-49j8v        0/1     Completed   0          21h
kube-system   ingress-nginx-admission-patch-cwrkz         0/1     Completed   0          21h
kube-system   ingress-nginx-controller-6f5f4f5cfc-9hw8f   1/1     Running     1          21h
kube-system   kube-apiserver-minikube                     1/1     Running     1          21h
kube-system   kube-controller-manager-minikube            1/1     Running     1          21h
kube-system   kube-proxy-72482                            1/1     Running     1          21h
kube-system   kube-scheduler-minikube                     1/1     Running     1          21h
kube-system   storage-provisioner                         1/1     Running     3          21h

minikube on WSL2 の面倒な点

minikube を --vm-driver=docker-driver で起動した場合に面倒なのは2点です。

  1. minikube の docker registry に push しないといけない
  2. ingress に Windows ホストからアクセスできない

minikube を docker driver で起動した場合、minikube の docker registry は Ubuntu とは別に構成されます。 そのため、bashログイン時に eval $(minikube docker-env) をする必要があるので注意です。

minikube を docker driver で起動した場合、ingress のAddress は docker ドライバーで起動した minikube ip のアドレスになります。 localhost ではないため、Windows から WSL2 への直接のアクセスはできません。 この場合、Service: LoadBalancer にしておくと minikube tunnel でlocalhost にトンネルされるので Windows からアクセス可能になります。

余談: なぜローカル Kubernetes 環境を必要とするのか

開発環境、開発ブランチ環境に対応するKubernetes があるとき、なぜローカルのKubernetes は必要なのかという話があります。

私はKubernetesの管理者であり、Devでもあるので、ローカル開発は何にもまして最速でプロダクトに期待する動作を確認する必須の場といえます。 ローカル開発は PoC であり、実際の運用環境とくらべて構成要素はミニマムだったりクラウド依存のなさなど差異もあるでしょう。 しかし、想定するアプリ動作を満たす場としては最適であり、常にメンテされていくべきと考えています。 また、自分のみに閉じているという意味でもローカル開発環境は整備する価値があります。 Kubernetes に各種 Operator/Controller を入れて動作させる場合は、ローカルKubernetes がない状況で、Devなどに展開するのは開発効率の面からみて避けたいものです。

ただしこれらの前提として、実際に動作する環境とかけ離れた構成になることは望ましくありません。 例えば、ローカルKubernetesではIngress に何かしらの制約がありLoad Balancer にする、といった状況は絶対的に避けたいものです。 運用環境とかけ離れ、期待する動作環境をローカルに作れず、メンテもできない場合、そのローカルKubernetes 環境はむしろ害悪になるので避けたいものがあります。

別の視点としてKubernetesの管理者という前提がない場合、ローカルKubernetes が必要である必然性が消えます。 例えば、通常のアプリコードを書いて動かすだけにおいては、Kubernetes だろうとどこででもいいからコードがデプロイされて動作すればいいです。 というか Kubernetes とか知りたくないし、ぐらいでもいいでしょう。

Kubernetes の管理をしつつDevもする身としては、ローカルKubernetes 環境はほしいものですが、メンテできるか、その構成は常に今のスタンダードからみてかけ離れていないかは常に気にする必要があります。Minikube はそういう意味ではスタンダードですが、悩ましい側面も多いですね。

kind on WSL2 はどうなのか

インストールはスムーズです。minikube より楽。 ただし、kind は Load Balancer に対応していないので使いにくさは否めません。

おまけ: WSL環境の選定

WSL には、WSL1 と WSL2 があり、この2つはホストOS のWindows との相互利用で結構な違いがあります。 Kubernetes を WSL に建てる場合、これは大きな違いになります。

docs.microsoft.com

WSL2 なのか WSL1 なのか

選択肢はおおむね2択であることが多いかと思います。

  • WSL2のみで利用し純粋にLinuxとほぼ同様に扱いたい、WSL2とWindows は速度面などで気にならない場合、WSL2 + Docker on WSL2 がいいでしょう
  • Windows と WSL2 のファイルマウント1 の9Pに由来する遅さ、Docker on WSL2のパフォーマンス制約、ホストOSのCPU/メモリ負荷が気になり許容できない場合、WSL1 がいいでしょう

別の構成として、Docker のみ Hyper-V で動かし、WSL2 でUbuntu を動かすことも考えられます。 この場合、docker 利用時のファイルマウントの遅さは生じず、WSL2 の Ubuntu はLinux 同様に扱えますが ホストWindows の Docker は共有できません。(tls:localhost:2375 がだめなんですよね)

WSL2 でないといけないのか WSL1 ではだめなのか

Windows/WSLの相互利用時のパフォーマンス、docker volume のマウントの取捨選択になります。 私の利用ケースはWindowsとWSLの相互利用がメインなので、WSL2 よりもWSL1 のほうが望ましいとなります。

ファイルマウント、ホストCPU負荷の両面でWSL1 は優れています。 一方で、純粋なLinux として使うには細かな差異とdocker 動作の制約が大きなハンデになります。(エラーもでないので罠に感じやすい)

WSL2

ほとんどのケースで問題ないことが多いでしょうが、パフォーマンス制約は軽く見るのは避けるほうがいいでしょう。

NOTE: Zen3 5900X + PCIE 4.0 NVMe でCPU負荷は無視できます。しかしWindowsホストファイルのマウントはどうしようもなく遅く回避策が現状ありません。

回避不可能な Windows ホストとのファイルボリュームマウント制約、そしてCPU負荷 (vmmem / COM Surrogate) があることに注意です。

NOTE: .wslconfing でメモリ、CPUの上昇をある程度止められますが十分に負荷が大きいので厄介です。

ただ、WSL2 Ubuntu と Docker WSL2 で Docker registry も共有され、docker/kubectl も入ってくるので非常に使いやすいです。

WSL1

WSLでdocker volume マウントができない制約があります。また、単純なWSL内部のファイルアクセスもWSL2 に比べて格段に遅いので注意がいります。

NOTE: docker実行はWindows でdocker run している限りは起こらないので回避策はあります。

WSL2 と違って、Windows ホストに対するパフォーマンスペナルティがなく、WSL での動作制約があるだけなのはバランスがいい選択になります。

WSL Ubuntu から docker を使うには、Docker Desktop で tls:localhost:2375 を開放して、WSLのDOCKER_HOST にする必要があります。 これで Windows の docker registry が WSL Ubuntu でも共有されるので、使い勝手の面で非常に楽になります。

おまけ: Kubernetes のインストール先の検討

ホストOSのWindowsでKubernetes クラスターを組んだ場合、Windows/WSLの両方からアクセスしても問題ありません。 WSL2 でKubernetes を組んだ場合、Windows からのアクセスには WSL2 の localhost でアクセスができる必要があります。

こういう意味では、Windows ホストにサクッとクラスターを組んで、いつでも爆破できる状況を作るのが最も使いやすいでしょう。

Docker Desktop for Windows なら、Settings 画面から Kubernetes Cluster のファクトリーリセット、再構成も用意なのでいいでしょう。


  1. それに伴うネットワーク速度の遅さも発生する