部署 长文慎入:将 Rails 程序部署到 Docker 容器中

xdoc · 2017年03月06日 · 最后由 zll11111 回复于 2018年05月16日 · 5961 次阅读
本帖已被设为精华帖!

关于在此过程中遇到的坑先不说了…… 新人仅作记录,请批评指正。

首先要感谢 @saiga用 docker-compose 部署 Rails , 静态资源加载不了,浏览器显示 404 贴中指点迷津。 看那个贴子,你就知道我有多新啦……😀

限于篇幅,还有一篇: 《长文慎入:将 Rails 程序部署到 Docker 容器中 (Q&A)》

在整个过程中,基本上按照下面这篇文章实现的,但中间还是有许多不一样的问题。 Docker for an Existing Rails Application

前言

Docker 是个好东西

如果你对 Docker 技术不了解,可以去 Docker 官网Google 检索一下,这样有助于阅读本文。

网站托管的技术实现,大概经历这样几种方式或阶段:

1 放到实体的计算机( 服务器 ) 2 放到虚拟的计算机当中,将硬件虚拟化( VM ) 3 放到容器当中,将所需服务虚拟化( Docker )

而 Docker 最大的好处则是:让开发环境与生产环境一致

而 Docker 的原意为「码头工人」,其实是很形像的说法。你需要什么样的程序或服务,Docker 把你搬过来就可以了。

将 Rails 部署到 Docker 的主要思路

  • 目标:将已经存在的 Rails 程序部署到 Docker 容器中
  • 思路:
    • docker-compose 将多个容器串起来,各司其职,协同服务。
    • 将 Rails + Unicorn、Nginx、、Postgres 都视为提供服务的对象,将它们分别位于独立的容器中
    • Nginx 在最前面解析请求并处理静态资源; Unicorn 位于 Nginx 于 Rails 程序之间,用于处理动态的请求;最后面还有一个数据存储的 Postgres
  • 具体:
    • 服务 app :存放 RailsUnicorn 程序
    • 服务 web :存放 nginx,负责解析各种外部请求,处理静态的资源
      • 静态资源就是运行 rake assets:precompile 生成在 public/assets 中的内容
    • 服务 db :即数据存储,使用 Postgres

我找了一张图,大概是这样(后面还少了一个 db 的):

部署环境

  • 宿主机:CentOS-7
  • Docker :1.11.2
  • Docker Compose : 1.11.2

准备容器

1、克隆 Rails 代码

$ git clone https://github.com/2liu/eshop

代码会存在 eshop 文件夹中

2、Dockerfile : 构建 Rails 的运行环境

在上述 eshop 目录中创建一个名为 Dockerfile 的文件,用于创建 Rails 环境

  1 # 使用安装了最少的软件的镜像,也有使用 ruby:alpine
  2 FROM ruby:2.3.3-slim
  3
  4 # Ruby 使用的是 Debian 系统
  5 # 刷新 debian 的软件管理源,否则,慢得让人抓狂
  6 RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
  7     echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list && \
  8     echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list && \
  9     echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list && \
10     echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
12
13 # 安装必要套件
14 RUN apt-get update -qq && apt-get install -y build-essential
15
16 # 安装 postgres
17 RUN apt-get install -y libpq-dev
18 # 安装 sqlite3,这个用于test,development
19 RUN apt-get install -y libsqlite3-dev
20
21 # 安装 JS runtime,一定要装
22 RUN apt-get install -y nodejs
23
24 # 用于图片上传
25 RUN apt-get install -y imagemagick
26 # 方便进到容器编辑、查看代码
27 RUN apt-get install -y vim
28
29 # 设置环境变量,即在后面可以用 $RAILS_ROOT
30 # 来指代容器中的 rails 程序放的目录
31 ENV RAILS_ROOT /var/www/eshop
32
33 # 创建 rails 程序目录和程序运行所需要的 pids 的目录
34 RUN mkdir -p $RAILS_ROOT/tmp/pids
35
36 # 设置容器里的工作目录
37 WORKDIR $RAILS_ROOT
38
39 # 备份 Gemfile 及 lock到容器的工作目录中
40 # 当Gemfile 没有改变时,省略下面的 bundle install
41 COPY Gemfile Gemfile
42 COPY Gemfile.lock Gemfile.lock
43
44 # 安装 Rails 环境
45 RUN gem install bundler
46 RUN bundle install
47
48 # 将 Dockerfile 目录下所有内容复制到容器工作目录
49 COPY . .
50
51 # 比较重要的一步,对静态资源进行 precompile
52 RUN bundle exec rake RAILS_ENV=$RAILS_ENV DATABASE_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@127.0.0.1/$POSTGRES_PRODUCT_DB assets:precompile
53
54 # 当容器启动时运行的脚本,即 unicorn
55 CMD ["config/containers/app_cmd.sh"]

