潮流特區

焦點文章

CyberCom3合1充電數據線 — 一條線搞掂晒,充電超方便!

科技新知
Cheers!・2025-06-12

而家大家手上嘅電子產品多到數唔清,手機、平板、藍牙耳機、遊戲機……每部機都有自己嘅充電線,仲有啲好舊款用USB,iPhone用Lightning,最新又係Type-C,搞到充電時成日手忙腳亂,尤其去旅行,帶幾多條線都唔夠! 呢條 CyberCom3合1充電數據線,就係為咗解決呢個煩惱而生。佢集合多種接口於一身,無論你係用緊iPhone定Android,甚至其他電子裝置,都可以用一條線搞掂晒,方便又慳位,真係你嘅生活充電好幫手! 一條線,多種接口,無懼設備多 CyberCom3合1充電數據線支援 Micro USB、USB A、Type C 同 Lightning 四大接口,無論你係iPhone、Samsung定係其他品牌,甚至係藍牙耳機、遊戲機,都可以用呢條線充電同傳輸數據。旅行唔使再帶一大堆線,行李又有位可以擺多啲戰利品啦! 快充快傳,效率up up! • 支援QC快充技術,最高60W輸出,手機、平板、智能手錶都可以超快充滿,唔使等成日。 • 傳輸速度高達480Mbps,無論係工作文件定係娛樂影片,一link就傳,節省你寶貴時間。
 耐用又防纏繞,攜帶超方便 • 採用彈性TPE物料,線身唔易打結,收納方便,唔怕亂晒。 • 線材耐用又有彈性,日常用或者出街旅行都好啱用。
 產品小百科 • 1米長度,無論係屋企、公司定旅行都啱用。 • 建議零售價 $99,依家優惠價$79就可以帶走。
 由此開始,充電更快更簡單! 設計貼心,性能強勁,係你工作同生活嘅好拍擋。無論係手機、平板、遊戲機定藍牙耳機,呢條線都幫你搞掂晒充電同數據傳輸,令你嘅數碼生活更輕鬆自在。了解CyberCom3合1充電數據線嘅詳情同優惠,幫你打造更智能、更方便嘅充電新體驗!購買網站: www.cyberportcom.com查詢電郵: info@cyberportcom.com

最新文章

CyberCom3合1充電數據線 — 一條線搞掂晒,充電超方便!

科技新知
Cheers!・2025-06-12

而家大家手上嘅電子產品多到數唔清,手機、平板、藍牙耳機、遊戲機……每部機都有自己嘅充電線,仲有啲好舊款用USB,iPhone用Lightning,最新又係Type-C,搞到充電時成日手忙腳亂,尤其去旅行,帶幾多條線都唔夠! 呢條 CyberCom3合1充電數據線,就係為咗解決呢個煩惱而生。佢集合多種接口於一身,無論你係用緊iPhone定Android,甚至其他電子裝置,都可以用一條線搞掂晒,方便又慳位,真係你嘅生活充電好幫手! 一條線,多種接口,無懼設備多 CyberCom3合1充電數據線支援 Micro USB、USB A、Type C 同 Lightning 四大接口,無論你係iPhone、Samsung定係其他品牌,甚至係藍牙耳機、遊戲機,都可以用呢條線充電同傳輸數據。旅行唔使再帶一大堆線,行李又有位可以擺多啲戰利品啦! 快充快傳,效率up up! • 支援QC快充技術,最高60W輸出,手機、平板、智能手錶都可以超快充滿,唔使等成日。 • 傳輸速度高達480Mbps,無論係工作文件定係娛樂影片,一link就傳,節省你寶貴時間。
 耐用又防纏繞,攜帶超方便 • 採用彈性TPE物料,線身唔易打結,收納方便,唔怕亂晒。 • 線材耐用又有彈性,日常用或者出街旅行都好啱用。
 產品小百科 • 1米長度,無論係屋企、公司定旅行都啱用。 • 建議零售價 $99,依家優惠價$79就可以帶走。
 由此開始,充電更快更簡單! 設計貼心,性能強勁,係你工作同生活嘅好拍擋。無論係手機、平板、遊戲機定藍牙耳機,呢條線都幫你搞掂晒充電同數據傳輸,令你嘅數碼生活更輕鬆自在。了解CyberCom3合1充電數據線嘅詳情同優惠,幫你打造更智能、更方便嘅充電新體驗!購買網站: www.cyberportcom.com查詢電郵: info@cyberportcom.com

Virtual IP | 為客戶端提供的流動IP

科技新知
MacauYeah・2025-06-11

