[實作筆記] 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)

[實作筆記] Gitlab CI/CD 與 GCP - Linux User 與資料夾權限

前言

請參考前篇,我們將建立兩台 VM,
一台作為 CI/CD 用的 Gitlab Runner,另一台作為 Web Server,
本篇將介紹 Gitlab Runner 的相關設定,
架構如下
GCP 與 Gitlab

參考我們的架構圖,

Linux 系統的使用者管理

使用 Linux 作業系統管理使用者帳號如新增、刪除或修改使用者帳號資訊等等。
以下是幾個常用的命令。

useradd

使用者帳號可以讓你進入系統,執行操作和管理文件等。
這是一個基本的命令,通常需要以系統管理員的權限執行。
範例如下:

1
useradd john

這個命令將會新增一個名為 john 的使用者帳號。

userdel

刪除一個使用者帳號,你可以使用 userdel。
刪除使用者帳號會刪除該使用者的所有文件,因此請確保你已經備份了重要的文件。
範例如下:

1
userdel john

這個命令將會刪除名為 john 的使用者帳號。

usermod

修改使用者帳號的資訊,例如姓名或者家目錄,你可以使用 usermod 命令。
範例如下:

1
usermod -c "John Smith" john

這個命令將會修改名為 john 的使用者帳號的姓名為 John Smith。

2023/05/23 補充使用情境

  • 查詢 www 的 group 有包含哪些 user?
  • 用 usermod 將 user gitlab-runner 加入 group www
  • 再次查詢 www 的 group 有包含哪些 user?
1
2
3
4
5
$ grep '^www:' /etc/group
www:x:1008:
$ sudo usermod -aG www gitlab-runner
$ grep '^www:' /etc/group
www:x:1008:gitlab-runner

補充:g+w 和 755 的差別

g+w 和 755 是針對文件或目錄的權限設置。

g+w 表示給與用戶組寫入權限(write permission)。
這意味著屬於該文件或目錄所屬組的成員可以對其進行寫操作,例如創建、修改或刪除文件。
其他用戶和組仍可能具有其他權限,如讀取或執行權限。

755 是八進製表示法,用於設置文件或目錄的權限。具體而言,它代表以下權限:

所有者(Owner)具有讀、寫和執行權限(rwx = 4 + 2 + 1 = 7)。
屬組(Group)具有讀和執行權限(r-x = 4 + 0 + 1 = 5)。
其他人(Others)具有讀和執行權限(r-x = 4 + 0 + 1 = 5)。
因此,g+w 僅給予了用戶組寫入權限,而 755 給予了所有者讀寫執行權限,用戶組和其他人的讀執行權限。

要注意的是,g+w 是一種相對於當前權限的增量設置,而 755 是一種直接指定權限的絕對設置。
具體取決於文件或目錄的初始權限,兩者可能會有不同的效果。

資料夾權限

可以用以下語法列出資料夾內的檔案與資料夾權限

1
ls -al

在 Linux 系統中,檔案的類型和權限使用特殊的符號來表示。
第一個字元表示檔案的類型,例如目錄、檔案或連結檔等等。
d 是目錄,-是檔案,l 是 link file;
b 是可儲存的周邊設備、c 是鍵盤、滑鼠等設備。

接下來的三個字元代表三種權限,分別是讀取(r)、寫入(w)和執行(x)。
每三個一組,分別對應檔案的擁有者、群組帳號和其他帳號。如果沒有權限,則會用減號來表示。
這些權限可以用來控制不同使用者對於檔案的存取權限。

在 macOS 中最後一個字元,有可能是 @+. 或沒有字元
@:代表此檔案有擴展屬性(Extended Attributes)。
+:代表此檔案有 ACL(Access Control List)權限限制。
.:代表此檔案有 SELinux 安全屬性(SELinux Security Attributes)。
沒有字元:代表此檔案沒有以上特性

舉例來說

1
drwx------ 87 mark.lin  rd      2784  4 12 13:21 Library

這是一個名為 Library 的資料夾,擁有者是使用者 mark.lin,群組是 rd。
權限設定為只有擁有者有讀、寫、執行權限,而群組和其他使用者都沒有任何權限。

調整資料夾權限

1
chown -R mark:mark /www/api

chown:用於更改文件或目錄的擁有者和群組的命令。
-R:對目錄及其子目錄進行遞歸操作。
mark:mark:擁有者和群組名之間使用冒號分隔,這裡將 /www/api 目錄及其所有子目錄和文件的擁有者和群組都設置為 mark。
如果您的系統中不存在名為 mark 的使用者和群組,這條命令會失敗