3. Unicorn 部分

容器启动之后, unicorn 会启动,启动时加载预设的配置文件和环境变量

3.1 app_cmd.sh

config 下创建 containers 目录,也有直接放在 config 中的 但这种方式将所有与容器相关的配置文件都放在一个文件夹当中,更直观 config/containers/app_cmd.sh

#!/usr/bin/env bash

exec bundle exec unicorn -c config/containers/unicorn.rb -E $RAILS_ENV;

保存好该文件之后,修改文件属性为可执行 $ chmod 775 app_cmd.sh

3.2 添加 Unicorn gem

Gemfile

gem ‘unicorn'

3.3 Unicorn 配置文件

这个配置文件我没有改动,直接拿来就用 所以注释,就直接用原文了。 文件:config/containers/unicorn.rb

# Where our application lives. $RAILS_ROOT is defined in our Dockerfile.
app_path = ENV['RAILS_ROOT']

# Set the server's working directory
working_directory app_path

# Define where Unicorn should write its PID file
pid "#{app_path}/tmp/pids/unicorn.pid"

# Bind Unicorn to the container's default route, at port 3000
listen "0.0.0.0:3000"

# Define where Unicorn should write its log files
stderr_path "#{app_path}/log/unicorn.stderr.log"
stdout_path "#{app_path}/log/unicorn.stdout.log"

# Define the number of workers Unicorn should spin up.
# A new Rails app just needs one. You would scale this
# higher in the future once your app starts getting traffic.
# See https://unicorn.bogomips.org/TUNING.html
worker_processes 1

# Make sure we use the correct Gemfile on restarts
before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = "#{app_path}/Gemfile"
end

# Speeds up your workers.
# See https://unicorn.bogomips.org/TUNING.html
preload_app true

#
# Below we define how our workers should be spun up.
# See https://unicorn.bogomips.org/Unicorn/Configurator.html
#

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end

  # Before forking, kill the master process that belongs to the .oldbin PID.
  # This enables 0 downtime deploys.
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end
end

