1. 程式人生 > >Jenkins+Ansible+Gitlab自動化部署三劍客

Jenkins+Ansible+Gitlab自動化部署三劍客

DDU 賬號密碼 rep 原因 ems select conf 當前 director

最近一直在學習Ansible的一些playbook的寫法, 所以一直沒有怎麽更新, 想到目前大家對諸如saltstack, docker, Ansible等自動化部署相關的工具很感興趣, 但又苦於沒有可學習的中文實例, 這裏我就把我這幾個月所接觸到目前國外比較流行的部署經驗給大家分享一下.

首先給大家介紹的是Ansible, 恩, 重要的問題說三遍, 不是Saltstack, Ansible作為一個python寫的自動化部署工具, 確實較之前我所接觸的Chef, saltstack, puppet更有自己的一些優勢, 首先就是agentless, 無需在Linux client安裝任何服務即可無縫連接Linux default ssh端口進行部署(windows需要安裝winrm 開啟ssh服務), 這點其實我覺得非常重要, 可以想象很多公司本身是對network管理非常嚴格的, 在部署一個產品的同時你需要考慮很多時間成本, 使用其他部署工具本身非常棘手的問題就是去申請開端口, client量少的話, 我們可以去等, 多的話本身你去request, waiting, unblock port等等long long process.... 最後會耗費很長時間. 這個對很多產品本身就是很致命的. 不推薦Saltstack的原因也是因為其需要在每臺agent逐一去安裝client service並測試, 這本身就會耗費一些時間成本.

其他呢? 其實我覺得就是容易上手, 語法簡單, 有現成模板讓你去學習, 加之是我們非常喜愛的python語法, why not?

Jenkins不用我多說, 估計懂行的人都在用它, 開源, 輕量級, 兼容性和擴展性強, 直觀的GUI管理這都是它的優勢, 配合Ansible我覺得用起來會非常easy going.

最後提一下Gitlab, 為什麽要用Gitlab? 他作為一個代碼版本控制系統和部署有什麽關系呢? 其實這裏就涉及一個我們Ansible playbook管理問題, 試想我們需要維護一個公司龐大的server集群, 我們所有需要部署的機器或者產品會對應我們相對的部署腳本, 我們使用的Ansible playbook如果只是保存在Ansible Server的具體某個目錄, 這本身就不便於我們進行編寫維護更新(想想每次都跑到遠程去編寫playbook或者每次在本地編寫好後再upload到遠程我都會腦補數以萬計的草泥馬從我眼前呼嘯而來).

這裏Gitlab就給我們提供一個非常方便以及直觀的Playbook management. 我們需要做的其實就是在Gitlab去建立一個對應產品或者server的playbook倉庫, 然後我們在本地寫好後直接commit到這個倉庫, 最後在部署的時候, 去讓Jenkins pull這個playbook到其workspace, 並作為一個Job去run這個playbook, 這樣是不是很規範, 而且便於管理?

當然Ansible本身企業版Tower也會提供一個類似管理並維護playbook以及監控ansible本身running process的GUI管理系統, 用起來也很不錯, 但作為收費版本, 我們在這裏就不做過多闡述了.

這裏我推薦Jenkins和Ansible可以安裝到同一個環境作為部署server, Gitlab作為版本控制系統可單獨部署在另一臺server.

總結:

Jenkins首先從Gitlab去抓取我們寫好的具體產品的playbook, 並使用virtualenv下的Ansible相關命令, 保證我們在一個clean的環境下使用stable version去批量部署我們的產品到遠程client.

Let‘s go.....

一. 安裝環境

System: CentOS 6.7 x64 (deploy.example.com)

Jenkins: Jenkins ver. 1.650

Ansible: Ansible 2.1.0

Gitlab: GitLab 7.14.3

二. Jenkins配置

我們創建deploy用戶作為jenkins_user, workspace為deploy家目錄下的jenkins目錄.

# su - root

# adduser deploy

# wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo

# rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key

# yum install jenkins -y

# vi /etc/sysconfig/jenkins

...
JENKINS_HOME="/home/deploy/jenkins"
JENKINS_USER="deploy"
...

# service jenkins start

