用travis和git hook搞個一鍵部署

磐衝發表於2016-06-26

前言

在自己的vps上做部落格系統已經有一段時間了,期間也是磕磕碰碰遇到不少問題,如今也算是有個基礎版本能用。可是vps上只放一個部落格有點浪費了,而且部落格系統也不光是用來寫文章的,所以自然就開始放一些其他的自己開發的應用。
正好老婆到了要數胎動的日子了,於是就做了一個數胎動的應用,可是到部署的時候卻遇到了不少問題。
原本部落格系統用的是vue+loopback的前後端搭配,用forever支撐應用。但是新應用嘗試了一下django rest framework,不同的後端應用伺服器的存在,自然地要求引入一個web伺服器做請求轉發,所以就在前面放了一個nginx。關於nginx的配置就放到另一篇文章裡吧,也是一個愉(keng)悅(die)的過程。
一切看似妥當,但是頻繁的調整以及部署自然成了頭疼的問題。

叫作部署的大問題

由於我比較菜,所以沒有寫測試模組。但是我還是有(yao)追(zhuang)求(bi)啊!!所以我堅持要做沒有測試持續整合(誤)!換言之,一鍵部署
我們只看前端部分,那部署要做的工作是什麼呢?

本地開發完成 -> git push到github -> 登入到vps -> git pull -> build -> copy到nginx靜態檔案對應目錄

首先,通過常用的工具鏈,把能做的事情先做掉點。由於我用的是vue作為前端部分,構建用了webpack,包管理和流程控制都是npm,所以第一步,在prod用的webpack部分設定了一下部署目錄

webpack.config.js
var PRD_BUILD_PATH = path.resolve(ROOT_PATH, `../../deploy/babyMoveCounter`);
....
if (process.env.NODE_ENV === `production`) {
  module.exports.output = {
    path: PRD_BUILD_PATH,
    publicPath: ``,
    filename: `[name].js`
  };
}

之後,在package的script部分裡,加上deploy的這句,就算準備好了

package.json
"scripts": {
    "deploy": "NODE_ENV=production webpack"
}

於是,上面的這個工序就減少了一點,現在變成了

本地開發完成 -> git push到github -> 登入到vps -> git pull -> npm run deploy

還是很煩啊!!!

開始正式搞一鍵部署,引入git hook

網上看一鍵部署有好多文章,然後會發現很多都是講jenkins的,但是並沒有什麼X用。為什麼?因為絕大多數都是java黨用來部署java的啊。。。
況且在自己的vps上放個jenkins,貌似有點太笨重了。於是在不斷的google中,找到了git hooks這個神奇的東西。
git hooks和其他hooks差不多,也就是在各個階段加個鉤子乾點別的唄,所以我們就可以用到post-receive這個鉤子。
到這裡就出現了第一個坑。如果有人去看參考文件裡講git hooks的文章,或者去git官網看,一定會有不明覺厲的感覺。而且是看似有操作步驟,但是總感覺不太對。
因為他們說的都是往github push的時候乾點啥,可是我的應用是在自己的伺服器上,跟github沒半毛錢關係。。
經過思考,原來在這裡我們需要將部署工序做一個調整,同時增加N步。。。又要多事情了= =

本地開發完成 -> git push到github
本地開發完成 -> git push到vps上私有倉庫 -> 登入到vps -> cd到vps上的程式碼目錄(並不是私有倉庫目錄) -> git pull -> npm run deploy

沒錯,建一個私有倉庫,那麼post-receive就可以有用武之地了,怎麼建私有倉庫就很方便的google一下好了
可能遇到的問題是關於git使用者的許可權問題
在我自己的vps上,我在根目錄下設了3個資料夾

/opt/git/ # 用來放所有的私有倉庫
/projects/ # 用來放伺服器上pull的程式碼
/deploy/ # 用來放build之後的程式碼,為什麼要和projects分開呢?你去問nginx呀(摔!)

然後,讓我們加入post-receive
post-receive的程式碼可以放在/opt/git/xxxxproject.git/hooks裡面
我的程式碼是這樣的

#!/bin/sh

LOGFILE=./post-receive.log
# The deployed directory (the running site)
DEPLOYDIR=/projects/babyMoveCounter

##  Record the fact that the push has been received
echo -e "Received Push Request at $( date +%F )" >> $LOGFILE
echo " - Old SHA: $oldrev New SHA: $newrev Branch Name: $refname" >> $LOGFILE

## Update the deployed copy
echo "Starting Deploy" >> $LOGFILE

echo " - Starting code update"
GIT_WORK_TREE="$DEPLOYDIR" git checkout -f
echo " - Finished code update"

echo " - Starting npm install"
cd "$DEPLOYDIR"; npm install;
echo " - Finished npm install"

echo " - Staring build"
cd "$DEPLOYDIR"; npm run deploy;
echo " - Finished build"

echo "Finished Deploy" >> $LOGFILE

