Hi there 👋

Welcome to my blog

清除冗餘的Docker layers

因為剛換了一份具有挑戰性的新工作,有好一陣子沒有發文了,雖然手上有幾個在進行的學習或者project,但進度都還沒到可以發表文章的程度。剛好最近工作上有一個之前沒碰過的問題,花了一些時間做處理,因此在這邊簡單地記錄一下。 問題敘述 在某個上班日的下午,筆者突然收到來自前端工程師的訊息,在跑CI process時GitLab Runner好像出了問題,導致pipeline fail了。經查詢後,發現是該Runner所在的VM disk 容量已達99.9%,已經不能夠再pull任何的image下來了。 GitLab Runner在執行CICD job時,會pull許多暫時的image來完成任務。在敝公司的每一台Runner中,都有一個固定於每日凌晨執行的cronjob來清除這些job產生出來的image、volume以及build cache等。因此在一開始,筆者懷疑cronjob是否根本沒有如期執行,從而往system log去查看,想確認cronjob執行的狀況。 不料,cronjob的確有在指定的時間執行。但僅從system log並沒有辦法得知更詳細的資訊——例如執行前的硬體容量多少?執行後多少?如果cronjob確實有做容量的清除,那麼硬碟滿載的情況下大都在一天中的何時發生?SRE該如何即時得知這些訊息? 可以從以上資訊得知,Gitlab Runner所在的虛擬機缺乏監控設施,導致我們沒有辦法即時、有效地獲得系統狀況,進而做出適合的判斷與解決方案。因此,建立系統的可觀測性便是開始解決問題的第一步。 建立系統可觀測性:收集系統指標 敝公司的其他專案使用Prometheus + Grafana作為主要監控工具之一,又筆者只需要監控Linux中的系統指標,Prometheus已有現成的exporter可以做使用。綜合考量下,筆者在每一台Runner起了node_exporter的container以收集host-level的metrics,再使用Grafana去收集這些指標,繪製成可視化圖表。 在這邊提一個小題外話,筆者先前也有自己實作過host-level的metrics exporter,只不過當時單純地認為,如果使用container部署這個服務,那麼觀察的指標不就只限於container裡面了嗎?事實上,在萬物皆檔案(Everything is a file)的Linux當中,我們可以在/proc、/sys等目錄找到CPU、Memory、Disk相關的資訊,因此如果想觀察host的指標,就只需要把host的檔案目錄掛載到container中就可以了! node_exporter的官方GitHub中就給了docker-compose.yaml的最佳範例: --- version: '3.8' services: node_exporter: image: quay.io/prometheus/node-exporter:latest container_name: node_exporter command: - '--path.rootfs=/host' network_mode: host pid: host restart: unless-stopped volumes: - '/:/host:ro,rslave' 在監控與告警皆架設完成後(這部分其實還有一些細碎的小東西可以談,例如告警的閾值怎麼設?要監控哪些指標?因為怕偏離重點,先暫且不提),筆者發現,在上班時間,每一台Runner的硬碟佔用量已達全部容量的70~80%,約是400多GB左右,那自然是撐不到在半夜執行的cronjob。此外,即便筆者手動執行完docker system prune -a -f這個可以清除unused container、image以及building cache的指令,也只清除了80G左右的容量。 基本上,每一台Runner所在的虛擬機就只有裝Runner這個應用程式,而且部署方式還是container,怎麼會佔用快要400GB的容量呢? 最後,透過資深同事的幫助,以下列指令發現這龐大的體積來源來自於Docker下面的overlay2資料夾。光是這個資料夾就佔用了300多GB。 du -ch --max-depth=1 /datadrive/docker/ Docker v25後才修復的bug 難道是連docker system prune都沒有辦法清理overlay嗎?錯了,docker system prune這個指令就是拿來清除任何暫停的container、沒有在使用的network, images, build cache。之所以會沒辦法完全清除,是因為這是Docker v25以前的一個bug(剛好敝公司使用的版本是v24…)...

October 3, 2024 · Hsuan-Ni Hsu