瀏覽器訪問Jenkins頁面

http://deploy.example.com:8080

安裝完成, 以下是我已經配置好的一些Jenkins Job.

技術分享圖片

這裏我們使用一個國內PHP網站模板phpcms作為我們需要部署的產品進行本次範例演示, 在進行最終的Build前我們需要做一些準備工作, 稍後我們會回到這個界面.

三. Ansible配置

這裏我們需要配置virtualenv去隔離我們ansible的發行版本為最新版本2.1.0, 默認pip或者yum安裝的1.9版本因為BUG以及對windows不兼容的原因, 這裏不推薦使用.

配置步驟傳送門: http://www.showerlee.com/archives/1862

Ansible-playbook範例傳送門: http://www.showerlee.com/archives/1649

四. Gitlab配置

部署並使用傳送門: http://www.showerlee.com/archives/1285

我們最終會創建一個ansible playbook倉庫 [email protected]:showerlee/Ansible-showerlee.git, 並在本地編寫好我們的規則, 最終commit到這個倉庫, 以便Jenkins去調用我們的部署規則.

這裏博主單獨clone出來一份部署phpcms的playbook倉庫, 算是給大家的福利:

https://git.showerlee.com/showerlee/leon-playbook-phpcms1.1

技術分享圖片

技術分享圖片

技術分享圖片

五.最終部署

準備工作完畢, 我們接下來給大家介紹Jenkins Job配置.

1.創建一個new item

技術分享圖片

2. 創建一個freestyle Job, 命名規則"產品名-環境", 這裏我們為Phpcms-Dev

技術分享圖片

3. Job配置

1). 定制Build參數.

這裏Dynamic Choice Parameter用來通過Groovy腳本來抓取這個git倉庫的所有branch, 並作為一個多選項, 方便我們在最終Build前去選擇我們需要的這個產品Branch分支.

Groovy抓取Git branch代碼:

def gettags = ("git ls-remote -h [email protected]:showerlee/phpcms.git").execute()  
gettags.text.readLines().collect { it.split()[1].replaceAll(‘refs/heads/‘, ‘‘)  }.unique() 

技術分享圖片

Choice Parameter也是用來給我們Job定制Build前的可選參數, 不過這裏的參數可以直接寫死

deploy_environment為我們的參數名, 定義我們的部署環境名, prod, qa為我們具體的可選項, 定義我們產品的兩個環境.

技術分享圖片2). 源代碼管理

我們可以利用Jenkins內置的Source Code Management工具去抓取遠程Git或者SVN倉庫的代碼到本地, 這裏我們抓取存放在我們Gitlab上的Playbook到Jenkins的workspace目錄, 用來進行後續部署工作, 這個倉庫如需認證, 我們可以在Credentials add這個倉庫的用戶賬號密碼, 其余均保持默認即可(默認Jenkins default不支持Git, 需要到其後臺安裝Git插件)

技術分享圖片3). Execute shell進行最終的CLI部署.

這個Build模塊下的Execute shell方法是Jenkins比較常用並非常核心的功能, 用來執行我們部署過程中核心的命令.

開頭和結尾的set +x, set -x用來打開和關閉該部分的擴展參數及命令

開啟virtualenv和加載ansible環境變量

# source /home/deploy/.virtualenv/bin/activate
# . /home/deploy/.virtualenv/ansible/hacking/env-setup -q

進入該Job的workspace目錄下保存該playbook的倉庫子目錄下, 檢查ansible版本, 並執行最終的部署命令.

cd $WORKSPACE/leon-playbook-phpcms1.1
ansible --version
ansible-playbook -i inventory/$deploy_environment ./deploy.yml -e project=phpcms -e branch=$branch_selector -e env=$deploy_environment

註: -i 用來自定義ansible host文件路徑, ./deploy.yml為ansible-playbook入口文件, -e 後可跟給當前session添加的環境變量.

這裏$deploy_environment $branch_selector 為該Job定義好的可選參數, 詳見3-1)

技術分享圖片

配置完畢後, save保存.

4. 執行Job.

技術分享圖片

選擇master分支和prod環境

技術分享圖片

