From c0ff24df888343b0df8797f2a799bb243905a353 Mon Sep 17 00:00:00 2001 From: Igor Alexandrov Date: Wed, 27 Nov 2024 12:15:35 +0400 Subject: [PATCH 1/5] Rails docks upgrade interation 1 --- content/guides/ruby/containerize.md | 109 ++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/content/guides/ruby/containerize.md b/content/guides/ruby/containerize.md index d968c28c873..a699153da12 100644 --- a/content/guides/ruby/containerize.md +++ b/content/guides/ruby/containerize.md @@ -18,19 +18,112 @@ aliases: ## Overview -This section walks you through containerizing and running a Ruby on Rails application. +This section walks you through containerizing and running a [Ruby on Rails](https://rubyonrails.org/) application. -## Get the sample application +Starting from Rails 7.1 [Docker is supported out of the box](https://guides.rubyonrails.org/7_1_release_notes.html#generate-dockerfiles-for-new-rails-applications). This means that you will get a `Dockerfile`, `.dockerignore` and `bin/docker-entrypoint` files generated for you when you create a new Rails application. -The sample application uses the popular [Ruby on Rails](https://rubyonrails.org/) framework. +If you have an existing Rails application, you will need to create the Docker assets manually. Unfortunately `docker init` command does not yet support Rails. This means that if you are working with Rails, you'll need to copy Dockerfile and other related configurations manually from the examples below. -Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: +## Initialize Docker assets -```console -$ git clone https://github.com/falconcr/docker-ruby-on-rails.git -``` +Rails 7.1 generates multistage Dockerfile out of the box, below is an example of such file generated from a Rails template. -## Initialize Docker assets +> Multistage Dockerfiles help create smaller, more efficient images by separating build and runtime dependencies, ensuring only necessary components are included in the final image. Read more about multi-stage builds [in a dedicated article](/get-started/docker-concepts/building-images/multi-stage-builds/) + + +```dockerfile {collapse=true,title=Dockerfile} +# syntax=docker/dockerfile:1 +# check=error=true + +# This Dockerfile is designed for production, not development. +# docker build -t app . +# docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name app app + +# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html + +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version +ARG RUBY_VERSION=3.3.6 +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base + +# Rails app lives here +WORKDIR /rails + +# Install base packages +# Replace sqlite3 with libpq-dev if using PostgreSQL, or libmysqlclient-dev if using MySQL +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + +# Throw-away build stage to reduce size of final image +FROM base AS build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential curl git pkg-config && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Install JavaScript dependencies and Node.js for asset compilation +# +# Uncomment the following lines if you are using NodeJS need to compile assets +# +# ARG NODE_VERSION=18.12.0 +# ARG YARN_VERSION=1.22.19 +# ENV PATH=/usr/local/node/bin:$PATH +# RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ +# /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \ +# npm install -g yarn@$YARN_VERSION && \ +# npm install -g mjml && \ +# rm -rf /tmp/node-build-master + +# Install application gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ + bundle exec bootsnap precompile --gemfile + +# Install node modules +# +# Uncomment the following lines if you are using NodeJS need to compile assets +# +# COPY package.json yarn.lock ./ +# RUN --mount=type=cache,id=yarn,target=/rails/.cache/yarn YARN_CACHE_FOLDER=/rails/.cache/yarn \ +# yarn install --frozen-lockfile + +# Copy application code +COPY . . + +# Precompile bootsnap code for faster boot times +RUN bundle exec bootsnap precompile app/ lib/ + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile + +# Final stage for app image +FROM base + +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN groupadd --system --gid 1000 rails && \ + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log storage tmp +USER 1000:1000 + +# Entrypoint prepares the database. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start server via Thruster by default, this can be overwritten at runtime +EXPOSE 80 +CMD ["./bin/thrust", "./bin/rails", "server"] +``` Now that you have an application, you can create the necessary Docker assets to containerize your application. You can use Docker Desktop's built-in Docker Init From 0fa67080724b32c7485201950fd43d34f5bfa3f2 Mon Sep 17 00:00:00 2001 From: Igor Alexandrov Date: Thu, 28 Nov 2024 11:55:49 +0400 Subject: [PATCH 2/5] Added example of .dockerignore and compose.yml files --- content/guides/ruby/containerize.md | 81 +++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/content/guides/ruby/containerize.md b/content/guides/ruby/containerize.md index a699153da12..4b792eab0c7 100644 --- a/content/guides/ruby/containerize.md +++ b/content/guides/ruby/containerize.md @@ -30,8 +30,10 @@ Rails 7.1 generates multistage Dockerfile out of the box, below is an example of > Multistage Dockerfiles help create smaller, more efficient images by separating build and runtime dependencies, ensuring only necessary components are included in the final image. Read more about multi-stage builds [in a dedicated article](/get-started/docker-concepts/building-images/multi-stage-builds/) +Even though the Dockerfile is generated for you, it's a good idea to understand what it does and how it works, so we recommend reading the following example. -```dockerfile {collapse=true,title=Dockerfile} + +```dockerfile {title=Dockerfile} # syntax=docker/dockerfile:1 # check=error=true @@ -49,9 +51,9 @@ FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base WORKDIR /rails # Install base packages -# Replace sqlite3 with libpq-dev if using PostgreSQL, or libmysqlclient-dev if using MySQL +# Replace libpq-dev with sqlite3 if using SQLite, or libmysqlclient-dev if using MySQL RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips libpq-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Set production environment @@ -125,6 +127,79 @@ EXPOSE 80 CMD ["./bin/thrust", "./bin/rails", "server"] ``` +The Dockerfile above assumes you are using Thruster and Puma as the application server. In case you are using any other server, you can replace the last three lines with the following: + +```dockerfile +# Start the application server +EXPOSE 3000 +CMD ["./bin/rails", "server"] +``` + +Besides the Dockerfile you will also need a `.dockerignore` file. This file is used to exclude files and directories from the context of the build. Below is an example of a `.dockerignore` file. + +```text {collapse=true,title=".dockerignore"} +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. + +# Ignore git directory. +/.git/ +/.gitignore + +# Ignore bundler config. +/.bundle + +# Ignore all environment files. +/.env* + +# Ignore all default key files. +/config/master.key +/config/credentials/*.key + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/.keep + +# Ignore assets. +/node_modules/ +/app/assets/builds/* +!/app/assets/builds/.keep +/public/assets + +# Ignore CI service files. +/.github + +# Ignore development files +/.devcontainer + +# Ignore Docker-related files +/.dockerignore +/Dockerfile* +``` + +The last thing that may be necessary, but not always required is `compose.yml` file, used by Docker Compose to define the services that make up your application. Since we are using SQLite as the database, we don't need to define a separate service for the database, and the only service we need is the Rails application itself. + +```yaml {collapse=true,title=compose.yaml} +services: + web: + build: . + volumes: + - .:/myapp + ports: + - "3000:80" +``` + + Now that you have an application, you can create the necessary Docker assets to containerize your application. You can use Docker Desktop's built-in Docker Init feature to help streamline the process, or you can manually create the assets. From 7b6fa56690894fef6070be078368eb118f824209 Mon Sep 17 00:00:00 2001 From: Igor Alexandrov Date: Fri, 29 Nov 2024 11:19:52 +0400 Subject: [PATCH 3/5] Removed old Docker assets examples from the Ruby guide --- content/guides/ruby/containerize.md | 325 ++-------------------------- 1 file changed, 21 insertions(+), 304 deletions(-) diff --git a/content/guides/ruby/containerize.md b/content/guides/ruby/containerize.md index 4b792eab0c7..0884dbffa48 100644 --- a/content/guides/ruby/containerize.md +++ b/content/guides/ruby/containerize.md @@ -127,7 +127,7 @@ EXPOSE 80 CMD ["./bin/thrust", "./bin/rails", "server"] ``` -The Dockerfile above assumes you are using Thruster and Puma as the application server. In case you are using any other server, you can replace the last three lines with the following: +The Dockerfile above assumes you are using Thruster together with Puma as an application server. In case you are using any other server, you can replace the last three lines with the following: ```dockerfile # Start the application server @@ -135,7 +135,26 @@ EXPOSE 3000 CMD ["./bin/rails", "server"] ``` -Besides the Dockerfile you will also need a `.dockerignore` file. This file is used to exclude files and directories from the context of the build. Below is an example of a `.dockerignore` file. +We specified the Docker entrypoint as `./bin/docker-entrypoint` which is a script that prepares the database and runs the application server. Below is an example of such a script. + +```bash {title=docker-entrypoint} +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ]; then + LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) + export LD_PRELOAD +fi + +# If running the rails server then create or migrate existing database +if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then + ./bin/rails db:prepare +fi + +exec "${@}" +``` + +Besides the two files above you will also need a `.dockerignore` file. This file is used to exclude files and directories from the context of the build. Below is an example of a `.dockerignore` file. ```text {collapse=true,title=".dockerignore"} # See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. @@ -199,308 +218,6 @@ services: - "3000:80" ``` - -Now that you have an application, you can create the necessary Docker assets to -containerize your application. You can use Docker Desktop's built-in Docker Init -feature to help streamline the process, or you can manually create the assets. - -`docker init`, the command for bootstrapping the Docker-related assets for a project, does not yet support the Ruby programming language. This means that if you are working with Ruby, you'll need to create Dockerfiles and other related configurations manually. - -Inside the `docker-ruby-on-rails` directory, create the following files: - -Create a file named `Dockerfile` with the following contents. - -```dockerfile {collapse=true,title=Dockerfile} -# syntax=docker/dockerfile:1 - -# Use the official Ruby image with version 3.2.0 -FROM ruby:3.2.0 - -# Install dependencies -RUN apt-get update -qq && apt-get install -y \ - nodejs \ - postgresql-client \ - libssl-dev \ - libreadline-dev \ - zlib1g-dev \ - build-essential \ - curl - -# Install rbenv -RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv && \ - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc && \ - echo 'eval "$(rbenv init -)"' >> ~/.bashrc && \ - git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build && \ - echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc - -# Install the specified Ruby version using rbenv -ENV PATH="/root/.rbenv/bin:/root/.rbenv/shims:$PATH" -RUN rbenv install 3.2.0 && rbenv global 3.2.0 - -# Set the working directory -WORKDIR /myapp - -# Copy the Gemfile and Gemfile.lock -COPY Gemfile /myapp/Gemfile -COPY Gemfile.lock /myapp/Gemfile.lock - -# Install Gems dependencies -RUN gem install bundler && bundle install - -# Copy the application code -COPY . /myapp - -# Precompile assets (optional, if using Rails with assets) -RUN bundle exec rake assets:precompile - -# Expose the port the app runs on -EXPOSE 3000 - -# Command to run the server -CMD ["rails", "server", "-b", "0.0.0.0"] -``` - -Create a file named `compose.yaml` with the following contents. - -```yaml {collapse=true,title=compose.yaml} -services: - web: - build: . - command: bundle exec rails s -b '0.0.0.0' - volumes: - - .:/myapp - ports: - - "3000:3000" -``` - -Create a file named `.dockerignore` with the following contents. - -```text {collapse=true,title=".dockerignore"} -git -.gitignore - -# Created by https://www.gitignore.io/api/git,ruby,rails,jetbrains+all -# Edit at https://www.gitignore.io/?templates=git,ruby,rails,jetbrains+all - -### Git ### -# Created by git for backups. To disable backups in Git: -# $ git config --global mergetool.keepBackup false -*.orig - -# Created by git when using merge tools for conflicts -*.BACKUP.* -*.BASE.* -*.LOCAL.* -*.REMOTE.* -*_BACKUP_*.txt -*_BASE_*.txt -*_LOCAL_*.txt -*_REMOTE_*.txt - -### JetBrains+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### JetBrains+all Patch ### -# Ignores the whole .idea folder and all .iml files -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 - -*.iml -modules.xml -.idea/misc.xml -*.ipr - -# Sonarlint plugin -.idea/sonarlint - -### Rails ### -*.rbc -capybara-*.html -.rspec -/db/*.sqlite3 -/db/*.sqlite3-journal -/public/system -/coverage/ -/spec/tmp -rerun.txt -pickle-email-*.html - -# Ignore all logfiles and tempfiles. -/log/* -/tmp/* -!/log/.keep -!/tmp/.keep - -# TODO Comment out this rule if you are OK with secrets being uploaded to the repo -config/initializers/secret_token.rb -config/master.key - -# Only include if you have production secrets in this file, which is no longer a Rails default -# config/secrets.yml - -# dotenv -# TODO Comment out this rule if environment variables can be committed -.env - -## Environment normalization: -/.bundle -/vendor/bundle - -# these should all be checked in to normalize the environment: -# Gemfile.lock, .ruby-version, .ruby-gemset - -# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: -.rvmrc - -# if using bower-rails ignore default bower_components path bower.json files -/vendor/assets/bower_components -*.bowerrc -bower.json - -# Ignore pow environment settings -.powenv - -# Ignore Byebug command history file. -.byebug_history - -# Ignore node_modules -node_modules/ - -# Ignore precompiled javascript packs -/public/packs -/public/packs-test -/public/assets - -# Ignore yarn files -/yarn-error.log -yarn-debug.log* -.yarn-integrity - -# Ignore uploaded files in development -/storage/* -!/storage/.keep - -### Ruby ### -*.gem -/.config -/InstalledFiles -/pkg/ -/spec/reports/ -/spec/examples.txt -/test/tmp/ -/test/version_tmp/ -/tmp/ - -# Used by dotenv library to load environment variables. -# .env - -# Ignore Byebug command history file. - -## Specific to RubyMotion: -.dat* -.repl_history -build/ -*.bridgesupport -build-iPhoneOS/ -build-iPhoneSimulator/ - -## Specific to RubyMotion (use of CocoaPods): -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# vendor/Pods/ - -## Documentation cache and generated files: -/.yardoc/ -/_yardoc/ -/doc/ -/rdoc/ - -/.bundle/ -/lib/bundler/man/ - -# for a library or gem, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# Gemfile.lock -# .ruby-version -# .ruby-gemset - -# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: - -# End of https://www.gitignore.io/api/git,ruby,rails,jetbrains+all -``` - You should now have the following three files in your `docker-ruby-on-rails` directory. From e273815db8c259609d2af841d768a05f069a78a3 Mon Sep 17 00:00:00 2001 From: Igor Alexandrov Date: Tue, 3 Dec 2024 13:03:20 +0400 Subject: [PATCH 4/5] Added more examples --- content/guides/ruby/containerize.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/content/guides/ruby/containerize.md b/content/guides/ruby/containerize.md index 0884dbffa48..140ae59e673 100644 --- a/content/guides/ruby/containerize.md +++ b/content/guides/ruby/containerize.md @@ -208,7 +208,7 @@ Besides the two files above you will also need a `.dockerignore` file. This file The last thing that may be necessary, but not always required is `compose.yml` file, used by Docker Compose to define the services that make up your application. Since we are using SQLite as the database, we don't need to define a separate service for the database, and the only service we need is the Rails application itself. -```yaml {collapse=true,title=compose.yaml} +```yaml {title=compose.yaml} services: web: build: . @@ -218,23 +218,23 @@ services: - "3000:80" ``` -You should now have the following three files in your `docker-ruby-on-rails` -directory. +You should now have the following files in your application folder: -- .dockerignore -- compose.yaml -- Dockerfile +- `.dockerignore` +- `compose.yaml` +- `Dockerfile` +- `bin/docker-entrypoint` To learn more about the files, see the following: -- [Dockerfile](/reference/dockerfile.md) -- [.dockerignore](/reference/dockerfile.md#dockerignore-file) +- [Dockerfile](/reference/dockerfile) +- [.dockerignore](/reference/dockerfile#dockerignore-file) - [compose.yaml](/reference/compose-file/_index.md) +- [docker-entrypoint](/reference/dockerfile/#entrypoint) ## Run the application -Inside the `docker-ruby-on-rails` directory, run the following command in a -terminal. +To run the application, run the following command in a terminal inside the application's directory. ```console $ docker compose up --build From c7fdf949e299c902c0fc5cf2c5322b1a715a9112 Mon Sep 17 00:00:00 2001 From: Igor Alexandrov Date: Wed, 4 Dec 2024 11:20:48 +0400 Subject: [PATCH 5/5] Updated docker compose command to start the application --- content/guides/ruby/containerize.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/guides/ruby/containerize.md b/content/guides/ruby/containerize.md index 140ae59e673..037a9693791 100644 --- a/content/guides/ruby/containerize.md +++ b/content/guides/ruby/containerize.md @@ -212,8 +212,8 @@ The last thing that may be necessary, but not always required is `compose.yml` f services: web: build: . - volumes: - - .:/myapp + environment: + - RAILS_MASTER_KEY ports: - "3000:80" ``` @@ -237,7 +237,7 @@ To learn more about the files, see the following: To run the application, run the following command in a terminal inside the application's directory. ```console -$ docker compose up --build +$ RAILS_MASTER_KEY= docker compose up --build ``` Open a browser and view the application at [http://localhost:3000](http://localhost:3000). You should see a simple Ruby on Rails application.