FPGA K3sクラスターを作成する Part2 (Agent Node/テスト編)

Kubernetes* 上でコンテナアプリケーションを実行するためにはまずクラスターと呼ばれるものを構築する必要があります。([参考][LINK_K8s_Component])
今回の記事ではTerasic社製のDE10-Nanoを用いてKubernetes\*のディストビューションの１つであるK3s\*を用いてFPGAクラスターを構築して最後にFPGAのアップデートを試してみます。
> 注意事項 本記事では、あくまでクラスター構築に必要な部分のみしか解説しないため、細かい用語等には触れません。

KubernetesとAzure IoT Edge\*はコンテナベースの開発という観点のみに着目すると非常に親和性が高いため、
Intel®︎ Developer Zone(IDZ)に公開されているドキュメント[(英語版)][LINK_IDZ], [(日本語(ページ内中央左にあるチュートリアル・モジュール1-4))][LINK_IDZ_JP]をよく引用しますので、合わせて読むことを推奨します。

[前編はこちら][LINK_Part1]

## Recap: クラスター構成

著者環境

+ Host OS : MacOS Catalina 10.15.7
+ Guest OS(Server Node) : Ubuntu 18.04.5
+ DE10-Nanos' OS(Agent Node) : Ubuntu 16.04.6
+ K3s version : v1.20.4+k3s1 (838a906a)

![cluster-architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698105/87123b86-e3ce-d257-e64c-49370e12d648.png)

Agent Node(Kubernetesでは一般的にWorker Nodeとも言います)には今回はTerasic社製のDE10-Nanoを利用します。
OSイメージとしては、同社が公開しているコンテナがすでに使える状態になっている、`DE10-Nano-Cloud_native.img`を使います。

*Note: 今後コマンドを実行する際は、どこから実行したかをわかりやすくするため前に(devPC=Client),(Guest=Server Node=VM),(de10nano=Agent Node)の三種類を付属します。*
> 本記事はde10nano側に焦点を当てています。前準備が必要になるので、そちらは[前編][LINK_Part1]をご覧ください。

## Agent Node準備（DE10-Nano)

本章は現在公開されているIDZドキュメント[英語版][LINK_IDZ_Module1], [日本語版][LINK_IDZ_Module1_JP]のStep2までとほぼ同様です。

microSDカードについては8GBが付属していますが、これは容量が少ないので非推奨です。可能であれば16/32GBのmicroSDカードを別途ご用意ください。

やらなければならないことは以下の通りです。  

1. コンテナが使えるOSイメージの準備/microSDカードへの焼き込み
2. シリアル通信での接続とrootFS領域の拡張
3. IPアドレスの固定 / Hostnameの変更(Optional)
4. ディスプレイを使うサービスの停止

### コンテナが使えるOSイメージの準備/microSDカードへの焼き込み 
[Terasicのサイト][LINK_Teresic_DE10Nano_Resource_Page]にアクセスし、`SD Card Image for AWS IoT Greengrass`となっているSDイメージをダウンロードしてください。
このイメージはファイル名が`DE10-Nano-Cloud-Native.zip`となっており、AWSだけではなく、Azureでも利用可能なLinuxのイメージとなっています。今回の用途においては、Kernelレベルでコンテナが動かせる状態となっているというのが重要です。

![cloud-native-download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698105/22a0404b-d1fc-235a-7486-46dc07b1a52e.png)

Downloadが完了したら、zipを解凍し.imgファイルをお好きなアプリやコマンドで書き込みを行います。私はddや、[balenaEtcher][LINK_balenaEtcher]を利用しています。

```
(devPC)$ sudo dd if=DE10-Nano-Cloud-Native.img of=/dev/<your device path> status=progress
```

### シリアル通信での接続とrootFS領域の拡張
書き込んだmicroSDカードをDE10-Nanoに挿入し、シリアル通信でつなぎます。
(pingしていきなりDE10-Nanoにつなぐという方法もありますが、手間なので非推奨です。)
シリアル通信をする際は、`screen`や、`putty`などのアプリケーションを使うとよいでしょう。
また、[FTDIのドライバ][LINK_FTDI_Driver]が入っているか確認しましょう。

