Skip to content

Commit a50ee92

Browse files
author
James Healy
committed
Add new stack parameter for enabling dualstack docker
Adds new `DockerNetworkingProtocol` Cloud Formation Parameter, for opting in to starting containers with both ipv4 and ipv6 addresses. This might be useful when running the elastic stack on dual stack and ipv6-only subnets. Prior to this change, elastic stack instances could run on dual stack and ipv6-only VPC subnets and many things continue to work as normal. However, docker builds and containers do not - containers are started with private ipv4 addresses and no ipv6 addresses. Attempts to connect to global or VPC ipv6 addresses from inside a container fail. When this new parameter is set to `dualstack`, containers will be assigned an ipv6 address and depending on the configuration of the VPC it's running in, containers may be able to connect to the outside world over ipv6. -- dualstack subnets When the new parameter is set to `dualstack` and the instances are running on a dualstack VPC subnet, we expect: * containers are started with a private ipv4 * containers are started with an ipv6 from 2001:db8::/32 * connecting to external resources over ipv4 works, with docker performing NAT from the container private ipv4 to the EC2 instance AWS assigned ipv4 * connecting to external resources over ipv6 works, with docker performing NAT from the container private ipv6 to the EC2 instance AWS assigned ipv6. NATing ipv6 feels weird, but it's the way docker 25.x works -- ipv6-only subnets When the new parameter is set to dualstack and the instances are running on a ipv6-only VPC subnet, here's what should happen: * containers are started with a private ipv4 * containers are started with an ipv6 from 2001:db8::/32 * connecting to external resources over ipv4 will fail with an error * connecting to external resources over ipv6 works, with docker performing NAT from the container private ipv6 to the EC2 instance AWS assigned ipv6. NATing ipv6 feels weird, but it's the way docker 25.x works * It's very likely your ipv6 subnet will have DNS64 enabled. In this case, DNS hostnames that return only A addresses will have a fake AAAA record added to the response. This record points to an AWS NAT gateway. The container should use the fake ipv6 address and the AWS NAT gateway will translate to ipv4 (for a fee). -- why 2001:db8::/32? https://chameth.com/ipv6-docker-routing/ is a good explaination of the issue. Given docker is NATing the containers ipv6 traffic, the natural ipv6 range to use would be fd00::/8 - the ULA range (https://en.wikipedia.org/wiki/Unique_local_address). That range will partially work, however on a dualstack subnet where containers have working ipv4 and ipv6 addresss, using fd00::/8 will make most requests default to ipv4 instead of ipv6. That's particularly undesirable in a CI environment on AWS because ipv4 traffic almost certainly goes via a NAT gateway with per Gb fees, and CI often generates a lot of AWS ingress traffic. By using 2001:db8::/32 the dualstack containers should default to using ipv6 in most cases where they have the choice of either protocol. This may avoid significant NAT gateway fees. 2001:db8::/32 is technically reserved for documentation use only. The cost benefits are significant though, and docker is using them privately so there should be no negative impact. -- why opt-in? It would be nice to just auto-detect when the instances have a valid ipv6 addresses assigned and automatically start docker in dualstack mode. However, there is significant potential to change the behavior of networking in the container and might have surprising side effects. This prosposes starting opt-in so we can start to build some experience with dualstack docker and AWS. Eventually, it would be great to make it Just Work.
1 parent 5b523a3 commit a50ee92

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

packer/linux/conf/bin/bk-configure-docker.sh

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,36 @@ else
6969
echo User namespace remapping not configured.
7070
fi
7171

