Let's implement our docker container architecture
You can also see that this directory structure already contains a logs/ directory, which will be the collection point for all container logs we've been talking about in the last part of this series.
Create the Docker Compose fileIf you've never heard of YAML before, let me just tell you what it is. YAML is a recursive acronym for "YAML Ain't Markup Language". Early in its development, YAML was said to mean "Yet Another Markup Language", but it was then reinterpreted (backronyming the original acronym) to distinguish its purpose as data-oriented, rather than document markup. YAML’s purpose is to have a human friendly data serialization standard for all programming languages. (see: http://yaml.org)
In our YAML file we will tell Docker Compose how our containers must be started, which volumes should be mounted, which containers should be linked together and what ports should be exposed. So it's basically everything from that list above.
Let's get into details - This is what our docker-compose.yaml file looks like:
Let's pick the nginx reverse proxy to explain our settings. Besides all other Docker Compose YAML possibilies, we'll only use build, expose, links, ports and volumes.
build: This is the path to the directory containing the Dockerfile for the image. We have supplied that value as a relative path, which means that it is interpreted relatively to the location of the YAML file itself. This directory is also the build context that is sent to the Docker daemon. All files belonging to the nginx reverse proxy reside in folder ./nginx-reverse-proxy, therefore we tell Docker Compose to build the image based on the following Dockerfile /opt/docker/nginx-reverse-proxy/Dockerfile, which we're going to create later.
expose: This section specifies the ports to be exposed without publishing them to the host machine - they'll only be accessible to linked services. Only the internal port can be specified - see the architecture diagram above, these exposed ports are the ports with the purple background color.
links: Here we specify all links to containers in other services. You can either specify both the service name and the link alias (SERVICE:ALIAS), or just the service name (which will also be used for the alias). In our design we'll use aliases, so we'll tell Docker that whenever we want to talk to our containers we want them to use blog (for the projectwebdev website) and blogapi (for our ioJS REST API).
ports: The ports we want to expose to the Docker host - see the yellow port in the architecture diagram above. You can either specify both ports (HOST:CONTAINER), or just the container port (a random host port will be chosen). Since we want to make sure that it's always the same port (in our case it's port 80) we specify the HOST and the CONTAINER port explicitly (which in both cases would be 80). If your nginx reverse proxy in your container uses port 8000 and you want that port to be accessible from outside via port 80, you'll specifiy it like this: "80:8000". Important: When mapping ports in the HOST:CONTAINER format, you may experience erroneous results when using a container port lower than 60, because YAML will parse numbers in the format xx:yy as sexagesimal (base 60). For this reason, Docker recommends always explicitly specifying your port mappings as String.
volumes: This section contains all mount paths as volumes, optionally specifying a path on the host machine (HOST:CONTAINER), or an access mode (HOST:CONTAINER:ro). The latter one (:ro = readonly) is used in our projectwebdev container, since we don't want the container to change the files for any reason. Only our host may provide the markup that is needed for the website.
We have now implemented our architecture with Docker Compose! Let's create each image and container so we can fire up docker compose. We'll start with our Ubuntu Base Image!