基于Docker的Rails集群+Ruby负载均衡代理

云计算
我打算做一个比较酷的使用Docker的例子:一个Rails集群,以及集群前面的一个Ruby负载均衡服务。我曾想过使用若干不同的机器(每台机器都有自己的IP地址和端口)来运行同样的Rails服务,然后在外部配置一台负载均衡的代理服务器,在各Railf服务之间进行请求分配。

我打算做一个比较酷的使用Docker的例子:一个Rails集群,以及集群前面的一个Ruby负载均衡服务。我曾想过使用若干不同的机器(每台机器都有自己的IP地址和端口)来运行同样的Rails服务,然后在外部配置一台负载均衡的代理服务器,在各Railf服务之间进行请求分配。

不过使用Docker的话,在一台机器上就可以模拟集群环境了。

alt

这里我们将演示一下如何来创建这个集群环境。

你可以从我的Github项目上下载所有这次测试的源代码。

 

 

创建Docker镜像

 

 

首先我将创建两个Docker镜像,1个用来运行Rails服务,1个运行Ruby代理服务。

 

1.首先我制作了一个可信Docker构建(Trusted Docker build),构建脚本也在我的Github上( Github project docker-ruby )。使用这个构建脚本创建的镜像为 murielsalvan/ruby ,它基于Ubuntu Precise,并且安装了Ruby 2.1.0p0。

 

  1. docker pull murielsalvan/ruby 

1.接着我基于murielsalvan/ruby来创建一个Docker容器,用来执行一个bash shell,然后在里面安装Rails并创建一个Rails应用程序。这个应用程序只有一页,它将打印出它所在服务器的主机名和IP地址(集群里的每台Rails服务都有不同的主机名和IP地址)。你可以从这里查看这个测试程序的源代码。这步的***,我将把这个容器提交,生成一个新的镜像,并命名为murielsalvan/server,这个镜像执行的时候,会启动Rails服务,并监听3000端口号(容器内的端口号)。

  1. docker run -t -i murielsalvan/ruby bash 
  2. docker commit -m=”Test server” -author=”Muriel Salvan <muriel@x-aeon.com>” -run='{“WorkingDir”: “/root/server/”, “Cmd”: ["rails""s"], “PortSpecs”: ["3000"]}’ acf566f7d155 murielsalvan/server 

1.***我们来基于murielsalvan/ruby创建第二个容器。这个容器也是启动一个bash然后安装一个Ruby代理服务(我用了 em-proxy,非常不错的东西,值得一试)。这个Ruby代理服务将接收一个地址列表作为输入参数,并且创建一个基于随机策略的负载均衡服务,轮询所有给定的IP地址以及3000端口。这个代理服务的源代码可以在 这里 查看。***我把这个容器也做了提交操作,生成了一个新的镜像 murielsalvan/proxy,它也将监听3000端口。

  1. docker run -t -i murielsalvan/ruby bash 
  2.  
  3. docker commit -m=”Proxy server” -author=”Muriel Salvan <muriel@x-aeon.com>” -run='{“PortSpecs”: ["3000"]}’ 7d2431c16b14 murielsalvan/proxy 

运行Rails cluster

在所有的镜像都创建完成之后,我们就可以启动容器了。我写了个小脚本来启动所有需要运行Rails服务的容器,它接收一个N参数,为Rails服务器个数。等Rails服务全部启动完成后,这个脚本会打印出这些服务的IP地址列表。

