打造自己部落格的CI/CD pipeline,以BitBucket環境為例
必須要解決的問題
在之前的一篇文章中,我們使用Gatsby
打造出了一個寫blog的環境,但是使用沒多久就會遇到一個問題,那就是,部落格的更版實在是非常地麻煩
,以我是把部落格自己放在自己在DigitalOcean
VPS上來說,每次不管是有新文章,或是我有修改內容時,我都必須要
先在自己的電腦上執行
npm run build
指令(其實就是執行gatsby build
)把雲端server上放部落格的位置原本的所有靜態檔案全部刪掉
把自己的電腦上build完的檔案用
rsync
或scp
之類的指令,將檔案送去雲端server上雖然
1~3
其實是有辦法寫成一個指令,然後一個enter
完成,不過我後來發現這是一個很好的練習部署CI/CD pipeline
的練習情境。
在git push之後自動在remote repository上固定做某些事
這邊並不想要介紹CI/CD
的定義,也不想去爭辯我們做的事能不能稱得上是CI/CD
這樣 高~大~上
的名詞。
我們想要做到的事情很簡單,就是我只想要負責寫文章,部署我最後產生好的靜態檔案的事情我只想要在第一次時做一遍就好,以後都應該要能讓電腦自動幫我做,連gatsby build
這個類似編譯
的指令,我都不想在本地開發電腦做,我希望能夠有一台雲端上的機器負責做這件事情,並在編譯都正確無誤之後,能夠自動幫我把產出的東西,推送到正式環境
,就是硬要耍懶DRY原則在deploy時的實現
在寫部落格時在本地我已經有gatsby develop
這樣的指令可以邊寫邊確定產生出來的網頁的樣子了,其實可以不用把gatsby build
這件事在我自己電腦上做。
該如何做到??
首先我們把我們的部落格在免費的git server服務上創一個
repo
然後codegit push
到它上面,像我一開始是使用BitBucket,因為它在之前是唯一能夠免費擁有private repo
的服務。接著在本地blog的repo裡創立一個檔案叫
bitbucket-pipelines.yml
,這個就是bitbucket
pipeline機制的設定檔,bitbucket
在我們把新的codegit push
上去之後,如果repo底下有bitbucket-pipelines.yml
這個檔案,bitbucket server上的CI/CD runner
就會開始解析這個yml檔
裡的內容,並按照裡面來做事
那麼底下會是可以作到我們上述想做到的事的內容
# 聲明image的話 Bitcucket上的runner就會知道是要走docker container模式,這邊我們要用alpine這個linux os,然後上面已經安裝好最新版的node.js
image: node:current-alpine
pipelines:
# 只有在push到master 或 PR進master時才跑pipeline
branches:
master:
- step:
caches:
- node
script:
# alpine上的OS library管理工具叫`apk` 這句script 等於Mac OS上的 brew update, 然後順便裝一下openssh
- apk add --update --no-cache openssh
# alpinen雖然本身很輕量(只有5mb),但是它也缺少了很多一般OS上會有的程式,這裡是在安裝 tar, python, g++, make...這些東西,因為它們是我們之後gatsby build時的那些plugin可能會用到的程式
- apk update && apk add tar python g++ make bash zlib-dev libpng-dev&& rm -rf /var/cache/apk/*
# access日本那邊的一個alpine軟體庫 以取得 libvips這個程式(一個node.js的影像處理library,它被我們的gatsby-transformer-sharp這個plugin所需要用到,因為我們在gatsby build的過程裡會對圖檔進行優化)
- apk add --update --no-cache --repository http://ftp.tsukuba.wide.ad.jp/Linux/alpine/v3.10/main/ vips-dev
# 如果真正放blog source的資料夾跟 `bitbucket-pipelines.yml`不是同一層的話 不要忘了要再cd進去,在現在的template底下是在同一層,所以無須執行
# - cd ./myblog
# 給予權限,這是用來排除 `sharp EACCES: permission denied, mkdir '/root/.npm'`這個問題
- npm config set user 0
- npm config set unsafe-perm true
# 終於開始看到熟悉的指令,安裝寫在package.json裡的那些套件
- npm install
# 開始build,然後不產生sourceMap,以減少網路傳輸量和部署的速度
- npm run buildWithouSourceMap
# 用tar 把 gatsby產生出來在public(這個template gatsby build完之後放靜態檔案的資料夾)裡的檔案做壓縮,壓縮檔取名為 `release.tar.gz`
- tar zcvf release.tar.gz public
# ssh(所以剛剛上面才要安裝openssh)到要部署的遠端server,把檔案推過去之前先刪掉之前的所有檔案,
- ssh your_account@your_server_ip 'rm -rf /path/to/your/blogFiles/* && exit'
# 再把現在這個container底下剛剛做好的壓縮檔scp到server上放blog檔案的路徑底下
- scp release.tar.gz your_account@your_server_ip:/path/to/your/blogFiles
# 再登入一次 把剛剛scp過的檔案解壓縮,從public資料夾那一層移動所有的檔案到nginx上設定好放blog檔案的那一層,最後把壓縮檔跟已經空的了public資料夾刪掉,再登出
- ssh your_account@your_server_ip 'cd /path/to/your/blogFiles && tar zxvf release.tar.gz && mv ./public/* ./ && rm release.tar.gz && rm -r ./public && exit'
# 壓縮後再傳輸可以大大減少scp的傳輸時間,不然檔案太多,會傳很久
因為我們要在遠端機器上要用Gatsby
去用我們的repo裡的code
跟markdown
, assets
build出我們的blog的靜態檔,
Gatsby
是靠node.js
去驅動,要跑node.js
要有作業系統
,在這裡我們選用alpine
這一個微量的Linux作業系統,用了一個已經在alipne上安裝好了node.js
的docker image檔。
BitBucket的CI/CD Runner
因為看到我們的設定裡要使用image檔,
就會自動以docker
模式來接著執行我們寫在yml檔裡的pipeline,
儘管alpine
只有5mb,雖然縮短了runner
從dockerHub
下載docker-image的時間,但是就是因為太小了,所以作業系統裡缺少了一些我們後面要執行gatsby build
時所需要用到的api,
就變成我們自己必須要知道為達到我們的目的,我們還另外必須要安裝什麼。
等到所有的準備工作都做好之後,就可以在script
裡再執行我們平常在本地開發電腦上執行的指令。最後把build出來的靜態檔案,用ssh的shell送到正式環境的server上。
用這樣的方式,只要repo裡有bitbucket-pipelines.yml
這個檔案,每次git push
到遠端的repository上時,這個pipeline
就會發動一次,作到自動部署。
要注意的是:
BitBucket
上免費帳號每個月的pipeline時間只有50分鐘,萬一用完了就沒辦法在pull request 或是 push
之後觸發pipeline
了- 初次接觸這樣的東西的人要注意
yml檔
的寫法必須按照固定格式, 如都是使用兩個空格的縮排
...之類的
所以bitbucket網站上有pipeline yml檔的檢驗頁面,幫助我們少走冤枉路😭😂 Validator for bitbucket-pipelines.yml - 要針對
scp
或rsync
過去的的server在Bitbucket的repo上設定一組ssh key
給openssh
使用。
Github的travis CI
, Gitlab上的CI/CD也是大同小異,只是yml檔
的檔名不同 和 yml檔裡的設定的詞有些差異,按照其文件設定,應該就能迎刃而解。另外注意我們是使用它們的服務,所以不必自己處理runner
(用來跑pipeline的電腦或process)的事情。如果是自己架起來的git server,如自己架的Gitlab
, Gitea
(人們稱的 self-hosted Git service )那樣的狀況,就必須要自己處理runner
的機器的設定。