[踩雷筆記] 移除 VM 外部 IP 後 GCS 檢查異常的問題

前情提要

我們使用 GCP Compute Engine 建立 VM,並且配置了一個對外的 IP(External IP),
同時設定了一個排程,定期將 VM 上產生的資料轉存到 GCS(Google Cloud Storage),
我們主要使用的工具是 gcloud 並且在 VM 上設定了某個 Service Account,
並且一切運作順利。

某日因為資安考量我們移除了 VM 的 External IP,而搬檔到 GSC 的排程就異常了。
我們簡單使用以下指令作檢查

1
gcloud storage ls

這個指令會列出我們 GCS 上的資源,
當 VM 有 External IP 時一切運作正常,而移除時會無回應。

解決方法

這裡只需要修改 GCP 的 VPC 設定,
選擇 subnet (ex:asia-east1,看你的 VM 在哪個 Zone)
啟用 Private Google access 為 On 然後存檔

參考

(fin)

[實作筆記] GCP 使用 IAP 連線 VM

簡介 IAP(Identity-Aware Proxy

GCP 的 IAP(Identity-Aware Proxy)是一個身份驗證和授權服務,
用於保護和管理對 Google Cloud 資源的訪問。
它提供了對虛擬機器(VM)實例的安全連接和控制,並可用於 SSH、RDP 等流量的安全轉發(Forwarding)。

通過 IAP,我們可以實現零信任訪問模型,僅允許經過身份驗證和授權的用戶訪問資源,
並且可以根據用戶的身份和上下文進行細粒度的訪問控制。
這提高了資源的安全性並減少了潛在的安全風險。

零信任訪問模型

零信任訪問模型(Zero Trust Access Model)是一種安全設計方法,該方法不假設內部網絡是可信的,
並對每個訪問請求進行驗證和授權,無論該請求來自內部還是外部網絡。
在零信任模型中,所有訪問都需要通過驗證和授權,並且需要進一步的身份驗證和授權步驟,
以確定用戶是否具有訪問資源的權限,更多可以查看參考資料。

比較 VPN 與 RDP

其他常見的連線方式包括傳統的虛擬專用網絡(VPN)和遠程桌面協議(RDP),
它們通常用於在企業內部建立安全連接,但對於雲環境和遠程用戶來說,這些方法可能不夠靈活和安全。

IAP 提供了一個更強大和安全的連接方式。
它在零信任訪問模型下工作,通過驗證和授權機制確保只有經過驗證的用戶可以訪問資源。
以下是 IAP 的優勢:

  • 精確的身份驗證和授權:IAP 可以根據用戶的身份和上下文進行細粒度的訪問控制,僅允許授權的用戶訪問資源。
  • 無需公開 IP 地址:IAP 通過提供 IAP 隧道和代理服務,不需要公開資源的實際 IP 地址,增強了安全性。
  • 雲原生和易於使用:IAP 是 GCP 的原生服務,與其他 GCP 服務整合,易於設置和管理。

前情提要

客戶在 GCP 上部署了多台 VM,每個 VM 都有公開的外部 IP 地址,並允許開發者使用 SSH 進行連線。
這樣的設置存在著安全風險,也需要為這些公開的 IP 付出額外的成本。

為了提高安全性並解決這個問題,我們使用 GCP 的 IAP(Identity-Aware Proxy)。
透過 IAP,我可以實現更安全的連線方式,不需要使用 Public IP 。
IAP 提供了精確的身份驗證和授權,只有經過驗證的使用者才能訪問 VM。
這種設置符合零信任訪問模型,提高了資源的安全性,同時減少了潛在的安全風險。

實務操作

我有一台測試用的 VM beta 已經拔除了 public IP,

  1. 首先啟用(enable) Cloud Identity-Aware Proxy API,「Security」->「Identity-Aware Proxy」

  2. 在 IAM > Permissions 找到需要登入 VM 主機的帳戶加上「IAP-secured Tunnel User」這個角色

  3. 防火牆規則,IP Range: 35.235.240.0/20, 開通指定的 Protocols and ports,比如我們要 SSH 連線,就開通 tcp:22,Targets 設為 Specified targets tags Target tags 為 ingress-from-iap,

  4. 需要登入 VM 加上 tag ingress-from-iap

  5. 可以用以下語法測試連線

    1
    gcloud compute ssh beta --project=my-project --zone=asia-east1-c --troubleshoot --tunnel-through-iap
  6. 排除所有問題後嚐試連線

    1
    gcloud compute ssh beta --project=my-project --zone=asia-east1-c --tunnel-through-iap

補充說明

  • 開發環境在連線時已經透過 gcloud auth login 取得權限了。
  • 連線的目標主機仍然需要設定 SSH 金鑰
  • 35.235.240.0/20 是 GCP 中 IAP 使用的特定 IP 範圍,不可修改

20230711 補充

透過 IAP 連線會出現 “Increasing the IAP TCP upload bandwidth” 的警告
可以參考官方文件

下載 Numpy

1
$(gcloud info --format="value(basic.python_location)") -m pip install numpy

設定環境變數

1
export CLOUDSDK_PYTHON_SITEPACKAGES=1

參考

(fin)

[生活筆記] 歌的故事-我也很想他

寫在前面

有時候聽到一些歌,會覺得很有畫面感,甚至有一個故事。
會想要了解,但是隨著時間流逝,這些資訊會越來越難取得。
如果有找到的話,就放在這裡備份吧。

我也很想他 – 孫燕姿

資料來源

e娛樂 updated: November 26, 2004 17:54

e歌樂全盒
彭學斌我也很想他

報導:王慧霞

故事

彭學斌因為陳穎見
寫下我也很想他

“一波三折”,可說是《我也很想他》的最佳寫照。

這首由大馬知名音樂人彭學斌填詞譜曲的《我也很想他》,在歷經兩度被退歌的命運后,
在轉轉折折之間,竟然落在亞洲小天后孫燕姿的手中,並且收錄在“東山復出”的專輯《
Stefanie》之中。

與此同時,《我》亦成了目前最受矚注的日本電影《在世界的中心呼喊愛情》台灣中文主
題曲,無疑為這首訴說一段淡淡情愁的情歌中,倍添了幾分秋天的落寞。

“歌曲背后的故事,我想是因為思念而開始了這首歌的創作。記得有一天,陳穎見的妹妹
專程來找我喝茶聊天,聊著聊著就聊起了遠在國外唸書的穎見。回到家之后,她突然傳了
個簡訊給我,告訴我說:我真的很想他(穎見);于是,我回簡訊給她,告訴她,我也很
想他。突然間,我泛起了一絲絲的靈感,就把當下的心情寫成歌詞,于是就有了這首歌的
誕生。”學斌如是說。

接著,經由版權公司的大力推薦,學斌便把這首歌交給當時一位正在收歌的台灣女新人演
唱,結果因曲風不適合而被彈回票,之后交由另一位偶像男歌手聽時,結果還是不獲青睞
。不過,有麝自然香,在幾經波折之下,這首歌卻落在了燕姿手中,叫他既意外又欣喜。

學斌透露,這首歌原本是決定收錄在燕姿“暫休”前的上一張的新歌加精選專輯當中,但
她和制作人的要求都頗高,一直覺得還唱不出這首歌所要的感覺,所以歌詞一修再修,挪
至1年后才見天日。

學斌欣慰地表示:“我不怕等待,只要對的人出現,那就是對的時機。”

對于修詞一事,學斌表示若符合歌手本身的特質,他倒是不介意。他表示,這首歌雖然被
修改了逾40%,由他對友人的思念意境,改為現在大家所看到的版本,但慶幸沒有乖离他
的本意,而燕姿深情的演繹,亦讓他倍覺感動。

學斌續指出,經過April的修改之后,把歌曲意境轉為是在訴說著兩女一男的感情故事。
透過燕姿的眼光,看著一對好友的經過相愛、傷害的情景,畫面楚楚動人,再加上悠悠的
旋律,輕易就勾起人們對于思念的因子。

(fin)

[實作筆記] MongoDB 解決方案評估-- Mongo Atlas

前情提要

最近在 VM 部署了 MongoDB, 不知道為什麼同事在 QA 與正式環境採取了兩個不同的作法,
因此產生了一些版本不一致的問題。
為此需要更換 MongoDB 版本,進而有一些討論,  
決策的考量主要有兩個面向,一、維運的成本。二、實際應付的帳務成本。
可行的方案比較如下,

  • VM 部署 MongoDB
    • GCP
    • Azure
    • Other Cloud …
  • Mongo Atlas(Start from GCP Marketing)
    • GCP VM with MongoDB(Pay as You Go)
    • Azure VM with MongoDB
  • Azure CosmosDB

實作隨筆:Mongo Atlas(Start from GCP Marketing)

測試規劃

mongo atlas 連線的測試架構

如圖,為了不影響原有的環境,我打算建一組新的 VPC Network 進行實驗,
並在這個網路中建立一台實體的 VM 機器,待 Mongo Atlas 設定完成後,
進行連線的測試。

測試的方法,由於 MongoDB Atlas 採用一種安全性較高的連線政策,
必需使用以下連線集群的三種方式才可以連到資料庫:
第一種,IP Access List,使用 GUI 建立 Cluster 的當下會自動加入一組你所在網路對外的 IP,
如果不是固定 IP 可能會有問題
第二種,Peering ,要付錢的版本 M10 以上才可用,也是我們這次實作的重點目標
最後一種,Create a Private Endpoint,也是
M10 以上才可用,但是不是我們這次的主要實作項目,所以不過多的展開。

從 GCP Marketing 建立 Mongo Atlas

在 GCP 的 Marketing 搜尋並訂閱 Mongo Atlas 後點擊 MANAGE ON PROVIDER
在 Mongo Atlas 建立 Cluster,也可以建立 Project 與 User 作更細緻的管控。
這時候可以到 Network Access 查看 IP Access List 的清單,應該會有你網路上設定的對外 IP,
這個流程是自動化的,但是我個人認為不是固定 IP 的話可能會有問題,如果有人可以給我一些提點會十分感激。

IP Access List

建立 VM

GCP 建立 VM 是十分簡單的,就不多作說明。
同時記得安裝我們的測試工具 - mongosh

Mongo DB 相關

切換 db

1
use mydb

查詢目前 DB 狀態

1
db.status()

Create User

1
db.createUser({ user: "username", pwd: "password", roles: [{ role: "roleName", db: "databaseName" }] });

Drop User

1
db.dropUser("username");

查詢 User

1
db.getUsers()

User 加入角色

1
db.grantRolesToUser("username", [{ role: "readWriteAnyDatabase", db: "mydb" }])

User 移除角色

1
db.revokeRolesFromUser("usernmae", [{ role: "readWrite", db: "admin" }])

前置作業: GCP VPC 與 Firewall Rules 設定

為了避免影響原有的系統,
建立 GCP 一組新的 VPC Network : vpc-lab,一般來說 GCP 的專案會有自動建立一組 default VPC Network,
default VPC Network 會預設建立以下的防火牆規則

  • default-allow-icmp – 允許來自任何來源對所有網路 IP 進行存取。ICMP 協議主要用於對目標進行 ping 測試。
  • default-allow-internal – 允許在任何埠口上的實例之間建立連接。
  • default-allow-rdp – 允許從任何來源連接到 Windows 伺服器的 RDP 會話。
  • default-allow-ssh – 允許從任何來源連接到 UNIX 伺服器的 SSH 會話。

與此對應,我也建立相同的規則給vpc-lab,如下:

  • vpc-lab-allow-icmp
  • vpc-lab-allow-internal
  • vpc-lab-allow-rdp
  • vpc-lab-allow-ssh

可以用以下的語法測試一下網路是否能連,如果可以連線再進行 mongodb connection 的測試

1
ping {ip address}
1
telnet {ip address} {port}
1
nc -zv {ip address}

測試連線用的語法

如果網路測試沒有問題,再進行 mongodb 的連線,由於目前沒有設定 Peering 連線,
所以在開發機上可以(網路環境需要在 IP Access List 內),而使用 GCP VM 會無法連線,
開發機連線 GCP MongoDB 語法

1
mongosh mongodb://{user:pwd}@{mongodb_ip}:27017/my_db

開發機連線 MongoDB Atlas 語法

1
mongosh mongodb+srv://{user:pwd}@{atlas_cluster_name}.mongodb.net/my_db

Peering 實作

首先需要在 Mongo Atlas 進行設定,
Network Access > Peering > Add Peering Connection
在 Cloud Provider 中選擇 GCP,

Mongo Atlas Setting

設定 Project ID、VPC Name 與 Atlas CIDR,
比較特殊的是 Atlas CIDR 在 GUI 的說明是

An Atlas GCP CIDR block must be a /18 or larger.
You cannot modify the CIDR block if you have an existing cluster.

但我遇到的狀況是,無法修改預設值為 192.168.0.0/16
建立後會產生一組 Peering 的資料,請記住 Atlas GCP Project ID 與 Atlas VPC Name

Mongo Atlas Peering

接下來到 GCP > VPC Network > GCP Network Peering 選擇 Create peering connection
在 Peered VPC network 中選擇 Other Project,並填入上面的 Atlas GCP Project ID 與 Atlas VPC Name

大概等待一下子就會生效了(網路上寫 10 分鐘,實測不到 3 分鐘)

參考

(fin)

[實作筆記] RWD 設計與 100vh 在行動裝置瀏覽器上的誤區

前情提要

參考圖片

RWD 設計與 100vh 在行動裝置瀏覽器上的誤區

我的網頁有作 RWD 的設計,需求大概是這樣,
綠色是在網頁底部懸浮的選單功能,
紅色區塊是一個控制面版,許多的功能、按鈕、連結都設定在上面。

我最一開始的設定方法如下,

1
height: 100vh;

在瀏覽器上使用模擬器顯示正常,但是在手機上就會出現異常
主要是手機上的 Chrome 和 Firefox 瀏覽器通常在頂部有一個 UI(例如導覽列及網址列等)  
而 Safari 更不同網址列在底部,這使得情況變得更加棘手。
不同的瀏覽器擁有不同大小的視窗,手機會計算瀏覽器視窗為(頂部工具列 + 文件 + 底部工具列)= 100vh。
使用 100vh 將整個文件填充到頁面上時可能導致顯示問題,因為內容可能超出視窗範圍或被遮擋。
因此,在移動設備上進行響應式設計時,應該避免使用 100vh 單位。

解決方法

解決的方法有好幾種,

JS 計算

使用 JavaScript 監聽事件,動態計算高度,缺點是效能較差,在主流的框架(EX:React)要小心觸發重新渲染的行為

1
2
3
4
5
6
const documentHeight = () => {
const doc = document.documentElement
doc.style.setProperty('--doc-height', `${window.innerHeight}px`)
}
window.addEventListener(‘resize’, documentHeight)
documentHeight()

CSS 變數

使用 CSS 變數

1
2
3
4
5
6
7
8
9
10
11
:root {
--doc-height: 100%;
}

html,
body {
padding: 0;
margin: 0;
height: 100vh; /* fallback for Js load */
height: var(--doc-height);
}

min-height 與 overflow-y

留言有人提到使用min-heightoverflow-y
但我的情境不適合,而且這個作法會產生捲軸

1
min-height: 100vh;
1
2
height: 100vh;
overflow-y: scroll;

進一步依照不同的使用情境也許我們需要 @supports

1
2
3
4
@supports (-moz-appearance: meterbar) {
/* We're on Mozilla! */
min-height: calc(100vh - 20px);
}

或是 Browser Hacks 的手法

1
2
3
4
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
/* We are on Internet Explorer! */
min-height: calc(100vh - 10px);
}

position:fixed 的作法

下面的方法可以將元素固定在畫面底部

1
2
3
4
position: fixed;
left: 0;
right: 0;
bottom: 0;

這是我最後選擇的方法,原文的討論相當精彩有趣,
但是我需要的其實是置底,而不是捲軸。
順帶一提我的專是基於 vue 與 tailwindcss,所以下面是 vue 與 tailwindcss 的寫法

1
2
3
<div class="fixed bottom-0 top-0">
<!--HERE THE FEATURES-->
</div>

參考

(fin)

[實作筆記] 管理 Linux 主機與 GCP VM 的磁碟

前情提要

在 GCP VM 上建立的 GitLab Runner 空間不足,導致執行失敗。
我們需要確保 GitLab Runner 有足夠的存儲空間來運行 CI 的 Jobs 。
有兩個解決方案可供選擇:
一、是清理磁碟空間
透過清理磁碟空間,可以移除不需要的文件和暫存資料,釋放寶貴的儲存空間。
二、是擴展磁碟空間
而擴展磁碟空間則是增加磁碟的容量,讓 GitLab Runner 可以持續運行而不受空間限制。
根據具體情況,可以選擇其中一種或兩種方案來解決空間不足的問題

第一部分:查詢與清理磁碟空間

要查詢 Linux 主機的磁碟空間,可以使用 df 命令。下面是使用 df 命令查詢磁碟空間的語法:

1
df -h

這將顯示磁碟空間的使用情況,包括每個檔案系統的大小、已使用的空間、可用的空間以及使用百分比。
要查詢特定目錄或資料夾的磁碟使用情況,可以使用 du 命令。以下是使用 du 命令查詢磁碟使用情況的語法:

1
du -h --max-depth=1 /path/to/directory

這將顯示指定目錄或資料夾的磁碟使用情況,並以人類可讀的格式顯示結果。
可以查詢到一些 cache 或是 log 如果沒有必要的話,可以將之刪除。

要清除 Docker 的磁碟空間,可以使用以下語法:

1
docker system prune -f

第二部分:擴展 GCP VM 的磁碟空間

要在 GCP VM 上垂直擴展 boot 磁碟空間,可以按照以下步驟進行操作:

在 GCP 控制台上,找到並選擇要擴展的 VM 實例。

  • 停止 VM:前往 VM instances 找到要停止的 VM,勾選後 STOP
  • 調整:前往 Disks,找到要 VM instances 所用的 Disk,點擊 Edit 後增加磁碟的大小。
  • 啟動 VM 實例。

小結

無論是清理磁碟空間還是擴展磁碟空間,都是為了讓我能確保足夠的空間供 GitLab Runner 使用,
確保順利運行 CI Jobs!

