CNI plugin の入力と出力をキャプチャする方法
概要
k8sでCNI pluginを使用する際、CNI pluginの入出力を確認したい場合があります。方法がいくつかある中で、plugin chainingを使用した場合を説明します。
plugin chaining
CNIにはplugin chainingという仕組みがあり、複数のpluginを順に実行することができます。例えばcni_a, cni_b, cni_cを並べることでこれらを順に実行します。 CNI_COMMANDがADDの場合はcni_a, cni_b, cni_cの順に、DELの場合はこの逆順に実行します。
この機能を利用して、例えば cni_pre, Calico, cni_post の順に並べることでCalicoの入出力を確認することができます。
準備
cni_preとcni_postのpythonでの実装例です。
cni_pre.py
#!/usr/bin/python3
import os
import subprocess
import json
import sys
import tempfile
import netifaces
import hashlib
import random
import fcntl
def parseArgs():
args = {}
args['CNI_COMMAND'] = os.environ.get('CNI_COMMAND')
if args['CNI_COMMAND'] == None:
with open(LOG_FILE_NAME, mode='a') as f:
f.write('env CNI_COMMAND not found')
exit(1)
args['CNI_CONTAINERID'] = os.environ.get('CNI_CONTAINERID')
if args['CNI_CONTAINERID'] == None:
with open(LOG_FILE_NAME, mode='a') as f:
f.write('env CNI_CONTAINERID not found')
exit(1)
args['STDIN_DATA'] = json.load(sys.stdin)
return args
def cmdAdd(args):
with open(LOG_FILE_NAME, mode='a') as f:
f.write('CNI_COMMAND=')
f.write(args['CNI_COMMAND'])
f.write(',CNI_CONTAINERID=')
f.write(args['CNI_CONTAINERID'])
f.write(',input=')
f.write(json.dumps(args['STDIN_DATA']))
f.write('\n')
print(json.dumps({'cniVersion': '0.4.0'}))
def cmdDel(args):
print("{}")
def cmdCheck(args):
print("{}")
LOG_FILE_NAME = '/tmp/cni.log'
LOCK_FILE_NAME = '/var/run/lock/cni.lock'
args = parseArgs()
if args['CNI_COMMAND'] == 'VERSION':
print('{"cniVersion": "0.4.0", "supportedVersions": ["0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0"]}')
exit(0)
lockfp = open(LOCK_FILE_NAME, "w")
fcntl.flock(lockfp.fileno(), fcntl.LOCK_EX)
if args['CNI_COMMAND'] == 'ADD':
cmdAdd(args)
elif args['CNI_COMMAND'] == 'DEL':
cmdDel(args)
elif args['CNI_COMMAND'] == 'CHECK':
cmdCheck(args)
fcntl.flock(lockfp.fileno(), fcntl.LOCK_UN)
lockfp.close()
cni_post.py
#!/usr/bin/python3
import os
import subprocess
import json
import sys
import tempfile
import netifaces
import hashlib
import random
import fcntl
def parseArgs():
args = {}
args['CNI_COMMAND'] = os.environ.get('CNI_COMMAND')
if args['CNI_COMMAND'] == None:
with open(LOG_FILE_NAME, mode='a') as f:
f.write('env CNI_COMMAND not found')
exit(1)
args['CNI_CONTAINERID'] = os.environ.get('CNI_CONTAINERID')
if args['CNI_CONTAINERID'] == None:
with open(LOG_FILE_NAME, mode='a') as f:
f.write('env CNI_CONTAINERID not found')
exit(1)
args['STDIN_DATA'] = json.load(sys.stdin)
return args
def cmdAdd(args):
with open(LOG_FILE_NAME, mode='a') as f:
f.write('CNI_COMMAND=')
f.write(args['CNI_COMMAND'])
f.write(',CNI_CONTAINERID=')
f.write(args['CNI_CONTAINERID'])
f.write(',output=')
f.write(json.dumps(args['STDIN_DATA']['prevResult']))
f.write('\n')
print(json.dumps(args['STDIN_DATA']['prevResult']))
def cmdDel(args):
with open(LOG_FILE_NAME, mode='a') as f:
f.write('CNI_COMMAND=')
f.write(args['CNI_COMMAND'])
f.write(',CNI_CONTAINERID=')
f.write(args['CNI_CONTAINERID'])
f.write(',input=')
f.write(json.dumps(args['STDIN_DATA']))
f.write('\n')
print("{}")
def cmdCheck(args):
print("{}")
LOG_FILE_NAME = '/tmp/cni.log'
LOCK_FILE_NAME = '/var/run/lock/cni.lock'
args = parseArgs()
if args['CNI_COMMAND'] == 'VERSION':
print('{"cniVersion": "0.4.0", "supportedVersions": ["0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0"]}')
exit(0)
lockfp = open(LOCK_FILE_NAME, "w")
fcntl.flock(lockfp.fileno(), fcntl.LOCK_EX)
if args['CNI_COMMAND'] == 'ADD':
cmdAdd(args)
elif args['CNI_COMMAND'] == 'DEL':
cmdDel(args)
elif args['CNI_COMMAND'] == 'CHECK':
cmdCheck(args)
fcntl.flock(lockfp.fileno(), fcntl.LOCK_UN)
lockfp.close()
これらをCalicoの前後で実行するために、Calicoの設定ファイルを編集します。
/etc/cni/net.d/10-calico.conflist
のplugins
配列の先頭にcni_pre.py
を、末尾にcni_post.py
を追加します。
--- 10-calico.conflist.prev 2021-05-07 17:57:21.252206104 +0900
+++ 10-calico.conflist 2021-05-07 17:56:43.820039515 +0900
@@ -3,6 +3,9 @@
"cniVersion": "0.3.1",
"plugins": [
{
+ "type": "cni_pre.py"
+ },
+ {
"type": "calico",
"log_level": "info",
"log_file_path": "/var/log/calico/cni/cni.log",
@@ -27,6 +30,9 @@
{
"type": "bandwidth",
"capabilities": {"bandwidth": true}
+ },
+ {
+ "type": "cni_post.py"
}
]
}
実行
下記コマンドでpodを起動します。
$ kubectl run nginx --image=nginx
該当Nodeにて/tmp/cni.log
を開いて内容を確認します。
ADDコマンドの場合に{"cniVersion": "0.3.1", "name": "k8s-pod-network", "type": "cni_pre.py"}
を受け取って{"cniVersion": "0.3.1", "interfaces": [{"name": "calic440f455693"}], "ips": [{"version": "4", "address": "10.244.19.140/32"}], "dns": {}}
を返していることがわかります。
CNI_COMMAND=ADD,CNI_CONTAINERID=280718f61fd143ce1e75e9a40557129559098a9fb911d909a07f84fd6f8532e2,input={"cniVersion": "0.3.1", "name": "k8s-pod-network", "type": "cni_pre.py"}
CNI_COMMAND=ADD,CNI_CONTAINERID=280718f61fd143ce1e75e9a40557129559098a9fb911d909a07f84fd6f8532e2,output={"cniVersion": "0.3.1", "interfaces": [{"name": "calic440f455693"}], "ips": [{"version": "4", "address": "10.244.19.140/32"}], "dns": {}}
Discussion