潮流特區

最新文章

概有雲供應商的K8S,為何要自己弄Docker Swarm / 本地K8S ?

科技新知
MacauYeah・2024-11-19

其實筆者寫了這麼多篇docker 的文章,可能有朋友會問,為何要自己從零建立Container環境,使用供應商直接提供的K8S服務不是很好嗎? 按照市場發展,各大雲供應商都越來多,競爭越嚟越激烈,作為用戶方,理應可以得到更合理的價格。不過作為使用VPS多年的筆者,真的沒有覺得雲服務的價格可以便宜到一個不用煩惱的水平,大家還是需要很㥀重地考量自己的業務是不是值得雲端化。 正常來講,在有足夠使用量的前提下,雲端化也是合適的,也真的有產到錢。但問題是大部份情況下公司內部自主開發的應用,都沒有去到這個程度。每個應用去租用一個VPS,即使使用最低配置,用起來的時候覺得不夠快,閒起來的時侯也是浪費錢。 這時,使用 Container 技術,就是讓多個不同的應用,共享同一個或多個VPS的好方法。因為 Container 可以簡易地做到應用之間的隔離,即使不同應用之間有依賴衝突,只要 Contianer 層面沒有衝突就可以共存。 Docker swarm 與 K8S 同為 container 技術,文章最前面,就提到了這個問題,為何不選現有的K8S,反而要自己弄Docker Swarm?其實關鍵亦是價錢的問題。使用K8S固然方便,但就每個節點都得使貴一級的雲端供應商服務,當我們的應用總是流量不足,就更易變得食之無味,棄之可惜。老實講,貴一級的雲端服務,有它存在的價值,很多東西可以做自動化擴展,例如概據流量自動擴容。另外,因為底層 Container 技術有供應商支援,也不用再另外購買支援服務。但這些都是業務有一定流量,才能展現出優勢。 反觀Docker Swarm,就是簡單可入手,初時一個VPS也可以。什至乎不上雲,找幾台舊電腦,實機做也可以。當然K8S也可以實機,不過就簡易程度來講,Docker Swarm 無得輸。待業務真正成長到一個有足夠流量的服務時,才進一步遷移到供應商的原生雲。在初期使用自建的Docker Swarm或小型K8S,可以先加入一些資源統計,以確定是否即裝滿負荷。

Swarm mode 上線 5 - load balancer | 還有那些事該考量?

科技新知
MacauYeah・2024-11-18

