• 2019.11.17 更新 -

1. 新建 Gemfile:

source 'https://rubygems.org'

gem 'rails'

2. 新建 Dockerfile (新架构 gem 安装到本地,这一步不用了)

FROM registry.cn-shanghai.aliyuncs.com/aaron_dev/ruby2.6-nodejs-yarn:v1

# 因为默认安装好 rails 后 Gemfile 的 source 总是会设置成默认源,如果在国内会非常慢以至于卡在 `bunlde install`,所以用这条命令覆盖 Gemfile 的 source
RUN bundle config mirror.https://rubygems.org https://gems.ruby-china.com

COPY Gemfile* /app/

RUN bundle install

3. 新建空的 Gemfile.lock

4. 新建 docker-compose.yml

version: '3.7'
services:
  web:    
    image: registry.cn-shanghai.aliyuncs.com/aaron_dev/rails-base-image:v1
    command: bash -c "rm -f /app/tmp/pids/server.pid && rails s -p 5000 -b 0.0.0.0"
    volumes:
      # old string format
      - .:/app
      - ./.gems:/usr/local/bundle
    ports:
      - [host-port]:5000
    environment:
      - WEBPACKER_DEV_SERVER_HOST=webpack_dev_server
    depends_on:
      - db

    # byebug, use attach
    stdin_open: true
    tty: true

  webpack_dev_server:
    image: registry.cn-shanghai.aliyuncs.com/aaron_dev/rails-base-image:v1
    command: bin/webpack-dev-server
    ports:
      - [host-port]:3035
    volumes:
      - .:/app
      - ./.gems:/usr/local/bundle
    environment:
      - WEBPACKER_DEV_SERVER_HOST=0.0.0.0

  db:
    image: postgres
    volumes:
      # new format
      - type: volume
        source: dbdata
        target: /var/lib/postgresql/data
        volume:
          nocopy: true
    ports:
      - [host-port]:5432

volumes:
  dbdata:

5. 在项目目录下运行:

docker-compose run --rm web gem install bundler
docker-compose run --rm web bundle install
docker-compose run --rm web rails new . --force --database=postgresql [--skip-sprockets --skip-turbolinks] --webpack
  • --skip-sprockets --skip-turbolinks 参数可选,webpack 允许将 css 依旧交给 sprockets 管理;turbolinks 除非特殊需求,否则不建议关闭

注:这里补充一个知识点,在服务器上安装 docker 后,因为一般我们不会用超级用户,所以是没有权限直接运行 docker 命令的,如果不想所有命令前都加上 sudo 的话,就要把你的用户加到 docker 组里,运行如下命令然后重新登录即可:

sudo usermod -aG docker [server_username]

然后检查 /etc/group 的 docker 组知否已经加入了用户: docker:x:999:[username]

6. 封装 image (新架构 gem 安装到本地,这一步不用了)

安装完 rails 后会得到一个 image:[project-name]_web,可以通过 docker images 查看。 这个 image 就是我们最终的应用镜像了,然后根据具体情况把它上传到镜像仓库。

docker tag [project-name]_web [image-repository]/[image-name]:[tag]
docker push [image-repository]/[image-name]:[tag]

接下来修改 docker-compose.yml,替换 web service 下的 build: .

image: [image-repository]/[image-name]:[tag]

7. 修改 database.yml 如下:

default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:

8. 项目初始化完成后,修改 views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_pack_tag 'application' %>
  </head>

  <body>
    <%= yield %>

    <!-- 加载完页面再 load js -->
    <%= javascript_pack_tag 'application' %>
  </body>
</html>

9. 启动项目

docker-compose up
docker-compose exec web rails db:create
docker-compose exec web rails db:migrate

至此,我们就快速建立了一个基于 webpack 的 Rails 项目,然后加入一些基础的前端依赖,并进一步完善 webpack 的基础配置。

进入 web 容器 docker-compose exec -it web bash,运行:

yarn add bootstrap
yarn add popper.js
yarn add jquery
yarn add @fortawesome/fontawesome-free

然后修改 config/webpack/environment.js 如下:

const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
/**
 * Automatically load modules instead of having to import or require them everywhere.
 * Support by webpack. To get more information:
 *
 * https://webpack.js.org/plugins/provide-plugin/
 * http://j.mp/2JzG1Dm
 * 
 * 配置文件,更改需要重启服务
 */
environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    echarts: 'echarts',
    $: 'jquery',
    jQuery: 'jquery',
    jquery: 'jquery',
    'window.jQuery': 'jquery',
    Popper: ['popper.js', 'default']
  })
)
module.exports = environment

这个文件可以参考这两篇文章:https://joey.io/how-to-use-jquery-in-rails-5-2-using-webpack/, https://webpack.js.org/plugins/provide-plugin/

再引入这些第三方组件 app/javascript/packs/application.js

// 第三方库加载
import "bootstrap"
import "bootstrap/dist/css/bootstrap.min.css"
import "@fortawesome/fontawesome-free/css/all.min.css"

10. 允许 webpack 打包 css 文件

webpacker 4 之后,默认只会打包 js,若要启用 css 打包,需修改 config/webpacker.yml:

# Extract and emit a css file
  extract_css: true

注:因为 webpack 将所有资源都打包成 JS 的方式伺服前端,所以如果不加这条,则 css 也会随着 JS 一起加载,结果就是 html 页面显示的时候 css 还未被加载,等到 JS 加载的时候 css 才渲染,所以每次在页面刷新的时候样式会从无到有地闪动一下

11. 启用 rails-ujs(可选)

在使用 webpacker 后,如果你没有同时启用 sprockets,那 rails 原生提供的 js 方法都会失效,需要手动添加。

比如,原来你可以利用 data-confirm="Are you sure..." 的辅助方法打开一个 js 的 alert 提示框,但是改用 webpacker 后就没有这个功能了,原因是这个方法是 rails-ujs 提供的,原本在自带的 sprockets 中,webpacker 默认是没有的,需要手动安装,方法如下:

先在前端安装 rails-ujs 依赖:

yarn add rails-ujs

然后,在 webpacker 中启用:

import Rails from "rails-ujs"
Rails.start()

12. 新增 Gem 包 (新架构 gem 安装到本地,这一步不用了)

最后讲一讲如果要更新 image,比如安装了新的 gem 包,该如何处理。

首先,当然是在 Gemfile 里加上 gem '[package-name]',然后我们有两种方法可以更新到 image。

  • 直接 commit 当前读写层

    docker-compose exec web bundle install
    docker commit [container] [image-repository]/[image-name]:[new-tag]
    

    这样,一个新版本的应用镜像就直接创建好了。这个方法的好处是非常直观,而且很快也很方便,只需要安装新的 gem 即可;但缺点是,它直接将当前 container 的读写层所有内容都 commit 到了新镜像,这会包含一定的风险,因为也许你会不记得之前是否对该 container 做了任何临时的修改,而你并不想将这些修改持久化到 image。(所以 docker commit 向来要慎用)

  • 基于原 image 再 build

    docker-compose exec web bundle install
    docker build . -t [image-repository]/[image-name]:[new-tag]
    

    这个方法的缺点在于,要运行两次 bundle install,一次给当前环境,一次在创建新的镜像,而且 build 会把所有的 gem 重新再安装一遍,所以非常慢。缺点很明显,但是优势是镜像内容可控,规避了之前的风险。其实这也是标准做法。

最后,将新的 image 更新到 docker-compose.yml

image: [image-repository]/[image-name]:[new-tag]