潮流特區

最新文章

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

Spring Data Jpa 自動化的選擇 - Code First

科技新知
MacauYeah・2025-01-22

Code First vs Database First 在早期SQL資料庫盛行的年代,在設計要使用資料庫儲存資料時,很經常遇到一個策略選擇的問題*Code First* vs *Database First* 這兩個策略的差異可能越來越講不清,筆者也找了一些現時網路上的講法。 Code First: 先從寫程式的角度出發,設計數據模型,再使用工具把你程式碼中的數據模型類(Class),生成一個對應用SQL資料庫的表(Table),自動編做好對應的數據結構(Schema)。這樣你在設計時,以程式設計為主導,方便熟悉程式的人使用。這常見於第一手開發設計,因為資料都是第一次收集和儲存,考慮收集程式的運作最為實際。 Database First: 先從SQL資料庫的儲存、取用資料的方式出發,先用SQL成生Table及Schema,再轉變成為程式碼中的數據模型。這樣的資料庫在日後作分析用途時,比較簡單易懂,方便使用熟悉SQL的人去使用。這也常見於二次開發程式,因為這樣可以確保不會錯誤地破壞原有資料庫。 那麼筆者為何講這兩個差異越來越講不清?那是因為現在的資料庫不能單純地只考慮初次或二次開發問題,而是需要考慮多個系統協調運行的問題。 多系統共享協定 - Database First 因為隨著資料系統發展,有些資料會作為數據源出現或用作共享媒界,如果一定要對設計策略作分類,在多系統協調運作下,這些應該叫使Database First。不論它們是SQL還是NoSQL資料庫,我們的程式碼都要為這個預先定義好的數據結構作出妥協。不論使用工具,還是人為分析,都要把共享的數據結構轉換成自己程式中的數據模型。 即使不是多系統協調運作,有時候因為要移植系統,但同時又要令兩個系統版本相容。新系統也是被逼使用Database First的方式設計。 自動化考量 - Code First 前述我們講到,很多時候我們也是從Database First的方式思考。不過筆者就這個Database First,也弄到滿身傷痕。 首先,拋開工具轉換的誤差,我們人為的把共享數據轉化為數據模型,共享數據有時會有一些先天的缺陷,例如: 資料沒有設計Primay Key (主鍵,唯一鍵)、日期時間的定義不明確等。面對一些意義不明的數據來源,要整合確實很要命。而且二次開發中,不可能100%重用原有的資料庫結構,很多時都會加入新的欄位或更多表格去計數。一旦加入新欄位,在團隊多人開發中,那麼使用唯一的共享開發環境,就變很易有程式碼上的衝突。 若需要多人開發,各人有一個Code First的開發用資料庫,是很必要的。這也可以在系統正式升級前,對比開發中資料庫及舊資料庫的結構,觀看它們之間的差異,評估升級的風險。 也許Code First並不是重點,重點是可以隨時建立一個測試用的資料庫,這才方便合作開發。自動化的地方,不單只限於數據結構,範例資料也該是如此。如果有維繫一個初始範例資料,可以在有需要時自動生成,對於多變的環境一定有很幫助。 現時,筆者基本上都會人為檢視資料庫,人工對照編寫程式中的資料結構(即是人工的Database First),並確保那時程式再次經自動化生成的測試用資料庫,並沒有失真(即是Code First)。至於範例資料,初期筆者也只使用SQL生成,但後期因為資料結構開始複雜,筆者也暫暫使用程式碼生成,雖然工作量會多了,但對於資料庫升級、品牌更換,這是很有效的手段,程式碼升級測試也更順暢,絕比SQL生成更易維護。 Ref - Code First vs Database First https://builtin.com/articles/code-first-vs-database-first-approach

快速做用 elasticsearch 做中文 n-gram 關鍵字全文搜尋

科技新知
MacauYeah・2025-01-16

