Docker on Amazon Web Services
上QQ阅读APP看书,第一时间看更新

Using dynamic port mapping

Currently, the release stage workflow runs the application using a static port-mapping, where port 8000 on the app service container is mapped to port 8000 on your Docker Engine. Although this will typically work fine when running locally (unless you have some other application that is using port 8000), this may cause problems when it comes to running the release-stage workflow on a remote continuous-delivery build service, which may be running multiple builds for many different applications.

A better approach is to use dynamic port-mapping, which maps the app service container port to a dynamic port on your Docker Engine that is currently not in use. The port is picked from what is referred to as the ephemeral port range, which is a port range reserved for dynamic use by applications.

To configure dynamic port-mapping, you need to change the port-mapping in the docker-compose.yml file for the app service:

version: '2.4'

volumes:
public:
driver: local

services:
test:
...
...
release:
...
...
app:
extends:
service: release
depends_on:
db:
condition: service_healthy
volumes:
- public:/public
healthcheck:
test: curl -fs localhost:8000
interval: 3s
retries: 10
ports:
- 8000
command:
- uwsgi
- --http=0.0.0.0:8000
- --module=todobackend.wsgi
- --master
- --check-static=/public
- --die-on-term
- --processes=4
- --threads=2
acceptance:
...
...
migrate:
...
...
db:
...
...

In the preceding example, we simply change the port-mapping from a static mapping of 8000:8000 to 8000, which enables dynamic port-mapping. With this configuration in place, one problem is that you don't know in advance what port is going to be assigned, however, you can use the docker-compose port <service> <container-port> command to determine the current dynamic port-mapping for a given service on a given container port:

> docker-compose port app 8000
0.0.0.0:32768

Of course, rather than manually type this command each time, we can incorporate it into our automation workflow:

.PHONY: test release clean

test:
docker-compose build --pull release
docker-compose build
docker-compose run test

release:
docker-compose up --exit-code-from migrate migrate
docker-compose run app python3 manage.py collectstatic --no-input
docker-compose up --exit-code-from acceptance acceptance
@ echo App running at http://$$(docker-compose port app 8000 | sed s/0.0.0.0/localhost/g)

clean:
docker-compose down -v
docker images -q -f dangling=true -f label=application=todobackend | xargs -I ARGS docker rmi -f --no-prune ARGS

In the preceding example, we use a command substitution to obtain the current port-mapping and pipe the output to a sed expression that replaces 0.0.0.0 with localhost. Note that because GNU Make interprets the dollar sign symbol as a Make variable reference, you are required to double-escape dollar signs ($$) if you want a single dollar sign evaluated by the shell command that will be executed. 

With this in place, the output of the make release command will now complete with the following:

> make release
...
...
docker-compose run app bats acceptance.bats
Starting todobackend_db_1 ... done
1..4
ok 1 todobackend root
ok 2 todo items returns empty list
ok 3 create todo item
ok 4 delete todo item
App running at http://localhost:32771