I can successfully do this task without docker-compose
, but I want to leverage docker-compose
for a bit more simplicity.
Without docker-compose
(which works as expected)
My app has a Dockerfile. From the root of my app I run this command:
docker build -t some_app . && docker run -i -t --rm -p 3000:3000 -p 3306:3306 some_app
What this does:
- builds the image from the Dockerfile and names the repository of the image "some_app".
- creates and starts a container from that "some_app" image in interactive mode. That container is set to be automatically deleted when the container stops, and the container has binding ports of 3000 and 3306 with the host.
Everything works as expected. When the container starts, it interactively puts me in a bash shell within the container. I can then run rails s -b 0.0.0.0
from the container. This kicks off a puma server within the container that is serving a rails app. From a web browser on the host machine I can run localhost:3000
and successfully make requests to the app.
I want to mention that this is the last line of my Dockerfile which is probably relevant:
ENTRYPOINT ["/bin/bash"]
With docker-compose (not working as expected)
I have the same Dockerfile at the root of my app. I also have a docker-compose.yml file at the root of my app with the following:
services:
some_app:
build: .
ports:
- "3000:3000"
- "3306:3306"
stdin_open: true
tty: true
I then run docker-compose run some_app
. I expect all the same functionality as the above code example where I did not use docker-compose.
Running docker-compose run some_app
does successfully start the container and puts me in an interactive shell within the container. I then run rails s -b 0.0.0.0
to start the server, and it appears the server successfully starts.
However, when I go to the host and try to makes requests via localhost:3000
I get a "Safari can’t connect to the server".
It appears as though the binding of 3000 and 3306 between container and host aren’t working when I attempt to kick things off with the docker-compose way.
2
Answers
I was missing the
--service-ports
flagTo kick things off successfully with docker-compose:
Setting the image’s
ENTRYPOINT
to an interactive shell will cause problems, including making it not run well in Compose. You don’t usually want to start a container, then run a command inside of it. Instead, set the image metadata to directly run the program you want the container to run.I might set, for example,
(Style-wise there are some questions on how
ENTRYPOINT
andCMD
should be used. I tend to prefer a style whereCMD
is a complete command; this makes it easier to e.g.docker-compose run some_app bash
if you do wind up needing an interactive debugging shell.)From there, when you
docker run
the container, you will not need to manually also start the Rails application, since it is already the main container process.When you run this using Compose, it is important that you use
docker-compose up
and notdocker-compose run
.run
here is intended to run a one-off command, usually a task like database migrations or a debugging shell, and it’s intended to run it while the "normal" process is already running. Correspondingly,docker-compose run
does not itself publishports:
, because they’d conflict with the main instance of the container. (There’s adocker-compose run --service-ports
option that will cause it to honorports:
, but justdocker-compose up
is easier.)In the Compose file, correspondingly, you do not need to set
stdin_open:
ortty:
, since you’re not trying to run an interactive shell, and the Rails server doesn’t need to read from stdin at all.