k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/common/dns/dns_performance-k8s-hostnames.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package dns 18 19 import ( 20 "context" 21 "embed" 22 "fmt" 23 "time" 24 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 clientset "k8s.io/client-go/kubernetes" 27 "k8s.io/klog/v2" 28 "k8s.io/perf-tests/clusterloader2/pkg/framework" 29 "k8s.io/perf-tests/clusterloader2/pkg/framework/client" 30 "k8s.io/perf-tests/clusterloader2/pkg/measurement" 31 "k8s.io/perf-tests/clusterloader2/pkg/util" 32 ) 33 34 /* 35 The DNS Performance test for K8s Hostnames creates the required permissions for 36 DNS client pods to get K8s hostnames (from services and endpoints). Then DNS 37 client deployment is created that uses dnsperfgo image 38 (https://github.com/kubernetes/perf-tests/tree/master/dns/dnsperfgo) to query 39 the hostnames within the cluster and generate Prometheus metrics for number of 40 errors, timeouts and their latencies. Outside this measurement, the metrics are 41 scraped and results verified to not cross the specified thresholds. 42 */ 43 44 const ( 45 dnsPerfK8sHostnamesMeasureName = "DNSPerformanceK8sHostnames" 46 dnsPerfTestNamespace = "dns-perf-test" 47 dnsPerfTestPermissionsName = "dns-test-client" 48 serviceAccountFilePath = "manifests/serviceaccount.yaml" 49 clusterRoleFilePath = "manifests/clusterrole.yaml" 50 clusterRoleBindingFilePath = "manifests/clusterrolebinding.yaml" 51 clientDeploymentFilePath = "manifests/dns-client.yaml" 52 ) 53 54 //go:embed manifests 55 var manifestsFS embed.FS 56 57 func init() { 58 klog.Info("Registering measurement: DNS Performance for K8s Hostnames") 59 if err := measurement.Register(dnsPerfK8sHostnamesMeasureName, createDNSPerfK8sHostnamesMeasurement); err != nil { 60 klog.Fatalf("Cannot register %s: %v", dnsPerfK8sHostnamesMeasureName, err) 61 } 62 } 63 64 func createDNSPerfK8sHostnamesMeasurement() measurement.Measurement { 65 return &dnsPerfK8sHostnamesMeasurement{} 66 } 67 68 type dnsPerfK8sHostnamesMeasurement struct { 69 k8sClient clientset.Interface 70 framework *framework.Framework 71 // testClientNamespace is the new namespace where the dns clients are going to 72 // be deployed. It's cleaned up when the test finishes. 73 testClientNamespace string 74 // podReplicas is the number of DNS client pods to be deployed. 75 podReplicas int 76 // qpsPerClient is the number of DNS queries each DNS client should send per 77 // second. 78 qpsPerClient int 79 // testDurationMinutes is the duration in minutes for DNS client pods to run. 80 testDurationMinutes int 81 } 82 83 func (m *dnsPerfK8sHostnamesMeasurement) Execute(config *measurement.Config) ([]measurement.Summary, error) { 84 err := m.initializeMeasurement(config) 85 if err != nil { 86 return nil, fmt.Errorf("failed to initialize the measurement: %v", err) 87 } 88 89 if err = client.CreateNamespace(m.k8sClient, m.testClientNamespace); err != nil { 90 return nil, fmt.Errorf("error while creating namespace: %v", err) 91 } 92 93 if err = m.createDNSClientPermissions(); err != nil { 94 return nil, fmt.Errorf("failed to create dns client permission resources: %v", err) 95 } 96 97 if err = m.createDNSClientDeployment(); err != nil { 98 return nil, fmt.Errorf("failed to create DNS client deployment: %v", err) 99 } 100 101 // Keep running the dns clients for the specified duration. 102 runDuration := time.Duration(m.testDurationMinutes) * time.Minute 103 klog.Infof("DNS tests are going to run for %v", runDuration) 104 time.Sleep(runDuration) 105 106 return nil, m.cleanUp() 107 } 108 109 func (m *dnsPerfK8sHostnamesMeasurement) initializeMeasurement(config *measurement.Config) error { 110 if m.framework != nil { 111 klog.Warningf("The %q was already started, but running it again", dnsPerfK8sHostnamesMeasureName) 112 } 113 114 var err error 115 if m.testClientNamespace, err = util.GetStringOrDefault(config.Params, "testNamespace", dnsPerfTestNamespace); err != nil { 116 return err 117 } 118 119 if m.podReplicas, err = util.GetIntOrDefault(config.Params, "podReplicas", 10); err != nil { 120 return err 121 } 122 123 if m.qpsPerClient, err = util.GetIntOrDefault(config.Params, "qpsPerClient", 10); err != nil { 124 return err 125 } 126 127 if m.testDurationMinutes, err = util.GetIntOrDefault(config.Params, "testDurationMinutes", 10); err != nil { 128 return err 129 } 130 131 m.framework = config.ClusterFramework 132 m.k8sClient = config.ClusterFramework.GetClientSets().GetClient() 133 134 return nil 135 } 136 137 // createDNSClientPermissions creates ServiceAccount, ClusterRole and 138 // ClusterRoleBinding for the test client pods to access Services and Endpoints. 139 func (m *dnsPerfK8sHostnamesMeasurement) createDNSClientPermissions() error { 140 templateMap := map[string]interface{}{ 141 "Name": dnsPerfTestPermissionsName, 142 "Namespace": m.testClientNamespace, 143 } 144 145 if err := m.framework.ApplyTemplatedManifests(manifestsFS, serviceAccountFilePath, templateMap); err != nil { 146 return fmt.Errorf("error while creating serviceaccount: %v", err) 147 } 148 149 if err := m.framework.ApplyTemplatedManifests(manifestsFS, clusterRoleFilePath, templateMap); err != nil { 150 return fmt.Errorf("error while creating clusterrole: %v", err) 151 } 152 153 if err := m.framework.ApplyTemplatedManifests(manifestsFS, clusterRoleBindingFilePath, templateMap); err != nil { 154 return fmt.Errorf("error while creating clusterrolebinding: %v", err) 155 } 156 157 return nil 158 } 159 160 func (m *dnsPerfK8sHostnamesMeasurement) createDNSClientDeployment() error { 161 templateMap := map[string]interface{}{ 162 "Namespace": m.testClientNamespace, 163 "PodReplicas": m.podReplicas, 164 "QPSPerClient": m.qpsPerClient, 165 "ServiceAccountName": dnsPerfTestPermissionsName, 166 } 167 168 return m.framework.ApplyTemplatedManifests(manifestsFS, clientDeploymentFilePath, templateMap) 169 } 170 171 func (m *dnsPerfK8sHostnamesMeasurement) deleteDNSClientPermissions() error { 172 klog.Infof("Deleting DNS client permission resources for measurement %q", dnsPerfK8sHostnamesMeasureName) 173 174 if err := m.k8sClient.CoreV1().ServiceAccounts(m.testClientNamespace).Delete(context.TODO(), dnsPerfTestPermissionsName, metav1.DeleteOptions{}); err != nil { 175 return err 176 } 177 178 if err := m.k8sClient.RbacV1().ClusterRoles().Delete(context.TODO(), dnsPerfTestPermissionsName, metav1.DeleteOptions{}); err != nil { 179 return err 180 } 181 182 return m.k8sClient.RbacV1().ClusterRoleBindings().Delete(context.TODO(), dnsPerfTestPermissionsName, metav1.DeleteOptions{}) 183 } 184 185 func (m *dnsPerfK8sHostnamesMeasurement) cleanUp() error { 186 if m.framework == nil { 187 klog.Warning("Cleanup skipped. The measurement is not running") 188 return nil 189 } 190 191 if err := m.deleteDNSClientPermissions(); err != nil { 192 return err 193 } 194 195 klog.Infof("Deleting namespace %q for measurement %q", m.testClientNamespace, dnsPerfK8sHostnamesMeasureName) 196 return m.k8sClient.CoreV1().Namespaces().Delete(context.TODO(), m.testClientNamespace, metav1.DeleteOptions{}) 197 } 198 199 // String returns a string representation of the measurement. 200 func (m *dnsPerfK8sHostnamesMeasurement) String() string { 201 return dnsPerfK8sHostnamesMeasureName 202 } 203 204 // Dispose cleans up after the measurement. 205 func (m *dnsPerfK8sHostnamesMeasurement) Dispose() { 206 if err := m.cleanUp(); err != nil { 207 klog.Infof("Cleanup failed: %v", err) 208 } 209 }