Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Jenn
Jenn

Posted on

     

Demystifying Docker Compose

My latest project used Rails and Postgres. After fighting my Mac and the Postgres install for an hour, I gave up and usedDocker instead.

Docker has always seemed kinda mystical to me and I really wanted to understand my setup this time instead of just copying a quick start. And since there is no better way to learn something than to teach it, I will share it with you!

There are three key files:

  • Dockerfile
  • docker-compose.yml
  • database.yml

And two main commands:

  • docker-compose build
  • docker-compose up

You will need to have Docker installed.

I prefer to use theDocker extension forVSCode to interact with my running containers, so there are more commands you may need if you are not using that extension.

Dockerfile

The Dockerfile is where the dependencies for your container are defined. I'll go through mine line by line.

FROM ruby:2.6.3
This is from whatimage it is starting from, in this case the Ruby 2.6.3 image.

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client
RUN gem install bundler

Docker containers are VERY bare bones and the next lines were installing Nodejs, a Postgres client, and Bundler.

WORKDIR /my-api
This should be the folder where all your application code is in. It is setting the working directory so the next commands are easier.

COPY Gemfile /my-api/Gemfile
COPY Gemfile.lock /my-api/Gemfile.lock

Copying the Gemfiles to the working space in the container.

RUN bundle install
This will run bundler in the container.

COPY . /my-api
Copy all the other files into the working space.

COPY Docker/database.yml config/database.yml
This line can be skipped. The project was shared with others who had local installations of Postgres and I didn't want to override the config/database.yml in the repository. I created a Docker folder for my specific database.yml file. This command copies my database.yml file to the standard location for the container.

EXPOSE 3000
Only open the ports you need to have open.

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]

Start rails.

So for this Rails and Postgres project, my full Dockerfile looked like this:

FROM ruby:2.6.3RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN gem install bundlerWORKDIR /my-apiCOPY Gemfile /my-api/GemfileCOPY Gemfile.lock /my-api/Gemfile.lockRUN bundle installCOPY . /my-apiCOPY Docker/database.yml config/database.yml EXPOSE 3000# Start the main process.CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose.yml

This file connects our newly defined Rails container to a Postgres database.

version: '3'
All docker-compose files start with this. Don't change the version, just leave it.

services:
This defines the services (aka containers) to be built and how they are connect.

db:
image: postgres

The first service is a Postgres database. This is pulling the base Postgres image. I do not need any add-ons or customizations for my database so a Dockerfile for this service isn't necessary.
If you want data to persist, you will have to add a volume.
volumes:
- ./tmp/db:/var/lib/postgresql/data

I didn't need persistent storage for this project so I didn't use a volume.

web:
The next service is the Rails web server.

`build: .`

Build the image from our newly defined Dockerfile.

command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
Run this bash command in our new service container. This command is removing the pid file and telling rails what ports to use.

`ports:  - "3000:3000"`

Mapping the Rails service port 3000 to my local machine's 3000.

`depends_on:  - db`

This service will not start until the database service has started.

Once again, avolumes line could be here but I didn't need it.

All together my docker-compose.yml looks like this.

version:'3'services:db:image:postgresweb:build:.command:bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"ports:-"3000:3000"depends_on:-db

database.yml

This is the database connection file.

default:&defaultadapter:postgresqlencoding:unicodehost:dbusername:postgrespassword:pool:5development:<<:*defaultdatabase:my-api_developmenttest:<<:*defaultdatabase:my-api_test

docker-compose build

This command builds the containers. With my set up, anytime I update the Gemfile I have to rebuild.

Building can take some time. Each line in the docker-compose.yml is a step in the building process that can be cached so each build afterwards is faster.

Example output from a quick rebuild.

$ docker-compose builddb uses an image, skippingBuilding webStep 1/11 : FROM ruby:2.6.3 ---> d529acb9f124Step 2/11 : RUN apt-get update -qq && apt-get install -y nodejs postgresql-client ---> Using cache ---> 32e807d05964Step 3/11 : RUN gem install bundler ---> Using cache ---> 0c1c757c6905Step 4/11 : WORKDIR /my-api ---> Using cache ---> b305131f9c6bStep 5/11 : COPY Gemfile /my-api/Gemfile ---> Using cache ---> 657fc337d0c4Step 6/11 : COPY Gemfile.lock /my-api/Gemfile.lock ---> Using cache ---> 09154df316d8Step 7/11 : RUN bundle install ---> Using cache ---> 7915826ca085Step 8/11 : COPY . /my-api ---> 4ff482944c8eStep 9/11 : COPY Docker/database.yml config/database.yml ---> 101f814ba0bcStep 10/11 : EXPOSE 3000 ---> Running in 45f91676a8f3Removing intermediate container 45f91676a8f3 ---> 9426b9097b78Step 11/11 : CMD ["rails", "server", "-b", "0.0.0.0"] ---> Running in 3f262f9679a2Removing intermediate container 3f262f9679a2 ---> a056b93d4bc8Successfully built a056b93d4bc8