20230707 補充說明

sudo chown -R www:www : 此命令用於更改指定目錄下的所有文件和子目錄的所有者(owner)和所屬群組(group)。chown 是 “change owner” 的縮寫。-R 選項表示要遞歸地更改所有子目錄和文件的所有者和所屬群組。www:www 是欲更改的目標所有者和所屬群組的用戶名和群組名,這裡是 www。

sudo chmod 775 -R : 此命令用於更改指定目錄下的所有文件和子目錄的權限設定。chmod 是 “change mode” 的縮寫。-R 選項表示要遞歸地更改所有子目錄和文件的權限。775 是許可權的數值表示法,其中第一個數字 7 表示所有者的權限設定,第二個數字 7 表示所屬群組的權限設定,最後一個數字 5 表示其他用戶的權限設定。每個數字由三個位元組組成,分別表示讀取(4)、寫入(2)和執行(1)權限。

建立 VM 的 Account

在 GCP 的實作上我們不需要直接建立帳戶與群組,記得架構圖中的 SSH Key 嗎?
當我們在 Compute Engine Metadata 建立 SSH KEYS 時,就會依照這把 KEY 的 Comments 建立一個 Linux VM 帳號
因此我們的 VM 就會有對應的帳號資料

參考

(fin)

[聲明] 關於支語警察

如果它看起來像支語警察、貼文像支語警察、留言像支語警察,那麼它可能就是支語警察。

聲明

  • 理解並尊重
  • 是否修改內容由我判斷
  • 文化交流本來就會相互影響
  • 只在增進理解上作文章的修改

緣由小記事

5566 與注音文的戰爭

記得 21世紀初有一段時間,在 PPT 當時的大學生/高中生分為兩派,
一派是喜歡 5566 寫注音文,較為年輕; 一派是不喜歡 5566 討厭注音文,較為年長,
長時間在網路論戰,PPT 大多是年長者的勝場,常見有禁注音文入版規,甚之 56 開版被閙到關版。

然後他們都老了,之所以說這段往事,是給自已的反省,
我還是不寫注音文,但是沒那麼討厭 56 了,
當時在版上的種種行為,被我認定為世代的集體霸凌,而我認為這種事不該再發生。

文化交流本來就會互相影響

不論是支語、晶晶體、古早的注音文,
我認為都有其歷史脈絡與背景故事,
注音文表示電腦打字的盛行、晶晶體是與國際交流、支語則代表的兩岸交流的頻繁程度。
而台灣科技發達、地理政治位置都處於世界各個文化的交匯地帶,本來就會有各式各樣的文化衝擊。

我理解你討厭中國

因為在國際上的各種打壓、飛彈、擾台,中國真的很雞掰。

我更討厭思想審查、蓋世太保

透過控制對方的言論的輸出、輸入來控制思想,
這很納粹、也很現在的中國。

正體中文的價值

理解各種母語面臨的危機,
但正體中文在中文語系也變少數了,
我覺得這很珍貴,也是台灣文化的一環,
不然看看滿族、內蒙、新彊、西藏被中國共產黨搞成什麼鬼。
不需特眨低正體中文,來強調自已母語多優秀。

專業從業人員應有標準

可以的話中文語系應該用同一套標準來說明相關的專業知識是最好的,
實務上應該有政治問題造成的困難。
不然就說原文吧,科技應該快要能解開巴別塔的詛咒了。

順帶一提,看過支語警察的對照表是互相衝突的,支語警察們請建立自已的標準吧。

AI 潤飾版

如果一個人的語言、貼文和留言都充滿著批評和指責他人使用支語,那麼這個人很可能就是所謂的支語警察。

聲明:本人尊重並理解不同的文化背景和價值觀,修改文章僅在於增進彼此理解,且修改內容應由原作者自行判斷。

文化交流是相互影響的,因此我們不應該排斥使用不同的語言和文字,例如注音文、晶晶體和支語。
這些語言和文字都有其歷史脈絡和背景故事,例如注音文表示電腦打字的盛行,晶晶體則表示了對英文的追崇,
而支語則反映了台灣與中國交流的頻繁程度。台灣處於世界各種文化的交匯地帶,因此各種的文化衝擊是不可避免的。

