SiamCafe.net Blog
Technology

Multus CNI Testing Strategy QA

multus cni testing strategy qa
Multus CNI Testing Strategy QA | SiamCafe Blog
2025-11-14· อ. บอม — SiamCafe.net· 1,765 คำ

Multus CNI Testing Strategy QA คืออะไร

Multus CNI เป็น meta-plugin สำหรับ Kubernetes ที่ช่วยให้ pods มี network interfaces หลายตัวพร้อมกัน นอกเหนือจาก default cluster network ปกติ รองรับ SR-IOV, Macvlan, IPVLAN, Bridge และ CNI plugins อื่นๆ Testing Strategy QA คือกลยุทธ์การทดสอบเพื่อรับรองคุณภาพของระบบ ครอบคลุม unit tests, integration tests, end-to-end tests และ performance tests การรวมสองแนวคิดนี้ช่วยให้ multi-network Kubernetes deployments มีความน่าเชื่อถือสูง ตรวจจับปัญหา networking ก่อน production

Multus CNI Architecture

# multus_arch.py — Multus CNI architecture overview
import json

class MultusArchitecture:
    CONCEPTS = {
        "multus": {
            "name": "Multus CNI",
            "description": "Meta-plugin ที่ chain CNI plugins หลายตัว — pod ได้หลาย network interfaces",
            "use_cases": "Telco/5G (user plane + control plane), NFV, storage networks, monitoring networks",
        },
        "net_attach_def": {
            "name": "NetworkAttachmentDefinition (NAD)",
            "description": "CRD ที่กำหนด additional networks — ระบุ CNI plugin + config",
            "example": "NAD สำหรับ Macvlan network ที่ connect กับ physical NIC",
        },
        "default_network": {
            "name": "Default Network (eth0)",
            "description": "Cluster network ปกติ (Calico, Flannel, Cilium) — ทุก pod มี",
        },
        "additional_networks": {
            "name": "Additional Networks (net1, net2...)",
            "description": "Networks เพิ่มเติมจาก Multus — SR-IOV, Macvlan, Bridge",
        },
    }

    NAD_EXAMPLE = """
# NetworkAttachmentDefinition — Macvlan network
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
  name: macvlan-net
  namespace: default
spec:
  config: |
    {
      "cniVersion": "0.3.1",
      "type": "macvlan",
      "master": "eth0",
      "mode": "bridge",
      "ipam": {
        "type": "host-local",
        "subnet": "192.168.1.0/24",
        "rangeStart": "192.168.1.200",
        "rangeEnd": "192.168.1.250",
        "gateway": "192.168.1.1"
      }
    }
---
# Pod with multiple networks
apiVersion: v1
kind: Pod
metadata:
  name: multi-net-pod
  annotations:
    k8s.v1.cni.cncf.io/networks: macvlan-net
spec:
  containers:
    - name: app
      image: nginx
      # Pod จะมี: eth0 (cluster) + net1 (macvlan)
"""

    def show_concepts(self):
        print("=== Multus CNI Concepts ===\n")
        for key, concept in self.CONCEPTS.items():
            print(f"[{concept['name']}]")
            print(f"  {concept['description']}")
            print()

    def show_nad(self):
        print("=== NAD Example ===")
        print(self.NAD_EXAMPLE[:500])

arch = MultusArchitecture()
arch.show_concepts()
arch.show_nad()

Testing Strategy Overview

# testing_strategy.py — Testing strategy for Multus CNI
import json

class TestingStrategy:
    PYRAMID = {
        "unit": {
            "name": "Unit Tests",
            "percentage": "40%",
            "scope": "CNI config validation, IPAM logic, NAD schema",
            "tools": "pytest, Go test (สำหรับ CNI plugins)",
            "speed": "เร็วมาก (< 1 วินาที)",
        },
        "integration": {
            "name": "Integration Tests",
            "percentage": "30%",
            "scope": "CNI plugin chain, network connectivity ระหว่าง pods, IPAM allocation",
            "tools": "Kind/Minikube + pytest, Go integration tests",
            "speed": "ปานกลาง (10-60 วินาที)",
        },
        "e2e": {
            "name": "End-to-End Tests",
            "percentage": "20%",
            "scope": "Full cluster tests — pod creation, multi-network connectivity, failover",
            "tools": "Ginkgo + Gomega, Robot Framework, custom scripts",
            "speed": "ช้า (1-10 นาที)",
        },
        "performance": {
            "name": "Performance Tests",
            "percentage": "10%",
            "scope": "Throughput, latency, pod startup time, network bandwidth",
            "tools": "iperf3, netperf, custom benchmarks",
            "speed": "ช้ามาก (10-60 นาที)",
        },
    }

    def show_pyramid(self):
        print("=== Test Pyramid ===\n")
        for key, level in self.PYRAMID.items():
            print(f"[{level['name']}] ({level['percentage']})")
            print(f"  Scope: {level['scope']}")
            print(f"  Tools: {level['tools']}")
            print(f"  Speed: {level['speed']}")
            print()

