- Rails 5.2.1 (latest)
- ruby 2.5.3
- postgresql
- reactjs
- imagemagick
-
Create new project
rails new project_name -d postgresql --webpack=react
-
Setup database
rails db:setup rails db:migrate
-
Create
index_controller.rb
to make sure Rails is running on localhost -
Start server and visit http://localhost:3000
rails server
The docker setup is modified from the docker-rails repo.
Features:
-
Use Alpine (ruby:2.5.3-alpine) as base image - smaller image size
-
Use Multi-stage build - The idea is to first build the stuff and then copy only the resulting artefacts into the final image.
-
Remove bundler cache - the cache folder, C source file and compiled object file for gems with native extensions.
RUN rm -rf /usr/local/bundle/cache/*.gem \ && find /usr/local/bundle/gems/ -name "*.c" -delete \ && find /usr/local/bundle/gems/ -name "*.o" -delete
-
Remove parts of the app not needed in resulting image -
node_modules/
,tmp/cache
,spec/
,test/
and theassets/
folders# Remove folders not needed in resulting image RUN rm -rf node_modules tmp/cache app/assets vendor/assets lib/assets spec
-
Add
Dockerfile
-
Build the docker image
docker build . -t terraform-rails
... ... Successfully built 04e42927d47b Successfully tagged terraform-rails:latest
-
Check that the image is built
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE terraform-rails latest 04e42927d47b About a minute ago 203MB
Compose is a tool for defining and running multi-container Docker applications.
Having dockerized your rails app is not enough, the app still needs to communite with other services, at the minimal, to a database server. Rails app contain is only one of the services.
By using Compose, you can define multiple container services and how they are linked to each other.
-
Add
docker-compose.yml
-
Build containers
docker-compose build
-
Check if the image is built
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE terraform-rails latest 724c9b8a43e8 32 seconds ago 203MB
-
Remember to change the database config
# docker-compose.yml services: db: environment: - POSTGRES_PASSWORD ... app: &app_base ... ... environment: - POSTGRES_HOST=db - POSTGRES_PASSWORD - POSTGRES_USER=postgres
# config/database.yml default: &default ... host: <%= ENV['POSTGRES_HOST'] %> username: <%= ENV['POSTGRES_USER'] %> password: <%= ENV['POSTGRES_PASSWORD'] %> ...
-
Start the containers
docker-compose up
To stop the containers
docker-compose down
There are many reasons to use nginx as web server between your app and the users.
- Static redirects - you could setup your nginx to redirect all http traffic to the same url with https. This way such trivial requests will never hit your app server.
- Multipart upload - Nginx is better suited to handle multipart uploads. Nginx will combine all the requests and send it as a single file to puma.
- Serving static assets - It is recommended to serve static assets (those in /public/ endpoint in rails) via a webserver without loading your app server.
- There are some basic DDoS protections built-in in nginx.
-
Add
devops/docker/web/Dockerfile
for nginx container -
Add
devops/docker/web/nginx.conf
-
Configure
docker-compose.yml
to use nginx# docker-compose.yml ... ... web: build: context: . dockerfile: ./devops/docker/web/Dockerfile depends_on: - app ports: - 8080:80 ... ...
-
Start the containers again
docker-compose up
Inspect your running containers, you should see 3 containers are running, only the web container are exposing the port 8080
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8cccc470542f terraform-rails_web "nginx -g 'daemon of…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp terraform-rails_web_1 382b29806b87 terraform-rails "./devops/docker/sta…" 2 minutes ago Up 2 minutes 3000/tcp terraform-rails_app_1 9b7077ef437b postgres:alpine "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 5432/tcp terraform-rails_db_1
Visit http://localhost:8080 for your app.
Developing with docker under OSX/ Windows is a huge pain, since sharing your code into containers will slow down the code-execution about 60 times.
-
Install
docker-sync
-
Configure
docker-sync
withdocker-sync.yml
# docker-sync.yml version: "2" options: verbose: true syncs: app-sync: # tip: add -sync and you keep consistent names as a convention src: './' # optional, default: docker-compose-dev.yml if you like, you can set a custom location (path) of your compose file. # Do not set it, if you do not want to use it at all # # if its there, it gets used, if you name it explicitly, it HAS to exist # HINT: you can also use this as an array to define several compose files to include. Order is important! compose-dev-file-path: 'docker-compose-sync.yml' # use 'native_osx' or 'unison' sync_strategy: 'unison' sync_excludes: ['.gitignore', '.git/', '.DS_Store'] # this does not user groupmap but rather configures the server to map # optional: usually if you map users you want to set the user id of your application container here sync_userid: '1000' # optional, a list of regular expressions to exclude from the fswatch - see fswatch docs for details # IMPORTANT: this is not supported by native_osx watch_excludes: ['.*/.git', '.*/node_modules', '.*/bower_components', 'tmp', '.*/sass-cache', '.*/.sass-cache', '.*/.sass-cache', '.coffee', '.scss', '.sass', '.gitignore'] # optional: use this to switch to fswatch verbose mode watch_args: '-v'
-
Modify
docker-compose.yml
or usedocker-compose-sync.yml
whichdocker-sync
will use to override thedocker-compose.yml
# docker-compose-sync.yml version: '3.4' services: app: volumes: - terraform-rails-app-sync:/app:nocopy worker: volumes: - terraform-rails-app-sync:/app:nocopy volumes: terraform-rails-app-sync: external: true
WARNING: Because unison is a two-way sync, do not use the same volume name as your other apps, your codes might get replaced with your other apps.
-
Use
docker-sync
commands to replacedocker-compose
to start, stop and cleanupTo start
docker-sync-stack start
To stop, just Ctrl-C
To clean up,
docker-sync-stack clean
-
Install
redis
gem and useredis
image# Gemfile gem 'redis', '~> 4.0'
redis
can also be used for ActionCable# docker-compose.yml ... redis: image: redis:5.0-alpine volumes: - redis_data:/data
-
Install
sidekiq
gem and configure it# Gemfile gem 'redis', '~> 4.0'
# config/sidekiq.yml :concurrency: 5 :pidfile: ./tmp/pids/sidekiq.pid :logfile: ./log/sidekiq.log :queues: - [default, 1] - [mailers, 2]
# config/initializers/sidekiq.yml require 'sidekiq/api' redis_config = { url: ENV['REDIS_SIDEKIQ_URL'] } Sidekiq.configure_server do |config| config.redis = redis_config end Sidekiq.configure_client do |config| config.redis = redis_config end
-
Add a
worker
container to thedocker-compose.yml
anddocker-compose-dev.yml
# docker-compose.yml worker: <<: *app_base volumes: - .:/app command: bundle exec sidekiq ports: [] depends_on: - app - redis
# docker-sync.yml worker: volumes: - terraform-rails-app-sync:/app:nocopy
-
Start your containers
docker-sync-stack start
Inspect your containers
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 90e32b9e3842 terraform-rails "bundle exec sidekiq" 17 minutes ago Up 17 minutes 3000/tcp terraform-rails_worker_1 c3d557215b8c terraform-rails_web "nginx -g 'daemon of…" 17 minutes ago Up 17 minutes 0.0.0.0:8080->80/tcp terraform-rails_web_1 b52b96a11ee3 terraform-rails "./devops/docker/sta…" 17 minutes ago Up 17 minutes 3000/tcp terraform-rails_app_1 f85601685371 postgres:alpine "docker-entrypoint.s…" 17 minutes ago Up 17 minutes 5432/tcp terraform-rails_db_1 3ca3a2e387e3 redis:5.0-alpine "docker-entrypoint.s…" 17 minutes ago Up 17 minutes 6379/tcp terraform-rails_redis_1 4f6028ab1d46 eugenmayer/unison:2.51.2.1 "/entrypoint.sh supe…" 17 minutes ago Up 17 minutes 127.0.0.1:32797->5000/tcp terraform-rails-app-sync
-
Configure ActiveJob to use sidekiq
# config/application.rb config.active_job.queue_adapter = :sidekiq
Docker image for production requires different docker-compose configuration.
The differences between development and production:-
restart: always
healthcheck
hostname
- cleaner and leaner - without
development:test
bundle, remove unnecessary folders, no EXECJS_RUNTIME - no
volumes: .:/app
for production - IMPORTANT: need to mount the
config/master.key
as volume for production enviroment
-
To run production environment on localhost
# start docker-compose for production docker-compose -f docker-compose-prod.yml up
Even with docker for production, you will still need to setup the infrastructure that support docker containers (and other services).
Terraform is a infrastructure-as-a-code tool to declare your infrastructure with easy to read and write DSL. A tool to provision and deploy infrastructure. (See Terraform for more information)