雖然我理解一些人對中國政府的態度很反感,但我更反對思想審查和控制言論的行為。
這種行為與納粹主義和現代中國的行為很相似。

正體中文是台灣文化的一環,也是中文語系的寶貴資源。
儘管我理解各種台灣本土多數母語面臨的危機,但我認為正體中文應該被尊重和保護。
與此同時,我們也不應該輕視其他語言和文字,以表達我們的優越性。

對於專業從業人員,使用同一套標準來說明相關的專業知識是最好的。
然而,由於政治問題的影響,實踐上可能會存在困難。
不過,我相信科技很快就可以幫助我們克服語言障礙,實現真正的文化交流。

最後,提醒一下支語警察們,他們的對照表往往互相矛盾,因此建立一套公正的標準將是有益的。

我猜有人反對使用 AI 寫的文章

我的建議啦: 不爽不要看

(fin)

[翻譯] 在 X 分鐘學會 YAML

本文

YAML 是一種用於數據序列化的語言,設計初衷是讓人類能夠直接讀寫。
它是 JSON 的一個嚴格超集,具有類似 Python 的有意義的換行和縮排。
不同的是,YAML 不允許使用文字 tab 字符進行縮排,而需要使用空格進行縮排。

範例檔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
---  # 文件開始

# YAML中的註解看起來像這樣。
# YAML支援單行註解。

################
# 純量類型 #
################

# 我們的 root 物件是一個 map,
# 就像是其它語言的 dictionary, hash 或 object .
key: value
another_key: Another value goes here.
a_number_value: 100
scientific_notation: 1e+12
hex_notation: 0x123 # 0x開頭為 16 進制,所以值為 291
octal_notation: 0123 # 0開頭為 8 進制,所以值為 83

# 1 會被視作數字而非 boolean 值
# 如果要用 boolean 請使用 true

boolean: true
null_value: null
another_null_value: ~
key with spaces: value

# Yes No (不分大小寫) 也視作 boolean
# Yes→true 而 No→false
# 如果要用實際值,請用單引號或雙引號
no: no # 視作 false
yes: No # 視作 false
not_enclosed: yes # 視作 true
enclosed: "yes" # 視作字串 「yes」

# 請注意,字符串不需要引號。但是,它們可以使用引號。
however: 'A string, enclosed in quotes.'
'Keys can be quoted too.': "Useful if you want to put a ':' in your key."
single quotes: 'have ''one'' escape pattern'
double quotes: "have many: \", \0, \t, \u263A, \x0d\x0a == \r\n, and more."
# UTF-8/16/32字符需要編碼
Superscript two: \u00B2

# 特殊字符必須用單引號或雙引號括起來
special_characters: "[ John ] & { Jane } - <Doe>"

# 多行字符串可以使用“文字塊”(使用|)或“折疊塊”(使用'>')來寫入。
# 文字塊會將字符串中的每個換行符轉換為文字換行符(\n)。
# 折疊塊則刪除字符串中的換行符。
literal_block: |
This entire block of text will be the value of the 'literal_block' key,
with line breaks being preserved.

The literal continues until de-dented, and the leading indentation is
stripped.

Any lines that are 'more-indented' keep the rest of their indentation -
these lines will be indented by 4 spaces.
folded_style: >
This entire block of text will be the value of 'folded_style', but this
time, all newlines will be replaced with a single space.

Blank lines, like above, are converted to a newline character.

'More-indented' lines keep their newlines, too -
this text will appear over two lines.

# |- 和 >- 可以刪除結尾的空行(也被稱為文字塊的 "strip")
literal_strip: |-
This entire block of text will be the value of the 'literal_block' key,
with trailing blank line being stripped.
block_strip: >-
This entire block of text will be the value of 'folded_style', but this
time, all newlines will be replaced with a single space and
trailing blank line being stripped.

# |+ 和 >+ 可以保留結尾的空行(也被稱為文字塊的 "keep")
literal_keep: |+
This entire block of text will be the value of the 'literal_block' key,
with trailing blank line being kept.

block_keep: >+
This entire block of text will be the value of 'folded_style', but this
time, all newlines will be replaced with a single space and
trailing blank line being kept.

####################
# 集合類型 #
####################

# 巢狀結構使用縮排. 建議值為 2 個空白 (非必要).
a_nested_map:
key: value
another_key: Another Value
another_nested_map:
hello: hello

# Maps 不一定要用 string 作key.
0.25: a float key

