OKEで作成したクラスターでistioのsidecarが利用できない問題の対応方法

# はじめに
Oracle Cloud Infrastructure Container Engine for Kubernetes（以下OKE）でarm64アーキテクチャのワーカーノードを利用して作成したクラスタでistioを利用しようとしたところ、sidecarのinitコンテナがエラーになってしまいpodが起動できませんでした。原因と実施した対応を残したいと思います。

# 利用環境
- OKEを利用して作成したマネージドのクラスター
- インスタンスのシェイプはVM.Standard.A1.Flex
※（数バージョン前までistioはarm64をサポートしていませんでしたので）arm64なのが悪いのかと思いamd64なシェイプを選択してノードに追加してみましたが同様のエラーとなりました。
- ワーカーのイメージはOracle-Linux-8.7-aarch64-2023.01.31-3-OKE-1.25.4-549を利用
※	OKEワーカーノードイメージでなく、プラットフォームイメージ（Oracle-Linux-8.7-aarch64-2023.02.28-1）を利用した場合でも同様のエラーとなりました。
- kubernetesのバージョンはv1.25.4
- istioctl（1.17.2）を利用してistioをインストール

# 発生した状態
namespaceに対してistio-injection=enabledのラベルを付与してsidecarのinjectionを有効にしたところ、podがInit:CrashLoopBackOffの状態になっていました。

