I have a rake task in my application, that should start a TCP server.
I have moved rake command to an entrypoint.sh
file and in docker-compose added new service, which is called tcp
.
That tcp service return incorrect path, when I run docker-compose up -d
. I tried different ways to setup, but no luck. How do I build docker image for a rake task?
unable to prepare context: path "/Users/mac_user/Projects/fmt100/lib/tasks/socketing.rake" not found
docker-compose.yml
version: '3.9'
services:
tcp:
build: ./lib/tasks/socketing.rake
depends_on:
- app
- database
- redis
- sidekiq
env_file: .env
volumes:
- .:/app
- tcp_server:/app/lib/tasks
entrypoint: ./entrypoints/tcp-entrypoint.sh
volumes:
tcp_server:
You should have it build:
the same image that contains your Rails application. Override the command:
to run the Rake task. In the Compose file it should look roughly like:
version: '3.8'
services:
tcp:
build: .
depends_on: [database, redis]
env_file: .env
command: rake socketing
app:
build: .
depends_on: [database, redis]
env_file: .env
ports: ['3000:3000']
database: { ... }
redis: { ... }
sidekiq: { ... }
The sidekiq
container probably looks very similar.
Since you have two containers labeled build: .
, Compose will try to build the image twice. However, these images have identical Dockerfiles and run on an identical source tree. This means that Docker's layer caching will take effect, and the second build will run extremely quickly and will not actually produce a new image.
To make this work correctly, you also need to correctly structure your Dockerfile. It's important here that you put the actual command to run as the Dockerfile CMD
and not the ENTRYPOINT
(or at the very least it must match the override you have in the docker-compose.yml
). As always, make sure the image is self-contained and includes all of its library dependencies and application code (note the absence of volumes:
in the Compose file above).
FROM ruby:2.7
WORKDIR /app
RUN gem install bundler:2.3.12
COPY Gemfile Gemfile.lock .
RUN bundle install
COPY . .
EXPOSE 3000
# ENTRYPOINT ["./entrypoint.sh"]
CMD rails server -b 0.0.0.0
You should not put the command in the ENTRYPOINT
or an entrypoint script. Instead, use the entrypoint to do some first-time setup, and maybe inject related wrappers like Bundler.
#!/bin/sh
# entrypoint.sh
# Run database migrations, but only if we're running the main server.
if [ "$1" = rails -a "$2" == server ]; then
bundle exec rake db:migrate
fi
# Run whatever command we were given under Bundler.
exec bundle exec "$@"
The command:
is very straightforward to override, and whatever you provide appears in the "$@"
in the final statement of the script. So if you want a Pry console, for example, you can
docker-compose run app \
rails console
which will override the command:
at the command line, leaving the image's ENTRYPOINT
unchanged. The sample Compose file does the same thing for your TCP server launched as a Rake task (and, again, I'd expect your Sidekiq worker to work the same way).