From 6fccf912e382e0532824c34567f1dfc6990fe7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=B6nthal?= Date: Thu, 23 Jan 2025 18:11:15 +0100 Subject: [PATCH] feat(dns): use dns resolvers to for dns failover routing scenarios --- .github/workflows/test.yaml | 87 +++++++++++++++++++++++++++++-------- Dockerfile | 2 +- README.md | 12 ++++- haproxy.cfg | 23 ++++++++-- 4 files changed, 98 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 71650f8..230d9b1 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -60,10 +60,13 @@ jobs: docker logs statista_proxy - name: Test Docker Image with Cookie Strategy without cookie + id: test-cookie-no-cookie + continue-on-error: true run: | curl -Is localhost:80 | grep -i -e "x-proxy-flow: route-to-legacy" - name: Test Docker Image with Cookie Strategy with cookie + continue-on-error: true run: | curl -Is --cookie "my_app_routing=new" localhost:80 | grep -i -e "x-proxy-flow: route-to-new" @@ -86,18 +89,26 @@ jobs: docker logs statista_proxy - name: Test Docker Image with Percentage Strategy backend selection + id: test-percentage-strategy + continue-on-error: true run: | curl -Is localhost:80 | grep -i -e "x-proxy-flow: route-to-percentage" - name: Test Docker Image with Percentage Strategy with sticky cookie on old domain + id: test-percentage-sticky-old-domain + continue-on-error: true run: | curl -Is localhost:80 --cookie "my_app=old_domain" | grep -i -e "server: Apache" - name: Test Docker Image with Percentage Strategy with sticky cookie on new domain + id: test-percentage-sticky-new-domain + continue-on-error: true run: | curl -Is localhost:80 --cookie "my_app=new_domain" | grep -i -e "x-served-by: cache-" - name: Test Docker Image with Percentage Strategy with round robin + id: test-percentage-round-robin + continue-on-error: true # sadly we dont know which server serves us first, so we have to check both run: | curl -Is localhost:80 | grep -i -e "server:" -e "x-served-by:" -e "set-cookie: my_app=new_domain; path=/" -e "set-cookie: my_app=old_domain; path=/" @@ -107,53 +118,91 @@ jobs: if: success() || failure() run: docker rm -f statista_proxy + # validation TEMPLATE + #- name: name of the test + # timeout-minutes: 1 # set so if the command runs successfully the test will fail + # id: test-validation-percentage-new # important since all steps run and we collect the failure at then end + # continue-on-error: true # important for allowing other steps to be run regardless of this one which might fail + # run: | + # ! docker run \ # important simply negate the exit code, since we expect the container to fail (which means succesfull validation) + # -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 \ + # "${{ env.IMAGE_NAME }}" + # variable validation - name: Run Docker Image with Invalid new Percentage + timeout-minutes: 1 + id: test-validation-percentage-new + continue-on-error: true run: | - set +e - - docker run \ + ! docker run \ -e STRATEGY=PERCENTAGE \ -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 \ -e PERCENTAGE_NEW=foo -e PERCENTAGE_OLD=50 \ -e COOKIE_PERCENTAGE_NAME=my_app \ "${{ env.IMAGE_NAME }}" - if [[ $? == 1 ]]; then exit 0; else exit 1; fi - - name: Run Docker Image with Invalid old Percentage + timeout-minutes: 1 + id: test-validation-percentage-old + continue-on-error: true run: | - set +e - - docker run \ + ! docker run \ -e STRATEGY=PERCENTAGE \ -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 \ -e PERCENTAGE_NEW=50 -e PERCENTAGE_OLD=foo \ -e COOKIE_PERCENTAGE_NAME=my_app \ "${{ env.IMAGE_NAME }}" - if [[ $? == 1 ]]; then exit 0; else exit 1; fi - - name: Run Docker Image with Invalid old domain + timeout-minutes: 1 + id: test-validation-old-domain + continue-on-error: true run: | - set +e - - docker run \ + ! docker run \ -e STRATEGY=PERCENTAGE \ -e NEW_DOMAIN=apache.org:443 \ -e COOKIE_PERCENTAGE_NAME=my_app \ "${{ env.IMAGE_NAME }}" - if [[ $? == 1 ]]; then exit 0; else exit 1; fi - - name: Run Docker Image with Invalid new domain + timeout-minutes: 1 + id: test-validation-new-domain + continue-on-error: true run: | - set +e - - docker run \ + ! docker run \ -e STRATEGY=PERCENTAGE \ -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:lol \ -e COOKIE_PERCENTAGE_NAME=my_app \ "${{ env.IMAGE_NAME }}" - if [[ $? == 1 ]]; then exit 0; else exit 1; fi + - name: Run Docker Image with Invalid server count + timeout-minutes: 1 + id: test-validation-server-count + continue-on-error: true + run: | + ! docker run \ + -e STRATEGY=PERCENTAGE \ + -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 \ + -e COOKIE_PERCENTAGE_NAME=my_app \ + -e SERVER_COUNT=foo \ + "${{ env.IMAGE_NAME }}" + + - name: Run Docker Image with Invalid DNS Resolver + timeout-minutes: 1 + id: test-validation-dns-resolver + continue-on-error: true + run: | + ! docker run \ + -e STRATEGY=PERCENTAGE \ + -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 \ + -e COOKIE_PERCENTAGE_NAME=my_app \ + -e DNS_RESOLVER=foo \ + "${{ env.IMAGE_NAME }}" + + - name: Check for failures + if: always() + env: + STEPS_CONTEXT: ${{ toJson(steps) }} + run: | + set +e + ! echo "$STEPS_CONTEXT" | grep -q 'failure' diff --git a/Dockerfile b/Dockerfile index d4eb599..b4c84b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM haproxy:lts-alpine -LABEL org.opencontainers.image.source=https://github.com/statista-oss/proxy-router +LABEL org.opencontainers.image.source="https://github.com/statista-oss/proxy-router" LABEL org.opencontainers.image.description="haproxy configurable through env vars for different routing strategies" USER root diff --git a/README.md b/README.md index 98c1b5c..6288af3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,14 @@ how much traffic should be routed to the old application ### PERCENTAGE_NEW how much traffic should be routed to the new application +### DNS_RESOLVER (optional, default=1.1.1.1) +The DNS resolver to use for resolving the domain names to IP addresses. +If you want to reroute internal traffic you might want to change that. + +### SERVER_COUNT (optional, default=5) +as we are using DNS Resolver we try to create a server for each IP address we get back from the DNS query. +This is the maximum amount of servers we will create. + ## Local Testing @@ -54,7 +62,7 @@ docker build -t statista-proxy . run the container ```bash -docker run -p80:80 -e STRATEGY=PERCENTAGE -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 -e PERCENTAGE_NEW=50 -e PERCENTAGE_OLD=50 -e COOKIE_PERCENTAGE_NAME=my_app statista_proxy +docker run -p80:80 -e STRATEGY=PERCENTAGE -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 -e PERCENTAGE_NEW=50 -e PERCENTAGE_OLD=50 -e COOKIE_PERCENTAGE_NAME=my_app statista-proxy ``` run requests (since its round robin every request should be flipped) @@ -69,7 +77,7 @@ further requests made by the same client will stick to this server ### Cookie based routing ```bash -docker run -p80:80 -e STRATEGY=COOKIE -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 -e COOKIE_STRATEGY_NAME="my_app_routing=new" statista_proxy +docker run -p80:80 -e STRATEGY=COOKIE -e OLD_DOMAIN=haproxy.com:443 -e NEW_DOMAIN=apache.org:443 -e COOKIE_STRATEGY_NAME="my_app_routing=new" statista-proxy ``` run requests (since its round robin every request should be flipped) diff --git a/haproxy.cfg b/haproxy.cfg index f036880..e0d45db 100644 --- a/haproxy.cfg +++ b/haproxy.cfg @@ -10,11 +10,15 @@ global # default some environment variables presetenv PERCENTAGE_NEW "0" presetenv PERCENTAGE_OLD "100" + presetenv SERVER_COUNT "5" + presetenv DNS_RESOLVER "1.1.1.1:53" # we source them from the environment here to actually typecheck them set-var proc.percentage_new int("${PERCENTAGE_NEW}") set-var proc.percentage_old int("${PERCENTAGE_OLD}") + set-var proc.server_count int("${SERVER_COUNT}") + #log stdout format raw local0 defaults @@ -22,6 +26,11 @@ defaults timeout client 1m timeout server 1m mode http + default-server check + default-server resolvers dns + default-server resolve-prefer ipv4 + default-server init-addr last,libc,none + default-server ssl verify required ca-file @system-ca #log global #option httplog @@ -47,24 +56,30 @@ frontend http-in backend old_domain http-response set-header X-Proxy-Flow route-to-legacy option tcp-check + balance leastconn - server default_old "$OLD_DOMAIN" check ssl verify none + server-template default_old "$SERVER_COUNT" "$OLD_DOMAIN" backend percentage_strategy http-response set-header X-Proxy-Flow route-to-percentage option tcp-check + balance leastconn cookie "$COOKIE_PERCENTAGE_NAME" insert indirect # old domain - server old_domain_percentage "$OLD_DOMAIN" check cookie old_domain ssl verify none weight "$PERCENTAGE_OLD" + server-template old_domain_percentage "$SERVER_COUNT" "$OLD_DOMAIN" cookie old_domain weight "$PERCENTAGE_OLD" # new domain - server new_domain_percentage "$NEW_DOMAIN" check cookie new_domain ssl verify none weight "$PERCENTAGE_NEW" + server-template new_domain_percentage "$SERVER_COUNT" "$NEW_DOMAIN" cookie new_domain weight "$PERCENTAGE_NEW" backend cookie_strategy + balance leastconn http-response set-header X-Proxy-Flow route-to-new option tcp-check - server default_new "$NEW_DOMAIN" check ssl verify none + server-template default_new "$SERVER_COUNT" "$NEW_DOMAIN" +resolvers dns + nameserver default "$DNS_RESOLVER" + accepted_payload_size 8192