docker-compose up

This command starts the containers.

$ docker-compose upStarting my-api_db_1 ... doneRecreating my-api_web_1 ... doneAttaching to my-api_db_1, my-api_web_1

Then visitlocalhost:3000 in a browser to confirm it is up and running.

For more information, check out the officialquickstart guide for Rails.

Top comments(9)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
yujinyuz profile image
Yujin
I'm a Software Engineer that loves (n)vim
  • Location
    /usr/local/bin/
  • Education
    Bachelor of Science in Information Technology
  • Work
    Software Developer
  • Joined
• Edited on• Edited

I hope this doesn't sound stupid but why didn't we just do

COPY . /my-apiRUN bundle install

instead of the longer version

COPY Gemfile /my-api/GemfileCOPY Gemfile.lock /my-api/Gemfile.lockCopying the Gemfiles to the working space in the container.RUN bundle installThis will run bundler in the container.COPY . /my-apiCopy all the other files into the working space.

Is there a huge difference between the two?

Great article btw!

CollapseExpand
 
geekgalgroks profile image
Jenn
Jenn is a self taught web developer who specializes in usability and accessibility. She is easily spotted at conferences by her bright lipstick and various code dresses and t-shirts.
  • Joined

Each line is cached, so if I only useCOPY . /my-api I will have to run bundler every time anything in my code changes, which takes time and would be annoying.

By copying over the gemfiles first and running bundler those packages are cached and rebuilds will be faster. As docker-compose is smart enough to realize those files didn't change so it will skip the gemfile and bundler steps when building.

CollapseExpand
 
yujinyuz profile image
Yujin
I'm a Software Engineer that loves (n)vim
  • Location
    /usr/local/bin/
  • Education
    Bachelor of Science in Information Technology
  • Work
    Software Developer
  • Joined

Ahh. That makes sense. Thanks for clearing things up for me 😁

CollapseExpand
 
killrazor profile image
Tyler Christian
  • Joined

You could instead use a volume in docker-compose

Thread Thread
 
geekgalgroks profile image
Jenn
Jenn is a self taught web developer who specializes in usability and accessibility. She is easily spotted at conferences by her bright lipstick and various code dresses and t-shirts.
  • Joined

It wouldn't change the need to rebuild and rerun bundler if the gemfiles changed. I always prefer to rebuild the container when updating gemfiles. I don't want my docker-compose file to get out of sync with what happens in my container.

CollapseExpand
 
pantsme profile image
Antonio Savage
Blockchain. activity < productivity < impact < value < meaning
  • Location
    Milwaukee, USA
  • Education
    AS in Networking, BS in Business Mgmt
  • Work
    Blockchain Network Engineer
  • Joined

Because I'm picky, size matters with Docker images so you might want to clean up the apt cache after you're done installing those apps. Just add a line after the apt installs
rm -rf /var/lib/apt/lists/*

Also, I'm not familiar with Ruby but you may be able to do some cleanup too after the build command or even build the app in a throwaway container and use the artifact in this one. Check it out here.docs.docker.com/develop/develop-im...

CollapseExpand
 
raghu_kn profile image
Raghu
  • Location
    Bangalore
  • Work
    Engineer at SomeWhere
  • Joined
• Edited on• Edited

May be a comment on would help further

$ docker-compose build db uses an image, skipping
CollapseExpand
 
geekgalgroks profile image
Jenn
Jenn is a self taught web developer who specializes in usability and accessibility. She is easily spotted at conferences by her bright lipstick and various code dresses and t-shirts.
  • Joined

That is the output fromdocker-compose build command when I reran it for this post.

It doesn't have to build anything for the db as it is pulling down the Postgres image (which it has cached). I have a comment about it in the docker-compose.yml section. I'll add a bit more info there.

CollapseExpand
 
andhop profile image
Andy Hopwood
I'm a software dev from Manchester in the UK. I love all things dev. I started with PHP but I've have written professionally in Go and C# for the last 3 years on varying projects.
  • Location
    Manchester, UK
  • Work
    Business Systems Application Analyst at BDP
  • Joined

Thanks for writing! I'm still yet to use docker myself. The whole thing is a bit of a mystery.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Jenn is a self taught web developer who specializes in usability and accessibility. She is easily spotted at conferences by her bright lipstick and various code dresses and t-shirts.
  • Joined

More fromJenn

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp