Blue-Green

Load balancing across two services.

Blue-Green

Images are clickable.

Manifests

services:
  blue:
    image: hashicorp/http-echo
    deploy:
      labels:
        - "millau.enabled=true"
        - "millau.hosts=company.com blue.local company.local"
        - "millau.cert=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRVENDQWlrQ0ZIemVjY3FYaVg0V0ZNRXZUWklvUHdSUXQvZktNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1GMHgKQ3pBSkJnTlZCQVlUQWtSRk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbApjbTVsZENCWGFXUm5hWFJ6SUZCMGVTQk1kR1F4RmpBVUJnTlZCQU1NRFdOdmJYQmhibmt1Ykc5allXd3dIaGNOCk1qVXdOVEEwTVRZd09ERXpXaGNOTWpZd05UQTBNVFl3T0RFeldqQmRNUXN3Q1FZRFZRUUdFd0pFUlRFVE1CRUcKQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVoTUI4R0ExVUVDZ3dZU1c1MFpYSnVaWFFnVjJsa1oybDBjeUJRZEhrZwpUSFJrTVJZd0ZBWURWUVFEREExamIyMXdZVzU1TG14dlkyRnNNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DCkFROEFNSUlCQ2dLQ0FRRUFsa01mMzRsZitjM1BtS2dvM09LbkN5L3Fjc0FSK1YxNVZQS1pUeU1id1VGQ0FLVGIKOUpic2Jha3pNWnAyVHlQTU53MmF5WXkyVGFtZ3JSNW92bUQzWlk3WmNSU2xKZVdIRXR6ZHN1OWkyc1dWeHFRaQpqWEtZa1ZqaXBSRE1RdERnWWlLN1Z1d25pNmFKWFlFNlAwN05ISVVYbVdRL1NyMERSL2ZRVkxJSzVqWFVidktoCmNYbnZsZWk3ck5takpxRXJ1Q3ppc1Q2dTBDWW1kdGlBZUxRbEdRZkt3bzBMVUh5bllVcmlQK3BiT05FRFF2eWEKTWgydThPdW1LUGdFaVI0czloZlFGNkFDL1BGaUhVQVd5R0dRS3BYMDJKK0R3eG5hc1hSbkwzMmNXYkFaanlkZgpTOVF5QUo3WWsvSUl2cDVSSzdKK05aaUxCbjZEYlNKNU03ZVJPUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBCkE0SUJBUUJLTW56QTkrU0FOVTFZNHdmR2FoK2MrTWpVSTA3UzZ6dHVWd3EvbEY3ZUxBVFNpUDc1cEJYOUUwR1AKd3dhMjY1SEVWUXFnVStmUHVLSlM4MFc5VUtxZGJxYVhQRHNlZng0MzBVc3cvUVVJYnhxTUVLTXZUcUpwcWk1NApMUlBjWHJCZTdvNzgyczFDUkF5UHpqNWJiRE1jeHlCVVdPeFFiQndZZ3UwY3ZnVFpTclRGWW1vbTRucnpiMCtmClA4eWZZMHRsTXFCYmo0Nm96Y0xaUU1JTFhLTWJVZlB2djZWVWlnYmoySFZ4aml3REQyL1B2elBwYnlsbnl5OXgKS1FlR1E5bU0xdm5wbm8valpCVjRqREg1OTV1UmNGNnFpY1JZYnNmdlZVWWwvNzhiREpxdXVmNHpwMHNIaks2egpCYlhHN29GV3pkQlJkQzBud0VXMGE4Q1FjVC9jCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
        - "millau.key=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ1dReC9maVYvNXpjK1kKcUNqYzRxY0xMK3B5d0JINVhYbFU4cGxQSXh2QlFVSUFwTnYwbHV4dHFUTXhtblpQSTh3M0RackpqTFpOcWFDdApIbWkrWVBkbGp0bHhGS1VsNVljUzNOMnk3MkxheFpYR3BDS05jcGlSV09LbEVNeEMwT0JpSXJ0VzdDZUxwb2xkCmdUby9UczBjaFJlWlpEOUt2UU5IOTlCVXNncm1OZFJ1OHFGeGVlK1Y2THVzMmFNbW9TdTRMT0t4UHE3UUppWjIKMklCNHRDVVpCOHJDalF0UWZLZGhTdUkvNmxzNDBRTkMvSm95SGE3dzY2WW8rQVNKSGl6MkY5QVhvQUw4OFdJZApRQmJJWVpBcWxmVFluNFBER2RxeGRHY3ZmWnhac0JtUEoxOUwxRElBbnRpVDhnaStubEVyc240MW1Jc0dmb050Cklua3p0NUU1QWdNQkFBRUNnZ0VBQlBUQjA1SjEvTHdZUi84ZGdGSC80SG5JNmRab3dWd0ZndC9EK0ZhazZJTGoKVHppSE5ZVStjTEsxbFk3YXJvVEJXUytCc0VkWXFNY2F0elNuOWl4Wll3RytZSzRNQ3RKWTJpNXJqbG02S3BOOAorNVVYN3FqUTlkUHFURGQ1bnRxT2NucjhZdHpEOTR3QjJLdXZTMWRDMWorVDh1TkFYL2hNQWM3ZmVEaDZVNDEyCkJvZFhpL1g3L0VnV05BQko3d3BBWXFheGNaN1ZneUMvMVZkYzNRcUlPL2d3dDdPYzFpVHhUYkh5R1VRUnZaTFAKSkJaY2N4OUN4MzM1YWdHNFN1anJKUEZTa0pab1FHWkZQMFpBbUw3UkxPTlI1WFJXeGNzSGdQejNNTi9lVlhOeApzUSszRTJxYUc5eC81UDUrWnBaQ2M4YUxUSll4a0htL3d2TldkRkxHWVFLQmdRQzgwTFQzMFZZQ3R5UzZRbWRnClJUREtEa2xiQm41ZGxjamJjK1lFQzF0dnQzc2V6aDM2eTJ0Ym5aOXdDSWYreFcxVUEvOEN6bE9MbGtDRjdsT3EKMWV3MEFuZlU4OGdGaE4vMitNZXg4djVNcTk3Z1dOOTFPRHk2RCtyNk9LYTU5NEpKL0ZuRkZSS1VzcEtSYlV2MQpHOFdqSjFJaFQrU1JOY1gwYUlqbzB6NUlRd0tCZ1FETHVwallhczZwVlVoYWoyTEhPVDdOcVF6c0R5WEwrV0xSCkJGNXNYaEhJZ2ViaHlGYW14Q1Z5ZkpyOEtjUzZuekRwWEVjOU5PemZxaW9xZHJmSTgwbmptbW81Y2Z6WFZXQ0cKVnpmTEZrTXp0U0o1bURhK3RGYUgvczMvNG54bTl1Z3VLZGxWb0h0cTVLclFpeFJXSFJWaCt2a3phRVlaL3lIaQorVDRRVW43VzB3S0JnQ0RIMDBOYkVxV3lDaXhVTHdKbHdNeXpva1N0WWU3NWZEK3RaWGU2U1F1aVBUdmg2UTZMCmxuMzNaYURidWxDZkVoWU9BNDRIS0RZSnhmWDZwSWY2VCtIUHMrSkMySzBzbm4yUVA0dERkcWIxQitMSFN6UmMKcVI1Y2VSMHNETFlvcEhRWDNiWDdySmdvVUkzeS9jYjJKVi9vQ2JoQzJKbmxUaExhdXpwQXdvVmxBb0dCQUx1OApMNnorZjRJN1BIbDNPQ2N0OFZuRE5rMHhpRHIyak1EZnJjTmV6RFJnd0VtL08xcGgxY0F5WXYrTDNlVzdYRzh1CndkYjlObXJGZkNXMVVKYTAycG9LYVdpV1NkQ0V0SFlYV1Nxa1VUa002Yi96TEQrQUgxMGl5T0hMNjZkV1Q0NTYKc2ZQZDl0MEI2d1pSbzJQWFdTVDVDVHIrRTl0UTZBRW9mTEM5MUhSUkFvR0FlR3dCWU9xUmxvTkprNk12dkxNdgpQZVBueXNQVkp5UmFQeFkremJObWZxa283ZldaSjRLOWRZc2ZQRkhybEVQVVpoN0E3YUFVQnQ0d3dRU2EyM2RXCml2Z1hmd2Nic05oWTZUckxBRDdTWHJwUkFHeDlkOTN5aXRTWnZLdmo3Sm9qUkljVytUNjVNVFNVeXJva29lU2YKS0t0dlVCenpTZklQY3Z3aFFoZDRWTU09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
        - "millau.port=9000"
        - "millau.path=/api/"
    command: ["-listen=:9000", "-text=blue"]
    ports:
      - "9000"
    networks:
      - millau
  green:
    image: hashicorp/http-echo
    deploy:
      labels:
        - "millau.enabled=true"
        - "millau.hosts=company.com green.local company.local"
        - "millau.cert=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRVENDQWlrQ0ZIemVjY3FYaVg0V0ZNRXZUWklvUHdSUXQvZktNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1GMHgKQ3pBSkJnTlZCQVlUQWtSRk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbApjbTVsZENCWGFXUm5hWFJ6SUZCMGVTQk1kR1F4RmpBVUJnTlZCQU1NRFdOdmJYQmhibmt1Ykc5allXd3dIaGNOCk1qVXdOVEEwTVRZd09ERXpXaGNOTWpZd05UQTBNVFl3T0RFeldqQmRNUXN3Q1FZRFZRUUdFd0pFUlRFVE1CRUcKQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVoTUI4R0ExVUVDZ3dZU1c1MFpYSnVaWFFnVjJsa1oybDBjeUJRZEhrZwpUSFJrTVJZd0ZBWURWUVFEREExamIyMXdZVzU1TG14dlkyRnNNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DCkFROEFNSUlCQ2dLQ0FRRUFsa01mMzRsZitjM1BtS2dvM09LbkN5L3Fjc0FSK1YxNVZQS1pUeU1id1VGQ0FLVGIKOUpic2Jha3pNWnAyVHlQTU53MmF5WXkyVGFtZ3JSNW92bUQzWlk3WmNSU2xKZVdIRXR6ZHN1OWkyc1dWeHFRaQpqWEtZa1ZqaXBSRE1RdERnWWlLN1Z1d25pNmFKWFlFNlAwN05ISVVYbVdRL1NyMERSL2ZRVkxJSzVqWFVidktoCmNYbnZsZWk3ck5takpxRXJ1Q3ppc1Q2dTBDWW1kdGlBZUxRbEdRZkt3bzBMVUh5bllVcmlQK3BiT05FRFF2eWEKTWgydThPdW1LUGdFaVI0czloZlFGNkFDL1BGaUhVQVd5R0dRS3BYMDJKK0R3eG5hc1hSbkwzMmNXYkFaanlkZgpTOVF5QUo3WWsvSUl2cDVSSzdKK05aaUxCbjZEYlNKNU03ZVJPUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBCkE0SUJBUUJLTW56QTkrU0FOVTFZNHdmR2FoK2MrTWpVSTA3UzZ6dHVWd3EvbEY3ZUxBVFNpUDc1cEJYOUUwR1AKd3dhMjY1SEVWUXFnVStmUHVLSlM4MFc5VUtxZGJxYVhQRHNlZng0MzBVc3cvUVVJYnhxTUVLTXZUcUpwcWk1NApMUlBjWHJCZTdvNzgyczFDUkF5UHpqNWJiRE1jeHlCVVdPeFFiQndZZ3UwY3ZnVFpTclRGWW1vbTRucnpiMCtmClA4eWZZMHRsTXFCYmo0Nm96Y0xaUU1JTFhLTWJVZlB2djZWVWlnYmoySFZ4aml3REQyL1B2elBwYnlsbnl5OXgKS1FlR1E5bU0xdm5wbm8valpCVjRqREg1OTV1UmNGNnFpY1JZYnNmdlZVWWwvNzhiREpxdXVmNHpwMHNIaks2egpCYlhHN29GV3pkQlJkQzBud0VXMGE4Q1FjVC9jCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
        - "millau.key=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ1dReC9maVYvNXpjK1kKcUNqYzRxY0xMK3B5d0JINVhYbFU4cGxQSXh2QlFVSUFwTnYwbHV4dHFUTXhtblpQSTh3M0RackpqTFpOcWFDdApIbWkrWVBkbGp0bHhGS1VsNVljUzNOMnk3MkxheFpYR3BDS05jcGlSV09LbEVNeEMwT0JpSXJ0VzdDZUxwb2xkCmdUby9UczBjaFJlWlpEOUt2UU5IOTlCVXNncm1OZFJ1OHFGeGVlK1Y2THVzMmFNbW9TdTRMT0t4UHE3UUppWjIKMklCNHRDVVpCOHJDalF0UWZLZGhTdUkvNmxzNDBRTkMvSm95SGE3dzY2WW8rQVNKSGl6MkY5QVhvQUw4OFdJZApRQmJJWVpBcWxmVFluNFBER2RxeGRHY3ZmWnhac0JtUEoxOUwxRElBbnRpVDhnaStubEVyc240MW1Jc0dmb050Cklua3p0NUU1QWdNQkFBRUNnZ0VBQlBUQjA1SjEvTHdZUi84ZGdGSC80SG5JNmRab3dWd0ZndC9EK0ZhazZJTGoKVHppSE5ZVStjTEsxbFk3YXJvVEJXUytCc0VkWXFNY2F0elNuOWl4Wll3RytZSzRNQ3RKWTJpNXJqbG02S3BOOAorNVVYN3FqUTlkUHFURGQ1bnRxT2NucjhZdHpEOTR3QjJLdXZTMWRDMWorVDh1TkFYL2hNQWM3ZmVEaDZVNDEyCkJvZFhpL1g3L0VnV05BQko3d3BBWXFheGNaN1ZneUMvMVZkYzNRcUlPL2d3dDdPYzFpVHhUYkh5R1VRUnZaTFAKSkJaY2N4OUN4MzM1YWdHNFN1anJKUEZTa0pab1FHWkZQMFpBbUw3UkxPTlI1WFJXeGNzSGdQejNNTi9lVlhOeApzUSszRTJxYUc5eC81UDUrWnBaQ2M4YUxUSll4a0htL3d2TldkRkxHWVFLQmdRQzgwTFQzMFZZQ3R5UzZRbWRnClJUREtEa2xiQm41ZGxjamJjK1lFQzF0dnQzc2V6aDM2eTJ0Ym5aOXdDSWYreFcxVUEvOEN6bE9MbGtDRjdsT3EKMWV3MEFuZlU4OGdGaE4vMitNZXg4djVNcTk3Z1dOOTFPRHk2RCtyNk9LYTU5NEpKL0ZuRkZSS1VzcEtSYlV2MQpHOFdqSjFJaFQrU1JOY1gwYUlqbzB6NUlRd0tCZ1FETHVwallhczZwVlVoYWoyTEhPVDdOcVF6c0R5WEwrV0xSCkJGNXNYaEhJZ2ViaHlGYW14Q1Z5ZkpyOEtjUzZuekRwWEVjOU5PemZxaW9xZHJmSTgwbmptbW81Y2Z6WFZXQ0cKVnpmTEZrTXp0U0o1bURhK3RGYUgvczMvNG54bTl1Z3VLZGxWb0h0cTVLclFpeFJXSFJWaCt2a3phRVlaL3lIaQorVDRRVW43VzB3S0JnQ0RIMDBOYkVxV3lDaXhVTHdKbHdNeXpva1N0WWU3NWZEK3RaWGU2U1F1aVBUdmg2UTZMCmxuMzNaYURidWxDZkVoWU9BNDRIS0RZSnhmWDZwSWY2VCtIUHMrSkMySzBzbm4yUVA0dERkcWIxQitMSFN6UmMKcVI1Y2VSMHNETFlvcEhRWDNiWDdySmdvVUkzeS9jYjJKVi9vQ2JoQzJKbmxUaExhdXpwQXdvVmxBb0dCQUx1OApMNnorZjRJN1BIbDNPQ2N0OFZuRE5rMHhpRHIyak1EZnJjTmV6RFJnd0VtL08xcGgxY0F5WXYrTDNlVzdYRzh1CndkYjlObXJGZkNXMVVKYTAycG9LYVdpV1NkQ0V0SFlYV1Nxa1VUa002Yi96TEQrQUgxMGl5T0hMNjZkV1Q0NTYKc2ZQZDl0MEI2d1pSbzJQWFdTVDVDVHIrRTl0UTZBRW9mTEM5MUhSUkFvR0FlR3dCWU9xUmxvTkprNk12dkxNdgpQZVBueXNQVkp5UmFQeFkremJObWZxa283ZldaSjRLOWRZc2ZQRkhybEVQVVpoN0E3YUFVQnQ0d3dRU2EyM2RXCml2Z1hmd2Nic05oWTZUckxBRDdTWHJwUkFHeDlkOTN5aXRTWnZLdmo3Sm9qUkljVytUNjVNVFNVeXJva29lU2YKS0t0dlVCenpTZklQY3Z3aFFoZDRWTU09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
        - "millau.port=9000"
        - "millau.path=/api/"
    command: ["-listen=:9000", "-text=green"]
    ports:
      - "9000"
    networks:
      - millau
  red:
    image: hashicorp/http-echo
    deploy:
      labels:
        - "millau.enabled=false"
        - "millau.hosts=company.com company.local"
        - "millau.port=9000"
        - "millau.path=/api/"
    command: ["-listen=:9000", "-text=red"]
    ports:
      - "9000"
    networks:
      - millau
  other:
    image: hashicorp/http-echo
    command: ["-listen=:9000", "-text=other"]
    ports:
      - "9000"
