OpenShiftクラスターへのBYOHのWindowsノード追加

# はじめに
OpenShift 4.8では、BYOH(Bring Your Own Host)のWindowsノードがサポートされました。

> We are proud to announce the general availability of Bring Your Own Host Support for Windows nodes

https://cloud.redhat.com/blog/announcing-bring-your-own-host-support-for-windows-nodes-to-red-hat-openshift

下記(1)に加えて(2)が追加され、利用が容易になったと理解しています。

(1) AWS・Azure・vSphereのIPI環境で利用可能。カスタムリソースMachineとして管理。
(2) vSphere・ベアメタルのUPI環境で利用可能。非Machineとして管理。

サポート条件は満たさないながら、IBM CloudにUPIインストールしたOpenShiftクラスターにBYOHのWindowsノードを追加することができたので記事として残します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/37e86f9a-fdca-4103-0ce0-767c849f4170.png)

下記のRed Hatブログを参考にしています。

https://cloud.redhat.com/blog/windows-containers-bring-your-own-host

# 1. 事前準備
## 1.1. ハイブリッドネットワークでOpenShiftをUPIインストール
サポート対象のネットワーク設定は、OVN-Kubernetesを使用したハイブリッドネットワークのみです。

https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.8/html/windows_container_support_for_openshift/understanding-windows-container-workloads#_supported-networking-2

[過去記事](https://qiita.com/y-akio/items/ad0093daa504be746573#32-ignition%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90)では、ハイブリッドネットワークを有効にしていなかったのでOpenShiftを再インストールします。ignitionファイルの作成方法が異なります(下記の追加部分)。

```bash:classicノード
mkdir bare-metal
cp -p install-config.yaml bare-metal/
./openshift-install create manifests --dir=bare-metal
cp -p 99_*.yaml bare-metal/openshift/

### 追加部分 開始
cp -p cluster-network-03-config.yml bare-metal/manifests/
### 追加部分 終了

./openshift-install create ignition-configs --dir=bare-metal
cp -p bare-metal/*.ign /var/www/html/
chmod 644 /var/www/html/*.ign
ls -l /var/www/html/*.ign
### 標準出力↓
-rw-r--r--. 1 root root 272100  9月 26 20:39 /var/www/html/bootstrap.ign
-rw-r--r--. 1 root root   1715  9月 26 20:39 /var/www/html/master.ign
-rw-r--r--. 1 root root   1715  9月 26 20:39 /var/www/html/worker.ign
```

```yaml:cluster-network-03-config.yml
apiVersion: operator.openshift.io/v1
kind: Network
metadata:
  name: cluster
spec:
  defaultNetwork:
    ovnKubernetesConfig:
      hybridOverlayConfig:
        hybridClusterNetwork:
        - cidr: 10.132.0.0/14
          hostPrefix: 23
```

インストール後、OVN-Kubernetesを使用したハイブリッドネットワークであることを確認します。

```bash:classicノード
oc get network.operator cluster -o jsonpath='{.spec.defaultNetwork.type}{"\n"}'
### 標準出力↓
OVNKubernetes

oc get network.operator cluster -o jsonpath='{.spec.defaultNetwork.ovnKubernetesConfig}{"\n"}' | jq -r
### 標準出力↓
{
  "genevePort": 6081,
  "hybridOverlayConfig": {
    "hybridClusterNetwork": [
      {
        "cidr": "10.132.0.0/14",
        "hostPrefix": 23
      }
    ]
  },
  …
}
```

## 1.2. 仮想サーバー・インスタンス作成
DNS Servicesに以下のエントリを追加し、仮想サーバー・インスタンス「worker-3」を作成します。「worker-3」はworker-0と同じサブネットで、OSはWindows Server 2019 Server Coreです。Windowsノードは、workerノードと同じサブネットにする必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/2fcf53f8-ab53-eece-a97a-4d751b704e22.png)

```bash:classicノード
./instance-create.sh worker-3 vpc-ocp jp-tok-1 bx2-4x16 subnet-tok1 10.244.0.112
```

```bash:instance-create.sh
#!/bin/sh

ins=${1}
vpc=${2}
zone=${3}
prof=${4}
sub=${5}
ip=${6}

vpcid=`ibmcloud is vpcs | grep ${vpc} | awk '{print $1}'`
subid=`ibmcloud is subnets | grep ${vpc} | grep ${sub} | awk '{print $1}'`
imageid=r022-c30b6a15-3e4a-45c0-8d2c-747af2f0d19a
secgid=`ibmcloud is security-groups | grep ${vpc} | awk '{print $1}'`
keyid=`ibmcloud is keys | grep "sshkey" | awk '{print $1}'`

tmp={\"name\":\"eth0\",\"subnet\":{\"id\":\"${subid}\"},\"primary_ipv4_address\":\"${ip}\",\"security_groups\":[{\"id\":\"${secgid}\"}]}

ibmcloud is instance-create ${ins} ${vpcid} ${zone} ${prof} ${subid} \
  --image-id ${imageid} \
  --primary-network-interface ${tmp} \
  --key-ids ${keyid}

exit 0
```

