NHN Kubernetes Service(NKS)を使用する際に発生する可能性のあるさまざまな問題の解決方法を説明します。
コンテナログファイル管理(最大ファイルサイズ、ログファイル数設定など)のためにワーカーノードに以下のような設定を追加します。
$ sudo bash -c "cat > /etc/logrotate.d/docker" <<EOF
/var/lib/docker/containers/*/*.log {
rotate 10
copytruncate
missingok
notifempty
compress
maxsize 100M
daily
dateext
dateformat -%Y%m%d-%s
create 0644 root root
}
EOF
ワーカーノードでは毎日3時頃cronを介して上記設定のコンテナログローテーションが行われます。
[参考]
CentOS 7.8 - Container (2021.07.27)
以降のインスタンスイメージには上記のようなログローテーション設定が基本的に提供されます。
クラスタ運用過程で次のような場合は一部ワーカーノードのログローテーション設定が変わる状況が発生することもあります。 * ノードグループ間のインスタンスイメージが異なる場合 * ログローテーション設定適用イメージベースのノードvs未適用イメージベースのノード * ログローテーション設定未適用イメージベースのノードに直接設定を追加した場合 * クラスタオートスケーラーまたはノードグループサイズ調整を行って追加された新規ノードvs既存ノード * ログローテーション設定履歴を直接変更適用した場合 * クラスタオートスケーラーまたはノードグループサイズ調整を行って追加された新規ノードvs既存ノード
上記のような状況で全てのワーカーノードに一貫性のあるログローテーション設定を維持したい場合は、次のような同期方法を検討することができます。
SSH経由でログローテーション設定ファイルを同期する
以下はクラスタのすべてのワーカーノードに対してsshを経由してログローテーション設定ファイルを比較した後、必要なノードにコピーするスクリプトを作成するコマンドです。
コマンド実行に先立って必要なことは次のとおりです。
下で3つのcpコマンドの最初のパラメータの値を適切に修正して実行します。
実行完了後に作成されたシェルスクリプトとcron jobを通して毎日0時に同期処理が行われます。
$ cd ~
$ mkdir logrotate_for_container
$ cd logrotate_for_container
$
$ cp /path/to/my/kubeconfig/file kubeconfig.yaml
$ cp /path/to/my/keypair/file keypair.pem
$ cp /path/to/my/docker/logrotate/file docker_logrotate_config
$
$ cat > sync_logrotate.sh <<EOF
#!/bin/bash
set -o errexit
##################################################################
# KUBECONFIG: kubeconfig file for a target cluster #
# KEYPAIR: keypair file for worker nodes #
# LOCAL_CONFIG: logrotate configuration file used as sync source #
##################################################################
KUBECONFIG="kubeconfig.yaml"
KEYPAIR="keypair.pem"
LOCAL_CONFIG="docker_logrotate_config"
REMOTE_CONFIG="/etc/logrotate.d/docker"
base_config_hash=`md5sum ${LOCAL_CONFIG} | awk '{print $1}'`
worker_nodes=$(kubectl --kubeconfig=$KUBECONFIG get nodes -A -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}')
echo "[`date`] Start to synchronize the logrotate configuration for docker container"
echo " * Worker nodes list = ${worker_nodes}"
echo " * Comparing local config hash with remote config hash (local config hash = ${base_config_hash})"
sync_nodes=""
for node in ${worker_nodes}; do
node_conf_hash=`ssh -i ${KEYPAIR} -o StrictHostKeyChecking=no centos@${node} "md5sum ${REMOTE_CONFIG}"| awk '{print $1}'`
if [ "${base_config_hash}" != "${node_conf_hash}" ]; then
echo " -> Different hash with /etc/logrotate.d/docker@${node} (remote config hash = ${node_conf_hash})"
sync_nodes="${sync_nodes} ${node}"
fi
done
if [ -n "${sync_nodes}" ]; then
echo " * Copying ${LOCAL_CONFIG} to ${REMOTE_CONFIG} at target nodes: ${sync_nodes}"
for node in ${sync_nodes}; do
scp -i ${KEYPAIR} -o StrictHostKeyChecking=no ${LOCAL_CONFIG} centos@${node}:~/${LOCAL_CONFIG}.tmp >/dev/null
node_conf_hash=`ssh -i ${KEYPAIR} -o StrictHostKeyChecking=no centos@${node} "sudo cp ${LOCAL_CONFIG}.tmp ${REMOTE_CONFIG} && rm ${LOCAL_CONFIG}.tmp && md5sum ${REMOTE_CONFIG}" | awk '{print $1}'`
if [ $? == 0 ]; then
echo " -> Copy done... New hash of ${REMOTE_CONFIG}@${node} = ${node_conf_hash}"
else
echo " -> Something's wrong at ${node}"
fi
done
else
echo " * Logrotate configurations are up to date on all worker nodes"
fi
echo "[`date`] Finish to synchronize logrotate configuration"
EOF
$
$ chmod +x sync_logrotate.sh
$
$ crontab <<EOF
0 0 * * * ~/logrotate_for_container/sync_logrotate.sh > ~/logrotate_for_container/sync_logrotate.log
EOF
$
[参考]上記の内容は同期を行うための1つの方法にすぎません。ユーザーの環境に、より適切な方法があれば、その方法で同期処理を行ってください。
2020年11月20日からdockerhubはコンテナイメージpullリクエスト回数に次のような制限を設けるポリシーを実施しました。制限の詳細については、Understanding Docker Hub Rate LimitingとPricing & Subscriptionsを参照してください。
アカウント等級 | 2020年11月20日以前 | 2020年11月20日以降 |
---|---|---|
未認証ユーザー | 2,500req/6H | 100req/6H |
Free Tier | 2,500 req/6H | 200 req/6H |
Pro/Team/Large Tier | Unlimit | Unlimit |
NKSのワーカーノードでdockerhubからコンテナイメージをダウンロードする(pull)場合、dockerhubにログインせずに6時間以内に100件以上をダウンロードすると、それ以上イメージを受け取ることができなくなります。特にFloating IPが接続されていないワーカーは共用パブリックIPを利用するため、このような制約がより早くかかることがあります。
解決策は次のとおりです。 * dockerhubにログインすると、イメージを受け取ることができる数が増え、パブリックIPによる制限ではなくアカウント等級に基づいて制限を受けます。dockerhubアカウントを作成し、必要なpull数を提供するTierに加入してNKSを利用します。 KubernetesでPrivate Registryを使用する方法を参照してください。 * dockerhubにログインしていない状況で独立したパブリックIPによる制約を受けたい場合は、ワーカーノードにFloating IPを割り当てます。
k8s.gcr.io/pause:3.2
が発生します。クローズドネットワーク環境のクラスターがパブリックレジストリからイメージを取得できないため発生する問題であり、2024年8月以前に作成されたクラスターで発生する可能性があります。k8s.gcr.io/pause:3.2`イメージのように、デフォルトで配布されているイメージは、ワーカーノード作成時にNHN Cloud内部レジストリからプルされます。しかし、最初にイメージをプルされた後、イメージが削除された場合、問題が発生する可能性があります。クラスター作成時、基本的に配布されるイメージのリストは次のとおりです。 * kubernetesui/dashboard * k8s.gcr.io/pause * k8s.gcr.io/kube-proxy * kubernetesui/dashboard * kubernetesui/metrics-scraper * quay.io/coreos/flannel * quay.io/coreos/flannel-cni * calico-kube-controllers * calico-typha * calico-cni * calico-node * coredns/coredns * k8s.gcr.io/metrics-server-amd64 * k8s.gcr.io/metrics-server/metrics-server * gcr.io/google_containers/cluster-proportional-autoscaler-amd64 * k8s.gcr.io/cpa/cluster-proportional-autoscaler-amd64 * k8s.gcr.io/cpa/cluster-proportional-autoscaler-amd64 * k8s.gcr.io/sig-storage/csi-attacher * k8s.gcr.io/sig-storage/csi-provisioner * k8s.gcr.io/sig-storage/csi-snapshotter * k8s.gcr.io/sig-storage/csi-resizer * k8s.gcr.io/sig-storage/csi-node-driver-registrar * k8s.gcr.io/sig-storage/snapshot-controller * docker.io/k8scloudprovider/cinder-csi-plugin * k8s.gcr.io/node-problem-detector * k8s.gcr.io/node-problem-detector/node-problem-detector * k8s.gcr.io/autoscaling/cluster-autoscaler * nvidia/k8s-device-plugin 該当イメージに対して同じ問題が発生する可能性があります。
基本イメージはkubeletのImage garbage collectionによって削除されることがあります。 kubelet garbage collection関連情報はGarbage Collectionをご覧ください。NKSの場合、imageGCHighThresholdPercent, imageGCLowThresholdPercentがデフォルト値に設定されています。
imageGCHighThresholdPercent=85 :ディスク使用率が85%を超える場合、常にイメージGarbage Collectionを実行して使用しないイメージを削除します。
imageGCLowThresholdPercent=80 :ディスク使用率が80%以下の場合、イメージGarbage Collectionを実行しません。
NKSレジストリを有効にすると、クローズドネットワーク環境でコンテナイメージをパブリックレジストリから取得せず、NHN Cloud内部レジストリから取得するようにクラスター設定を変更できます。NKSレジストリは、クラスター照会画面で有効化できます。
pulling from host docker.pkg.github.com failed
エラーが発生し、イメージpullが失敗します。githubのパッケージレジストリがDockerレジストリからContainerレジストリに変更されたため発生した問題です。 v1.24以前のバージョンのクラスタはコンテナランタイムとしてDockerを使用して docker.pkg.github.com
レジストリからイメージpullが可能でしたが、v1.24以降のバージョンのNKSクラスタはコンテナランタイムとしてcotainerdを使用するため、docker.pkg.github.com
レジストリからイメージpullができません。パッケージレジストリの移行に関する詳細はMigration to Container registry from the Docker registry を参照してください。
解決方法は次のとおりです。
Podマニフェストに定義されたimage URLのbaseをdocker.pkg.github.com
からgchr.io
に変更します。
cannot allocate memory
エラーが発生し、Podの状態がFailedCreatePodContainer
と表示されます。Linuxカーネルの機能の中でmemory cgroupに対するkernel object accounting機能のバグで発生する現象です。主にLinuxカーネル3.x, 4.xバージョンで発生し、dying memory cgroup problem問題として知られています。ユーザーがイメージレベルでmemory cgroupに対するkernel object accounting機能を無効にしてこの問題を回避できます。
ワーカーノードに接続して起動オプションを変更した後、再起動します。
/etc/default/grub
ファイルを開き、GRUB_CMDLINE_LINUX
の既存値にcgroup.memory=nokmem
を追加します。# vim /etc/default/grub
- GRUB_CMDLINE_LINUX="..."
+ GRUB_CMDLINE_LINUX="... cgroup.memory=nokmem"
$ grub2-mkconfig -o /boot/grub2/grub.cfg
$ reboot
この問題は常に発生するわけではなく、ユーザーのアプリケーション特性によって発生する可能性があります。もし問題発生が懸念される場合、NKSのカスタムイメージ機能を利用して、最初から上記のような解決策が適用されたワーカーノードイメージを使用できます。
NKSではユーザーのカスタムイメージをベースにしたワーカーノードグループを作成する機能を提供しています。NKSカスタムイメージ機能を使用してmemory cgroupに対するkernel object accounting機能が無効化されたイメージを作成し、クラスタ作成時に活用することができます。カスタムイメージの使用機能の詳細については、カスタムイメージをワーカーイメージとして活用を参照してください。
#!/bin/bash
args="cgroup.memory=nokmem"
grub_file="/etc/default/grub"
sudo sed -i "s/GRUB_CMDLINE_LINUX=\"\(.*\)\"/GRUB_CMDLINE_LINUX=\"\1 $args\"/" "$grub_file"
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
この問題は、CalicoイメージがKubeletのGarbage Collectionによって削除された後、不適切なコンテナイメージリポジトリアドレスが原因で再ダウンロードできないために発生します。Kubeletは、ノードのディスク使用量を管理するために、未使用のコンテナイメージをクリーンアップするGarbage Collection機能を提供しています。この機能の詳細については、Garbage Collection文書で確認できます。 NKSの場合KubeletのimageGCHighThresholdPercent, imageGCLowThresholdPercentがデフォルト値に設定されています。
imageGCHighThresholdPercent=85 :ディスク使用率が85%を超える場合、常にイメージGarbage Collectionを実行して未使用のイメージを削除します。
imageGCLowThresholdPercent=80 :ディスク使用率が80%以下の場合、イメージGarbage Collectionを実行しません。
2024年05月以前に作成されたクラスタで問題が発生する可能性があります。kubectl get all -n kube-system
コマンドを確認すると、calico-kube-controllerまたはcalico-typha Podの状態がImagePullBackOffまたはErrImagePullで維持されます。calico-node PodはRunning状態に見えますが、Ready項目は0/1と表示されます。calico-node Podはdaemonsetとして配布されるため、kubeletのGCによるイメージ削除対象ではありません。しかし、calico-typhaとの通信の失敗により正常に動作せず、上記のような問題が発生する可能性があります。2024年05月以降に作成されたクラスタの場合、calico imageリポジトリの設定が変更され、この問題は発生しません。
calico関連imageリポジトリurlをpublicリポジトリに変更するスクリプトを実行して解決できます。ただし、この解決策はインターネットに接続可能なクラスタにのみ適用することができ、スクリプト実行中に一時的にクラスタPodネットワーキングが切断される可能性があるため、作業進行時に注意が必要です。スクリプトを実行する前に、すべてのワーカーノードが'Ready'状態であることを確認する必要があります。問題解決スクリプトは次のとおりです。
#!/bin/bash
tag="v3.24.1"
namespace="kube-system"
calico_cni_image="calico/cni:$tag"
calico_node_image="calico/node:$tag"
calico_typha_image="calico/typha:$tag"
calico_kube_controllers_image="calico/kube-controllers:$tag"
images=($calico_cni_image $calico_node_image $calico_typha_image $calico_kube_controllers_image)
default_timeout=4
declare -a failed_updates
check_image_match() {
local resource_type=$1
local resource_name=$2
local namespace=$3
local expected_image=$4
current_image=$(kubectl get $resource_type $resource_name -n $namespace -o jsonpath="{.spec.template.spec.containers[*].image}")
echo "Current $resource_type $resource_name image: $current_image"
if [ "$current_image" == "$expected_image" ]; then
echo "The image repo is not a target because it does not match the $expected_image"
exit 1
fi
}
pull_and_verify_image() {
local node=$1
local image=$2
local pod_name=$(kubectl debug node/"$node" --image="$image" --namespace=$namespace -- sleep 1 --quiet | awk '{print $4}')
echo "Created pod $pod_name in $namespace namespace"
local start_time=$(date +%s)
local timeout_seconds=360
while :; do
local current_time=$(date +%s)
local elapsed_time=$((current_time - start_time))
if [ $elapsed_time -ge $timeout_seconds ]; then
echo "Timeout reached: $timeout_seconds seconds for node $node, image $image."
echo "Exiting due to timeout failure."
kubectl delete pod $pod_name -n $namespace >/dev/null 2>&1
exit 1
fi
local container_state=$(kubectl get pod $pod_name -n $namespace -o jsonpath='{.status.containerStatuses[0].state}' 2>/dev/null)
if echo "$container_state" | grep -q "terminated"; then
echo "Container has terminated, deleting pod $pod_name from $namespace namespace"
kubectl delete pod $pod_name -n $namespace >/dev/null 2>&1
break
elif echo "$container_state" | grep -q "running"; then
echo "Container is running, deleting pod $pod_name from $namespace namespace"
kubectl delete pod $pod_name -n $namespace >/dev/null 2>&1
break
elif echo "$container_state" | grep -q "waiting"; then
local reason=$(kubectl get pod $pod_name -n $namespace -o jsonpath='{.status.containerStatuses[0].state.waiting.reason}' 2>/dev/null)
if [[ "$reason" == "ImagePullBackOff" || "$reason" == "ErrImagePull" ]]; then
echo "Failed to pull image $image on node $node due to $reason. Exiting."
kubectl delete pod $pod_name -n $namespace >/dev/null 2>&1
exit 1
fi
fi
sleep 5
done
}
update_image() {
local resource_type=$1
local resource_name=$2
local timeout=$3
shift 3
echo ""
echo "Updating $resource_type $resource_name with timeout ${timeout} minutes..."
local update_command="kubectl set image $resource_type/$resource_name"
for arg in "$@"; do
local container_name=$(echo $arg | cut -d'=' -f1)
local image_name=$(echo $arg | cut -d'=' -f2)
update_command+=" $container_name=$image_name"
done
update_command+=" -n $namespace"
if ! eval $update_command; then
echo "Failed to update $resource_type $resource_name"
failed_updates+=("$resource_type/$resource_name")
return
fi
check_rollout_status $resource_type $resource_name $timeout
return $?
}
check_rollout_status() {
local resource_type=$1
local resource_name=$2
local timeout=$3
echo "Checking rollout status for $resource_type $resource_name..."
if ! kubectl rollout status $resource_type $resource_name -n $namespace --timeout=${timeout}m; then
echo "Rollout status check failed for $resource_type $resource_name"
failed_updates+=("$resource_type/$resource_name")
return 1
fi
echo "$resource_type $resource_name updated successfully."
return 0
}
delete_old_pods() {
local resource_type=$1
local resource_name=$2
local old_pods=$3
for pod in $old_pods; do
if kubectl get pods $pod -n $namespace &> /dev/null; then
echo "Deleting old pod: $pod"
kubectl delete pod $pod -n $namespace
fi
done
}
update_calico_node() {
local resource_type="daemonset"
local resource_name="calico-node"
local timeout=$(( $(kubectl get nodes --no-headers | wc -l) * 4 ))
update_image $resource_type $resource_name $timeout "$resource_name=$calico_node_image" "install-cni=$calico_cni_image" "mount-bpffs=$calico_node_image"
}
update_calico_kube_controller() {
local resource_type="deployment"
local resource_name="calico-kube-controllers"
update_image $resource_type $resource_name $default_timeout "$resource_name=$calico_kube_controllers_image"
}
update_calico_typha_image() {
local resource_type="deployment"
local resource_name="calico-typha"
local old_pods=$(kubectl get pods -n $namespace -l k8s-app="$resource_name" -o jsonpath="{.items[*].metadata.name}")
if ! update_image $resource_type $resource_name $default_timeout "$resource_name=$calico_typha_image"; then
delete_old_pods $resource_type $resource_name $old_pods
fi
}
check_image_match "daemonset" "calico-node" $namespace $calico_node_image
check_image_match "deployment" "calico-kube-controllers" $namespace $calico_kube_controllers_image
check_image_match "deployment" "calico-typha" $namespace $calico_typha_image
for node in $(kubectl get nodes --no-headers | awk '{print $1}'); do
echo ""
echo "Worker node : [$node] calico images pull start!!"
for image in "${images[@]}"; do
echo "Pulling $image"
pull_and_verify_image $node $image
done
done
echo "The calico image pull has been completed!"
echo ""
update_calico_node
update_calico_kube_controller
update_calico_typha_image
echo ""
if [ ${#failed_updates[@]} -eq 0 ]; then
echo "Calico images update completed!"
exit 0
else
echo "[WARNING] Please check to resources status:"
for resource in "${failed_updates[@]}"; do
echo "- $resource"
done
exit 1
fi
スクリプトの流れは次のとおりです。 1. 全てのワーカーノードにcalico関連イメージをpulllします。 2. calico-node daemonsetイメージリポジトリを変更するローリングアップデートを行います。 3. calico-kube-controllers deploymentイメージリポジトリを変更するローリングアップデートを行います。 4. calico-typha deploymentイメージリポジトリを変更するローリングアップデートを行います。
このスクリプトはkubectlコマンドが使える環境で実行できます。実行方法は次のとおりです。 * vim calico_manifest_image_change.sh * 本文スクリプトの内容を保存 * KUBECONFIG環境変数にkubeconfig設定ファイルのパスを保存 * chmod 755 calico_manifest_image_change.sh * ./calico_manifest_image_change.sh
ワーカーノードのrpc.statdプロセスがゾンビプロセスになったり、管理者のコマンドによって停止して発生する問題です。ボリュームをマウントするためには、ワーカーノードでrpcbindとrpc.statdプロセスが正常に実行されている必要があります。解決策は次のとおりです。
systemctl restart rpc-statd
systemctl restart rpcbind
2024年8月以前に作成された1.20以上のバージョンのクラスターで発生する可能性がある問題です。下記のスクリプトを実行してクラスターに配布されたcinder-csi-driverをアップデートして問題を解決できます。スクリプトの実行後、新しく作成されたPVまたは容量が増設されたPVにのみ設定の更新が反映されます。
kubeconfig_file_path変数にクラスターのkubeconfigファイルが位置する絶対パス値を定義した後、スクリプトを実行します。
#!/bin/bash
kubeconfig_file_path={kubeconfigファイルの絶対パス}
SECRET_NAME="cinder-csi-cloud-config"
NAMESPACE="kube-system"
# Fetch the secret using kubectl and parse the JSON output with jq
secret_data=$(kubectl --kubeconfig=$kubeconfig_file_path get secret "$SECRET_NAME" -n "$NAMESPACE" -o json)
# Extract the 'cloud-config' key and decode its value
cloud_config_base64=$(echo "$secret_data" | jq -r '.data["cloud-config"]')
if [[ "$cloud_config_base64" != "null" ]]; then
# Decode the base64 value
cloud_config=$(echo "$cloud_config_base64" | base64 --decode)
# Add the [BlockStorage] section with rescan-on-resize=true
modified_cloud_config=$(cat <<EOF
$cloud_config
[BlockStorage]
rescan-on-resize=true
EOF
)
# Encode the modified cloud-config back to base64
modified_cloud_config_base64=$(echo "$modified_cloud_config" | base64 | tr -d '\n')
# Update the Kubernetes secret with the new base64-encoded data
kubectl --kubeconfig=$kubeconfig_file_path patch secret "$SECRET_NAME" -n "$NAMESPACE" --type=json \
-p="[{'op': 'replace', 'path': '/data/cloud-config', 'value':'$modified_cloud_config_base64'}]"
echo "Secret $SECRET_NAME updated successfully."
else
echo "cloud-config key not found in secret $SECRET_NAME"
fi
kubectl -n kube-system rollout restart daemonet cinder-csi-nodeplugin
kubectl -n kube-system rollout restart statefulset cinder-csi-controllerplugin
これは、大きなサイズのボリュームをPodにマウントする場合に発生する可能性のある問題です。基本的に、Kubernetesはボリュームをマウントする際、各ボリュームの内容に対する所有権と権限を変更し、PodのSecurityContextに指定されたfsGroupと一致するようにします。ボリュームが大きい場合、所有権と権限を確認して変更するのに時間がかかり、タイムアウトが発生する可能性があります。
タイムアウトの発生を防ぐために、securityContextのfsGroupChangePolicyフィールドを使用して、Kubernetesがボリュームの所有権と権限を確認して管理する方法を変更できます。詳細はConfigure volume permission and ownership change policy for podsを参照してください。
Calico v3.28.0でUDP通信中、BPF NATテーブルがネットワークパケットを正しく処理できないために発生する問題です。 eBPFを使用する場合、TCPはCTLB(connect-time load balancing)
方式で通信し、UDPはBPFが管理するNATテーブル
を通じて通信します。この問題はUDP通信もCTLB方式に変更すれば解決できます。
CTLB(connect-time load balancing)
は、ネットワークロードバランシング技術の1つで、クライアントがサーバーに初めて接続する際、最初のパケットでバックエンドサーバーを選択し、その後の全てのトラフィックは選択されたバックエンドサーバーに直接転送されます。これにより、セッションの永続性が保証され、毎回ロードバランシングを実行するオーバーヘッドを減らすことができます。
calico-node daemonsetのUDP CTLB設定を変更することで問題を解決できます。 以下は設定を変更する過程です。
kubectl edit daemonset.apps/calico-node -n kube-system
spec.template.spec.containers.env項目に下記のような設定を追加する必要があります。 Podのテンプレートが修正されると、ローリングアップデート方式でcalico-nodeが再起動されます。
- name: FELIX_BPFCONNECTTIMELOADBALANCING
value: "Enabled"
- name: FELIX_BPFHOSTNETWORKEDNATWITHOUTCTLB
value: "Disabled"
UDPは非接続型プロトコルで、サーバー/クライアント通信時、別途セッションを設定したり、接続を維持することなくデータを送信します。しかし、Golang net.DialUDP()
関数などのUDPのconnect()
関数を使用するとUDPソケットを特定アドレスと接続して指定されたアドレスにのみデータを送受信できます。
CalicoのeBPFを使用すると、UDPのCTLB(connect-time load balancing)が有効化になっているクラスターにUD connect()
関数を使用するPodを配布した場合、サーバーの役割を行うPodが再配布されると、通信問題が発生する可能性があります。これは、UDPソケットが最初に接続されたサーバーアドレスにのみデータ転送を試みるためです。サーバーPodが再配布されると、IPアドレスやネットワークパスが変更される可能性がありますが、UDP connect()ソケットは以前のサーバーアドレスにのみデータを送信するため、通信に失敗する可能性があります。
この問題はUDP connect()の動作方法とCTLB環境で発生する既知の問題なので、Calico eBPFとUDP CTLBを使用するクラスターでUDPのconnect()関数を使用する場合、このような通信問題が発生する可能性があることを認識して注意する必要があります。
eBPFが有効になると、CTLB(connect-time load balancing)
方式で接続時にロードバランシングを行い、クライアントがサービスに接続しようとする最初のパケットでバックエンドPodを選択し、その後の全てのパケットは該当バックエンドに直接転送されます。一方、Istioはサービスメッシュを構成するためにサイドカープロキシを配備し、プロキシはアプリケーショントラフィックを傍受して制御とモニタリングの役割を果たします。
CTLBが有効になっている場合、パケットはBPF MAPから目的地Podに直接転送され、この過程でパケットが改ざんされます。そのため、Istioのプロキシを経由せず、パケットは目的地Podに直接転送されます。このようなeBPFネットワーク構造により、Istioの機能が正常に動作しない場合があります。istioを使ったクラスター管理が必要な場合は、Calico-VXLANクラスターの使用を考慮する必要があります。
Calico v3.28.0のcalico/kube-controllersで発見されたバグにより発生する問題です。ノード削減の際、calico/kube-cube-controllers Podが配布されたノードが削除されると、そのノードは他のノードにスケジューリングされて実行されます。calico/kube-controllersが再実行されている間、ノード情報が同期されません。この状態で削除したノードと同じ名前のノードが追加されると、ネットワーク障害が発生する可能性があります。
この問題はCalico v3.28.2で修正されました。Calico v3.28.2を使うためには、Kubernetesのバージョンをアップグレードするか、クラスターを再作成する必要があります。