はじめに
Kubernetes の Windows 対応は v.1.14 でGAとなりました。
本記事では、既存の Kubernetes クラスタに0から Windows ワーカーノードを追加する方法をご紹介します。
実行環境
今回は実行環境として Azure を使用しています。
- Kubernetes クラスタ(hard way on azure で作成)
- Linux VM(ubuntu 18.04)×6
- Windows ノード
- Windows VM(Windows Server 2019 Datacenter)×1
# クラスタには3台の Linux ワーカーノード
$ kubectl get no
NAME STATUS ROLES AGE VERSION
worker-0 Ready <none> 12m v1.15.0
worker-1 Ready <none> 7m26s v1.15.0
worker-2 Ready <none> 5m59s v1.15.0
クラスタ側の修正
hard way で作成したクラスタですが、kube-controller-manager の起動オプションが足りていません。
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
--address=0.0.0.0 \
+ --allocate-node-cidrs=true \
--cluster-cidr=10.200.0.0/16 \
--cluster-name=kubernetes \
--cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \
--cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \
--kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \
--leader-elect=true \
--root-ca-file=/var/lib/kubernetes/ca.pem \
--service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \
--service-cluster-ip-range=10.32.0.0/24 \
--use-service-account-credentials=true \
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
このオプションがないとcluster-cidr
が適用されず、ノードのpodCIDR
が未設定になってしまいます。
以降で使用するスクリプトでpodCIDR
が必要になるので、事前に修正しておきましょう。
Windows ノードの追加
途中までMSのドキュメントを参考に進めていきます。
Docker のインストール
手順に従って Docker をインストールし、マシンを再起動します。
Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name Docker -ProviderName DockerMsftProvider
Restart-Computer -Force
再起動後、docker pull
でコンテナイメージを取得します。
ここで取得するイメージは Pod 内のインフラ用コンテナの作成に使用します。
Dockerfile に記載されているイメージはタグが未指定なので、取得したイメージに latest タグを付加してください。
docker pull mcr.microsoft.com/windows/nanoserver:1809
docker tag mcr.microsoft.com/windows/nanoserver:1809 mcr.microsoft.com/windows/nanoserver:latest
ちなみにインストールが完了すると、Get-HNSNetwork
など HNS の操作が可能になります。
実行ファイルの用意
Kubernetes のファイルを配置するディレクトリを作成します。
mkdir c:\k
ディレクトリを作成したら以下のファイルを配置してください。
- クラスタに接続できる kubeconfig(ファイル名は config)
- kubernetes ノード用バイナリ
- kubectl.exe
- kubelet.exe
- kube-proxy.exe
バイナリは Kubernetes のリリースページにアクセスし、CHANGE LOGから取得できます。
次のような構成になっていればOKです。
ls config,*exe
Directory: C:\k
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 12/22/2019 11:49 AM 6254 config
-a---- 12/11/2019 1:14 PM 40086016 kube-proxy.exe
-a---- 12/11/2019 1:20 PM 47195136 kubectl.exe
-a---- 12/11/2019 1:20 PM 119127552 kubelet.exe
スクリプトの実行
ではいよいよ、各種設定とバイナリの起動を行う powershell スクリプトを実行していきます。
github からスクリプトをダウンロードしてください。
cd c:\k
Start-BitsTransfer https://github.com/microsoft/SDN/raw/master/Kubernetes/windows/start.ps1
ここではMSのドキュメントの手順とは異なるスクリプトをダウンロードしています。
MSのドキュメントでは CNI プラグインとして flannel を使用し、vxlan overlay ネットワークを構成しています。
flannel はクラスタに事前にインストールする必要がありますが、hard way のクラスタでは別の CNI プラグインを使用しているため、今回は flannel を使用しない方法を取りました。
ダウンロードしたスクリプトでは wincni.exe を使用し、L2 ブリッジネットワークを構成します。(overlay も選択可)
start.ps1 は以下の処理を順番に行います。
- 必要なバイナリ、スクリプト、Dockerfile をダウンロード
- コンテナイメージを作成(InstallImages.ps1)
- HNS ネットワークを初期化
- kubelet を起動(start-kubelet.ps1)
- kube-proxy を起動(start-kubeproxy.ps1)
- ルーティングテーブルに追加(AddRoutes.ps1)
スクリプト1つで必要な処理をすべて行ってくれるようです。
またスクリプトの中を見てみると、2つの param を設定できることがわかります。
Param(
[parameter(Mandatory = $true)] [string] $masterIp,
[parameter(Mandatory = $false)] $clusterCIDR="192.168.0.0/16"
)
masterIp
は VM のプライベートIP(10.240.0.4)、clusterCIDR
は上述の kube-controller-manager で設定した値(10.200.0.0/16)を使用します。
.\start.ps1 -masterIp 10.240.0.4 -clusterCIDR 10.200.0.0/16
これでノードの追加完了!…とはいきませんでした。
動かない…
案の定そのままでは動きませんでした。
powershell ウィンドウにはWaiting for the Network to be created
が延々と出力され続けていると思います。
一度実行すると必要なファイル等はダウンロードされるので、start.ps1
は捨てて個別のスクリプトを修正&実行していきます。
スクリプトの修正&実行
helper.psm1
function Get-PodCIDR()
{
- return c:\k\kubectl.exe --kubeconfig=c:\k\config get nodes/$($(hostname).ToLower()) -o custom-columns=podCidr:.spec.podCIDR --no-headers
+ return "10.200.3.0/24"
}
まずはモジュールの修正から。
これは本当に疑問なのですが、ノードの追加前なのにクラスタから自身のノード情報を取得しようとしているのはどうしてでしょうか…?(間違っていたらご指摘ください)
当然取得はできないので、自分で決めた podCIDR を返すように修正します。
モジュールをインポート済みの場合は一度削除しておいてください。
Remove-Module helper
InstallImages.ps1
問題なく実行できているので、修正・再実行は行いません。
事前に取得した Docker イメージから、pause コンテナを作成しています。
docker image ls | Out-String -Stream | Select-String pause
kubeletwin/pause latest 14d3bdf2f5cb 5 hours ago 251MB
start-kubelet.ps1
Param(
+ $clusterCIDR="10.200.0.0/16",
- $clusterCIDR="192.168.0.0/16",
$NetworkMode = "L2Bridge",
$NetworkName = "l2bridge",
[ValidateSet("process", "hyperv")]
$IsolationType = "process"
)
# Todo : Get these values using kubectl
$KubeDnsSuffix ="svc.cluster.local"
+$KubeDnsServiceIp="10.32.0.10"
-$KubeDnsServiceIp="11.0.0.10"
+$serviceCIDR="10.32.0.0/24"
-$serviceCIDR="11.0.0.0/8"
Todo と記載された変数は外から設定できないようです。なぜこんなところに?
$KubeDnsServiceIp
には kube-dns サービスのクラスタIP、$serviceCIDR
には kube-controller-manager で設定したservice-cluster-ip-range
を使用します。
ついでに param の$clusterCIDR
も修正しておきました。これでオプションなしでスクリプトを実行できるはずです。
.\start-kubelet.ps1
成功するとフォアグラウンドで kubelet.exe が実行中になるので、別の powershell ウィンドウを起動してください。
(start powershell
で別ウィンドウで起動しても良いです)
kubelet を無事に起動できると、クラスタにノードが追加されていることが確認できます。
$ kubectl get no
NAME STATUS ROLES AGE VERSION
win-server Ready <none> 173m v1.16.4
worker-0 Ready <none> 28h v1.15.0
worker-1 Ready <none> 28h v1.15.0
worker-2 Ready <none> 28h v1.15.0
start-kubelet.ps1
Param(
[parameter(Mandatory = $false)] $LogDir = "C:\k",
$NetworkName = "cbr0"
)
特に修正の必要はありません。
初回起動時にはここまで到達していないので、スクリプトを実行します。
他の箇所とは違い$NetworkName
の初期値がなぜかcbr0
になっていますが、惑わされずにl2bridge
を指定しましょう。(私は惑わされました)
.\start-Kubeproxy.ps1 -NetworkName l2bridge
成功するとフォアグラウンドで kube-proxy.exe が実行中になります。
AddRoutes.ps1
最後にこのスクリプトを実行します。
こちらも修正は不要です。
初回起動時と同様、$masterIp
には VM のプライベートIPをしていてください。
.\AddRoutes.ps1 -masterIp 10.240.0.4
これでノードの追加完了です!
動作確認
Windows コンテナのデプロイを試したり、Linux ノード上のコンテナから Windows ノード上のコンテナにアクセスしてみたり、色々と試してみてください。
$ kubectl get po,svc -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/busybox 1/1 Running 13 13h 10.200.2.4 worker-2 <none> <none>
pod/nginx 1/1 Running 0 38h 10.200.1.3 worker-1 <none> <none>
pod/ubuntu 1/1 Running 0 20h 10.200.1.4 worker-1 <none> <none>
pod/win-webserver-784d66c84f-bqh9l 1/1 Running 0 10h 10.200.3.159 win-server <none> <none>
pod/win-webserver-784d66c84f-ssxfr 1/1 Running 0 10h 10.200.3.39 win-server <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.32.0.1 <none> 443/TCP 38h <none>
service/nginx NodePort 10.32.0.52 <none> 80:32121/TCP 37h run=nginx
service/win-webserver NodePort 10.32.0.65 <none> 80:31663/TCP 10h app=win-webserver
サービスとして起動する
今回は powershell 上で kubelet.exe と kube-proxy.exe を起動しましたが、これらを Windows のサービスとして起動する方法もあります。
詳細はこちらをご参照ください。
感想
最後までたどり着いて本当に良かった…
色々と悩んで調べまわった結果、ネットワーク周りと powershell の知識が身についてきたのは良かったです。
Issue はあまり確認する時間がなかったので、きちんと調べたら別の方法があったかもしれません。何かご存知の方は教えてくださいm(_ _)m
実際に Windows ノードを使用する場合は、やはりマネージドサービスがおススメです。AKS であれば Windows ノードプールを指定するだけで簡単に始められます。
オンプレの場合は kubeadm + flannel を使用した方法が kubernetes.io で紹介されています。その他の方法についてもサイト下部に記載があるので、興味のある方は読んでみてください。
参考
Joining Windows Server Nodes to a Cluster
github: microsoft/SDN/Kubernetes/Windows
KubernetesとFlannelでWindows上にPod間VXLAN Overlayネットワークを構成
Kubernetes Networking: Behind the scenes
Troubleshooting Kubernetes Networking on Windows: Part 1