有些時候,我們對一些文章資料,光是使用Ctrl-F文字區配搜尋,很難找到完全吻合的結果。這時候,我們可以試試看快速搭建自己的中文搜尋引擎,看看能不能更易地找到資料。而中文搜尋引擎,其實用免費的elasticsearch也可以做到。我們就來看看怎樣快速起lab吧。 經 docker 下載及運行 elasticsearch docker run -p 127.0.0.1:9200:9200 -d --name elasticsearch \ -e "discovery.type=single-node" \ -e "xpack.security.enabled=false" \ -e "xpack.license.self_generated.type=basic" \ -v "elasticsearch-data:/usr/share/elasticsearch/data" \ docker.elastic.co/elasticsearch/elasticsearch:8.17.0 建立資料庫。在elasticsearch 中,示作index,並建立自己的n-gram analyzer和tokenizer。 curl -X PUT "localhost:9200/book-ngram?pretty" -H 'Content-Type: application/json' -d' { "settings": { "index" : { "max_ngram_diff" : 4 }, "analysis": { "analyzer": { "my_analyzer": { "tokenizer": "my_tokenizer" } }, "tokenizer": { "my_tokenizer": { "type": "ngram", "min_gram": 1, "max_gram": 5, "token_chars": [ "letter", "digit" ] } } } } } ' 假設資料庫每筆記錄有 record_id,title 和 content 三個欄位,其title, content都是中文內容。它們都套用 n-gram analyzer 。 curl -X PUT "localhost:9200/book-ngram/_mapping?pretty" -H 'Content-Type: application/json' -d' { "properties": { "title": { "type": "text", "analyzer": "my_analyzer", "fields": { "keyword": { "type": "keyword" } } }, "content": { "type": "text", "analyzer": "my_analyzer", "fields": { "keyword": { "type": "keyword" } } }, "record_id" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword" } } } } } ' 批量上傳內容。(如果要上載json檔,請把 -d'xxx' 改為 --data-binary @FILENAME) curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d' { "index" : { "_index" : "book-ngram" } } {"record_id":"1","title":"紅樓夢","content":"甄士隱夢幻識通靈賈雨村風塵懷閨秀"} { "index" : { "_index" : "book-ngram" } } {"record_id":"2","title":"西遊記","content":"混沌未分天地亂,茫茫渺渺無人見。自從盤古破鴻蒙,開闢從茲清濁辨。覆載群生仰至仁,發明萬物皆成善。"} { "index" : { "_index" : "book-ngram" } } {"record_id":"3","title":"水滸傳","content":"張天師祈禳瘟疫洪太尉誤走妖魔"} ' 多欄位搜尋,並指定title的權重為content的兩倍。 curl -X GET "localhost:9200/book-ngram/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "multi_match": { "query" : "開天闢地", "fields": ["title^2", "content"], "analyzer": "my_analyzer" } } } '

為程所困-是什麼讓你不想寫自動化測試?

科技新知
MacauYeah・2025-01-08

測試場 VS 自動化測試 筆者一直地更新自己過去所編寫的程式,很恐怖的是,那時的自己很少思考過怎樣寫測試Test Case。致使每次做更新時,都膽戰心驚,要手動建立測試場,人肉去測試每個可能有受影響的地方。在那些年的時候,有能力自己搭建測試場,已經是萬幸。但當面對一些要長期維護的程式,測試場的人肉測試並不是一個有效的方法,一來費時間,二來人腦記憶並不可靠。單靠自己去想想那些地方受影響,再測試,某程度是在挑戰人腦的記憶上限。如果是團隊合作,就更麻煩,你以為修改不會影響到其他人,結果卻是翻天覆地。 所以為求長治久安,編寫自動化測試,是有必要的。這些自動化測試,都算是回歸測試,每次程式有任何地方改動,都確保所有自動化測試被通過。理想始終是理想,但實際操作又會遇到怎麼的問題? 以筆者剛更新的程式為例,難以測試主要是當初沒有想過要測試這件事,所以程式結構通常是【連續順序】地執行。想分段測試?除非先重構。 Function中太多自己創建的Object 回顧自己的程式,初期編寫時,總會我手寫我心,每想要創建任何資源,在java中就會使用 new 字眼,或是自行呼叫某些 builder 類來取得資源,這是其中一個令自己無法寫測試的原因。 我們要想想,這些資源,是不是自己Function中所關心的核心。如果這個資源是被直接回傳的,我們要保留,如果它是HttpClient,只是要來獲取其他資源的媒介,我們或許可以利用依賴注入來取得它,即是把 HttpClient 改為經呼叫方傳入。注入的好處時,我們可以在Test中,修改那些資源的行為和結果。更進一步的是,把那些資源改為 interface 的方式存取,那麼在 Test 中就能更任意地控制該資源的行為。 首次重構某些資源成為依賴注入,大部份都會影響呼叫方,很多地方都要重寫。不論使用constructor injection, setter injection, annotation injection 等,上傳呼叫方,或多或少都會要加減改變參數。極致地,我們把構建都交給Program 框架去做,例如Spring Boot中,各種資源,都交給框架去自動配致。當然,這種做法的學習成本高,除錯成本也高。 【注入】其實是想在控作那些資源,在測試中運行得到固定的行為。使用前述的HttpClient例子,當我們業務邏輯是先訪問外部Web API,再根據結果做處理,那麼我們測試時,就會想模擬Web Api的結果。如果要做到自動化測試,最強硬的手段,就修改自己的HttpClient,模疑給出固定結果。 想要做到這種,在傳統的Java中,我們需要透過進一步抽離Interface去做。但這樣做很累,所以筆者通常會用如Mockito的程式庫,去修改HttpClient的行為。有興趣直接看程式碼的讀者,可以去看 github 。 當然,上述的 HttpClient 例子,使用測試場也有可做測試,自己再去模擬那些Web Api的回傳,有些情況下,這樣會更真實,但大家就必需好好定義測試場的行為。因為測試場可能與團隊的其他成員所共用,有機會其他人可能想要更多互動的測試方式,而非固定的結果。但並上非固定結果的測試場,自動化要測試的可控度就減少。