这个脚本的另一个工作就是将运行Rails服务的容器内的3000端口,绑定到本机的5000+i 端口上,这样就可以非常方便的透过代理服务直接通过 wget -S -O – http://localhost:5000 命令来确认每台Rails服务是否运行正常。

 

 

 

  1. # run_cluster.rb 
  2.  
  3. np_servers = ARGV[0].to_i 
  4.   
  5. pipes_in = {} 
  6. np_servers.times do |idx| 
  7.   port = 5000 + idx 
  8.   pipe_cmd_in, pipe_cmd_out = IO.pipe 
  9.   cmd_pid = Process.spawn("docker run -p #{port}:3000 murielsalvan/server", :out => pipe_cmd_out, :err => pipe_cmd_out) 
  10.   puts "Launch server on port #{port}: PID=#{cmd_pid}" 
  11.   Process.detach(cmd_pid) 
  12.   pipe_cmd_out.close 
  13.   pipes_in[cmd_pid] = pipe_cmd_in 
  14. end 
  15. # Wait for all servers to be up 
  16. pipes_in.each do |pid, pipe_in| 
  17.   puts "Waiting for PID #{pid} to be listening..." 
  18.   found_info = false 
  19.   while !found_info 
  20.     out = pipe_in.readline.chomp 
  21.     puts out 
  22.     found_info = out.match(/WEpick::HTTPServer/) != nil 
  23.     sleep 0.01 if !found_info 
  24.   end 
  25. end 
  26.   
  27. puts 'All servers up and running.' 
  28.   
  29. # Get their IP addresses 
  30. ips = [] 
  31. `docker ps | sed -e 's/^\\(............\\).*$/\\1/' | tail -#{np_servers}`.split("\n").each do |container_id| 
  32.   ips << `docker inspect #{container_id} | grep IPAddress | sed -e 's/.*: \\"\\(.*\\)\\".*/\\1/g'`.chomp 
  33. end 
  34.   
  35. puts ips.join(' '

这段代码执行后输出结果如下所示,请注意***的那IP地址,这些地址是Rails服务所监听的IP地址,我们在后面的Ruby代理服务器中会使用到这些地址。

  1. > ruby -w run_cluster.rb 5 
  2.  
  3. Launch server on port 5000: PID=6559 
  4. Launch server on port 5001: PID=6561 
  5. Launch server on port 5002: PID=6565 
  6. Launch server on port 5003: PID=6571 
  7. Launch server on port 5004: PID=6573 
  8. Waiting for PID 6559 to be listening... 
  9. [2014-02-05 18:19:44] INFO  WEpick 1.3.1 
  10. [2014-02-05 18:19:44] INFO  ruby 2.1.0 (2013-12-25) [x86_64-linux] 
  11. [2014-02-05 18:19:44] INFO  WEpick::HTTPServer#start: pid=1 port=3000 
  12. Waiting for PID 6561 to be listening... 
  13. [2014-02-05 18:19:42] INFO  WEpick 1.3.1 
  14. [2014-02-05 18:19:42] INFO  ruby 2.1.0 (2013-12-25) [x86_64-linux] 
  15. [2014-02-05 18:19:42] INFO  WEpick::HTTPServer#start: pid=1 port=3000 
  16. Waiting for PID 6565 to be listening... 
  17. [2014-02-05 18:19:44] INFO  WEpick 1.3.1 
  18. [2014-02-05 18:19:44] INFO  ruby 2.1.0 (2013-12-25) [x86_64-linux] 
  19. [2014-02-05 18:19:44] INFO  WEpick::HTTPServer#start: pid=1 port=3000 
  20. Waiting for PID 6571 to be listening... 
  21. [2014-02-05 18:19:43] INFO  WEpick 1.3.1 
  22. [2014-02-05 18:19:43] INFO  ruby 2.1.0 (2013-12-25) [x86_64-linux] 
  23. [2014-02-05 18:19:43] INFO  WEpick::HTTPServer#start: pid=1 port=3000 
  24. Waiting for PID 6573 to be listening... 
  25. [2014-02-05 18:19:41] INFO  WEpick 1.3.1 
  26. [2014-02-05 18:19:41] INFO  ruby 2.1.0 (2013-12-25) [x86_64-linux] 
  27. [2014-02-05 18:19:41] INFO  WEpick::HTTPServer#start: pid=1 port=3000 
  28. All servers up and running. 
  29. 172.17.0.16 172.17.0.14 172.17.0.15 172.17.0.13 172.17.0.12 

启动Ruby代理

同样,我们在这里也通过一段Ruby代码来完成启动代理服务器的工作:

  1. 同样,我们在这里也通过一段Ruby代码来完成启动代理服务器的工作: 
  2. #run_proxy.rb 
  3. lst_ips = ARGV.cloneProcess.wait(Process.spawn("docker run -p 3000:3000 -t murielsalvan/proxy ruby -w /root/run_proxy.rb #{lst_ips.join(' ')}")) 

这段代码将启动运行Ruby代理服务的Docker容器,其执行结果如下:

 

 

 

  1. > ruby -w run_proxy.rb 172.17.0.16 172.17.0.14 172.17.0.15 172.17.0.13 172.17.0.12 
  2.  
  3. /root/run_proxy.rb:149: warning: `&' interpreted as argument prefix 
  4. /root/run_proxy.rb:150: warning: `&' interpreted as argument prefix 
  5. /root/run_proxy.rb:151: warning: `&' interpreted as argument prefix 
  6. /root/run_proxy.rb:152: warning: `&' interpreted as argument prefix 
  7. /usr/local/lib/ruby/gems/2.1.0/gems/em-proxy-0.1.8/lib/em-proxy/backend.rb:37: warning: method redefined; discarding old debug 
  8. /usr/local/lib/ruby/gems/2.1.0/gems/em-proxy-0.1.8/lib/em-proxy/connection.rb:126: warning: method redefined; discarding old debug 
  9. /root/run_proxy.rb:168: warning: method redefined; discarding old stop 
  10. /usr/local/lib/ruby/gems/2.1.0/gems/em-proxy-0.1.8/lib/em-proxy/proxy.rb:17: warning: previous definition of stop was here 
  11. Launching proxy at 0.0.0.0:3000... 

这将启动Ruby代理服务程序,并且监听3000端口。(请先忽略 上面的警告信息吧,也许em-proxy需要做一些清理操作吧 :-))

 