## 1.3. Windowsノードの設定
WindowsノードにSSHサーバーを導入し、classicノードで作成した公開鍵で認証が可能なように設定します。後続の手順でWindows Machine Configオペレーターに秘密鍵を登録します。SSHサーバーの構成には、[Red Hatブログ](https://cloud.redhat.com/blog/windows-containers-bring-your-own-host)掲載のスクリプトを利用しています。

```bash:classicノード(10.192.36.132)
ssh-keygen -t rsa -f /root/.ssh/windows_node -q -N ''
cp -p /root/.ssh/windows_node.pub /var/www/html/
```

```powershell:Windowsノード
wget https://red.ht/3gOAA6y -OutFile install-openssh.ps1
wget http://10.192.36.132:8080/windows_node.pub -OutFile windows_node.pub
.\install-openssh.ps1 .\windows_node.pub
```

次にDockerを導入します。

```powershell:Windowsノード
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Set-PSRepository PSGallery -InstallationPolicy Trusted
Install-Module -Name DockerMsftProvider -Repository PSGallery
Install-Package -Name docker -ProviderName DockerMsftProvider
Restart-Computer -Force
```

※1 OpenShiftはclassicノードをプロキシとしてインターネットに接続しています。しかし、WindowsノードへのSSH導入などをプロキシ経由とする方法が分からなかったので、パブリック・ゲートウェイをWindowsノードのサブネットに接続しました。
※2 Windowsノードのビルドバージョンは「17763.1879」でした。

# 2. OpenShiftへのBYOHのWindowsノード追加
## 2.1. Windows Machine Configオペレーター導入
OpenShiftコンソールから、Windows Machine Configオペレーターを導入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/db666147-4c16-f49e-5504-ee8133837ea8.png)
![pic.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/e3a5c711-264b-3db1-4874-a10368c26d04.png)

OVN-Kubernetesでハイブリッドネットワークを有効にしていれば、ネームスペース「openshift-windows-machine-config-operator」のオペレーターPodがRunning状態になります。

```bash:classicノード
oc get pod -n openshift-windows-machine-config-operator
### 標準出力↓
NAME                                               READY   STATUS    RESTARTS   AGE
windows-machine-config-operator-68c98d44fc-22xr5   1/1     Running   5          3d22h
```

また、ネームスペース「openshift-windows-machine-config-operator」にWindowsノード接続用のsecretを作成します。

```bash:classicノード
oc create secret generic cloud-private-key \
  --from-file=private-key.pem=/root/.ssh/windows_node \
  -n openshift-windows-machine-config-operator
```

## 2.2. Windowsノード追加
ConfigMapを適用すると、Windows Machine ConfigオペレーターPodがWindowsノードを追加します。

```bash:classicノード
oc get nodes
### 標準出力
master-0   Ready    master   4d    v1.21.1+051ac4f
master-1   Ready    master   4d    v1.21.1+051ac4f
master-2   Ready    master   4d    v1.21.1+051ac4f
worker-0   Ready    worker   4d    v1.21.1+051ac4f
worker-1   Ready    worker   4d    v1.21.1+051ac4f
worker-2   Ready    worker   4d    v1.21.1+051ac4f

oc apply -f windows-node.yaml

oc get nodes
### 標準出力
NAME       STATUS   ROLES    AGE     VERSION
master-0   Ready    master   4d2h    v1.21.1+051ac4f
master-1   Ready    master   4d2h    v1.21.1+051ac4f
master-2   Ready    master   4d2h    v1.21.1+051ac4f
worker-0   Ready    worker   4d1h    v1.21.1+051ac4f
worker-1   Ready    worker   4d1h    v1.21.1+051ac4f
worker-2   Ready    worker   4d1h    v1.21.1+051ac4f
worker-3   Ready    worker   3m39s   v1.21.1-1398+98073871f173ba
```

```yaml:windows-node.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: windows-instances
  namespace: openshift-windows-machine-config-operator
data:
  10.244.0.112: |-
    username=Administrator
```