免費自用的私人AI助理 | Ollama - 本地大型語言模型

科技新知
MacauYeah・2025-01-06

不知道在澳門的朋友,有多少可以正常接觸openai?因為地方政策問題,像openai這種國外的大型語言模型(下稱LLM),澳門區都沒法接觸到。但隨著時間過去,即使我們不能直接接觸到算力很強的收費AI,我們只要有電腦,也可以佈署一些開源版本的LLM。只要我們可以安裝到ollama這套本地運算軟件就好 ollama是一個giuthub上的開源工具,讓用戶能夠在自己的電腦上運行各種大型語言模型(LLM)。基本上只要電腦是普通的桌上型windows, linux, mac,都可以運行它。下以面就介紹一下筆者的安裝經驗。 windows windows ollama windows 本地安裝ollama,真的很簡單,就是直接去官網下載就好 - https://ollama.com/download/windows 安裝完成後,在windows cmd再加一個基本的模型就可以了 ollama pull llama3.2 之後就可以開始跟llama問問題 ollama run llama3.2 windows openwebui 如果大家不習慣windows cmd的醜醜介面,想經過瀏覽器存取,我們可以再加裝openwebui。但這個必需要經第三方python或docker安裝。openwebui github指引 - https://github.com/open-webui/open-webui 經python pip install open-webui open-webui serve 經docker docker run -d -p 8080:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main 最後,打開browser,訪問 http://localhost:8080,openwebui就會要求大家先設立管理員帳號。 就那麼簡單,大家就有一個真正的私人AI助理。 steamdeck steamdeck 因為很多linux功能都有被限制,所以筆者就直接使用 podman 安裝 git clone https://github.com/macauyeah/ollama-steamdeck-podman.git cd ollama-steamdeck-podman podman compose -f podman-compose.yaml up -d podman exec -it ollama ollama pull llama3.2 同樣地,打開browser,訪問 http://localhost:8080就可以了,因為這個版本已有預設的管理員帳號,立即打開就可以使用了。 Ollama的開源模型 上文中一直提及 llama3.2 其實是 Meta 公司的開源模型,因為它的參數相對少,算力要求較低,可以在沒有GPU的環境下執行。若然大家算力足夠,可以使用其他模型,詳見 https://ollama.com/library 。見到合心水的模型,大家可以經 pull 指令下載。例如:小紅書的網紅們很多都推薦qwen2,我們可以 ollama pull qwen2 備註: openwebui 及 ollama 並不直接支援自己建立自己的資料庫。我們需要其他工具去補完,但筆者觀看各種教學,自己建資料庫的效果都不太好,所以暫時不做任何教學。 只要我們一直經ollama pull,就可以更新語言模型。但如果大家追求即時的網絡最新資料,大家可以看看LLM RAG的相關文章。但筆者亦未有成功的案例,有更新會另作教學。 opewebui並不是PDF閱讀器,但它可以預覽PDF中的文本,我們需要手動複制PDF中的文件後,才能經ollama分析文件內容。 若想切換模型,在指令介面中,我們多開一個分頁就可以了。若經openwebui,則可以在每句對話之前,經左上方選擇不同模型。

