容器化一个Ruby on Rails应用程序

先决条件

  • 您已安装最新版本的 Docker Desktop
  • 您有一个 Git客户端。本节中的示例展示了Git CLI,但您可以使用任何客户端。

概述

本节将引导您完成容器化并运行一个Ruby on Rails应用程序。

获取示例应用程序

示例应用程序使用了流行的 Ruby on Rails 框架。

克隆示例应用程序以与本指南一起使用。打开终端,将目录更改为您想要工作的目录,并运行以下命令来克隆存储库:

$ git clone https://github.com/falconcr/docker-ruby-on-rails.git

初始化 Docker 资源

现在你已经有了一个应用程序,你可以创建必要的Docker资源来容器化你的应用程序。你可以使用Docker Desktop内置的Docker Init功能来帮助简化这个过程,或者你也可以手动创建这些资源。

docker init,用于为项目引导Docker相关资产的命令,目前还不支持Ruby编程语言。这意味着如果你正在使用Ruby,你需要手动创建Dockerfiles和其他相关配置。

docker-ruby-on-rails目录中,创建以下文件:

创建一个名为 Dockerfile 的文件,内容如下。

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"]

创建一个名为 compose.yaml 的文件,内容如下。

compose.yaml
services:
  web:
    build: .
    command: bundle exec rails s -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"

创建一个名为 .dockerignore 的文件,内容如下。