```
(devPC)$ sudo screen /dev/tty.usbserial-A107T0IQ 115200 #Example
Ubuntu 16.04.6 LTS de10nano ttyS0

de10nano login: root
Password:
```

つながったら、デフォルトのログイン情報を使ってログインします。

```
Default login credentials:
- Username: root  
- Password: de10nano
```

ログイン後、ディスクの拡張を行います。`expand_rootfs.sh`というスクリプトが入っているので、それを実行、書いてあることに従い、再起動、`resize2fs_once`を実行します。実行後、`df -h`を行うとディスクいっぱいまで領域が拡張されていることが確認できます。

```
(de10nano)# ls
BT_LED_AP     Desktop   expand_rootfs.sh  hps_gpio      opencv
ControlPanel  NET_Time  gsensor           my_first_hps
(de10nano)# ./expand_rootfs.sh
```

例では、32GBのSDカードを使った場合です。

```
(de10nano)# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root        29G  2.2G   25G   8% /
devtmpfs        485M     0  485M   0% /dev
tmpfs           501M     0  501M   0% /dev/shm
tmpfs           501M   13M  489M   3% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           501M     0  501M   0% /sys/fs/cgroup
tmpfs           101M  4.0K  101M   1% /run/user/0
```

### IPアドレスの固定 / Hostnameの変更(Optional)

次は、IPアドレスの固定とHosenameの変更を行います。
この作業をしないと、ボードを再起動するたびIPアドレスが変わってしまい、Development PC(VM)と通信できなくなるのを防ぐためです。IPアドレスはEthernet InterfaceのMAC Addressが毎回変更されるため、DHCP側でほぼ確実に異なるアドレスを振ってくるので必須です。

ネットワーク設定の変更は、`nmcli`コマンドを使って行います。
DHCP接続時の設定確認方法:

```
(de10nano)# nmcli con show Wired\ connection\ 1
```

設定の確認方法例:

```
(de10nano)# nmcli con mod Wired\ connection\ 1 connection.interface-name "eth0"
(de10nano)# nmcli con mod Wired\ connection\ 1 802-3-ethernet.mac-address ""
(de10nano)# nmcli con mod Wired\ connection\ 1 ipv4.address 192.168.100.201
(de10nano)# nmcli con mod Wired\ connection\ 1 ipv4.gateway "192.168.100.1"
(de10nano)# nmcli con mod Wired\ connection\ 1 ipv4.dns "8.8.8.8,8.8.4.4"
(de10nano)# nmcli con mod Wired\ connection\ 1 ipv4.method "manual"
(de10nano)# nmcli con down Wired\ connection\ 1 && nmcli con up Wired\ connection\ 1 
```

特に大事なのは、mac-addressを消しておくことです。これをしておくことで、interfaceの名前だけで設定を紐づけてくれるようになります。

Hostnameの変更はServer Nodeと同様Node名を被らないようにするためです。
変更する際は `/etc/hosts`と`/etc/hostname`を編集します。

*Note: 現在はk3s構築時にNode名を自由に指定できるオプションが追加されているため、必ずしも行う必要はありません。*

最後に自動化用のスクリプトも作ってあるので、参考までにどうぞ。
少なくとも、ベースとなる(192.168.100.)とgatewayは適宜変更しないと動きませんので、この点はご留意ください。