(fin)

[踩雷筆記] Hexo 網站跑版

前情提要

我的部落格是基於 Hexo 建立的靜態網站,
前幾天,我發佈了一篇新文章,但樣式出現了錯亂。
為了了解發生的問題與解決方案,必須先知道我們的架構,
相關的有三個儲存庫:

Gitlab Group

Github Page

這是部落格的主要內容,使用 Github Page 部署。
通常情況下,我們不會直接對它進行修改,因為大部分的更動都是透過串接的 Github Action 自動完成的。
這個儲存庫代表了整個流程的最終成果。

hexo action

這是我們的自動化工具,結合 Github Action 可以實現持續集成與部署(CI/CD)。
雖然平時不太需要特別注意,但它是相當重要的一環。

Source

這是我們撰寫文章的主要地方,也是整個部落格的功能和版面設計的編排處。

問題追蹤記錄

首先檢查了網頁載入的樣式,發現找不到 style.css 檔案,但有 style.styl
為了緊急處理,我先提供一個正確的 style.css

幸好我在本地端能夠生成正確的 style.css 檔案,所以我先緊急上傳了一個版本到 Github Page
重新執行本地端的測試,一切正常。再次進行部署,結果樣式又跑掉了。
於是我再次手動修正了 Github Page 的問題。