Virtual IP 雖然我們Docker Swarm、Galera等服務可以很容易地提供到Cluster的功能。但以用戶來講,怎樣知道該連線去那台伺服器,又是另一個問題。用戶不可能逐台伺服器逐台IP去訪問。通常,大家會以為在Cluster服務外部,加個 Load Balancer(負載均衡器)就已經可以解決問題。但其實Load Balancer本身也需要做Cluster,其中一個掛了,別的也需要頂上。那麼用戶到底是怎樣訪問伺服器的? 我們簡單地,可以經過 Virtual IP (簡稱VIP) 來解決這件事。即是把我們網絡服務的域名,綁到VIP上,然後這個VIP可以在不同伺服器上游走,只要有一台伺服器活著,都可以回應這個VIP的請求。而這個VIP的功能,可以經keepalived簡單地做到。 配置 假設我們的配置如下 node 1 IP: 192.168.0.2, network interface: eth1 node 2 IP: 192.168.0.3, network interface: eth1 node 3 IP: 192.168.0.4, network interface: eth1 virtual IP: 192.168.0.5 每個node,都有自己的IP,而virtual IP只會附在其中一台機上。 如果在 GaleraCluster 的情況下,可以看成只有virtual IP剛好附在其上的那台機工作,即是以 active passive 的方式運作。 如果在 Docker Swarm 的情況下,在預設模式下就已經有的mesh IP的機制,即使用virtual IP只在其中一台機上運作,但ingress networks都會擴散到所有機器上,所以是active active的方式運作。 設定 Keepalived 在三個node上,都各自安裝 keepalived。以下以 ubuntu 24.04 為例 # ubuntu 24.04 apt-get update && apt-get install keepalived -y node 1 的 keepalived 設定 # vim /etc/keepalived/keepalived.conf vrrp_instance VI_1 { # change interface according to machine status interface eth1 # one node is MASTER, other nodes are BACKUP state MASTER # all nodes in same group must be same value virtual_router_id 101 # higher 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 # VRRP Advert interval in seconds (e.g. 0.92) (use default) advert_int 1 virtual_ipaddress { 192.168.0.5 } } node 2 的 keepalived 設定 # vim /etc/keepalived/keepalived.conf vrrp_instance VI_1 { # change interface according to machine status interface eth1 # one node is MASTER, other nodes are BACKUP state BACKUP # all nodes in same group must be same value virtual_router_id 101 # higher value will become master priority 99 # VRRP Advert interval in seconds (e.g. 0.92) (use default) advert_int 1 virtual_ipaddress { 192.168.0.5 } } node 3 的 keepalived 設定 # vim /etc/keepalived/keepalived.conf vrrp_instance VI_1 { # change interface according to machine status interface eth1 # one node is MASTER, other nodes are BACKUP state BACKUP # all nodes in same group must be same value virtual_router_id 101 # higher value will become master priority 98 # VRRP Advert interval in seconds (e.g. 0.92) (use default) advert_int 1 virtual_ipaddress { 192.168.0.5 } } 然後在各node上執行。 systemctl restart keepalived 上述設定中,除了 priority 外,應該所有 config 都一樣。state指的是初始化狀態,在 priority 不為255的情況下,即使設定 MASTER 或 BACKUP ,也會動態改變。又因為 priority 不是動態改變的,為方便管理及人眼辨識,筆者認為priority最高的那個node應該預設為MASTER,可以減少日後發生誤會。 如果一切正常的話,192.168.0.5只會出現在node1上。當node1掛了,192.168.0.5才會出現在node2。當node1、node2同時掛了,192.168.0.5才會出現在node3上。這個VIP,同一時間只會出現當時活著的機器中,priority最高的那一台。priority 最高的那一台,它的狀態為MASTER。這些狀態,我們可以經以下指令確認 # confirm state systemctl status keepalived # confirm ip ip a | grep 192.168.0.5 Keepalived 可能的異常 如果 Keepalived 之間無法溝通,每個node都自認為MASTER,192.168.0.5會同時出現在所有node上。這個情況下,網絡請求還是可能的,但當真正出現 failover (故障轉移)時,因為 ARP (Address Resolution Protocol) 等問題,路徑可能無法那上跳到活著的機器上,通常要等個十幾秒才會恢復。在前述的設定中, advert_int 就是各node溝通的時間間隔,以秒為單位。正常若果只有一個MASTER的話,failover可以在一至兩秒內完成。 造成 keepalived 無法溝通的原因很多,其中一個就為設定上的失誤,筆者初期就試過誤設定 virtual_router_id 。在有需要溝通的機器中,應該設定為相同的值。另一個原因則是防火牆,所幸的是 ubuntu 24.04 中, iptables 預設就接受它們之間的連線。如果是其他 Linux 版本,遇到無法溝通的情況,所以先關掉 iptables 服務,或者把 iptables 上的所有 rule 刪掉再試試。

不用Multipass,自動化還有什麼選擇?

科技新知
MacauYeah・2025-05-28