```bash:fixed-node.sh
#!/bin/bash
#for initialization
UUID=$(nmcli -t -f UUID c show --active)
NAME="EthConnection-for-k3s"
#FIXME
ADDR=192.168.100.2XX/24
OLDNODE="de10nano"
NODENAME=k3s-de10-workerXX

if [ $# -ne 1 ]; then
    echo "Usage : ./fix-node.sh nodenumber(01/../09/10/.../55)"
    exit 1
fi
flag=false
for index in 0{1..9} {10..55};
do
    if [ $1  = $index ]; then 
        NODENAME=${NODENAME/XX/$index}
        ADDR=${ADDR/XX/$index}
        echo "NODENAME is $NODENAME"
        echo "ADDR is $ADDR"
        flag=true
        break; 
    fi
done

if ! "${flag}"; then 
    echo "Error Format : Plz use 01/../09/10/.../55"
    exit 1
fi
#Edit Hostname
sudo sh -c "echo $NODENAME > /etc/hostname"
sudo sed -i -e"s/$OLDNODE/$NODENAME/g" /etc/hosts

#Edit Network Connection
sudo nmcli con mod $UUID connection.id $NAME
sudo nmcli con mod $NAME connection.interface-name "eth0"
sudo nmcli con mod $NAME 802-3-ethernet.mac-address ""
sudo nmcli con mod $NAME ipv4.address $ADDR
sudo nmcli con mod $NAME ipv4.gateway "192.168.100.1"
sudo nmcli con mod $NAME ipv4.dns "8.8.8.8,8.8.4.4"
sudo nmcli con mod $NAME ipv4.method "manual"
#Please reboot
```

今回は例として下記のように実行しました。ここだけはわかりやすくするためにターミナルと同じ表示にしています。
Reboot後はHostnameとipアドレスが指定したものに変わっていることがわかります。

```
root@de10nano:~# ./fixed-node.sh 10
NODENAME is k3s-de10-worker10
ADDR is 192.168.100.210/24
sudo: unable to resolve host de10nano
sudo: unable to resolve host de10nano
sudo: unable to resolve host de10nano
sudo: unable to resolve host de10nano
sudo: unable to resolve host de10nano
sudo: unable to resolve host de10nano
sudo: unable to resolve host de10nano
root@de10nano:~# reboot
root@k3s-de10-worker10:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
        valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether c6:3d:a3:4f:5d:5a brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.210/24 brd 192.168.100.255 scope global eth0
        valid_lft forever preferred_lft forever
    inet6 fe80::c63f:f196:b0eb:8344/64 scope link
        valid_lft forever preferred_lft forever
```

> Note: "unable to resolve host de10nano"というメッセージは再起動すると出なくなります。

### ディスプレイを使うサービスの停止

FPGA をリコンフィグレーションするときにHPSからFPGAへのアクセスがあるとシステムがハングアップする可能性があるので、それを止めておきます。
具体的には、ディスプレイ周りのサービスを止めるよう`/overlay/fpgaoverlay.sh`に下記を追加します。
実際に手動で止めてみるとわかりますが、lightdmはGUIの表示、gettyはCLIモードの時にターミナルを表示するサービスです（カーソルがチカチカします）。

```
(de10nano) vim /overlay/fpgaoverlay.sh
# 下記を追記
systemctl stop lightdm
systemctl stop getty@tty*
echo 0 > /sys/class/graphics/fbcon/cursor_blink
```
    
## K3sインストール for DE10-Nano
> ここからは[前編][LINK_part1]の作業を完了していることを前提とします。

前編ではk3s serverをGuest VMにインストールしましたが、今回はk3s agentをDE10-Nanoにインストールします。
基本的には[公式ガイド][LINK_K3S_INSTALL]に従いますが、`K3S_URL`と`K3S_TOKEN`をインストールオプションに追加して実行しましょう。
> [k3sup][LINK_TO_K3sup]というユーティリティーツール現在ではリリースされています。こちらを用いるのもよいでしょう。

`K3S_URL`は[前編][LINK_part1]で設定したServerのIPアドレス+ポート番号(Defaultでは6443)を指定します。
`K3S_TOKEN`はServer Node内の/var/lib/rancher/k3s/server/tokenの中に格納されています。

```
(Guest) # cat /var/lib/rancher/k3s/server/token
K107f205d9a138abd18b5f8f4f2ac170398600128fd6c37ab8fd3869a200a66cc02::server:fd287d907c641f05970b1b67a2d4eaf0
```

また、前章でDE10-Nanoの Node名を変更していない場合は、`K3S_NODE_NAME`オプションもつけるとよいでしょう。

```bash:install-k3s.sh
#This is a sample
MASTERURL="https://192.168.100.100:6443"
MASTERTOKEN="K107f205d9a138abd18b5f8f4f2ac170398600128fd6c37ab8fd3869a200a66cc02::server:fd287d907c641f05970b1b67a2d4eaf0"
curl -sfL https://get.k3s.io | K3S_URL=$MASTERURL K3S_TOKEN=$MASTERTOKEN sh -
```