networks:
  millau:
    external: true

Features

Load Balancing

curl -i -H 'Host: company.com' localhost:8080/api/

returns HTTP 200 and responds from the blue or green services due to multiple host matching.

No Load Balancing

The service red excluded from the load balancing. The service other is not controlled by Millau at all.

Host and Path Matching

curl -i -H 'Host: green.local' localhost:8080/api/

returns HTTP 200 and responds from the green service only due to single host matching.

curl -i -H 'Host: blue.local' localhost:8080/api/

returns HTTP 200 and responds from the blue service only due to single host matching.

No Matching

curl -i -H 'Host: company.locall' localhost:8080

returns HTTP 502 No matching services found because there is no matching host.

curl -i -H 'Host: company.local' localhost:8080/apii/

returns HTTP 502 No matching services found because there is no matching path.

TLS Termination

curl -k --resolve company.local:8443:127.0.0.1 --connect-to company.local:8443 -H 'Host: company.local' https://company.local:8443/api/
curl -k --http2 --resolve company.local:8443:127.0.0.1 --connect-to company.local:8443 -H 'Host: company.local' https://company.local:8443/api/

returns HTTP 200 and responds from the blue or green services due to multiple host matching.

This HTTPS request uses s self-signed TLS certificate issued to company.local. The labels millau.cert and millau.key contain the certificate components encoded in base64.