[工作筆記] 採蘋果

前情提要

公司有三台 Macbook pro 2017 年款,普遍都有電池的問題,
為此研究了一下在台灣舊機換新機的策略。

折抵

選擇好你的新機,在「加入購物袋」前,找到「加入換購方案」,依指示輸入郵遞區號、選擇製造商 Apple 後
輸入序號後選擇相關的硬體設備。
就會出現一個換購的金額,再繼續完成你的購物袋即可。

企業商店

企業採購的話可以有累積的金額優惠,如果滿足到累積的金額(一年內 5000 美金),就可以以優惠的方式來做訂購,
需要打電話(見文末參考),使用統編建立企業商店。
其它方式與折抵一樣。
訂單成立後可以刷卡或是匯款,刷卡可以立即看到訂單進度,
而匯款後需要再打電話(0800-020-021)確定訂單進度。

折抵的部份會再出貨後 14 天來回收舊機器,應該有足夠的時間轉移資料。

參考

(fin)

[工作筆記] 軟體工程的檢傷分類

前情提要

回想一下你手頭上的工作,你如何決定你的工作順序?
常見的有緊急/重要四象限法。
另外在一些方法論上,利如 Scrum 交由特定角色排序,
而急診室的檢傷分類如下。

1
2
3
4
5
6
7
8
9
第一級(復甦急救,立即處理-病況危急生命或肢體,需立即處理。如:心跳、呼吸停止、肢體及嘴唇發青、發紫,體溫高於41度或低於32度,無意識或意識混亂、持續抽搐且無意識。

  第二級(危急,可能等候時間10分鐘)-潛在性危及生命、肢體及器官功能,需快速控制與處理。如:急性意識狀態改變、持續胸悶、胸痛且冒冷汗、低血糖(<40mg/dl)、外傷造成之大量出血,頭頸軀幹骨盆部位血流不止、高處墜落、車禍(乘客被拋出車外)、頭部撞擊後曾失去意識等。

  第三級(緊急,可能等候時間30分鐘)-病況可能持續惡化,需急診處置;病人可能伴隨明顯不適症狀,影響日常生活。如:無法控制的腹瀉或嘔吐、外傷後肢體腫脹變形疑似骨折/脫臼、高血壓(收縮壓>200mmHg或舒張壓>110mmHg)且沒有任何症狀等。

  第四級(次緊急,可能等候時間60分鐘)-病況可能為慢性病及急性發作,或某些疾病之合併症相關,需在1至2小時做處置避免惡化。如:局部蜂窩性組織炎、急性咳嗽但沒有發燒、發燒但無其他不適、反覆性疼痛或暈眩等。

  第五級(非緊急,可能等候時間120分鐘)-病況非緊急,需做鑑別性診斷或轉介門診。如:慢性噁心、嘔吐或打嗝、輕微擦傷,瘀青,軟組織受傷、螫傷或咬傷,但無發燒或疼痛不適、輕微腹瀉,無脫水現象等。

我們可以參考。以等級與具體案例作為分析。

個人的經驗是以 3+1 個維度分析,
主要為發生風險、發生頻率、影響範圍和可否重現。

判斷流程

  1. 是否為線上問題:線上問題較可能產生價值的耗損
  2. 發生頻率:高頻的問題如果不處理會帶來大量干擾、下一階段可能會麻痺,甚至讓真正的問題從眼前跑走
  3. 影響範圍:誰會感到痛苦?老闆?客戶?PM ? RD ? 營運?怎麼作可以停止或減輕他們的痛苦。
  4. 風險評估:不作會怎麼樣?作了會怎麼樣?

定義

x 的維度是風險的高低,風險高者需安排修正,風險低小者可以再觀察
y 的維度是影響範圍的大小,範圍大者需優先停損縮小範圍,範圍小者就安排修正
z 的維度是發生的頻率,高頻者需優先停損止傷,低頻者需要觀察,如果發生一次就會形成持續性或永久性的傷害即為高頻
加上可否重現分析如下:

可重現

高風險/高範圍的情況下,頻率已經不重要,重要的不能讓發生 0 次的事情發生 1次,
一但發生只能砍掉重練,具體例子如廣達被勒索病毒攻擊

維度一:高風險/高範圍/高頻率:終止營業、暫停營運的等級
維度二:高風險/高範圍/低頻率:終止營業、暫停營運的等級

應急辦法,止血: 先限縮風險、範圍、頻率至少其中之一。

低風險/高範圍的情況,即使發生了仍有轉還的空間,高頻時處理的重點在縮小範圍與暫時的降噪
一但修正,理應停止降噪的設定,低頻時安排修正計劃即可

維度三:低風險/高範圍/高頻率:部份功能停止運作。應暫時的降噪(暫時停止通止,避免有其它警告被淹沒),安排修正計劃
維度四:低風險/高範圍/低頻率:部份功能停止運作。儘速安排修正計劃。

在高風險的情況,如果有小範圍的異常,都應立即處理,重點在於能不能根除問題,或是緩解發生頻次

維度五:高風險/低範圍/高頻率:根除問題,或是緩解發生頻次
維度六:高風險/低範圍/低頻率:根除問題,或是緩解發生頻次

在低風險/低範圍的情況下,要小心避免讓人習慣這類型的錯誤(麻痺),或是產生過多雜訊影響判斷。
偶一為之但是知道原因的話可以不處理

維度七:低風險/低範圍/高頻率:降噪或是設定提示水位
維度八:低風險/低範圍/低頻率:不處理

不可重現

不知道自已不知道是最可怕的,如果發生時是高風險/高範圍大多也沒救了,
就死前能不能有所收獲

維度一:高風險/高範圍/高頻率:終止營業、暫停營運的等級
維度二:高風險/高範圍/低頻率:終止營業、暫停營運的等級

低風險/高範圍的情況,即使發生了仍有轉還的空間,高頻時處理的重點在縮小範圍與暫時的降噪
一但修正,理應停止降噪的設定,低頻時安排修正計劃即可

維度三:低風險/高範圍/高頻率:暫時的降噪(暫時停止通止,避免有其它告警被淹沒),查明原因,安排修正計劃
維度四:低風險/高範圍/低頻率:查明原因,安排修正計劃

在高風險的情況,如果有小範圍的異常,都應立即處理,重點會在能不能根除問題,或是緩解發生頻次

維度五:高風險/低範圍/高頻率:查明原因,根除問題,或是緩解發生頻次
維度六:高風險/低範圍/低頻率:查明原因,根除問題,或是緩解發生頻次

在低風險/低範圍的情況下,要小心避免讓人習慣這類型的錯誤(麻痺),或是產生過多雜訊影響判斷。
偶一為之但是不知道原因的話要特別小心標記,這很可能是別處發生問題的潛兆

維度七:低風險/低範圍/高頻率:查明原因,降噪或是設定提示水位
維度八:低風險/低範圍/低頻率:查明原因

小結

幾個重點,預防勝於治療,
真的發生時儘可能讓風險降低、範圍縮小、頻率減少,
十萬火急時可以先用暫解去處理,減少傷害。
但是回到正常流程就是要找出根本原因,並用正確的方式治療。

我作了個表以供參考

情境 風險 範圍 頻率 行動
可重現情況(Reproducible)
1 立即處理,確保不再發生。
2 立即處理,確保不再發生。
3 暫時降噪並制定修正計劃。
4 制定修正計劃。
5 根除問題或減少頻率。
6 根除問題或減少頻率。
7 降噪或設定提示水位。
8 不處理。
不可重現情況(Non-Reproducible)
9 查明原因,可能無法挽救。
10 查明原因,可能無法挽救。
11 暫時降噪,查明原因,制定修正計劃。
12 查明原因,制定修正計劃。
13 查明原因,根除問題或減少頻率。
14 查明原因,根除問題或減少頻率。
15 查明原因,降噪或設定提示水位。
16 查明原因,特別小心標記可能是其他問題的潛在預兆。

(fin)

[生活筆記] 一些程式轉職相關的問答

前情提要

一直有在作程式相關的助教、導師相關的工作,有些學生的問題很棒,也是我的盲點。
沒有心思好好整理,至少記錄下來,給未來的自已一絲反芻機會。

問題

Express.js在業界的實用性如何? 優點和缺點是什麼? 有需要再學習第二種語言/框架嗎?,建議的選擇是?

算常見的 Nodejs 的 Web 框架,優點就是夠主流,資源好找。
缺點就太簡單。

語言/框架可以多學,自已判斷。不知道就先跟著公司或社群走。

參考文章10 Best Nodejs Frameworks for App Development in 2023

Google 關鍵字: Nodejs Web Framework 2023

思路:不論什麼樣的程式語言,我的本質是 Web 開發,不同的語言與框架有不同的場景,總不缺乏新的挑戰者,
可以觀望、研究、測試、入坑,沒有一定的正確的答案,能夠快速作出判斷與取捨是重要的,但這需要經驗。

2. 如果想要在自有主機上部署Express.js,比較適合搭配的web伺服器軟體是?

這裡的”web伺服器軟體”是指什麼? Nginx 或 Apache 嗎?
選擇適合的場景就好。目前業界主流是 Nginx,思路參考第一題

3. 樣板引擎在業界的使用度高嗎? 有使用的話會是在什麼情況呢? 有比較主流的選擇嗎? 還是大部分被前端框架取代了?

註:學生這裡指的是 express + handlebars 的開發方式
大部份都前後分離端了,維護舊案可以能會碰到。
樣板引擎主要會發生在 Web Form 這類的舊專案,主流可能是 Php 或 Asp.Net 的某些專案。
或是在 2010 很主流的 MVC 開發方式,這段時間各家語言也有自已的解決方案。  

思路:理解為什麼會有前後端分離的發生,某些情況的小專案,樣板引擎的開發方式會比前後端分離有效率。

4. AC一個練習教案的規模,大概有幾十個到百個檔案要維護。實際業界專案的檔案數量應該更多,檔案間的關係複雜,維護人員要如何弄得清楚呢? 有沒有什麼比較好的管理工具?

檔案是指程式碼的部份嗎?  
學會架構分層,基本就三層式架構、MVC、MVVM、MVP、MV* 等,
使用 OOP 語言的話可能也要依 Pattern 分層,現在比較流行 DDD、六角架構等…
再大一點會是微服務的情況。

思路:想一下檔案間的關係複雜,那你會怎麼作?基本上就是分門別類,
好的分類可以讓錯誤發生時可以快速定位、實作功能時可以職責分離。
舉個生活化的例子,就是像是整理你的房間。

5. 在前後端的協作中,如何決定一項功能應由前端製作或後端製作? 舉例來說,要加上分頁的功能,本質上只是資料的整理重排,這樣的功能應該由前端或後端做呢?

Case by Case, 以我來說跟畫面有關的歸前端、其它歸後端。  
分頁通常會由後端處理、但是有些情況前端也要處理。
Ex: 避免太頻繁的 request 到 Server,一次性回傳大量資料,由前端自行製作分頁。

思路:還是以需求為依歸、預設一個簡單的原則。團隊合作的話,最理想是團隊共識,次之由 Leader 決定,不要發生由某端強勢主導的情況。
舉例說明,欄位檢查,前後端都要作,前端的目的是引導使用者正確操作,後端是真正的防護。

6. 求職網站中許多職缺要求會其他程式語言如: C# or PHP,會建議再去學其他語言嗎? 有推薦的學習平台或課程?

我會想學 TypeScript、Python。
程式在 web 開發上有類似的作法,更多領域其實也需要程式人才,
例如:手機 App 開發、IoT 開發、AI 工程師等…
建議找一個自已可以生存的領域,專研打磨裡面的技術會建議
社群活動可以用來搜集關鍵字,
Udemy 的課程很便宜,YT 也有很多大神的頻道可以學習。

思路:唯一的問題是學不完,怎麼整合應用變現,這個時候就不只是求職了,公司只是某一些技術棧整合的結果,
當你學的更多時,或許可以有別的出路?

7. 建議如何練習LeetCode? 才不會好像只是背答案,而不是懂背後的邏輯。有聽說 NeetCode 平台不錯用?不知道助教是否推薦

就是多刷多累績經驗,看題目、看答案。
兩個重點,把題目看懂、把答案看懂,試著把自已的思路轉化成程式
平台很多:NeetCodeLeetcodeHackerrank

思路:助教用 TDD 刷 LeetCode,實務上蠻常遇到面試造火箭、入職擰螺絲的狀態,
但是仍然不知道要學什麼或作什麼樣的 Side Project 時,刷題是一個好的選擇。

8. Junior工程師除了準備作品集,LeetCode 與程式語言的觀念外,通常面試還會考什麼嗎?

我主要會問架構與流程題,如果能把自已開發的架構與流程說明清楚,就可以同時觀察到技術能力與溝通能力。

思路:常常去面試就會知道現在業界常問什麼了。

10. 職場上在開發專案時,後端工程師的實作範圍會是全部一手包辦還是還會細分工作?或者工作架構會是怎麼分配的?

看公司,建議去有分工的公司,一手包辦很可能會很雷,學到的可能也是一堆大泥球的作法

12. 除了現有AC課程之外,助教建議Junior後端工程師還需要提升哪方面的技術

掌握好版控與協作流程/自動化與容器化技術/實用精實的開發實踐

  • Git、Docker、AWS/Azure/GCP、K8S
  • TDD (BDD、ATDD)/ Refactoring /Design Pattern

13. 目前軟體業有受到景氣影響而減少職缺數嗎或停止招聘

國外有一波大裁員,有影響到,但台灣本身還好
至少我還好

14. 請問助教平常寫程式會如何結合AI加速開發,有建議相關課程嗎

Github Copilot+TDD

15. 網站若有多國語言版本,技術上要怎麼實作呢?

關鍵字i18n

1
2
3
4
5
6
7
8
{
"tw":{
"login":"登入"
},
"en":{
"login":"Login"
}
}

16. 手機版和電腦版的網站要怎麼分割呢?

我知道的兩種作法

結語

部份題目涉及專案內容,所以排除掉。
但是有關從無到有開發一個專案,或是與人合作開發專案,是整個職涯中會很常發生的事。
現在不作,以後也會遇到,可以儘量去嚐試。

(fin)

[實作筆記] Websocket 初體驗

前情提要

tl;dr, websocket 至少是一個 10 年以上的技術了,但是筆者一直沒有機會實作它。
大部份的時候是應用場景不符合,或是簡單 Long Polling 已經足夠,甚至就算用 Polling 這個方法也無法把 Server 打掛。
另一個情況是團隊已經很成熟,有專門負責的部門在統一處理這塊邏輯,
而通常這塊邏輯會與主要的核心功能作切分,所以我也沒有機會接觸到。
這次難得有個小型的專案,有機會實作,故稍作記錄一下。

需求

使用者會停留在某些頁面等待系統的資料狀態更新,大約每 10 秒要作一次 Polling,
而平均資料狀態更新需要 3~10 分鐘不等。在這個情況下要改用 websocket,
(註:我不認為這是一個很好的應用場景,但是牽扯更多未揭露的調整故不展開討論,
EX:Heartbeat、Keep-Alive 等等…機制也未討論到)

WebSocket Server 實作

使用的技術:Typescript + Express + ServerSocket

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
// import library
import express, { Request, Response } from "express";
import { Server as ServerSocket } from "ws"; // 引用 Server

if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}
// form env 指定一個 port
const PORT: number = Number(process.env.WS_PORT);
// 設定斷開時間
const CT: number = 1000 * Number(process.env.CONNECTION_TIMEOUT);
const app = express();
const server = app.listen(PORT, () =>
console.log(
`[Server] Listening on http://localhost:${PORT} , timeout is ${CT} ms`
)
);
const wsServer = new ServerSocket({ server });