前面介紹了 ingress network ,亦介紹了 proxy gateway 。能做到的基本都做到了,再來就是考量安全性的問題。因為加了 proxy gateway ,前述的例子是所有 service ,都放在同一個 yaml 檔中。好處是,所有相關的東西存放在同一個檔中, gateway ,背後的 service 都一眼看到。但壞處就是有其中一個 service 更新,都要改那個 yaml 檔。更大的問題是, stack deploy 的指令,不單只更新其中一個 service ,就連其他 service 都會自動取得最新 image 而 redeploy 。 對於一個緊密的系統來講,同步更新可能不是大問題。但對於一些預定排程發佈的系統可不能這樣因為副作用而更新了。如果你也有這樣的分開管理需求,可以參考下面做法,把 gateway service 及 upstream service 放在不同的檔案中,然後經過 external network把所有 service 串連起來。 # nginx-stack.yaml, docker stack deploy -c nginx-stack.yaml nginx services: http-gateway: image: http-gateway ports: - 8080:8080 deploy: replicas: 1 update_config: delay: 10s restart_policy: condition: on-failure # manager-stack.yaml services: managerhttp: image: bretfisher/httpenv networks: - nginx_default - default deploy: replicas: 3 update_config: delay: 10s restart_policy: condition: on-failure placement: constraints: - node.labels.zone==manager networks: nginx_default: external: true # dmz-stack.yaml services: dmzhttp: image: bretfisher/httpenv networks: - nginx_default - default deploy: replicas: 2 update_config: delay: 10s restart_policy: condition: on-failure placement: constraints: - node.labels.zone==dmz networks: nginx_default: external: true 這樣,不同 service 的維護人員,就可以獨自控制自己的檔案。在第一次發佈時,確認 nginx-stack.yaml 先行發佈就可以了。對應的發佈指令是docker stack deploy -c nginx-stack.yaml nginx,它會自動産生一個 nginx_default (即 stack名字_default )的網絡。之後其他service,就可以經networks的設定找到它了。 services: YOUR_SERVICE: networks: - nginx_default - default networks: nginx_default: external: true 上述即使分離檔案,在安全性考量時還是有一個問題,就是 ingress network 的問題。試想一下,dmzhttp (Demilitarized Zone)原本被設定的原因,就是想限制某些訪問只能一些可以公開的服務。但因為經過 ingress network 之後,它們會在所有機器上開放這些 port。那就是,以下面的例子來講,若 dmzhttp 是公開的服務, intrahttp 是內部服務,即使用 intrahttp 使用不同的port 8889。但一經 swarm mode 預設的 ingress network ,在node.labels.zone==dmz的那些節點,還是可以訪問到 intrahttp 。 services: dmzhttp: image: bretfisher/httpenv ports: - 8888:8888 deploy: replicas: 2 update_config: delay: 10s restart_policy: condition: on-failure placement: constraints: - node.labels.zone==dmz intrahttp: image: bretfisher/httpenv ports: - 8889:8888 deploy: replicas: 3 update_config: delay: 10s restart_policy: condition: on-failure placement: constraints: - node.labels.zone==intra 我們前述介紹的 proxy gateway ,其實已經有一定程度可以解決這個問題。因為 proxy gateway 是根據 http 協定中的 host header 去做分流。在邊界網絡進來的「合法」訪問,道理上會好好地經引導到我們的 dmzhttp 。不過網路的邪惡可容小看, proxy gateway 也會有被騙的一日。有特定能力的攻擊者,只需找到目標域名,還是可以接觸到 intrahttp 。 若要做進一步隔離,在這種情況下,我們可以在 dmz , intra 機器中各設定一套 swarm ,完全獨立,這是最安全的做法。但這樣做的管理成本就會變高,因為兩個網段都會有自己的 manager 節點,而且在 dmz 網段的 manager 節點也有被攻擊的可能。 若我們回到單一 swarm 的方向,可以修改各個 service 中的 port 和 deploy 。利用 post mode 中的「host」,配合 deploy mode 中的「global」,完全跳開 ingress network。 services: dmzhttp: image: nginx ports: - target: 80 published: 8888 mode: host deploy: mode: global update_config: delay: 1s restart_policy: condition: any placement: constraints: - node.labels.zone==dmz intrahttp: image: bretfisher/httpenv ports: - target: 8888 published: 8888 mode: host deploy: mode: global update_config: delay: 10s restart_policy: condition: on-failure placement: constraints: - node.labels.zone==intra 上面的例子中, dmzhttp 會在所有 dmz 的機器中,每個節點只運行一份服務,而且直接使用該機的 8888 port ,外面不會再有 ingress network 的 存在。同樣地,intrahttp 會在 intra 的所有節點,運行一份服務,佔用它們的8888 。這兩個服務,即使使用一個 port ,swarm 也不會說有任何問題。因為它們不會經 ingress network 搶佔其他人的 8888。 可能會有讀者問,如果 host mode 這麼安全,為什麼預設會是 ingress network,那我們就要先了理清 ingress network 與 host mode 有有什麼分別?假設我們只運行一個service,它佔用8888。 功能ingress modehost mode replicas 數 同一個 service replicas 為任意數量,什至比節點的數目多 因為有 port 限制,每個節點最多只能運行一份 Virtual IP Virtual IP 任意在節點中跳轉也可以,因為 ingress 會自動找到對應的 service 所在的節點 Virtual IP必需要與 service 所在節點綁定,其他節點訪問不到 load balance 有 沒有 host mode 就像我們傳統在各自的節點上自行佈署自己的程序,各個節點只有一份。所以不會有自動 load balance 的效果,如果客戶端訪問固定的IP,就會得到是固定的接器接受請求。我們有需要,就要在前面加一個 Proxy Gateway (或 HA proxy )。 Virtual IP 也一樣, host mode 下需要好好地自動跟著 service 的生命期,不過幸運的是, Docker 預設己經有自動重啟 service 功能,即前文中的 restart_policy ,它在 host mode 下也適用。如果大家有配合 deploy 中的 global mode , Virtual IP 的並沒有實際變動。但如果沒有 global mode ,就要再想想辦法了。 最後考慮 load balance 的問題,如果進入點的 service 的真的不太消耗資源,沒有 load balance 也是可以的 ,但若超負荷,就必需要自建 proxy gateway 。經過進入點後,若我有背後的 service 就沒有所謂的 ingress 和 host mode 選擇。

Swarm mode 上線 5 - load balancer | proxy gateway 代理伺服器

科技新知
MacauYeah・2024-11-11