# Keys 也可以很複雜, 比如多行物件
# 我們用 ?| 開頭表示複雜的物件
? |
This is a key
that has multiple lines
: and this is its value

# YAML 也可以用複雜物件作 Key Value 的映射
# 但有解析器可能會不過
# 如下例
? - Manchester United
- Real Madrid
: [ 2001-01-01, 2002-02-02 ]

# 序列(Sequences 或 lists 或 arrays) 看起來像這樣
# (注意 '-' 有縮排):
a_sequence:
- Item 1
- Item 2
- 0.5 # sequences 可以包含不同類型
- Item 4
- key: value
another_key: another_value
- - This is a sequence
- inside another sequence
- - - Nested sequence indicators
- can be collapsed

# 因為 YAML 是 JSON 的超集, 你也可以寫出 JSON-風格的 maps 與 sequences:
json_map: { "key": "value" }
json_seq: [ 3, 2, 1, "takeoff" ]
and quotes are optional: { key: [ 3, 2, 1, takeoff ] }

#######################
# 額外 YAML 功能 #
#######################

# YAML 很方便的功能叫 'anchors', 讓你可以很輕易的複製文件內容
# 使用 & 來定義值
# 使用 * 來呼叫值
# 下面兩個 key 有相同的值
anchored_content: &anchor_name This string will appear as the value of two keys.
other_anchor: *anchor_name

# Anchors 可以用於複製與繼承的屬性
base: &base
name: Everyone has same name

# << 表示式代表 'Merge Key Language-Independent Type'.
# 用於合併已存在 map 成為新的 map 並保留 key value
# 注意: 如果合併時要合併的 key已經存在,則該鍵的別名(alias)不會被合併
# 也就是說,如果要合併的 key 已經存在於數據結構中,則合併操作只會更新該 key 的值
foo:
<<: *base # doesn't merge the anchor
age: 10
name: John
bar:
<<: *base # base anchor will be merged
age: 20

# foo 與 bar 將會有相同的名字

# YAML 也有 tags, 請參考以下 Syntax
# Syntax: !![typeName] [value]
explicit_boolean: !!bool true
explicit_integer: !!int 42
explicit_float: !!float -42.24
explicit_string: !!str 0.5
explicit_datetime: !!timestamp 2022-11-17 12:34:56.78 +9
explicit_null: !!null null

# 其中有一些解析器(parser)會實作語言特定的 tag,像是 Python 的複數類型(complex number)
# 可以使用 !!python/complex 這個 tag 來表示複數。
# 在這個範例中,python_complex_number 變數的值被指定為一個 Python 複數類型的物件,其實際值為 1+2j。
python_complex_number: !!python/complex 1+2j

# 我們可以使用 yaml 的 keys 轉換為語言的特定資料類型
? !!python/tuple [ 5, 7 ]
: Fifty Seven
# 以上將被 Python 解析為 {(5, 7): 'Fifty Seven'}

####################
# 額外的 YAML 類型 #
####################

# 除了基本型別外(string number)
# Yaml 也支援 ISO-formatted 的日期與時間
datetime_canonical: 2001-12-15T02:59:43.1Z
datetime_space_separated_with_time_zone: 2001-12-14 21:59:43.10 -5
date_implicit: 2002-12-14
date_explicit: !!timestamp 2002-12-14

# 你可以用 !!binary 這個 tag 表示其後的值是 base64-encoded 的二進位編碼
gif_file: !!binary |
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=

# YAML 也有集合(set)類型
set:
? item1
? item2
? item3
or: { item1, item2, item3 }

# Sets 將 maps 向 null 值; 上面的 yaml 範例將與下面相等
set2:
item1: null
item2: null
item3: null

... # document end

參考

(fin)

[實作筆記] GCP Load Balancer

前言

Load Balancing 的主要目的是確保系統的可靠性、高可用性和性能。
透過平均分發網絡流量到多個伺服器,我們可以防止單一伺服器的過度負載,從而減少系統故障的風險。
此外,使用負載平衡器還可以提供可擴展性,使我們能夠根據流量需求自動調整伺服器的數量,確保系統在高負載情況下仍能保持良好的性能。

但是,我希望使用負載平衡器來達到降低對外 IP 地址和憑證費用的目的,以降低維運成本。
透過只需為負載平衡器配置一個對外 IP 地址和憑證,而不需要為背後的多個伺服器分別配置對外 IP 地址和憑證。