// Connection opened
wsServer.on("connection", (ws: WebSocket, req: Request) => {
// Connection 建立時發生的邏輯

// Listen for messages from client
ws.on("message", (data: string) => {
try {
// 發送 Message 時發生的邏輯
} catch (error) {
console.error("Invalid JSON format: ", data);
}
// Get clients who has connected
const clients = wsServer.clients;
// Use loop for sending messages to each client
clients.forEach((client) => {
client.send(JSON.stringify(docStatus));
});
});
// 設定 ping 時間間隔,用來讓連線太久的 Client 斷開
const interval = setInterval(() => {
if (ws.alive) {
ws.terminate();
clearInterval(interval);
return;
}

ws.alive = false;
ws.ping("", false, () => {
console.log("[Timeout]", ws.key);
});
}, CT); // 30 秒

// Connection closed
ws.on("close", () => {
// Connection 關閉時發生的邏輯
console.log("[Close connected]", ws.key);
});
});

// 新增一個路由處理器
app.get("/", (req: Request, res: Response) => {
// health and version check
res.send(`WebSocket Running Version ${process.env.WS_VERSION}`);
});

前端實作,收送雙向

使用的技術:JavaScript
以下內容大多參考於神 Q 超人的文章,
作為參考用,實務上會搭配使用的前端框架作修改。

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
var ws;

