You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by jb...@apache.org on 2020/11/17 19:24:46 UTC

[geode-benchmarks] branch develop updated: Improves on SNI proxy benchmarking. (#136)

This is an automated email from the ASF dual-hosted git repository.

jbarrett pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-benchmarks.git


The following commit(s) were added to refs/heads/develop by this push:
     new 8d72231  Improves on SNI proxy benchmarking. (#136)
8d72231 is described below

commit 8d722312047e673b832e6c73d5ca704048b6819b
Author: Jacob Barrett <jb...@pivotal.io>
AuthorDate: Tue Nov 17 11:24:36 2020 -0800

    Improves on SNI proxy benchmarking. (#136)
    
    * Adds support for Envoy as proxy server.
    * Adds support for manually configured and managed proxy.
    * Adds ability to switch between proxy implementations.
    * Adds support for overriding docker image:tag for proxy implementations.
---
 README.md                                          |  45 ++---
 geode-benchmarks/build.gradle                      |  10 +-
 .../tasks/DefineHostNamingsOffPlatformTask.java    |  70 --------
 .../apache/geode/benchmark/tasks/StartClient.java  |   5 +-
 .../geode/benchmark/tasks/StartClientSNI.java      |  36 ++--
 .../apache/geode/benchmark/tasks/StartEnvoy.java   | 179 ++++++++++++++++++++
 .../apache/geode/benchmark/tasks/StartHAProxy.java | 142 ++++++++++++++++
 .../geode/benchmark/tasks/StartLocatorSNI.java     |  47 ------
 .../apache/geode/benchmark/tasks/StartServer.java  |  12 +-
 .../geode/benchmark/tasks/StartServerSNI.java      |  46 ------
 .../geode/benchmark/tasks/StartSniProxy.java       | 183 ---------------------
 .../apache/geode/benchmark/tasks/StopSniProxy.java |   8 +-
 .../benchmark/tests/ClientServerBenchmark.java     |  17 +-
 .../benchmark/topology/ClientServerTopology.java   |   3 +-
 .../topology/ClientServerTopologyWithSNIProxy.java |  62 +++++--
 .../geode/benchmark/topology/P2pTopology.java      |   3 +-
 .../org/apache/geode/benchmark/topology/Ports.java |  17 +-
 .../geode/benchmark/tasks/StartEnvoyTest.java      | 132 +++++++++++++++
 .../geode/benchmark/tasks/StartHAProxyTest.java    |  69 ++++++++
 .../geode/benchmark/tasks/StartSniProxyTest.java   |  64 -------
 .../geode/benchmark/tests/GeodeBenchmarkTest.java  |  44 +++--
 .../ClientServerTopologyWithSNIProxyTest.java      |  13 +-
 .../scripts/aws/image/files/docker-compose.yml     |  26 ---
 infrastructure/scripts/aws/image/packer.json       |  11 --
 24 files changed, 678 insertions(+), 566 deletions(-)

diff --git a/README.md b/README.md
index 721d05e..9d662da 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,8 @@ Options:
     -PwithSslProtocols    : Specifies enabled SSL protocols. See Geode property `ssl-protocols`
     -PwithSslCiphers      : Specifies enabled SSL chipher suites. See Geode property `ssl-ciphers`
     -PwithSecurityManager : Flag to start Geode with the example implementation of SecurityManager
+    -PwithSniProxy        : Use SNI proxy topology.
+    -PwithSniProxyImage   : Provide an alternative Docker image coordinate for SNI proxy.
     -PwithGc              : Select which GC to use. Valid values CMS (default), G1, Z.
     -PwithHeap            : Specify how large a heap the benchmark VMs should use, default "8g". Accepts any `-Xmx` value, like "32g".
     -PwithThreads         : Specify how many threads to use when executing the benchmark. Default varies by benchmark.
@@ -150,32 +152,21 @@ public class PutTask extends BenchmarkDriverAdapter implements Serializable {
 }
 ```
 
-## SNI
+## SNI Proxy
 
-On AWS, you can run any benchmark on a topology that routes all client-server communication through an SNI proxy (HAproxy).
+You can run any benchmark on a topology that routes all client-server communication through an SNI proxy.
+
+The `withSniProxy` property accepts:
+ * `HAProxy` for HAProxy based SNI proxy (default).
+ * `Envoy` for Envoy based SNI proxy.
+ * `Manual` for providing your own SNI proxy and managing its lifecycle.
+ 
+The `withSniProxyImage` property can be used to provide an alternative Docker image to one of the 
+supported proxy implementations. The value should be set to a valid Docker image coordinate. 
  
-To run a test, e.g. `PartitionedGetBenchmark`, with SNI:
-
-`./run_tests.sh -t anytagname -- -PwithSniProxy '--tests=PartitionedGetBenchmark'`
-
-Since SNI is a feature of TLS, running with the SNI topology incurs TLS overheads.
-
-### TODO for SNI
-* ~~verify `StartSniProxy` runs on proxy node~~
-* ~~don't require operator to supply `-PwithSSL`/`-DwithSSL=true` when running SNI tests~~
-* ~~generate `haproxy.cfg` with client-visible SNI hostnames~~
-* ~~turn on SNI via `setPoolSocketFactory` in a new `StartClientSNI` task~~
-* ~~set `--hostname-for-clients` on locator and servers for SNI~~
-* ~~reinstate thread-per-core in `PrePopulateRegion.run()` and in `PartitionedPutBenchmark[SNI]` ya~~
-* ~~set `keyRange` back to 1e6 in `PartitionedPutBenchmark[SNI]` after client-server connections are healthy~~
-* ~~make topology orthogonal to tests so all tests can run with SNI; have a `-PwithSniProxy`/`-DwithSniProxy=true` flag~~
-* Potential performance improvement: HAproxy as configured runs one process with the max threads-per-process of 64 threads, ostensibly using 64/72 cores (89%.) We might be able to improve performance by configuring HAproxy to run in daemon mode where we can run two processes, each multithreaded, to run more than 64 threads, thereby utilizing 100% of our cores.  
-
-## TODO (General)
-* add logic to clean up existing locator.dat files before running a locator on a node
-* eliminate `harness` module dependency on Geode by moving Geode keystore/truststore setting out of `harness` module and up into `geode-benchmarks` i.e. set 'em in properties sent to `Locator.startLocatorAndDS` in `StartLocator`, `StartServer`
-* move `docker-compose.yml` distribution out of `harness` module up into `geode-benchmarks` so it gets distributed whenever it changes (without requiring rebuilding AWS AMI and cluster on AWS) 
-* generate 2048-bit keys (instead of 1024-bit ones) for TLS; will slow TLS handshakes which may necessitate a new baseline
-* make `StartServer` task use `ServerLauncher` (instead of `CacheFactory`) for symmetry with `LocatorLauncher`&mdash;also too: encapsulation!
-* `./run_tests.sh` sometimes seems to hang after benchmarks have completed, requiring operator to enter ^C to un-stick it
-* make `rsync:` Git "scheme" work in `run_tests.sh` script for benchmark repo (not just for geode repo)
+To run a test, e.g. `PartitionedGetBenchmark`, with HAProxy SNI Proxy:
+
+`./run_tests.sh -t anytagname -- -PwithSniProxy=HAProxy --tests=PartitionedGetBenchmark`
+
+Since SNI is a feature of TLS, running with the SNI topology incurs TLS overheads with implied `-PwithSsl`.
+
diff --git a/geode-benchmarks/build.gradle b/geode-benchmarks/build.gradle
index c1cb54d..c58f7ea 100644
--- a/geode-benchmarks/build.gradle
+++ b/geode-benchmarks/build.gradle
@@ -58,6 +58,7 @@ dependencies {
   // Required for missing dependency on geode-core.
   runtime(group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.4.12.v20180830')
 
+  testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter-params', version:project.'junit-jupiter-engine.version')
   testImplementation(group: 'org.mockito', name: 'mockito-all', version: project.'mockito-all.version')
   testImplementation(group: 'org.assertj', name: 'assertj-core', version: project.'assertj-core.version')
 }
@@ -102,13 +103,20 @@ task benchmark(type: Test) {
   if (project.hasProperty('withDuration')) {
     systemProperty 'withDuration', project.findProperty('withDuration')
   }
+
   systemProperty 'withSsl', project.hasProperty('withSsl')
   systemProperty 'withSslProtocols', project.findProperty('withSslProtocols')
   systemProperty 'withSslCiphers', project.findProperty('withSslCiphers')
-  systemProperty 'withSniProxy', project.hasProperty('withSniProxy')
+
+  if (project.hasProperty('withSniProxy')) {
+    systemProperty 'withSniProxy', project.findProperty('withSniProxy')
+  }
+  systemProperty 'withSniProxyImage', project.findProperty('withSniProxyImage')
+
   systemProperty 'withSecurityManager', project.hasProperty('withSecurityManager')
   systemProperty 'benchmark.profiler.argument', project.findProperty('benchmark.profiler.argument')
 
+
   doFirst {
     if(!project.hasProperty('hosts')) {
       throw new GradleException("You must set the hosts property to a comma separated list of hosts. Eg ./gradlew benchmark -Phosts=localhost,localhost,localhost")
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/DefineHostNamingsOffPlatformTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/DefineHostNamingsOffPlatformTask.java
deleted file mode 100644
index f17cf9e..0000000
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/DefineHostNamingsOffPlatformTask.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.geode.benchmark.tasks;
-
-import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
-import static org.apache.geode.benchmark.topology.Roles.SERVER;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.geode.benchmark.topology.HostNamingOffPlatform;
-import org.apache.geode.benchmark.topology.Roles;
-import org.apache.geode.perftest.Task;
-import org.apache.geode.perftest.TestContext;
-
-public class DefineHostNamingsOffPlatformTask implements Task {
-
-  public static final String HOST_NAMINGS_OFF_PLATFORM = "HOST_NAMINGS_OFF_PLATFORM";
-
-  public static String getOffPlatformHostName(final TestContext context,
-      final InetAddress addy) throws UnknownHostException {
-    @SuppressWarnings("unchecked")
-    final Map<InetAddress, String> namings =
-        (Map<InetAddress, String>) context.getAttribute(HOST_NAMINGS_OFF_PLATFORM);
-    return namings.get(addy);
-  }
-
-  @Override
-  public void run(final TestContext context) throws Exception {
-    final Map<InetAddress, String> namings =
-        Stream.concat(
-            generateHostNamingsFor(context, LOCATOR), generateHostNamingsFor(context, SERVER))
-            .collect(
-                Collectors.toMap(naming -> naming.internalAddy, naming -> naming.externalName));
-    context.setAttribute(HOST_NAMINGS_OFF_PLATFORM, namings);
-  }
-
-  private Stream<HostNamingOffPlatform> generateHostNamingsFor(final TestContext context,
-      final Roles role) {
-    final AtomicInteger i = new AtomicInteger(0);
-    final String roleName = role.name();
-    return context.getHostsForRole(roleName).stream().map(host -> new HostNamingOffPlatform(
-        formOffPlatformHostName(roleName, i.getAndIncrement()),
-        host));
-  }
-
-  private String formOffPlatformHostName(final String roleName, final int i) {
-    return roleName + "-OFF-PLATFORM-" + i;
-  }
-
-}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClient.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClient.java
index 3c442f4..89b0451 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClient.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClient.java
@@ -23,7 +23,6 @@ import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
 import java.io.File;
 import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.Properties;
 
 import org.apache.geode.cache.client.ClientCache;
@@ -67,12 +66,12 @@ public class StartClient implements Task {
       final String statsFile,
       final Properties properties,
       final TestContext context)
-      throws UnknownHostException, NoSuchMethodException, InvocationTargetException,
+      throws NoSuchMethodException, InvocationTargetException,
       IllegalAccessException, ClassNotFoundException {
     return new ClientCacheFactory(properties)
         .setPdxSerializer(new ReflectionBasedAutoSerializer("benchmark.geode.data.*"))
         .setPoolIdleTimeout(-1)
         .set(ConfigurationProperties.STATISTIC_ARCHIVE_FILE, statsFile)
-        .addPoolLocator(locator.getHostAddress(), locatorPort);
+        .addPoolLocator(locator.getHostName(), locatorPort);
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClientSNI.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClientSNI.java
index d5acc7d..76fe1c7 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClientSNI.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartClientSNI.java
@@ -17,26 +17,23 @@
 
 package org.apache.geode.benchmark.tasks;
 
-import static org.apache.geode.benchmark.tasks.DefineHostNamingsOffPlatformTask.getOffPlatformHostName;
-import static org.apache.geode.benchmark.topology.Ports.SNI_PROXY_PORT;
-import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
 import static org.apache.geode.benchmark.topology.Roles.PROXY;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.Properties;
 
 import org.apache.geode.cache.client.ClientCacheFactory;
-import org.apache.geode.distributed.ConfigurationProperties;
-import org.apache.geode.pdx.ReflectionBasedAutoSerializer;
 import org.apache.geode.perftest.TestContext;
 
 public class StartClientSNI extends StartClient {
 
-  public StartClientSNI(final int locatorPort) {
+  private final int proxyPort;
+
+  public StartClientSNI(final int locatorPort, final int proxyPort) {
     super(locatorPort);
+    this.proxyPort = proxyPort;
   }
 
   @Override
@@ -44,28 +41,20 @@ public class StartClientSNI extends StartClient {
       final String statsFile,
       final Properties properties,
       final TestContext context)
-      throws UnknownHostException, NoSuchMethodException, InvocationTargetException,
+      throws NoSuchMethodException, InvocationTargetException,
       IllegalAccessException, ClassNotFoundException {
 
-    final InetAddress firstLocatorAddy =
-        context.getHostsForRole(LOCATOR.name()).iterator().next();
-    final String offPlatformLocatorName =
-        getOffPlatformHostName(context, firstLocatorAddy);
-    final InetAddress proxyAddy =
-        context.getHostsForRole(PROXY.name()).iterator().next();
+    final ClientCacheFactory cacheFactory =
+        super.createClientCacheFactory(locator, statsFile, properties, context);
 
-    final ClientCacheFactory cacheFactory = new ClientCacheFactory(properties)
-        .setPdxSerializer(new ReflectionBasedAutoSerializer("benchmark.geode.data.*"))
-        .setPoolIdleTimeout(-1)
-        .set(ConfigurationProperties.STATISTIC_ARCHIVE_FILE, statsFile)
-        .addPoolLocator(offPlatformLocatorName, locatorPort);
-    final String proxyHostAddress = proxyAddy.getHostAddress();
-    return reflectivelySetSniSocketFactory(cacheFactory, proxyHostAddress);
+    final InetAddress proxyInetAddress =
+        context.getHostsForRole(PROXY.name()).stream().findFirst().get();
+    return reflectivelySetSniSocketFactory(cacheFactory, proxyInetAddress);
   }
 
   protected ClientCacheFactory reflectivelySetSniSocketFactory(
       final ClientCacheFactory clientCacheFactory,
-      final String proxyHostAddress)
+      final InetAddress proxyInetAddress)
       throws NoSuchMethodException, InvocationTargetException, IllegalAccessException,
       ClassNotFoundException {
     /*
@@ -83,7 +72,8 @@ public class StartClientSNI extends StartClient {
     final Method sniStaticMethod =
         proxySocketFactoriesClass.getMethod("sni", String.class, int.class);
 
-    final Object sniSocketFactory = sniStaticMethod.invoke(null, proxyHostAddress, SNI_PROXY_PORT);
+    final Object sniSocketFactory =
+        sniStaticMethod.invoke(null, proxyInetAddress.getHostName(), proxyPort);
 
     final Class<?> socketFactoryClass =
         Class.forName("org.apache.geode.cache.client.SocketFactory");
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartEnvoy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartEnvoy.java
new file mode 100644
index 0000000..a32b476
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartEnvoy.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.tasks;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.lang.String.format;
+import static java.lang.System.getProperty;
+import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+import org.apache.geode.perftest.Task;
+import org.apache.geode.perftest.TestContext;
+
+/**
+ * Task to start the SNI proxy
+ */
+public class StartEnvoy implements Task {
+  public static final String START_DOCKER_DAEMON_COMMAND = "sudo service docker start";
+  public static final String START_PROXY_COMMAND =
+      "docker run --rm -d -v %s:/etc/envoy/envoy.yaml:ro --name proxy -p %d:%d %s";
+
+  private final int locatorPort;
+  private final int serverPort;
+  private final int proxyPort;
+  private final String image;
+
+  public StartEnvoy(final int locatorPort, final int serverPort, final int proxyPort,
+      final String image) {
+    this.locatorPort = locatorPort;
+    this.serverPort = serverPort;
+    this.proxyPort = proxyPort;
+    this.image = isNullOrEmpty(image) ? "envoyproxy/envoy:v1.16-latest" : image;
+  }
+
+  @Override
+  public void run(final TestContext context) throws Exception {
+
+    final Path configFile = Paths.get(getProperty("user.home"), "envoy.yaml");
+    rewriteFile(generateConfig(context), configFile);
+
+    final ProcessControl processControl = new ProcessControl();
+    processControl.runCommand(START_DOCKER_DAEMON_COMMAND);
+    processControl.runCommand(format(START_PROXY_COMMAND, configFile, proxyPort, proxyPort, image));
+  }
+
+  private void rewriteFile(final String content, final Path path) throws IOException {
+    try (final BufferedWriter writer = new BufferedWriter(new FileWriter(path.toFile(), false))) {
+      writer.write(content);
+    }
+  }
+
+
+  String generateConfig(final TestContext context) {
+
+    StringBuilder yaml = new StringBuilder("static_resources:\n"
+        + "  listeners:\n"
+        + "    - name: geode_listener\n"
+        + "      address:\n"
+        + "        socket_address:\n"
+        + "          address: 0.0.0.0\n"
+        + "          port_value: ").append(proxyPort).append("\n"
+            + "      reuse_port: true\n"
+            + "      tcp_backlog_size: 1000\n"
+            + "      listener_filters:\n"
+            + "        - name: envoy.filters.listener.tls_inspector\n"
+            + "      filter_chains:\n"
+            + "        - filter_chain_match:\n"
+            + "            server_names:\n");
+
+    context.getHostsForRole(LOCATOR.name())
+        .forEach(inetAddress -> yaml.append("              - '").append(inetAddress.getHostName())
+            .append("'\n"));
+
+    yaml.append("            transport_protocol: tls\n"
+        + "          filters:\n"
+        + "            - name: envoy.filters.network.sni_dynamic_forward_proxy\n"
+        + "              typed_config:\n"
+        + "                '@type': type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3alpha.FilterConfig\n"
+        + "                port_value: ").append(locatorPort).append("\n"
+            + "                dns_cache_config:\n"
+            + "                  name: geode_cluster_dns_cache_config\n"
+            + "                  dns_lookup_family: V4_ONLY\n"
+            + "            - name: envoy.tcp_proxy\n"
+            + "              typed_config:\n"
+            + "                '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy\n"
+            + "                stat_prefix: tcp\n"
+            + "                cluster: geode_cluster\n"
+            + "                max_connect_attempts: 1000000000\n"
+            + "                access_log:\n"
+            + "                  - name: envoy.access_loggers.file\n"
+            + "                    typed_config:\n"
+            + "                      \"@type\": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog\n"
+            + "                      log_format:\n"
+            + "                        text_format: \"[%START_TIME%] %DOWNSTREAM_REMOTE_ADDRESS% %UPSTREAM_HOST% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION%\\n\"\n"
+            + "                      path: /dev/stdout\n"
+            + "        - filter_chain_match:\n"
+            + "            server_names:\n");
+
+    context.getHostsForRole(SERVER.name())
+        .forEach(inetAddress -> yaml.append("              - '").append(inetAddress.getHostName())
+            .append("'\n"));
+
+    yaml.append("            transport_protocol: tls\n"
+        + "          filters:\n"
+        + "            - name: envoy.filters.network.sni_dynamic_forward_proxy\n"
+        + "              typed_config:\n"
+        + "                '@type': type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3alpha.FilterConfig\n"
+        + "                port_value: ").append(serverPort).append("\n"
+            + "                dns_cache_config:\n"
+            + "                  name: geode_cluster_dns_cache_config\n"
+            + "                  dns_lookup_family: V4_ONLY\n"
+            + "            - name: envoy.tcp_proxy\n"
+            + "              typed_config:\n"
+            + "                '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy\n"
+            + "                stat_prefix: tcp\n"
+            + "                cluster: geode_cluster\n"
+            + "                max_connect_attempts: 1000000000\n"
+            + "                access_log:\n"
+            + "                  - name: envoy.access_loggers.file\n"
+            + "                    typed_config:\n"
+            + "                      \"@type\": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog\n"
+            + "                      log_format:\n"
+            + "                        text_format: \"[%START_TIME%] %DOWNSTREAM_REMOTE_ADDRESS% %UPSTREAM_HOST% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION%\\n\"\n"
+            + "                      path: /dev/stdout\n"
+            + "  clusters:\n"
+            + "    - name: geode_cluster\n"
+            + "      connect_timeout: 10s\n"
+            + "      lb_policy: CLUSTER_PROVIDED\n"
+            + "      cluster_type:\n"
+            + "        name: envoy.clusters.dynamic_forward_proxy\n"
+            + "        typed_config:\n"
+            + "          '@type': type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig\n"
+            + "          dns_cache_config:\n"
+            + "            name: geode_cluster_dns_cache_config\n"
+            + "            dns_lookup_family: V4_ONLY\n");
+    return yaml.toString();
+  }
+
+  @Override
+  public boolean equals(final Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    final StartEnvoy that = (StartEnvoy) o;
+    return locatorPort == that.locatorPort &&
+        serverPort == that.serverPort &&
+        proxyPort == that.proxyPort;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(locatorPort, serverPort, proxyPort);
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartHAProxy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartHAProxy.java
new file mode 100644
index 0000000..be12957
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartHAProxy.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.tasks;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.lang.String.format;
+import static java.lang.System.getProperty;
+import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.geode.perftest.Task;
+import org.apache.geode.perftest.TestContext;
+
+/**
+ * Task to start the SNI proxy
+ */
+public class StartHAProxy implements Task {
+  public static final String START_DOCKER_DAEMON_COMMAND = "sudo service docker start";
+  public static final String START_PROXY_COMMAND =
+      "docker run --rm -d -v %s:/usr/local/etc/haproxy:ro --name proxy -p %d:%d %s";
+
+  private final int locatorPort;
+  private final int serverPort;
+  private final int proxyPort;
+  private final String image;
+
+  public StartHAProxy(final int locatorPort, final int serverPort, final int proxyPort,
+      final String image) {
+    this.locatorPort = locatorPort;
+    this.serverPort = serverPort;
+    this.proxyPort = proxyPort;
+    this.image = isNullOrEmpty(image) ? "haproxy:1.8-alpine" : image;
+  }
+
+  @Override
+  public void run(final TestContext context) throws Exception {
+
+    final Path configPath = Paths.get(getProperty("user.home")).toAbsolutePath();
+    final Path configFile = configPath.resolve("haproxy.cfg");
+    rewriteFile(generateConfig(context), configFile);
+
+    final ProcessControl processControl = new ProcessControl();
+    processControl.runCommand(START_DOCKER_DAEMON_COMMAND);
+    processControl.runCommand(format(START_PROXY_COMMAND, configPath, proxyPort, proxyPort, image));
+  }
+
+  private void rewriteFile(final String content, final Path path) throws IOException {
+    try (final BufferedWriter writer = new BufferedWriter(new FileWriter(path.toFile(), false))) {
+      writer.write(content);
+    }
+  }
+
+  String generateConfig(final TestContext context) {
+
+    final Set<InetSocketAddress> members = new HashSet<>();
+
+    context.getHostsForRole(LOCATOR.name()).stream()
+        .map(a -> InetSocketAddress.createUnresolved(a.getHostName(), locatorPort))
+        .forEachOrdered(members::add);
+
+    context.getHostsForRole(SERVER.name()).stream()
+        .map(a -> InetSocketAddress.createUnresolved(a.getHostName(), serverPort))
+        .forEachOrdered(members::add);
+
+    return generateConfig(members);
+  }
+
+  String generateConfig(final Set<InetSocketAddress> members) {
+    StringBuilder conf = new StringBuilder("global\n"
+        + "  daemon\n"
+        + "  maxconn 64000\n"
+        + "  spread-checks 4\n"
+        + "defaults\n"
+        + "  log global\n"
+        + "  timeout connect 30000ms\n"
+        + "  timeout client 30000ms\n"
+        + "  timeout server 30000ms\n"
+        + "frontend sniproxy\n"
+        + "  bind *:").append(proxyPort).append("\n"
+            + "  mode tcp\n"
+            + "  tcp-request inspect-delay 5s\n"
+            + "  tcp-request content accept if { req_ssl_hello_type 1 }\n");
+
+    members
+        .forEach(s -> conf.append("  use_backend ").append(s.getHostName())
+            .append(" if { req.ssl_sni ").append(s.getHostName()).append(" }\n"));
+
+    members
+        .forEach(s -> conf.append("backend ").append(s.getHostName())
+            .append("\n"
+                + "  mode tcp\n"
+                + "  server host ")
+            .append(s.getHostName())
+            .append(":").append(s.getPort()).append("\n"));
+
+    return conf.toString();
+  }
+
+  @Override
+  public boolean equals(final Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    final StartHAProxy that = (StartHAProxy) o;
+    return locatorPort == that.locatorPort &&
+        serverPort == that.serverPort &&
+        proxyPort == that.proxyPort;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(locatorPort, serverPort, proxyPort);
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocatorSNI.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocatorSNI.java
deleted file mode 100644
index b54f2b0..0000000
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocatorSNI.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.geode.benchmark.tasks;
-
-import static org.apache.geode.benchmark.tasks.DefineHostNamingsOffPlatformTask.getOffPlatformHostName;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.util.Properties;
-
-import org.apache.geode.distributed.LocatorLauncher;
-import org.apache.geode.perftest.TestContext;
-
-public class StartLocatorSNI extends StartLocator {
-
-  public StartLocatorSNI(final int locatorPort) {
-    super(locatorPort);
-  }
-
-  @Override
-  protected void startLocator(final Properties properties, final int locatorPort,
-      final TestContext context) throws IOException {
-    new LocatorLauncher.Builder()
-        .set(properties)
-        .setPort(locatorPort)
-        .setHostnameForClients(
-            getOffPlatformHostName(context, InetAddress.getLocalHost()))
-        .build()
-        .start();
-  }
-
-}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java
index 9689261..6118eda 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java
@@ -21,7 +21,6 @@ import static org.apache.geode.benchmark.parameters.GeodeProperties.serverProper
 
 import java.io.File;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.Properties;
 
 import org.apache.geode.cache.Cache;
@@ -37,10 +36,12 @@ import org.apache.geode.perftest.TestContext;
  */
 public class StartServer implements Task {
 
-  private int locatorPort;
+  private final int locatorPort;
+  private final int serverPort;
 
-  public StartServer(int locatorPort) {
+  public StartServer(final int locatorPort, final int serverPort) {
     this.locatorPort = locatorPort;
+    this.serverPort = serverPort;
   }
 
   @Override
@@ -71,10 +72,9 @@ public class StartServer implements Task {
    *
    * @param cacheServer is modified by this method!
    */
-  protected void configureCacheServer(final CacheServer cacheServer,
-      final TestContext context) throws UnknownHostException {
+  protected void configureCacheServer(final CacheServer cacheServer, final TestContext context) {
     cacheServer.setMaxConnections(Integer.MAX_VALUE);
-    cacheServer.setPort(0);
+    cacheServer.setPort(serverPort);
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServerSNI.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServerSNI.java
deleted file mode 100644
index 9c8f03b..0000000
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServerSNI.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.geode.benchmark.tasks;
-
-import static org.apache.geode.benchmark.tasks.DefineHostNamingsOffPlatformTask.getOffPlatformHostName;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import org.apache.geode.cache.server.CacheServer;
-import org.apache.geode.perftest.TestContext;
-
-public class StartServerSNI extends StartServer {
-
-  private final int serverPortForSni;
-
-  public StartServerSNI(final int locatorPort, final int serverPortForSni) {
-    super(locatorPort);
-    this.serverPortForSni = serverPortForSni;
-  }
-
-  @Override
-  protected void configureCacheServer(final CacheServer cacheServer, final TestContext context)
-      throws UnknownHostException {
-    cacheServer.setMaxConnections(Integer.MAX_VALUE);
-    cacheServer.setPort(serverPortForSni);
-    cacheServer.setHostnameForClients(
-        getOffPlatformHostName(context, InetAddress.getLocalHost()));
-  }
-
-}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartSniProxy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartSniProxy.java
deleted file mode 100644
index f0f4e48..0000000
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartSniProxy.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.geode.benchmark.tasks;
-
-import static org.apache.geode.benchmark.tasks.DefineHostNamingsOffPlatformTask.HOST_NAMINGS_OFF_PLATFORM;
-import static org.apache.geode.benchmark.topology.Ports.SERVER_PORT_FOR_SNI;
-import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
-import static org.apache.geode.benchmark.topology.Roles.SERVER;
-
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.geode.benchmark.topology.Roles;
-import org.apache.geode.perftest.Task;
-import org.apache.geode.perftest.TestContext;
-
-/**
- * Task to start the SNI proxy
- */
-public class StartSniProxy implements Task {
-  public static final String START_DOCKER_DAEMON_COMMAND = "sudo service docker start";
-  public static final String START_PROXY_COMMAND = "docker-compose up -d haproxy";
-
-  private final int locatorPort;
-
-  public StartSniProxy(int locatorPort) {
-    this.locatorPort = locatorPort;
-  }
-
-  @Override
-  public void run(TestContext context) throws Exception {
-
-    @SuppressWarnings("unchecked")
-    final Map<InetAddress, String> namings =
-        (Map<InetAddress, String>) context.getAttribute(HOST_NAMINGS_OFF_PLATFORM);
-
-    final String config = generateHaProxyConfig(
-        internalHostNamesFor(context, LOCATOR),
-        externalHostNamesFor(context, LOCATOR, namings),
-        internalHostNamesFor(context, SERVER),
-        externalHostNamesFor(context, SERVER, namings));
-
-    rewriteFile(config, "haproxy.cfg");
-
-    final ProcessControl processControl = new ProcessControl();
-    processControl.runCommand(START_DOCKER_DAEMON_COMMAND);
-    processControl.runCommand(START_PROXY_COMMAND);
-  }
-
-  private void rewriteFile(final String content, final String fileName) throws IOException {
-    try (final BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, false))) {
-      writer.write(content);
-    }
-  }
-
-  private Stream<String> internalHostNamesFor(final TestContext context, final Roles role) {
-    return addysFor(context, role).map(InetAddress::getHostName);
-  }
-
-  private Stream<String> externalHostNamesFor(final TestContext context, final Roles role,
-      final Map<InetAddress, String> namings) {
-    return addysFor(context, role).map(addy -> namings.get(addy));
-  }
-
-  private Stream<InetAddress> addysFor(final TestContext context, final Roles role) {
-    return context.getHostsForRole(role.name()).stream();
-  }
-
-  String generateHaProxyConfig(final Stream<String> locatorsInternalStream,
-      final Stream<String> locatorsExternalStream,
-      final Stream<String> serversInternalStream,
-      final Stream<String> serversExternalStream) {
-
-    final Iterable<String> locatorsInternal = locatorsInternalStream.collect(Collectors.toList());
-    final Iterable<String> locatorsExternal = locatorsExternalStream.collect(Collectors.toList());
-    final Iterable<String> serversInternal = serversInternalStream.collect(Collectors.toList());
-    final Iterable<String> serversExternal = serversExternalStream.collect(Collectors.toList());
-
-    final StringBuilder stuff = new StringBuilder(
-        /*
-         * log to stdout per:
-         * https://www.haproxy.com/documentation/hapee/latest/administration/docker-logging/
-         */
-        "global\n"
-            + "  log stdout format raw local0 debug\n"
-            + "  maxconn 5000\n"
-            + "defaults\n"
-            + "  log global\n"
-            /*
-             * We're leaving timeouts unspecified so they are infinite. Benchmarks do bad things
-             * when the proxy breaks connections.
-             */
-            // + " timeout client 100s\n"
-            // + " timeout connect 100s\n"
-            // + " timeout server 100s\n"
-            + "frontend sniproxy\n"
-            + "  bind *:15443\n"
-            + "  mode tcp\n"
-            + "  tcp-request inspect-delay 5s\n"
-            + "  tcp-request content accept if { req_ssl_hello_type 1 }\n");
-
-    generateUseBackendRule(locatorsInternal, locatorsExternal, stuff, "locators-");
-    generateUseBackendRule(serversInternal, serversExternal, stuff, "servers-");
-
-    final String firstLocatorInternal = locatorsInternal.iterator().next();
-    stuff.append("  default_backend ").append("locators-").append(firstLocatorInternal)
-        .append("\n");
-
-    generateBackendSection(locatorsInternal, stuff, "locators-",
-        "locator1", locatorPort);
-
-    generateBackendSection(serversInternal, stuff, "servers-",
-        "server1", SERVER_PORT_FOR_SNI);
-
-    return stuff.toString();
-  }
-
-  private void generateUseBackendRule(final Iterable<String> internalsIterable,
-      final Iterable<String> externalsIterable,
-      final StringBuilder stuff,
-      final String backendNamePrefix) {
-    final Iterator<String> internals = internalsIterable.iterator();
-    final Iterator<String> externals = externalsIterable.iterator();
-    while (internals.hasNext() && externals.hasNext()) {
-      final String internal = internals.next();
-      final String external = externals.next();
-      stuff.append("  use_backend ").append(backendNamePrefix).append(internal)
-          .append(" if { req.ssl_sni -i ").append(external).append(" }\n");
-    }
-  }
-
-  private void generateBackendSection(final Iterable<String> internalsIterator,
-      final StringBuilder stuff,
-      final String backendNamePrefix,
-      final String singleHostRoleName,
-      final int port) {
-    for (final String addy : internalsIterator) {
-      stuff.append("backend ").append(backendNamePrefix).append(addy).append("\n")
-          .append("  mode tcp\n").append("  server ").append(singleHostRoleName).append(" ")
-          .append(addy)
-          .append(":").append(port).append("\n");
-    }
-  }
-
-  @Override
-  public boolean equals(final Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    final StartSniProxy that = (StartSniProxy) o;
-    return locatorPort == that.locatorPort;
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(locatorPort);
-  }
-}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopSniProxy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopSniProxy.java
index 4014588..0719bb1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopSniProxy.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopSniProxy.java
@@ -25,17 +25,11 @@ import org.apache.geode.perftest.TestContext;
  */
 public class StopSniProxy implements Task {
 
-  public static final String STOP_PROXY_COMMAND = "docker-compose down";
-  // TODO: fix: this exits w/ status 1
-  public static final String CAPTURE_PROXY_LOG =
-      "bash -c 'docker-compose logs haproxy > haproxy.log'";
-
-  public StopSniProxy() {}
+  public static final String STOP_PROXY_COMMAND = "docker kill proxy";
 
   @Override
   public void run(TestContext context) throws Exception {
     final ProcessControl processControl = new ProcessControl();
-    // processControl.runCommand(CAPTURE_PROXY_LOG);
     processControl.runCommand(STOP_PROXY_COMMAND);
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ClientServerBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ClientServerBenchmark.java
index 982c332..b4ecf76 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ClientServerBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ClientServerBenchmark.java
@@ -18,19 +18,22 @@ package org.apache.geode.benchmark.tests;
 
 import org.apache.geode.benchmark.topology.ClientServerTopology;
 import org.apache.geode.benchmark.topology.ClientServerTopologyWithSNIProxy;
+import org.apache.geode.benchmark.topology.ClientServerTopologyWithSNIProxy.SniProxyImplementation;
 import org.apache.geode.perftest.TestConfig;
 
 public class ClientServerBenchmark extends GeodeBenchmark {
 
 
   public static TestConfig createConfig() {
-    TestConfig config = GeodeBenchmark.createConfig();
-
-    final String sniProp = System.getProperty("withSniProxy");
-    final boolean doSni = sniProp != null && !sniProp.equals("false");
-
-    if (doSni) {
-      ClientServerTopologyWithSNIProxy.configure(config);
+    final TestConfig config = GeodeBenchmark.createConfig();
+
+    String sniProp = System.getProperty("withSniProxy");
+    if (sniProp != null) {
+      if (sniProp.isEmpty()) {
+        sniProp = SniProxyImplementation.HAProxy.name();
+      }
+      ClientServerTopologyWithSNIProxy.configure(config,
+          SniProxyImplementation.valueOfIgnoreCase(sniProp));
     } else {
       ClientServerTopology.configure(config);
     }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
index 4ef9ffb..3678596 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
@@ -17,6 +17,7 @@ package org.apache.geode.benchmark.topology;
 import static org.apache.geode.benchmark.Config.after;
 import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.role;
+import static org.apache.geode.benchmark.topology.Ports.EPHEMERAL_PORT;
 import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
@@ -43,7 +44,7 @@ public class ClientServerTopology extends Topology {
     configureCommon(config);
 
     before(config, new StartLocator(LOCATOR_PORT), LOCATOR);
-    before(config, new StartServer(LOCATOR_PORT), SERVER);
+    before(config, new StartServer(LOCATOR_PORT, EPHEMERAL_PORT), SERVER);
     before(config, new StartClient(LOCATOR_PORT), CLIENT);
 
     after(config, new StopClient(), CLIENT);
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxy.java
index d9a72f3..0dd157b 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxy.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxy.java
@@ -18,22 +18,20 @@ import static org.apache.geode.benchmark.Config.after;
 import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.role;
 import static org.apache.geode.benchmark.parameters.Utils.configureGeodeProductJvms;
+import static org.apache.geode.benchmark.topology.ClientServerTopologyWithSNIProxy.SniProxyImplementation.Manual;
 import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
-import static org.apache.geode.benchmark.topology.Ports.SERVER_PORT_FOR_SNI;
-import static org.apache.geode.benchmark.topology.RoleKinds.GEODE_PRODUCT;
+import static org.apache.geode.benchmark.topology.Ports.SERVER_PORT;
+import static org.apache.geode.benchmark.topology.Ports.SNI_PROXY_PORT;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
 import static org.apache.geode.benchmark.topology.Roles.PROXY;
 import static org.apache.geode.benchmark.topology.Roles.SERVER;
-import static org.apache.geode.benchmark.topology.Topology.WITH_SSL_ARGUMENT;
 
-import java.util.stream.Stream;
-
-import org.apache.geode.benchmark.tasks.DefineHostNamingsOffPlatformTask;
 import org.apache.geode.benchmark.tasks.StartClientSNI;
-import org.apache.geode.benchmark.tasks.StartLocatorSNI;
-import org.apache.geode.benchmark.tasks.StartServerSNI;
-import org.apache.geode.benchmark.tasks.StartSniProxy;
+import org.apache.geode.benchmark.tasks.StartEnvoy;
+import org.apache.geode.benchmark.tasks.StartHAProxy;
+import org.apache.geode.benchmark.tasks.StartLocator;
+import org.apache.geode.benchmark.tasks.StartServer;
 import org.apache.geode.benchmark.tasks.StopClient;
 import org.apache.geode.benchmark.tasks.StopSniProxy;
 import org.apache.geode.perftest.TestConfig;
@@ -44,7 +42,24 @@ public class ClientServerTopologyWithSNIProxy extends Topology {
   private static final int NUM_CLIENTS = 1;
   private static final int NUM_PROXIES = 1;
 
-  public static void configure(TestConfig config) {
+  public enum SniProxyImplementation {
+    Manual,
+    HAProxy,
+    Envoy;
+
+    public static SniProxyImplementation valueOfIgnoreCase(String name) {
+      name = name.toLowerCase();
+      for (SniProxyImplementation sniProxyImplementation : SniProxyImplementation.values()) {
+        if (sniProxyImplementation.name().toLowerCase().equals(name)) {
+          return sniProxyImplementation;
+        }
+      }
+      throw new IllegalArgumentException();
+    }
+  }
+
+  public static void configure(final TestConfig config,
+      final SniProxyImplementation sniProxyImplementation) {
     role(config, LOCATOR, NUM_LOCATORS);
     role(config, SERVER, NUM_SERVERS);
     role(config, CLIENT, NUM_CLIENTS);
@@ -54,16 +69,29 @@ public class ClientServerTopologyWithSNIProxy extends Topology {
 
     configureGeodeProductJvms(config, WITH_SSL_ARGUMENT);
 
-    Stream.concat(Roles.rolesFor(GEODE_PRODUCT), Stream.of(PROXY))
-        .forEach(role -> before(config, new DefineHostNamingsOffPlatformTask(), role));
+    before(config, new StartLocator(LOCATOR_PORT), LOCATOR);
+    before(config, new StartServer(LOCATOR_PORT, SERVER_PORT), SERVER);
 
-    before(config, new StartLocatorSNI(LOCATOR_PORT), LOCATOR);
-    before(config, new StartServerSNI(LOCATOR_PORT, SERVER_PORT_FOR_SNI), SERVER);
-    before(config, new StartSniProxy(LOCATOR_PORT), PROXY);
-    before(config, new StartClientSNI(LOCATOR_PORT), CLIENT);
+    final String image = System.getProperty("withSniProxyImage");
+
+    switch (sniProxyImplementation) {
+      case HAProxy:
+        before(config, new StartHAProxy(LOCATOR_PORT, SERVER_PORT, SNI_PROXY_PORT, image), PROXY);
+        break;
+      case Envoy:
+        before(config, new StartEnvoy(LOCATOR_PORT, SERVER_PORT, SNI_PROXY_PORT, image), PROXY);
+        break;
+      case Manual:
+        // expect proxy already configured.
+    }
+
+    before(config, new StartClientSNI(LOCATOR_PORT, SNI_PROXY_PORT), CLIENT);
 
     after(config, new StopClient(), CLIENT);
-    after(config, new StopSniProxy(), PROXY);
+
+    if (Manual != sniProxyImplementation) {
+      after(config, new StopSniProxy(), PROXY);
+    }
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java
index f186926..0242bdb 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java
@@ -18,6 +18,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.geode.benchmark.Config.after;
 import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.role;
+import static org.apache.geode.benchmark.topology.Ports.EPHEMERAL_PORT;
 import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
 import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
 import static org.apache.geode.benchmark.topology.Roles.SERVER;
@@ -40,7 +41,7 @@ public class P2pTopology extends Topology {
     configureCommon(config);
 
     before(config, new StartLocator(LOCATOR_PORT), LOCATOR);
-    before(config, new StartServer(LOCATOR_PORT), SERVER);
+    before(config, new StartServer(LOCATOR_PORT, EPHEMERAL_PORT), SERVER);
 
     after(config, new Sleep(10, SECONDS), SERVER);
     after(config, new StopServer(), SERVER);
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java
index b4f0f58..41da7f1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java
@@ -16,17 +16,22 @@ package org.apache.geode.benchmark.topology;
 
 public class Ports {
   /**
-   * The port used to create the locator for the tests
+   * The ephemeral port constant.
+   */
+  public static final int EPHEMERAL_PORT = 0;
+
+  /**
+   * The port used to create the locator for the tests.
    */
   public static final int LOCATOR_PORT = 10334;
 
   /**
-   * With an SNI proxy, both the locator ports and the server ports
-   * have to be well-known (static) since the proxy has to know them
-   * and, in general, SNI proxies don't have visibility into locator
-   * responses carrying server port numbers.
+   * The port used to create the servers for tests.
    */
-  public static final int SERVER_PORT_FOR_SNI = 40404;
+  public static final int SERVER_PORT = 40404;
 
+  /**
+   * The SNI proxy port.
+   */
   public static final int SNI_PROXY_PORT = 15443;
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartEnvoyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartEnvoyTest.java
new file mode 100644
index 0000000..cc1b459
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartEnvoyTest.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.tasks;
+
+import static java.net.InetAddress.getByAddress;
+import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.geode.perftest.TestContext;
+
+class StartEnvoyTest {
+
+  @Test
+  void generateConfig() throws UnknownHostException {
+    final TestContext testContext = mock(TestContext.class);
+    final Set<InetAddress> locators =
+        Collections.singleton(getByAddress("l1", new byte[] {0, 0, 0, 0}));
+    when(testContext.getHostsForRole(LOCATOR.name()))
+        .thenReturn(locators);
+    final HashSet<InetAddress> servers = new HashSet<InetAddress>() {
+      {
+        add(getByAddress("s1", new byte[] {0, 0, 0, 1}));
+        add(getByAddress("s2", new byte[] {0, 0, 0, 2}));
+      }
+    };
+    when(testContext.getHostsForRole(SERVER.name())).thenReturn(servers);
+
+    final StartEnvoy startEnvoy = new StartEnvoy(0, 0, 3, null);
+    final String config = startEnvoy.generateConfig(testContext);
+
+    assertThat(config).isEqualTo("static_resources:\n"
+        + "  listeners:\n"
+        + "    - name: geode_listener\n"
+        + "      address:\n"
+        + "        socket_address:\n"
+        + "          address: 0.0.0.0\n"
+        + "          port_value: 3\n"
+        + "      reuse_port: true\n"
+        + "      tcp_backlog_size: 1000\n"
+        + "      listener_filters:\n"
+        + "        - name: envoy.filters.listener.tls_inspector\n"
+        + "      filter_chains:\n"
+        + "        - filter_chain_match:\n"
+        + "            server_names:\n"
+        + "              - 'l1'\n"
+        + "            transport_protocol: tls\n"
+        + "          filters:\n"
+        + "            - name: envoy.filters.network.sni_dynamic_forward_proxy\n"
+        + "              typed_config:\n"
+        + "                '@type': type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3alpha.FilterConfig\n"
+        + "                port_value: 0\n"
+        + "                dns_cache_config:\n"
+        + "                  name: geode_cluster_dns_cache_config\n"
+        + "                  dns_lookup_family: V4_ONLY\n"
+        + "            - name: envoy.tcp_proxy\n"
+        + "              typed_config:\n"
+        + "                '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy\n"
+        + "                stat_prefix: tcp\n"
+        + "                cluster: geode_cluster\n"
+        + "                max_connect_attempts: 1000000000\n"
+        + "                access_log:\n"
+        + "                  - name: envoy.access_loggers.file\n"
+        + "                    typed_config:\n"
+        + "                      \"@type\": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog\n"
+        + "                      log_format:\n"
+        + "                        text_format: \"[%START_TIME%] %DOWNSTREAM_REMOTE_ADDRESS% %UPSTREAM_HOST% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION%\\n\"\n"
+        + "                      path: /dev/stdout\n"
+        + "        - filter_chain_match:\n"
+        + "            server_names:\n"
+        + "              - 's1'\n"
+        + "              - 's2'\n"
+        + "            transport_protocol: tls\n"
+        + "          filters:\n"
+        + "            - name: envoy.filters.network.sni_dynamic_forward_proxy\n"
+        + "              typed_config:\n"
+        + "                '@type': type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3alpha.FilterConfig\n"
+        + "                port_value: 0\n"
+        + "                dns_cache_config:\n"
+        + "                  name: geode_cluster_dns_cache_config\n"
+        + "                  dns_lookup_family: V4_ONLY\n"
+        + "            - name: envoy.tcp_proxy\n"
+        + "              typed_config:\n"
+        + "                '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy\n"
+        + "                stat_prefix: tcp\n"
+        + "                cluster: geode_cluster\n"
+        + "                max_connect_attempts: 1000000000\n"
+        + "                access_log:\n"
+        + "                  - name: envoy.access_loggers.file\n"
+        + "                    typed_config:\n"
+        + "                      \"@type\": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog\n"
+        + "                      log_format:\n"
+        + "                        text_format: \"[%START_TIME%] %DOWNSTREAM_REMOTE_ADDRESS% %UPSTREAM_HOST% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION%\\n\"\n"
+        + "                      path: /dev/stdout\n"
+        + "  clusters:\n"
+        + "    - name: geode_cluster\n"
+        + "      connect_timeout: 10s\n"
+        + "      lb_policy: CLUSTER_PROVIDED\n"
+        + "      cluster_type:\n"
+        + "        name: envoy.clusters.dynamic_forward_proxy\n"
+        + "        typed_config:\n"
+        + "          '@type': type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig\n"
+        + "          dns_cache_config:\n"
+        + "            name: geode_cluster_dns_cache_config\n"
+        + "            dns_lookup_family: V4_ONLY\n");
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartHAProxyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartHAProxyTest.java
new file mode 100644
index 0000000..dde12ef
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartHAProxyTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.tasks;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+import java.net.InetSocketAddress;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+class StartHAProxyTest {
+
+  @Test
+  void generateConfig() {
+    final Set<InetSocketAddress> members = new HashSet<InetSocketAddress>() {
+      {
+        add(InetSocketAddress.createUnresolved("l1", 1));
+        add(InetSocketAddress.createUnresolved("s2", 2));
+        add(InetSocketAddress.createUnresolved("s3", 3));
+      }
+    };
+    final StartHAProxy startHAProxy = new StartHAProxy(0, 0, 3, null);
+    final String config = startHAProxy.generateConfig(members);
+
+    assertThat(config).isEqualTo("global\n"
+        + "  daemon\n"
+        + "  maxconn 64000\n"
+        + "  spread-checks 4\n"
+        + "defaults\n"
+        + "  log global\n"
+        + "  timeout connect 30000ms\n"
+        + "  timeout client 30000ms\n"
+        + "  timeout server 30000ms\n"
+        + "frontend sniproxy\n"
+        + "  bind *:3\n"
+        + "  mode tcp\n"
+        + "  tcp-request inspect-delay 5s\n"
+        + "  tcp-request content accept if { req_ssl_hello_type 1 }\n"
+        + "  use_backend s2 if { req.ssl_sni s2 }\n"
+        + "  use_backend s3 if { req.ssl_sni s3 }\n"
+        + "  use_backend l1 if { req.ssl_sni l1 }\n"
+        + "backend s2\n"
+        + "  mode tcp\n"
+        + "  server host s2:2\n"
+        + "backend s3\n"
+        + "  mode tcp\n"
+        + "  server host s3:3\n"
+        + "backend l1\n"
+        + "  mode tcp\n"
+        + "  server host l1:1\n");
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartSniProxyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartSniProxyTest.java
deleted file mode 100644
index 2b8cd21..0000000
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/StartSniProxyTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License. You may obtain a
- * copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License
- * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.geode.benchmark.tasks;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.stream.Stream;
-
-import org.junit.jupiter.api.Test;
-
-class StartSniProxyTest {
-
-  @Test
-  public void generateConfigTest() {
-    final StartSniProxy starter = new StartSniProxy(42);
-    final String config =
-        starter.generateHaProxyConfig(Stream.of("locator-one-internal"),
-            Stream.of("locator-one-external"),
-            Stream.of("server-one-internal", "server-two-internal"),
-            Stream.of("server-one-external", "server-two-external"));
-    assertThat(config).isEqualTo("global\n"
-        + "  log stdout format raw local0 debug\n"
-        + "  maxconn 5000\n"
-        + "defaults\n"
-        + "  log global\n"
-        /*
-         * We're leaving timeouts unspecified so they are infinite. Benchmarks do bad things
-         * when the proxy breaks connections.
-         */
-        // + " timeout client 100s\n"
-        // + " timeout connect 100s\n"
-        // + " timeout server 100s\n"
-        + "frontend sniproxy\n"
-        + "  bind *:15443\n"
-        + "  mode tcp\n"
-        + "  tcp-request inspect-delay 5s\n"
-        + "  tcp-request content accept if { req_ssl_hello_type 1 }\n"
-        + "  use_backend locators-locator-one-internal if { req.ssl_sni -i locator-one-external }\n"
-        + "  use_backend servers-server-one-internal if { req.ssl_sni -i server-one-external }\n"
-        + "  use_backend servers-server-two-internal if { req.ssl_sni -i server-two-external }\n"
-        + "  default_backend locators-locator-one-internal\n"
-        + "backend locators-locator-one-internal\n"
-        + "  mode tcp\n"
-        + "  server locator1 locator-one-internal:42\n"
-        + "backend servers-server-one-internal\n"
-        + "  mode tcp\n"
-        + "  server server1 server-one-internal:40404\n"
-        + "backend servers-server-two-internal\n"
-        + "  mode tcp\n"
-        + "  server server1 server-two-internal:40404\n");
-  }
-}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/GeodeBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/GeodeBenchmarkTest.java
index 84337f4..58eb5c8 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/GeodeBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/GeodeBenchmarkTest.java
@@ -18,14 +18,18 @@
 package org.apache.geode.benchmark.tests;
 
 import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
+import static org.apache.geode.benchmark.topology.Ports.SERVER_PORT;
+import static org.apache.geode.benchmark.topology.Ports.SNI_PROXY_PORT;
 import static org.apache.geode.benchmark.topology.Roles.PROXY;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import org.apache.geode.benchmark.tasks.StartSniProxy;
+import org.apache.geode.benchmark.tasks.StartEnvoy;
+import org.apache.geode.benchmark.tasks.StartHAProxy;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestStep;
 
@@ -37,12 +41,17 @@ import org.apache.geode.perftest.TestStep;
 class GeodeBenchmarkTest {
 
   private TestConfig config;
-  private TestStep startProxyStep;
+  private TestStep startHAProxyStep;
+  private TestStep startEnvoyStep;
 
   @BeforeEach
   public void beforeEach() {
-    startProxyStep =
-        new TestStep(new StartSniProxy(LOCATOR_PORT), new String[] {PROXY.name()});
+    startHAProxyStep =
+        new TestStep(new StartHAProxy(LOCATOR_PORT, SERVER_PORT, SNI_PROXY_PORT, null),
+            new String[] {PROXY.name()});
+    startEnvoyStep =
+        new TestStep(new StartEnvoy(LOCATOR_PORT, SERVER_PORT, SNI_PROXY_PORT, null),
+            new String[] {PROXY.name()});
   }
 
   @AfterAll
@@ -54,28 +63,35 @@ class GeodeBenchmarkTest {
   public void withoutSniProxy() {
     System.clearProperty("withSniProxy");
     config = ClientServerBenchmark.createConfig();
-    assertThat(config.getBefore()).doesNotContain(startProxyStep);
+    assertThat(config.getBefore()).doesNotContain(startHAProxyStep, startEnvoyStep);
   }
 
   @Test
-  public void withSniProxyFalse() {
-    System.setProperty("withSniProxy", "false");
+  public void withSniProxyInvalid() {
+    System.setProperty("withSniProxy", "invalid");
+    assertThatThrownBy(() -> ClientServerBenchmark.createConfig())
+        .isInstanceOf(IllegalArgumentException.class);
+  }
+
+  @Test
+  public void withSniProxyDefault() {
+    System.setProperty("withSniProxy", "");
     config = ClientServerBenchmark.createConfig();
-    assertThat(config.getBefore()).doesNotContain(startProxyStep);
+    assertThat(config.getBefore()).contains(startHAProxyStep).doesNotContain(startEnvoyStep);
   }
 
   @Test
-  public void withSniProxyTrue() {
-    System.setProperty("withSniProxy", "true");
+  public void withSniProxyHAProxy() {
+    System.setProperty("withSniProxy", "HAProxy");
     config = ClientServerBenchmark.createConfig();
-    assertThat(config.getBefore()).contains(startProxyStep);
+    assertThat(config.getBefore()).contains(startHAProxyStep);
   }
 
   @Test
-  public void withSniProxyNotLowercaseFalse() {
-    System.setProperty("withSniProxy", "AnythING");
+  public void withSniProxyEnvoy() {
+    System.setProperty("withSniProxy", "Envoy");
     config = ClientServerBenchmark.createConfig();
-    assertThat(config.getBefore()).contains(startProxyStep);
+    assertThat(config.getBefore()).contains(startEnvoyStep);
   }
 
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxyTest.java
index 51d7ee5..f2d4fbc 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxyTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSNIProxyTest.java
@@ -15,7 +15,6 @@
 
 package org.apache.geode.benchmark.topology;
 
-
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -23,8 +22,10 @@ import java.util.Properties;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
 
+import org.apache.geode.benchmark.topology.ClientServerTopologyWithSNIProxy.SniProxyImplementation;
 import org.apache.geode.perftest.TestConfig;
 
 public class ClientServerTopologyWithSNIProxyTest {
@@ -41,11 +42,11 @@ public class ClientServerTopologyWithSNIProxyTest {
     System.setProperties(systemProperties);
   }
 
-
-  @Test
-  public void configWithNoSsl() {
+  @ParameterizedTest
+  @EnumSource(SniProxyImplementation.class)
+  public void configWithNoSsl(final SniProxyImplementation sniProxyImplementation) {
     TestConfig testConfig = new TestConfig();
-    ClientServerTopologyWithSNIProxy.configure(testConfig);
+    ClientServerTopologyWithSNIProxy.configure(testConfig, sniProxyImplementation);
     assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-DwithSsl=true");
   }
 
diff --git a/infrastructure/scripts/aws/image/files/docker-compose.yml b/infrastructure/scripts/aws/image/files/docker-compose.yml
deleted file mode 100644
index 78faf55..0000000
--- a/infrastructure/scripts/aws/image/files/docker-compose.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-version: '3'
-services:
-  haproxy:
-    container_name: 'haproxy'
-    image: 'haproxy:2.1'
-    ports:
-      - "15443:15443"
-    volumes:
-      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
-
diff --git a/infrastructure/scripts/aws/image/packer.json b/infrastructure/scripts/aws/image/packer.json
index 7a09685..a22e827 100644
--- a/infrastructure/scripts/aws/image/packer.json
+++ b/infrastructure/scripts/aws/image/packer.json
@@ -41,11 +41,6 @@
       "destination": "/tmp/defaults.cfg"
     },
     {
-      "type": "file",
-      "source": "./files/docker-compose.yml",
-      "destination": "/home/geode/docker-compose.yml"
-    },
-    {
       "type": "shell",
       "inline": [
         "# install Docker per https://docs.docker.com/engine/install/ubuntu/",
@@ -61,12 +56,6 @@
     {
       "type": "shell",
       "inline": [
-        "sudo apt install -y docker-compose"
-      ]
-    },
-    {
-      "type": "shell",
-      "inline": [
         "# permissions per https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user",
         "sudo usermod -aG docker geode"
       ]