Morris's blog

打造自己部落格的CI/CD pipeline,以BitBucket環境為例

2019-08-04

必須要解決的問題

在之前的一篇文章中,我們使用Gatsby打造出了一個寫blog的環境,但是使用沒多久就會遇到一個問題,那就是,部落格的更版實在是非常地麻煩,以我是把部落格自己放在自己在DigitalOceanVPS上來說,每次不管是有新文章,或是我有修改內容時,我都必須要

  1. 先在自己的電腦上執行npm run build指令(其實就是執行gatsby build)
  2. 把雲端server上放部落格的位置原本的所有靜態檔案全部刪掉
  3. 把自己的電腦上build完的檔案用rsyncscp之類的指令,將檔案送去雲端server上

雖然1~3其實是有辦法寫成一個指令,然後一個enter完成,不過我後來發現這是一個很好的練習部署CI/CD pipeline的練習情境。

在git push之後自動在remote repository上固定做某些事

這邊並不想要介紹CI/CD的定義,也不想去爭辯我們做的事能不能稱得上是CI/CD這樣 高~大~上的名詞。
我們想要做到的事情很簡單,就是我只想要負責寫文章,部署我最後產生好的靜態檔案的事情我只想要在第一次時做一遍就好,以後都應該要能讓電腦自動幫我做,連gatsby build這個類似編譯的指令,我都不想在本地開發電腦做,我希望能夠有一台雲端上的機器負責做這件事情,並在編譯都正確無誤之後,能夠自動幫我把產出的東西,推送到正式環境,就是硬要耍懶DRY原則在deploy時的實現

在寫部落格時在本地我已經有gatsby develop這樣的指令可以邊寫邊確定產生出來的網頁的樣子了,其實可以不用把gatsby build這件事在我自己電腦上做。

該如何做到??

  1. 首先我們把我們的部落格在免費的git server服務上創一個repo然後code git push到它上面,像我一開始是使用BitBucket,因為它在之前是唯一能夠免費擁有private repo的服務。

  2. 接著在本地blog的repo裡創立一個檔案叫bitbucket-pipelines.yml,這個就是bitbucket pipeline機制的設定檔,bitbucket在我們把新的code git 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:
# default的話代表在每個git 分支上都會做以下的流程
  default:
    - step:
        caches:
          - node
        script:
# alpine上的OS library管理工具叫`apk` 這句script 等於Mac OS上的 brew update, 然後順便裝一下openssh
          - apk add --update --no-cache openssh
# alpinen雖然本身很輕量(只有5mb),但是它也缺少了很多一般OS上會有的程式,這裡是在安裝 python, g++, make...這些東西,因為它們是我們之後gatsby build時的那些plugin可能會用到的程式
          - apk update && apk add 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/edge/community/ 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
# ssh(所以剛剛上面才要安裝openssh)到要部署的遠端server,把檔案推過去之前先刪掉之前的所有檔案,
# 再把現在這個container底下的public資料夾(這個template gatsby build完之後放靜態檔案的資料夾)裡的檔案scp過去到server上放blog檔案的路徑底下
          - ssh your_account@your_server_ip 'rm -rf /path/to/your/blogFiles/* && exit' && scp -r ./public/* account@your_server_ip:/path/to/your/blogFiles

因為我們要在遠端機器上要用Gatsby去用我們的repo裡的codemarkdown, assets build出我們的blog的靜態檔, Gatsby是靠node.js去驅動,要跑node.js要有作業系統,在這裡我們選用alpine
這一個微量的Linux作業系統,用了一個已經在aline上安裝好了node.jsdocker image檔

BitBucket的CI/CD Runner因為看到我們的設定裡要使用image檔,
就會自動以docker模式來接著執行我們寫在yml檔裡的pipeline,
雖然alpine只有5mb,縮短了runnerdockerHub的時間,但是就是因為太小了,所以作業系統裡缺少了一些我們後面要執行gatsby build時所需要用到的api,
就變成我們自己必須要知道為達到我們的目的,我們還另外必須要安裝什麼。

等到所有的準備工作都做好之後,就可以在script裡再執行我們平常在本地開發電腦上執行的指令。最後把build出來的靜態檔案,用ssh的shell送到正式環境的server上。

用這樣的方式,只要repo裡有bitbucket-pipelines.yml這個檔案,每次git push到遠端的repository上時,這個pipeline就會發動一次,作到自動部署。

要注意的是:

  • BitBucket上免費帳號每個月的pipeline時間只有50分鐘,萬一用完了就沒辦法在git push之後觸發pipeline
  • 初次接觸這樣的東西的人要注意yml檔的寫法必須按照固定格式, 如都是使用兩個空格的縮排...之類的
  • 要針對scprsync過去的的server在Bitbucket的repo上設定一組ssh keyopenssh使用。 addSSHKeyForRunner_bitbucket

Github的travis CI, Gitlab上的CI/CD也是大同小異,只是yml檔的檔名不同 和 yml檔裡的設定的詞有些差異,按照其文件設定,應該就能迎刃而解。因為我們是使用它們的服務,所以不必自己處理runner(用來跑pipeline的process)的事情。如果是自己架起來的git server,如自己架的Gitlab, Gitea那樣的狀況,就必須要自己處理runner的機器的設定。

我的blog的pipeline紀錄 myblog_pipelineHistory

哪裡還能做得更好

  • 現在我們不管push到remote的哪個分支上,都會觸發pipeline並佈署到正式環境,但這不合理,照理來說理想的狀況是,merge request進master時才觸發佈署。

reference

response