前面的例子,我們已經成功設定 ingress Network,也加了 virtual ip 。如果大家的目標是單一 web 應用,應該就已經很足夠。但作為一個足夠節儉的老闆,怎會讓一個 Swarm 只跑一個 Web 應用?但問題來了,一個 docker swarm service 就已經佔用一個公開端口 (例如上述的8888,或是更常見的443)。怎麼可以做到多個 service 分享同一個端口?答案就是回到傳統的 Web Server 當中,使用它們的 virtual host 及 proxy 功能,以達到這一效果。我們就以 Nginx 為例,去建立一個守門口的網關 (gateway) 。 以下就是一個最簡單的例子,最前端的 http-gateway (nginx) 對外公開端口 8080 ,它根據 virtual host,去分派對應的請求去 dmzhttp (bretfisher/httpenv) 及 managerhttp (bretfisher/httpenv) 。構架圖就是以下這樣。 ┌───────────┐ ┌──────────────►│ dmzhttp │ │ └───────────┘ │ ┌───────────────┐ │ http-gateway │ ────────►│ (nginx:8080) │ └──┬────────────┘ │ │ ┌─────────────┐ └─────────────►│ managerhttp │ └─────────────┘ 換成 docker stack ,就大概如下 services: http-gateway: image: http-gateway ports: - 8080:8080 deploy: replicas: 1 update_config: delay: 10s restart_policy: condition: on-failure dmzhttp: image: bretfisher/httpenv deploy: replicas: 2 update_config: delay: 10s restart_policy: condition: on-failure managerhttp: image: bretfisher/httpenv deploy: replicas: 3 update_config: delay: 10s restart_policy: condition: on-failure docker stack有一個很好的功能,就是 service 名會自動成為同一段網絡中的 hostname 。即是http-gateway中,它可以經DNS,找到 dmzhttp 、 managerhttp,也就是它的 nginx 可以設定成如下的樣子。 # default.conf server { listen 8080; listen [::]:8080; server_name managerhttp; resolver 127.0.0.11 valid=30s; location ^~ / { set $upstream_manager managerhttp; proxy_cache off; proxy_pass http://$upstream_manager:8888$request_uri; } } server { listen 8080; listen [::]:8080; server_name dmzhttp; resolver 127.0.0.11 valid=30s; location ^~ / { set $upstream_dmz dmzhttp; proxy_cache off; proxy_pass http://$upstream_dmz:8888$request_uri; } } 上面的例子中,就是一般的 virtual host + nginx proxy 設定。特別要說明的是 resolver 那一行,它指向 docker DNS (127.0.0.11), 而且還可以讓nginx在找不到上游時,不要馬上死亡。這樣 docker swarm 中各個 service 隨時加加減減,有保命的作用。 最後我們的 http-gateway 就是 nginx image + default.conf 上述的 docker 就可以用以下方式打包。 # Dockerfile # docker image build -t http-gateway ./ FROM nginx:latest COPY default.conf /etc/nginx/conf.d/default.conf 上面的 docker stack 和 nginx config,只要同步增加 service 及對應的 proxy pass,就可以o讓同一個端口,根據不同hostname做分流。當然,如果大家可以共用端口及 hostname 也可以,分流就改用 nginx location 來設定,不過這是更加偏向 nginx 的內容,日後有機會再介紹。本篇就先集中於 docker 相關的議題。 在安全性的角度, docker 還有一些配置可以做,就是讓 dmzhttp 和 managerhttp 在不同的機器上發佈。假設我們的網絡分開兩段,一段是 manager 專用,一段是 dmz 專用。在建立 docker swarm 後,我們可以為不同的節點加入對應的標簽。 docker node update --label-add zone=manager YOUR_MANAGER_NODE docker node update --label-add zone=dmz YOUR_DMZ_NODE 然後我們通過修改 docker stakc 中的 placement -> constraints ,限制不同的 service 在不同的節點上運行。 services: http-gateway: image: http-gateway ports: - 8080:8080 deploy: replicas: 1 update_config: delay: 10s restart_policy: condition: on-failure dmzhttp: image: bretfisher/httpenv deploy: replicas: 2 update_config: delay: 10s restart_policy: condition: on-failure + placement: + constraints: + - node.labels.zone==dmz managerhttp: image: bretfisher/httpenv deploy: replicas: 3 update_config: delay: 10s restart_policy: condition: on-failure + placement: + constraints: + - node.labels.zone==manager 使用上面的例子,我們就可以達到簡單分離的效果。但大家緊記,這個分離效果始終是一個規則式功能,它與防火牆的隔離還是有本質上的區別。除了利用傳統的防火牆技術外,我們的docker swarm network,其實也可以做更多隔離,我們日後再慢慢加強這個例子。

Git 歴史修正

科技新知
MacauYeah・2024-10-29

有時候,我們修正一系統檔案,例如某個commit中,多了一個不該放的檔案,又或者想修改該commit的作者,我們就要追搜到某個commit,然後用rebase隨個改。 例如本次repo,有一個githubAction.md,因為錯誤原因,被加到了main中,也藏了很久。如果我們想連根拔起,我們需要加出它第一次出現的commit。 $ git log githubAction.md commit 60ccd70f6b768138cbe23c93ffcfa32574ce895c 那我們就以它前一個commit作為rebase的根據,進行逐個commit修正。 $ git rebase -i 60ccd70f6b768138cbe23c93ffcfa32574ce895c^ pick 60ccd70 draft some content pick e2ee9a3 add some senario. pick b91afc1 refine submodule; pick 98cd366 add notes about submodule specific checkout; pick 064b06f test directly commit in submodule main pick 7b648d2 update git submodules notes pick 556f25e add notes about merge timing pick 5244804 Create git-continuous-integration-strategy.md pick 107e486 add more pratical nodes about ci; pick d93cbee add mono repo challenge pick 1c471b6 add worktree notes pick 9063ccb notes about different of git flow and github flow; pick b72e89e Update github-flow.md, add ref more link pick 0b8f2a9 draft github flow release problem pick 8b333fc finalize github flow release strategy 在rabase選項中,把需要改的commit由pick改為edit。(rebase會以舊到新顯示)。然後儲存。例如 edit 60ccd70 draft some content edit e2ee9a3 add some senario. edit b91afc1 refine submodule; pick 98cd366 add notes about submodule specific checkout; pick 064b06f test directly commit in submodule main pick 7b648d2 update git submodules notes pick 556f25e add notes about merge timing pick 5244804 Create git-continuous-integration-strategy.md pick 107e486 add more pratical nodes about ci; pick d93cbee add mono repo challenge pick 1c471b6 add worktree notes pick 9063ccb notes about different of git flow and github flow; pick b72e89e Update github-flow.md, add ref more link pick 0b8f2a9 draft github flow release problem pick 8b333fc finalize github flow release strategy 我們第一次會在60ccd70,我們作出想要的改動,然後經amend去改掉60ccd70 $ rm githubAction.md $ git add -u $ git commit --amend --author="newuser " 確定無誤的話,就可以去下一步,即是到了e2ee9a3 $ git rebase --continue 因為已經rebase過,你此時看到的不會再是hash不再是e2ee9a3,而是自動rebase完的e2ee9a3。若大家有東西要改,就使用commit --amend。如果沒有東西要改,也沒有conflict,可以繼續rebase --continue下去。