Steam OS 內建 podman DNS 問題解決方法

科技新知
MacauYeah・2024-12-20

前幾天筆者在介紹SteamDeck 內建的podman時,沒有測試得很清楚。在長期使用下,的確有些問題需要進一步處理,這裏就補充一下解決方法。 我們前一篇介紹的 Steam Deck 內建 podman ,配上再自行安裝 podman-compose 有時會出現warning :`WARN[0002] aardvark-dns binary not found, container dns will not be enabled`。這不單影響到沒有在 service 之間自動産生 DNS 記錄,還會令互聯網功能失效,因為它會是整個 DNS 解釋功能丢失了,只是在 service 中定義 DNS 的地址並不會解決問題。筆者亦測試過,照著原本的 docker 思路,使用最傳統的做法,自己起 network ,自己起 container ,然後再串連在一起,依然會出現問題:`Error: "slirp4netns" is not supported: invalid network mode`。所以根源問題應該不在 podman-compose 上,而是在內建的 podman 依賴上。 緊急的解決方案,我們需要用到 "network_mode: host" 的方式去解決。例如以下例子 "network_mode: host" 的主要作用,就是讓 container 直接在主機的網段上執行。上面的例子中的 postgres 資料庫,它預設使用5432端口,我們並不需要再獨立宣告,即使在 container 內外,都可以直接使用 localhost:5432 溝通。而使用了主機的網段後,DNS 也可以正常運作。但這個做法的缺點就是 container 內的所有 port 都自動佔用了 host 上的使用,有時候那怕我們並不需要,它也會被暴露在外。更可能的是增加了不同 container 之間的 port 衝突。不過筆者要用於開發環境,所以這並不會是很太大的問題。

Docker 來源掃瞄 - Docker Image Scan

科技新知
MacauYeah・2024-12-19

當網安要求越來越高時,我們也要留心 docker image 的來源是不是有漏洞問題。 docker hub 本身就已經有一些安全掃瞄報告,以 nginx 的 1.27.3 版本為例, docker hub nginx 1.27.3 , docker hub 已經列出相當多的CVE漏洞。 不過對於不公開的 docker image ,安全描瞄可是要收費的。作為小團隊,可能想先尋求一些簡單的免費方案。如果你想同樣的需求,可能Trivy會幫到你。 Trivy Trivy 是一個用於描瞄軟件版本依賴或設定檔是否引用到一些有漏洞問題的軟件,它也能檢測 docker image 是否有漏洞或錯誤設定的問題。而且更好的是, Trivy 本身亦有 Docker Image 版本,我們就不用煩惱怎樣弄一個 Trivy 的執行環境,只要可以運行 docker ,有網路就可以了。但使用 Docker Image 版的 Trivy 有一個額外要求,就是它要有主機上的 docker.sock 權限。 描瞄的指令如下,其中 docker.sock 就是為了讓 containers 內部的程式可以存取主機的 docker daemon , .cache 則是為了方便暫在下載資源。 上面故意用 nginx 的兩個同版本號不同平台的 docker image,其實就是為了引出一些潛在問題。nginx 預設是使用的debain OS的,在筆者寫文章的當下,已經更新到最近的 image ,但始終有一大部份可能的漏洞。反觀 alpine OS 版本,就找不到這麼多問題。 這是因為 alpine 預設安裝的依賴較少,所以找到的漏洞也少。正所謂,做多錯多,唔做唔錯(大誤)。這其實有好有不好,因為在發生問題時,在 alpine 下可能連基本的除錯工具都沒有。除非大家有完整測試,或者對 alpine 有相當的認識,你才會選擇一個非官方預設的版本。但就以事論事,引用較少的依賴,長久之下的確是不會有那麼多隱患。大家如果有條件,也可以試試 alpine 或其他版本。 前一節我們可以看到,Trivy需要經過 socket 的方式才能存取主機上的 container daemon 操作權。但 podman 作為一個不主張 daemon (daemon less),亦主張不需要 root (rootless),那麼它該怎樣執行? 其實podman也有user層面上的 socket,而且 trivy 也有對應的方式去轉用第三方 socket (有點像使用遠端主機 socket,但官方並未宣佈正式支援遠端的方式。) 具體使用方式,筆者亦已在 steam deck 上測試,使用方式如下。不過因為 steam deck 預設沒有 root,筆者就省略 cache 指令,免得之後要有權限問題要手動清理。 Ref Podman socket activation Trivy: Support for rootless podman

