skip to Main Content

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.
rails server started

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


  1. Chosen as BEST ANSWER

    I was missing the --service-ports flag

    -P, --service-ports            Run command with all service's ports enabled and mapped to the host

    # corrected docker-compose.yml
    
    services:
      some_app:
        build: .
        ports:
          - "3000:3000"
          - "3306:3306"
        stdin_open: true
        tty: true
    

    To kick things off successfully with docker-compose:

    docker-compose run --service-ports some_app
    

  2. 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,

    ENTRYPOINT ["bundle", "exec"]
    CMD ["rails", "-b", "0.0.0.0"]
    

    (Style-wise there are some questions on how ENTRYPOINT and CMD should be used. I tend to prefer a style where CMD 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 not docker-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 publish ports:, because they’d conflict with the main instance of the container. (There’s a docker-compose run --service-ports option that will cause it to honor ports:, but just docker-compose up is easier.)

    In the Compose file, correspondingly, you do not need to set stdin_open: or tty:, since you’re not trying to run an interactive shell, and the Rails server doesn’t need to read from stdin at all.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search