因為multipass 升級同時轉換driver的關係,很久之前筆者介紹的multipass static ip 慢慢開始失效。如果大家只是為了做lab,雖然multipass預設的不是fix ip,但它的dhcp ip並不常更換,在multipass上起VM還是有一定優勢。 但若大家在更大的環境下,不可能有類似multipass exec 的型式去下指令,又或者,我們本地也沒有足資源做VM,必需使用公有雲,我們還有其他可以自動化的方法嗎? 有的。那就最初的ssh。 假設在公有雲,開了三台Linux VM,要作為聯機實驗用。我們只需要再一台Linux跳板機(可以是cloud VM或是local Mac / Linux,就可以順序以ssh為三台VM下指令。我們不需要開三台terminal,在不同VM之間切換,我們是直接在跳板機下指令,也就在跳板機上,實現自動化為三台機進行一系列的設定。 即是如果之前可以經multipass exec 完成的自動化,只要不涉及重置網絡操作,道理上也可以經ssh 實現。例如筆者之前的docker init可以這樣改寫 # local multipass exec -n NODE_NAME -- docker swarm init # remote ssh USERNAME@NODE_NAME -- docker swarm init 抄檔案也可以改寫 # local multipass transfer SOME_SCRIPT_FILE NODE_NAME:. # remote scp SOME_SCRIPT_FILE NODE_NAME:. 也因為公有雲或某些公司網絡,我們什少可以改變它的網絡設定,我們基本只可以使用預留的IP進行設定。不過也因為這樣,我們什少再作出重置網絡的操作。 但大家還是要留意,如果要真順暢ssh或scp,需要預先綁定ssh key。這些預先綁定ssh key的功能,一般在各大的public cloud都會有。如果沒有,我們也可以自動化開始之前,先使用ssh-copy-id為所有VM加入ssh key,這邊筆者就不再重複敍述。 參考資料 https://www.cyberciti.biz/faq/what-does-double-dash-mean-in-ssh-command/

Galera 4 (Mariadb cluster) on ubuntu 24

科技新知
MacauYeah・2025-04-28

前述我們一直在介紹docker cluster,但docker也不是萬能的。某些依賴HDD的程式,而且檔案權限相對有要求的程式,例如:資料庫,用docker去接入共享的HDD (mout share storage),反而更麻煩。一來需要程式本身支援,二來要修改官方docker的初始化程序,過程相關折騰。所以有這方法需求的,都可以先考慮原本的VM做成Cluster。 本文就介紹一下傳統的Mariadb 做成Cluster的方式。老實講,Mariadb 官方手冊可能因為要適配各個不同的OS品牌,並沒有提供一個平台完整的安裝流程。最後筆者也是轉向一些非官方的網絡教學,才成功設定。 Galera 4 (Mariadb cluster) on ubuntu 24 https://www.linode.com/docs/guides/how-to-set-up-mariadb-galera-clusters-on-ubuntu-2204/ 筆者參考上述文章,配合自己測試的結果,以下簡介一下在Ubuntu 24.04的安裝過程 準備3台VM,假設它們的IP為 192.168.0.2, 192.168.0.3, 192.168.0.4 ,確保它們之間的網路可以互通,每一台機都執行以下的安裝script NODE1 192.168.0.2 修改/etc/mysql/mariadb.conf.d/60-galera.cnf, 留意 wsrep_node_address, wsrep_node_name 部份,要與本機相同。 設定好後,我們可以關掉mariadb,經galera 新起cluster的方式叫起它,然後經sql 在內部查看現時成功有加到cluster的機器數量。 應該要看到數量為1 NODE2 192.168.0.3 在node2,跟進述一樣,修改 /etc/mysql/mariadb.conf.d/60-galera.cnf,記得wsrep_node_address, wsrep_node_name要換成新機的值。 設定好後,就重啟mariadb,順便看看現時成功有加到cluster的機器數量。應該要看到數量為2 NODE3 192.168.0.4 在node3,跟進述一樣,設定值筆者就省略了。我們可以在測試一次真實的改動,是否可以同步到其他node。 我們先試在node3加入新的資料庫test1,然後在node2查看是否存在。 node2應該是可以找到test1的,不然就要經過查wsrep_cluster_size看看node3是否成功接入。 然後我們再在node2試試修改root的密碼,看看會不會同步到其他node。 最後node1, node3都需要使用新的password才能登入。 當一切都如預期,你的Mariadb Galera cluster就成功了。

Coding Anywhere: 依賴服務的選擇

科技新知
MacauYeah・2025-04-22

年多前,筆者購入steamdeck, 經過一輪軟件定制,把它變成一個可以作為IT從業員開發機的方案,也介紹了一些coding anywhere的想法 https://lifemag.cyberctm.com/zh_TW/blog/macauyeah/14175/Coding Anywhere 工作方案 https://lifemag.cyberctm.com/zh_TW/blog/macauyeah/14352/Steam OS 3.5更新,內建 podman, distrobox https://lifemag.cyberctm.com/zh_TW/blog/macauyeah/14149/開發者在Steamdeck上的另一個選擇: Gnome box 在試驗了一年多後,筆者對於依賴服務的模疑,又有另一層感受。什麼是依賴服務?就像你寫的程式庫,可能需要資料庫儲存、可能需要問AI等等。所以在開發時,都要確保這些服務的存在。一般,要麼就是在本機上自行安裝,要麼就是經過互聯網使用雲服務(public cloud或者你團隊提供的private cloud),也就是本地模擬還是互聯網模擬。 本地模擬的得失 本地模擬,主要是考慮金錢上的優勢與資源的獨立性。 金錢成本 - 互聯網資源大部份都不會是免費的,如果本機的硬件足夠,可以在本地完全模疑,有一定上的優勢。但如果該服務在本地安裝,都要計授權,可能不沒有太大差異,例如那些report engine, report designer,即使本地開發都要逐台開發機計算。但其他大部份,如資源庫的實現,都有本地開發免費授權。所以本地安裝道理上有一定的成本優勢。 資源獨立性 - 當一個團隊共用一些互聯網服務時,可能會互相干援。即使團隊在開發時,可以經profile使用不同的資源,但發生誤用的情況還是很常見。(除非大家已經有一套很健全的開發用profile,只在本機生效,亦只在必要時才會被提升到程式碼的版本控制當中,不會誤會地覆蓋他人,也不會忘了提交。但這是很有挑戰的一件事)。反觀本地模擬,因為那些服務並不會在團隊中分享,就保證不會被誤用。 學習成本高 - 本地模擬,就有一個莫大的痛點,就是學習成本高。我們可以找到很多本也安裝資料庫的教學,本地LLM AI的架設也不少。但我們並不是很輕易地就可以無師自通,有時為了初次安裝,所花的時間成本也大得令人卻步。 coding anywhere轉移成本高 - 因為全部本地模疑,代表我們必需要有一台足夠強大的主機。但如果我們的移動接入點,綁定了在某台特定的強大主機,我們活動空間也相對減少。 互聯網模擬的得失 直接使用互聯網的服務,主要體現於用錢解決問題的優勢 即開即用 - 能用現成的就用現成的。例如你目標是使用mysql cloud database,就直接伸請使用。如果你還要在本地安裝或使用Cloud VM安裝,就還要自行安裝管理介面等工具。因為成本問題,實在要自行安裝,使用cloud vm也有一定的方便性。使用cloud vm 有一定的快取,可以減少安裝所需要的時間。當我們養成自動化的習慣,clould VM 也可以隨時刪掉,有需要才重起。 解決單機無法模擬的情況 - 某性依賴,並不能簡單地經過本地單一部主機去做到。例如我們要模擬一些叢集功能。我們可能要在主機或網絡設備作出一定的調整,才可能提供bridge network。這一點在辦公室網絡下限制更多,不是隨便就可以建一個可以互通,又可以訪問互聯網的環境。另一些如block storage等資源,還會對硬件有一定的要求,也不是軟件模擬就可以做到。我們若不經過互聯網取得,至少也要在團隊下的private cloud上去建立。(不過如果是從零自建private cloud環境,初次投入的成本可能直接使用public cloud 低。 ) coding anywhere轉移成進一步下降 - 作為移動接入點,就剩下那些不可互聯網化的部份,例如domain name,有時還是localhost比較方便,又例如有一些硬件相關開發,硬件部份必需經過本地接入。 就以筆者的個人經驗來講,除非public cloud的價錢實在不可接受又或是自動化幾乎不可能,否則使用public cloud會有時間成本上的絕對優勢。如果要走本機模擬方向,必需要對Container、VM、網絡等有深刻的了解,才會成事。

Swarm mode 上線 6 - 2| 升級陷阱

科技新知
MacauYeah・2025-04-14

上一期筆者就介紹寺過swarm的相容性,可以任意地刪除其中node、加入新的node,系統會自動同步各機狀態。今日,我們就來討論一下加減的流程吧。 實戰輪調流程 假設我們有5個 node,都為manager,各個 docker 版本都為28.0.4 ,我們將要關掉node 5 (ubuntu 22),並加入node 6 (ubuntu24),輪調流程如下 如果node5有vvip,login node 5,關掉vvip systemctl stop keepalived login node1, 把node5降為drain模式,變為worker,並從群集中刪除 docker node update --availability drain node5 docker node demote node5 若然node5不是直接關機、刪除,只想好好地離開群集,可以 login node5, 在node5上預先執行 docker swarm leave docker node rm --force node5 如果之前node5有好好地離開群集,而且狀態已經轉為down,那麼就不用"force"了,用最保守的刪除指令就可以 docker node rm node5 login node1, 取得manager token docker swarm join-token manager node5關機,新增node6,使用相容的ip段,或者使用node5的ip login node6, 加入群集,設定vvip docker swarm join --token xxxx XX_IP:XX_PORT systemctl stop keepalived 這陷阱這陷阱 偏我遇上 上述的操作,有一些可能的陷阱,筆者就剛好踩過,未來不知道會不會有官方保證 docker的版本需要相同,不同版本可能不能加入群集,例如 docker 28.0.4 不能加到 docker 27.5.1。 docker 27.2.x 不能加到 docker 27.5.1。 docker swarm,官方雖然宣稱支援不同版本共存,但這指的是已加入的node,在不解綁的情況下原機升級。 在swarm已有多版本共存的情況下,有一個node選擇完全脫離,它想再加入,也是會失敗的。可能這不是docker自身的限制,而是底層library的相容性問題。筆者在實測不同版本時,就得到這樣的error。docker credentials: cannot check peer: missing selected ALPN property

Swarm mode 上線 6 | OS升級前的準備

科技新知
MacauYeah・2025-04-08

如果大家一直有跟進安全更新,基本上每個一至兩個月,都會有OS kernel和Docker Engine Update。也許大家習慣還是以不變應萬變,但有些時候還是不可避免地遇到嚴重漏動,需要強制更新。那麼當我們在這個情況時,我們該如何做呢?在開始做之前,我們先測試一下Swarm的容錯率有多高。 官方就宣稱,只要swarm中,manager例下的數目沒有超過半數,就依然可以運行。這個部份,筆者相信大家一早就感受過。但筆者認為,在直正出意外的情況是,少於半數的manager倒下了,但其餘的manager又不幸要重啟,到底又些活著的manager,又可否成功重新啟動?所以下面就來做些測試。 測試1 測試REPO 初始化script initDockerCluster.sh 筆者在原本的教學中,就有一個3個manager node + 2 worker node的範例,我們只要安裝ubuntu OS, packer, 並使用 # run setupMultipassWithFixIP.sh will install multipass and config fix ip ./setupMultipassWithFixIP.sh # install packer, please refer to https://github.com/macauyeah/ubuntuPackerImage/blob/main/README.md packer init template.pkr.hcl packer build template.pkr.hcl # initialize docker cluster in multipass ./initDockerCluster.sh 在上述的環境中,node21, node22, node23是manager, node24, node25則是worker。在全關的情況下,只要正常啟動兩台manager,worker就可以成功復活。 測試2 測試REPO 初始化script initDockerClusterLoopJoin.sh 筆者寫了另一個起始方案,會有5個manager node,而且依賴順序如下 node22 => node21 node23 => node22 node24 => node23 node25 => node24 node21 => node22 # initialize docker cluster in multipass ./initDockerClusterLoopJoin.sh 在上述的環境中,5台機也是manager。 initDockerClusterLoopJoin 的前半部份,它會建立順序依賴,即每一台機器,都經前一台機器進行加入的動作。而後半部份會把node21刪掉,並經multipass用一個全新的guest os重起,重新加入到。 在全關的情況下,只要正常啟動三台manager,它們都可以繼續運行。 這個測試的例子表明,即使原本作為依賴的機器死了,只要群齊中其餘多數manager仍然存在,它們也是可以復活的。更重要的是,即使最初引領一切的node21死掉了,什至是被刪掉重來,也是相安無事的。 結論 更新時,最保守的做法,是先加入新的manager,再除去舊有的manager。但這個做法下,manager的IP就不可避免地被改變。若然DNS或者防火牆沒有相應的自動化幫忙,先加入再替換就變得很痛苦了。 而上述的測試,其實代表了我們可以先暫停或移除舊有的manager,更新完後再接回去,這樣部份IP就可以重用。我們只要維持多於原本半數的manager活著,然後逐一替換或升級原有的機器,也不會有問題。即使在升級途中,其他manager不幸地斷線,重啟後它們還是有條件自行修復。我們也不需要顧及更新順序,只需想好Virtual IP的分配策略就足夠,其餘就像是單機升級一樣。

Docker 中的非管理員用户 Docker non-root user

科技新知
MacauYeah・2025-03-14

Container USER為何重要 在制作Docker Image的過程中,有時會接觸到 USER 這個設定。這事關到最後的 Docker Container內部運行的那個 user 到底會有什麼權限。大家也要知道,Docker Container 其實也只是一個 Linux 上的程序,也就是如果Container內權限過大,也有機會從 Container 內部存取到 Host上的資料。 一般情況下,Docker Image 預設的 USER 就是 root,最基礎的base image都是一樣。而我們想換,其實也相當簡單,就像Linux上起User一樣,只要經指令RUN adduser xxx 或RUN useradd xxx 也可以在 Docker Image 中創建帳號和 home 資料夾,之後就隨時經USER xxx來切換 實際上是不是這麼簡單? 如果你將要Container中執行的程序,是一個binary,平常你在Linux中也是以 non-root 方式執行,那麼是的,就是那麼簡單。例如你執行系統中的java, node, python,原本在Linux中就已經是誰都可以,那麼你的docker container 也應該沒有難度。 但如果原本的安裝包,預設是由system service來啟動,我們就要花點力氣,看看那個service是怎樣呼叫binary的,然後就一步一步模擬它的做法。例如筆者有打包的codeserver,預設是system service啟動,但它也有提共binary的執行方法,安定好home資料夾後,我們也可以手動啟動。 泛生之檔案權限問題 上述binary的情境之所以簡單,是因為大部份情況下,我們都只對於container 內部運行考慮即可,因為預設投產情況下的運作模式,都是隨時起、隨時刪、隨時砍掉重練,只要container內部運作可以自給自足,就可以了。Docker Swarm的運作也是如此,所以它不預期有的持久化資料權限的問題。 而持久化資料權限的問題,其實早在單個Linux伺服器就已經存在。同一個伺服器中,不同process就有不同的UID,當他們需要共同讀/寫某些檔案,就會設定多人權限。同理,當多個Container要共同檔案,也是同樣問題。在討論共享檔案之前,我們先看看預設 Docker Storage Mount 會給我們什麼權限。 如果是bind mount,bind mount的權限預設會是Host內的檔案或者資料夾的權限。 如果Host是root,container內是non-root,container有機會無法讀寫bind mount內的檔案。 留意權限設置就可以解決問題 如果Host是non-root,但container 內是root,從container內生成的檔案,Host的non-root user就無法使用。 Host是non-root的話就一定無解,Host至少有sudo權限,臨時變成管理員,去修正問題。 如果host和container也是non-root,但UID不夾,其實也不能交換使用。 跟上述一樣,最後要靠sudo來解決問題。 如果host和container也是root,就沒有權限問題,但就有安全性的風險。 如果是volume mount,就還是看看 mount path 是docker image layer中現有的 path還是新起的path 大部份手動建立的named volume都是root 經docker compose起的named volume滿足以下條件的話,將會是non-root。 docker image 中的已有該path存在。 named volume未存在,docker compose會把對應path的內容在初次建立時抄到named volume 中。 例如ubuntu:24.04中的/home/ubuntu,存在於docker image中,它的擁有者就是UID 1000,我們經docker compose HOME_VOLUME:/home/ubuntu,在HOME_VOLUME建立時,就會是UID 1000。但如果是 NOT_EXISTS:/home/ubuntu/somethingNotExists,那麼NOT_EXISTS建立時,也會是root 上述討論的Storage mount是集中在單機情況下,使用HOST OS的本地儲存。若現在的場境是多機共享的share storage,就會更麻煩,還要看看那個share storage本身的屬性。例如常見的Linux NFS,其實有指定的權限,跟NFS的Login權限有關,如果你的process本身對檔案權限很敏感,就請先不要挑戰NFS(例如postgresql)。 Rootless mode - Rootless 模式 Rootless 模式指的是在Host中,執行Container的使用者,不需要是管理員,筆者就常用於開發環境中。投產環境中反而沒有聽過這樣的討論,因為投產環境很少可以讓非管理員去執行這麼重要的環境管理。 雖然只是開發環境,但這像前述的bind mount討論中,如果Host是non-root,但container 內是root,又或是兩者non-root,但UID不夾,也會出現權限問題。無腦的將host user加入docker group,只可以讓非管理員可以運行docker,但解決不了權限問題。 真正有條件解決的,可能就會向linux subgroup的方式發展。暫時筆者用得比較順的rootless mode,可以無腦用的,不是docker,是podman。有興趣的朋友可以經podman官網看看教學,它給筆者的感覺就像是自動轉換UID。 podman rootless mode 想看更多 筆者已經將過去的文章重新整理成gitbook,有興趣睇更多的讀者,可以來筆者的gitbook再翻一翻 https://macauyeah.github.io/AProgrammerPrepares/

Spring Boot 08 - 多情境設置 maven profile 與 application.properties 進階篇

科技新知
MacauYeah・2025-03-11

上期我們介紹完最直觀的用法,這期我們再來討論多管齊下的方向。 在開始之前,筆者總結一下上期的 Profile 的要點。 Spring boot 是經過 spring.profiles.active 去選擇什麼 (spring boot) Profile 生效 spring.profiles.active 它可以在runtime(運行時)動態更改 maven 是經過 xml 去選擇編譯時的 (maven) profile maven 編譯時為 spring.profiles.active 填入一個固定值 另外,筆者亦在測試途中,發現一個現像。 maven 並不提供混合 profile,即使下指令同時觸發兩個 profile ,最後亦只有一個 maven profile 生效。但這個部份筆者未在官方文件中找到,大家如果有任何發現,可以幫忙修正。 Spring boot 混合 Profile 當我們經IDE編譯時,可以為 spring.profiles.active 填入多個值,各值之間用逗號分隔,就可以觸發多個 profile 。 spring.profiles.active=dev,uat 程式碼中的application.properties, application-dev.properties, application-uat.properties 都會生效 Spring boot會先後載入上述三個檔案,如果有重複值,後面出現的會覆蓋前面的值。 spring.profiles.active如果填入的值與現在的application-xxx.properties不匹配,該部份不生效,例如 spring.profiles.active=dev,uat 程式碼中只有application.properties, application-dev.properties,但沒有application-uat.properties Spring boot會先後載入上述兩個檔案 上述的都好理解,當大家都接受上面的結論後,再來看這個現像。 spring.profiles.active 是啟動spring boot時,作為選擇profile的依據。 application.properties可以有一個預設的spring.profiles.active,正常跑spring boot就會看它。 正常跑spring boot時,還可以通過傳入參數--spring.profiles.active=xx,改變那個值。 Spring boot test 因為結構特殊,它只會看到 application.properties 中的那個spring.profiles.active值。 Spring boot test 暫時沒有方法傳入參數spring.profiles.active,但可以經程式碼 @ActiveProfiles 硬改運行中的 profile 。spring.profiles.active亦只會顯示 application.properties中的那個值。 Spring boot 混合 Profile 例子 大家看完概念之後,可以來看看實際例子。 當什麼都不加,就是根據application.properties的spring.profiles.active來啟動profile。 mvn clean compile spring-boot:run # or mvn clean compile package java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar 正常spring-boot:run的情況下,可以經的 --spring.profiles.active 覆蓋過application.properties內的值。 mvn clean compile spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=dev --spring.profiles.active=uat" mvn clean compile spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=dev,uat" # or mvn clean compile package java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --spring.profiles.active=uat java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev,uat 上述例子,若dev,uat內的值沒有衝突,沒有覆蓋問題。但如果有衝突,最後會是uat內定義的值。 Spring boot test Profile 例子 因為不是正常spring-boot:run,所以那些參數都沒有用,具體只會看application.properties內預設spring.profiles.active mvn clean compile test -Dspring-boot.run.arguments="--spring.profiles.active=dev,uat" # arguments will be ignored, same as mvn clean compile test Maven Profile 例子 加入Maven之後,就可以修改application.properties內的預設spring.profiles.active。但要注意,maven只會有單profile 假設pom.xml如下 application.properties如下 spring.profiles.active=@active.profile@ 下述三組例子,有且只有uat生效。因為maven的uat生效後,會修改 mvn clean compile spring-boot:run -Puat # or mvn clean compile package -Pdev -Dci=true java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar # or mvn clean compile test -Puat 當然,你想要弄一個maven mix profile 也可以 以下例子可以令 dev, uat 同時出現在spring.profiles.active mvn clean compile spring-boot:run -Pmix # or mvn clean compile package -Pmix java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar # or mvn clean compile test -Pmix Maven Profile Spring boot test例子 上述例子都了解後,最後就來看看全部混合的情況 當Test case中沒有硬改 profile 定義,application.properties中的spring.profiles.active就直接作用。以下情況就是同時運行dev,uat // java @SpringBootTest class ProfileTests { } // bash mvn clean compile test -Pmix 當Test case中有定義@ActiveProfiles ,application.properties中的spring.profiles.active的值會保留,但不在該test case中生效。以下情況就是同時運行uat,dev,但讀取spring.profiles.active的值會是dev,uat。 // java @SpringBootTest @ActiveProfiles(value = { "uat", "dev" }) class MultipleProfileUatDevTests { } // bash mvn clean compile test -Pmix 如果我們把maven 指令中的加入package,預期 test 執行的是 uat,dev 。而 jar 的打包結果會是 dev,uat。 // java @SpringBootTest @ActiveProfiles(value = { "uat", "dev" }) class MultipleProfileUatDevTests { } // bash mvn clean compile test package -Pmix 但請盡量不要這些做,因為會越來越混亂,特別是打包 prod 環境。為減少出錯的機會,例如test污染了prod的環境,筆者在package時,通常都會跳過test。 mvn clean compile package -Pprod -Dmaven.test.skip=true

Spring Boot 08 - 多情境設置 maven profile 與 application.properties

科技新知
MacauYeah・2025-02-25

為何要有不同的建構 Profile Profile這一字,很難在IT技術文章中翻譯,它在Spring boot中的語意大概就是一個設定一個固定的運行環境參數合。例如我們做開發時,有些只想在開發環境中出現的設定,諸如測試用的資料庫、細緻一點的LOG層級,都寫在dev profile中。當換成正式環境時,我們也有一套全新的配置,而且會集中寫在prod profile中。把這些參數設定從程式碼邏輯中抽離,可以讓你的程式碼簡潔很多,也方便對比不同環境的設定。 application.properties Spring Boot (Spring Boot Starter) 就提供了 Profile 管理。我們可以為一個Spring Boot 模組設定多個不同的 application.properties src/main/resources/application.properties 為預設 (default profile) src/main/resources/application-uat.properties 為驗收環境專用 src/main/resources/application-prod.properties 為投產環境專用 src/main/resources/application-test.properties 為自動測試專用 在執行程式時,我們只要動改變啟動的參數spring.profiles.active,例如 mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=uat" # or mvn package && java -jar target/YOUR_JAR_NAME --spring.profiles.active=uat Spring Boot 就會指定載入 application-uat.properties 的內容,如果有些值沒有定義,它會再追溯到預設的 application.properties中。 在運行中改變啟動參數的情況可能不多,筆者更常用的情況是在編譯期間產生多個 Jar 檔,不同 Jar 檔指定不同的環境,方便系統管理員取用測試。想做到這個效果,我們需要在 application.properties 中,我們還需要加入一句spring.profiles.active=@active.profile@,並在編譯工具中加入這個變量,例如筆者常用的 maven pom.xml 中,就會有這一串設定 它在 maven clean compile package 時,就已經可以在JAR中填入固定spring.profiles.active。那麼每次執行時,都會是指定的profile。 mvn package -Puat java -jar target/YOUR_JAR_NAME 在這個例子中,JAR 中的 spring.profiles.active 就會固定是uat,我們不需要在啟動參數中加入字眼。 如果大家不會碰到混合Profile的話,其實上述的資訊已經足夠大家應付很多情境。 但當大家有追求,需要寫自動測試,有機會不同自動測試需要啟用不同的 Profile ,更有可能出現混合Profile的情況,這件事就變得很複雜。我們需要繼續深入了解一下 Spring Boot 的覆蓋機制,下面將會以測試方式導出結論。 如果真的對混合 Profile 沒有太多信心,我們也可以用單一 Profile 重組不同 properties 的方式,自行去模擬混合 Profile ,例如除了dev, uat, test之外,我們可以加入 dev-test, uat-test, default-test 作為驅分。這樣應該可以簡化測試的複雜度,不過 properties 檔案就可能會成幾何級成長。 但在某情特殊情況下,我們不可能簡單地重組 properties 等型式去做測試,例如針對部份uat-test的測試,只有部份可以執行,部份不可以,那麼我們還是需要用到混合 Profile ,限定某些測試需要執個某個 profile ,但其餘部份可以動態切換。 有條件的讀者,也可以先行試玩一下混合 profile 的特性,下期筆者再為不同情況作解紹。 混合Profile Source code spring boot profile

Github copliot vs Intellij IDEA ultimate

科技新知
MacauYeah・2025-02-18

github copliot 最近正式開放每月限量免費使用,只要有github 帳號,就可以經過vscode copliot plugin,向 github copliot 交互式生成程式碼,又或是經 copliot 提供 code completion。大家會不會想過,加了github copliot的vscode,是不是效率暴升,可以跟傳統的付費IDE 例如intellij 的IDEA ultimate版本平起平坐? 流暢度明顯提高 是的,在生成程式碼方面,特別是code completion,在開啟copilot之後,實在好太多了。筆者長期寫java,vscode 原生的 java code completion,實在太陽春。java class name都很長,而且是強型別,很多時候都要完整打出class name。但大部份時候,筆者都要打很多個字之後,vscode才猜到你想打什麼,再給你可能的code completion,但這樣一來你也快打完了,幫助不大。要麼就是自己複制貼上,要麼就自己全拼出來。 在開了github copilot之後,在空行開始時,它就會開始猜你的意途,在打幾個字母以後,它雖然會頓一頓,但總在筆者跳去其他部份複制class name之前,就給出更新結果,實在省心太多。但猜測始終是猜測,大部份時候還是邊打邊修正。不過code completion方面,已經是追得上intellij,有些時候更是超越了intellij。例如我們有時寫 javascript 時,需要做多語言顯示,我們需要為每個語言設定一份i18n的翻譯。copilot 在這方面也能幫到忙,它會自動推薦可能的翻譯,你連問都不用問,這些功能,都不是 intellij 的本地運算會有的支援。 另一個要提提的是 copilot chat,它跟大家平時使用 chatgpt 程式碼生成的方面類似,只是它可以直接在vscode的某個檔裏直接交換生成程式碼。不過生成的品質都很一般,很初階的事可以做,深一點的就不懂。例如你很常寫java,但突然要寫javascript,有些javascript的array操作你懶得查,這時你可以叫copilot chat幫你生成。但若果你今日使用 javascript 框架,有一些 vuejs 或 reactjs 的結構參數傳遞你不太了解,你想找copilot chat,那就幫助不太。它依然可以生成一些程式碼,但對你碰釘的地方沒有修正意義。你還是需要自行從官方文件較對、研究Stack overflow中相似問題的解決方案。就跟chatgpt差不多。當然這些不是傳統IDE可以給你的。但如果現今對比的是收費的copilot chat和本地免費的ollama qwen2.5-coder,copilot chat就沒有太大性價比。 可以作為付費IDE的平替嗎? 如果我們拿vscode + github copilot 跟 intellij IDEA ultimate來比較,前者入門價錢是120美元一年,後者入門則是169美元(次年續期有優惠,但到了第三年才會比 github copilot便宜)。單看價錢的話,github copilot的確比較便宜。想省點錢,github copliot絕對是一個可以考慮的選擇。但除了錢以外,或者我們還要考慮一些其他因素。 公司立場上,介不介意你的IDE上傳資料到cloud service上面? 付費IDE的除錯功能、多環境整合、程式碼品質分析,這些關係到長期維護,非程式碼生成部份,是不是可以忽略不計。 筆者在開發開源的程式,長期都使用vscdoe,在配上 github copilot 後,明顯覺得它提升了 vscode 的流暢度。但相對實際工作上,筆者還是會集中使用 intellij IDEA ultimate 。因為即使vscode 有明顯改善,但日常碰到的問題更多不是在生成部份,而是解決那些似是而非的程式碼結構陷阱,這方面還是intellij 更幫到忙。(當然stack overflow和其他網路資源才是真正的救命靈藥。)

學習寫程式,除了複制貼上還有什麼?

科技新知
MacauYeah・2025-02-07

不知道大家是如何學習特定程式語言/框架的建構? 也不知道大家可如何保持程式庫/框架的最新狀態? 筆者就分享一下最新的經驗,看看對大家有沒有得著。 制作自己的範本 跟著程式/框架的導覽教學(Tutorial)走一偏 從零起一個新專案 設定專案,該用的基本功能全部設定好,作為概念驗證(Proof of Concept),也作為日後範本(Template)之用。 有需要用新專案,就複制之前的範本,再逐一修改名字或路徑的設定。 上述做法,是筆者過去比較常用的策略。面對很統一要求的專案,都有效。當程式庫有更新,我們可以選擇只局部修改,範本就可以長期用。我們也不需要經常從零走一篇。 練手的Code - 從零起一個新專案 上述的範本做法,對於現時需求多變的專案,可能不是很有效。例如有些專案使用Session Auth,有些則是Api Auth,有些則是Open Auth。同一個範本中有齊多種Auth的設定,原本難度就有夠高,之後複制完還要自行禁用不相關的部份,也是相當的煩人。當範本中多有個地方都有互相衝突的地方,複制範本就不是一個很易的做法。 面對那些複雜的配對,我們務必要真正了解技術的運作原理,然後為每個功能都從零建一個專案,做一個最簡單的Proof of Concept。重點不是在未來拿它們複制貼上,而是用來厘清概念,哪段程式對這個功能至關重要,哪段其實沒有作用。 如果可以,每次程式庫/框架升級時,都從零建一次。這樣一來可以練手,加深記憶,二來是每次版本的變動,有些程式碼可能已經變得沒有作用,原本的寫法並不再是最簡的。當然這個也可以為每個功能獨立做成範本,到有需要的時候再抄少量的程式碼就好。 其實練手的過程中,我們亦會慢慢熟習IDE的功能,有些IDE或Plugin已經很方便地自行完成一些設定。所以筆者漸漸的也習慣了不抄程式碼,改為以IDE Plugin的方式建立,某些真的很不熟練的部份才會維持範本複制的型式。 這是筆者最近學習vue3 的練習清單,還在持續新增中。讀者們有興趣也可以一起來修訂。 https://github.com/macauyeah/AProgrammerPrepares/blob/main/src/vuejs/TimeAttack.md


英語版  馴龍記
4DX    馴龍記
4DX  英語版  馴龍記
4DX  殺神John Wick之芭蕾殺姬
劇場版 我與機器子
大風殺
那些年的我們
英語版  史迪仔
私家偵探
IMAX with Laser 馴龍記 英語版
腓尼基大作戰
殺神John Wick之芭蕾殺姬
不赦之罪
MX4D 職業特工隊:最終清算
酷兒 3rd MIQFF
史迪仔 英語版
殺神JOHN WICK外傳:芭蕾殺姬
罪人們
獵狐行動
關於我和鬼變成家人的那封利是
還魂
職業特工隊:最終清算
馴龍記
功夫夢:傳奇少年
史迪仔
死神來了:血脈
獵金•遊戲
英語版  馴龍記