tech.guitarrapc.cóm

Technical updates

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 のまま動作させます。

f:id:guitarrapc_tech:20210101032549p:plain

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 の起動が確認できます。

f:id:guitarrapc_tech:20210101033522p:plain

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 があり、この二つはホストOS のWindows との相互利用で結構な違いがあります。 Kubernetes を WSL に建てる場合、これは大きな違いになります。

docs.microsoft.com

f:id:guitarrapc_tech:20210101024249p:plain

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. それに伴うネットワーク速度の遅さも発生する