これを実行することで自動的にインストール、k3s-agentが起動します。
しばらくすると、master側にも Nodeが登録されます。

```
(de10nano) # sudo systemctl status k3s-agent.service
● k3s-agent.service - Lightweight Kubernetes
    Loaded: loaded (/etc/systemd/system/k3s-agent.service; enabled; vendor preset: enabled)
    Active: active (running) since Wed 2021-03-31 06:45:11 UTC; 1min 23s ago
        Docs: https://k3s.io
    Process: 2100 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
    Process: 2090 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Main PID: 2107 (k3s-agent)
        Tasks: 41
    Memory: 223.1M
        CPU: 50.162s
    CGroup: /system.slice/k3s-agent.service
            ├─2107 /usr/local/bin/k3s agent
            ├─2146 containerd
            └─2517 /var/lib/rancher/k3s/data/912de41a65c99bc4d50bbb78e6106f3acbf3a70b8dead77b4c4ebc6755b4f9d6/bin/containerd-shim-runc-v
(devPC) $ kubectl get nodes
NAME                STATUS     ROLES                  AGE     VERSION
k3s-de10-worker10   Ready      <none>                 3m12s   v1.20.4+k3s1
k3sserver           Ready      control-plane,master   10m     v1.20.4+k3s1
```

最後にworkerであることを明示するためにラベルをつけておきましょう。

```
(devPC) $ kubectl label node k3s-de10-worker10 node-role.kubernetes.io/node=
node/k3s-de10-worker10 labeled
(devPC) $ kubectl label node k3s-de10-worker10 type=worker
node/k3s-de10-worker10 labeled
```

## FPGAのアップデートを試す

### ビルドイメージの作成
今回はK3sを使ってFPGAをアップデート(リコンフィグレーション)するというところにフォーカスするため、
FPGAイメージはすでにアップロードされているものを利用します。(FPGAの開発手順については[別記事(英語のみ)][LINK_Development]を参照ください。)
具体的には、IDZ内のデモ[(英語版)][LINK_Demo_IDZ], [(日本語版)][LINK_Demo_IDZ_JP]で利用されているものを利用します。
必要なファイルは以下の通りです。

```bash
LHoWa_TPat.rbf # FPGA bitstream GitHubより引用
config_sysid.dtbo # FPGA Reconfigurationを実行するDevice Tree Overlay File GitHubより引用
overlay.sh # DeviceTreeOverlayを実行するスクリプト、上記リンク内のドキュメントに存在するものを引用
Dockerfile #上記３つのファイルをコピーして保存するDockerfile 上記リンク内に存在するものを引用
```

[GitHubレポジトリ][LINK_Demo_Repo]

<details><summary>overlay.sh, Dockerfile</summary><div>

[ドキュメント][LINK_Demo_IDZ], [日本語版][LINK_Demo_IDZ_JP]より引用

```bash
#!/bin/bash

overlay_dir="/sys/kernel/config/device-tree/overlays/socfpga"
overlay_dtbo="config_sysid.dtbo"
overlay_rbf="LHoWa_TPat.rbf"

if [ -d $overlay_dir ];then
  echo "Deleting $overlay_dir"
  rmdir $overlay_dir
fi
echo "Copy DTBO and RBF"
cp $overlay_dtbo /lib/firmware/
cp $overlay_rbf /lib/firmware/

echo "creating $overlay_dir"
mkdir $overlay_dir

echo "Doing Device Tree Overlay"
echo config_sysid.dtbo > $overlay_dir/path

echo "Successfully Device Tree Overlay Done."
```

```Dockerfile
FROM arm32v7/ubuntu:xenial AS base

WORKDIR /app

COPY overlay.sh /app

COPY LHoWa_TPat.rbf /app
COPY config_sysid.dtbo /app

CMD ["./overlay.sh"]
```
</div></details>

