1414"""Runs wrk2 clients against replicated nginx servers behind a load balancer."""
1515
1616import functools
17+ import logging
1718import os
1819import shutil
1920import tempfile
21+ import time
2022
2123from absl import flags
2224from perfkitbenchmarker import background_tasks
2325from perfkitbenchmarker import configs
2426from perfkitbenchmarker import data
27+ from perfkitbenchmarker import errors
2528from perfkitbenchmarker .linux_benchmarks import nginx_benchmark
26- from perfkitbenchmarker .resources .container_service import kubernetes_commands
2729
2830FLAGS = flags .FLAGS
2931
32+ flags .DEFINE_string (
33+ 'nginx_conf' ,
34+ None ,
35+ 'Path to a custom nginx configuration file.' ,
36+ )
37+
3038flags .DEFINE_string (
3139 'kubernetes_nginx_runtime_class_name' ,
3240 None ,
4856 vm_spec: *default_dual_core
4957 nodepools:
5058 nginx:
51- vm_count: 3
59+ vm_count: 1
60+ vm_spec:
61+ GCP:
62+ machine_type: n2-standard-4
63+ AWS:
64+ machine_type: m6i.xlarge
65+ Azure:
66+ machine_type: Standard_D4s_v5
67+ upstream:
68+ vm_count: 2
5269 vm_spec:
5370 GCP:
5471 machine_type: n2-standard-4
@@ -95,49 +112,159 @@ def GetConfig(user_config):
95112 return config
96113
97114
115+ def _MergeNginxConfigs (global_conf_path , server_conf_path , force_http = False ):
116+ """Merges global and server nginx configs into a single file."""
117+ with open (global_conf_path ) as f :
118+ global_conf = f .read ()
119+ with open (server_conf_path ) as f :
120+ server_conf = f .read ()
121+
122+ # Replace placeholder with K8s upstream service DNS
123+ # Upstream service name is 'nginx-upstream' in 'default' namespace
124+ upstream_dns = 'nginx-upstream.default.svc.cluster.local'
125+ # Hardcode Upstream Port to 80 (Profile 2 / Single TLS Parity)
126+ upstream_port = 80
127+
128+ # The placeholder in rp_apigw.conf includes :443;
129+ # We replace the first occurrence (fileserver_1)
130+ server_conf = server_conf .replace (
131+ '# server <fileserver_1_ip_or_dns>:443;' ,
132+ f'server { upstream_dns } :{ upstream_port } ;' ,
133+ )
134+
135+ if (not FLAGS .nginx_use_ssl ) or force_http :
136+ # Convert HTTPS config to HTTP
137+ server_conf = server_conf .replace ('ssl on;' , '# ssl on;' )
138+ server_conf = server_conf .replace ('ssl_certificate' , '# ssl_certificate' )
139+ server_conf = server_conf .replace ('ssl_ciphers' , '# ssl_ciphers' )
140+ server_conf = server_conf .replace ('listen 443 ssl' , 'listen 80' )
141+
142+ # ALWAYS force upstream connection to HTTP (Profile 2)
143+ # This ensures Proxy -> Upstream is unencrypted even if Proxy listener is HTTPS
144+ server_conf = server_conf .replace ('proxy_pass https://' , 'proxy_pass http://' )
145+
146+ # Simple merge: replace the include line with the actual content
147+ # global.conf has: include /etc/nginx/conf.d/*.conf;
148+ merged_conf = global_conf .replace (
149+ 'include /etc/nginx/conf.d/*.conf;' , server_conf
150+ )
151+ return merged_conf
152+
153+
98154def _CreateNginxConfigMapDir ():
99155 """Returns a TemporaryDirectory containing files in the Nginx ConfigMap."""
156+ temp_dir = tempfile .TemporaryDirectory ()
157+
158+ # 1. Get paths to source config files
159+ global_conf_path = data .ResourcePath ('nginx/global.conf' )
160+ proxy_conf_path = data .ResourcePath ('nginx/rp_apigw.conf' )
161+ upstream_conf_path = data .ResourcePath ('nginx/file_server.conf' )
162+
100163 if FLAGS .nginx_conf :
101- nginx_conf_filename = FLAGS .nginx_conf
102- else :
103- relative_nginx_conf_filename = 'container/kubernetes_nginx/http.conf'
104- if FLAGS .nginx_use_ssl :
105- relative_nginx_conf_filename = 'container/kubernetes_nginx/https.conf'
106- nginx_conf_filename = data .ResourcePath (relative_nginx_conf_filename )
164+ # If custom config provided, use it for proxy (legacy behavior support)
165+ # But for 3-tier, we really need the split configs.
166+ # For now, let's assume nginx_conf overrides the proxy config if given.
167+ proxy_conf_path = FLAGS .nginx_conf
168+
169+ # 2. Prepare Proxy Config (global + rp_apigw)
170+ proxy_conf_content = _MergeNginxConfigs (global_conf_path , proxy_conf_path )
171+ with open (os .path .join (temp_dir .name , 'nginx-proxy.conf' ), 'w' ) as f :
172+ f .write (proxy_conf_content )
173+
174+ # 3. Prepare Upstream Config (global + file_server)
175+ # Force HTTP for Upstream (Profile 2) similar to the GCE fix
176+ upstream_conf_content = _MergeNginxConfigs (
177+ global_conf_path , upstream_conf_path , force_http = True
178+ )
179+ with open (os .path .join (temp_dir .name , 'nginx-upstream.conf' ), 'w' ) as f :
180+ f .write (upstream_conf_content )
107181
108- temp_dir = tempfile .TemporaryDirectory ()
109- config_map_filename = os .path .join (temp_dir .name , 'default' )
110- shutil .copyfile (nginx_conf_filename , config_map_filename )
111182 return temp_dir
112183
113184
185+ def _WaitForConnectivity (benchmark_spec ):
186+ """Waits for the proxy to be reachable."""
187+ lb_ip = benchmark_spec .nginx_endpoint_ip
188+ logging .info ('Waiting for connectivity to %s...' , lb_ip )
189+
190+ # Try to connect to the endpoint
191+ scheme = 'https' if FLAGS .nginx_use_ssl else 'http'
192+ url = f'{ scheme } ://{ lb_ip } /'
193+
194+ # Use curl to check connectivity
195+ cmd = f'curl -k -v { url } '
196+
197+ # Retry for up to 5 minutes
198+ start_time = time .time ()
199+ while time .time () - start_time < 300 :
200+ try :
201+ # We run this from the client VM to verify end-to-end connectivity
202+ benchmark_spec .vm_groups ['clients' ][0 ].RemoteCommand (cmd )
203+ logging .info ('Connectivity established.' )
204+ return
205+ except errors .VirtualMachine .RemoteCommandError :
206+ logging .info ('Still waiting for connectivity...' )
207+ time .sleep (10 )
208+
209+ raise errors .Benchmarks .PrepareException (
210+ f'Timed out waiting for connectivity to { url } '
211+ )
212+
213+
114214def _PrepareCluster (benchmark_spec ):
115215 """Prepares a cluster to run the Nginx benchmark."""
216+ # 1. Create ConfigMap
116217 with _CreateNginxConfigMapDir () as nginx_config_map_dirname :
117218 benchmark_spec .container_cluster .CreateConfigMap (
118- 'default-config ' , nginx_config_map_dirname
219+ 'nginx-configs ' , nginx_config_map_dirname
119220 )
120- container_image = benchmark_spec .container_specs ['kubernetes_nginx' ].image
121- replicas = benchmark_spec .container_cluster .nodepools ['nginx' ].num_nodes
122221
123- nginx_port = 80
124- if FLAGS .nginx_use_ssl :
125- nginx_port = 443
222+ container_image = benchmark_spec . container_specs [ 'kubernetes_nginx' ]. image
223+ proxy_port = 443 if FLAGS .nginx_use_ssl else 80
224+ upstream_port = 80 # Hardcoded for Profile 2
126225
127- kubernetes_commands .ApplyManifest (
128- 'container/kubernetes_nginx/kubernetes_nginx.yaml.j2' ,
226+ # 2. Deploy Upstream
227+ # We use the 'upstream' nodepool
228+ upstream_replicas = benchmark_spec .container_cluster .nodepools [
229+ 'upstream'
230+ ].num_nodes
231+ benchmark_spec .container_cluster .ApplyManifest (
232+ 'container/kubernetes_nginx/nginx_upstream.yaml.j2' ,
129233 nginx_image = container_image ,
130- nginx_replicas = replicas ,
234+ nginx_upstream_replicas = upstream_replicas ,
131235 nginx_content_size = FLAGS .nginx_content_size ,
132- nginx_port = nginx_port ,
133- nginx_worker_connections = FLAGS .nginx_worker_connections ,
236+ nginx_port = upstream_port ,
134237 runtime_class_name = FLAGS .kubernetes_nginx_runtime_class_name ,
135238 )
136239
240+ # 3. Deploy Proxy
241+ # We use the 'nginx' nodepool (renamed to proxy in our minds, but key is 'nginx')
242+ proxy_replicas = benchmark_spec .container_cluster .nodepools ['nginx' ].num_nodes
243+ benchmark_spec .container_cluster .ApplyManifest (
244+ 'container/kubernetes_nginx/nginx_proxy.yaml.j2' ,
245+ nginx_image = container_image ,
246+ nginx_proxy_replicas = proxy_replicas ,
247+ nginx_port = proxy_port ,
248+ runtime_class_name = FLAGS .kubernetes_nginx_runtime_class_name ,
249+ )
250+
251+ # 4. Wait for deployments
252+ benchmark_spec .container_cluster .WaitForResource (
253+ 'deploy/nginx-upstream-deployment' , 'available'
254+ )
137255 benchmark_spec .container_cluster .WaitForResource (
138- 'deploy/nginx-deployment' , 'available'
256+ 'deploy/nginx-proxy- deployment' , 'available'
139257 )
140258
259+ # 5. Get LoadBalancer IP
260+ # Get LoadBalancer IP using PKB's built-in retry method
261+ benchmark_spec .nginx_endpoint_ip = (
262+ benchmark_spec .container_cluster .GetLoadBalancerIP ('nginx-cluster' )
263+ )
264+
265+ # 6. Wait for connectivity
266+ _WaitForConnectivity (benchmark_spec )
267+
141268
142269def Prepare (benchmark_spec ):
143270 """Install Nginx on the K8s Cluster and a load generator on the clients.
@@ -154,10 +281,7 @@ def Prepare(benchmark_spec):
154281
155282 background_tasks .RunThreaded (lambda f : f (), prepare_fns )
156283
157- benchmark_spec .nginx_endpoint_ip = (
158- benchmark_spec .container_cluster .GetClusterIP ('nginx-cluster' )
159- )
160-
284+ # benchmark_spec.nginx_endpoint_ip is set in _PrepareCluster
161285
162286def Run (benchmark_spec ):
163287 """Run a benchmark against the Nginx server."""
0 commit comments