user3775217
user3775217

Reputation: 4803

Docker, Rails, Postgres - db service not found error

Dockerfile

FROM ruby:2.6.3
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client
RUN mkdir /paper_scammer_docker
WORKDIR /paper_scammer_docker
COPY Gemfile /paper_scammer_docker/Gemfile
COPY Gemfile.lock /paper_scammer_docker/Gemfile.lock
RUN bundle install
COPY . /paper_scammer_docker

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

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

docker-compose.yml

version: '3'
services:
  db:
    image: postgres:10
    ports:
      - "5432:5432"
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/paper_scammer_docker
    ports:
      - "3000:3000"
    links:
      - "db:db"
    depends_on:
      - db

I am successfully able to build image using the following command

docker-compose build

If i do docker-compose up in project root directory my app runs fine and i can see two containers one for my rails app and other for postgres when i do docker container ls. I observed that postgres image is loaded from library.

database.yml file

# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
#
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000
  host: db
  username: postgres
  password:
  # user: gnpsllco_papaer
  # password: sharma@123

development:
  <<: *default
  database: paper_scammer_development
  # user: postgres
  # password: sharma@123

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: paper_scammer_test
  # user: postgres
  # password: sharma@123

production:
  <<: *default
  database: gnpsllco_paper_scammer

Problem

I run the following command to start docker container from the image of my rails app.

sudo docker run -p 3000:3000 paperscammer_web

postgres container is not running or missing.

I want to understand how this dockerfile-compose handle this, when i spin the image of rails app which says its depends in postgres service shouldn't it also spin a postgres container and set 'db' as host for connections.

or do it needs to be spinned manually for postgres.

I get the following error when i run http://localhost:3000

could not translate host name "db" to address: Name or service not known

I can see postgres container is not running.

Thanks

Upvotes: 1

Views: 3071

Answers (3)

Vasily  Bodnarchuk
Vasily Bodnarchuk

Reputation: 25294

Solution

./Dockerfile

ROM ruby:2.6.3-alpine

ENV BUNDLER_VERSION=2.0.2

RUN apk add --update --no-cache \
      binutils-gold \
      build-base \
      curl \
      file \
      g++ \
      gcc \
      git \
      less \
      libstdc++ \
      libffi-dev \
      libc-dev \ 
      linux-headers \
      libxml2-dev \
      libxslt-dev \
      libgcrypt-dev \
      make \
      netcat-openbsd \
      nodejs \
      openssl \
      pkgconfig \
      postgresql-dev \
      python \
      tzdata \
      yarn 

RUN mkdir -p /app
WORKDIR /app

COPY Gemfile* ./
RUN gem install bundler
RUN bundle config build.nokogiri --use-system-libraries
RUN bundle check || bundle install 

COPY package.json yarn.lock ./
RUN yarn install --check-files

COPY . ./

COPY ./sh/entrypoints/docker-entrypoint.sh /usr/bin/entrypoint.sh
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

./.env

APP_NAME=api
APP_PORT=3100

DATABASE_NAME=rails_db
DATABASE_USER=batman
DATABASE_PASSWORD=super_pass_123
DATABASE_PORT=5342
DATABASE_HOST=api_db # must be equal to the name of the postgres service in docker-compose.yml

./docker-compose.yml

version: '3.7'
services:
  api: 
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ${APP_NAME}
    depends_on:     
      - api_db
    ports: 
      - "${APP_PORT}:${APP_PORT}"
    volumes:
      - .:/app
      - gem_cache:/usr/local/bundle/gems
      - node_modules:/app/node_modules
    env_file: .env
    environment:
      RAILS_ENV: development

  api_db: # must be equal to `DATABASE_HOST ` in ./.env
    image: postgres
    command: postgres -c logging_collector=on -c log_destination=stderr -c log_directory=/logs -p ${DATABASE_PORT}
    ports:
      - "${DATABASE_PORT}:${DATABASE_PORT}"
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./log/db:/logs
    environment:
      - POSTGRES_USER=${DATABASE_USER}
      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
      - POSTGRES_DB=${DATABASE_NAME}