```bash:classicノード
oc logs windows-machine-config-operator-68c98d44fc-22xr5 -n openshift-windows-machine-config-operator -f
### 標準出力↓
{"level":"info","ts":1633082380.6159787,"logger":"controllers.configmap","msg":"processing","instances in":"windows-instances"}
{"level":"info","ts":1633082410.7295609,"logger":"wc 10.244.0.112","msg":"configuring"}
{"level":"info","ts":1633082410.896953,"logger":"wc 10.244.0.112","msg":"transferring files"}
{"level":"info","ts":1633082444.3505418,"logger":"wc 10.244.0.112","msg":"configured","service":"windows_exporter","args":"--collectors.enabled cpu,cs,logical_disk,net,os,service,system,textfile,container,memory,cpu_info\""}
{"level":"info","ts":1633082447.0613866,"logger":"wc 10.244.0.112","msg":"configured kubelet","cmd":"C:\\k\\\\wmcb.exe initialize-kubelet --ignition-file C:\\Windows\\Temp\\worker.ign --kubelet-path C:\\k\\kubelet.exe --node-ip=10.244.0.112","output":"Bootstrapping completed successfully"}
{"level":"info","ts":1633082477.1159537,"logger":"wc 10.244.0.112","msg":"configure","service":"hybrid-overlay-node","args":"--node worker-3 --k8s-kubeconfig c:\\k\\kubeconfig --windows-service --logfile C:\\var\\log\\hybrid-overlay\\hybrid-overlay.log\" depend= kubelet"}
{"level":"info","ts":1633082598.3541875,"logger":"wc 10.244.0.112","msg":"configured","service":"hybrid-overlay-node","args":"--node worker-3 --k8s-kubeconfig c:\\k\\kubeconfig --windows-service --logfile C:\\var\\log\\hybrid-overlay\\hybrid-overlay.log\" depend= kubelet"}
{"level":"info","ts":1633082644.9759097,"logger":"wc 10.244.0.112","msg":"configured kubelet for CNI","cmd":"C:\\k\\wmcb.exe configure-cni --cni-dir=\"C:\\k\\cni\\ --cni-config=\"C:\\k\\cni\\config\\cni.conf","output":"CNI configuration completed successfully"}
{"level":"info","ts":1633082649.1335378,"logger":"wc 10.244.0.112","msg":"configured","service":"kube-proxy","args":"--windows-service --v=4 --proxy-mode=kernelspace --feature-gates=WinOverlay=true --hostname-override=worker-3 --kubeconfig=c:\\k\\kubeconfig --cluster-cidr=10.132.0.0/24 --log-dir=C:\\var\\log\\kube-proxy\\ --logtostderr=false --network-name=OVNKubernetesHybridOverlayNetwork --source-vip=10.132.0.196 --enable-dsr=false --feature-gates=IPv6DualStack=false\" depend= hybrid-overlay-node"}
{"level":"info","ts":1633082664.1910317,"logger":"nc 10.244.0.112","msg":"instance has been configured as a worker node","version":"3.1.0+06e96071"}
{"level":"info","ts":1633082664.2283046,"logger":"metrics","msg":"Prometheus configured","endpoints":"windows-exporter","port":9182,"name":"metrics"}
{"level":"info","ts":1633082664.2285357,"logger":"controllers.configmap","msg":"processing","instances in":"windows-instances"}
{"level":"info","ts":1633082664.2289386,"logger":"controllers.configmap","msg":"instance is up to date","node":"worker-3","version":"3.1.0+06e96071"}
{"level":"info","ts":1633082664.2518897,"logger":"metrics","msg":"Prometheus configured","endpoints":"windows-exporter","port":9182,"name":"metrics"}
```

# 3. OpenShiftへのWindowsコンテナのデプロイ
## 3.1. コンテナイメージのビルド
当初、BuildConfigを作成してWindowsノードでビルドすることを考えたのですが、ビルドPodで下記のログが出力されてエラーになってしまいました。

```bash:classicノード
Warning  FailedMount       <invalid>                      kubelet            MountVolume.SetUp failed for volume "build-ca-bundles" : mkdir c:\var\l
ib\kubelet\pods\0336dac4-a2ff-4ff1-9ad4-43c30523a470\volumes\kubernetes.io~configmap\build-ca-bundles\..2021_10_02_01_43_01.821169354\certs.d\image-re
gistry.openshift-image-registry.svc:5000\: The directory name is invalid.
```

※WindowsノードにビルドPodをスケジュールするためには、BuildConfigにnodeSelectorを追加したり、「build.config.openshift.io/cluster」にtolerationsを追加したりする必要があります。