チュートリアルでも触れているように今回はあくまでも、FPGAのReconfigurationを行うのみのイメージとなります。
実用ではソフトウェアなどもイメージにまとめることでホストアプリケーションの実行も可能です。  
この手法では、Device Tree OverlayというLinuxの機能を活用しFPGAのリコンフィグレーションを行います。
具体的な手法については、[ドキュメント][LINK_Demo_IDZ], [日本語版][LINK_Demo_IDZ_JP]内に解説がありますので、そちらをご参照ください。

すべてのファイルを同一のディレクトリへとコピーが完了したら、Buildxが使える状態でコンテナをビルドします。

```bash
(devPC) $ docker build --rm -f Dockerfile -t <AWS account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:<tag> .
e.g. docker build --rm -f Dockerfile -t 309190452075.dkr.ecr.us-east-2.amazonaws.com/kubernetes-demo-image:testv0 .
```


問題なくビルドができれば、ECRにプッシュします。ログアウトしてしまっている場合は再度ログインしなおしてください。

```bash
#aws ecr get-login-password | docker login --username AWS --password-stdin <AWS account ID>.dkr.ecr.<region>.amazonaws.com
(devPC) $ docker push <AWS account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:<tag> .
e.g. docker push 309190452075.dkr.ecr.us-east-2.amazonaws.com/kubernetes-demo-image:testv0 .
```

### Kubernetesに載せてみる

スケジューリングからServer Nodeを除外します。
この設定はすでにデフォルトで組み込まれているはず([Issue][LINK_MASTER_SCHEDULING])ですが、確認するか、念のため実行しておくとよいでしょう。

```
(devPC) $ kubectl taint nodes k3sserver  node-role.kubernetes.io/master=effect:NoSchedule
```

続いて、下記の内容をファイルにまとめて、`kubectl apply -f overlay.yaml`で実行してみます。
FPGAをReconfigurationするためには、`/lib/firmware`と`/sys/kernel/config`をコンテナにマウントする必要があります。
また、ホスト側のリソースにアクセスするため、Privileged権限も必用です。

FPGAのアップデートすることを主眼においているので、ワークロードリソースに関してはJobを指定しています。

```yaml:overlay.yaml
kind: Job
apiVersion: batch/v1
metadata:
  name: overlay-test
spec:
  completions: 1
  parallelism: 1
  backoffLimit: 10
  template:
    metadata:
      labels:
        app: overlay
        env: de10nano
    spec:
      restartPolicy: Never
      containers:
        - name: fpga-overlay
          image: <AWS account ID>.dkr.ecr.<region>.amazonaws.com/<repository name>:<tag>
          #e.g. image: 309190452075.dkr.ecr.us-east-2.amazonaws.com/kubernetes-demo-image:testv0
          imagePullPolicy: IfNotPresent
          securityContext:
            privileged: true
          volumeMounts:
            - name: fpga-image
              mountPath: /lib/firmware
            - name: overlay-dir
              mountPath: /sys/kernel/config
      volumes:
        - name: fpga-image
          hostPath:
            path : /lib/firmware
            type : Directory
        - name: overlay-dir
          hostPath:
            path : /sys/kernel/config
            type : Directory
      imagePullSecrets:
        - name: aws
```

JobがCompleteされ、HDMIをつないでカラーバーが出るか、LEDがぼやぁと変化するようになったら成功です。

```
(devPC) $ kubectl get all
NAME                     READY   STATUS      RESTARTS   AGE
pod/overlay-test-csznn   0/1     Completed   0          14s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.43.0.1    <none>        443/TCP   398d

NAME                     COMPLETIONS   DURATION   AGE
job.batch/overlay-test   1/1           3s         14s
(devPC) $ kubectl logs overlay-test-csznn
Deleting /sys/kernel/config/device-tree/overlays/socfpga
Copy DTBO and RBF
creating /sys/kernel/config/device-tree/overlays/socfpga
Doing Device Tree Overlay
Successfully Device Tree Overlay Done.
```

*Note: Podの最後はハッシュなので、実行するたびに変わります。*

今回はFPGA SoCをKubernetesに載せてコンテナからリコンフィグレーションをしてみるというものを行ってみました。細かな技術的話は端折ってしまいましたが、まずはこれを機に始めてもらえたら嬉しいです。

