用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧

Bitbucket Pipline 每個月有 60 分鐘的免費扣打,如果是自己的 project 使用實在是綽綽有餘!不給他用一下怎麼對得起自己呢!剛好最近在搞 line chatbot 作為自己的指令介面並 deploy 在 rockpi 4b 上,立刻就來試試看!

建立 bitbucket-piplines.yml

第一步就是在自己的 repo 下建立一個 bitbucket-piplines.yml,在裡面描述需要做的事情,基本概念和 jenkins 非常相似,如果有使用過應該很容易上手!

這邊以我的 yml 檔為例,簡單解說每個參數的意義。

image: python:3.8.5  # 給定跑 pipline 要用的 image

pipelines:  # 開始定義 pipline
  default:  # 如果沒有符合其他條件(如 branch, tag 等等)預設跑這裡
    - step:  # 開始定義步驟
      name: Test  # 為這個步驟命名
      caches:  # 下 cache 後,pip 那一層就不用每次都浪費時間跑
        - pip
      script:  # 從這裡開始定義要跑的指令
        - export ENV=local DEBUG=True
        - pip install -r requirements.txt
        - python -m pytest -vv -o junit_family=xunit2 --junitxml=test-reports/junit.xml

  branches:  # 定義特定 branch 要跑的
    master:  # 如果是 master branch 跑這裡
      - step:
        name: Test
        caches:
          - pip
        script:
          - export ENV=local DEBUG=True
          - pip install -r requirements.txt
          - python -m pytest -vv -o junit_family=xunit2 --junitxml=test-reports/junit.xml
      - step:
          name: Deploy to Prod
          deployment: production  # 定義這個步驟是 deploy to production,並使用對應的環境變數,後面會提到 
          script:
            - pipe: atlassian/ssh-run:0.2.8  # 定義接下來 deploy 時要使用的工具,這邊使用官方的 ssh run
              variables:
                SSH_USER: $SSH_USER  # 使用 production 環境的變數
                SERVER: $SERVER
                PORT: $SSH_PORT
                COMMAND: 'git pull && docker-compose up -d --build'  # 要跑的 deploy command

詳細觀察可以發現,test 的部分其實是重複寫了兩次,所以我們可以改寫如下:

image: python:3.8.5

commonStep: &testStep  # 共用的 step 放在這裡宣告
 - step:
    name: Test
    caches:
      - pip
    script:
      - export ENV=local DEBUG=True
      - pip install -r requirements.txt
      - python -m pytest -vv -o junit_family=xunit2 --junitxml=test-reports/junit.xml

pipelines:
  default:
    - <<: *testStep  # 使用 testStep

  branches:
    master:
      - <<: *testStep   # 使用 testStep
      - step:
          name: Deploy to Prod
          deployment: production
          script:
            - pipe: atlassian/ssh-run:0.2.8
              variables:
                SSH_USER: $SSH_USER
                SERVER: $SERVER
                PORT: $SSH_PORT
                COMMAND: 'git pull && docker-compose up -d --build'

同時也可以使用 bitbucket 官方的 pipline validator 來檢查 yml 檔有沒有寫錯,避免重複測試浪費時間!

pipline validator: https://bitbucket-pipelines.prod.public.atl-paas.net/validator

yml 檔關於 deploy 的部分有使用 pipe 設定,官方文件表示可以透過這裡使用第三方工具來跑指令,如 aws s3 deploy 等等,詳細可參考 https://support.atlassian.com/bitbucket-cloud/docs/what-are-pipes/

增加 deployment environment parameters

剛剛 yml 檔中可以注意到我們有使用環境變數,因為在 dev, stg, prod 等環境會有不同的設定。此時我們就可以到 Repository settings -> Deployments 下去對不同的環境給予對應的變數。

用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧

如果是需要加密的部分,就把勾選 secured,參數旁邊就會多一個鎖頭。屆時 pipline log 就不會顯示變數內容了!

用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧
可以看到對應的 SSH_USER 部分在 log 不會顯示他的真實值

增加 SSH keys

如果 deploy 的部分是需要 ssh 到遠端主機,就需要給 piplines 一組 ssh key。

在 repo 下點選左邊清單的 Repository settings -> SSH keys (在大項目 Piplines 下)。

用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧

點選 Generate keys,bitbucket 會立刻產生一組 key,將 public key 貼到要 deploy 的主機中的 authorized_keys 檔案內。

用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧

防火牆設定與 Known hosts

如果要讓 bitbucket ssh 進主機做 deploy,一定需要開 ssh port 給他使用。但你一定不想把 ssh port open 給全世界吧!好在 bitbucket 有列出他們的主機清單,把他們的 ip 全部加入防火牆即可!

https://ip-ranges.atlassian.com/

老實說還不少,但因為每次連進來的 ip 都會不一樣,只能做苦工一一加入。

完成後,就可以回到剛剛新增 ssh key 的頁面下方 Known hosts 部分,輸入主機 host address 並點擊 Fetch。如果發生錯誤,通常是防火牆設定有問題,要再調整一下。

用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧

如果 ssh port 不是 22,可以這樣寫

<host address>:<port>

啟動我們的 pipline

設定大功告成!現在只要 push 看看有沒有動就可以啦!

用 Bitbucket Pipline 為自己的 project 做 CI/CD 吧

延伸閱讀:
使用 AWS Parameter Store 實現 Config 和 Credentials 外置
自動化測試的重要性

封面圖片備註

pipline 就像工廠流水線一樣,一步一步把產品做出來

Written by J
雖然大學唸的是生物,但持著興趣與熱情自學,畢業後轉戰硬體工程師,與宅宅工程師們一起過著沒日沒夜的生活,做著台灣最薄的 intel 筆電,要與 macbook air 比拼。 離開後,憑著一股傻勁與朋友創業,再度轉戰軟體工程師,一手扛起前後端、雙平台 app 開發,過程中雖跌跌撞撞,卻也累計不少經驗。 可惜不是那 1% 的成功人士,於是加入其他成功人士的新創公司,專職開發後端。沒想到卻在採前人坑的過程中,拓寬了眼界,得到了深層的領悟。