Swarm mode 上線 5 - load balancer | 負載平衡器

科技新知
MacauYeah・2024-10-28

前面我們一直談 swarm 的設定,但對於真實的服務,我們還要考慮客戶端是如何連接我們的伺服器群集。通常網路服務,客戶端都會經過域名轉換成IP,然而通過IP連線服務。 Ingress Network 假設我們 swarm 內有5個節點,那到底域名應該指向我們哪一個節點的 IP 呢? 如果我們不考慮節點死機的話,其實5個節點的IP都可以。因為 swarm 會自動把同一個公開的 port ,在每一個節點上都可以訪問到。 以下例子,即使只有一個 container 運行,佔用 port 8888,它還是會在5個節點上全開。 swarm 通過自己的 ingress network,它所有節點的 8888 串連起來。 services: http: image: bretfisher/httpenv ports: - 8888:8888 deploy: replicas: 1 update_config: delay: 10s restart_policy: condition: on-failure 我們可以在每個節點上,都會找到這個 ingress network,而且那個Network ID,應該是一樣的 > docker network ls | grep ingress t7rmk6g9zybm ingress overlay swarm 如果上述的 service 的 replicas 調成大於1的數量, ingress network 還會方便地自動 round robin (輪替) 地分派流量,達到最簡單的負載平衡。 Virtual IP 前述的設定,我們有一最大的假設,就是節點不會死機。但實際情況下,各種原因,例如安全性更新、重啟中,都會讓節點暫時無法使用。即使所有 service 都是會自動 failover (故障轉移),但客戶端還是用舊機 IP ,它還是無法訪問。因為該機 IP 已無法使用,除非我們連 IP 也懂 failover。這時, Virtual IP 就是我們的救命靈藥。 在 ubuntu 上,我們可以經過 keepalived 去設定 Virtual IP apt-get update && apt-get install keepalived -y 然後設定 keepalived , 假設 172.22.1.5 是我們的 Virtual IP 。 然後每個節點都要加入conf # vim /etc/keepalived/keepalived.conf # assume failover ip is 172.22.1.5 vrrp_instance VI_1 { # change interface according to machine status interface eth1 state MASTER # 101 for node1, 102 for node2 # you can start seq from other value, remind unqiue for each node is ok; virtual_router_id 101 # lower value will become master # ex, node1 priority 100, node2 priority 200, node3 priority 150. # if node 1, 2, 3 alive, node2 will become master. # if node 2 gone, node 3 will become master. priority 100 advert_int 1 authentication { auth_type PASS auth_pass YOUR_RANDOM_PASSWORD } virtual_ipaddress { 172.22.1.5 } } 上述需要特別注意的是 virtual_router_id : 每個節點應該都要不一樣,以作唯一標識。 priority : 每個節點應該都要不一樣,最大的那個節點,就會優先使用 Virtual IP 。 auth_pass : 每個節點都相同,但大家在抄時,記得更改。 還有的是開通 iptables ,讓各個節點可以經網絡廣播的方式互相看到對方。 iptables -I INPUT -d 224.0.0.0/8 -j ACCEPT iptables -I INPUT -p vrrp -j ACCEPT systemctl restart keepalived

Docker Tag 命名

科技新知
MacauYeah・2024-10-24

一般來講,同一個docker image會提供多個不同的版本,每個版本會附予不同的tag,以作標識。但以docker image的維護者來講,它的tag通常代表的是自己程式的版本號。不過這個版本號卻存在很多變數,就讓筆者好好地逐一說明。 程式的版本號 在沒有Docker的年代,其實所有軟件在發佈時,都會標示版本號,方便使用方明確追蹤問題,自行選擇升級、降級以解決相容性問題。大家要重現問題,也能清清楚地重現。所以docker image的tag,在某程度,都是代表發佈自己的程式版本號。但以前的年代,軟件底層的依賴,例如OS層面的共享程式庫,則不在發佈的管控中,所以過去的程式,在跨電腦安裝時,都會出現缺少某些共享庫的問題。而使用了Docker後,image以內的共享庫的都會在打包的那一刻固定和發佈,就不會有漏的問題。 庫更新,怎麼辦 上面說到image可以打包共享庫,但問題是共享庫也會有安全性更新問題,那麼對docker image的維護者來講,它自己的tag又該如何命名? 因為庫的量可大可少,所以一般來說,都不可能完全把各個庫的版本號寫在自己的tag上。退而求其次,就是用"版本號+日期",庫的細版本號,就存在原始碼當中。Ubuntu 就是這樣的例子。 不過"版本號+日期"的命名方式真的方便嗎?每次下遊用戶想更新去最近版本,都要自己找一次最近的日期。這樣對很多用戶來講都不夠方便。所以docker又提供了一個重tag的功能。例如ubuntu:noble,在早些時候指著noble-20240904.1,然後過幾天,又指向更新的noble-20241009。更常見的是latest,每次image都預設會存在,docker也希望大家會定期更新這個tag,讓大家可以更易地找到最新版本。 註: 這跟git tag有所不同,git tag並不預期會變的。當協作者收到tag後,那怕上遊刻意更新tag指針,協作者沒有刪除原tag之前,都不會知道tag更新去了哪裏。 我們該如何選 在發佈方和引用方來講,引用時可以明確使用唯一的"版本號+日期",對穩定性來講是有意義的。不過多多少少,會產生額外的時間成本。發佈方來說,就是多用了一些儲存空間,方便引用方可以隨時找到舊(庫)版本。而引用方,就要手動修改引用號,作為驗收依據,自動更新的難度比較大。 但對於自動更新要求比較大的情況下,可能就是使用latest或者會隨時更新的share tag(共用tag)比較實際。但我們也依然要定一些方式去版本更新記錄,例如:同時使用 beta latest archive 每日自動更新beta,只有所有測試都通過時,才把archive指向現在的latest,再把latest指向現在的beta。這樣做的好處是,核心的docker stack檔案改變的機會較少,也可以免除docker swarm做太細緻的權限管理。

