If you use the host network mode for a ECS task on EC2 launch type, the network stack of the task is not isolated from the underlying EC2 host. The containers do not get its own IP-addresses allocated.
Host mode networking can be useful to optimize performance, and in situations where a container needs to handle a large range of ports, as it does not require network address translation (NAT), and no “userland-proxy” is created for each port.
For example, if you run a container which binds to port 3000 and you use host networking, the container’s application is available on port 3000 on the host’s primary IP address
Below is a diagram of the host mode for EC2 launch type:
In the task definition enter the following parameter for network mode
{
...
"containerDefinitions": [
...
"portMappings": [
{
"containerPort": "3000",
"protocol": "tcp"
}
],
...
"networkMode": "host",
...
}
Very simple approach but with significant disadvantages:
One can leverage the new “ECS exec” feature to access containers and check the network configuration.
Note: The executables you want to run in the interactive shell session must be available in the container image!
source ~/.bashrc
cd ~/environment/ecsworkshop/content/ecs_networking/setup
export TASK_FILE=ecs-networking-demo-host-mode.json
envsubst < ${TASK_FILE}.template > ${TASK_FILE}
export TASK_DEF=$(aws ecs register-task-definition --cli-input-json file://${TASK_FILE} --query 'taskDefinition.taskDefinitionArn' --output text)
export TASK_ARN=$(aws ecs run-task --cluster ${ClusterName} --task-definition ${TASK_DEF} --enable-execute-command --launch-type EC2 --query 'tasks[0].taskArn' --output text)
aws ecs describe-tasks --cluster ${ClusterName} --task ${TASK_ARN}
# sleep to let the container start
sleep 30
aws ecs execute-command --cluster ${ClusterName} --task ${TASK_ARN} --container nginx --command "/bin/sh" --interactive
Inside the container run the following commands:
ip a sh
ip link sh
ip r sh
curl localhost:80
# to leave the interactive session type exit
Sample outputs for host network mode of a task running a nginx:alpine container using ECS exec.
Note that all EC2 host interfaces and routes are visible within the container because it shares the network namespace of the underlying ECS host!
$ aws ecs execute-command--cluster staging --task a1e924295a744c878f237133f58803ed --container nginx --command "/bin/sh" --interactive
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Starting session with SessionId: ecs-execute-command-00f95f492d720713f
/ # ip a sh
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP qlen 1000
link/ether 02:2e:5d:33:eb:ce brd ff:ff:ff:ff:ff:ff
inet 10.0.100.120/24 brd 10.0.100.255 scope global dynamic eth0
valid_lft 3046sec preferred_lft 3046sec
inet6 fe80::2e:5dff:fe33:ebce/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ad:eb:00:35 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:adff:feeb:35/64 scope link
valid_lft forever preferred_lft forever
5: veth92275a3@if4: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0 state UP
link/ether 82:a4:97:04:7d:a5 brd ff:ff:ff:ff:ff:ff
inet6 fe80::80a4:97ff:fe04:7da5/64 scope link
valid_lft forever preferred_lft forever
...
/ # ip link sh
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP qlen 1000
link/ether 02:2e:5d:33:eb:ce brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ad:eb:00:35 brd ff:ff:ff:ff:ff:ff
5: veth92275a3@if4: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0 state UP
link/ether 82:a4:97:04:7d:a5 brd ff:ff:ff:ff:ff:ff
...
/ # ip r sh
default via 10.0.100.1 dev eth0
10.0.100.0/24 dev eth0 scope link src 10.0.100.120
169.254.169.254 dev eth0
172.17.0.0/16 dev docker0 scope link src 172.17.0.1
/ # curl 10.0.100.120:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
…
Another approach is to access the ECS EC2 instance running your task as a priviledged Linux user to observe some details:
CONT_INST_ID=$(aws ecs list-container-instances --cluster ${ClusterName} --query 'containerInstanceArns[]' --output text)
EC2_INST_ID=$(aws ecs describe-container-instances --cluster ${ClusterName} --container-instances ${CONT_INST_ID} --query 'containerInstances[0].ec2InstanceId' --output text)
aws ssm start-session --target ${EC2_INST_ID}
and to run the following commands inside the instance:
sudo -i
docker ps
CONT_ID=$(docker ps --format "{{.ID}} {{.Image}}" | grep nginx:alpine | awk '{print $1}')
PID=$(docker inspect -f '{{.State.Pid}}' $CONT_ID)
ip a sh eth0
nsenter -t $PID -n ip a sh eth0
netstat -tulpn | egrep "Program name|nginx"
curl localhost:80
# try the following command which gives lots of details regarding the nginx container: docker inspect $CONT_ID
# to leave the interactive session type exit twice
Sample outputs for host network mode of a task running a nginx container:
[root@ip-xxx ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a6f88d328c2 nginx "/docker-entrypoint.…" 28 seconds ago Up 26 seconds ecs-web-host-2-web-host-faac92eda1bdc9f01c00
[root@ip-xxx ~]# docker inspect 1a6f88d328c2 | jq -r '.[0].NetworkSettings'
{
...
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"host": {
...
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
}
[root@ip-xxx ~]# netstat -tulpn | egrep "Program name|nginx"
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 26732/nginx: master
tcp6 0 0 :::80 :::* LISTEN 26732/nginx: master
[root@ip-xxx ~]# curl localhost:80
<!DOCTYPE html>
<html>
...
<h1>Welcome to nginx!</h1>
...
</html>
Try to run the task a second time:
aws ecs run-task --cluster ${ClusterName} --task-definition ${TASK_DEF} --enable-execute-command --launch-type EC2
and you will observe an error like the following with “reason”: “RESOURCE:PORTS”. This is because the host port is already in use!
Admin:~/environment/ecsworkshop/content/ecs_networking/setup (main) $ aws ecs run-task --cluster ${ClusterName} --task-definition ${TASK_DEF} --enable-execute-command --launch-type EC2
{
"failures": [
{
"reason": "RESOURCE:PORTS",
"arn": "arn:aws:ecs:eu-central-1:xxx:container-instance/f8579ac602054f81ac9f6b19c3ed33af"
}
],
"tasks": []
}
Cleanup the task:
aws ecs stop-task --cluster ${ClusterName} --task ${TASK_ARN}