追查了一下 Github Action,發現在這個 commit 之後,異常情況開始發生:

1
2
-"hexo-renderer-stylus": "^2.1.0",
+"hexo-renderer-stylus": "^3.0.0",

從這個記錄中可以得知,問題出在 hexo-renderer-stylus 的更新,而且是一個較大的版本變動。
接下來我嘗試在本地端測試,但卻沒有發現問題。
我開始思考是不是我的部署環境(CI/CD)與本機環境的差異,
第一個是 Node 版本太舊了(12.x.x),所以我先更新了本機的 Node 版本到 20.x.x。
所以我修改了 hexo action,使用最新的 Node 20.x 版本的映像檔。
然而,重新執行部落格的持續部署作業仍然失敗,無法產生 style.css

盲點與解決方式

在這裡我卡住了很久,始終找不到原因。
再次梳理持續部署的流程,結果發現我使用的 hexo-action 版本為 v1.0.5。
原來我忽略了一個步驟,需要為 hexo action 添加新的標籤。
此外,在我們的 Source 的流程中(.github/workflows),
也需要修改為新的 action 標籤 uses: marsen/[email protected]

1
2
3
4
5
    # Deploy hexo blog website.
- name: Deploy
id: deploy
- uses: marsen/[email protected]
+ uses: marsen/[email protected]