原文说,此时就可以运行下面命令, $ docker build -t eshop_app . (注意最后有一个 . 表示从 Dockerfile 所在目录开始。 创建 Rails 程序的镜像了,虽然不能运行,但可以通过 docker images 查看。

但实际操作过程中,都是设置好,最后执行一步 docker-compose build 完事。 所以说,这一步可以不执行。

4. 用 Nginx 负责前端

4.1 Nginx 的 Dockerfile

Nginx 在另一个容器里,也需要创建一个 Dockerfile

config/containers/Dockerfile-nginx

1 # build from the official Nginx image
  2 FROM nginx
  3
  4 # 更新 163 源
  5 RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
  6     echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list && \
  7     echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list && \
  8     echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list && \
  9     echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
10
11
12 # install essential Linux packages
13 RUN apt-get update -qq && apt-get -y install apache2-utils vim
14
15 # establish where Nginx should look for files
16 ENV RAILS_ROOT /var/www/eshop
17
18 # Set our working directory inside the image
19 WORKDIR $RAILS_ROOT
20
21 # create log directory
22 RUN mkdir log
23
24 # copy over static assets
25 # 这一步实做时,复制是复制到 nginx 容器中的 public 文件夹下
26 # 但静态资料却显示不了,最后在 docker-compose 文件中设置了 volumes 才可以
27 # 可能是哪个地方我没有设置正确。
28 COPY public public/
29
30 # copy our Nginx config template
31 COPY config/containers/nginx.conf /tmp/eshop.nginx
32
33 # substitute variable references in the Nginx config template for real values from the environment
34 # put the final config in its place
35 RUN envsubst '$RAILS_ROOT' < /tmp/eshop.nginx > /etc/nginx/conf.d/default.conf
36
37 # Use the "exec" form of CMD so Nginx shuts down gracefully on SIGTERM (i.e. `docker stop`)
38 CMD [ "nginx", "-g", "daemon off;" ]

4.2 Nginx 配置文件

有了 nginx 镜像,我们还需要配置一下 Nginx

config/containers/nginx.conf

# define our application server
# Nginx 处理不了由 Rails 产生的动态的请求,所以我们要告诉它如何将这些请求传递给 Unicorn。
# 这里我们将它指向将会在后面 docker-compose 中定义的 app 这个服务的 3000 端口
# 其实就是 Unicorn
upstream unicorn {
  server app:3000;
}

# 处理对www.xxx.com 的网址请求
server {
  # default - 如果请求的网址没有找到匹配的 sever 模式,就用这个 server
  # deferred - 在 Linux 上用,为了优化性能
  listen 80 default deferred;
  server_name www.xxx.com;

  # 静态文件就保存在这里
  root   $RAILS_ROOT/public;
  index  index.html;

  # 定义日志文件保存位置
  access_log $RAILS_ROOT/log/nginx.access.log;
  error_log $RAILS_ROOT/log/nginx.error.log;

  # 上面的 server_name 是处理对域名的请求
  # location - 负责处理对特定文件或文件夹的请求
  # 禁止诸如 .env .git 之类的文件或目录被访问
  location ~ /\. {
    deny all;
  }

  # 禁止访问 .rb .log 文件
  location ~* ^.+\.(rb|log)$ {
    deny all;
  }

  # serve static (compiled) assets directly if they exist (for rails production)
  # 这里其实是个正则表达式,
  # 匹处理类似网址: `domain.com/assets/aaaa.jpg` 这样的请求
  # 加上了 uploads
  location ~ ^/(assets|images|javascripts|stylesheets|swfs|system|uploads)/ {
     # $uri : 地址栏里输入,abc.com/def/hig.html,则 $uri 为 `/def`
     # @rails : 在后面定义的 named location
     # 如果地址匹配进来,则先按 $uri 处理,若没有找到,则交给 @rails 处理
    try_files $uri @rails;

    # 关闭访问记录
    access_log off;
    # `gzip_static` :设置为 `on` ,在处理压缩之前,先查找已经预压缩的文件(.gz)
    # 避免每次对同一个文件进行重复的压缩处理
    gzip_static on; # to serve pre-gzipped version

     # 对请求进行缓存,并设为最长失效日期
     # 这种做法已过时,放在这里是为了兼容性
     # 替代的做法就是下面的设置 `Cache-Control` 头
    expires max;
    # public 对每个用户有效; private 对当前用户有效
    add_header Cache-Control public;

    # Some browsers still send conditional-GET requests if there's a
    # Last-Modified header or an ETag header even if they haven't
    # reached the expiry date sent in the Expires header.
    add_header Last-Modified "";
    add_header ETag "";
    break;
  }

  # send non-static file requests to the app server
  location / {
    try_files $uri @rails;
  }

  location @rails {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    # 阻止 Nginx 做其它别的重定向请求
    proxy_redirect off;
    # 将所有不符合上面的 server_name 或 location 的请求全部发送到这里
    # 交由最上面定义的 `unicorn` 这个 upstream 服务来处理
    # 而 unicorn 又交给我们下面会定义的 app 来处理
    proxy_pass http://unicorn;
  }
}

5. 数据库:

5.1 database.yml

这个不需要过多的配置 所以,我们直接使用 postgres: 9.4.5 这个镜像文件,所以不用创建 Dockerfile 文件了。 但我们需要设置 database.yml 文件

config/database.yml

default: &default
  adapter: postgresql
  host: db
  port: 5432
  username: postgres
  password: <%= ENV['POSTGRES_PASSWORD'] %>
  timeout: 5000


test:
  <<: *default
  database: eshop_dd_test


development:
  <<: *default
  database: eshop_dd_development

production:
  <<: *default
  database: eshop_dd_production

整个文件还是比较简单的,就说一下 password: <%= ENV['POSTGRES_PASSWORD'] %> 的设置 这里将密码保存到环境变量当中, 而环境变量的设置方法既可以在 Shell 中 通过 export ENV_VAR=value 来设置 又可以将环境变量写到 .env 文件中,这也是我采用的方法

5.2 添加 product 组

$ vi Gemfile 添加

group :production do
   gem 'pg'
end

6. .env 文件

出于安全因素的考虑,所以建议将隐密信息,如数据库用户名、密码等保存到环境变量中。 采用的方法是使用了 dotenv-rails 这个 Gem

  • 在 Gemfile 中添加:gem ‘dotenv-rails’
    • 这个 gem 要放在需要用到环境变量的 gem 之前,我就直接放到最上面了。
  • config/application.rbBundler.require(*Rails.groups) 下面添加一句:Dotenv::Railtie.load
  • 在项目根目录下创建 .env 文件(注意变量名大写,= 号两边没有空格)
RAILS_ENV=production
APP_HOME=/var/www/eshop
SECRET_KEY_BASE=long_long_code
OSTGRES_USER=postgres
POSTGRES_PASSWORD=keep_secret_ps
…...

别的还好,可以直接输入,就是那个 secret_key_base 这个可以通过 RAILS_ENV=production rake secret 来生成 而我们后面要用到 docker-compose ,那么可以这样运行 docker-compose exec -e RAILS_ENV=production app rake secret (不太确定是不是条命令了)

生成是一个长长的字符串,复制,粘贴到,本来是要放在 config/secrets.yml 中 但 在 production 那一块提示,要放到环境变量中 也就是上面的 .evn 文件中

添加需要被忽略的文件

.dockerignore

.git
.env
.dockerignore

.gitignore

.env
public/

超强变身合体(Docker Compose)

docker-compose 文件

到目前为止,上面三个容器还是各自为战的,而我们要用 将它们合体,从而超强变身

注意 yml 文件是严格缩进的,一定要注意层级关系 且 ymlyaml (好像推荐用这个)禁止用 tab 键缩进,就是用两个空格

与 Dockerfile 一样,都在根目录下 docker-compose.yml

version: '2'
services:
  app:
    build: .

    env_file: .env
    environment:
      RAILS_ENV: $RAILS_ENV

    # 在 version : 2 中这一项,好像是可以去掉的,下同
     links:
       - db

     expose:
       - "3000"

    # 这一步很重要,让宿主的程序目录加载到容器里的 `/var/www/eshop`
    # 这样当容器运行后,我们修改代码后,不需要重新 build
    # 只需要停止再启动 `docker-compose` 即可。
     volumes:
       - .:/var/www/eshop

  db:
    image: postgres:9.4.5
    volumes:
      - eshop-postgres:/var/lib/postgresql/data

  web:
    build:
      context: .
      dockerfile: config/containers/Dockerfile-nginx

    links:
      - app

    ports:
      - "80:80"
    volumes_from:
      - app

# 在 version 2 中 命名的 volume 要放到 顶级的 volumes 下
volumes:
  eshop-postgres:

创建容器

  • 运行 docker-compose build 就可以创建相关容
    • 运行前,先把 config/routes.rb 中的 devise_for :users 注释掉
    • build 结束之后,再打开
  • 创建完之后,就可以数据库相关操作了
    • docker-compose run app rake db:create
    • docker-compose run app rake db:migrate
    • docker-compose run app rake db:seed
  • 运行 docker-compose up -d
    • 一开始测试时,可以不带 -d 选项,会显示更多信息
  • 停止:docker-compose stop ,不带 -d 时,按 Ctrl + C
  • 可以通过 docker-compose ps 来查看容器运行状态

运行时状态

$ dc ps
    Name                   Command               State              Ports
------------------------------------------------------------------------------------
eshopdd_app_1   config/containers/app_cmd.sh     Up      3000/tcp
eshopdd_db_1    /docker-entrypoint.sh postgres   Up      5432/tcp
eshopdd_web_1   nginx -g daemon off;             Up      443/tcp, 0.0.0.0:80->80/tcp

Docker 相关命令

  • docker build -t app_name_app . : 创建带标签的镜像
  • docker exec -it container_name /bin/bash :进入容器里面
  • docker-compose run service_name env :查看服务容器的环境变量

------------------

------------------


下面是部署过程中所记录的一些错误或笔记。

没有过多整理,备查。也没有排版,见谅。


进到 容器里使用命令: docker exec -it container_name /bin/bash

设置 环境变量

Learn to use Docker in Development - Part 1

有两种方法:

  • 一种是直接设置
    • 在运行 docker-compose 的 shell 下运行
    • export ENV_VAR=value
  • 另一种是设置在 .env文件中
    • 在运行 docker-compose 的 shell 下创建 .env文件
    • 在文件中添加:export ENV_VAR=value 存疑 ### 使用 环境变量
  • 在终端命令中使用
    • docker run -d -e POSTGRES_USER=$POSTGRES_USER -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD —net-blog —name db postgres

在测试时,因为 Gemfile 中用到了 sqlite3 ,所以一直提示需要安装

在 Dockerfile 中第 4 行最后,添加 libsqilte3-dev

错误:Bundler::GemRequireError: There was an error while trying to load the gem 'uglifier'.

$ docker-compose run app rake db:create Starting eshop_db_1 rake aborted! Bundler::GemRequireError: There was an error while trying to load the gem 'uglifier'. Gem Load Error is: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.

解决: in Ubuntu: apt-get install nodejs In OSX : brew install nodejs

http://stackoverflow.com/questions/34420554/there-was-an-error-while-trying-to-load-the-gem-uglifier-bundlergemrequire

错误:Specified 'postgresql' for database adapter, but the gem is not loaded. Add gem 'pg' to your Gemfile

$ vi Gemfile 添加

group :production do
   gem 'pg'
end

错误:Devise.secret_key was not set. Please add the following to your Devise initializer:

config.secret_key = '9c8abd0f6a9257625ad384ce95467d106811141bf6792345ab06c45028dfb4520f2184a3ef1a4c2aa46882e5f2f810656899a715b6e91b759cabebd88a3f3b10'

解决: 在 stackoverflow 上看到一句: devise-secret-key

原因应该是,在创建 devise model 之前,而在 config/routes.rb 中使用了 devise_for :users

将它注释掉,再重新生成就可以了。

Status Code: 404 Not Found

经过一番折腾后,终于可以 看到页面了,只是有两个问题:一个是图片找不到,二是 CSS 没有加载 查 Chrome Network 发现 :Status Code:404 Not Found

搜索了一下,才知道,在 development 下没有关系的,而到 production 之前要先将 assets 中的资源 precompile ,它会保存到 public/assets 下,可供 rails 读取。 因为 在 production 下,是不会自动处理静态资源的。

执行 docker exec -it appname_app_1 /bin/bash后 进入 app 的容器中 再执行 rake assets:precompile 没有错误报出,查看 public 中 assets 也看到有资料生成

但 还是老样子。

再找原因,后来想起,可能在生成 web 容器时就将 assets 执行 precompile 了,而这些生成的资料会被复制放到 web 容器中。 而我在 app 容器执行,但生成的资料是放在 app 容器里了,没有在 web 容器里,所以要重新生成,或拷到 web 容器里。

http://blog.carbonfive.com/2015/03/17/docker-rails-docker-compose-together-in-your-development-workflow/

Precompiling Assets

$ RAILS_ENV=production bin/rails assets:precompile

http://guides.rubyonrails.org/asset_pipeline.html#in-production

Development workflow

http://blog.carbonfive.com/2015/03/17/docker-rails-docker-compose-together-in-your-development-workflow/ When you want to run: With Docker Compose, you would run: bundle install docker-compose run web bundle install rails s docker-compose run web rails s rspec spec/path/to/spec.rb docker-compose run web rspec spec/path/to/spec.rb RAILS_ENV=test rake db:create docker-compose run -e RAILS_ENV=test web rake db:create tail -f log/development.log docker-compose run web tail -f log/development.log

FROM ruby:2.2.0
RUN apt-get update -qq && apt-get install -y build-essential nodejs npm nodejs-legacy mysql-client vim
RUN npm install -g phantomjs

RUN mkdir /myapp

WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install

ADD . /myapp
WORKDIR /myapp
RUN RAILS_ENV=production bundle exec rake assets:precompile --trace
CMD ["rails","server","-b",”0.0.0.0"]

更新太慢了,如何指定 apt-get 源

As the Ruby image itself is based on a

Debian image, we use apt-get to install those.

Ruby image 是基于 Debian 镜像的,所以我们使用 apt-get 来安装软件 而更新源,则可以修改 相关的源 FROM dockerfile/ubuntu 查了一下,发现并不是 Debian ,而实际情况用下面的方法也是有效的

# 更新源
RUN echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" > /etc/apt/sources.list

#更新apt-get源 使用163的源
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
    echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list && \
    echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list && \
    echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list && \
    echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list

http://mirrors.163.com/.help/debian.html

而 web 容器呢,又不一样了 查看一下 nginx Dockerfile 可以看到 FROM dockerfile/ubuntu 所以:

http://www.cnblogs.com/52fhy/p/5836429.html

ERROR: for app Invalid volume spec ".": Invalid volume destination path: '.' mount path must be absolute.

docker-compose.yml 中添加 volumes 代码如下:

app:
     build: .
     volumes:
          - .:$RAILS_ROOT

就报出:ERROR: for app Invalid volume spec ".": Invalid volume destination path: '.' mount path must be absolute.

同时还报出:

WARNING: The RAILS_ROOT variable is not set. Defaulting to a blank string.

后来 将代码 改成:

app:
     build: .
     volumes:
          - .:/var/www/app_name

就不再报错。 docker-compose build 执行也无错。

就是 docker-compose up 时报错,提示:

app_1  | bundler: failed to load command: unicorn (/usr/local/bundle/bin/unicorn)
app_1  | ArgumentError: directory for pid=/var/www/app_name/tmp/pids/unicorn.pid not writable

这又 是因为 在 Rails 的 Dockerfile 有这样一句:

RUN mkdir -p $RAILS_ROOT/tmp/pids 所以 这样是不可行的。

后来 ,又 仔细 看了一下文章 的最后 有个 docker-compose.override.yml

app:

  # map our application source code, in full, to the application root of our container
  volumes:
    - .:/var/www/docker_example

web:

  # use whatever volumes are configured for the app container
  volumes_from:
    - app

http://stackoverflow.com/a/14854266/4455426 能否用此方法,先给 nginx 容器添加一个 volume ,而后再把指定 Just solved the same problem. In my case Ryan's answer was not helpful. Bratsche pointed to the Rails Guides, unfortunately this didn't work for me too. However the resource was helpful. So I took Nginx configuration from there and added the root directive, pointing to the public directory. Without this it doesn't work.


# serve static assets
location ~ ^/assets/ {
  expires 1y;
  root  /path/to/my/cool_project/public;
  add_header Cache-Control public;

  add_header ETag "";
  break;
}

Restart nginx, and that's it.

复制文件从容器到主机

In order to copy a file from a container to the host, you can use the command

docker cp :/file/path/within/container /host/path/target

Here's a handy way to get at your latest container if you're simply using docker for a temp Linux environment:

docker ps -alq.

location ~ ^/(assets)/ { http://pothibo.com/2015/10/rails-in-production-with-coreos

查看容器内的环境变量

docker-compose run web env

Update for stack overflow:

39 #RUN RAILS_ENV=production bundle exec rake assets:precompile --trace 40 RUN bundle exec rake assets:precompile --trace 41 42 # Define the script we want run once the container boots 43 # Use the "exec" form of CMD so our script shuts down gracefully on SIGTERM (i.e. docker stop) 44 CMD [ "config/containers/app_cmd.sh” ]

$ docker-compose build ERROR: yaml.parser.ParserError: while parsing a block mapping in "./docker-compose.yml", line 1, column 1 expected , but found '' in "./docker-compose.yml", line 26, column 2

这个 问题主要 是缩进的不一致导致的,将缩进严格为 2 个空格,注意层级

$ docker-compose build ERROR: Validation failed in file './docker-compose.yml', reason(s): Unsupported config option for services.web: 'dockerfile'

查看版本: docker-compose --version docker-compose version 1.7.0rc2, build ea2d526

升级 docker-compose https://docs.docker.com/compose/install/

访问 github 速度太慢了,用了替代的方法

$ pip install docker-compose

装完,别忘记改权限:

$ sudo chmod +x /usr/local/bin/docker-compose

否则 会报错: $ docker-compose build
Cannot open self /usr/local/bin/docker-compose or archive /usr/local/bin/docker-compose.pkg

改了权限还是,报同样错误

测试这一步:https://github.com/docker/compose/issues/1135

same problem but I could not find a certain useful answer to solve it

#docker-compose Cannot open self /usr/local/bin/docker-compose or archive /usr/local/bin/docker-compose.pkg

yeah, i have solved this problem now! you can do like this.

do not use the command line curl -L https://github.com/docker/compose/releases/download/1.9.0/docker-compose-uname -s-uname -m > /usr/local/bin/docker-compose to download, maybe this makes it too slow to download.

choose the way of downloading the file , visit this https://github.com/docker/compose/releases website and choose file to download docker-compose-Linux-x86_64 7.67 MB docker-compose-Windows-x86_64.exe 5.97 MB then rename as docker-compose and copy into /usr/local/bin/

that's OK.

now you can use docker-compose --version to test whether your docker-compose is installed

Try this: If you have problems installing with curl, you can use pip instead: pip install -U docker-compose Then you need apply executable permissions to the binary:

chmod +x /usr/local/bin/docker-compose Let me know if the problem is fixed or not.

$ curl -L https://github.com/docker/compose/releases/download/1.7.0/docker-compose-`uname -s`-`uname -m` > ./docker-compose
$ sudo mv ./docker-compose /usr/bin/docker-compose
$ sudo chmod +x /usr/bin/docker-compose

最后,

部署 Capistrano 3 实现 Rails 自动化部署

https://ruby-china.org/topics/18616

Deploy a Rails app to Docker with Capistrano https://libertyseeds.ca/2015/09/09/Deploy-a-Rails-app-to-Docker-with-Capistrano/

PG::DatatypeMismatch: ERROR: column "is_slide" cannot be cast automatically to type boolean

HINT: You might need to specify "USING is_slide::boolean". : ALTER TABLE "banners" ALTER COLUMN "is_slide" TYPE boolean

现在的问题是: rake assets:precompile 生成的 assets 是在 app 容器当中,并不能被 nginx 容器查找到 所以,考虑将内容用 volumes, 和 volumes_from 来指向 而用法可以参考:https://github.com/ogihara-ryo/froide-task-management-tool/blob/1688e37758c3ab1ad8e770c1ed55347a18236cbb/docker-compose.yml

经过 上述操作之后,nginx 容器中是有那些已经生成的文件 了,但网页访问 还是没有 找到 assets

到此时,才意识到:

这根本 就是 nginx 对 assets 文件的访问控制上出了问题 这也可以 通过 Chrome 的开发者工具了解到一些信息,

搜索 关键词 nginx assets 404 http://stackoverflow.com/questions/11404502/rails-3-2-2-getting-a-404-on-stylesheets-and-js-assets-after-deployment-with-cap

gzip_static 检测: http://www.whatsmyip.org/http-compression-test/ 可复制 js 等地址(可从 chrome-dev 中查看)进去检测

直接在 app 容器中安装 nginx

  • sudo apt-get install nginx
    • 查看帮助 nginx -h
    • 查看启动帮助: /etc/init.d/nginx -h
    • 启动 nginx : sudo service nginx start

Dockerizing an Existing Rails Application

流程

  • docker-compose.yml
  • Dockerfile
  • config/database.yml
  • config/secrets.yml
  • .env
  • Termianl 运行 缓存容器 : docker-compose up -d db cache
  • docker-compose build app jobs
  • docker-compose run —rm app rake db:create db:migrate
  • ``
  • ``
  • ``
  • ``

Nginx and Docker 演示了一个简单的示例,创建 nginx 容器、docker-compose 一些命令, 以及更重要的演示了如何通过添加或减小容器数量来做负载平衡,简单容易操作 (docker-compose scale

  • d-c ps
  • d-c exec service_name bash
    • nslookup app
    • ping app
    • mount
      • 可以看到 /etc/resolv.conf
      • more /etc/resolv.conf
        • nameserver 127.0.0.11 (这个在后面会用到) *
    • more /etc/resolv.conf
  • d-c scale

    • d-c scale app=4
    • docker ps
    • 当用 scale 将 容器数量从高降低时,则会有错误出现,此时即需要修改 proxy.conf
    • 修改好之后,就可以将 request 自动均衡地分配到各个容器里了 *

    在 docker-compose 的 version 2 中 并不需要 使用 links 来关联,因为 已经有 services 存疑

upstream unicorn { server unix:/tmp/unicorn.phindee.sock fail_timeout=0;}server { server_name www.phindee.com; return 301 $scheme://phindee.com$request_uri;}server { listen 80 default deferred; server_name phindee.com; root /var/www/phindee/current/public; location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; } try_files $uri/index.html $uri @unicorn; location @unicorn { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://unicorn; } error_page 500 502 503 504 /500.html; keepalive_timeout 10;}

Nginx 是不会处理由 Rails 产生的动态请求,我们要把这些动态的请求交给 Unicorn。

Uncaught TypeError: Cannot read property 'offsetWidth' of undefined

添加 seeds.rb

是将所需图片存到本地 host 的 public/images 下面,提示 no such file

Errno::ENOENT: No such file or directory @ rb_sysopen - /var/www/myapp/public/images/banners/slide1.jpg

查看容器的 mount 信息 docker inspect container_name 发现有一个 mount 是 命名的 volume

rake aborted!

ActiveRecord::RecordInvalid: Validation failed: Cover translation missing: en.errors.messages.mini_magick_processing_error

注意最后的 mini_magick_processing_error ,应该是没有安装 mini_magick 的原因。

修改 Dockerfile 添加一句:RUN apt-get install -y imagemagick

docker-compose scale port is already allocated

应用 docker-compose exec scale web=4 app=4 ,提示此错误

ArgumentError: Already running on PID:1 (or pid=/var/www/eshop/tmp/pids/unicorn.pid is stale)

表象为: 运行 docker-compose upapp 先启动,马上就退出,并提示 eshopdd_app_1 exited with code 1

vi log/nginx.error.log

17 2017/03/06 03:05:09 [error] 5#5: *2 connect() failed (113: No route to host) while connecting to upstream, client: 192.168.94.193, server: we.nb2j.com, request: "GET / HTTP/1.1", upstream: "http://172.22.0.4:3000/", host: "we.nb2j.com"

vi log/unicorn.stderr.log

ArgumentError: Already running on PID:1 (or pid=/var/www/eshop/tmp/pids/unicorn.pid is stale)

ps aux | grep 'unicorn'

root 19108 0.3 2.4 267468 94140 ? Ssl 11:22 0:03 unicorn master -c config/containers/unicorn.rb -E production root 19353 0.0 2.2 267468 88728 ? Sl 11:36 0:00 unicorn worker[0] -c config/containers/unicorn.rb -E production

kill -9 19108 19353

docker-compose up

ERROR: for app Cannot start service app: rpc error: code = 2 desc = "oci runtime error: container with id exists: c53ad59009ded923a82eba0957b4247f17845e06a6470e44df7b946119e30229" ERROR: Encountered errors while bringing up the project.

最后 重新执行 : dc scale app=1 把已经生成的相关的容器全部 删除,而后再重新 启动,就OK 了

共收到 14 条回复

小标题反映文字组织能力。。。

martin91 回复

哈,谢谢你的指出。抱歉,那后面部分本来就没有排版的,只是作为一个附件。做了一下分隔。

请问多对部署部分有什么差错,或可以优化提点意见?

既然用了docker,就不应该区分开发模式和生产模式。

nouse 回复

多谢回复,能否详细说一下,对这一块没有什么概念。

用了docker 肯定会分开发模式和生产模式的, 只是去用namesapce 去划分就好了,个人觉得

Dockerfile 里安装 PG?

huacnlee 将本帖设为了精华贴 03月30日 16:17
10楼 已删除

说到部署,我用过Git Hook,Capistrano和Docker来部署Rails应用。最近确实产生了一个疑问:如果我不拆微服务,那么用Docker部署Rails的优势和成本究竟有哪些?

a0nqm 回复

首先必须要承认容器化是大势所趋,其次容器化并不是Docker,lxc和rkt也是容器化。

容器化的目标是开发环境和生产环境的一致性,从开发到测试再到部署可以很容易的保持一致。至于自动重启,auto scale等等高级玩法则是容器化基础之上实现的服务,并不是容器化技术本身具备的特性。

nouse 回复

继续请教:保证开发环境和生产环境的一致性,解决的又是什么问题呢?

我的理解,这样做,通过保证开发与生产环境库、变量一致的手段,既方便了测试与调试,也降低了代码部署失败的风险,从而降低了运维成本,并为高级自动化的手段提供了更多可能。

你的包打出来多大啊,你打入了太多不必要的东西了,编译依赖在编译完是需要移除的,而且得在同一层中

@IChou 能麻烦说详细一些吗?移除哪些,如何移除?谢谢

请教两个问题:

1 nginx 或者 db 用docker 来跑的意义是怎么样的?

2 nginx放在容器中时,如果要重新构建nginx容器,怎么实现服务不中断?

PG::DatatypeMismatch: ERROR: column "is_slide" cannot be cast automatically to type boolean HINT: You might need to specify "USING is_slide::boolean". : ALTER TABLE "banners" ALTER COLUMN "is_slide" TYPE boolean

  1. 感觉app的dockerfile里第49行 COPY . .这句是多此一举,因为已经在docker-compose.yml里映射了源文件位置和工作目录,多了这一句,在源文件修改后,build就会从此处开始重新生成镜像层。
  2. 感觉既然在docker-compose用了env,就不用引入 dotenv-rails了。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册