Tmux - 繼 Screen以後的Linux多工神器

科技新知
MacauYeah・2024-10-08

因為各硬件/軟件的發難,筆者又不得不回到那個只有純純linux tty console的世界。很多時候,那怕使用tty,我們在Desktop mode,也有現代terminal 可以用,需要多分頁,滑鼠選取文字、複制、貼上,都可以輕易做到。 但在mobile / tablet device 上,手指操作真的很不方便。又或者你像筆者一樣,即使有電腦,但要操作一些Linux VM,它們連ssh都沒有,只能直接登入它們的tty,那麼懂得使用Tmux進行分頁及複制、貼上,就變得很重要。 Tmux 是什麼? Tmux 就是可以在Linux Terminal 同一個窗口中,實現多工處理的小程式。就像我們利用多分頁一樣,不同分頁做不同的事。不過最大的差異就是,生成分頁,排列分頁,我們都要使用鍵盤來完成。有時筆者也會用它來作為背景程式,以免不小心關了Terminal就會把所有運行中的指令都停掉。 我們就馬上來看實際例子吧 前置事項: 安裝Tmux及運行Tmux Debain & Ubuntu 安裝: sudo apt-get update && sudo apt-get install tmux 運行:tmux 進入tmux後,你就會至少有一個分頁,而且不會因為Terminal關閘而中斷 用法一: 建立兩個分頁,並切換 增加分頁: 先按 “Ctrl + b” (前置鍵),再按”c” (create) 切換分頁: 在多於一個分頁的情況下,先按 “Ctrl + b” (前置鍵),再按”n” (next) 用法二: 同一個分頁中,建立左右並排的窗口 增加水平窗口: 先按 “Ctrl + b” (前置鍵),再按 “ (雙引號) 切換窗口: 在多於一個窗口的情況下,先按 “Ctrl + b” (前置鍵),再按方向鍵左或右 用法三: 回到前一個tmux session中 因為不小必關閉了terminal,又或是remote ssh中,ssh斷線後,需要回到之前的工作狀態 未進入tmux 的狀態下:tmux attach 要留意tmux 可以有很多個session,要去到指定的session,就要為session命名。但這個不是筆者常用的情境,原本多個分頁已經很夠用,還要多個session,會很混亂。但不排除它在某些情況下有特別用途,有興趣的朋友可以自行挖挖看。 進階: 回頭看過去的terminal screen output 在現代的Terminal中,原本按滑鼠滑輪向上滾,就可以看到過去的資訊,但tmux下反而不行,所以我們需要進入特殊模式 進入Copy Mode: 先按 “Ctrl + b” (前置鍵),再按 [ (開括號中括號) 向上翻頁: 上方向鍵或PageUp 離開Copy Mode: Copy Mode中任何時候按”q” 進階: 複制貼上 進入Copy Mode: 先按 “Ctrl + b” (前置鍵),再按 [ (開括號中括號) 選擇範圍: 移到需要複制的文字起點,“Ctrl + Space” ,然後再移動到結束點,再按”Ctrl + w” 複制 貼上: 離開Copy Mode後,再按”Ctrl + b” ,然後 ] (關括號中括號) 進行貼上 進階: 複制貼上2 某些情況下,我們不允許使用“Ctrl + Space” 或 ”Ctrl + w”,因為它們可能跟系統的組合鍵有衝突,所以需要改為單鍵。 讓tmux使用類似vim的操作模式: echo “set-window-option -g mode-keys vi” >> ~/.tmux.conf 關掉所有使用中的tmux,重開tmux 進入Copy Mode: 先按 “Ctrl + b” (前置鍵),再按 [ (開括號中括號) 選擇範圍: 移到需要複制的文字起點,按單鍵“Space” ,然後再移動到結束點,再按”Enter” 複制 貼上: 離開Copy Mode後,再按”Ctrl + b” ,然後 ] (關括號中括號) 進行貼上 筆者常用的功能就這些,有興趣的朋友可以再深挖一下。 Reference https://tmuxcheatsheet.com/

Ceph Storage 水很深

科技新知
MacauYeah・2024-09-25

筆者不才,早前為大家介紹了一篇關於Ceph Storage的最入門安裝教學。但在後續測試中,發現了一些概念上的問題,需要盡早說明,不然就會像筆者一樣,要砍掉重來很多次。 OSD HDD Ceph Storage的主要功能,就是為Contiainer提供外置儲存空間,它對儲存空間有特定的要求。我們最好在建立ceph cluster(cephadm bootstrap)之前,就為每個node上增加合適的HDD 引述官方說明: OSD (Object Storage Daemons) The device must have no partitions. The device must not have any LVM state. The device must not be mounted. The device must not contain a file system. The device must not contain a Ceph BlueStore OSD. The device must be larger than 5 GB. 簡而言之,大家需要準備新的HDD,不要做任何格式化,讓OS見到HDD但不作任何操作。筆者試過,使用hyper-v VM + hyper-v HDD,也是可以做到的。不過之前筆者於教學中用的 multipass 就沒有這個模擬HDD功能,我們需要使用比較強大的VM作為實驗。 若然HDD是在ceph cluster(cephadm bootstrap)建立之前,就存在的。我們可以經過ceph的網頁介面,或經指令自動加入。 ceph orch apply osd --all-available-devices 若然HDD是在ceph cluster(cephadm bootstrap)建立之後,才加入的。那麼ceph有機會沒法自動發現它,筆者當前的dev版本就出現這問題。我們就需要經指令手動增加 ceph orch daemon add osd NODENAME:/dev/sdb OSD 官方說明文件 https://docs.ceph.com/en/reef/cephadm/services/osd/#cephadm-deploy-osds Reset 在我們做實驗時,若我們想回復到上一個狀態,測試不同的參數差異,Ceph指令並不會即時執行。例如前一句的add osd,想倒回來自行刪掉一些osd,即 ceph orch osd rm OSDID 它就會排隊慢慢做刪除。 但這個過程筆者未有成功過,OSD一直處於繁忙狀態。有機會是因為系統需要保持同步狀態,待成功遷移資料前,什麼都不能動,所以一直都在待刪除的狀態中。 同樣地,當我們想要刪除一些node時,我們使用以下指令 ceph orch host drain NODENAME ceph orch host rm NODENAME 最後也是會卡在刪除OSD的情況 Removing Hosts 官方說明文件 https://docs.ceph.com/en/reef/cephadm/host-management/ Static IP 因為 container 技術,很多都需要固定 IP ,我們做實驗之前,最好先了解你的VM engine如果提供Static ip 。以 hyper-v 建立的 VM ,其實可以同時建立兩張網卡的,一張為預設網卡,用於連網用,另一張則設定為內部網絡。在安裝 ceph 時,經 cephadm bootstrap 所引用的IP則設定為內部網絡的IP。之後基本上使用任何一張網卡的 ip ,也可以訪問到cephadm的網頁介面。如果不是在一開始的階段上準備Static IP ,我們又會在重設/解綁cluster時,同樣因為機器繁忙而卡在不上不下的狀況。

純文字圖案 | 懶人出圖工具

科技新知
MacauYeah・2024-09-20

早前,筆者就介紹過 Markdown / mdbook 等說明檔編寫工具,也分享過用於制於遊戲攻略時,如何加上插圖的情況。那怕是教學、說明、遊戲攻略,使用圖表的方式表達,的確有助於讀者理解。 在Markdown的技術上,圖文並茂是可以的,只是不太方便而已。以制作及修改的成本來講,出【圖】可能都不算最難,更麻煩的是管理。 怎樣教對?點開圖檔整個閱讀?。怎樣搜尋圖片,可以加附註嗎?更新後名字該怎樣取? 老實講,如果可以,有些【圖】,直接經文字轉譯成圖表就最好。 mermaid Markdown 轉成圖,其實坊間早就有一些免費的工具,筆者選擇了 Github 也預設支援的 mermaid 。廢話不多說,直接送出 web 版的編輯工具。 https://mermaid.live/ mermaid 官網 使用它的好處 Github markdown 直接支援,mdbook經插件也可以使用。 易於編寫,也易於閱讀 有支援IT其他範疇的圖表,例如ER,State。 有支援更多其他範疇的圖表,例如gantt,mindmap。 使用它的問題 不支援手動調整位置,全部靠自動調整 ascii chart 若想要更多的位置掌控,其實我們可以回到過去BBS的年代,用文字方塊來砌圖。這個方法很有局限,但也不是完全不能用。 廢話不多說,直接送出 web 版的編輯工具。 https://asciiflow.com/#/ https://kirilllive.github.io/ASCII_Art_Paint/ascii_paint.html 可以選擇中文字符 使用它的好處 任意手繪圖表 使用它的問題 使用中文等字元,還要考慮是否等寛字型問題。 修改文字長度,邊界要重畫。 筆者有試過用來制作遊戲簡略地圖說明,這是比不斷截圖來要得更直觀。但限制就是不要在圖中加入文字,加入英數等符號就算了,再於其他地方加以解釋。如果我們必需在圖中使文字,我們就要控制輸出字型為等寛字型,例如使用【細明體】,就無問題了。不然就要全部使用中文字元(全型符號),不要混合英數。 .-=. .:*%%+.:. :#%%%%%+#. :#%%%%%%%%%%*. .*%%%%%%%%%%%%%%%*+-.. .-%%%%%%%%%%%%%%%%%%%%%%*:. .-%%%%%%%%%%%%%%%%%%%%%%%%%%-. .=%%%%%%*=-::::::::::::::-=#%%#: :#%%*-:::::::::::::::::::::::-*%+. .*#=-:::::::::::--=========-::::-#+. .=#==-::::-=*%%%%%%%%%%%%%%%%%%=::-#-. .#*==--#%%%%%%%%%%%%%%%#..#%%%%%+::**. :#+==#%%%=.+%%%%%%%%%#-:**::+%%%#-:=--*= .-+#*=+%%#.=%+.+%%%%%%%#--====*%%%#-::::-*. :*====+%%%%#.-%%%%%%%%%%%%-:+%%%%%*::::-*: .*=====#%%%%%%%%%####%%%%%%%%%%%%*::-%+. .:##==#%%%%+-:::::::::::--===-::::##. .##====-::::::::::::=#-:::::::-##. .*%*====---==+**#*+=::::::::+%+. .-#%#+====-:-----::::::-+%%#:. :#%%%%#*+===---+*%%%%%*. ..:=#%%%%%%%%%%%%%+:. ..::---:...

為何Python這麼熱門?

科技新知
MacauYeah・2024-08-27

在資料處理、資料科學領域,什麼是最近的AI模型,Python都是做這些事的熱門選擇。對於以前從未用過Python來處理業務的筆者來講,實在不懂為何Python會那麼大熱。不過最近,筆者實戰過後,真心覺得它是提高生產力的重要工具,而且並不限於資料科學上面,一些簡單的腳本操作也是很有優勢的。 筆者前述有討論過 型別對程式語言的重要性,到現時這一刻,筆者都會覺得【型別】是有助於長期的程式開發。而Python這個語言,大部份人都會介紹它是動態語言,可以使用弱型別,然後,就沒有其他講法了。動態弱型別,筆者一直都不認為它的根本上的原因。就像Javascript一樣,它亦發展出類靜態強型別的Typescript版本,而且它亦不因此而被人棄用。所以Python的強大,動態語言並不一最重要的原因,它也可以模疑寫出有規有距的type hinting。 或者用另一個方向問,大家覺得 Excel / SpreadSheet 好用嗎?它們可以很簡易地做出資料計算、篩選。而且可以一邊做,一邊調整公式。例如要大家做一個陣列的總和,大家會想打開一個Javascript,初始化陣列的每個數字,然後寫個For迴圈去計算總和嗎?還是打開 Excel / SpreadSheet,打下一欄或一列的數字,然後叫出Sum函數?筆者一定會選擇後者,不單止因為寫函數比較方便,那怕之後要調整數字,也比較方便。 大家有感受到差異嗎?筆者想表達的是,在操作 Excel / SpreadSheet 我們並不是整個程式重新執行一次,我們是修改完一部份,那上看到結果。但傳統的語言,例如C、Java、那怕是Javascript,我們都難以局部地更新或執行特定某一個區塊。那怕是現在我們有hot reload,但其實我們編寫的思維,都是讓我們完整執行起一個頁面,再人手輸入,看結果。如果我們只想運行某個單一Function函數,我們只能寫test case測試,但寫test case又是一個很大的入門門檻。 但大家如果看看Python,在古早的年代,Python已經有Python shell,那就像是Linux Shell或Window CMD一樣,可以一邊寫腳本,一邊看結果。寫了10行的程式,發現在第10行引用第5行的部份有問題,修正並執行第5行後,就可以回來馬上重跑第10行的語句,就馬上有結果了。第6至9行,因為沒有關聯性,就不需要逐一重新執行,那是多麼的方便阿。道理上,我們若沒有完整執行整個程式,可能還是有一些盲點,開發重要的,需要長期維護的程式,還是要像傳統一樣,有test case,有程式進入點,整個運行。但對於臨時性的操作,看看效果,我們實在無必要寫一個原整程式。 舉個例子,假如我們臨時有需要,要取得某個政府網站的即時數據,例如澳門的停車場資訊,空位的上下限是多少,我們絕對可以用python寫幾行就取得結果,然後順便做個資料運算。我們沒有必要很嚴僅地為考慮不同數據的出現情況,我們什至可以hard code - 硬編碼地計算某個Array的元素。直到突然有一天,這個操作變得恆常化,我們還是有條件把之前的python程式碼,改寫成一個規規矩矩的完整腳本,包括異常處理,函數複用。(其實Javascript在改用 NodeJs 作為引擎後,我們還是可以經過 Node.js REPL,來做互動操作,只是Python Shell出現得更早,也是官方支援的功能。) Python這個臨時操作的便利,對於資訊爆炸的年代來講,實在很幫得上忙。再加上現在除了Python Shell以前,還有Jupter Notebook,讓大家可以在Web頁面上,執行像Python Shell的互動操作,對於修過特定區域的程式碼,就更加方便。這些便利,都是不是因為動態語言來創造的優勢,而是實實在在的Coding Anywhere。

Github flow 沒有提及的發佈 - 佈署 | Release - Deployment

科技新知
MacauYeah・2024-08-23

不知道之前為大家介紹的github flow,大家覺得怎樣?好用嗎?今天,筆者又來講講筆者心中認為它沒有好好給出指引的地方。 我們的信心指數,其實沒有那麼高 在前文中,經過 pull request 、 code review 、 auto test ,道理上,開發者可以做的都已經做過了,然後就是等待發佈 - Release。 對於單純的庫類型的程式碼,筆者認為,的確沒有事可以再做,實務上就是直接找人其他程多員試用最新版本,看看有沒有問題。只要 main / master 上,明確的表示版本號的變更,就差不多等於直接發佈。有需要提供binary版本的,就還需要觸發上載binary的流程,但這個跟 pull request 觸發 auto test 差不多, auto test 成功後就上載。 對於服務類型的程式碼,例如 Web App 等,直接發佈到正式環境還是有些不妥吧?始終會即時影響到業務,我們至少有個測試場,經用戶做實際的業務操作去驗收。但這個時機,應該是在Github flow的什麼時候做? 在原始的git flow中,有一個叫做 develop 的相對穩定分支,僅次於 main 。它是功能開發完成後第一次pull request 的地方,我們可以用這個概念來做自動發佈到測試場。但若在github flow 中加入了這個 develop / uat / staging 分支,其實就等於複雜地回到過去傳統的 git flow中,對好多新手來講難以接受。Github flow 的成功簡化,其實很大依賴著自動化測試。現在的測試用例,並不再限於單元測試。就連整合測試,也可以經Docker等容器化技術去做,只要我們的自動化測試有足夠信心,就可以發佈。但反觀我們的 Web App 例子,我們認為自動化測試難似涵蓋所有情境,也難以開發。所以我們還在有個時間發佈到測試場,進行人工測試。 pull request + 快速迭代 筆者結合自己的經驗,配上國外討論區 Stack overflow 的內容,筆者認為Github flow上進行 pull request 後,就是最好的發佈測試場時機。所以我們需要盡快進行驗收測試,完成後在Git commit上加上Tag,以示通過驗收測試,可以發佈正式環境的版本。 不過這個模式是有一個很重要的前題假設:快速迭代。當我們驗收完成後,盡可能快地發佈到正式環境,不然會阻礙下一個功能的pull request驗收,或是覆蓋了上一個pull request的驗收環境。 用反面的例子來說明,如果我們有很多功能需要驗收,或變化很多,或存在多輪的里程碑開發,我們就不適宜那上述模式。最保險的做法,還是回到傳統的 git flow ,引入 develop / uat / staging 分支。但如果大家還是那麼討厭傳統 git flow,筆者還是有另一個提議。 既不想回到傳統 git flow ,但又需要慬㥀的考慮驗收發佈流程 如果開發的功能變化比較大,需要多方面協調、測試、驗收,經歷多次里程碑後,才有一個對外發佈的版本,大家可以考慮分開 Repository 做開發。例如: v1,v2的 Repository 完全獨立。 v1 是已發佈的版本,有獨立的測試場,任何即時候需要修正,就在v1的 Repository 做 pull request。 v2 則是未發佈版本,亦有獨立的測試場。加入任何新功能後,就在v2的 Repository 做 pull request,用自己專用的測試場做驗收。到 v2 正式發佈後, v1 就封存處理,再開一個 v3 作為下一個大版本的開發。這個模式,那怕在庫類型的程式碼也用得上。 這樣做的好處是 git Repository 和歷史記錄都會獨立,自動發佈的腳本程也會簡單明確一些。壞處則是 v1 v2 難以做功能對比,我們只能靠人腦記著 v1 有沒有什麼後期加入的修正和功能,需要同步移植到 v2 中 (相對的,著是同一個Repository,可以利用merge 功能確保 v1 有的,v2 都己處理,只是必需要很懂處理版本衝突問題。

Swarm Mode 上線番外篇:Ceph

科技新知
MacauYeah・2024-08-20

在預設Docker和K8s的容器主導世界裏面,其實一直都缺少了直觀的儲存空間。當你的程序需要讀寫故定的來源資料,該來源就必需是外部的穩定儲存空間,例如是資料庫、NFS。但資料庫、NFS等,要做到真的正穩定,其實就要走Cluster(叢集)模式,確保它們自己本身不是做成single point of failure (單點故障)的元兇。 坊間,只要付得起錢,其實找個穩定的資料庫或NFS,也是有的。但如果你像筆者一樣,只有一塊或多塊【鐵】,就要試試開源的儲存引擎Ceph Storage。 Ceph Storage,有自己特有的CephFS格式,但也支援NFS https://docs.ceph.com/en/quincy/cephadm/install/。也就是,只要我們有足夠多人力,道理上可以自己用實體機去模擬一個穩定的NFS。 因為只是試裝,筆者暫時只用VM來測試,完整的安裝script,可以在這裏找到。script使用Multipass VM,大家有條件的話,可以使用其他VM引擎來看重複。以下是一些官網上沒有提的重點: Ubuntu 24.04 還未能正式使用。在筆者做POC的當是,Ceph v18 在 Ubuntu 24.04上需要先解決,即使大家使用Curl base下載 binary,也未必能成功。 筆者成功測的版本是 Ubuntu 22.04 + Ceph v17,全使用Ubuntu 發佈的內置版本。但大家也要留意自己的Ubuntu apt 有沒有更新到最新版,過去的 cephadm,引用的container image url也變更。記得更新到v17 的最新版,cephadm 指令才能成功取得image。 在官方說明文件的【Deploying a new Ceph cluster】中的【Adding Hosts】https://docs.ceph.com/en/reef/cephadm/install/#adding-hosts 節章可能有些誤導,大家應該要看 【Host Management】中的【Adding Hosts】 https://docs.ceph.com/en/reef/cephadm/host-management/#cephadm-adding-hosts 在每個節點內,可以直觀地連接地Ceph Dashboard,但若大家需要Port Forword,要注意你的Network Interface,筆者就只能經過預設的IPv4的public ip 進行ssh port forward,不能經過0.0.0.0。 Script 位置 https://github.com/macauyeah/ubuntuPackerImage/blob/main/initCephCluster.sh