第一节介绍了gitolite mirror,使得代码能够被传送到服务器组的内部网络中,但是接下来如何将代码再部署到上千台服务器上,而且还能够实现代码回滚呢?
capistrano使用ruby语言开发,所以懂得ruby就可以熟练使用capistrano了。鉴于capistrano3.x需要使用ruby1.9以上的ruby版本,所以我还是选择了2.x。
我使用的是CentOS6.4,自带"ruby-1.8.7.352-7.el6_2.x86_64":
# yum install ruby # yum intall rubygems # gem install capistrano -v 2.15.5 # gem install capistrano-ext
capistrano2.x与3.x版本有很大的不同,所以2.x上面的命令不能直接在3.x上面直接运行,比如工作目录的初始化就不同:
# mkdir /tmp/test3 # cap install # mkdir /tmp/test2 # cd /tmp/test2 # capify . # tree 这里是初始化后的工作目录,deploy.rb是主配置文件。 . ├── Capfile └── config └── deploy.rb
管理端capistrano执行后,会给app服务器带来什么改变呢?
首先会在目标目录中创建release目录,主要用来存放代码,而且所有的代码均以不同的版本号命名。另外还会创建share目录。最后创建的是current软连接,current用来指向release目录中最新的代码版本号。
# tree . ├── current -> /data/capistrano/pandaarcher/releases/20150107020401 ├── releases │ └── 20150107020401 │ ├── a │ ├── config.json │ ├── db.js │ ├── def.js │ ├── Deploy.txt │ ├── log -> /data/capistrano/pandaarcher/shared/log │ ├── log.js │ ├── panda_score.js │ ├── public │ │ └── system -> /data/capistrano/pandaarcher/shared/system │ ├── REVISION │ ├── tmp │ │ └── pids -> /data/capistrano/pandaarcher/shared/pids │ └── worker.js └── shared ├── log ├── pids └── system 12 directories, 9 files
capistrano的回滚功能:
当我们执行rollback以后,就会将release目录中最近一次部署的代码版本删除,同时将current重新连接到上次的版本号目录。最后还会帮助我们restart服务。所以我们要使用rollback功能,你的配置文件中必须有restart功能,否则会报错奥!
capistrano的工作原理很简单,与ansible一样都是基于SSH从capistrano管理主机传递命令到app服务器。所以执行账户必须能够使用app服务器上面已有的账号(其shell不能是/sbin/nologin)执行命令。
在这里明确下我们最终的需求:将程序猿提交到公司内部git服务器的代码通过gitolite mirror主动传输到线上服务器所在的内网git服务器,然后运维人员搭建对应项目的capistrano工作目录,对其做好配置,确保其能够正常工作。然后在Hudson上面建立此项目,添加此项目的程序猿的工作账号并授权。当代码需要更新时,程序猿自己就可以通过网页界面直接更新此项目,而无需去再和运维协调。
在这里再简单的描述下Hudson,我们需要的只是Hudson可以执行shell命令的功能,它在执行后不仅可以返回执行结果,而且还可以返回详细执行过程。所以,我们让Hudson去调用capistrano中对应的项目即可,其中需要注意的是权限问题:
(1)管理机Hudson用户必须有足够的权限去进入capistrano工作目录并执行命令,所以我会直接将此工作目录的属主、属组定为Hudson用户。
(2)管理机Hudson账号必须能够通过ssh连接到app服务器指定的账号去启动,比如在app服务器建立capistrano账号,然后将管理主机的Hudson账号的公钥添加到app服务器的capistrano账号的authorized_keys中建立单向密钥认证,当然你也可以每次执行输入密码。
(3)当将代码推送到app后,所有的文件的属主、属组必然是capistrano用户的,作为web主机,一般向外提供服务时为了安全起见,是不能够让启动者有登录权限的。所以capistrano用户不能作为web服务器的启动者。所以需要赋予capistrano用户可以执行/sbin/runuser的sudo权限,使得capistrano可以调用www(web启动者)启动服务。
(4)但是权限问题依然存在,由于代码的属主、数组都属于capistrano,所以www能不能进入代码目录并执行代码依然是个问题。同时由于需要对代码可以进行回滚,capistrano用户也必须同时具有代码的RWX权限。所以sgid和setfacl功能是解决此问题的好办法。
(5)考虑到app会产生日志,所以我们还需要额外建立log目录,并设定其属主、属组为www。
回到capistrano,默认情况下,它会一次性并发向所有的服务器发送对应的指令,此过程是阻塞的,也就是我们会一直等待capistrano给我们返回结果。对于update代码,对线上的业务是没有影响的,但是对于restart操作,那么如果你指定了LVS后面的所有的app服务器,那么此app在此期间是完全不可用的。我的处理方法是写多个stage文件,然后分别执行,或者在restart代码中加上一些很丑的代码。
具体环境和操作方法如下:
10.0.5.90:公司内部git服务器,本机root管理gitolite-admin
10.0.5.91:线上gitolite mirror服务器,本机root管理gitolite-admin;同时也是capistrano+Hudson服务器
10.0.6.153:线上app服务器
我们希望将代码部署到app服务器的/data/capistrano/car/目录中,然后实际的对外发布的目录是/data/html/。而且这里启动服务是以capistrano用户启动的,暂时不使用上面提到的www用户。
at 10.0.6.153: # useradd capistrano # mkdir /data/capistrano/car # mkdir /data/car # mkdir /data/logs/car # chown capistrano.capistrano /data/capistrano/car # chown capistrano.capistrano /data/html/ # chown capistrano.capistrano /data/logs/car # sudo su - capistrano # ssh-keygen -b 2048 # scp /home/capistrano/.ssh/id_rsa [email protected]:/root/10.0.6.153.capistrano.pub 赋予capistrano用户访问git仓库的权限
at 10.0.5.91: # rpm -ivh hudson # usermod -s /bin/bash hudson # sudo su - hudson # ssh-keygen -b 2048 # scp /var/lib/hudson/.ssh/id_rsa.pub [email protected]:/root/10.0.5.91.hudson.pub hudson用户在调用capistrano时也必须具有访问git仓库的权限 # exit # cd /root/gitolite-admin # mv /root/{10.0.5.91.hudson.pub,10.0.6.153.capistrano.pub} key/ # vim conf/gitolite.conf repo gitolite-admin RW+ = admin repo car RW+ = server-zhu R = 10.0.6.153.capistrano R = 10.0.5.91.hudson option mirror.master = zhu option mirror.slaves = cong
所以capistrano项目部署如下:
set :application, "car" app服务器代码部署目录 set :repository, "[email protected]:car" 指定git服务器, role :app, "10.0.6.153","10.0.6.155" 指定app服务器的ip 这里可以指定多个role set :keep_releases, 30 指定保存多少个版本,默认是5个 set :scm, :git 这里可以使用git、svn等。甚至可以不使用,也就是从file调用 set :deploy_to, "/data/capistrano/#{application}" 部署到我们指定的位置 set :user, "capistrano" 指定在app服务器上的用户 set :runner, "capistrano" 指定运行用户 default_run_options[:pty] = true 当我们下面需要sudo时,就必须使用一个pty。 set :use_sudo, false 这个与执行命令中的sudo无关。 加上后,app主机中的capistrano执行的操作都是以sudo权限运行的。 set :normalize_asset_timestamps, false namespace :deploy do task :default, :roles => :app do 这里":roles => :app",可以省略,但加上以后使得配置更加直观。 update link stop start end task :link, :roles => :app do run "ln -nfs #{deploy_to}/current/ /data/html/car" 强制建立/data/capistrano/car/current到/data/car的软连接。 另外由于软连接的权限是777,所以如果你是第一次部署,其属主为root, capistrano也可以强制奥 另外,由于ruby会自动的在#{deploy_to}/current/下面生成一些没用的东西, 所以如果想要/data/html/car比较干净的话,可以在程序猿一开始提交时, 就提交一整个的目录,而不是散列的东西到git仓库。 然后做软连接时,可以是ln -nfs #{deploy_to}/current/realcode /data/html/car end task :start, :roles => :app do run "cd /data/html/#{applicate} && (nohup node car.js >/dev/null 2>&1 &)" 这里是设定启动car服务的命令 如果car服务是启动在1024以下的端口,那么必须以root用户启动了,stop亦然 end task :stop, :roles => :app do run "ps aux | grep car | grep -vw grep | awk '{print $2}' | xargs sudo /bin/kill && sleep 3" end task :restart, :roles => :app do find_servers_for_task(current_task).each do |server| run "ps aux | grep car | grep -vw grep | awk '{print $2}' | xargs sudo /bin/kill && sleep 3 && cd /data/html/#{application} && (nohup node car.js >/dev/null 2>&1 &)",:hosts => server.host end 以上命令只是上面的stop,start重新写了一遍, 如果不需要逐个主机的重启,那么我们可以直接写stop回车后start end end
当你的源不是git,而是本地的文件,可以这么写:
set :repository, "127.0.0.1:/source/" set :deploy_via, :copy
当你需要处理git分支时:
set :branch, 'staging'
stage的多段处理:
(1)当你一个项目中又包含多个子项目,所有子项目需要分别的管理,那么就需要stage了。
(2)或者你不希望使用上面restart中那样格式的追个重启主机,那么你也可以使用stage将各个主机分别写入一个文件中。方法如下:
(1)修改deploy.rb文件,删除namespace以后的内容和role行,并添加如下内容: require "capistrano/ext/multistage" set :stages, %w(app1 app2) set :default_stage, "app1" 制定默认,可以省略 (2)在config目录下创建deploy目录,并在在其下面创建app1.rb、app2.rb文件。 在app1.rb文件中添加namespace内容,并且添加对应的role。 执行方法:cap app1 deploy:start
cap命令:
(1)cap deploy:setup,初始化,创建release和share目录
(2)cap deploy:update,上传代码目录到release并命名为日期格式的数字名称目录名,同时创建其软连接到#{delploy_to}/current
(3)cap deploy:start,启动服务
(4)cap deploy:stop,关闭服务
(5)cap deploy:rollback,回滚
(6)cap deploy:check,检查
最后说Hudson,Hudson功能很强大,但是我们只利用其shell功能。同样,Hudson是安装在管理主机10.0.5.91上面的。
# rpm -ivh hudson-3.2.1-1.1.noarch.rpm
然后使用浏览器访问:http://10.0.5.91:8080
(1)登录后,首先会让我们在线安装Hudson组件,这里只选核心组件。安装过程十分漫长。
(2)用户授权:
首先需要注册一个用户,这第一个用户其实就是具有supper权限,然后"系统管理"→"Configure Security”→“Hudson专有用户数据库”→"项目矩阵授权策略"
上面的user1、user2也是先注册后授权的奥!
(3)对项目car进行用户权限管理
这样,管理员新建的这个项目car,在只授予user1权限时,其他用户是看不到此项目的
而且,此项目只能被执行,不能被修改
(4)最后是如何让Hudson执行命令:
(5)让程序猿进入此项目,直接点击构建就可以更新代码了。