また、Windows 10 Home Editionに導入したDocker DesktopでWindowsコンテナをビルドすることもできませんでした。下の画像はDocker Desktopのタスクトレイを右クリックしたときものですが、Windowsコンテナを扱うためにはProかEnterpriseエディションが必要な旨が表示されています。
![タスクトレイ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/c4439230-27b8-7421-ba02-cf3c816cfe37.png)

止む無く今回はWindowsノードのDockerでビルドしDocker Hubにプッシュしました。

```bash:windowsノード
docker build -t iis:latest .
docker tag iis <アカウント名>/<リポジトリ名>:iis
docker login
docker push <アカウント名>/<リポジトリ名>:iis
```

Dockerfile等は下記URLのサンプルを使用しています。

https://github.com/MicrosoftDocs/Virtualization-Documentation/tree/master/windows-container-samples/iis

## 3.2. コンテナのデプロイ

Docker Hubにプッシュしたイメージからコンテナをデプロイします。

```bash:classicノード
oc new-project iis
oc apply -f iis.yaml
oc get pod -o wide
### 標準出力↓
NAME                   READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
iis-855bf8d65d-rc54k   1/1     Running   0          7s    10.132.0.12   worker-3   <none>           <none>
```

```yaml:iis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: iis
  labels:
    app: iis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: iis
  template:
    metadata:
      labels:
        app: iis
    spec:
      containers:
        - name: iis
          image: <アカウント名>/<リポジトリ名>:iis
          ports:
            - containerPort: 80
      tolerations:
      - key: os
        value: Windows
        Effect: NoSchedule
      nodeSelector:
        kubernetes.io/hostname: worker-3
---
apiVersion: v1
kind: Service
metadata:
  name: iis
spec:
  selector:
    app: iis
  type: ClusterIP
  ports:
   - protocol: TCP
     port: 80
     targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: iis
spec:
  rules:
    - host: iis.apps.ocp.cloud.vpc
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: iis
                port:
                  number: 80
```

Windowsノード(worker-3)にスケジュールするために、nodeSelectorとtolerationsを設定しています。masterノードのようにTaintsが設定されているためです。

```bash:classicノード
oc describe node worker-3 | grep "Taints"
### 標準出力↓
Taints:             os=Windows:NoSchedule

oc describe node worker-0 | grep "Taints"
### 標準出力↓
Taints:             <none>

oc describe node master-0 | grep "Taints"
### 標準出力↓
Taints:             node-role.kubernetes.io/master:NoSchedule
```

ブラウザで`http://iis.apps.ocp.cloud.vpc/`を開くと以下のような画面が表示されます。RHCOSのノード(worker-0～2いずれか)のルーターPodからWindowsノード(worker-3)のIIS Podへの連携ができていることになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/7847ee47-b867-2882-6717-2e66a04e8294.png)

OpenShift側から確認すると、Windowsノード(worker-3)で動作しているPodはIISのみです。

```bash:classicノード
oc get pod -o wide | grep "worker-3"
### 標準出力↓
iis-855bf8d65d-rc54k   1/1     Running   0          4m21s   10.132.0.12   worker-3   <none>           <none>
```

Windowsノードで起動されているコンテナは2つでした。Kubernetes関連タスクは3つ確認できました。

```bash:Windowsノード(worker-3)
docker ps
### 標準出力↓
CONTAINER ID   IMAGE                                          COMMAND               CREATED         STATUS         PORTS     NAMES
4ce7c95d5563   0be281f1cc6f                                   "ping localhost -t"   2 minutes ago   Up 2 minutes             k8s_iis_iis-855bf8d65d-rc54k_iis_907b0fb8-bc39-4cb2-94fe-62b6012acbfa_0
d116c232b897   mcr.microsoft.com/oss/kubernetes/pause:3.4.1   "/pause.exe"          2 minutes ago   Up 2 minutes             k8s_POD_iis-855bf8d65d-rc54k_iis_907b0fb8-bc39-4cb2-94fe-62b6012acbfa_0

tasklist
### 標準出力↓
…
hybrid-overlay-node.exe       5516 Services                   0     36,328 K
…
kubelet.exe                   1360 Services                   0     82,908 K
kube-proxy.exe                5904 Services                   0     43,232 K
…
```

# おわりに
2021年10月10日現在、IBM CloudではOpenShiftのUPI/IPIインストールはサポートされていません。IBM Cloudの学習を始めて最も残念なことの1つだったのですが、「What's new in Red Hat OpenShift 4.9」のロードマップでは2022年上半期に対応予定とされていました(動画7分25秒頃)。Windowsノードを含めサポートされる機能が広がることを期待しています。

>PLATFORM
>Alibaba, Nutanix & IBM Cloud(UPI/IPI)

https://www.youtube.com/watch?v=Q1j_mt_XLqE&t=469s