capistrano+hudson

第一節介紹了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專有用戶數據庫”→"項目矩陣授權策略"


wKioL1Ss3ibTPAGTAAH3fsNms1g604.jpg

 上面的user1、user2也是先註冊後授權的奧!


(3)對項目car進行用戶權限管理

wKioL1Ss3P6yD6I3AACTdCm1dQA501.jpg

    這樣,管理員新建的這個項目car,在只授予user1權限時,其他用戶是看不到此項目的

    而且,此項目只能被執行,不能被修改

wKiom1Ss3M6TN3epAABxbWD7EP4964.jpg



(4)最後是如何讓Hudson執行命令:

wKiom1Ss3vaAFnn9AALZmm0PVpk035.jpg

(5)讓程序猿進入此項目,直接點擊構建就可以更新代碼了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章