```
kubectl logs my-podname -c istio-init
```
を実行しログを確認したところ以下のようになっていました。
```
（省略）
2023-04-15T02:33:07.240933Z     info    Running command: iptables-restore --noflush /tmp/iptables-rules-1681525987240733799.txt650228252
2023-04-15T02:33:07.245568Z     error   Command error output: xtables parameter problem: iptables-restore: unable to initialize table 'nat'

Error occurred at line: 1
Try `iptables-restore -h' or 'iptables-restore --help' for more information.
2023-04-15T02:33:07.245590Z     error   Failed to execute: iptables-restore --noflush /tmp/iptables-rules-1681525987240733799.txt650228252, exit status 2
```
なにやらenvoy-proxy用のiptablesの設定実行が失敗しているようです。

# 原因
OKEが自動構築したワーカーノードで必要なカーネルモジュールが読み込まれていないためでした。

lsmodした結果は以下の状態でした。
```
Module                  Size  Used by
xt_nat                 16384  2
xt_addrtype            16384  10
ipt_REJECT             16384  54
nf_reject_ipv4         20480  1 ipt_REJECT
ip6table_filter        16384  1
ip6table_mangle        16384  1
ip6table_nat           16384  1
ip6_tables             40960  3 ip6table_filter,ip6table_nat,ip6table_mangle
ip_set                 65536  0
ip_vs_sh               20480  0
ip_vs_wrr              20480  0
ip_vs_rr               20480  0
ip_vs                 229376  6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
xt_MASQUERADE          20480  3
xt_mark                16384  15
xt_conntrack           16384  19
xt_comment             16384  102
nft_compat             20480  235
nft_counter            16384  97
nft_chain_nat          16384  8
nf_nat                 61440  4 ip6table_nat,xt_nat,nft_chain_nat,xt_MASQUERADE
nf_conntrack          221184  5 xt_conntrack,nf_nat,xt_nat,xt_MASQUERADE,ip_vs
nf_defrag_ipv6         28672  2 nf_conntrack,ip_vs
nf_defrag_ipv4         16384  1 nf_conntrack
overlay               196608  10
rfkill                 45056  1
cuse                   20480  1
vfat                   28672  1
fat                   110592  1 vfat
aes_ce_blk             45056  0
crypto_simd            28672  1 aes_ce_blk
cryptd                 32768  1 crypto_simd
virtio_gpu             90112  0
aes_ce_cipher          20480  1 aes_ce_blk
crct10dif_ce           20480  1
virtio_dma_buf         16384  1 virtio_gpu
ghash_ce               28672  0
sha1_ce                20480  0
drm_kms_helper        372736  3 virtio_gpu
cec                    69632  1 drm_kms_helper
drm                   811008  3 drm_kms_helper,virtio_gpu
joydev                 36864  0
fb_sys_fops            20480  1 drm_kms_helper
syscopyarea            16384  1 drm_kms_helper
sysfillrect            16384  1 drm_kms_helper
sysimgblt              16384  1 drm_kms_helper
button                 24576  0
sch_fq_codel           28672  1
fuse                  184320  2 cuse
br_netfilter           32768  0
xfs                  2199552  3
iscsi_tcp              32768  0
libiscsi_tcp           40960  1 iscsi_tcp
libiscsi               98304  2 libiscsi_tcp,iscsi_tcp
nvme_tcp               57344  0
nvme_fabrics           40960  1 nvme_tcp
nvme                   61440  0
nvme_core             180224  3 nvme_tcp,nvme,nvme_fabrics
dm_multipath           53248  0
sha2_ce                24576  0
virtio_net             69632  0
virtio_scsi            32768  3
net_failover           24576  1 virtio_net
failover               20480  1 net_failover
sha256_arm64           32768  1 sha2_ce
qemu_fw_cfg            28672  0
virtio_pci             32768  0
virtio_pci_legacy_dev    16384  1 virtio_pci
virtio_pci_modern_dev    24576  1 virtio_pci
sunrpc                794624  1
scsi_transport_iscsi   188416  4 libiscsi_tcp,iscsi_tcp,libiscsi
```

以下のistioのドキュメントに必要となるカーネルモジュールの一覧が記載されています。
https://istio.io/latest/docs/ops/deployment/platform-requirements/

# 仮対応
ワーカーノードのipアドレスを確認しsshします。
以下を実行しカーネルモジュールを読み込ませます。
```
sudo modprobe -a br_netfilter ip6table_mangle ip6table_nat ip6table_raw iptable_mangle iptable_nat iptable_raw xt_REDIRECT xt_connmark xt_conntrack xt_mark xt_owner xt_tcpudp bridge ip6_tables ip_tables nf_conntrack nf_conntrack_ipv4 nf_conntrack_ipv6 nf_nat nf_nat_ipv4 nf_nat_ipv6 nf_nat_redirect x_tables
```

これでistio-initのiptables実行が成功するようになりpodにsidecarがinjectされて正常に上がった状態になりました。
しかしこれではワーカーノードが再起動した場合にカーネルモジュールの読み込み設定が揮発してしまいますので、その対応をしたいと思います。

※一部のものがないと言われましたが、存在するものだけ対応するだけでもとりあえずsidecarのinitは動くようにはなったのでひとまず以下のものは無視して進めることとします。
```
modprobe: module 'xt_tcpudp' not found
modprobe: module 'bridge' not found
modprobe: module 'nf_conntrack_ipv4' not found
modprobe: module 'nf_conntrack_ipv6' not found
modprobe: module 'nf_nat_ipv4' not found
modprobe: module 'nf_nat_ipv6' not found
modprobe: module 'nf_nat_redirect' not found
modprobe: module 'x_tables' not found
```


# 恒久対応1
ワーカーノードのipアドレスを確認しsshします。
以下を実行し、起動時にカーネルモジュールの読み込みを自動で行うように設定します。
```
echo "br_netfilter
ip6table_mangle
ip6table_nat
ip6table_raw
iptable_mangle
iptable_nat
iptable_raw
xt_REDIRECT
xt_connmark
xt_conntrack
xt_mark
xt_owner
ip6_tables
ip_tables
nf_conntrack
nf_nat" | sudo tee /etc/modules-load.d/istio.conf
```
ワーカーをrebootして確認しましたが、必要なカーネルモジュールが自動で読み込まれていることが確認できました。（lsmodで確認できます。）

この対応でもいいのですが、ワーカーをスケールして増やしたり再構築した際に毎回手動でsshして、この設定して回るのは面倒です。そもそもマネージドで作成されたワーカーに対して直接sshして設定をいじくり回すのもなにか違う気がします。
そこでkubenetes側から自動で何とかする方法を考えることにしました。

# 恒久対応2
以下をkubernetesにデプロイし、特権状態のpodからホストのワーカーにカーネルモジュールを読み込ませる設定をすることにしました。DaemonSetなので各ノードにPodを一つずつ配置してくれますので、新規にワーカーが増えた場合でも自動で設定してくれます。ワーカー再起動時もpodが再作成される手続きの中で設定が行われます。（/etc/modules-load.d/istio.confに設定を書き込む処理を入れても良いのですが、読み込まれるし良いかと思いmodprobeだけを実行しています。）

```
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: modprobe-istio
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: modprobe-istio
  template:
    metadata:
      labels:
        app: modprobe-istio
    spec:
      containers:
        - name: modprobe-istio-sleeper
          image: busybox:1.36
          command: ['sh', '-c', 'echo I keep pod running! && sleep 3600']
      initContainers:
        - name: modprobe-istio-init
          image: busybox:1.36
          securityContext:
            privileged: true
          command: ['sh', '-c', 'echo br_netfilter ip6table_mangle ip6table_nat ip6table_raw iptable_mangle iptable_nat iptable_raw xt_REDIRECT xt_connmark xt_conntrack xt_mark xt_owner ip6_tables ip_tables nf_nat | xargs -n 1 modprobe']
          volumeMounts:
          - name: lib-modules
            mountPath: /lib/modules
      volumes:
      - name: lib-modules
        hostPath:
          path: /lib/modules
```

# さいごに
以上の方法で対応を行い問題を解決できましたが、マネージドkubernetesならistioくらい入れれば動く状態にしておいて欲しいものです。
OCIのドキュメントにもOKEにistioを導入する記載があるくらいなので（ https://docs.oracle.com/ja-jp/iaas/Content/ContEng/Tasks/contengistio-intro-topic.htm ）Oracleさん的にもこんな手続きが必要な想定ではなさそうで一時的な問題なのかもしれません。そのうちOracleさんがOKEのワーカー作成処理を直してくれることに期待したいと思います。