(fin)

[實作筆記] 基於 Docker 的 CI Docker 映像檔加速構建流程

前情提要

在現代軟體開發的流程中,持續整合(Continuous Integration,簡稱 CI)是一個重要的概念,
它可以幫助開發團隊在頻繁的程式碼變更中保持項目的穩定性和品質。
而 Docker 作為一個輕量級的容器技術,在 CI 中也扮演著重要的角色。
本篇文章將介紹一個基於 Docker 的 CI Docker 映像檔(Dockerfile_project)的實作記錄(手動,未來將調整為自動化)。

實作

首先,這個 CI Docker 映像檔是基於 php:8.x 映像檔建立的,並且預先安裝了 composer 和 ext-mongodb 擴展。
這樣開發團隊在進行 PHP 項目的 CI 時,就可以直接使用這個映像檔,而不需要額外安裝這些依賴。

在開始使用之前,我們需要訪問 GitLab 的 Docker Registry,所以我們首先需要創建一個 Token。
在 GitLab 的項目設置中,可以找到「Settings > Repository > Deploy tokens」,
在這裡我們需要勾選「read_registry」和「write_registry」權限,以便於讀取和寫入映像檔。

接下來,我們需要使用以下指令來登錄到 GitLab 的 Docker Registry:

1
docker login $GITLAB_REGISTRY_URL -u $GITLAB_REGISTRY_USERNAME -p $GITLAB_REGISTRY_TOKEN

登錄成功後,我們就可以開始建立映像檔了。使用以下指令可以建立映像檔:

1
docker build -t registry.gitlab.com/my_group/subgroup/project .

如果你的電腦跟我一樣是 Mac M2 晶片,可以使用以下指令進行建立:

1
docker buildx build --platform linux/amd64 -t registry.gitlab.com/my_group/subgroup/project --load .

最後,我們需要將建立的映像檔推送到 GitLab 的 Docker Registry 中:

1
docker push registry.gitlab.com/my_group/subgroup/project

至此,我們已經完成了 CI Docker 映像檔的建立和推送的過程。
現在開發團隊可以在持續整合的過程中使用這個映像檔,從而確保項目的穩定性和品質。

小結

在本文中,我們介紹了一個基於 Docker 的 CI Docker 映像檔(Dockerfile_project)的實作步驟。
透過這個映像檔的建立和使用,開發團隊在持續整合(CI)過程中取得了顯著的效率提升。

使用這個新的 Docker 映像檔後,我們見證了從原本的 4 分鐘大幅改進至僅需 30 秒的驚人效果。
這意味著開發團隊現在能夠更快速地完成整個 CI 流程,並且在更短的時間內獲得即時的反饋。

這種效率的提升主要歸功於 Docker 的輕量級容器技術,以及映像檔中預先安裝的依賴(如 composer 和 ext-mongodb)。
透過這些優勢,開發團隊可以在相同的硬體資源下更有效地執行構建和測試操作,從而節省了寶貴的時間。

因此,使用這個新的 Docker 映像檔對於任何需要進行持續整合的專案來說都是一個明智的選擇。
它不僅能提升開發團隊的效率,同時也能確保項目的穩定性和品質。

(fin)

[實作筆記] Mac M2 建立 Docker Image

前情提要

在我的 Gitlab-CI 當中有個步驟 — 建立 Php Image — 十分的冗贅,
需要花3分鐘左右安裝 ext-mongo 與 composer,
如果可以找到已經安裝好的 Image 版本就可以省下這3分鐘。
但是找不到,所以我決定自已建一個 Image 吧

遇到的問題