72+
# One day we can auto-detect whether the instance is v4-only, dualstack or v6-only. To avoid a
73+
# breaking change though, we'll default to ipv4 only and users can opt into v6 support. The elastic
74+
# stack has always defaulted to v4-only so this ensuring no breaking behaviour.
75+
# v6-only is currently not an option because docker doesn't support it, but maybe one day....
76+
echo Customising docker network configuration...
77+
78+
if [[ "${DOCKER_NETWORKING_PROTOCOL}" == "ipv4" ]]; then
79+
# This is the default
80+
cat <<<"$(
81+
jq \
82+
'."default-address-pools"=[{"base":"172.17.0.0/12","size":20},{"base":"192.168.0.0/16","size":24}]' \
83+
/etc/docker/daemon.json
84+
)" >/etc/docker/daemon.json
85+
elif [[ "${DOCKER_NETWORKING_PROTOCOL}" == "dualstack" ]]; then
86+
# Using v6 inside containers requires DOCKER_EXPERIMENTAL. This is configured
87+
# further down
88+
DOCKER_EXPERIMENTAL=true
89+
cat <<<"$(jq '.ipv6=true' /etc/docker/daemon.json)" >/etc/docker/daemon.json
90+
cat <<<"$(jq '."fixed-cidr-v6"="2001:db8:1::/64"' /etc/docker/daemon.json)" >/etc/docker/daemon.json
91+
cat <<<"$(jq '.ip6tables=true' /etc/docker/daemon.json)" >/etc/docker/daemon.json
92+
cat <<<"$(
93+
jq \
94+
'."default-address-pools"=[{"base":"172.17.0.0/12","size":20},{"base":"192.168.0.0/16","size":24},{"base":"2001:db8:2::/104","size":112}]' \
95+
/etc/docker/daemon.json
96+
)" >/etc/docker/daemon.json
97+
else
98+
# docker 25.0 doesn't support ipv6 only, so we don't support it either
99+
true
100+
fi
101+
72102
if [[ "${DOCKER_EXPERIMENTAL:-false}" == "true" ]]; then
73103
echo Configuring experiment flag for docker daemon...
74104
cat <<<"$(jq '.experimental=true' /etc/docker/daemon.json)" >/etc/docker/daemon.json
@@ -85,13 +115,6 @@ else
85115
echo Instance storage not configured.
86116
fi
87117

88-
echo Customising docker IP address pools...
89-
cat <<<"$(
90-
jq \
91-
'."default-address-pools"=[{"base":"172.17.0.0/12","size":20},{"base":"192.168.0.0/16","size":24}]' \
92-
/etc/docker/daemon.json
93-
)" >/etc/docker/daemon.json
94-
95118
echo Cleaning up docker images...
96119
systemctl start docker-low-disk-gc.service
97120

templates/aws-stack.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ Parameters:
462462
- "false"
463463
Default: "true"
464464

465+
DockerNetworkingProtocol:
466+
Type: String
467+
Description: Which IP version to enable for docker containers and building docker images. Only applies to Linux instances, not Windows.
468+
AllowedValues:
469+
- "ipv4"
470+
- "dualstack"
471+
Default: "ipv4"
472+
465473
EnableSecretsPlugin:
466474
Type: String
467475
Description: Enables s3-secrets plugin for all pipelines
@@ -1205,6 +1213,7 @@ Resources:
12051213
<powershell>
12061214
$Env:DOCKER_USERNS_REMAP="${EnableDockerUserNamespaceRemap}"
12071215
$Env:DOCKER_EXPERIMENTAL="${EnableDockerExperimental}"
1216+
$Env:DOCKER_NETWORKING_PROTOCOL="${DockerNetworkingProtocol}"
12081217
powershell -file C:\buildkite-agent\bin\bk-configure-docker.ps1 >> C:\buildkite-agent\elastic-stack.log
12091218
12101219
$Env:BUILDKITE_STACK_NAME="${AWS::StackName}"
@@ -1260,6 +1269,7 @@ Resources:
12601269
#!/bin/bash -v
12611270
DOCKER_USERNS_REMAP=${EnableDockerUserNamespaceRemap} \
12621271
DOCKER_EXPERIMENTAL=${EnableDockerExperimental} \
1272+
DOCKER_NETWORKING_PROTOCOL=${DockerNetworkingProtocol} \
12631273
BUILDKITE_ENABLE_INSTANCE_STORAGE="${EnableInstanceStorage}" \
12641274
/usr/local/bin/bk-configure-docker.sh
12651275
--==BOUNDARY==

0 commit comments

Comments
 (0)