自架GitLab Runner來執行單元測試

接下來即將進入的新公司與職位,會需要大量與GitLab CI接觸,為了做好準備,這幾天花了些時間自己跑了一遍CI流程。 GitLab CI Process 根據GitLab官方文件所述,所有CI/CD任務的執行都必須仰賴gitlab-ci.yml這個文件: To use GitLab CI/CD, you start with a .gitlab-ci.yml file at the root of your project. This file specifies the stages, jobs, and scripts to be executed during your CI/CD pipeline. 當定義好gitlab-ci.yml後,整個GitLab執行CI/CD的流程就會變成: 當指定分支的程式碼更新(commit or push or pull request) GitLab根據gitlab-ci.yml的定義,同步平行地觸發job (編按:在沒有任何其他設定之下,job都是平行執行的,除非另外在yml設定stage或者needs參數) GitLab server會去檢查每個job所指名的Runner,並把job分配給適當的Runner執行 Runner執行後的結果(CI Logs)會回傳到GitLab server,顯示於Pipeline上 在本篇文章中,會逐一介紹GitLab中Runner的種類,並且嘗試自定義Runner,並在上面運行單元測試。 GitLab Runner 當GitLab在運行gitlab-ci.yml中定義的Jobs時,實際上是在GitLab Runner上運作的。在這裡,Runner又能根據作用範圍分作三種類型,分別是: Shared Runner: 在該GitLab server下的所有group或者project都能夠使用。當我們建立project時,系統會自動產生Shared Runner,可以用來處理無標籤(tag)的任務。 Group Runner: 只有在該group下的所有project可以使用。 Specific Runner: 只有在特定的project下可以被使用。本次會使用Specific Runner作為實驗範例。 Executor 這時,Runner會根據我們選擇的Executor決定要採用何種方式與工作環境來完成CI Job。換言之,一個Runner可以被允許擁有多個executor,讓一個Job能運行在多種環境之中。...

July 30, 2024 · Hsuan-Ni Hsu

2024 06月份讀書狀態回顧

前陣子發現一直有在關注的部落格作者,會在自己的部落格分享每個月的學習回顧,覺得這是很好的自省方式,所以也想要跟著做。 本月重點 這個月主要專注準備AWS SAA-C03證照,中間有跑去CNTUG申請LAB來試著架架看Kubernetes,透過Helm架設Prometheus+Grafana來監控cluster上面運行的服務,也有寫一點Python,刷leetcode防止失智。 必須要檢討的是,這個月除了證照以外,並沒有訂定明確的讀書計畫,可能也跟我這個月其實很迷惘,到底要往開發還是往SRE走有關,就變成甚麼都碰一點。所以整個六月只有考到證照這件事算是有收穫。 AWS Solutions Architect - Associate 這張證照總共花費三周時間進行準備。我參考的是Udemy上的知名課程 Ultimate AWS Certified Solutions Architect Associate SAA-C03 搭配EXAMTOPICS的考古題做準備。 大約花了一周時間把Udemy課程上完,第二周開始邊刷題邊檢討,第三周開始模擬考試情境,每次連續寫65題並計時、算分,再仔細檢討題目,並且與之前學習到的觀念做對照與補充。 在筆記部份我使用Heptabase,這個筆記軟體之前就有聽聞,但感覺操作起來蠻複雜的就一直沒有想要使用,直到看到AppWorks School同學的推坑文,實際使用之後也算滿意,非常適合準備SAA這種知識比較零散,但解題時又需要組織這些零散知識的考試。 體感上來說,SAA與其說是考你各個服務的特性,不如比較像是考你「要如何貼合客戶需求,運用AWS服務設計架構,解決當前問題」,因此我覺得雖然很多人說刷題就好,但寫題目看答案的時候還是要問自己「為什麼這個正確答案要這樣設計?」因為實際進考場後真正有寫到重複的考古題並不多,但觀念其實都是類似的。 儘管考過證照,但自認還是缺乏實作上的經驗,剛好下個月會有一些實做專案的機會,如果把專案丟到AWS上或許能有更多的收穫。 下月重點 其實以時間點來看已經是這個月了…因為面試都集中在七月前半段,因此安排如下 七月中前: 面試作業,準備面試 資料結構演算法(Binary Tree, Graphs, Backtracking, DP) 七月中後: missing semester of your CS education(看完) 清大計算機網路概論(有開始就好) 每日進度細項 六月份共有6天沒有讀書。 6/1 參加Docker Workshop,回家後整理筆記 6/2 使用Golang實作簡單的container,並發表部落格文章 6/3 Golang web server架設+撰寫部落格文章 6/4 Golang web server practice 6/5 AWS EC2 6/6 AWS ELB & ASG 6/7 端午節家族旅遊 6/8 端午節家族旅遊 6/9 端午節家族旅遊...