strategy = TestingStrategy()
strategy.show_pyramid()

Test Implementation

# tests.py — Multus CNI test implementations
import json

class MultusTests:
    UNIT_TESTS = """
# test_nad_validation.py — Unit tests for NAD config
import pytest
import json

class TestNADValidation:
    def test_valid_macvlan_config(self):
        config = {
            "cniVersion": "0.3.1",
            "type": "macvlan",
            "master": "eth0",
            "mode": "bridge",
            "ipam": {
                "type": "host-local",
                "subnet": "192.168.1.0/24",
            }
        }
        assert validate_cni_config(config) == True
    
    def test_missing_cni_version(self):
        config = {"type": "macvlan", "master": "eth0"}
        with pytest.raises(ValueError, match="cniVersion required"):
            validate_cni_config(config)
    
    def test_invalid_subnet(self):
        config = {
            "cniVersion": "0.3.1",
            "type": "macvlan",
            "ipam": {"type": "host-local", "subnet": "invalid"}
        }
        with pytest.raises(ValueError, match="Invalid subnet"):
            validate_cni_config(config)
    
    def test_ipam_range_overlap(self):
        config = {
            "cniVersion": "0.3.1",
            "type": "macvlan",
            "ipam": {
                "type": "host-local",
                "subnet": "192.168.1.0/24",
                "rangeStart": "192.168.1.250",
                "rangeEnd": "192.168.1.200",  # End before Start
            }
        }
        with pytest.raises(ValueError, match="Invalid range"):
            validate_cni_config(config)

def validate_cni_config(config):
    if "cniVersion" not in config:
        raise ValueError("cniVersion required")
    # ... validation logic
    return True
"""

    INTEGRATION_TESTS = """
# test_multus_integration.py — Integration tests
import subprocess
import pytest
import time
import json

class TestMultusIntegration:
    @pytest.fixture(autouse=True)
    def setup(self):
        # Create test namespace
        subprocess.run(["kubectl", "create", "ns", "multus-test"], check=False)
        yield
        subprocess.run(["kubectl", "delete", "ns", "multus-test", "--grace-period=0", "--force"])
    
    def test_pod_gets_additional_interface(self):
        '''Pod with Multus annotation gets extra network interface'''
        # Create NAD
        subprocess.run(["kubectl", "apply", "-f", "test-nad.yaml", "-n", "multus-test"], check=True)
        
        # Create pod with annotation
        subprocess.run(["kubectl", "apply", "-f", "test-pod.yaml", "-n", "multus-test"], check=True)
        
        # Wait for pod ready
        subprocess.run(["kubectl", "wait", "--for=condition=Ready", 
                        "pod/test-pod", "-n", "multus-test", "--timeout=60s"], check=True)
        
        # Verify interfaces
        result = subprocess.run(
            ["kubectl", "exec", "-n", "multus-test", "test-pod", "--", "ip", "addr"],
            capture_output=True, text=True
        )
        
        assert "eth0" in result.stdout  # Default network
        assert "net1" in result.stdout  # Multus additional network
    
    def test_cross_pod_connectivity(self):
        '''Two pods on same additional network can communicate'''
        # Create 2 pods on same macvlan network
        subprocess.run(["kubectl", "apply", "-f", "test-2pods.yaml", "-n", "multus-test"], check=True)
        time.sleep(10)
        
        # Get pod2 IP on net1
        result = subprocess.run(
            ["kubectl", "exec", "-n", "multus-test", "pod-a", "--",
             "ping", "-c", "3", "-W", "2", "192.168.1.201"],
            capture_output=True, text=True
        )
        assert result.returncode == 0
    
    def test_network_isolation(self):
        '''Pods on different additional networks cannot communicate'''
        # pod-a on macvlan-net-a, pod-b on macvlan-net-b
        # ping should fail
        result = subprocess.run(
            ["kubectl", "exec", "-n", "multus-test", "pod-a", "--",
             "ping", "-c", "2", "-W", "2", "10.0.2.100"],
            capture_output=True, text=True
        )
        assert result.returncode != 0  # Should fail — isolated networks
"""

    def show_unit(self):
        print("=== Unit Tests ===")
        print(self.UNIT_TESTS[:600])

    def show_integration(self):
        print(f"\n=== Integration Tests ===")
        print(self.INTEGRATION_TESTS[:600])