// 監聽 click 事件
document.querySelector("#connect")?.addEventListener("click", (e) => {
console.log("[click connect]");
connect();
});

document.querySelector("#disconnect")?.addEventListener("click", (e) => {
console.log("[click disconnect]");
disconnect();
});

document.querySelector("#sendBtn")?.addEventListener("click", (e) => {
const msg = document.querySelector("#sendMsg");
sendMessage(msg?.value);
});

function connect() {
// Create WebSocket connection
ws = new WebSocket("wss://localhost:8088");
// 在開啟連線時執行
ws.onopen = () => {
// Listen for messages from Server
ws.onmessage = (event) => {
// 收到訊息的 Logic
console.log(`[Message from server]:\n %c${event.data}`, "color: yellow");
};
};
}

function disconnect() {
ws.close();
// 在關閉連線時執行
ws.onclose = () => console.log("[close connection]");
}

// 監聽 click 事件
document.querySelector("#sendBtn")?.addEventListener("click", (e) => {
const msg = document.querySelector("#sendMsg");
sendMessage(msg?.value);
});

// Listen for messages from Server
function sendMessage(msg) {
// Send messages to Server
ws.send(msg);
console.log("[send message]", msg);
}

後端實作,只送不收

使用技術: php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include "./vendor/autoload.php";

