部署策略與環境分離:應對「朝令夕改」的開發伺服器

比規格書更有力的一句話:「先讓我看一下吧」

身為一個在公部門 SI 現場獨自負責開發的人,我面對的最大難題並不是技術,而是「不確定性」。完全不存在那種依照嚴謹需求規格書按部就班設計的理想環境。許多開發知識不足的承辦人員,在實際看到畫面之前,連自己想要什麼都不太清楚。

「嗯,看圖片我也說不上來……可不可以實際點點看?」

我每做一個功能,就在本機(自己的電腦)上跑一次、截圖、用紅筆加註,做成「報告檔(HWP/PPT)」寄出去。但只靠靜態圖片根本沒辦法滿足承辦人員。

「啊,剛剛那個功能好像比較好,可以幫我改回去嗎?」「按這個按鈕會發生什麼事?幫我錄成影片吧。」

昨天說 A 比較好,今天又改要 B。在本機改程式碼、再截圖、再寫報告的無限迴圈。寫報告的時間比寫程式還長,完全本末倒置。

我深刻地體會到:「不是把自己電腦的畫面截圖寄出去,而是需要一個讓承辦人員可以親自進來盡情點擊、盡情把它玩壞的遊樂場(開發伺服器)。」

勝過千言萬語(報告)的,是一次實際執行(開發伺服器)。

機會是偶然降臨的:被遺忘伺服器的再發現

剛好在公司分拆的過程中,我們團隊很幸運地分到了一台閒置伺服器(空主機)。規格雖然不算好,但裝著 Linux 並接著公網 IP,是非常珍貴的資源。

「主管,這台沒在用的伺服器我可以用嗎?我想把它架成給承辦人員確認用的伺服器。」

得到了允許。我把這幾個月學到的所有技術全都灌注在這台空殼伺服器上,開始打造「屬於我的小小基礎設施帝國」。這是把過去幾個月辛苦摸索的內容(Works on My Machine & The Pipeline 系列)做總整理的過程。

  1. GitLab (程式碼儲存庫): 架設私有儲存庫,安全地管理程式碼。
  2. Docker Registry (映像檔儲存庫): 打造一座專屬於我們、用來存放建置好的 Docker 映像檔的倉庫。
  3. Portainer (容器管理): 為了害怕 CLI 的我,讓 Docker 可以用 GUI 來管理。
  4. Nginx (網頁伺服器): 設立一位替前端與後端請求指揮交通的守門人。
在空伺服器上集結至今所學的所有技術,完成了基礎設施的搭建。

解決方案:環境的徹底分離(Dev vs Prod)

現在我有了兩個舞台(Server)。為了一邊接住承辦人員的反覆變動,一邊守住正式環境,我把部署策略分了開來。

  1. 開發伺服器 (Development):「自由的實驗室」
    • 根本不給承辦人員開口問「那個好了嗎?」的機會。
    • 只要程式碼一進到 develop 分支,流水線就會立刻跑起來,自動部署。
    • 承辦人員隨時都能連上開發伺服器的 URL 測試功能,並給予回饋。
  2. 正式伺服器 (Production):「經過驗證的聖域」
    • 只有在承辦人員宣布「就用這個功能,最終定案!」的時候才會部署。
    • 把程式碼合併(Merge)到 master 分支後,還必須由我在 GitLab 上按下「部署核准按鈕」(Manual)才會部署。

[Code Verification] 用條件式部署來建立秩序

GitLab CI 可以針對不同分支指定不同的部署路徑。把這個策略用程式碼實作出來,大致如下。

stages:
  - build
  - deploy

# 1. 建置(共用)
build_job:
  stage: build
  script:
    - ./gradlew build
    - docker build -t my-reg/app:$CI_COMMIT_SHA .
    - docker push my-reg/app:$CI_COMMIT_SHA

# 2. 部署到開發伺服器(自動)
# 只要程式碼進到 'develop' 分支,二話不說就部署。
deploy_to_dev:
  stage: deploy
  script:
    - ssh user@dev-server "docker service update --image my-reg/app:$CI_COMMIT_SHA app-dev"
  only:
    - develop 

# 3. 部署到正式伺服器(手動核准)
# 即使程式碼進到 'master' 分支,在我按下按鈕前都不會部署。
deploy_to_prod:
  stage: deploy
  script:
    - ssh user@prod-server "docker service update --image my-reg/app:$CI_COMMIT_SHA app-prod"
  only:
    - master
  when: manual # 重點!只有「人按下按鈕」才會執行。

核心重點:

  • only: - develop: 開發分支一律自動部署。速度就是生命。
  • when: manual: 正式分支則加上一道安全裝置,由人再確認一次之後才按下按鈕。

結語:終於完成的「一個人的生活」

到這裡,單人開發的我也終於擁有了完整的系統。

  1. 本機(Local): 在自己的電腦上開發功能。
  2. 開發(Dev): 只要一個 git push,5 分鐘後就會反映到開發伺服器上。由承辦人員親自確認。
  3. 正式(Prod): 只把確認過的功能集中起來,點一下就能安全地部署。

那個剛進公司、開著截圖工具、在 HWP 檔上加註解、加班到深夜的我已經不在了。承辦人員說「這個再幫我改一下」時,我可以從容地說「好的,我已經改完並上到開發伺服器,麻煩您確認看看」,然後悠閒地喝杯咖啡。因為系統正在替我工作。

到此,漫長的 [Re: Booting] 系列就告一段落了。從計算機科學基礎開始,一路走過 Linux、Docker,再到 CI/CD 流水線。希望這些從零開始硬撐起來的知識,能成為某處還在獨自奮戰的「為生計打拚的全端工程師」們的一盞小燈。

發佈留言