5
votes

I dockerized a rails application which runs perfectly on my local machine. I configured my Github Actions to push the application into a private image on Docker Hub and when I try to pull and run it locally or on another machine, it pulls the image but the container then exits because Rails fails with the following error:

app  | rm: cannot remove 'tmp/pids/server.pid': No such file or directory
app  | => Booting Puma
app  | => Rails 6.0.3.3 application starting in development 
app  | => Run `rails server --help` for more startup options
app  | Exiting
app  | (erb):9:in `<main>': Cannot load database configuration: (NoMethodError)
app  | undefined method `[]' for nil:NilClass
app  |   from /usr/local/lib/ruby/2.7.0/erb.rb:905:in `eval'
app  |   from /usr/local/lib/ruby/2.7.0/erb.rb:905:in `result'
app  |   from /usr/local/bundle/gems/railties-6.0.3.3/lib/rails/application/configuration.rb:228:in `database_configuration'

I commented out the rm command and also both bin/rails commands to see if I can get the container to stay up and debug in the console but I had no chance.

From searching other SO questions I suppose there must be something wrong with my ENV or the database.yml in general but I don't understand why it runs perfectly when I don't pull the image from Docker Hub. Shouldn't the credentials be resolved either way? There are credentials for every environment in separate credential files.

Here is my code:

docker-compose.yml

version: '3'

volumes:
  db_data:
    driver: local
  app_data:
    driver: local

services:
  # database container
  postgres:
    image: postgres
    volumes:
      - app_data:/var/lib/postgresql/data
    ports:
      - 5439:5432
    environment:
      POSTGRES_DB: $DATABASE_NAME
      POSTGRES_USER: $DATABASE_USER
      POSTGRES_PASSWORD: $DATABASE_PASSWORD
    command: ["postgres", "-c", "log_statement=all"]
    restart: always

  backend:
    build: ./server
    volumes:
      - ./server:/var/workdir
    ports:
      - "3009:3000"
    depends_on:
      - postgres

Dockerfile

FROM ruby:2.7.1

WORKDIR /var/workdir
COPY . /var/workdir

# Install NodeJS and Yarn.
RUN curl https://deb.nodesource.com/setup_12.x | bash
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y nodejs yarn

RUN yarn install --check-files

RUN bundle install

ENV PORT 3000
EXPOSE $PORT
CMD /var/workdir/entrypoint.sh

entrypoint.sh

#!/bin/bash

rm tmp/pids/server.pid

bin/rails db:create
bin/rails db:migrate

bin/rails server -b 0.0.0.0 -p $PORT

config/database.yml

# PostgreSQL. Versions 9.3 and up are supported.

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: postgres
  port: 5432
  database: <%= Rails.application.credentials.database[:name] %>
  username: <%= Rails.application.credentials.database[:username] %>
  password: <%= Rails.application.credentials.database[:password] %>

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default

.github/workflows/main.yml

name: App

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
jobs:
  build:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres
        env:
          POSTGRES_DB: ${{ secrets.DATABASE_NAME }}
          POSTGRES_USER: ${{ secrets.DATABASE_USER }}
          POSTGRES_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
        ports:
          - 5439:5432
        # needed because the postgres container does not provide a healthcheck
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

    steps:
      - name: Checkout repo
        uses: actions/checkout@v2
      - name: Install PostgreSQL package
        run: |
          sudo apt-get -yqq install libpq-dev
      - name: Install yarn
        run: yarn install --check-files
      - uses: actions/setup-node@v1
        with:
          node-version: '12.13.0'
      - name: Setup Ruby
        uses: actions/setup-ruby@v1
        with:
          ruby-version: 2.7.1
      - name: Ruby gem cache
        uses: actions/cache@v2
        with:
          path: vendor/bundle
          key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-gems-
      - name: Build and create DB
        env:
          RAILS_ENV: test
          RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
        run: |
          cd server
          gem install bundler
          bundle config path vendor/bundle
          bundle install --jobs 4 --retry 3
          bin/rails db:setup
  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v2
      - name: Docker login
        run: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }}
      - name: Build
        run: docker build -t app ./server
      - name: Tags
        run: |
          docker tag app ${{ secrets.DOCKER_USER }}/app
          docker tag app ${{ secrets.DOCKER_USER }}/app:latest
      - name: Push
        run: |
          docker push ${{ secrets.DOCKER_USER }}/app
          docker push ${{ secrets.DOCKER_USER }}/app:latest

UPDATE: By adding the master.key as a secret to Github Actions, the error in the pipeline reads:

rails aborted!
ActiveSupport::MessageEncryptor::InvalidMessage: Cannot load database configuration:
ActiveSupport::MessageEncryptor::InvalidMessage
(...)
Caused by:
OpenSSL::Cipher::CipherError:

When instead I add the test.key as a secret, the original error returns.

This is the docker-compose.yml I created locally to fetch and run the image from Docker Hub:

version: '3'

services:
  app:
    image: user/app:latest

I replaced all user and repo names for this question.

1
you're never really building and using the image locally. You map the volume on the host and if there's something different it doesn't "remain" in the image. it might be that the image needs to be rebuilt and repushed. I suggest to remove the volume mapping from the docker-compose.yml when testing locally. - Stefano
I am now trying to bundle and build inside Github actions (see updated main.yml), but I get the same error in the pipeline now. - J. Unkrass
but did you try to have it working on your local machine before? - Stefano
It works fine in development.The container with Rails and Postgres is up and running and accessible. I just found this guide to add secrets to the build process and maybe that's where I go wrong - J. Unkrass
after you removed the volumes as I wrote in the other comment? - Stefano

1 Answers

0
votes

I'm quite sure (but not 100%) that your project doesn't have a credentials file. It should be here:

your_app/config/master.key

That file is generated when you run this command:

EDITOR=vim bundle exec rails credentials:edit

That file is used to decrypt your credentials.yml.enc file, where the secret_key_base (and your credentials of course) are stored.
During servers provisioning, I've seen the same error when the secret_key_base was missing and that was happening because I forgot to create the master.key that would decrypt my credentials file.

Sorry for not giving you a real answer but more a you can look at that way one.