$client = new WebSocket\Client("wss://localhost:8088");

$message = json_encode(array(
"your_property" => "Your Data",
"complex_property" => array(
"no" => 1,
"status" => "Hello word"
)
));

$client->text($message);
$client->close();
?>

後記

實務上在股票看盤即時更新、多人聊天室、多人網頁遊戲上或許十分有用,
但實際上在這此的案例上就有些大材小用了,先當作學習了。
在查找資料的過程有一段話很受用,
我稍作總結如下:
當討論到 WebSocket 時,不應用 Http 的標準去審視它,
更應該關注這些 Connection 會持續連接多久? Connection 之間交互的行為是什麼?  
是運算密集的行為還是讀寫密集的行為?…等等,才是你決策的關鍵。

參考

(fin)

[踩雷筆記] Gitlab CI 執行 Command 結果與本地環境不一致

前情提要

我們建立 CI 的一個目標是讓繁鎖的工作流程自動化,
而自動化的前題是標準化,而自動化的未來是可以規模化。
每一次的自動化,就像是在為工作流程添加柴火,讓未來的的每一步可以走的更穩更快。

問題

一直以來我認為只要在本機環境上可以執行的指令(command),就一定可以在 CI 中執行,
沒想到這次不一樣。

下面這個語法是我 CI 流程的一部份,主要的目的是要重啟 pm2 的服務