打开浏览器

 

服务程序都启动之后,就可以打开浏览器访问了。输入网址 http://localhost:3000,可以确认返回结果里的主机名和IP地址。为了确保每次请求都会发给代理服务来处理,要在每次请求这个网址的时候先清空一下缓存(强制刷新)。

下面的图是两次请求的结果,从中我们可以看出,两次请求是分别由两台不同的Rails服务器返回的。

 

 

alt

 

刷新浏览器之后:

alt

怎样,是不是很简单的一种Rails集群方案?你也可以在自己的机器上尝试一下。

我个人的测试环境如下:64位的Windows 7 主机,然后通过VirtualBox运行了Ubuntu 14.04 Alpha,所有的操作都在虚拟机里进行。性能也还算说的过去(每个请求的响应时间都低于1秒)。

原文出自:https://docker.cn/p/rails-cluster-with-ruby-load-balancer-using-docker-zh

责任编辑:Ophira 来源: Docker中文社区
相关推荐

2015-07-29 13:21:58

DockerRails 集群高可用架构

2023-01-10 08:37:45

Docker开发架构

2018-10-14 08:39:52

NginxTomcat服务器

2010-05-06 15:00:58

集群负载均衡

2009-08-27 10:21:22

Ruby on Rai

2017-12-18 12:04:02

Nginx代理均衡

2017-11-14 10:59:41

LVS负载均衡集群

2010-08-13 08:59:58

Rails

2019-06-19 15:34:39

Nginx反向代理负载均衡

2012-05-07 10:20:12

LVS集群

2009-08-06 09:13:36

Ruby on Rai

2010-05-10 14:17:46

负载均衡技术

2010-05-05 18:21:18

集群负载均衡

2010-05-06 09:34:36

负载均衡集群

2010-04-28 11:35:25

集群负载均衡

2010-09-25 14:39:29

Bruce Tate

2019-11-04 15:35:53

Nginx反向代理负载均衡

2018-01-17 09:57:41

2019-09-18 10:39:08

负载均衡反向代理TCP

2009-10-23 19:11:32

linux集群
点赞
收藏

51CTO技术栈公众号