部署 生产环境使用 Docker 部署 Rails 应用 Puma 和 Sidekiq

embbnux · 发布于 2016年05月23日 · 最后由 uestc_bird 回复于 2017年02月06日 · 2693 次阅读
15999

有幸拿到docker beta的测试资格, 在Mac OSX下使用docker更加方便好玩了。这篇博文介绍如何在生产环境也就是线上利用docker实现快速部署以及横向扩展,为大规模负载均衡做准备。这里使用一个docker容器来跑rails应用,另一个容器来跑异步队列sidekiq等服务, 数据库和redis使用RDS和云redis,直接使用docker镜像的数据库也可以。

本文原文出处: 生产环境使用docker部署rails应用puma和sidekiq

一、生产环境使用docker前准备

首先你的web应用要足够干净,rails也好,nodejs也一样,不依赖于本地的任何东西,应该是一个docker镜像pull下来,加上一些环境变量等配置就能直接跑起来。

  1. 数据库url可配置,可以连接远程数据库,或者连接其他数据库容器
  2. redis也应该是远程连接可配置, redis独立出去,异步队列像sidekiq的也可以在单独容器里跑了,因为一个docker容器只支持一直进程跑,所以server和队列是分开的,通过redis通讯。
  3. 只处理动态流量,静态资源请走CDN, 图片的上传也不是储存在本地磁盘的, 图片的上传可以上传到容器再由容器传到云存储服务器,或者直接由客户端上传到云存储服务器,数据库里只存图片的地址就可以。

二、配置生产环境Dockerfile

首先讲一些我的工程目录结构,主要就是rails的结构,这里只列出关键的文件目录结构:

|__ app
|__ config
|__ |__ puma_docker.rb
|__ |__ database.yml
|__ |__ redis.yml
|__ |__ sidekiq.yml
|__ public
|
|__ Dockerfile
|__ Gemfile
|__ Gemfile.lock

Dockerfile原则应该是只添加有需要的:

##########################################
# Dockerfile for rails app with puma and sidekiq postgres
# Author: Embbnux Ji
# HomePage: www.embbnux.com
##########################################

FROM ruby:2.3.1

MAINTAINER Embbnux embbnux@embbnux.com
RUN apt-get update && apt-get install -y build-essential libssl-dev libpq-dev libxml2-dev libxslt1-dev nodejs git imagemagick libbz2-dev libjpeg-dev libevent-dev libmagickcore-dev libffi-dev libglib2.0-dev zlib1g-dev libyaml-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*

ENV APP_HOME /app

RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME

COPY Gemfile $APP_HOME/
COPY Gemfile.lock $APP_HOME/

RUN bundle install

COPY . $APP_HOME

RUN bundle exec rake assets:precompile RAILS_ENV=production

EXPOSE 8080
CMD ["bundle", "exec", "puma", "-C", "config/puma_docker.rb"]

三、配置rails工程

我的rails是使用puma来作为web服务器的,docker自然也一样,所以app容器默认是执行puma启动server的命令,对外输出接口为8080, 使用nginx代理流量到这个服务端口即可。 puma这里需要配置为暴露8080端口:

# puma_docker.rb:

threads 4, 16
workers 1
environment 'production'
bind 'tcp://0.0.0.0:8080'
preload_app!

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

数据库配置:

# database.yml:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5
  timeout: 5000

production:
  <<: *default
  pool: 10
  database: <%= ENV["DATABASE"] %>
  host: <%= ENV['DATABASE_HOST'] %>
  username: <%= ENV["DATAUSER"] %>
  password: <%= ENV["DATAPASSWD"] %>

redis的配置也一样,redis的地址用环境变量代替: ENV["REDIS_URL"]

四、使用远程仓库自动构建

我这边采取的远程仓库方案是Github加Docker Hub,实现代码更新自动构建镜像,方法很简单,就是使用docker hub的自动构建功能,关联github仓库即可。需要在工程根目录下有一个Dockerfile. 这样git push代码后过几分钟镜像就会被自动构建完成。 也可以使用docker hub的webhook功能实现构建完成自动部署,这个我暂时没测试。

五、部署docker镜像到生产环境

docker镜像的部署很简单,直接pull下来跑就可以了。这里为了演示, 数据库和redis也用一个单独的docker容器来跑,模拟远程连接,云储存用docker的volume功能实现,具体如下:

# 下载redis镜像
docker pull redis
# 下载postgres镜像
docker pull postgres
# 下载已经自动构建完成的app镜像
docker pull embbnux/app
# 后台运行redis容器
docker run --name app_redis -d redis
# 后台运行postgres容器, 指定用户名密码
docker run --name app_postgres -e POSTGRES_PASSWORD=password -e POSTGRES_USER=user -e POSTGRES_DB=app_db -d postgres
# 后台运行app容器, 环境变量使用.env.docker文件传入, 映射容器的8080端口到本地的8080端口
docker run --env-file ./.env.docker --link app_redis:redis --link app_postgres:postgres -v /var/www/public/uploads:/app/public/public -v /var/log/app:/app/log --name app_web -p 127.0.0.1:8080:8080 -d embbnux/app
# 上传assets文件到cdn
# docker run --env-file ./.env.docker --link app_redis:redis --link app_postgres:postgres --name app_assets --rm embbnux/app rake cdn:upload_assets
# 运行sidekiq容器
docker run --env-file ./.env.docker --link app_redis:redis --link app_postgres:postgres -v /var/www/public/uploads:/app/public/public -v /var/log/app:/app/log --name app_sidekiq -d embbnux/app bundle exec sidekiq -C config/sidekiq.yml