1
pm2 restart app.js

這段在 Gitlab-runner CI 的寫法如下,主要的目的就在 Gitlab-runner 中將指令送到指定的機器$VM

1
- ssh -i ~/.ssh/id_rsa gitlab-runner@$VM 'pm2 restart app.j'

不過我會收到錯誤訊息

1
bash: pm2: command not found

當我直接在$VM執行時,確又不會有錯誤

根本原因

原來在 Unix-like 系統中,shell 分為 Interactive Shell(互動式)和 Non-Interactive Shell(非互動式)。
兩者環境變數和 PATH 不同,導致在 CI 或遠程機器上執行命令可能與本地不一致

查詢了一下兩者的 PATH 如下

Interactive Shell

/home/gitlab-runner/.nvm/versions/node/v20.4.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

Non-Interactive Shell

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

明顯可見的差異在於NVM的路徑設定,這個 PATH 是在安裝 NVM 時被加上去的

解法

我們可以選擇重新 export NVM_DIR 這個作法

1
2
- ssh -i ~/.ssh/id_rsa gitlab-runner@$VM 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" &&
pm2 restart app.js

另一種作法會多上一層 shell,並再次執行互動式的 bash 我覺得較不易理解
不好維護,故不採用。

1
- ssh -i ~/.ssh/id_rsa gitlab-runner@$VM 'bash -i -l -c "pm2 restart app.js"'