July 3, 2024 · Hsuan-Ni Hsu

使用kubespray在OpenStack上搭建kubernetes cluster

先前參加SRE Conference時,認識了CNTUG(Cloud Native Taiwan User Group)這個開源社群,除了推廣雲端原生的相關技術以外,也提供Lab讓大家能夠申請、在上面做一些很難在自己本機上面的實驗(例如kubernetes cluster的建立),很幸運地前陣子遞交的Lab申請通過了,就也打算來寫一篇文章記錄整個實驗的架設與心得。 環境準備 參考了CNTUG網站與kubernetes官方網站上面關於VM硬體條件的文件,這次在openstack上架了四台VM,一台bastion host、一台control plane(m0)、兩台worker node(n0, n1)。 四台VM分別的硬體條件如下: IP Address Server Name Role CPU Ram OS 192.168.200.100 bastion-host Bastion Host 2 2G Ubuntu 22.04 192.168.200.101 k8s-m0 Master Node - 0 4 4G Ubuntu 22.04 192.168.200.102 k8s-n0 Worker Node - 0 4 4G Ubuntu 22.04 192.168.200.103 k8s-n1 Worker Node - 1 4 4G Ubuntu 22.04 網路設定 準備兩張網卡:public與private。本實驗環境會將kubernetes cluster都放在內網,僅讓bastion host做對外的連線。 在拿到openstack的帳號時,public網卡已經先幫我們建立好了,接下來要自己手動新增內網,並且能讓內網去連接到外網。在設定內網時,記得勾選「啟用DHCP」讓每個加進這個網路的VM都會被自動分配到唯一的ip位址。 Public Network: Public IPv4: 103.122.XXX.0/23 Private Network: 子網路名稱:private-net 網路位址:192....

June 22, 2024 · Hsuan-Ni Hsu

動手用Golang實作一個container - 實作篇

來實作一個container吧 延續上篇的動手用Golang實作一個container - 概念篇,了解container的底層技術是如何實踐之後,我們就可以開始使用Golang來做出屬於我們自己的container了。 以Docker為例,當我們要啟動一個container的時候,會使用這個指令: docker container run <image-name> [cmd] 以此為發想點,當我想要使用我的程式碼啟動container時,他長得會像這樣子: go run main.go run [cmd] 在這裡,我們會分別定義兩種function: run() : parent process要執行的function。負責創建child process及並配置其運行的環境(如namespace)。 child() : child process要執行的function。負責管理在container環境中,要如何運行用戶端所指定的命令。 而must()function則會作為error handler使用。 func main() { switch os.Args[1] { case "run": run() case "child": child(); default: panic("what??") } } func must(err error) { if err != nil { panic(err) } } 這邊還蠻想提一下os.Args這個指令。os.Args 是Golang裡面用來儲存命令行參數的一個變數。如果我們只單印出os.Args,他會長的像這樣: sophie@Sophie-Desktop:~/go-container$ go run test.go run echo 'hi' [/tmp/go-build3547203082/b001/exe/test run echo hi] 第一行是我執行Go程式碼的指令,第二行是os.Args印出來的結果。到這裡會發現為什麼go run test....

June 3, 2024 · Hsuan-Ni Hsu