在構建 GitLab CI 流程時,我遇到了一個問題。
我在開發機上建立了 PHP 的 Docker Image,但在 Gitlab-CI Runner 運行 Image 時,
出現了 “standard_init_linux.go:219: exec user process caused: exec format error” 的錯誤。
這個錯誤表示容器內部執行的可執行文件格式不正確或無效。

進一步分析後,我意識到問題可能是由於容器映像與主機操作系統的處理器架構不兼容引起的。
由於我的筆電是基於 Apple M2 晶片的 Mac,它使用的是 ARM 架構,而我的 CI 使用的容器映像可能是針對 x86 架構設計的。

解決方法

Docker for Mac 提供了一個功能稱為「多架構建置(Buildx)」,它允許我們在具有不同處理器架構的環境中建立 Docker Image。
以下是在 Mac 上建立 x86 架構的 Docker Image 的步驟:
確保您已安裝 Docker for Mac,並確保 Docker 客戶端已啟用 Buildx 功能。
可以使用以下命令來檢查是否啟用了 Buildx:

1
docker buildx version

如果 Buildx 功能已啟用,您應該能夠看到有關 Buildx 的相關訊息。

創建一個新的 Buildx builder,指定 x86 架構。使用以下命令:

1
2
docker buildx create --name mybuilder --use
docker buildx inspect --bootstrap

這將創建一個名為 “mybuilder” 的 Buildx builder,並將其設置為當前使用的 builder。

使用新建立的 builder 建立 Docker Image。
在 Dockerfile 所在的目錄中執行以下命令:

1
docker buildx build --platform linux/amd64 -t Image-name:tag .

在這個命令中,–platform linux/amd64 指定要建立的架構為 x86 架構。

為了將結果映像推送到容器倉庫,您可以加上 –push 選項,例如:

1
docker buildx build --platform linux/amd64 -t Image-name:tag --push .

這將在建置完成後將映像推送到指定的容器倉庫。

如果您只是想將映像載入到本地的 Docker 中,您可以使用 –load 選項,例如:

1
docker buildx build --platform linux/amd64 -t Image-name:tag --load .

實際上使用 --load 在本地端建立的 Image 因為架構不同也無法在本地端執行。

小結

使用了 Docker for Mac 的「多架構建置(Buildx)」功能,
讓我在 M2 Mac 上也可以成功建立 x86 架構的 Docker Image。
使用這個 Image 讓我可以省下 89% 的時間(3 分鐘 →20 秒)

(fin)

[實作筆記] Gitlab CI/CD 與 GCP - 機敏資料的處理

前言

請參考前篇 架構,
我們已經可以部署程式了.
更進一步討論一些實務上的狀況,這篇我們來討論如何處理一些特別的環境設定。

功能、組態與祕密

我們的程式有時會有一些設定,依照不同情境我的分類如下:
功能,是指提供給使用者的設定,簡單的有訂閱通知的開關,或是回呼的 callback url 等.
組態,不牽扯商業邏輯,純屬系統的設定,例如:站台第三方交互的網址,Stage 的名稱等.
祕密,相當於組態,但是不可以外留,否則有資安風險,例如:ClientId、Token、Secret 等.

今天討論的會是組態與祕密,然後我想參考一些資料,
一個是分散系統的建議作法 12 Factors - Config

The twelve-factor app stores config in environment variables (often shortened to env vars or env).
Env vars are easy to change between deploys without changing any code; unlike config files,
there is little chance of them being checked into the code repo accidentally; and unlike custom config files,
or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.

另一個是 Php Laravel 的官方作法

Laravel utilizes the DotEnv PHP library. In a fresh Laravel installation,
the root directory of your application will contain a .env.example file that defines many common environment variables.
During the Laravel installation process, this file will automatically be copied to .env.

想法

理想上、長遠上來我想要走到符合 12 Factory 的建議,
實務上考慮到現實情況和時間限制,加上我對 PHP 和 Laravel 不太熟悉:
現有系統的架構又不是分散式,我決定優先處理減少人為部署的工,而選擇以下的解決方案,

  1. 使用 Gitlab CI/CD 變數來保護敏感資料:
    Gitlab 提供了 CI/CD Variables 功能,可以將敏感資料儲存在 Group Variables 變數中
  2. 在 CI 過程中置換這些變數。
    這樣可以避免將敏感資料直接寫入到 .env 檔案中,提高了安全性(至少機敏資料不會在 Teams 或 Slack 中丟來丟去,或是寫入版控)。
    使用讀取 .env.example 檔案的方式,然後根據需要替換其中的參數值。
    這樣一來,我們可以根據不同的環境需求,動態生成 .env 檔案,而不需要手動修改原始 .env 檔案。