steam deck 辦公用途工具資訊 2024/12更新

科技新知
MacauYeah・2024-12-17

購入steam deck 已經有一年多的時間,之前亦發過一些安裝教學。適逢筆者系統重裝,又有機會重新檢視工具的可用性。 Podman / Distrobox 一項很重要的更新,Steam OS已經有預安裝podman,不需要經home brew安裝。但預裝的podman 沒有內置DNS,可能會對K8S等進階應用有影響,不過筆者只作為單機測試用,普通的podman compose沒有問題。 另一項很重要的更新,就是 distrobox,它可以經 podman 讓大家方便地執行不同的 linux 版本,想在Steam OS上使用Ubuntu?沒問題,開箱即用;想用VScode官方維護版本,沒問題,安裝指定Linux再按裝VScode就好。 詳見上一期的介紹。Steam OS 3.5更新,內建 podman, distrobox 中文輸入法 我們不需要再安裝Fcitx5 + Rime,如果大家在遊戲模式下,已經設定好中文輸入法,那麼在輸到桌面模式下,照樣以Steam Button + X Button的方式,打開內鍵輸入法。你會見到有虛擬鍵盤彈出,它會覆寫桌面模式的。此時,即使你關掉虛擬鍵盤,並用實體鍵盤,中文輸入法都會保留。不過大家要確認,彈出當下必需要是【中文】。 若然你彈出的虛擬鍵盤是英文,請按【地球】轉換到【中文】後,關掉虛擬鍵盤,再重來一次(重按Steam Button + X Button),確保彈出的時候是經【中文】覆寫。 Discovery Discovery 是 Steam OS 預設用來安裝第三方程式的地方,它經 flathub / flatpak 存取程式。但筆者一定要先強調,它提供的第三方程式,並不一定會得到原開發者的支援,它屬於打包移植之類。 例如 flathub 中可以找到 Google Chrome (https://flathub.org/apps/com.google.Chrome) ,但它實際是經過一個github 專案移植過去的 (https://github.com/flathub/com.google.Chrome)。它有提供持續更新,但筆者不保證當中有沒有任何被植入過任何不正當軟件。 相反,flathub中有一些程式是經過原開發者支援的,例如 Firefox (https://support.mozilla.org/en-US/kb/install-firefox-linux), Gnome Boxes (https://gitlab.gnome.org/GNOME/gnome-boxes), Gimp (https://www.gimp.org/downloads/) ,這類可以比較放心使用。 筆者過去使用 Discovery ,的確有遇到一些權限問題,但最後估計應該是筆者安裝 Homebrew 所影響。筆者在重裝 Steam OS 後,直接使用 Discovery,就沒有問題。過去需要經 Homebrew 安裝的東西,現在經 podman , distrobox 或 Gnome boxes 就可以了,不再需要經Homebrew去折騰一輪。而且Homebrew也不是萬能的,因為Steam OS有限制一些套件的事用,即使安裝Homebrew也解決不了。若想要完全開放套件限制或解決依賴問題,還不如直接開啟Steam OS覆寫權限,轉用Arch Linux的安裝包。


破.地獄
摘星廚神•巴黎夢
愛情咀咀咒
摘星廚神:巴黎夢
穿越時空的初吻
你的顏色
摘星廚神.巴黎夢
穿越時空の初吻
贖夢
SEVENTEEN RIGHT HERE WORLD TOUR IN CINEMAS
迪士尼《白雪公主》
4DX  白雪公主
白雪公主
魔域女巫
高空殺機
孤獨的美食家
無痛俠
猴
機動戰士Gundam GQuuuuuuX -Beginning-
門逃
米奇17號
4DX  劇場版 「進擊的巨人」完結篇 THE LAST ATTACK
4DX  哪吒之魔童鬧海
IMAX with Laser 哪吒之魔童鬧海
看我今天怎麼說
哪吒之魔童鬧海
美國隊長4:勇敢新世界
破.地獄
摘星廚神•巴黎夢