最關鍵的主要是3步,拉程式碼,npm install,npm run deploy
於是乎,我們的部署流程就大大的簡化了,現在是2鍵部署

本地開發完成 -> git push到github
本地開發完成 -> git push到vps上私有倉庫

離一鍵部署越來越近了,如果你喜歡2一點,那就不要看下去了

一鍵部署達成,引入travis-ci

其實做到上面這部,如果不想開源的話,一鍵部署也已經達成了。但是,一般自己專案都是開源放到github的,為了分(炫)享(技)嘛
既然放到了github,那麼travis-ci作為一個優秀的工具就可以被引入到流程中,同時,它也能作為連結2個倉庫的橋樑
引入travis-ci後,部署流程就會是這樣的

本地開發完成 -> git push到github -> 通知travis-ci -> 跑測試 -> git push到vps上私有倉庫

整合travis-ci

參考文件中hexo作者的部落格裡已經寫的挺清楚了,但是仍然會讓我覺得雲裡霧裡,為什麼呢?還是那個原因,他們是放到github.io上去的啊!!我要整合到我的vps呀!!
讓我們繼續。。。
首先,我們瞭解一下travis官方說的如何用git整合(注意,非github)
其中的樣例程式碼是這樣的

after_success:
  - eval "$(ssh-agent -s)" #start the ssh agent
  - chmod 600 .travis/deploy_key.pem # this key should have push access
  - ssh-add .travis/deploy_key.pem
  - git remote add deploy DEPLOY_REPO_URI_GOES_HERE
  - git push deploy

從中我們發現

  • 這又是一個hook

  • 這貨能替我們ssh

到這一步,再回想一下之前的部落格,我們可以推斷出,github到travis再到我們私有倉庫的業務流程應該是這樣的

git push到github -> 通知travis-ci -> 跑測試 -> 測試跑成功 -> travis開啟ssh -> git push到vps上私有倉庫

所以我們需要給travis一個部署用的rsa金鑰才行。所以就有了以下這幾步(這些命令都在專案根目錄中執行)
首先,我們生成一個金鑰

$ ssh-keygen -t rsa -C "your_email@example.com" #就像hexo作者的部落格裡說的,別加密碼,除非你想自虐

生成的檔案我們叫它們travis_deploy & travis_deploy.pub (記得在gitignore裡面把它們加進去)
由於我們是讓travis agent通過ssh上傳程式碼,所以我們把travis_deploy加密傳到travis上,然後,把travis_deploy.pub放到我們的私有倉庫裡
之前的部落格中是在github的專案裡放一個deploy的key,原理其實是一樣的,因為他的after success是在github上面執行,而我是在自己的vps上執行


讓我們一步步來

先寫一個基本的.travis.yml

language: node_js
node_js:
- `4`
script:
- npm run test
branches:
  only:
  - master

加密travis_deploy

$ gem install travis # 你需要ruby的gem
$ travis login --auto
$ travis encrypt-file travis_deploy --add

之後travis會自動修改專案中的.travis.yml

.travis.yml最終配置

language: node_js
node_js:
- `4`
script:
- npm run test
branches:
  only:
  - master
before_install:
- openssl aes-256-cbc -K $encrypted_cb1b65bfda52_key -iv $encrypted_cb1b65bfda52_iv
  -in travis_deploy.enc -out travis_deploy -d
- chmod 600 travis_deploy
- cp travis_deploy ~/.ssh/travis_deploy
- cp travis_config ~/.ssh/config

after_success:
- eval "$(ssh-agent -s)"
- ssh-add ~/.ssh/travis_deploy
- git remote add deploy git@xx.xx.xx.xx:/opt/git/yourPrivateRepo.git
- git push deploy

travis_deploy.pub

# sftp上傳到vps上,然後在server上執行
cat travis_deploy.pub >> /home/git/.ssh/authorized_keys

ssh config

到這一步,travis是能夠做git push了,但是自己的vps並不是travis agent的認證伺服器,所以我們需要增加一點ssh config,不然會出現Are you sure you want to continue connecting (yes/no)?

Host xx.xx.xx.xx
  User git
  StrictHostKeyChecking no
  IdentityFile ~/.ssh/travis_deploy
  IdentitiesOnly yes

到此,一切大功告成!我們的部署流程變成了

本地開發完成 -> git push到github

好high~ 好high~

補充一些過程中的坑

npm共享問題

一般在vps上安裝node都是用nvm來控制的,但是給git使用者用的npm總不能也用nvm來裝,所以需要跑一些指令讓root用的當前版本的node,同步給其他使用者

n=$(which node);n=${n%/bin/node}; chmod -R 755 $n/bin/*; sudo cp -r $n/{bin,lib,share} /usr/local

每次切換node版本如果git使用者也需要換的話,必須再執行一次

總結

沒啥總結的,一鍵部署,好high~~~

參考文件

travis & git

git post-receive hook

travis unauthed host issue

install nvm & share node to other user

相關文章