tests = MultusTests()
tests.show_unit()
tests.show_integration()

E2E & Performance Tests

# e2e_perf.py — E2E and performance tests
import json
import random

class E2EPerformanceTests:
    E2E_SCENARIOS = {
        "pod_lifecycle": {
            "name": "Pod Lifecycle with Multi-Network",
            "steps": ["Create NAD", "Create Pod", "Verify interfaces", "Delete Pod", "Verify IP released"],
            "timeout": "120s",
        },
        "rolling_update": {
            "name": "Rolling Update Preserves Network",
            "steps": ["Deploy with Multus", "Rolling update image", "Verify no connectivity loss"],
            "timeout": "300s",
        },
        "node_failure": {
            "name": "Node Failure Recovery",
            "steps": ["Pod on node A", "Drain node A", "Pod rescheduled to B", "Verify new interfaces"],
            "timeout": "600s",
        },
        "scale_test": {
            "name": "Scale Test — 100 Pods",
            "steps": ["Create 100 pods with Multus", "Verify all get IPs", "Verify connectivity", "Cleanup"],
            "timeout": "600s",
        },
    }

    PERF_BENCHMARKS = """
# perf_test.py — Network performance benchmarks
import subprocess
import json
import time

class NetworkPerfTest:
    def __init__(self, namespace="perf-test"):
        self.ns = namespace
    
    def bandwidth_test(self, interface="net1"):
        '''iperf3 bandwidth test on additional network'''
        # Start iperf3 server pod
        subprocess.run(["kubectl", "exec", "-n", self.ns, "iperf-server",
                       "--", "iperf3", "-s", "-D", "--bind", "0.0.0.0"])
        
        # Run client
        result = subprocess.run(
            ["kubectl", "exec", "-n", self.ns, "iperf-client", "--",
             "iperf3", "-c", "SERVER_IP", "-t", "30", "-J"],
            capture_output=True, text=True
        )
        
        data = json.loads(result.stdout)
        return {
            "bandwidth_gbps": data["end"]["sum_sent"]["bits_per_second"] / 1e9,
            "retransmits": data["end"]["sum_sent"]["retransmits"],
        }
    
    def latency_test(self, target_ip, count=100):
        '''Ping latency test'''
        result = subprocess.run(
            ["kubectl", "exec", "-n", self.ns, "ping-pod", "--",
             "ping", "-c", str(count), "-i", "0.1", target_ip],
            capture_output=True, text=True
        )
        # Parse avg latency
        return {"avg_latency_ms": 0.5}  # parsed from output
    
    def pod_startup_time(self, count=10):
        '''Measure pod startup time with Multus'''
        times = []
        for i in range(count):
            start = time.time()
            subprocess.run(["kubectl", "run", f"timing-{i}", 
                          "--image=alpine", "--restart=Never",
                          f"--overrides={{...multus annotation...}}"], check=True)
            subprocess.run(["kubectl", "wait", f"--for=condition=Ready",
                          f"pod/timing-{i}", "--timeout=120s"], check=True)
            times.append(time.time() - start)
        
        return {
            "avg_startup_s": sum(times) / len(times),
            "p99_startup_s": sorted(times)[int(len(times) * 0.99)],
        }
"""

    def show_scenarios(self):
        print("=== E2E Test Scenarios ===\n")
        for key, scenario in self.E2E_SCENARIOS.items():
            print(f"[{scenario['name']}] Timeout: {scenario['timeout']}")
            for step in scenario["steps"][:3]:
                print(f"  → {step}")
            print()

    def show_benchmarks(self):
        print("=== Performance Benchmarks ===")
        print(self.PERF_BENCHMARKS[:500])

    def sample_results(self):
        print(f"\n=== Sample Performance Results ===")
        print(f"  Default network (eth0):")
        print(f"    Bandwidth: {random.uniform(8, 10):.1f} Gbps")
        print(f"    Latency: {random.uniform(0.1, 0.5):.2f} ms")
        print(f"  Multus macvlan (net1):")
        print(f"    Bandwidth: {random.uniform(7, 9.5):.1f} Gbps")
        print(f"    Latency: {random.uniform(0.05, 0.3):.2f} ms")
        print(f"  Multus SR-IOV (net1):")
        print(f"    Bandwidth: {random.uniform(9, 10):.1f} Gbps")
        print(f"    Latency: {random.uniform(0.02, 0.1):.2f} ms")
        print(f"  Pod startup (with Multus): {random.uniform(3, 8):.1f}s avg")

