diff --git a/modules/python/clusterloader2/slo/config/ccnp_template.yaml b/modules/python/clusterloader2/slo/config/ccnp_template.yaml new file mode 100644 index 000000000..6279a7e71 --- /dev/null +++ b/modules/python/clusterloader2/slo/config/ccnp_template.yaml @@ -0,0 +1,14 @@ +apiVersion: cilium.io/v2 +kind: CiliumClusterwideNetworkPolicy +metadata: + name: {{.basename}} +spec: + endpointSelector: + matchLabels: + app: nginx + egress: + - toEndpoints: + - {} + ingress: + - fromEndpoints: + - {} \ No newline at end of file diff --git a/modules/python/clusterloader2/slo/config/cnp_template.yaml b/modules/python/clusterloader2/slo/config/cnp_template.yaml new file mode 100644 index 000000000..d542a07c3 --- /dev/null +++ b/modules/python/clusterloader2/slo/config/cnp_template.yaml @@ -0,0 +1,12 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: {{.basename}} + namespace: slo-1 +spec: + endpointSelector: + matchLabels: + app: nginx + egress: + - toEndpoints: + - {} \ No newline at end of file diff --git a/modules/python/clusterloader2/slo/config/deployment_template.yaml b/modules/python/clusterloader2/slo/config/deployment_template.yaml index 9e07175df..cc4fb0a79 100644 --- a/modules/python/clusterloader2/slo/config/deployment_template.yaml +++ b/modules/python/clusterloader2/slo/config/deployment_template.yaml @@ -1,5 +1,10 @@ {{$CpuRequest := DefaultParam .CpuRequest "5m"}} {{$MemoryRequest := DefaultParam .MemoryRequest "20M"}} +{{$cnpsPerNamespace := .cnpsPerNamespace}} +{{$ccnps := .ccnps}} +{{$cnp_test:= .cnp_test}} +{{$ccnp_test:= .ccnp_test}} +{{$dualstack := .dualstack}} {{$Image := DefaultParam .Image "mcr.microsoft.com/oss/kubernetes/pause:3.6"}} @@ -35,6 +40,23 @@ spec: nodeSelector: slo: "true" containers: +{{if or $cnp_test $ccnp_test}} + {{if $dualstack}} + - name: nginx + image: mcr.microsoft.com/mirror/docker/library/nginx:1.25 + command: ["sh", "-c", "while true; do curl ipv6.bing.com; sleep 6; done"] + ports: + - name: http + containerPort: 80 + {{else}} + - name: nginx + image: mcr.microsoft.com/mirror/docker/library/nginx:1.25 + command: ["sh", "-c", "while true; do curl ipv4.bing.com; sleep 6; done"] + ports: + - name: http + containerPort: 80 + {{end}} +{{else}} - env: - name: ENV_VAR value: a @@ -42,6 +64,7 @@ spec: imagePullPolicy: IfNotPresent name: {{.Name}} ports: +{{end}} resources: requests: cpu: {{$CpuRequest}} diff --git a/modules/python/clusterloader2/slo/config/load-config.yaml b/modules/python/clusterloader2/slo/config/load-config.yaml index 61ceacd1b..fa864dfa8 100644 --- a/modules/python/clusterloader2/slo/config/load-config.yaml +++ b/modules/python/clusterloader2/slo/config/load-config.yaml @@ -2,6 +2,8 @@ name: load-config # Config options for test type {{$SERVICE_TEST := DefaultParam .CL2_SERVICE_TEST true}} +{{$CNP_TEST := DefaultParam .CL2_CNP_TEST false}} +{{$CCNP_TEST := DefaultParam .CL2_CCNP_TEST false}} # Config options for test parameters {{$nodesPerNamespace := DefaultParam .CL2_NODES_PER_NAMESPACE 100}} @@ -34,6 +36,12 @@ name: load-config {{$smallDeploymentPods := SubtractInt $podsPerNamespace (MultiplyInt $bigDeploymentsPerNamespace $BIG_GROUP_SIZE)}} {{$smallDeploymentsPerNamespace := DivideInt $smallDeploymentPods $SMALL_GROUP_SIZE}} +# CNP & CCNP Test +{{$CNPS_PER_NAMESPACE := DefaultParam .CL2_CNPS_PER_NAMESPACE 0}} +{{$CCNPS := DefaultParam .CL2_CCNPS 0}} +{{$nameOfNs := "slo-1"}} +{{$DUALSTACK := DefaultParam .CL2_DUALSTACK false}} + namespace: number: {{$namespaces}} prefix: slo @@ -85,6 +93,24 @@ steps: bigServicesPerNamespace: {{$bigDeploymentsPerNamespace}} {{end}} +{{if $CNP_TEST}} + - module: + path: /modules/ciliumnetworkpolicy.yaml + params: + actionName: "Creating" + namespaces: {{$namespaces}} + nameOfNs: {{$nameOfNs}} + cnpsPerNamespace: {{$CNPS_PER_NAMESPACE}} +{{end}} + +{{if $CCNP_TEST}} + - module: + path: /modules/ciliumclusternetworkpolicy.yaml + params: + actionName: "Creating" + ccnps: {{$CCNPS}} +{{end}} + - module: path: /modules/reconcile-objects.yaml params: @@ -92,10 +118,20 @@ steps: namespaces: {{$namespaces}} tuningSet: DeploymentCreateQps operationTimeout: {{$operationTimeout}} + {{if or $CCNP_TEST $CNP_TEST}} + bigDeploymentSize: 0 + bigDeploymentsPerNamespace: 0 + {{else}} bigDeploymentSize: {{$BIG_GROUP_SIZE}} bigDeploymentsPerNamespace: {{$bigDeploymentsPerNamespace}} + {{end}} smallDeploymentSize: {{$SMALL_GROUP_SIZE}} smallDeploymentsPerNamespace: {{$smallDeploymentsPerNamespace}} + cnpsPerNamespace: {{$CNPS_PER_NAMESPACE}} + ccnps: {{$CCNPS}} + cnp_test: {{$CNP_TEST}} + ccnp_test: {{$CCNP_TEST}} + dualstack: {{$DUALSTACK}} CpuRequest: {{$latencyPodCpu}}m MemoryRequest: {{$latencyPodMemory}}M Group: {{$groupName}} @@ -108,10 +144,20 @@ steps: namespaces: {{$namespaces}} tuningSet: Sequence operationTimeout: {{$operationTimeout}} + {{if or $CCNP_TEST $CNP_TEST}} + bigDeploymentSize: 0 + bigDeploymentsPerNamespace: 0 + {{else}} bigDeploymentSize: {{$BIG_GROUP_SIZE}} bigDeploymentsPerNamespace: {{$bigDeploymentsPerNamespace}} + {{end}} smallDeploymentSize: {{$SMALL_GROUP_SIZE}} smallDeploymentsPerNamespace: {{$smallDeploymentsPerNamespace}} + cnpsPerNamespace: {{$CNPS_PER_NAMESPACE}} + ccnps: {{$CCNPS}} + cnp_test: {{$CNP_TEST}} + ccnp_test: {{$CCNP_TEST}} + dualstack: {{$DUALSTACK}} CpuRequest: {{$latencyPodCpu}}m MemoryRequest: {{$latencyPodMemory}}M Group: {{$groupName}} @@ -124,13 +170,18 @@ steps: namespaces: {{$namespaces}} tuningSet: DeploymentDeleteQps operationTimeout: {{$operationTimeout}} + {{if or $CCNP_TEST $CNP_TEST}} + bigDeploymentSize: 0 + bigDeploymentsPerNamespace: 0 + {{else}} bigDeploymentSize: {{$BIG_GROUP_SIZE}} bigDeploymentsPerNamespace: 0 + {{end}} smallDeploymentSize: {{$SMALL_GROUP_SIZE}} smallDeploymentsPerNamespace: 0 deploymentLabel: restart Group: {{$groupName}} - +{{if $SERVICE_TEST}} - module: path: /modules/services.yaml params: @@ -139,6 +190,23 @@ steps: smallServicesPerNamespace: 0 bigServicesPerNamespace: 0 {{end}} +{{if or $CCNP_TEST $CNP_TEST}} + - module: + path: /modules/ciliumnetworkpolicy.yaml + params: + actionName: "Deleting" + namespaces: {{$namespaces}} + nameOfNs: {{$nameOfNs}} + cnpsPerNamespace: 0 + + - module: + path: /modules/ciliumclusternetworkpolicy.yaml + params: + actionName: "Deleting" + namespaces: {{$namespaces}} + ccnps: 0 +{{end}} +{{end}} {{if $CILIUM_METRICS_ENABLED}} - module: diff --git a/modules/python/clusterloader2/slo/config/modules/ciliumclusternetworkpolicy.yaml b/modules/python/clusterloader2/slo/config/modules/ciliumclusternetworkpolicy.yaml new file mode 100644 index 000000000..084d4c349 --- /dev/null +++ b/modules/python/clusterloader2/slo/config/modules/ciliumclusternetworkpolicy.yaml @@ -0,0 +1,15 @@ +## CCNP module provides a module for creating / deleting CCNPs. + +## Input params +{{$actionName := .actionName}} +{{$ccnps := .ccnps}} + +steps: +- name: "{{$actionName}} k8s CCNPs" + phases: + - namespaceRange: null + replicasPerNamespace: {{$ccnps}} + tuningSet: Sequence + objectBundle: + - basename: ccnp + objectTemplatePath: ccnp_template.yaml \ No newline at end of file diff --git a/modules/python/clusterloader2/slo/config/modules/ciliumnetworkpolicy.yaml b/modules/python/clusterloader2/slo/config/modules/ciliumnetworkpolicy.yaml new file mode 100644 index 000000000..9ca0f3fc0 --- /dev/null +++ b/modules/python/clusterloader2/slo/config/modules/ciliumnetworkpolicy.yaml @@ -0,0 +1,20 @@ +## CNP module provides a module for creating / deleting CNPs. + +## Input params +{{$actionName := .actionName}} +{{$namespaces := .namespaces}} +{{$cnpsPerNamespace := .cnpsPerNamespace}} +{{$nameOfNs := .nameOfNs}} + +steps: +- name: "{{$actionName}} k8s CNPs" + phases: + - namespaceRange: + min: 1 + max: {{$namespaces}} + replicasPerNamespace: {{$cnpsPerNamespace}} + tuningSet: Sequence + objectBundle: + - basename: cnp + objectTemplatePath: cnp_template.yaml + \ No newline at end of file diff --git a/modules/python/clusterloader2/slo/config/modules/reconcile-objects.yaml b/modules/python/clusterloader2/slo/config/modules/reconcile-objects.yaml index e169dd699..aaee52173 100644 --- a/modules/python/clusterloader2/slo/config/modules/reconcile-objects.yaml +++ b/modules/python/clusterloader2/slo/config/modules/reconcile-objects.yaml @@ -14,6 +14,12 @@ {{$smallDeploymentSize := .smallDeploymentSize}} {{$smallDeploymentsPerNamespace := .smallDeploymentsPerNamespace}} +{{$cnpsPerNamespace := .cnpsPerNamespace}} +{{$ccnps := .ccnps}} +{{$cnp_test:= .cnp_test}} +{{$ccnp_test:= .ccnp_test}} +{{$dualstack:= .dualstack}} + steps: - name: Starting measurement for '{{$actionName}}' measurements: @@ -55,7 +61,13 @@ steps: objectTemplatePath: deployment_template.yaml templateFillMap: Replicas: {{$smallDeploymentSize}} + {{if or $cnp_test $ccnp_test}} + cnp_test: {{$cnp_test}} + ccnp_test: {{$ccnp_test}} + dualstack: {{$dualstack}} + {{else}} SvcName: small-service + {{end}} Group: {{.Group}} deploymentLabel: {{.deploymentLabel}} diff --git a/modules/python/clusterloader2/slo/slo.py b/modules/python/clusterloader2/slo/slo.py index c6fe4af7c..f4808b484 100644 --- a/modules/python/clusterloader2/slo/slo.py +++ b/modules/python/clusterloader2/slo/slo.py @@ -24,7 +24,7 @@ } # TODO: Remove aks once CL2 update provider name to be azure -def calculate_config(cpu_per_node, node_count, provider, service_test): +def calculate_config(cpu_per_node, node_count, pods_in_node, provider, service_test, cnp_test, ccnp_test): throughput = 100 nodes_per_namespace = min(node_count, DEFAULT_NODES_PER_NAMESPACE) @@ -32,6 +32,8 @@ def calculate_config(cpu_per_node, node_count, provider, service_test): if service_test: pods_per_node = LOAD_PODS_PER_NODE + if cnp_test or ccnp_test: + pods_per_node = pods_in_node # Different cloud has different reserved values and number of daemonsets # Using the same percentage will lead to incorrect nodes number as the number of nodes grow # For AWS, see: https://github.com/awslabs/amazon-eks-ami/blob/main/templates/al2/runtime/bootstrap.sh#L290 @@ -47,15 +49,21 @@ def configure_clusterloader2( node_count, node_per_step, max_pods, + pods_in_node, repeats, operation_timeout, provider, cilium_enabled, service_test, + cnp_test, + ccnp_test, + num_cnps, + num_ccnps, + dualstack, override_file): steps = node_count // node_per_step - throughput, nodes_per_namespace, pods_per_node, cpu_request = calculate_config(cpu_per_node, node_per_step, provider, service_test) + throughput, nodes_per_namespace, pods_per_node, cpu_request = calculate_config(cpu_per_node, node_per_step, pods_in_node, provider, service_test, cnp_test, ccnp_test) with open(override_file, 'w') as file: file.write(f"CL2_LOAD_TEST_THROUGHPUT: {throughput}\n") @@ -81,6 +89,20 @@ def configure_clusterloader2( if service_test: file.write("CL2_SERVICE_TEST: true\n") + else: + file.write("CL2_SERVICE_TEST: false\n") + + if cnp_test: + file.write("CL2_CNP_TEST: true\n") + file.write(f"CL2_CNPS_PER_NAMESPACE: {num_cnps}\n") + file.write(f"CL2_DUALSTACK: {dualstack}\n") + file.write("CL2_GROUP_NAME: cnp-ccnp\n") + + if ccnp_test: + file.write("CL2_CCNP_TEST: true\n") + file.write(f"CL2_CCNPS: {num_ccnps}\n") + file.write(f"CL2_DUALSTACK: {dualstack}\n") + file.write("CL2_GROUP_NAME: cnp-ccnp\n") with open(override_file, 'r') as file: print(f"Content of file {override_file}:\n{file.read()}") @@ -109,12 +131,18 @@ def collect_clusterloader2( cpu_per_node, node_count, max_pods, + pods_in_node, repeats, cl2_report_dir, cloud_info, run_id, run_url, service_test, + cnp_test, + ccnp_test, + num_cnps, + num_ccnps, + dualstack, result_file, test_type="default_config", ): @@ -128,7 +156,7 @@ def collect_clusterloader2( else: raise Exception(f"No testsuites found in the report! Raw data: {details}") - _, _, pods_per_node, _ = calculate_config(cpu_per_node, node_count, provider, service_test) + _, _, pods_per_node, _ = calculate_config(cpu_per_node, node_count, pods_in_node, provider, service_test, cnp_test, ccnp_test) pod_count = node_count * pods_per_node # TODO: Expose optional parameter to include test details @@ -192,6 +220,7 @@ def main(): parser_configure.add_argument("node_count", type=int, help="Number of nodes") parser_configure.add_argument("node_per_step", type=int, help="Number of nodes per scaling step") parser_configure.add_argument("max_pods", type=int, help="Maximum number of pods per node") + parser_configure.add_argument("pods_in_node", type=int, nargs='?', default=0, help="Number of pods per node") parser_configure.add_argument("repeats", type=int, help="Number of times to repeat the deployment churn") parser_configure.add_argument("operation_timeout", type=str, help="Timeout before failing the scale up test") parser_configure.add_argument("provider", type=str, help="Cloud provider name") @@ -199,6 +228,14 @@ def main(): help="Whether cilium is enabled. Must be either True or False") parser_configure.add_argument("service_test", type=eval, choices=[True, False], default=False, help="Whether service test is running. Must be either True or False") + parser_configure.add_argument("cnp_test", type=eval, choices=[True, False], nargs='?', default=False, + help="Whether cnp test is running. Must be either True or False") + parser_configure.add_argument("ccnp_test", type=eval, choices=[True, False], nargs='?', default=False, + help="Whether ccnp test is running. Must be either True or False") + parser_configure.add_argument("num_cnps", type=int, nargs='?', default=0, help="Number of cnps") + parser_configure.add_argument("num_ccnps", type=int, nargs='?', default=0, help="Number of ccnps") + parser_configure.add_argument("dualstack", type=eval, choices=[True, False], nargs='?', default=False, + help="Whether cluster is dualstack. Must be either True or False") parser_configure.add_argument("cl2_override_file", type=str, help="Path to the overrides of CL2 config file") # Sub-command for validate_clusterloader2 @@ -220,6 +257,7 @@ def main(): parser_collect.add_argument("cpu_per_node", type=int, help="CPU per node") parser_collect.add_argument("node_count", type=int, help="Number of nodes") parser_collect.add_argument("max_pods", type=int, help="Maximum number of pods per node") + parser_collect.add_argument("pods_in_node", type=int, nargs='?', default=0, help="Number of pods per node") parser_collect.add_argument("repeats", type=int, help="Number of times to repeat the deployment churn") parser_collect.add_argument("cl2_report_dir", type=str, help="Path to the CL2 report directory") parser_collect.add_argument("cloud_info", type=str, help="Cloud information") @@ -227,25 +265,33 @@ def main(): parser_collect.add_argument("run_url", type=str, help="Run URL") parser_collect.add_argument("service_test", type=eval, choices=[True, False], default=False, help="Whether service test is running. Must be either True or False") + parser_collect.add_argument("cnp_test", type=eval, choices=[True, False], nargs='?', default=False, + help="Whether cnp test is running. Must be either True or False") + parser_collect.add_argument("ccnp_test", type=eval, choices=[True, False], nargs='?', default=False, + help="Whether ccnp test is running. Must be either True or False") + parser_collect.add_argument("num_cnps", type=int, nargs='?', default=0, help="Number of cnps") + parser_collect.add_argument("num_ccnps", type=int, nargs='?', default=0, help="Number of ccnps") + parser_collect.add_argument("dualstack", type=eval, choices=[True, False], nargs='?', default=False, + help="Whether cluster is dualstack. Must be either True or False") parser_collect.add_argument("result_file", type=str, help="Path to the result file") parser_collect.add_argument("test_type", type=str, nargs='?', default="default-config", help="Description of test type") args = parser.parse_args() - + if args.command == "configure": configure_clusterloader2(args.cpu_per_node, args.node_count, args.node_per_step, args.max_pods, - args.repeats, args.operation_timeout, args.provider, args.cilium_enabled, - args.service_test, args.cl2_override_file) + args.pods_in_node, args.repeats, args.operation_timeout, args.provider, args.cilium_enabled, + args.service_test, args.cnp_test, args.ccnp_test, args.num_cnps, args.num_ccnps, args.dualstack, args.cl2_override_file) elif args.command == "validate": validate_clusterloader2(args.node_count, args.operation_timeout) elif args.command == "execute": execute_clusterloader2(args.cl2_image, args.cl2_config_dir, args.cl2_report_dir, args.cl2_config_file, args.kubeconfig, args.provider) elif args.command == "collect": - collect_clusterloader2(args.cpu_per_node, args.node_count, args.max_pods, args.repeats, + collect_clusterloader2(args.cpu_per_node, args.node_count, args.max_pods, args.pods_in_node, args.repeats, args.cl2_report_dir, args.cloud_info, args.run_id, args.run_url, - args.service_test, args.result_file, args.test_type) + args.service_test, args.cnp_test, args.ccnp_test, args.num_cnps, args.num_ccnps, args.dualstack, args.result_file, args.test_type) if __name__ == "__main__": main() diff --git a/pipelines/perf-eval/CNI Benchmark/cnp-ccnp-feature.yml b/pipelines/perf-eval/CNI Benchmark/cnp-ccnp-feature.yml new file mode 100644 index 000000000..45a2a9fb9 --- /dev/null +++ b/pipelines/perf-eval/CNI Benchmark/cnp-ccnp-feature.yml @@ -0,0 +1,44 @@ +trigger: none + +variables: + SCENARIO_TYPE: perf-eval + SCENARIO_NAME: cnp-ccnp-feature + SCENARIO_VERSION: main + OWNER: aks + +stages: + - stage: azure_eastus2 + dependsOn: [] + jobs: + - template: /jobs/competitive-test.yml + parameters: + cloud: azure + regions: + - $(LOCATION) + engine: clusterloader2 + engine_input: + image: "ghcr.io/azure/clusterloader2:v20241022" + topology: cilium-usercluster + matrix: + azure_cilium: + cpu_per_node: 4 + node_count: $(NODES) + node_per_step: $(STEP_NODES) + max_pods: 110 + pods_in_node: $(PODS) + repeats: 1 + scale_timeout: "15m" + cilium_enabled: True + network_policy: cilium + network_dataplane: cilium + service_test: False + cnp_test: $(CNP) + ccnp_test: $(CCNP) + num_cnps: $(CNPS_NUM) + num_ccnps: $(CCNPS_NUM) + dualstack: $(DUAL) + cl2_config_file: load-config.yaml + max_parallel: 2 + timeout_in_minutes: 720 + credential_type: service_connection + ssh_key_enabled: false diff --git a/steps/engine/clusterloader2/slo/collect.yml b/steps/engine/clusterloader2/slo/collect.yml index 2cb5d22cf..1cf06b246 100644 --- a/steps/engine/clusterloader2/slo/collect.yml +++ b/steps/engine/clusterloader2/slo/collect.yml @@ -16,8 +16,8 @@ steps: set -eo pipefail PYTHONPATH=$PYTHONPATH:$(pwd) python3 $PYTHON_SCRIPT_FILE collect \ - $CPU_PER_NODE $NODE_COUNT $MAX_PODS $REPEATS \ - $CL2_REPORT_DIR "$CLOUD_INFO" $RUN_ID $RUN_URL $SERVICE_TEST $TEST_RESULTS_FILE \ + $CPU_PER_NODE $NODE_COUNT $MAX_PODS ${PODS_IN_NODE:-0} \ + $REPEATS $CL2_REPORT_DIR "$CLOUD_INFO" $RUN_ID $RUN_URL $SERVICE_TEST ${CNP_TEST:-False} ${CCNP_TEST:-False} ${NUM_CNPS:-0} ${NUM_CCNPS:-0} ${DUALSTACK:-False} $TEST_RESULTS_FILE \ $TEST_TYPE workingDirectory: modules/python/clusterloader2 env: diff --git a/steps/engine/clusterloader2/slo/execute.yml b/steps/engine/clusterloader2/slo/execute.yml index b317c8a00..1a4c05b7c 100644 --- a/steps/engine/clusterloader2/slo/execute.yml +++ b/steps/engine/clusterloader2/slo/execute.yml @@ -13,9 +13,9 @@ steps: set -eo pipefail PYTHONPATH=$PYTHONPATH:$(pwd) python3 $PYTHON_SCRIPT_FILE configure \ - $CPU_PER_NODE $NODE_COUNT $NODE_PER_STEP $MAX_PODS \ + $CPU_PER_NODE $NODE_COUNT $NODE_PER_STEP $MAX_PODS ${PODS_IN_NODE:-0} \ $REPEATS $SCALE_TIMEOUT $CLOUD $CILIUM_ENABLED \ - $SERVICE_TEST ${CL2_CONFIG_DIR}/overrides.yaml + $SERVICE_TEST ${CNP_TEST:-False} ${CCNP_TEST:-False} ${NUM_CNPS:-0} ${NUM_CCNPS:-0} ${DUALSTACK:-False} ${CL2_CONFIG_DIR}/overrides.yaml PYTHONPATH=$PYTHONPATH:$(pwd) python3 $PYTHON_SCRIPT_FILE execute \ ${CL2_IMAGE} ${CL2_CONFIG_DIR} $CL2_REPORT_DIR $CL2_CONFIG_FILE \ ${HOME}/.kube/config $CLOUD