Rails-Docker
Docker Notes
Run ruby code without installing Ruby
$> docker run [OPTIONS] <image> <command>
# This command starts a new container based on <image>, and executes <command> inside the container.
# You may find it helpful to think about it in two parts: docker run [OPTIONS] <image> says what type
# of container we’re going to run, whereas <command> says what we want to run inside the container.
$> docker run ruby:3.2.2 ruby -e "puts :hello"
hello
# Mount PWD in docker's `/usr/src/app` volume `-v`, with `-i` input to `bash`, `--rm` for ephemeral/throwaway,
# `-t` for pseudoterminal/pty
# whenever you need a long-lived, interactive session, you need to specify both the -i and -t options.
$> docker run -i -t --rm -v ${PWD}:/usr/src/app ruby:3.2.2 bash
Run multiple commands via docker run
$> docker run <options> [image:version] \
bash -c "command1 && command2 && command3..."
Dockerfile
# Install required image of Ruby
# Every Dockerfile start with "FROM" base image
FROM ruby:3.2.2
# Download latest package information
# -y : Yes to any prompt, -qq : Really queit mode
RUN apt-get update -yqq
# install nodejs and DO NOT install other recommended packages as we don't need them
# NOTE: nodejs is NOT required with Rails 7
RUN apt-get install -yqq --no-install-recommends nodejs
# Copy the all files in current folder (.) i.e. Application root to `/usr/src/app`
COPY . /usr/src/app
# docker run [OPTIONS] <our custom image> bin/rails server
# This command will fail, because by default container's working directory is `/`
# which doesn't have our rails application. Our rails application is IN `/usr/src/app`
# Thus change the working directory with WORKDIR
WORKDIR /usr/src/app
# Now we can run any command with `RUN`
RUN bundle install
.dockerignore
Add unnecessary/sensitive files to .dockerignore
to ensure it’s not added to docker image
Image build cache
Docker creates a cache of each step in Dockerfile
. Any change in the file at line x, line x
onwards will be built fresh. However, x-
will be reusing docker cache.
Adding non-relevant files to .dockerignore
helps docker build run faster as cache will not be invalidated due to change in these files.
Best practices
Always:
- Combine
apt-get update
withapt-get install
command, so that we’ve latest version of new packages. Because earlierRUN apt-get update -yqq
was cached, so in next build, it won’t pull latest packages. - list each package in new line in alphabetical order for easier search and maintenance
```shell
RUN apt-get update -yqq && apt-get install -yqq –no-install-recommends \
nodejs
vim
#### docker-compose.yml
```yaml
version: '3' # Version of docker-compose - https://docs.docker.com/compose/compose-file/compose-versioning/
services:
web: # Service name
build: . # build image from `Dockerfile` in current directory
ports:
- "3000:3000" # <host-machine-port>:<docker-container-service-port>
volumes:
- .:/usr/src/app # <host-directory>:<docker-container-directory> - Ensures our host machine current dir/code is sync with docker container, i.e. change in our app in host machine will immediately reflect on browser reload
redis:
image: redis
database:
image: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: root
POSTGRES_DB: orendaa_development
docker-compose commands
$> docker-compose start
$> docker-compose start <service-name>
$> docker-compose stop
$> docker-compose stop <service-name>
$> docker-compose restart <service-name>
$> docker-compose up
$> docker-compose up <service-name>
Logs
# NOTE: This follows docker's log, not service(i.e. rails s) logs here
$> docker-compose logs -f web # -f means follow the logs from `web` service - similar to tail
$> docker-compose logs -f redis
Running one time commands
If the docker container is NOT running:
$> docker-compose run --rm web echo "I'm spinning up a container, run the echo and then deleting it, i.e. in ephemeral mode"
If the docker container is running:
$> docker-compose exec web echo "Since the web docker image is running , just execute echo on same"
Docker compose up/down
± ➜ docker compose up
[+] Building 0.0s (0/0)
[+] Running 2/2
✔ Network orendaa_default Created 0.0s
✔ Container orendaa-web-1 Created 0.0s
Attaching to orendaa-web-1
orendaa-web-1 | => Booting Puma
orendaa-web-1 | => Rails 7.0.5 application starting in development
orendaa-web-1 | => Run `bin/rails server --help` for more startup options
orendaa-web-1 | Puma starting in single mode...
orendaa-web-1 | * Puma version: 5.6.6 (ruby 3.2.2-p53) ("Birdie's Version")
orendaa-web-1 | * Min threads: 5
orendaa-web-1 | * Max threads: 5
orendaa-web-1 | * Environment: development
orendaa-web-1 | * PID: 1
orendaa-web-1 | * Listening on http://0.0.0.0:3000
orendaa-web-1 | Use Ctrl-C to stop
^CGracefully stopping... (press Ctrl+C again to force)
Aborting on container exit...
[+] Stopping 1/1
✔ Container orendaa-web-1 Stopped 0.4s
canceled
chandan@~/Workspace/2023/orendaa (main_dockerize_app) ± ➜ docker-compose down
[+] Running 2/2
✔ Container orendaa-web-1 Removed 0.0s
✔ Network orendaa_default Removed
Interact with redis container
$> docker-compose up -d redis # Run redis in detached mode
[+] Building 0.0s (0/0)
[+] Running 1/1
✔ Container orendaa-redis-1 Started
$> docker-compose run --rm redis redis-cli -h redis # The option -h redis says, “Connect to the host named redis.”
[+] Building 0.0s (0/0)
[+] Building 0.0s (0/0)
redis:6379> ping
PONG
$> docker-compose stop redis
± ➜ docker-compose exec redis redis-cli -h redis
service "redis" is not running container #1
± ➜ docker-compose up -d redis
[+] Building 0.0s (0/0)
[+] Running 1/1
✔ Container orendaa-redis-1 Started 0.3s
± ➜ docker-compose exec redis redis-cli -h redis # The option -h redis says, “Connect to the host named redis.”
redis:6379>
Interact with database container
Startup the database
container and check it’s running
± ➜ docker-compose up -d database
[+] Running 14/1
✔ database 13 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 22.4s
[+] Building 0.0s (0/0)
[+] Running 1/1
✔ Container orendaa-database-1 Started 1.1s
chandan@~/Workspace/2023/orendaa (main_dockerize_app) ± ➜ docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
orendaa-database-1 postgres "docker-entrypoint.s…" database 6 seconds ago Up 4 seconds 5432/tcp
orendaa-redis-1 redis "docker-entrypoint.s…" redis About an hour ago Up 38 minutes 6379/tcp
orendaa-web-1 orendaa-web "bin/rails s -b 0.0.…" web 44 minutes ago Up 38 minutes 0.0.0.0:3000->3000/tcp
Interact with postgres via CLI
± ➜ docker-compose run --rm database psql -U postgres -h database
[+] Building 0.0s (0/0)
[+] Building 0.0s (0/0)
Password for user postgres:
psql (15.3 (Debian 15.3-1.pgdg120+1))
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
---------------------+----------+----------+------------+------------+------------+-----------------+-----------------------
orendaa_development | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc |
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
(4 rows)
postgres=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+-----------
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
postgres=# \q
Access rails console
$> docker-compose exec web rails console # Assuming container running
$> docker-compose run --rm web rails console
Force recreate
- Force recreate service(s)
$> docker-compose up -d --force-recreate <service-name> $> docker-compose up -d --force-recreate # Recreates all services and start them in detached mode
Connecting to PSQL/Redis terminal from docker
$> docker-compose run --rm <service-name> <command> -h <service-name>
$> docker-compose run --rm database psql -U postgres -h database
$> docker-compose run --rm redis redis-cli -h redis
Access the bash
shell inside a service container
$> docker-compose run --rm <service-name> bash
$> docker-compose run --rm web bash
Remove a service container
$> docker-compose rm -f <service-container-name>
$> docker-compose rm -f database
Inspect a volume
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
env_file:
- .docker/development/web
- .docker/development/database # TODO - It has to be dynamic
redis:
image: redis
database:
image: postgres
build:
context: .
dockerfile: Dockerfile-database
env_file:
- .docker/development/database
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Inspect the db_data
volume:
$> docker volume inspect --format '' <app-name>_db_data || ls
$> docker volume inspect --format '' orendaa_db_data || ls
/var/lib/docker/volumes/orendaa_db_data/_data
Related Notes:
References:
- rails-development-environment-with-docker-compose
- Digital Ocean : containerizing-a-ruby-on-rails-application-for-development-with-docker-compose
This is a sapling 🌱 in my digital garden 🏡.