volumes:
  gem_cache:
  db_data:
  node_modules:  

./sh/entrypoints/docker-entrypoint.sh

https://stackoverflow.com/a/59047028/4488252

DB_INITED=0
if db_version=$(bundle exec rake db:version 2>/dev/null)
then
    if [ "$db_version" = "Current version: 0" ]
    then
        echo "DB is empty"
    else
        echo "DB exists"
        DB_INITED=1
    fi
    bundle exec rake db:migrate 
else
    echo "DB does not exist"
    bundle exec rake db:setup
fi

if [ $DB_INITED == 0 ]
then
    echo "Performing initial configuration"
    # init some plugins, updated db if need, add initial data
fi

./config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  database: <%= ENV['DATABASE_NAME'] %>
  username: <%= ENV['DATABASE_USER'] %>
  password: <%= ENV['DATABASE_PASSWORD'] %>
  port: <%= ENV['DATABASE_PORT'] || '5432' %>
  host: <%= ENV['DATABASE_HOST'] %>

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default

./.dockerignore

https://gist.github.com/neckhair/ace5d1679dd896b71403fda4bc217b9e

.git
.gitignore
README.md

#
# OS X
#
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

#
# Rails
#
.env
.env.sample
*.rbc
capybara-*.html
log
tmp
db/*.sqlite3
db/*.sqlite3-journal
public/system
coverage/
spec/tmp
**.orig

.bundle

.ruby-version
.ruby-gemset

.rvmrc

# if using bower-rails ignore default bower_components path bower.json files
vendor/assets/bower_components
*.bowerrc
bower.json

Usage

https://docs.docker.com/compose/reference/down/

build and run: docker-compose up --build -d

https://docs.docker.com/compose/reference/down/

stop: docker-compose down

stop + delete images and volumes: docker-compose down --rmi all --volumes

Upvotes: 2

LinPy
LinPy

Reputation: 18598

sudo docker run -p 3000:3000 paperscammer_web will start only the web server, you need to run docker-compose :

sudo docker-compose up -d --build

second option:

 sudo docker run --name db -p 5432:5432 -v ./tmp/db:/var/lib/postgresql/data postgres:10
 sudo docker run -p 3000:3000 --link db:db -v .:/paper_scammer_docker paperscammer_web

Upvotes: 3

Victor Calatramas
Victor Calatramas

Reputation: 869

docker-compose up and docker run are different commands. Using the latter one won't use the compose file, only the parameters passed to the command.

When you use docker run you must specify which containers you want to run specifically, if you want to run both db and web you should use this command twice.

In the other hand, when you use docker-compose up you run all the containers specified in docker-compose.yml with all its options.

Also, the depends_on property just specifies that the container web will be launched once the container db is successfuly launched.

As a final remark, if you want your containers to be able to communicate with each other you should add a network to the compose file:

version: '3'
services:
  db:
    image: postgres:10
    container_name: db
    ports:
      - "5432:5432"
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    networks:
      - my-network-name

  web:
    build: .
    container_name: web
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/paper_scammer_docker
    ports:
      - "3000:3000"
    links:
      - "db:db"
    depends_on:
      - db
    networks:
      - my-network-name

networks:
  my-network-name:
    driver: bridge

Also is a good practice to add a container_name for referencing them.

With thoose options now containers can communicate with their names as IPs/hostnames:

http://<container_name>:<port>

For example, the container web will be able to connect to the database in the db container like this:

http://db:5432/gnpsllco_paper_scammer

Notice the IP/hostname is now the name of the container.

As @LinSel pointed, you can build and run all the containers with the command (sudo is not needed):

docker-compose up -d --build

Upvotes: 0

Related Questions