I need a postgres container linked to a rails container. The rails container needs to install SpreeCommerce, but not before pointing to the postgres container for the database connection.
Using docker-compose to orchestrate these things leads to a common problem. All of the containers launch concurrently, and the rails container attempts the Spree Commerce installation before the postgres server is actually listening for connections.
Let it be known I am holding back from venting about the dichotomy between Docker’s amazing ability to simplify the development/deployment process, and Docker’s grand reluctance to implement even a basic ready-state for containers (instead insisting on multi-step implementation of ready-state scripts). Docker has their reasons, but google the issue and you’ll see this is a sorely needed functionality. Whoops, I just vented.
This post aims to create more of a ‘lay tutorial’ for linking containers. I explore the method of using a shell script (on container1) that waits and listens for a postgres server (on container2) to become ready, after which container1 can install Spree Commerce (as spree initially populates its database).
Using the example from Docker’s recommendation. The docker-compose file looks like this:
entrypoint: ./wait-for-it.sh db:5432
docker-compose.yml has two services (actions), and thus builds two images. Whenever you want to use those images, they create separate entities called containers.
#builds the web image using the dockerfile located in the directory you run the “docker-compose build .” command from. This directory is alternatively called the ‘current context’. Note that those dockerfile instructions are not shown in this post.
#expose ports to the host as HOST:CONTAINER. That is, container port 8000 is routed to host port 80. For example, if you are running a webserver in your container that is expecting traffic from port 8000, you can communicate with it from the computer running docker via your-docker-host-ip:80. Note that if you try to expose ports via your dockerfile (not covered here), those ports are only available to other containers within your virtual docker network. For example, using EXPOSE 8000 in your dockerfile would allow outgoing traffic from other containers (whatever port they choose) to be sent to port 80 on the container you EXPOSED.
#some newcomers may be tempted to think that the web image is somehow being built from db. That is not true here. The depends_on key instead means the Web container will not run without the companionship of the DB container (example: if you were to use docker-compose to build these two images, create their respective containers, manually delete the db container, you would not be able to launch the web container).
entrypoint: ./wait-for-it.sh db:5432
#First off, wait-for-it.sh is a github project by vishnubob–big thanks to him (and his employer Ginkgo Bioworks). In a nutshell, this instruction looks for Work-dir-set-by-dockerfile/wait-for-it.sh located in the Web image. That is, in the dockerfile you can set a work directory (such as /usr/home/scripts/) and that will become the relative path used whenever you don’t explicitly specify an absolute path. The arguments passed to the script (db:5432) tell the script to repeatedly check the linked container db for a port 5432 response.
#This is analogous to the “build: .” key used above in Web. Instead of looking to the dockerfile for image build instructions, this points docker to use an image that already exists (docker looks locally for the existence of a “postgres” image, and if not found, looks on the official docker repository).