查看該Job最終的console output, 也就是顯示我們實際在CLI下的輸出結果.

技術分享圖片

Console Output

Started by user Leon Li
Building in workspace /home/deploy/jenkins/workspace/Phpcms-Dev
 > git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
 > git config remote.origin.url [email protected]:showerlee/Ansible-showerlee.git # timeout=10
Fetching upstream changes from [email protected]:showerlee/Ansible-showerlee.git
 > git --version # timeout=10
 > git fetch --tags --progress [email protected]:showerlee/Ansible-showerlee.git +refs/heads/*:refs/remotes/origin/*
 > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
 > git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
Checking out Revision 6bf787dcad68219d8eee09cecb83cbca36edbef1 (refs/remotes/origin/master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 6bf787dcad68219d8eee09cecb83cbca36edbef1
 > git rev-list 6bf787dcad68219d8eee09cecb83cbca36edbef1 # timeout=10
[Phpcms-Dev] $ /bin/sh -xe /tmp/hudson7452069223867148990.sh
+ set +x
ansible 2.1.0 (devel 6ddea3e915) last updated 2016/02/16 16:13:32 (GMT +800)
  lib/ansible/modules/core: (detached HEAD 8d126bd877) last updated 2016/02/16 16:19:09 (GMT +800)
  lib/ansible/modules/extras: (detached HEAD f6c5ed987f) last updated 2016/02/16 16:19:40 (GMT +800)
  config file = /home/deploy/jenkins/workspace/Phpcms-Dev/leon-playbook-phpcms1.1/ansible.cfg
  configured module search path = /home/deploy/active-ansible-modules/

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [deploy : Backup current source code] *************************************
changed: [127.0.0.1]

cmd: mv /data/deploy_dir/phpcms /data/deploy_dir/phpcms_master_1457681152

start: 2016-03-11 15:25:54.774716

end: 2016-03-11 15:25:54.927415

delta: 0:00:00.152699

TASK [deploy : Get new source code] ********************************************
changed: [127.0.0.1]

TASK [deploy : Check if caches/configs/database.php exists] ********************
ok: [127.0.0.1]

TASK [deploy : Check if test_dir exists] ***************************************
ok: [127.0.0.1]

TASK [deploy : debug] **********************************************************
ok: [127.0.0.1] => {
    "msg": "/data/deploy_dir/phpcms_master_1457681152/caches/configs/database.php exists"
}

msg: /data/deploy_dir/phpcms_master_1457681152/caches/configs/database.php exists

TASK [deploy : debug] **********************************************************
ok: [127.0.0.1] => {
    "msg": "/data/deploy_dir/phpcms_master_1457681152/test_dir exists"
}

msg: /data/deploy_dir/phpcms_master_1457681152/test_dir exists

TASK [deploy : Copy remote necessary original config to new release when Product env] ***
changed: [127.0.0.1] => (item={u‘name‘: u‘db_config‘, u‘dir‘: u‘caches/configs/database.php‘})
changed: [127.0.0.1] => (item={u‘name‘: u‘version_config‘, u‘dir‘: u‘caches/configs/version.php‘})

msg: All items completed

results: [
  {
    "src": "/data/deploy_dir/phpcms_master_1457681152/caches/configs/database.php", 
    "changed": true, 
    "group": "deploy", 
    "uid": 606, 
    "dest": "/data/deploy_dir/phpcms/caches/configs/database.php", 
    "checksum": "91869c2faa244f8c5de8a586636c6b4f3c0a2817", 
    "md5sum": "fd88a78a4629bca012a79d22fdcecadd", 
    "owner": "deploy", 
    "_ansible_no_log": false, 
    "item": {
      "name": "db_config", 
      "dir": "caches/configs/database.php"
    }, 
    "state": "file", 
    "gid": 608, 
    "mode": "0644", 
    "invocation": {
      "module_args": {
        "src": "/data/deploy_dir/phpcms_master_1457681152/caches/configs/database.php", 
        "directory_mode": null, 
        "force": true, 
        "remote_src": true, 
        "dest": "/data/deploy_dir/phpcms/caches/configs/database.php", 
        "selevel": null, 
        "seuser": null, 
        "setype": null, 
        "group": null, 
        "content": null, 
        "serole": null, 
        "original_basename": null, 
        "delimiter": null, 
        "mode": "0644", 
        "regexp": null, 
        "owner": null, 
        "follow": false, 
        "validate": null, 
        "backup": false
      }
    }, 
    "size": 302
  }, 
  {
    "src": "/data/deploy_dir/phpcms_master_1457681152/caches/configs/version.php", 
    "changed": true, 
    "group": "deploy", 
    "uid": 606, 
    "dest": "/data/deploy_dir/phpcms/caches/configs/version.php", 
    "checksum": "d0eaedb46a36303eb3f3e2a77cc2a623062eff3c", 
    "md5sum": "7917d8199b7c6d5bc87ff3035a72670e", 
    "owner": "deploy", 
    "_ansible_no_log": false, 
    "item": {
      "name": "version_config", 
      "dir": "caches/configs/version.php"
    }, 
    "state": "file", 
    "gid": 608, 
    "mode": "0644", 
    "invocation": {
      "module_args": {
        "src": "/data/deploy_dir/phpcms_master_1457681152/caches/configs/version.php", 
        "directory_mode": null, 
        "force": true, 
        "remote_src": true, 
        "dest": "/data/deploy_dir/phpcms/caches/configs/version.php", 
        "selevel": null, 
        "seuser": null, 
        "setype": null, 
        "group": null, 
        "content": null, 
        "serole": null, 
        "original_basename": null, 
        "delimiter": null, 
        "mode": "0644", 
        "regexp": null, 
        "owner": null, 
        "follow": false, 
        "validate": null, 
        "backup": false
      }
    }, 
    "size": 127
  }
]

TASK [deploy : Copy dir test_dir to new release when Product env] **************
changed: [127.0.0.1]

cmd: cp -a /data/deploy_dir/phpcms_master_1457681152/test_dir /data/deploy_dir/phpcms/

start: 2016-03-11 15:26:16.966237

end: 2016-03-11 15:26:17.069705

delta: 0:00:00.103468

TASK [deploy : Get php version] ************************************************
changed: [127.0.0.1 -> localhost]

cmd: python /home/deploy/jenkins/workspace/Phpcms-Dev/leon-playbook-phpcms1.1/roles/deploy/files/get_php_version.py http://www.showerlee.com

start: 2016-03-11 15:26:17.468311

end: 2016-03-11 15:26:51.560313

delta: 0:00:34.092002

stdout: PHP/5.4.13

TASK [deploy : debug] **********************************************************
ok: [127.0.0.1] => {
    "msg": {
        "changed": true, 
        "cmd": "python /home/deploy/jenkins/workspace/Phpcms-Dev/leon-playbook-phpcms1.1/roles/deploy/files/get_php_version.py http://www.showerlee.com", 
        "delta": "0:00:34.092002", 
        "end": "2016-03-11 15:26:51.560313", 
        "rc": 0, 
        "start": "2016-03-11 15:26:17.468311", 
        "stderr": "", 
        "stdout": "PHP/5.4.13", 
        "stdout_lines": [
            "PHP/5.4.13"
        ], 
        "warnings": []
    }
}

msg: {
  "changed": true, 
  "end": "2016-03-11 15:26:51.560313", 
  "stdout": "PHP/5.4.13", 
  "cmd": "python /home/deploy/jenkins/workspace/Phpcms-Dev/leon-playbook-phpcms1.1/roles/deploy/files/get_php_version.py http://www.showerlee.com", 
  "start": "2016-03-11 15:26:17.468311", 
  "delta": "0:00:34.092002", 
  "stderr": "", 
  "rc": 0, 
  "stdout_lines": [
    "PHP/5.4.13"
  ], 
  "warnings": []
}

TASK [deploy : debug] **********************************************************
ok: [127.0.0.1] => {
    "msg": "PHP/5.4.13"
}

msg: PHP/5.4.13

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=12   changed=5    unreachable=0    failed=0   

Finished: SUCCESS

這樣我們就利用Jenkins+Ansible+Gitlab, 成功部署phpcms到遠程Client

Jenkins+Ansible+Gitlab自動化部署三劍客