环境变量文件如下:

# .env.docker
RAILS_ENV=production
SECRET_KEY_BASE=1237293729347238719422b4e25fe42a311bc4e5ffb242397934cbad3adabfbcfae4b431a5029ad6486bce777382470327493287402
DATABASE_HOST=app_postgres
DATABASE=app_db
DATAUSER=user
DATAPASSWD=password
REDIS_URL=redis://app_redis:6379

以后升级代码,只需要把app pull下来跑就可以了, 多机器部署建议用capistrano等工具。 在开发环境使用docker快速构建rails开发环境可以看之前的博客。

本文原文出处,Embbnux博客,生产环境使用docker部署rails应用puma和sidekiq.欢迎转载,转载请注明原文出处,并保留原文链接

共收到 14 条回复
26677

正好用到了。。。学习了

13229

赞,按LZ教程操作了一遍确实不错。如果是国内下载docker镜像慢的话可以跑下面的命令设置:

pinata get daemon | jq -cm '."registry-mirrors" = ["https://XXXXX.mirror.aliyuncs.com"]' | pinata set daemon -

镜像加速可以用阿里云的 (https://dev.aliyun.com/search.html),也可以用网易。

27

uploads 和 logs 目录挂载,我觉得可以写进 Dockerfile 里

11562

学习了. 支持. 我之前研究了自发现服务. 然后感觉用起来就省很多事情了.

23412

有配合jenkins使用的教程吗?

26123

谢谢楼主分享,学到了新知识

24405
# 后台运行redis容器
docker run --name app_redis -d redis
# 后台运行postgres容器, 指定用户名密码
docker run --name app_postgres -e POSTGRES_PASSWORD=password -e POSTGRES_USER=user -e POSTGRES_DB=app_db -d postgres
# 后台运行app容器, 环境变量使用.env.docker文件传入, 映射容器的8080端口到本地的8080端口
docker run --env-file ./.env.docker --link app_redis:redis --link app_postgres:postgres -v /var/www/public/uploads:/app/public/public -v /var/log/app:/app/log --name app_web -p 127.0.0.1:8080:8080 -d embbnux/app
# 上传assets文件到cdn
# docker run --env-file ./.env.docker --link app_redis:redis --link app_postgres:postgres --name app_assets --rm embbnux/app rake cdn:upload_assets
# 运行sidekiq容器
docker run --env-file ./.env.docker --link app_redis:redis --link app_postgres:postgres -v /var/www/public/uploads:/app/public/public -v /var/log/app:/app/log --name app_sidekiq -d embbnux/app bundle exec sidekiq -C config/sidekiq.yml

这些docker run ...的操作可以组织到docker-compose.yml配置文件里,通过Docker Compose来管理各个容器的衔接和配置,会不会显得更方便一点?

24405

另外,因为Rails容器正常运行,需要连接DB容器并执行Migrate操作。

这里是不是应该在所有容器启动之后,再配合GitLab-CI这类持续部署工具做接下来的步骤?

因为运用了Docker完全封装了环境和源码,很多观念跟Cap/Mina工具不一致,是不是应该更换成GitLab-CI或Jenkins了?

15999

#7楼 @pinewong 我记得现在生产环境还不推荐用docker-compose。 是要配合 CI 的, 从自动构建 到 自动测试 到 自动部署,这里说 cap 只是为了实现多机同时部署,不用也可以,jenkins 上调用 cap 去部署,或者直接用一些第三方提供的 docker 云管理也可以

24405

#9楼 @embbnux 最近在弄部署,Docker Compose是有挺多不如意的地方,不过暂时没有找到更好组织这些镜像容器的方案(似乎可以用makefile,但手动管理命令成本也高,而且以后不好跟着官方升级),所以先用着吧。

用Cap多机部署是挺方便的,适用场景的工具用着也行吧。

18832

学习啦

24405 pinewong 运维环境简单化 - 容器封装 中提及了此贴 11月19日 18:57
96

按照这种部署rails的方法,每次都要把rails的代码拷贝到docker镜像里面,感觉效率很低。 是不是可以把rails的代码作为卷加载到docker里面,每次只去启动一个app server就可以了? 而且这种启动rails的方法,导致dockerfile每次都要在copy命令之后重新构建,不能利用镜像缓存,相当于每次想部署一次应用都要去构建一次docker镜像,这明显不合理,一个rails应用在上线后,一般其依赖的环境是比较稳定的。 说了这么多,我想说的是:用docker来部署rails应用实质上就只需要部署rails的app server即可

15999

#13楼 @uestc_bird 并不同意你的观点,首先 rails 一般是敏捷迭代开发,依赖的环境比如 gem 包,以及前端文件,在每次迭代后都会有更新,每次更新后把这些 build 在镜像里可以保证环境的统一,而且构建也是有缓存的,比如除非 Gemfile 变化,不然不会重新构建这一步的。这里的镜像都是无状态的,每次启动一个 container 也就是一个 app server

96

#14楼 @embbnux 1. 前端资源和后端server的分离我个人觉得是非常有利于加快开发的速度的(当然这个目前争议也比较大),rails5的api化也说明了这一点;

  1. 如果把rails的代码和静态资源放入docker中也会导致生成的image过大;
  2. 每次代码变更,哪怕是改了一行代码都要rebuild image,这个很明显不爽;
  3. docker build时,在首次遇到COPY指令之后就不再使用之前构建生成的中间镜像的缓存了;
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册