Closed adolski closed 1 year ago
I dabbled with Docker for a project several months ago but didn't experience setting it up myself, so I'll definitely take a look at the resources you linked. Thanks!
@adolski I have made some decent progress (i think), with a docker image built for the tests but am figuring out the best way to utilize Docker Compose to run everything at once. Right now in my docker-compose.yml file,
I have two services (book-tracker and postgres db), but I'm not sure if I need to split this up into three services (book-tracker app, book-tracker tests, and postgres db?) or if the way I have it set up covers both the web app and the tests:
version: '3'
services:
postgres:
hostname: postgres
image: postgres:14-alpine
environment:
POSTGRES_DB: book_tracker
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
book-tracker:
build:
context: .
dockerfile: docker/book-tracker-test/Dockerfile
environment:
RAILS_ENV: test
ports:
- "3000:3000"
depends_on:
- postgres
postgres:14-alpine
based on seeing that I have postgres v 14
running, but unsure if that's relevantdepends_on
so that the book-tracker service knows to wait for postgres connection; do I also need to include any commands here?
docker ps
I get the following output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a8655e516656 postgres:14-alpine "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 5432/tcp book-tracker-postgres-1
docker-compose up
I get several errors aboutport 5432
that I'm confused by
could not connect to server: Connection refused (ActiveRecord::ConnectionNotEstablished)
book-tracker-book-tracker-1 | Is the server running on host "localhost" (127.0.0.1) and accepting
book-tracker-book-tracker-1 | TCP/IP connections on port 5432?
I'll continue reading through the docs/watching docker videos, but wanted to see if you might notice anything here that's an immediate red flag.
The way I've dealt with this in other projects is to break out the development and test versions of the app into separate docker-compose files. (We don't really even need a development one for Book Tracker.) For example, in this project, there are three different compose files:
docker-compose.yml
contains all of the services that both versions of the app depend on, but not the app itselfdocker-compose.development.yml
contains only the development version of the appdocker-compose.test.yml
contains only the test version of the appThen docker compose
can be invoked like:
docker compose -f docker-compose.yml -f docker-compose.test.yml up --build --exit-code-from ideals-test
Postgres 14 should be fine, unless any errors turn up, but I doubt they will.
And the project link above, and the docker-compose.test.yml
file within it should answer the other questions. (I should have linked to it before, sorry.)
@adolski Thank you! I'll see how it goes splitting it up like this.
Should I follow the structure of how it's set up in Ideals, where all Dockerfiles are nested inside the docker directory? The way book-tracker is currently set up, has one Dockerfile living in the same directory as the project root.
I think it's better to put them in a subdirectory, because it reduces clutter in the root directory, and moreso if you ever have to add more of them (for other services).
Update:
I set up a subdirectory for the dockerfiles: /docker/book-tracker-app/Dockerfile
and /docker/book-tracker-test/Dockerfile
Here's how I set up the test Dockerfile:
FROM ruby:3.0.3-slim
ENV RAILS_ENV=test
ENV RAILS_LOG_TO_STDOUT=true
ENV RAILS_MAX_THREADS=5
ENV RAILS_SERVE_STATIC_FILES=true
RUN apt-get update && apt-get install -y \
build-essential \
curl \
git \
libpq-dev
RUN mkdir app
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN gem install bundler \
&& bundle config set without 'development' \
&& bundle install
COPY . ./
CMD ["bin/rails", "test"]
Here is my docker-compose.yml:
version: '3'
services:
postgres:
hostname: postgres
image: postgres:14-alpine
environment:
POSTGRES_DB: book_tracker
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
book-tracker:
build:
context: .
dockerfile: docker/book-tracker-app/Dockerfile
ports:
- "3000:3000"
depends_on:
- postgres
Here is the docker-compose.test.yml:
version: '3'
services:
book-tracker-test:
build:
context: .
dockerfile: docker/book-tracker-test/Dockerfile
depends_on:
- postgres
command: bash -c "sleep 40 && bin/rails db:prepare & bin/rails test"
I also updated my config/database.yml to include 'host:postgres' in the default config block:
default: &default
adapter: postgresql
encoding: unicode
host: postgres
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
min_messages: warning
template: template0
Finally, upon running docker compose -f docker-compose.yml -f docker-compose.test.yml up
here's the output I'm getting (I'm running into an error with the postgres username and password not being authenticated, which I'm having trouble figuring out why)
It looks like Book Tracker in the container is trying to connect to Postgres using your (gaurijoshi's) credentials.
One thing that is missing from the Dockerfile, I think, is a Rails configuration file. In the metaslurp-test Dockerfile, there is this line near the end:
RUN cp config/credentials/ci.yml config/credentials/test.yml
config/credentials/ci.yml
contains the database connection info, among all the other settings. Since the containerized Book Tracker is running the test Rails environment, it should pick up the config/credentials/test.yml
file and use the credentials from that (which are postgres
/postgres
).
I think what's happening is that, since that file doesn't exist in the container, the variables in database.yml
are getting assigned to empty hashes, which cause the test environment settings to be blank, so it tries to fall back to using your Mac account's username.
I think you're right!
I noticed in my config/credentials/test.yml
, that the credentials fordb_username
is set to gaurijoshi and the db_password
is empty. I think this was done upon set-up, as I don't recall making any changes to it since then.
postgres
/ postgres
so that the variables called in database.yml
are correct?RUN cp
command, since this app doesn't have a config/credentials/ci.yml
, is the idea to follow something like this, where the container_id
is the Postgres Container id?RUN cp <location_of_file_on_host> <container_id or container_name>:<file_destination>
<file_destination>
would be in that case.Your config/credentials/test.yml
file is different from the one that you want to be using in the container. It contains settings for your desktop environment, but some of the settings in the container (like the postgres credentials) will be different. That's the reason for the separate ci.yml
file that gets copied over test.yml
in the image. Since this app doesn't have a ci.yml
file, you will have to create one. You could base it on your test.yml
file, and then just change whatever settings you need to.
Thanks for clarifying, this makes a lot more sense to me now.
I created a ci.yml
file based off my test.yml
and looking at how it was set up in Metaslurp. Here is what it looks like (similar to metaslurp, I didn't include the secret_key_base
). I'm still getting the same error with password authentication failing for 'gaurijoshi'. I've made sure the credentials in the ci.yml
are set to postgres/postgres
and are now being copied over the test.yml in the image with the RUN cp <ci.yml> <test.yml>
command inside my test dockerfile.
I wonder if I might just need to restart Docker, if everything else looks like it's set up correctly, but I'm not confident that is the case here:
Hmm. Are you sure the containers are getting rebuilt when you run them, i.e. are you passing --build
to the docker compose
command? And are you sure the test.yml
is making it into the image? Those are my only ideas.
If you continue to have trouble, if you can open a PR with your work in progress, I can take a look at it. (Is that #24?)
I'll double check if the containers are in fact getting rebuilt and get back to you, but yes# 24 is my current work in progress for issue. Thanks!
So I made sure I'm passing --build
in my docker compose
command, and am getting a "failed to solve: read-only file system".
Is this a disk space issue? I tried making more room but it keeps coming up.
I also tried running docker build prune
to get rid of any dangling build cache, but get a similar error.
And same if I just run the docker compose
command without the --build
flag:
Update: The tests are now running in Docker successfully! (The 2 errors are pointing toward the User model tests).
Great job!
I can't remember if you had Docker experience already, @gaurijo, but if not, one way to learn about it is through the Docker documentation. You also have access to LinkedIn Learning courses through the U of I, and it looks like they have quite a few on Docker.
The basic idea is, instead of running
rails test
from our shell, we want to spin up the Book Tracker in a container and runrails test
in that. Alongside the Book Tracker container we will need another container for PostgreSQL, so we will want to use docker compose to orchestrate these multiple containers.The Book Tracker is already containerized for running in AWS ECS. But the image is built for running the web app and not the tests. It would be possible to use the same image for both, but, in other projects, the way I've set it up is with separate images, generated by separate Dockerfiles.
An example of another app that is setup with separate web app & test containers, and works in ECS and GitHub Actions, is metaslurp. In this project there is a root-level Dockerfile (for the web app) and a separate test Dockerfile in
docker/metaslurp-test/Dockerfile
. The docker-compose.yml file is used for testing, and it spins up three separate containers, one for the main app and two for its dependent services.One thing I'm not sure about yet is how to build an x86 container image from an ARM Mac, but I think that's a solvable problem once we get to it.