參考

(fin)

[生活筆記] 2023 半年工作回顧

前情提要

這份是 2023 年, 3 至 6 月間,我在工作上獲得的成果,
以理性務實的態度推動各項工作,以下是在這段時間內所取得的重要成果:

敏捷觀念的引進與實踐

我引進了迭代開發要事優先的敏捷觀念,
同時建立了 PBI(產品待辦項目)與工作看板,以確保團隊的工作透明度
我們每日召開立會,及時更新資訊,並建立了文件系統,以便於知識的分享與傳承。

前後端分離的優化

我推動了前後端分離的工作方式,使專業得以適當分工,減少了彼此等待的時間。
這樣的優化使得服務可以被重複使用,避免了重複開發,節省了寶貴的時間與資源。

自動化的部署機制引入(CI/CD)

我們成功建立了自動化的部署機制,每次部署都節省了至少 10~30 分鐘的時間,
同時大幅減少了人工操作可能帶來的錯誤風險
這項優化為團隊帶來了高效率與更穩定的工作環境。
這是持續性的正面效益,假設每周部署 10 人次以上,以換算時薪為 600 計算,
每年每位 RD 約可以省下等值於 8 萬的工作時間。並大幅下降風險。

準時交付 N 系統,完成參展目標

我們順利地在新加坡、法國、台北和高雄四地參展,並提供了展場現場的支援,
這已超出我們本職專業的範疇。
但我們成功地達成目標,贏得了更多的曝光與商機。

帶回自主開發的 I 上鏈系統,省下外包費用

為了節省成本,我們將原本要外包的 I 上鏈系統帶回自主開發,從而節省了公司大筆的費用與時間。
這項舉措不僅讓我們獲得了更多的控制權,還提升了整體效率。
此項目,粗估至少擁有 300 萬以上的產值。

其他突破與調整

除了以上成果,我還做了以下貢獻:

  • 解決了公司 domain 數量達到上限的問題,通過與系統商的溝通,成功解除了限制。
  • 幫助查找了未關閉的 AWS 資源,避免了資源的浪費。
  • 調整了資安需求,提高了系統的安全性。
  • 協助調解了系統與需求之間的衝突,確保了順利推進工作。
  • 未來的發展計劃
  • 為了持續推動工作的進步,我擬定了以下計劃:

下一步

  • 將預計外包的 P 系統帶回自主開發,進一步節省公司費用。粗估其價值為 110~800 萬之間。
  • 積極招募人才,引進新血,提升團隊實力。
  • 推進單雲轉多雲的轉型,增加靈活性與彈性。
  • 針對資安問題進行調整,包括關閉公開 IP 和加密資料等措施。
  • 處理舊系統 L 的大泥球,逐步優化系統結構,提高整體效率。

結論

整體而言,在短短三個月內,接手的專案已為公司撙節了 150 萬 左右的支出,
而在自動化工具,用一個粗略評估的方式,假設每周每個 RD 會部署 5 次,
一年 52 周,一次部署可以省下 10 分鐘的情況下,目前公司 5 位 RD:
5*5*52*10= 13000 分鐘,大約可以省下 1.25 個人月,用來作更有意義的事。
而其它工作在長久看來也會持續帶來收益,整體而言,這三個月的產值已超過 500 萬
希望在未來的日子裡,能夠持續取得進步,為公司的發展做出更大的貢獻。

(fin)

[實作筆記] GitHub Actions:停用 save-state 和 set-output 命令的措施

前情提要

GitHub Actions 是一個強大的自動化工作流程工具,可讓您在 GitHub 存儲庫中執行各種自動化任務。
它允許您根據事件觸發工作流程,例如提交代碼或創建拉取請求。
您可以使用預設的操作或自定義操作來建立工作流程,並將其用於自動化測試、部署、持續集成等開發流程。
GitHub Actions 提供了一個靈活、可擴展和可自訂的方式來增強您的開發工作流程。

而我實務上的情境是用來寫 Blog,並進行自動部署,而我注意到了一個警告訊息。

1
Warning: The `set-output` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/

問題

在 Github 的官方 Blog 指出,自 CI Runner 版本 2.298.2 起,
如果用戶在使用 save-state 或 set-output 將開始發出警告。
並計劃在 2023 年 5 月 31 日完全禁用它們。
從 2023 年 6 月 1 日開始,使用 save-stateset-output 命令的工作流程將因錯誤而失敗。

補充

實際上根據 Github 觀測數據顯示,這些命令的使用率相當高。
考慮到受影響的客戶數量,而推遲了移除的時間。

我的情況

為什麼我會用到這值呢?看看我的 CD 部署檔

1
2
3
4
5
6
7
8
9
10
11
12
13
## Skip ...
step:
# Deploy hexo blog website.
- name: Deploy
id: deploy
uses: marsen/[email protected]
with:
deploy_key: ${{ secrets.DEPLOY_KEY }}
- name: Get the output
run: |
echo "${{ steps.deploy.outputs.notify }}"

## Skip ...

在我這裡有兩步驟,Deploy  與 Get the output
Deploy 直接用了我的另一專案進行部署,內部的工作簡單的說明如下:

  1. 用 Dockerfile 建立執行環境
  2. 拉取 Hexo Blog
  3. 執行相關指令 ex: hexo g
  4. 部署完成印出成功訊息

問題就出在這個第四步,在 Github Runner 啟動的 image 執行過程的輸出,
並不會顯示在 Action Steps 的 Output 資料之中。
所以在這個專案多作一步 Get the output 來印出這個訊息。

1
2
3
- name: Get the output
run: |
echo "${{ steps.deploy.outputs.notify }}"

可以知道我們的訊息是放在 steps.deploy.outputs.notify 之中,
而原始接出資料的方式如下,這個作法即將被逃汰了(為了避免無意間的資訊外洩)

1
echo ::set-output name=notify::"Deploy complate."

解決方式

如果官方 Blog 所說,你只要將 key-value 用以下的形式設定到環境變數 $GITHUB_OUTPUT 即可,

1
echo "{key}={value}" >> $GITHUB_OUTPUT

舉例來說:

1
2
result="Deploy complate."
echo "notify=$result" >> $GITHUB_OUTPUT

參考

(fin)

[踩雷筆記] 移除 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)

Please enable JavaScript to view the LikeCoin. :P