[前編][LINK_part1]へ戻る

## References

+ [Kubernetesのコンポーネント][LINK_K8s_Component]
+ [Build, Deploy, and Manage your FPGA-Based IoT Edge Applications using Microsoft* Azure][LINK_IDZ]
+ [インテルのエッジセントリック FPGA ソリューション][LINK_IDZ_JP]
+ [Install the Azure* IoT Edge Runtime][LINK_IDZ_Module1]
+ [FPGA クラウド・コネクティビティー・キット・チュートリアル・モジュール 1][LINK_IDZ_Module1_JP]
+ [Terasic DE10-Nano Resource][LINK_Teresic_DE10Nano_Resource_Page]
+ [balenaEtcher][LINK_balenaEtcher]
+ [D2XX Drivers - FTDI][LINK_FTDI_Driver]
+ [alexellis/k3sup: bootstrap Kubernetes with k3s over SSH][LINK_TO_K3sup]
+ [Rancher Docs: Installation][LINK_K3S_INSTALL]
+ [Develop and Deploy Container Using DE10-Nano RFS Daughter card sensor][LINK_Development]
+ [Reconfigure an FPGA from the Azure* Cloud using a Container Application][LINK_Demo_IDZ]
+ [FPGA クラウド・コネクティビティー・キット・チュートリアル・モジュール 4][LINK_Demo_IDZ_JP]
+ [terasic-de10-nano-kit repository][LINK_Demo_Repo]
+ [feat: add NoSchedule toleration on key node-role.kubernetes.io/master][LINK_MASTER_SCHEDULING]

[LINK_part1]:https://qiita.com/hikawai/items/718e1cee50e83d33e448
[LINK_K8s_Component]:https://kubernetes.io/ja/docs/concepts/overview/components/
[LINK_IDZ]:https://software.intel.com/content/www/us/en/develop/articles/build-and-deploy-iot-edge-applications-using-azure.html
[LINK_IDZ_JP]:https://www.intel.co.jp/content/www/jp/ja/products/programmable/edge-centric-fpga-solutions.html
[LINK_IDZ_Module1]: https://software.intel.com/content/www/us/en/develop/articles/install-the-azure-iot-edge-runtime.html
[LINK_IDZ_Module1_JP]: https://www.intel.co.jp/content/www/jp/ja/products/docs/programmable/cloud-connectivity-kit-tutorial-module-1.html
[LINK_Teresic_DE10Nano_Resource_Page]: https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=205&No=1046&PartNo=4
[LINK_balenaEtcher]: https://www.balena.io/etcher/
[LINK_FTDI_Driver]: https://ftdichip.com/drivers/d2xx-drivers
[LINK_K3S_INSTALL]: https://rancher.com/docs/k3s/latest/en/installation/
[LINK_TO_K3sup]: https://github.com/alexellis/k3sup
[LINK_Development]: https://github.com/intel-iot-devkit/terasic-de10-nano-kit/blob/master/azure-de10nano-document/sensor-aggregation-reference-design-for-azure/top.md
[LINK_Demo_IDZ]:https://software.intel.com/content/www/us/en/develop/articles/reconfigure-an-fpga-from-the-cloud-with-containers.html
[LINK_Demo_IDZ_JP]:https://www.intel.co.jp/content/www/jp/ja/products/docs/programmable/cloud-connectivity-kit-tutorial-module-4.html
[LINK_Demo_Repo]:https://github.com/intel-iot-devkit/terasic-de10-nano-kit/tree/master/azure-de10nano-document/module04-device-tree-overlay-guide
[LINK_MASTER_SCHEDULING]: https://github.com/k3s-io/k3s/pull/1275

## Notices & Disclaimers
No product or component can be absolutely secure. 
Your costs and results may vary. 

Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-548-4725 or visiting <http://www.intel.com/design/literature.htm>. ​

Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.​​​​

​No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document.​​
© Intel Corporation.  Intel, the Intel logo, and other Intel marks are trademarks of Intel Corporation or its subsidiaries.  Other names and brands may be claimed as the property of others​.​​