現在的公司,在舊架構上開了多台的 VM,並且都提供了對外的 IP address 與憑証,
對我來說,不僅實際上較為省錢,管理上也會更好理解。

架構

GCP LB

如圖所示,我有兩台 VM , 一台用來放置 api 相關的服務,比如說,member 會員系統,
另外還有一些整合性的服務,分別使用 Php 與 Nodejs 開發,簡單想成 api Server 就好,
實際上在 VM 當中有掛載 Nginx 系統來管理其相對應的路由設定。
另一台 VM 也不單純,儘管他只提供靜態的 Landing Page 網頁與 Vue 建置 SPA 網站,
但在可預知的未來他會 Host 更多的靜態網站。

也許可以考慮其它的 GCP 解決方案,ex:App Engine、Cloud Storage;
但總之目前就是這樣的架構,而我主要目的是不希望花太多時間處理憑証與對外的固定 IP 的問題,
所以本篇將會以這樣的架構進行建置 Load Balancer

實作

  1. 建立後端服務:在這個案例我們建立兩台 VM,實際上你也可使用 Cloud Storage 或 Container。
  2. 配置後端服務的群組:在這個案例我們建立兩個 Group,可以參加架構圖,真正的實務上,你的 Group 應該會有多個(至少要兩個) VM。
  3. 建立 Load Balancer:在本案我們使用 HTTP(S) Load Balancing
  4. 設定 Frontend configuration,在這裡我們給它一個對外的固定 IP 並建立憑証(certificate)
  5. 設定 Backend configuration,在這裡我們會建立一個名叫 Backend Service 的抽像層,選擇 Instance group(參考第 2 點,也可以在這階段再進行建立)
  6. 設定 Routing rules,選擇 Simple host and path rule:

    本案中,我們會有兩個 domain 分代表前後端 api.exampleapp.example
    host 設定為 domain,而路徑設定為/*,後端指向對應的 group ,如下
    api.example|/*|prod-api-group
    app.example|/*|prod-f2e-group

GCP 與 Load Balancer 相關的服務

整個建置流程與 SSL 憑証、IP Address、backend-services、instance-groups 等服務相關
雖然在建立 LB 的過程中就會建立憑証(certificate),
但有些情況可以使用 gcloud 指令作細節的控管

比如說,列出 SSL 憑證:

1
gcloud compute ssl-certificates list

更多資訊請執行以下語法,

1
gcloud compute addresses --help

同理,可以用類似的語法查詢其它的資源

1
gcloud compute instance-groups --help
1
gcloud compute addresses --help
1
gcloud compute backend-services --help

ChatGpt 有時會亂掰一些指令出來,我沒有一一實際執行過的這裡就不提供了。
測試負載平衡器:在將負載平衡器放入實際生產環境之前,您應該進行測試,確保負載平衡器正確運作並且可以處理預期的流量。
監控和調整:一旦負載平衡器正式運行,您應該定期監控其性能和健康狀況,並根據需求進行必要的調整和優化。

20230706 補充

如果想要從特定的 GCP VM 下載檔案到本地端可以用以下指令

1
gcloud compute scp {your-db-name}:/{some-service}.log ~

參考

(fin)

[實作筆記] Gitlab CI/CD 與 GCP - 防火牆設定

前言

請參考前篇
我們已經建立兩台 VM,
本篇將介紹 Firewall 的相關設定,
架構如下
GCP 與 Gitlab

情境

為了建立我們的 QA 測試環境,我們需要在 Google Cloud Platform (GCP) 上進行一些設定。
由於 QA 環境只能允許公司內部網路的存取,但我們又需要使用 Google 登入功能,這就需要綁定真實的 Domain 與對外 IP。
為了確保安全性,我們還需要設定防火牆,只允許經過公司路由的流量進入 QA 環境,以限制訪問權限。
這樣可以確保只有公司內部的流量可以訪問 QA 環境,保證資料的安全。
這個設定可以讓我們擁有一個安全、受控的 QA 環境,並且能夠順利使用 Google 登入功能,提供更好的使用體驗。

實作

登入至 GCP > VPC 網路防火牆設定頁面。在此頁面中,您可以新增防火牆規則。

接著,建立名稱為 “block-all” 的防火牆規則,並將優先級設定為 1000,以阻擋所有流量。
設定來源 IP 範圍為 0.0.0.0/0,並將允許的協定設定為 “deny”,以阻擋所有網路流量。
同時,建立名稱為 “allow-my-company” 的防火牆規則,並將優先級設定為 999,以允許公司內部網路的流量通過。
設定來源 IP 範圍為公司內部網路的 IP 範圍,並設定允許的協定包括 HTTP(80)、HTTPS(443)、SSH(22)和 ICMP,以便允許相應的流量通過。

最後,在創建防火牆規則時,務必勾選 “套用於標籤” 選項,並選擇要套用防火牆規則的 VM 的標籤。
確認防火牆規則的設定與標籤套用正確無誤後,點選 “建立” 來創建防火牆規則。
確保您的 VM 上都已經套用了 “block-all” 和 “allow-my-company” 這兩個防火牆規則的 tag,以確保該 VM 可以通過防火牆規則的設定進行存取。
最後,確認這兩個防火牆規則都已保存並生效,以確保 QA 環境只能通過公司內部網路存取,並且仍然可以使用 Google 登入功能。

建立 GCP 防火牆規則並套用 VM 標籤,確保 QA 環境的存取限制符合公司的需求。

參考

(fin)

[實作筆記] Gitlab CI/CD 與 GCP - 建立 Gitlab Runner VM

前言

請參考前篇,我們將建立兩台 VM,
一台作為 CI/CD 用的 Gitlab Runner,另一台作為 Web Server,
本篇將介紹 Gitlab Runner 的相關設定,
架構如下
GCP 與 Gitlab

建立 VM

建立 GCP VM 步驟如下:

  1. 登入 GCP 控制台並選擇適合的專案。
  2. 在主選單中選擇「Compute Engine」,進入虛擬機器的管理頁面。
  3. 點選「建立」(Create) 按鈕,開始建立新的虛擬機器。
  4. 選擇適合的地區 (Region) 與區域 (Zone),這會決定虛擬機器的物理位置與可用性。
  5. 選擇適合的機器類型 (Machine type) 與規模 (Instance size),這會決定虛擬機器的運算能力與資源配置。
  6. 選擇適合的作業系統映像 (Operating System Image),這會作為虛擬機器的基本作業系統。基本上我都選 Ubantu
  7. 選擇適合的開機磁碟 (Boot disk) 配置與容量,這會作為虛擬機器的主要儲存空間。
  8. 選擇適合的防火牆設定 (Firewall) 與網路 (Network) 設定,這會決定虛擬機器的網路連線與安全性設定。
  9. 完成其他選項設定,例如使用者資料 (User data)、預設密鑰 (SSH Keys)、啟用擴展 (Enable extensibility) 等。
  10. 檢閱與確認虛擬機器的設定,並按下「建立」(Create) 按鈕,開始建立虛擬機器。
  11. 等待 GCP 完成虛擬機器的建立與啟動。
  12. 成功建立虛擬機器後,可以透過 SSH 連線或其他遠端連線方式進入虛擬機器,並進行相關的設定與應用程式部署。

註冊 Group Runner

  1. 安裝 GitLab Runner, 參考 GitLab Runner 官方文件 上的指南進行安裝。

  2. 將該 Runner 註冊到您的 GitLab 項目中。注意 Runner 有 Group/Project/Shared 之分。

  3. 我們選擇 Group Runner, 請安裝 CLI

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 添加 Gitlab Runner 存儲庫的 GPG 金鑰
    $ curl -L https://packages.gitlab.com/gpgkey/gitlab-runner/gpgkey | apt-key add -

    # 添加 Gitlab Runner 存儲庫
    $ curl -L https://packages.gitlab.com/runner/gitlab-runner/ubuntu/$(lsb_release -cs)/

    # 更新 apt 軟體包索引
    $ apt-get update

    # 安裝 Gitlab Runner
    $ apt-get install gitlab-runner
  4. 執行註冊

1
2
# 註冊 Gitlab Runner,請按照提示輸入 Gitlab 的 URL 和 Runner 的註冊 token
$ gitlab-runner register

您可以在 GitLab 網站的項目設定中找到 Runner 註冊的相關資訊,並按照指南進行註冊。
下面是 2023 年的實作記錄,如果你有遇到任何狀況,再查閱 Gitlab 相關文件.

註: 需注意 Gitlab 的官方文章指出,未來將計劃棄用 Gitlab Register Token 的方式

The new registration process is expected to become available in %16.0,
and the legacy registration process will be available side-by-side
for a few milestones before the being sunset through a feature flag.
Removal is planned for %17.0.

參考

首先,你必須是 Group Owner。
接下來在 Group > CI/CD > Register a group runner > Registration token > 👁️ 取得 Token
執行 gitlab-runner register, 依提示輸入

  • url 這是指 gitlab 的 服務網址,如果你不是自已架設的 gitlab server 請輸入 “https://gitlab.com/
  • registration-token 這是在前面步驟取得的 token ,但這個作法預計在 Gitlab 17 版被棄用需注意
  • executor 請輸入 “docker” , 更多資訊請參考

其它參數請參考
執行命令後,應該可以在 Group > CI/CD > Runner 看到它,記得要啟動才會開始作業

建立 Group Variable

我們現在完成了 Gitlab-Runner,如果你也完成了你的服務伺服器設定,回頭看一下架構圖,
可以注意到,我們會透過 docker container 執行我們的 CI/CD 工作,
並且存取 GCP 的資源,在我們的例子中是使用 VM ,讓我們先忽略防火牆與使用者帳號的相關設定,
單純的連線機器實體,我會採用 SSH 的連線方式。

在一般的情況,我們會在 Client 端機器上生成 Private Key 與 Public Key,並且將 Public Key 放到 Server 端(服務伺服器)上,
不過在使用 docker container 的情況下,我們的 Client 端可以想像成是一個拋棄式的機器,
每次都建立新的 Private/Public Key 會讓 Server 端記錄著一大堆沒有用的 Public Key,
所以我們先建立好一組 Key,然後透過 Gitlab Variable 的機制傳遞到 Docker Container。

設定

  • 在群組中,前往「Settings > CI/CD」。
  • 選擇「Add variable」並填入相關資訊:
    • Key:必須是單行文字,不能包含空格,只能使用字母、數字或底線。
    • Value:無限制。
    • Type:變數類型,預設為「Variable」,也可以選擇「File」。
    • Environment scope:可選,可以選擇套用到全部環境 (All) 或特定環境。
    • Protect variable:可選,如果選中,該變數只會在運行於受保護的分支或標籤的流程中使用。
    • Mask variable:可選,如果選中,該變數的值將在作業日誌中被遮蔽。如果變數的值不符合遮蔽要求,則無法保存。

在此例中我,我用「File」儲存 Private Key。
未來可能會再評估一下資安風險。

參考

[實作筆記] Gitlab CI/CD 與 GCP - 建立 Web Server VM

前言

請參考前篇,我們將建立兩台 VM,
一台作為 CI/CD 用的 Gitlab Runner,另一台作為 Web Server,
本篇將介紹 Web Server 的相關設定,
架構如下
GCP 與 Gitlab

建立 VM

建立 GCP VM 步驟如下:

  1. 登入 GCP 控制台並選擇適合的專案。
  2. 在主選單中選擇「Compute Engine」,進入虛擬機器的管理頁面。
  3. 點選「建立」(Create) 按鈕,開始建立新的虛擬機器。
  4. 選擇適合的地區 (Region) 與區域 (Zone),這會決定虛擬機器的物理位置與可用性。
  5. 選擇適合的機器類型 (Machine type) 與規模 (Instance size),這會決定虛擬機器的運算能力與資源配置。
  6. 選擇適合的作業系統映像 (Operating System Image),這會作為虛擬機器的基本作業系統。基本上我都選 Ubantu
  7. 選擇適合的開機磁碟 (Boot disk) 配置與容量,這會作為虛擬機器的主要儲存空間。
  8. 選擇適合的防火牆設定 (Firewall) 與網路 (Network) 設定,這會決定虛擬機器的網路連線與安全性設定。
  9. 完成其他選項設定,例如使用者資料 (User data)、預設密鑰 (SSH Keys)、啟用擴展 (Enable extensibility) 等。
  10. 檢閱與確認虛擬機器的設定,並按下「建立」(Create) 按鈕,開始建立虛擬機器。
  11. 等待 GCP 完成虛擬機器的建立與啟動。
  12. 成功建立虛擬機器後,可以透過 SSH 連線或其他遠端連線方式進入虛擬機器,並進行相關的設定與應用程式部署。

Web Server

Web Server 我們建置得很簡單,
使用 Vue 建立的靜態網站,並且由 Nginx 來 host 它

  • 安裝 Nginx
1
2
sudo apt update
sudo apt install nginx

設定 Nginx:預設的 Nginx 設定檔通常位於 /etc/nginx/nginx.conf.。
可以編輯此設定檔,設定 Nginx 如何處理 Vue 的靜態檔案。
以下是一個範例設定檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
listen 80;
server_name example.com; # 替換為你的網域名稱

root /www/example_output; # Vue 建置的靜態檔在 .output/public 之中

location / {
try_files $uri $uri/ /index.html; # 使用 Vue 的路由模式,將所有請求都導向 index.html
}
}

server {
listen 443 ssl;
server_name example.com;
root /www/example_output;
index index.html;

ssl_certificate /etc/letsencrypt/live/example.com/full_chain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/private_key.pem;

location /www/example_output {
index index.html;
}
}

在建立測試環境的 Web Server 時,需設定 public IP 以對外提供服務,
但同時需要建立防火牆以保障安全性。其他如 DNS、憑證、防火牆等設定,之後再補充。

建立 Vue 專案

將 Vue 應用程式編譯成靜態檔案:在 Vue 應用程式的專案目錄中,可以使用以下命令編譯 Vue 應用程式成靜態檔案:

1
npm run generate

編譯完成後,靜態檔案會生成在 ./output 目錄下。

將編譯完成的靜態檔案放置到 Nginx 的目錄,
重新啟動 Nginx 即完成部署,如果有設定 Hot Reload 則不需要重新啟動
現在可以透過瀏覽器來測試 Vue 的靜態網站是否正常運作。

Tips

重啟 nginx

1
systemctl restart nginx

Nginx hot reload

1
nginx -s start

參考

(fin)

[實作筆記] Gitlab CI/CD 與 GCP - 架構全貌

前言

在一個團隊合作開發的場景中,多個開發人員可能同時對同一個程式碼庫進行更改,
而 CI/CD 可以自動進行建置、測試和部署,以確保程式碼的品質和穩定性。
此外,CI/CD 還可以幫助提高交付速度,減少錯誤,並支持快速反饋和迭代開發。

在這個背景下,選擇 GitLab CI/CD 與 Google Cloud Platform(GCP)作為 CI/CD 解決方案有幾個優點。
首先,GitLab CI/CD 提供了完整的 CI/CD 功能,包括強大的持續整合和持續部署工具,能夠無縫集成到 GitLab 的版本控制平台中,
方便團隊進行版本控制、自動建置和測試,並實現自動化的程式碼部署。
其次,GCP 是一個廣泛使用的雲端平台,提供了豐富的計算、儲存、網路和資料庫等服務,
方便團隊在雲端環境中進行應用程式部署和運行,並且與 GitLab CI/CD 有良好的整合性,可以輕鬆設定和管理 CI/CD 流程。

替代方案:若不選擇 GitLab CI/CD 與 GCP,團隊可能需要考慮其他 CI/CD 工具和雲端平台的組合。
例如,替代 GitLab 的版本控制工具可以是 GitHub、Bitbucket 等;替代 GCP 的雲端平台可以是 AWS、Azure 等。

架構

在我們的架構之中,我們使用 Docker 作為 GitLab Runner 的執行環境,
並在 GCP 上使用虛擬機器(VM)作為運行應用程式(web server)部署環境。
透過 GitLab Runner 和 Docker,我們能夠自動執行 CI/CD 任務,
並根據需要動態調整運行環境,確保我們的應用程式在開發和部署過程中保持高品質和穩定性。
如下圖。

GCP 與 Gitlab

步驟

接下來我將進行以下動作:

  • 在 GCP 上建立兩個虛擬機器(VM),一個作為我們的 Web Server,另一個作為 GitLab Runner。
  • 將 GitLab Runner VM 註冊為 GitLab Group runner,以便進行自動化的建置和部署。
  • 設定適當的防火牆規則,讓 Gitlab Runner 可以存取 Web Server
  • 讓 GitLab Runner VM 的 Docker 容器可以存取我們的 Web Server,我會設定相應的公鑰和私鑰,以確保安全的連線。
  • 我會撰寫一個適合的 gitlab-ci.yml 檔案,來定義建置和部署的流程,並將其配置在 GitLab CI/CD 中,以實現自動化的流程。
  • 最後,我會在 Web Server 上設定好使用者、群組與 Nginx 的 Hot reload。

以上的步驟將協助我們建立基於 Gitlab 與 GCP 的 CI/CD 流程,並以此提供程式開發的品質和穩定性。

參考

(fin)

Please enable JavaScript to view the LikeCoin. :P