IBM CloudのManaged OpenShift(略してROKS、Red hat Openshift on ibm cloud Kubernetes Service)は、SDNドライバーにCalicoを使っているのが特徴の一つとして挙げられるが(通常はVXLANのOpenShift SDN)、Calicoの機能としてクラスター外のホストからKubernetes SDNのネットワークに参加する方法があるのを知り、試してみた。
Calico - About non-cluster hosts
https://docs.projectcalico.org/getting-started/bare-metal/about
初めに書いておくと、これが出来たところで構成上何かがうれしいという事は特にない。任意のPodにクラスター外から通信したければNodePort使えよという事である故。
ROKSクラスター
せっかくなのでVPC gen2上に作成。2ノード。
https://cloud.ibm.com/kubernetes/catalog/create?platformType=openshift
やれ標準プランのICOSが必要だの、SecurityGroupに穴を空けなきゃいかんだのをクリアしてクラスターを作成。結局image-registryのPodが証明書が違うとかエラーを吐いて起動してこない(p11-kit: couldn't complete writing file: /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt: Operation not permitted)。
安定の不安定ぶりだが、今回の検証には関係ないのでそこはスキップ。
仮想サーバー
ROKSクラスターを作った同じサブネットにVPC仮想サーバーを1台作成。CentOS7の一番小さいの。
仮想サーバー名は、例として「neighbor」。
なお、CentOS8はiptablesがなくなっているため、Calicoとは相性悪いかもと思ってやめた。今ざっとググったら動くことは動くらしいが。
OpenShiftにCluster AdminのSAを作る
仮想サーバーのCalicoがアクセスできるよう、OpenShiftクラスターにCluster Adminロールを持ったSAを作って、そのトークンを入手する。
1.ocコマンドを入手する。
https://mirror.openshift.com/pub/openshift-v4/clients/oc/
2.OpenShiftクラスターのWeb管理画面にログインし、右上の「アカウント名」>「Copy Login Command」からCLIでのログインの文字列を入手する。
3.ocコマンドでクラスターにログインする。
# oc login --token=Y4PsrZ4LSgWHMd7gnB0G-ZXuHv9Vlyi6q0P67GyAcgw --server=https://c100-e.jp-tok.containers.cloud.ibm.com:31248
4.SAを作り、cluster-adminロールを与える。
# oc create sa admin
# oc adm policy add-cluster-role-to-user cluster-admin -z admin
5.SAのトークンを入手し、kubeconfigに仕込む。
# oc get sa admin -o jsonpath='{.secrets}'
→ 「admin-token-*」のシークレット名を取得する。
# token=$(oc get secret <シークレット名> -o jsonpath='{.data.token}' | base64 -d)
# echo $token
→ eyJhbGciOiJSUzI1NiIsIm...みたいな、10行ぐらいのランダム文字列が表示
# server=$(grep -o 'https://.*$' .kube/config | head -n 1)
# cat > .kube/config << EOF
apiVersion: v1
kind: Config
preferences: {}
clusters:
- name: cluster
cluster:
server: ${server}
contexts:
- name: default
context:
cluster: cluster
namespace: default
user: admin
current-context: default
users:
- name: admin
user:
token: ${token}
EOF
作ったら、kubeconfigが有効なことを確かめる。
# oc get svc
(実行結果)
[root@neighbor ~]# oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.21.0.1 <none> 443/TCP 6h55m
openshift ExternalName <none> kubernetes.default.svc.cluster.local <none> 6h27m
openshift-apiserver ClusterIP 172.21.34.217 <none> 443/TCP 6h55m
Calico Felixをインストールする
コンテナ版を導入する必要がある(記事作成時点では)。
1.Dockerをインストールする。
# sudo yum install -y yum-utils
# sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# sudo yum install docker-ce docker-ce-cli containerd.io
# sudo systemctl start docker
2.OpenShiftクラスターに疑似ノードを作る。
(参考)https://github.com/projectcalico/calico/issues/3551
oc create -f - <<EOF
apiVersion: v1
kind: Node
metadata:
name: neighbor
EOF
3.OpenShiftクラスターで動いているCalicoのバージョンを確認する。
# oc get ds -n calico-system calico-node -o jsonpath='{.spec.template.spec.containers[0].image}'
→ ~calico/node:v3.16.5
4.Calicoを起動する。
起動前に、その時点のroute状態を確認してもいい。
# route -n
(実行結果)
[root@neighbor ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.244.64.1 0.0.0.0 UG 0 0 0 eth0
10.244.64.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
起動する。
# docker run -d --rm --net=host --privileged \
--name="calico-node" \
-e DATASTORE_TYPE="kubernetes" \
-e KUBECONFIG="/tmp/kube_config" \
-e CALICO_NODENAME="neighbor" \
-e CALICO_NETWORKING_BACKEND="bird" \
-v /var/log/calico:/var/log/calico \
-v /run/docker/plugins:/run/docker/plugins \
-v /lib/modules:/lib/modules \
-v /var/run/calico:/var/run/calico \
-v /etc/pki:/pki \
-v /root/.kube/config:/tmp/kube_config \
calico/node:v3.16.5
起動後、routeに以下「172.17.9.64 10.244.64.4」、「172.17.43.128 10.244.64.5」、「172.17.55.192 0.0.0.0」の3つの行が追加されていれば成功である。
[root@neighbor ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.244.64.1 0.0.0.0 UG 0 0 0 eth0
10.244.64.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.17.9.64 10.244.64.4 255.255.255.192 UG 0 0 0 eth0
172.17.43.128 10.244.64.5 255.255.255.192 UG 0 0 0 eth0
172.17.55.192 0.0.0.0 255.255.255.192 U 0 0 0 *
また、例えばクラスターのcalico-nodeに接続しても、仮想サーバーのアドレス(ここでは10.244.64.8)がルーターとしてテーブルに追加されているのを確認できるはずである。
# oc exec -it -n calico-system calico-node-g4dpg bash
# ip route
...
172.17.55.192/26 via 10.244.64.8 dev eth0 proto bird
...
Podにアクセスしてみる
oc get pod -o wideコマンドでPodを確認すると、表示されるPodのIPアドレスは172.17.9.64、172.17.43.128のいずれかのサブネットに所属しているはずである。
それらPodのIPアドレスに、仮想サーバーから直接アクセスできることを確認する。
例えば、、Prometheusとか?
# ipaddr=$(oc get pod -n openshift-monitoring prometheus-k8s-0 -o jsonpath='{.status.podIP}')
# echo $ipaddr
→ 172.17.9.98
# start=$(date -d yesterday +%s)
# end=$(date +%s)
# curl -k -H "Authorization:Bearer $token" \
https://${ipaddr}:9091/api/v1/query_range \
--data-urlencode "start=$start" \
--data-urlencode "end=$end" \
--data-urlencode "step=30" \
--data-urlencode 'query=sum(rate(container_cpu_usage_seconds_total{namespace="default"}[5m])) by (pod_name) * 100'
{"status":"success","data":...といった、100行ぐらいのデータが出力されれば成功である。
ついでに
最初に書いたが、これで何が嬉しいというのも実のところ特には無く、例えば上記のPrometheusはデフォルトでrouteが公開されているし、よしんば無かったとしても以下のようなNodePortを追加してそこからアクセスすれば良いのである。
# oc expose pod -n openshift-monitoring prometheus-k8s-0 --type=NodePort --port 9091
# oc get node
# ipaddr=<適当なノードのIPアドレス>
# nodeport=$(oc get svc -n openshift-monitoring prometheus-k8s-0 -o jsonpath='{.spec.ports[0].nodePort}')
# curl -k -H "Authorization:Bearer $token" \
https://${ipaddr}:${nodeport}/api/v1/query_range \
--data-urlencode "start=$start" \
--data-urlencode "end=$end" \
--data-urlencode "step=30" \
--data-urlencode 'query=sum(rate(container_cpu_usage_seconds_total{namespace="default"}[5m])) by (pod_name) * 100'