.dockerignore
<button @click="window.navigator.clipboard.writeText(atob(code).replaceAll(/^[\$>]\s+/gm, '')); copying = true; setTimeout(() => copying = false, 2000);" class="absolute right-3 top-3 z-10 text-gray-light-300 dark:text-gray-dark-600" title="复制" x-data="{ code: 'Z2l0Ci5naXRpZ25vcmUKCiMgQ3JlYXRlZCBieSBodHRwczovL3d3dy5naXRpZ25vcmUuaW8vYXBpL2dpdCxydWJ5LHJhaWxzLGpldGJyYWlucythbGwKIyBFZGl0IGF0IGh0dHBzOi8vd3d3LmdpdGlnbm9yZS5pby8/dGVtcGxhdGVzPWdpdCxydWJ5LHJhaWxzLGpldGJyYWlucythbGwKCiMjIyBHaXQgIyMjCiMgQ3JlYXRlZCBieSBnaXQgZm9yIGJhY2t1cHMuIFRvIGRpc2FibGUgYmFja3VwcyBpbiBHaXQ6CiMgJCBnaXQgY29uZmlnIC0tZ2xvYmFsIG1lcmdldG9vbC5rZWVwQmFja3VwIGZhbHNlCioub3JpZwoKIyBDcmVhdGVkIGJ5IGdpdCB3aGVuIHVzaW5nIG1lcmdlIHRvb2xzIGZvciBjb25mbGljdHMKKi5CQUNLVVAuKgoqLkJBU0UuKgoqLkxPQ0FMLioKKi5SRU1PVEUuKgoqX0JBQ0tVUF8qLnR4dAoqX0JBU0VfKi50eHQKKl9MT0NBTF8qLnR4dAoqX1JFTU9URV8qLnR4dAoKIyMjIEpldEJyYWlucythbGwgIyMjCiMgQ292ZXJzIEpldEJyYWlucyBJREVzOiBJbnRlbGxpSiwgUnVieU1pbmUsIFBocFN0b3JtLCBBcHBDb2RlLCBQeUNoYXJtLCBDTGlvbiwgQW5kcm9pZCBTdHVkaW8gYW5kIFdlYlN0b3JtCiMgUmVmZXJlbmNlOiBodHRwczovL2ludGVsbGlqLXN1cHBvcnQuamV0YnJhaW5zLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDY1NDQ4MzkKCiMgVXNlci1zcGVjaWZpYyBzdHVmZgouaWRlYS8qKi93b3Jrc3BhY2UueG1sCi5pZGVhLyoqL3Rhc2tzLnhtbAouaWRlYS8qKi91c2FnZS5zdGF0aXN0aWNzLnhtbAouaWRlYS8qKi9kaWN0aW9uYXJpZXMKLmlkZWEvKiovc2hlbGYKCiMgR2VuZXJhdGVkIGZpbGVzCi5pZGVhLyoqL2NvbnRlbnRNb2RlbC54bWwKCiMgU2Vuc2l0aXZlIG9yIGhpZ2gtY2h1cm4gZmlsZXMKLmlkZWEvKiovZGF0YVNvdXJjZXMvCi5pZGVhLyoqL2RhdGFTb3VyY2VzLmlkcwouaWRlYS8qKi9kYXRhU291cmNlcy5sb2NhbC54bWwKLmlkZWEvKiovc3FsRGF0YVNvdXJjZXMueG1sCi5pZGVhLyoqL2R5bmFtaWMueG1sCi5pZGVhLyoqL3VpRGVzaWduZXIueG1sCi5pZGVhLyoqL2RibmF2aWdhdG9yLnhtbAoKIyBHcmFkbGUKLmlkZWEvKiovZ3JhZGxlLnhtbAouaWRlYS8qKi9saWJyYXJpZXMKCiMgR3JhZGxlIGFuZCBNYXZlbiB3aXRoIGF1dG8taW1wb3J0CiMgV2hlbiB1c2luZyBHcmFkbGUgb3IgTWF2ZW4gd2l0aCBhdXRvLWltcG9ydCwgeW91IHNob3VsZCBleGNsdWRlIG1vZHVsZSBmaWxlcywKIyBzaW5jZSB0aGV5IHdpbGwgYmUgcmVjcmVhdGVkLCBhbmQgbWF5IGNhdXNlIGNodXJuLiAgVW5jb21tZW50IGlmIHVzaW5nCiMgYXV0by1pbXBvcnQuCiMgLmlkZWEvbW9kdWxlcy54bWwKIyAuaWRlYS8qLmltbAojIC5pZGVhL21vZHVsZXMKIyAqLmltbAojICouaXByCgojIENNYWtlCmNtYWtlLWJ1aWxkLSovCgojIE1vbmdvIEV4cGxvcmVyIHBsdWdpbgouaWRlYS8qKi9tb25nb1NldHRpbmdzLnhtbAoKIyBGaWxlLWJhc2VkIHByb2plY3QgZm9ybWF0CiouaXdzCgojIEludGVsbGlKCm91dC8KCiMgbXBlbHRvbmVuL3NidC1pZGVhIHBsdWdpbgouaWRlYV9tb2R1bGVzLwoKIyBKSVJBIHBsdWdpbgphdGxhc3NpYW4taWRlLXBsdWdpbi54bWwKCiMgQ3Vyc2l2ZSBDbG9qdXJlIHBsdWdpbgouaWRlYS9yZXBsc3RhdGUueG1sCgojIENyYXNobHl0aWNzIHBsdWdpbiAoZm9yIEFuZHJvaWQgU3R1ZGlvIGFuZCBJbnRlbGxpSikKY29tX2NyYXNobHl0aWNzX2V4cG9ydF9zdHJpbmdzLnhtbApjcmFzaGx5dGljcy5wcm9wZXJ0aWVzCmNyYXNobHl0aWNzLWJ1aWxkLnByb3BlcnRpZXMKZmFicmljLnByb3BlcnRpZXMKCiMgRWRpdG9yLWJhc2VkIFJlc3QgQ2xpZW50Ci5pZGVhL2h0dHBSZXF1ZXN0cwoKIyBBbmRyb2lkIHN0dWRpbyAzLjErIHNlcmlhbGl6ZWQgY2FjaGUgZmlsZQouaWRlYS9jYWNoZXMvYnVpbGRfZmlsZV9jaGVja3N1bXMuc2VyCgojIyMgSmV0QnJhaW5zK2FsbCBQYXRjaCAjIyMKIyBJZ25vcmVzIHRoZSB3aG9sZSAuaWRlYSBmb2xkZXIgYW5kIGFsbCAuaW1sIGZpbGVzCiMgU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qb2VibGF1L2dpdGlnbm9yZS5pby9pc3N1ZXMvMTg2IGFuZCBodHRwczovL2dpdGh1Yi5jb20vam9lYmxhdS9naXRpZ25vcmUuaW8vaXNzdWVzLzM2MAoKLmlkZWEvCgojIFJlYXNvbjogaHR0cHM6Ly9naXRodWIuY29tL2pvZWJsYXUvZ2l0aWdub3JlLmlvL2lzc3Vlcy8xODYjaXNzdWVjb21tZW50LTI0OTYwMTAyMwoKKi5pbWwKbW9kdWxlcy54bWwKLmlkZWEvbWlzYy54bWwKKi5pcHIKCiMgU29uYXJsaW50IHBsdWdpbgouaWRlYS9zb25hcmxpbnQKCiMjIyBSYWlscyAjIyMKKi5yYmMKY2FweWJhcmEtKi5odG1sCi5yc3BlYwovZGIvKi5zcWxpdGUzCi9kYi8qLnNxbGl0ZTMtam91cm5hbAovcHVibGljL3N5c3RlbQovY292ZXJhZ2UvCi9zcGVjL3RtcApyZXJ1bi50eHQKcGlja2xlLWVtYWlsLSouaHRtbAoKIyBJZ25vcmUgYWxsIGxvZ2ZpbGVzIGFuZCB0ZW1wZmlsZXMuCi9sb2cvKgovdG1wLyoKIS9sb2cvLmtlZXAKIS90bXAvLmtlZXAKCiMgVE9ETyBDb21tZW50IG91dCB0aGlzIHJ1bGUgaWYgeW91IGFyZSBPSyB3aXRoIHNlY3JldHMgYmVpbmcgdXBsb2FkZWQgdG8gdGhlIHJlcG8KY29uZmlnL2luaXRpYWxpemVycy9zZWNyZXRfdG9rZW4ucmIKY29uZmlnL21hc3Rlci5rZXkKCiMgT25seSBpbmNsdWRlIGlmIHlvdSBoYXZlIHByb2R1Y3Rpb24gc2VjcmV0cyBpbiB0aGlzIGZpbGUsIHdoaWNoIGlzIG5vIGxvbmdlciBhIFJhaWxzIGRlZmF1bHQKIyBjb25maWcvc2VjcmV0cy55bWwKCiMgZG90ZW52CiMgVE9ETyBDb21tZW50IG91dCB0aGlzIHJ1bGUgaWYgZW52aXJvbm1lbnQgdmFyaWFibGVzIGNhbiBiZSBjb21taXR0ZWQKLmVudgoKIyMgRW52aXJvbm1lbnQgbm9ybWFsaXphdGlvbjoKLy5idW5kbGUKL3ZlbmRvci9idW5kbGUKCiMgdGhlc2Ugc2hvdWxkIGFsbCBiZSBjaGVja2VkIGluIHRvIG5vcm1hbGl6ZSB0aGUgZW52aXJvbm1lbnQ6CiMgR2VtZmlsZS5sb2NrLCAucnVieS12ZXJzaW9uLCAucnVieS1nZW1zZXQKCiMgdW5sZXNzIHN1cHBvcnRpbmcgcnZtIDwgMS4xMS4wIG9yIGRvaW5nIHNvbWV0aGluZyBmYW5jeSwgaWdub3JlIHRoaXM6Ci5ydm1yYwoKIyBpZiB1c2luZyBib3dlci1yYWlscyBpZ25vcmUgZGVmYXVsdCBib3dlcl9jb21wb25lbnRzIHBhdGggYm93ZXIuanNvbiBmaWxlcwovdmVuZG9yL2Fzc2V0cy9ib3dlcl9jb21wb25lbnRzCiouYm93ZXJyYwpib3dlci5qc29uCgojIElnbm9yZSBwb3cgZW52aXJvbm1lbnQgc2V0dGluZ3MKLnBvd2VudgoKIyBJZ25vcmUgQnllYnVnIGNvbW1hbmQgaGlzdG9yeSBmaWxlLgouYnllYnVnX2hpc3RvcnkKCiMgSWdub3JlIG5vZGVfbW9kdWxlcwpub2RlX21vZHVsZXMvCgojIElnbm9yZSBwcmVjb21waWxlZCBqYXZhc2NyaXB0IHBhY2tzCi9wdWJsaWMvcGFja3MKL3B1YmxpYy9wYWNrcy10ZXN0Ci9wdWJsaWMvYXNzZXRzCgojIElnbm9yZSB5YXJuIGZpbGVzCi95YXJuLWVycm9yLmxvZwp5YXJuLWRlYnVnLmxvZyoKLnlhcm4taW50ZWdyaXR5CgojIElnbm9yZSB1cGxvYWRlZCBmaWxlcyBpbiBkZXZlbG9wbWVudAovc3RvcmFnZS8qCiEvc3RvcmFnZS8ua2VlcAoKIyMjIFJ1YnkgIyMjCiouZ2VtCi8uY29uZmlnCi9JbnN0YWxsZWRGaWxlcwovcGtnLwovc3BlYy9yZXBvcnRzLwovc3BlYy9leGFtcGxlcy50eHQKL3Rlc3QvdG1wLwovdGVzdC92ZXJzaW9uX3RtcC8KL3RtcC8KCiMgVXNlZCBieSBkb3RlbnYgbGlicmFyeSB0byBsb2FkIGVudmlyb25tZW50IHZhcmlhYmxlcy4KIyAuZW52CgojIElnbm9yZSBCeWVidWcgY29tbWFuZCBoaXN0b3J5IGZpbGUuCgojIyBTcGVjaWZpYyB0byBSdWJ5TW90aW9uOgouZGF0KgoucmVwbF9oaXN0b3J5CmJ1aWxkLwoqLmJyaWRnZXN1cHBvcnQKYnVpbGQtaVBob25lT1MvCmJ1aWxkLWlQaG9uZVNpbXVsYXRvci8KCiMjIFNwZWNpZmljIHRvIFJ1YnlNb3Rpb24gKHVzZSBvZiBDb2NvYVBvZHMpOgojCiMgV2UgcmVjb21tZW5kIGFnYWluc3QgYWRkaW5nIHRoZSBQb2RzIGRpcmVjdG9yeSB0byB5b3VyIC5naXRpZ25vcmUuIEhvd2V2ZXIKIyB5b3Ugc2hvdWxkIGp1ZGdlIGZvciB5b3Vyc2VsZiwgdGhlIHByb3MgYW5kIGNvbnMgYXJlIG1lbnRpb25lZCBhdDoKIyBodHRwczovL2d1aWRlcy5jb2NvYXBvZHMub3JnL3VzaW5nL3VzaW5nLWNvY29hcG9kcy5odG1sI3Nob3VsZC1pLWNoZWNrLXRoZS1wb2RzLWRpcmVjdG9yeS1pbnRvLXNvdXJjZS1jb250cm9sCiMgdmVuZG9yL1BvZHMvCgojIyBEb2N1bWVudGF0aW9uIGNhY2hlIGFuZCBnZW5lcmF0ZWQgZmlsZXM6Ci8ueWFyZG9jLwovX3lhcmRvYy8KL2RvYy8KL3Jkb2MvCgovLmJ1bmRsZS8KL2xpYi9idW5kbGVyL21hbi8KCiMgZm9yIGEgbGlicmFyeSBvciBnZW0sIHlvdSBtaWdodCB3YW50IHRvIGlnbm9yZSB0aGVzZSBmaWxlcyBzaW5jZSB0aGUgY29kZSBpcwojIGludGVuZGVkIHRvIHJ1biBpbiB
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

你现在应该在 docker-ruby-on-rails 目录中有以下三个文件。

  • .dockerignore
  • compose.yaml
  • Dockerfile

要了解更多关于文件的信息,请参阅以下内容:

运行应用程序

docker-ruby-on-rails目录内,在终端中运行以下命令。

$ docker compose up --build

打开浏览器并在 http://localhost:3000查看应用程序。您应该会看到一个简单的Ruby on Rails应用程序。

在终端中,按ctrl+c停止应用程序。

在后台运行应用程序

您可以通过添加-d选项来从终端分离运行应用程序。在docker-ruby-on-rails目录内,在终端中运行以下命令。

$ docker compose up --build -d

打开浏览器并在 http://localhost:3000查看应用程序。

你应该会看到一个简单的Ruby on Rails应用程序。

在终端中,运行以下命令以停止应用程序。

$ docker compose down

有关Compose命令的更多信息,请参阅 Compose CLI 参考

摘要

在本节中,您学习了如何使用 Docker 容器化并运行您的 Ruby 应用程序。

相关信息:

下一步

在下一节中,您将学习如何使用容器开发您的应用程序。