e2e = E2EPerformanceTests()
e2e.show_scenarios()
e2e.sample_results()

CI/CD Integration

# cicd.py — CI/CD for Multus CNI testing
import json

class CICDPipeline:
    GITHUB_ACTIONS = """
# .github/workflows/multus-tests.yml
name: Multus CNI Tests
on:
  push:
    branches: [main]
    paths: ['network/**', 'tests/**']
  pull_request:
    paths: ['network/**', 'tests/**']

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install pytest pyyaml jsonschema
      - run: pytest tests/unit/ -v

  integration-tests:
    needs: unit-tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Create Kind cluster with Multus
        run: |
          kind create cluster --config tests/kind-config.yaml
          kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
          kubectl wait --for=condition=Ready -n kube-system ds/kube-multus-ds --timeout=120s
      
      - name: Run integration tests
        run: pytest tests/integration/ -v --timeout=300
      
      - name: Cleanup
        if: always()
        run: kind delete cluster

  e2e-tests:
    needs: integration-tests
    runs-on: self-hosted  # Needs real hardware for SR-IOV
    steps:
      - uses: actions/checkout@v4
      - name: Run E2E tests
        run: pytest tests/e2e/ -v --timeout=600
"""

    def show_pipeline(self):
        print("=== CI/CD Pipeline ===")
        print(self.GITHUB_ACTIONS[:600])

    def test_matrix(self):
        print(f"\n=== Test Matrix ===")
        matrix = [
            {"cni": "Macvlan", "unit": "Pass", "integration": "Pass", "e2e": "Pass"},
            {"cni": "Bridge", "unit": "Pass", "integration": "Pass", "e2e": "Pass"},
            {"cni": "IPVLAN", "unit": "Pass", "integration": "Pass", "e2e": "Skip"},
            {"cni": "SR-IOV", "unit": "Pass", "integration": "Skip", "e2e": "Pass"},
        ]
        print(f"  {'CNI Plugin':<12} {'Unit':>6} {'Integration':>12} {'E2E':>6}")
        for m in matrix:
            print(f"  {m['cni']:<12} {m['unit']:>6} {m['integration']:>12} {m['e2e']:>6}")

cicd = CICDPipeline()
cicd.show_pipeline()
cicd.test_matrix()

FAQ - คำถามที่พบบ่อย

Q: Multus CNI จำเป็นเมื่อไหร่?

A: จำเป็นเมื่อ pod ต้องการ network interfaces มากกว่า 1: Telco/5G: แยก user plane กับ control plane, Storage network: แยก data path กับ management, NFV: multiple VLANs per pod, High-performance: SR-IOV สำหรับ line-rate networking ถ้า pod ใช้แค่ 1 network → ไม่ต้องใช้ Multus

Q: Test Multus ยากไหม?

A: Unit tests: ง่าย — validate config, schema Integration: ปานกลาง — ต้อง Kubernetes cluster + Multus installed E2E: ยาก — SR-IOV ต้อง hardware จริง (NIC ที่รองรับ) ใช้ Kind cluster สำหรับ Macvlan/Bridge tests (ไม่ต้อง hardware พิเศษ) SR-IOV tests: ต้อง self-hosted runners + physical NICs

Q: Performance ของ Multus เป็นอย่างไร?

A: Macvlan: ใกล้เคียง bare metal (~95% throughput), latency ต่ำ Bridge: ดี แต่มี overhead จาก software bridge SR-IOV: ดีที่สุด — bypass kernel, near line-rate (99%+) Pod startup: เพิ่ม 1-3 วินาที ต่อ additional interface Multus overhead เอง: น้อยมาก (< 100ms) — ส่วนใหญ่ขึ้นกับ CNI plugin ที่ใช้

Q: Troubleshoot Multus ยังไง?

A: 1) kubectl describe pod — ดู events สำหรับ network errors 2) kubectl logs -n kube-system ds/kube-multus-ds — ดู Multus logs 3) kubectl exec pod -- ip addr — ตรวจสอบ interfaces 4) kubectl get net-attach-def — ตรวจสอบ NAD configs 5) journalctl -u kubelet — ดู CNI plugin errors ที่ node level

📖 บทความที่เกี่ยวข้อง

Multus CNI Cloud Native Designอ่านบทความ → Multus CNI Pod Schedulingอ่านบทความ → Multus CNI Pub Sub Architectureอ่านบทความ → Multus CNI Data Pipeline ETLอ่านบทความ → Multus CNI High Availability HA Setupอ่านบทความ →

📚 ดูบทความทั้งหมด →