這樣的解決方案結合了 Gitlab CI/CD 的變數功能和動態生成 .env 檔案的方式,使得我們能夠更安全和靈活地管理參數。
雖然這不是 Laravel 官方的最佳實踐,但在缺乏 Laravel 經驗的情況下,這是一個可行且有效的解決方案。

重要的是,這個解決方案能夠讓我們在開發過程中保護敏感資料,同時又不需要深入理解 Laravel 的配置機制。
希望這個解決方案能對其他面臨相同問題的開發者有所幫助。
如果你對於 Laravel 的配置機制比較熟悉,當然也可以探索更適合的方法來保護參數。

實作

Gitlab Group
如圖,GitLab Group 是 GitLab 平台上的一個功能,它允許用戶在同一組織或專案的上下文中管理多個項目,方便協作、權限管理和組織層次的控制。

在 GitLab 中,Group 除了提供組織和專案的管理功能外,還可以使用 CI/CD Variables(持續整合/持續部署變數)來保護敏感資料。
CI/CD Variables 是在 GitLab CI/CD 過程中使用的環境變數,可以在項目層級或 Group 層級定義。

通過 Group 層級的 CI/CD Variables,你可以在整個 Group 內的多個專案中共享和管理變數。
這樣可以方便地保護和管理敏感資訊,例如 API 金鑰、密碼、配置設定等。
透過使用 Group 層級的 CI/CD Variables,你可以在所有專案中統一管理這些敏感資料。

而下面是一段 gitlab-ci 的範例,CI 過程中讀取 Gitlab 的 Group Variables 的並生成 .env 檔

1
2
3
4
- sed -e "s|#{APP_KEY}|$APP_KEY|"
-e "s|#{GOOGLE_CLIENT_ID}|$GOOGLE_CLIENT_ID|"
-e "s|#{GOOGLE_CLIENT_SECRET}|$GOOGLE_CLIENT_SECRET|"
.env.qa > .env

然後在 GCP 的 VM 加上 IAM 設定,Gitlab 對每個帳號設定適合的角色,
初步達成自動化與安全限制。

可能的(?)更好的作法

  • 環境變數與分散式系統 ???
  • 使用雲服務的 Secret Management ???
  • 實作 Laravel Best Practice ???

如果你更好的作法請推薦給我

20230607 補充

使用 CI 建置不同環境的 .env 檔是一件痛苦的事,再更好的方法出現前,我只能儘量減少重複,
下面是一個例子,我需要建立 QA 與 Production 的 .env 檔,
而我目前的作法會在 Gitlab 的 CI/CD Variables 建立不同的 Key,
像是 MY_QA_APP_KEYMY_PROD_APP_KEY
而基礎的設定值又來自不同的檔案 .env.qa 與 .env.production apl
這真是一個糟糕的實作,不過為了動態組出不同的 key,
可以參考以下的寫法,注意單雙引號有不同行為

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.config_temp: &config_script
script:
# Create Env
- echo "KEY:$(eval echo '${'${PREFIX}'APP_KEY}')"
- sed -e "s|#{APP_KEY}|$(eval echo '${'${PREFIX}'APP_KEY}')|"
-e "s|#{GOOGLE_CLIENT_ID}|$(eval echo '${'${PREFIX}'GOOGLE_CLIENT_ID}')|"
-e "s|#{GOOGLE_CLIENT_SECRET}|$(eval echo '${'${PREFIX}'GOOGLE_CLIENT_SECRET}')|"
.env.${suffix} > .env
# 中略
config-qa:
stage: build
variables:
PREFIX: "MY_QA_"
suffix: "qa"
needs: [build]
<<: *config_script

config-production:
stage: build
variables:
PREFIX: "MY_PROD_"
suffix: "production"
needs: [build]
<<: *config_script

參考

(fin)