[{"content":"前言 最近有了在住處自架一個簡易 Homelab 的想法。 首先想到的是可以把這個 Devblog 從 Github Page 移動到自己的主機上， 同時在工作上也遇到一些可以利用 Github Actions 達到方便快速地更新服務的情境。 然而在筆者實際摸索時，發現整套 Github Actions 的系統相當有彈性，可以應付小型大型的測試 / 部署需求，但也因此我認為這不是一個容易入門的系統。 故我在此以我的 Devblog 自動化部署為例，一步步地說明設置的過程，提供一個學習的出發點。\n設定 Self-hosted Runner 在你的 Github Repo 上，開啟 Setting \u0026gt; Actions \u0026gt; Runners，然後選擇 Add new self-hosted runner。 裡面會指示你在自己的 Runner 上下載 runner package，然後使用 ./config.sh 設定。 設定時需要提供 repo url 跟驗證用的 token，讓 Github 允許這個 Runner 加入到 repo 中。\n設定時，config.sh 會詢問是否要為該 runner 加入標籤，標籤可以讓 workflow 在執行時能夠篩選可以分配工作的 runner， 在這裡，我新增了一個 development 的標籤，用來表示這是部署測試環境用的主機。\n完成以上步驟後，就可以在 runners 列表看到自己的 runner。\n最後需要啟動 runner。Github 雖然指示你直接執行 ./run.sh，但這種方式會佔據你的 shell process，因此更好的方式是把它設定成一個 systemd service。 runner package 提供 ./svc.sh 可以快速地幫助你安裝 service 然後啟動。\n$ sudo ./svc.sh install $ sudo ./svc.sh start $ sudo ./svc.sh status 可以回到 Runners 頁面查看自己的 runner status 是否顯示上線。\n撰寫 Workflow 在 .github/workflows 目錄中，每一個 .yml / .yaml 檔案就代表了一個 workflow。以下我們先釐清 workflow 中的層級概念：\n一個 workflow 代表一套想要完整執行的目標，Github Actions 執行時就是以一個 workflow 為單位去完成。 一個 workflow 由一個或多個 jobs 組成。Github Actions 會以 job 為單位，派發到 runner 上，因此一個 workflow 中的 jobs 可能是由不同的 runners 協力完成的。jobs 之間可以定義依賴關係（哪個 job 必須在哪個 job 完成之後才能開始。） 一個 jobs 由多個 runs / actions 構成。run 是一個想要機器執行的 shell command process，actions 本質上也是一個 process，只是由 Github 上的其他 action 專案包裝好一個特定的行為。runner 會按照順序執行指定的 runs / actions。 接下來我們以上面部署 devblog 為例子：\nname: Deploy site to self-hosted development server on: push: branches: - dev workflow_dispatch: permissions: contents: read concurrency: group: development cancel-in-progress: false jobs: build: runs-on: [self-hosted, development] steps: - uses: actions/checkout@v7 with: submodules: recursive - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci - run: npm run build - run: | mkdir -p /srv/devblog/release/${{ github.sha }}/devblog rsync -a --delete public/ /srv/devblog/release/${{ github.sha }}/devblog rm -f /srv/devblog/current ln -s /srv/devblog/release/${{ github.sha }} /srv/devblog/current on: workflow 的觸發條件。例如可以設定在某個 branch 有 push 時、某個 branch 的 PR 被創建時、符合特定 Regex 的 Tag 被建立時，或是允許管理者手動觸發。 permissions: 允許 runner 什麼樣的權限。有些 workflow 只是做 testing 時，通常只需要 read 權限就夠了，如果是想要達成自動 commitment 的情境，可能就需要允許對 Repo 進行寫入。 jobs: 在這裡面定義你的每一個 job。上面的範例中，只有一個 job-id 為 build 的 job。 jobs.\u0026lt;job_id\u0026gt;.runs-on: 這個欄位可以指定 job 要分配給什麼樣的 runner。上面使用了 self-hosted 代表只運行在自架的 runner 上，然後 runner 還必須有 development 這個標籤。這個標籤是我用來代表開發伺服器的標籤。假設我的專案今天想要分別有測試環境跟正式部署環境，那麼我就可以有兩台不同的 runner，並且給他們不同的標籤來區分。 接下來，我們可以看到 steps 裡面有五個 uses / run 子項目，runner 會依序執行這五個 action / shell command。讀者可以從字面意思了解執行內容，白話來說就是以下的步驟：\n用 actions/checkout@v7 clone 出 repo 中的內容。 用 actions/setup-node@v4 建立 nodejs 環境。 安裝專案的 node packages。 執行專案的 build 流程。 把生出來的靜態檔案整包丟到 /srv/devblog/release 裡面交給 nginx 處理。 瞭解目錄結構 我在撰寫 workflow 時遇到的一個小難題就是搞不懂 runner 如何管理目錄結構，因此在這邊簡單說明整理。\n假設 runner 的路徑為 ~/my-runner，repo 名稱為 my-repo，那麼你的每一個 step 預設的 working directory 都是 ~/my-runner/_work/my-repo/my-repo/\n讀者可能會疑惑「為什麼路徑裡面會有兩層 my-repo 目錄？」第一層存在的目的是因為一個 runner 可能可以接受來自不同 repo 的 job。例如今天 repo-A 跟 repo-B 都登錄了這個 runner，那麼 ~/my-runner/_work/repo-A/ 就會用來放置執行 repo-A 的 job 產生的檔案，而 ~/my-runner/_work/repo-B/ 亦同。第二層則是因為 job 可能可以 checkout 多個 repo，例如 ~/my-runner/_work/my-repo/my-repo/ 跟 ~/my-runner/_work/my-repo/another-repo/。\n如果想要修改 working directory，可以透過 jobs.\u0026lt;job_id\u0026gt;.steps[*].working-directory 來設定該特定步驟的 working directory，或是透過 jobs.\u0026lt;job_id\u0026gt;.defaults.run.working-directory 來為整個 job 修改預設的 working directory。\n環境變數與 context Github 有兩種方式可以傳遞資訊給 runner。\n首先先介紹環境變數 (environment variable)。這裡的環境變數指的就是 runner process 會看到的作業系統環境變數。我們可以給一個 step、一個 job 或是整個 workflow 指定環境變數。看一下以下的例子：\nname: Greeting on variable day on: workflow_dispatch env: DAY_OF_WEEK: Monday jobs: greeting_job: runs-on: ubuntu-latest env: Greeting: Hello steps: - name: \u0026#34;Say Hello Mona it\u0026#39;s Monday\u0026#34; run: echo \u0026#34;$Greeting $First_Name. Today is $DAY_OF_WEEK!\u0026#34; env: First_Name: Mona 這個 workflow 在三個不同的層級都設定了一些環境變數，這些變數會傳遞給 runner，並套用在 runner 執行 step 啟動的 process 上，因此該 echo 指令將會輸出：\nHello Mona. Today is Monday! 另一方面，上下文變數 (context variable) 則是 Github Action 在 workflow 被執行時，Github 自動帶入的數值。舉例來說，我上面 devblog 的 workflow 有這麼一行：\nmkdir -p /srv/devblog/release/${{ github.sha }}/devblog 其中的 ${{ github.sha }} 就是 github 這個 context 的 sha 屬性，代表觸發 workflow 的 commit 之雜湊值。Context variables 可以取得 workflow 相關的各種資訊，還有 repository variable 等等。可以參考 Github Context reference 了解有哪些 context 可以使用。\nContext variable 跟 environment variable 不同之處在於，context variable 在 workflow 開始時會由 Github 決定好數值並填入，runner 並不知道 context variable 的存在。以上面的指令來說，runner 的 shell 實際上收到的指令是 mkdir -p /srv/devblog/release/abc123.../devblog，因此 runner 無法取得有關 context 的資訊。\nReference https://www.reddit.com/r/devops/comments/1favu4a/why_is_github_actions_doubling_my_working/ https://docs.github.com/en/actions/reference/workflows-and-actions/variables https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#github-context\n","permalink":"https://blog.iceyic.dev/post/github-action-intro/","summary":"以自架伺服器部署 Devblog 為例，介紹如何設定 GitHub Actions Self-hosted Runner，並透過 Workflow 自動建置與部署網站。從 Workflow 的基本結構、Runner 的目錄管理，到環境變數與 Context 的差異，逐步說明 GitHub Actions 的運作方式。","title":"Github Actions 實例：自架伺服器自動部署 Blog"},{"content":"基本介紹 今天要學習的指令是dd。這個工具的 man page 相當簡短，只有152行內容，但它是一個小而精美的工具：從一個檔案讀取內容，然後寫入到另一個檔案，byte to byte，就是這樣一個很 low-level 的操作，筆者推薦各位一定要親自閱讀一下 man page。它的基本用法是：\ndd if=/path/to/input_file of=/path/to/output_file 這個指令讓dd從 /path/to/input_file讀出 bytes，然後寫入到/path/to/output_file。這兩個選項if跟of是最常被指定的選項，若不指定，則dd會分別從stdout跟stdin來讀寫。\n我們可以看到在dd指令的格式中，都是以arg=value的形式在傳遞參數的，一些其他常用的參數有：\nbs=BYTES: 指定dd一次讀和寫多少 bytes，也就是所謂的 block size，預設是 512。 ibs=BYTES, obs=BYTES: 和 bs 類似，但分開指定輸入的 block size 和 輸出的 block size。 count=N: 指定最多讀寫N個 block (總共是N * block size個 bytes)。 status=LEVEL: 資訊輸出，例如我們經常指定值為progess讓他顯示目前進度。 conv=CONV: 有關dd的輸入輸出轉換行為都在這邊可以調整，多個值用逗號分隔。常見的值有： nocreat: dd預設若找不到輸出檔案，則會自行創建，使用這個選項可以讓dd只在輸出檔案存在時才執行，避免不必要的檔案複製。 noerror: 即使發生讀取錯誤也繼續執行。 sync: 根據 man page，這個選項可以使當 input block 讀進來大小不滿 ibs (input block size) 的時候，在尾端 padding NUL，讓內容可以實際上和 block 對齊。 (註：我不太理解為什麼這個選項叫做sync，不過這在硬碟複製的時候蠻常用的，參考：這篇StackExchange) iflag=FLAG, oflag=FLAG: 指定在dd open file 的時候 (記得讀寫檔案之前都需要先 open() 它們嗎？) 要傳入什麼相關的 flag。 一些使用情境 磁碟複製 假設我們想要將/dev/sda磁碟內容完整地複製到/dev/sdb中，我們可以使用以下指令。\ndd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress 這裡的 sync，根據 Archlinux Wiki 的說法，可以想成是 data offsets stay in sync。\n注意由於我們完整複製了硬碟的內容，這兩個硬碟的UUID也會相同，因此若後續要做使用，則應該先透過tune2fs或mkswap等工具隨機生成UUID。\n備份 MBR (Master Boot Record) MBR 位於硬碟中的前 512 bytes，我們可以透過類似的指令將 MBR 存成檔案：\ndd if=/dev/sdX of=/path/to/mbr_file.img bs=512 count=1 將 iso 燒錄到 USB 碟上 製作開機碟也是dd可以做到的事：\ndd if=ubuntu.iso of=/dev/sdX bs=4M conv=fsync oflag=direct status=progress 這裡的fsync選項讓dd確保內容的確物理地寫入到磁碟上再結束，而非可能只寫入到 kernel buffer 中。direct則與 syscall open()的 flag O_DIRECT有關。(這個flag是一個我認為有點難解釋清楚的的 flag，可以詳見這篇StackOverflow)\n將硬碟上的內容全數寫為零 我們可以利用/dev/null來讀出無限量的0位元：\ndd if=/dev/null of=/dev/sdX bs=4M status=progess 就能把整個磁碟寫滿 zero-bit 了。\n小結 dd指令本身不太複雜，但學會使用它可以幫助你在很多資料簡單搬移的情境中省去使用其他特化工具的麻煩，另外關於 iflag、oflag 的部分，主要都跟 syscall open() 的 flag 設定有關，筆者目前對相關內容不太熟悉，有興趣的讀者可以去了解相關 flag 可能造成的影響。\n","permalink":"https://blog.iceyic.dev/post/command-everyday/day3/","summary":"dd 這麼方便的小工具你一定要會！","title":"一天一指令 Day 3 - dd"},{"content":"今天要來介紹的是htop。htop可以用於監控主機資源和 process 並進行一定程度的管理，例如可以向 process 發送 signal、改變 nice value、設定 CPU Affinity 等等功能。\n這個工具可以說是top的進化版。top隨 Linux distro 預先安裝，但htop相較top有更良好的介面、功能及可自訂性，支援滑鼠操作，因此大部分機器上也會安裝htop。htop有使用ncurses這個 library 來編寫，因此它提供了相當完整的 TUI 介面。\n主要介面 輸入htop之後，會進入到主頁面：\nhtop 主要的介面\n先來介紹這個頁面上有什麼東西。最上面的區域稱為 Meters ，顯示主機資源的使用狀況，例如 CPU 使用百分比、Memory \u0026amp; Swap 使用量、Task 跟 Thread 的數量、平均 CPU 負載量 (過去1min、過去5mins、過去15mins)。\n下面的大視窗是一個 Screen，可以使用Tab來切換各個 Screen，例如圖中有兩個 Screen：main 和 I/O。\n每個 Screen 都會有一系列的 Columns，htop會找出主機上的所有 process/thread，並且列在 Screen 中，而這些 Columns 則決定了要顯示關於 process/thread 的哪些資訊。\nMeters 上面要顯示的資訊，以及 Screen 顯示的 Column，都可以自行做設定，只要按F2進行 Setup 即可。操作方法直觀，因此這邊不多贅述。\n快捷鍵與你會想知道的功能 htop的優秀之處在於它非常容易上手。頁面最下方大部分時間都會顯示可以使用的按鍵以及對應的操作。但為了方便，我們這邊介紹幾個可以幫助提升效率的快捷鍵。\nh for help: 顯示幫助頁面 在主頁按下h可以顯示幫助頁面，功能跟F1一樣。幫助頁面有彙整了精簡的有用資訊，例如 CPU usage / Memory usage 的條狀圖中各個顏色代表什麼樣的使用、process state 的字母縮寫分別代表什麼、各快捷鍵的功能等。\nhtop 幫助頁面\nt for tree: 將 process/thread 以樹狀層級表示 在主頁按下t便可以讓 Command 欄位的內容以 process parent/child 或是 process / thread 以他們的父子關係排列並顯示出來。可以進一步用-來摺疊。\n讓 thread 以不同顏色顯示 實際上在htop中，每一筆 record 其實是代表一個 thread，而非單一的 process。我在這裡推薦把 Setup \u0026gt; Display Options \u0026gt; Display threads in a different color打開，這樣如果是一個某 process 的子 thread 的話，他的 Command 就會以綠色顯示，而如果是 process 本身 (或者也可以叫他 main thread)，則會維持白色。\nH for Hide: 隱藏 user threads 有時候 threads 數量太多了，而且我們其實比較需要 process 本身相關資訊的時候，可以按H (大寫，也就是shift + h)，來隱藏 threads。\nu for user: 顯示特定 uesr 的 process 當你想只顯示特定使用者的 process 時，按下u便會在左側顯示一個選單，讓你選擇要顯示哪一個使用者。如果使用者太多，你可以在按下u之後直接用鍵盤輸入 username，htop會幫你跳到符合的選項。\n更快速地 sorting 我們知道F6的 SortBy 功能可以選擇一個 Column 並根據那個 Column 進行排序 (或是用滑鼠點 Column 項目也可以)，但如果我們想要快速地用鍵盤做到這件事呢？ 某幾個常用項目中，htop其實有提供快捷鍵來讓我們排序：\nN (shift + n): 以 PID 進行排序 M (shift + m): 以 MEM% (memory usage) 進行排序 P (shift + p): 以 CPU% (processor usage) 進行排序 T (shift + t): 以 TIME 進行排序 另外，I (shift + i) 可以改變排序的升降。\nF for Focus: 跟隨一個 process 將游標移動到你想關切的 process 上，按下F (shift + f) 之後，該 process 會變為黃色底，這時候htop會保持這個 process 持續地出現在畫面中，直到你移動游標，取消 focus 狀態。\n對多個 process 同時進行操作 我們可以對 process 發送 signal，或是調整 nice value，但如果我想一次對很多 process 做一樣的操作該怎麼辦？\n使用 space，你會發現游標當前指向的 process 的文字會變為黃色，這代表這個 process 被 \u0026ldquo;tag\u0026rdquo; 了。使用c的話，則可以 tag 該 process 以及它所有的 children。\ntag 完畢後，進行的操作便會全部套用在選擇的 processes 上。若要取消，則使用U進行 Untag。\ntagging 完之後的樣子\ns for strace, l for lsof s可以利用strace查看 process 的 live syscalls；l可以裡用lsof查看 process 的 open files。\n後記 在寫這邊的時候我突然想到：「如果htop的每一個 record 是代表一個 thread，為什麼屬於同一個 process 的 thread 卻有不同的 PID 呢？」這個問題的答案，簡單來說，是因為在 Linux 中，PID 實際上是用來 identify task_struct結構的：每一個task_struct都有自己獨一無二的 PID。而 TPID (Thread Group ID) 其實才是真正意義上的 \u0026ldquo;Process ID\u0026rdquo;。在 Linux 中，main thread 的 TPID 會與它的 PID 相同，而它所產生的其他 threads，則會使用相同的 TPID，但由於不同的 thread 實際上就是不同的task_struct，因此就會有不同的PID了。\n關於這些 \u0026ldquo;___ID\u0026rdquo;，有機會之後可能可以另外寫一篇來了解了解。\n","permalink":"https://blog.iceyic.dev/post/command-everyday/day2/","summary":"關於 htop 的便捷小技巧","title":"一天一指令 Day 2 - htop"},{"content":"前言 之所以想要開啟這個系列，主要是因為發覺自己在使用很多工具程式，需要某種特定用法的時候，幾乎每一次都會打開 man page 或是 Chrome 去搜尋正確下 OPTION 的方法。雖然看似只是小問題，但我覺得這件事其實會影響整體工作效率。另一點就是因為有個系上同學都會知道很多「原來這個工具還有這樣的功能！」因此這個系列希望可以每天找一個常用的Linux指令，來學習它具有的各種功能，並嘗試記住然後使用在日常工作中。\n今天要學習的是grep。\ngrep grep可以在檔案或是stdin中用regex搜尋內容。\n首先來看SYNOPSIS：\ngrep [OPTION...] PATTERNS [FILE...] grep [OPTION...] -e PATTERNS ... [FILE...] grep [OPTION...] -f PATTERN_FILE ... [FILE...] PATTERNS 是你要搜尋的regex表示式，FILE 則是要搜尋的檔案。 FILE 可以一次輸入多個，grep 會搜尋每一個檔案。 PATTERNS 也可以多個，但是要使用選項-e來重複指定。\n$ grep -e pattern1 -e pattern2 file.txt 如果檔名為-，則會改為讀stdin。如果沒有給檔名，那麼會有兩種情況：\nnonrecursive search: 讀stdin recursive search: 讀 working directory 常用 Options r for recursive: 遞迴搜尋整個目錄 使用-r便可以遞迴地搜尋目錄內的所有檔案，這在尋找C code中的definition時會很好用。 使用-r時，也會預設開啟-H選項：顯示各行所屬的檔名。 -R與-r類似，但會 follow symbolic link。\ngrep -r \u0026#34;sys_open\u0026#34; xv6/kernel $ grep -r \u0026#34;sys_open\u0026#34; xv6/kernel xv6/kernel/syscall.c:extern uint64 sys_open(void); xv6/kernel/syscall.c:[SYS_open] sys_open, xv6/kernel/kernel.asm:000000008000558e \u0026lt;sys_open\u0026gt;: xv6/kernel/kernel.asm:sys_open(void) xv6/kernel/kernel.asm: 800055b0: 0c054163 bltz a0,80005672 \u0026lt;sys_open+0xe4\u0026gt; xv6/kernel/kernel.asm: 800055c2: 0a054863 bltz a0,80005672 \u0026lt;sys_open+0xe4\u0026gt; xv6/kernel/kernel.asm: 800055d6: cbdd beqz a5,8000568c \u0026lt;sys_open+0xfe\u0026gt; ... grep: xv6/kernel/sysfile.o: binary file matches grep: xv6/kernel/syscall.o: binary file matches grep: xv6/kernel/kernel: binary file matches 但是注意在上面的例子中，有些檔案不是文字檔，因此grep會回傳錯誤訊息。\ns for suppress: 不顯示錯誤訊息 使用-s可以讓grep不顯示檔案不存在，或是檔案無法讀取等錯誤。\nn for number: 顯示行數 使用-n可以顯示matching line在檔案中的行數。\ni for ignore: 忽略大小寫差異 使用-i可以在matching時忽略大小寫。\negrep、fgrep 和 rgrep 有時候我們會看到grep的變種，這些變種實際上等價於grep加上一些選項：\negrep: 等價於grep -E，使用 extended regular expression。 rgrep: 等價於grep -r。 fgrep: 等價於grep -F，將 PATTERNS 視為普通字串，而非 regex。 這些指令實際上為 deprecated 狀態，保留它們只因相容性問題，實務上時盡量避免使用。\n","permalink":"https://blog.iceyic.dev/post/command-everyday/day1/","summary":"關於grep的常用功能及常見問題","title":"一天一指令 Day 1 - grep 和他的兄弟：egrep、fgrep、rgrep"},{"content":"前情提要 大一還懵懂無知的iceyic，在各種推薦文與學長姊的介紹之下，誤打誤撞進入了這門名為NASA的神祕課程。 雖然課程內容繁重，但iceyic仍然很高興能夠學到如此實用又多樣化的電腦管理技能。 殊不知，一場暴風雨即將改變安寧，iceyic接下來要面對的，是一場殘酷的腥風血雨\u0026hellip;\n以上\u0026hellip;全部都是瞎掰的。想知道前幾週發生什麼事的可以參考我上一篇文章。\n第5週：虛擬機器、Docker和Kubernetes 本週的教學內容就是虛擬化技術了。入門最常接觸到的我想大概就是VirtualBox了， 不過在實務環境中，通常使用Linux作業系統，這次的作業就使用QEMU-KVM，然後再使用virsh指令工具，透過libvirt API來進行管理。 libvirt是一個C Library，因為Hypervisor的種類繁多，例如：KVM、IOS的Hypervisor.framework、Xen等等， 每個工具的參數眾多，難以使用，因此，libvirt就為了方便管理而誕生。\n什麼是Hypervisor呢？QEMU這類的模擬器，是運用程式來模擬電腦中CPU的運算、暫存器和記憶體的狀態、網路卡的狀態等等手法來達到虛擬機的效果，實際上，我們電腦的CPU沒有真正「執行」到虛擬機裡的CPU instruction。可以預料到，這樣的效能會相當差。 Hypervisor是一種特別的軟體，能夠讓CPU直接「執行」虛擬機中的instruction，因此得以實際上讓虛擬機能夠享用CPU的運算資源。\n課程的後半段，就是在討論近段時間崛起的容器化技術了，Docker就是一個非常廣泛且普遍的容器化服務。Container的優點就是體積小，建置方便，很多常用的伺服器在Docker Hub上面都找得到可用的image，例如：反向代理伺服器nginx、資料庫伺服器MySQL server，甚至是minecraft server！\n而更進階的container管理工具就是Kubernetes了，又簡稱K8s。他提供的功能強大，包括：使用複製容器(replicas)讓上線容器在出狀況時能快速替換、負載平衡、CI/CD等等。Kubernetes是在企業環境中廣泛使用的工具，因此在這邊不多加贅述。\n本次的作業就是試著使用Virsh在系工作站開啟一個虛擬機器，並且在這個虛擬機器中用docker以及kubernetes啟動一些服務。 照著各工具的Docs提供的選項來設定應該不會過於困難。\n第6週：Network Layer 接續第3週的Link Layer，這週我們的上課內容是討論Network Layer。我們先來複習一下Ethernet Frame的結構：\n乙太網路封包的結構\n其中，Payload的部分，正是Ethernet Frame所要攜帶的資訊，除了像是之前介紹的ARP就是放在這裡面以外，還有大名鼎鼎的IP也是。\nIP packet中包含了許多資訊，重要的幾個有：\n來源IP及目的IP 上層協議，例如：UDP、TCP checksum TTL IP packet fragmentation所使用的 3 bit flags + 13 bit offset etc\u0026hellip; 其中注意到Fragmentation，這是IP在data太大時，為了分割內容所使用的技巧， 這16個bit可以告訴其他網路設備是否有其他片段、可否進行fragmentation、這個片段是從data的第幾個byte開始。\nIP位址分為兩個部分：網路位址跟主機位址，使用設定好的子網路遮罩便可知道IP位址中到哪個bit的範圍是網路位址，剩下的部分就是主機位址。 網路位址跟主機位址的關係就好像是一個巷子裡會有好幾號的住戶，這些住戶住得近，可以直接彼此交流。\n當主機想要發送IP封包時，它得要透過乙太網路來發送嘛，這時就要先決定目的MAC address，這要怎麼做呢？ 首先，他會檢查目的IP位址是否跟自己主機網卡的IP位址擁有同樣的網路位址，同樣的網路位址代表它們在Link Layer中是同一個network的， 此時，就可以使用ARP來取得目標MAC address，最後成功將封包送出。 如果網路位址不同，我們就會嘗試將封包送到預先設定好的「default gateway」這個IP位址，通常會是你路由器的IP位址，讓路由器替你轉送， 此時，目標MAC address就會是路由器的MAC address。\n以上就是IP大致上運作的方式。除此之外，課程中還討論了ICMP、DHCP、NAT等協定，不過它們不複雜，在理解IP之後都可以快速理解，這裡不多贅述。\n作業時間！本週的作業是OPNSense，一款開源的防火牆軟體，我們會在自己的電腦上架設一個OPNSense和兩台虛擬主機， 用來模擬一個區域網路中防火牆控制流量的功能。OPNSense有提供Web GUI讓我們操作設定，所以不花太多時間就能習慣操作。\n第7週：Transport Layer、DNS 有了網路協定(IP)後，我們能夠傳遞資訊給任何一台已知IP的主機了，接下來呢？假設A主機想傳送3個bytes給B主機，他要怎麼做？把資料放進IP packet中送出嗎？ 如果這3個bytes非常重要，幾乎不容許出錯呢？A主機要多送幾次嗎？要送幾次？10次？50次？這樣會造成網路壅塞嗎？\n從上面的假想問題可知，我們的確需要幾套可以讓各個主機遵循的固定流程，來進行有效通訊。 而Transport Layer最具代表性的協定就是UDP與TCP了。\nUDP的核心概念就是「不管怎樣把資料送過去就對了」。UDP frame中只有四個欄位：\nsource port destination port data checksum UDP是IP之上的協定，所以實際上這個UDP frame是放在IP packet中的data欄位來進行傳輸的。 UDP frame中提供的資訊，大概就只能讓對方主機知道封包是給哪個程式、以及資料是否有誤。這可以幹嘛呢？ 舉例來說，語音通訊時，因為聲音是連續的資訊，即使丟失了一部分的資訊也不會造成嚴重的影響，因此通常使效率較高(與TCP相比之下)的UDP。\nTCP更為複雜，簡明扼要地說明就是，TCP一開始會進行一個名為「三方交握(Three-way Handshaking)」的流程，讓雙方都知道這個TCP連線建立了。 接著，每次傳送資料時，接收方都要回應一個對應的封包來表明自己的確有收到，否則傳送方就必須要把那個可能缺失的資料重新傳一次。\nDNS最主要的功能就是將域名轉換成IP，這可以透過名為DNS resolver的主機、以及name server的主機做到。電腦想查詢一個domain name的IP時， 會發送DNS請求到一台DNS resolver，要使用哪一台resolver可以由使用者自行設定，或是取得IP時DHCP伺服器指派。 DNS resolver接著會「遞迴地」跟各個層級的name server詢問，最後得到答案後，resolver可以將結果cache起來，以便之後重複拜訪時可以快速回應。\n作業時間！儘管業界目前大部分都使用Bind 9架設DNS為主，不過這次我們要的是使用PowerDNS，並且練習使用dig指令來進行DNS查詢。 由於PowerDNS有提供Web GUI，因此設定起來相對簡單。\n第8週：期中考 期中考之前會先以三人一組事先分好，每個人的考試成績就是該小組的分數。\n期中考以上機操作的形式進行，題目基本上涵蓋了第1到7週的各個主題，其中也包含需要操作額外設備的題目(例如:一台無法開機的電腦，需要在考試中找出問題並修復)。 考試時間只有3小時，題目卻有8題，因此事前的分配是很重要的，通常會3個人下去分配自己要負責哪些主題，然後考前可以專心研究該主題的一些指令、觀念和cheatsheet。 滿分100分，最高分的組別拿到了43分，可知考試的難度非同小可。不過根據其他網站上的消息，通常期末的分數會好拿很多，希望到時可以彌補期中的分數漏洞。\n(待續\u0026hellip;)\n","permalink":"https://blog.iceyic.dev/post/nasa-2/","summary":"關於NASA(網路管理及系統管理)課程的內容摘要、筆記與心得，之二","title":"台大資工NASA修課心得(2)-第5~8週"},{"content":"什麼是NASA？跟太空有關嗎？ 網路管理及系統管理(Nerwork Administration \u0026amp; System Administation)，英文縮寫NASA，跟某太空總署一點關係也沒有。 這是一門台大資工系學士部開設的課程，當你修完這3學分後，視學習成果而定，有機會能參加更進一步的培訓，最後甚至能參與系上的網路與系統管理團隊。 不過這門課在系上也是出了名的「充實」，據上這門課的蔡教授本人稱，每週平均會花10~20小時在這門課的作業上。 而且上課內容通常是比較概論性的(畢竟一個Topic怎麼可能在短短三個小時內講完呢)， 而作業通常要求實作，因此很考驗學生在網路上搜尋、看Docs、以及解決問題的能力。\n這個系列我大概會分四篇出，一篇寫4週的內容，四篇恰好可以完成16週的課程。\n第0週：想修NASA嗎？先做作業吧 雖然前面說這門課很難，但想加選的人依舊絡繹不絕，因此寒假期間會公佈Homework 0，給了大概一個月左右的時間讓想選課的人完成， 最後會依據分數來錄取約70人左右得以參加後續的正式課程。據教授所述，最後加入課程的學生都至少在HW0中拿到100分(滿分為103分)。以下說說筆者今年的情形。\nHW0分成NA(網路管理)與SA(系統管理)兩個大章節。NA主要是問答題，內容大概是一些網路常見協定或網路相關的基本知識，包括但不限於：\nVPN NAT IP/TCP DDoS HTTP/HTTPS \u0026hellip;more 諸如此類。不過問題通常不會太細節，主要是考驗你對這些名詞有沒有一個基本的認識。\nSA部分則是提供了一個Arch Linux虛擬機，學生要在虛擬機中用sh command，找出各個題目要求的flag：一組特定的答案字串(總之就是CTF啦)。 這邊的題目需要的操作不會太難，指令通常很簡單，資工人不能不會的那種，像是切換目錄、連ssh、調整檔案權限等等，網路基本上都找得到。\n第1週：系統管理都在幹嘛？ 第1週時，HW0還沒有完成批改，因此本週沒有作業：)\n這週的課程主要在討論作為一個「系統管理者」，我們該如何安排我們的計畫。這聽起來簡單，但系統管理最重要的就是「防範未然」以及「處理突發狀況」的能力。 假設今天NTU COOL掛了，全校的師生沒辦法繳交作業、觀看上課錄影、甚至是在期中考週時，在NTU COOL上進行期中考的課程該怎麼辦？ 這些都是作為System Admin都要處理的課題。\n教授舉了幾個以前管理NTU COOL時的例子，最重要的是必須要有計畫。硬碟內的東西一定要有備份，這樣當某顆硬碟壞掉時，才不會丟了重要的資料。 升級韌體與設備時，必須考慮到如果無法正常運作，那要麼樣恢復為原本的配置與版本？這些都是必須進行事先規劃，才能一邊更動服務的同時，又讓服務持續在線。\n第2週：Bash Script 這週的課程主題就是Bash Script啦！Bash是一種unix shell，接收使用者輸入的指令來進行各種互動，而Bash Script就是這些指令的腳本了。 只要將想執行的一連串指令先寫成檔案，需要使用時只要將腳本傳給Bash，就能直接執行裡面的內容，因此，只要會使用Bash Script，就能將許多開發時的固定流程給自動化。 課堂上簡單講解了變數、If-Else、For跟While迴圈、Function，以及一些常用指令的列表。Bash的指令多且複雜，因此可以搭配Bash Cheatsheet當作小抄，以及shellcheck幫助找出錯誤以及風格問題。 另外，為了要在終端機環境中也能編輯檔案，我們也要學習使用Vim，在終端機中只用鍵盤完成文件編輯。Interactive Vim Tutorial這個網頁可以讓你對Vim的操作有基本的認識。\n第3週：磁碟分割 這一週的主題是磁碟分割。要完成這週的作業，主要需要使用的技能為：\n使用fdisk幫磁碟建立GPT partition table並切分磁區 使用mdadm建立RAID磁碟陣列 使用cryptsetup幫磁碟加密 使用lvm建立虛擬磁區 在空無一物的磁碟上安裝Archlinux並成功開機 實際上，上課內容以及lab都只提到了1~4點的內容，真正的難點在第5點。這是目前我在NASA課上花最多時間研究的一週，這個Archlinux安裝的部分也占了我繳交出去作業報告中一半的頁數\u0026hellip;\nArchlinux沒有像Ubuntu那樣的安裝光碟或映像檔，因此連boot loader(在本次作業中使用GRUB)以及initramfs都要自行裝在硬碟中。認識電腦開機流程後，基本上做法可以參考Archlinux wiki的各種頁面來學習。(花了很多時間在看這些頁面呢。)\n第4週：乙太網路、VLAN 這週是第一次NA領域的課程，介紹了IP/TCP Model中最基礎的Link Layer。目前最常用的Link Layer技術就是乙太網路了。 乙太網路以前一般是用同軸纜線構成的，意思就是同一個網路的電腦都連接在一條大纜線上，但是纜線一次只能傳送一個封包的訊號， 兩個封包在纜線中會彼此干擾訊號，稱之為碰撞(Collision)，所以我們通常使用CSMA/CD協定來偵測及避免碰撞。\n另一個有關Link Layer的技術就是VLAN。我們可以設定連接網路的交換器，為各個端口指定一個VLAN編號， 然後在轉送封包時，只轉發那些來源端口跟目的端口擁有相同VLAN的封包，就能在一個實體網路中隔離出多個虛擬的網路。\n這週的Lab與Homework要使用Cisco Packet Tracer來練習用cisco cli設定交換器。 除了最重要的設定VLAN外，也要設定交換器的ssh連線和使用者密碼，整體來說是個相對簡單的作業。 Cisco Packet Tracer的使用介面\n(待續\u0026hellip;)\n","permalink":"https://blog.iceyic.dev/post/nasa-1/","summary":"關於NASA(網路管理及系統管理)課程的內容摘要、筆記與心得","title":"台大資工NASA修課心得(1)-第0~4週"},{"content":"前言 升上大學之後，開始覺得想要紀錄一下自己學習的軌跡，另一方面也可以將習得的知識寫成文章，日後便可以自己回頭複習檢視， 甚至讓其他和我一樣遇到相同瓶頸的人得到解決方法。再者，撰寫自己的Blog大概也算是某種品牌經營吧？ 諸如此類，現在這個網站才會誕生啦！\nFramework選擇 到2024年3月為止，比較主流的靜態網頁生成框架有：\nJekyll Hexo Hugo Pelican 我最後選擇使用Hugo來架設這個Blog，主要是它在Github上面的Stars蠻多的，而且很多人都因它在速度上的優勢推薦它。\nLogo of Hugo\nTheme選擇 我選擇使用PaperMod這個Theme，我個人偏好這種簡潔的版面設計， 而且調整起來相當簡單，不需要花費太多心力就能了解它提供的Features。\n設定Markup Parser Hugo預設使用Goldmark這個Parser來將以Markdown寫成的文章在生成網頁時轉換為適當的html。 其中我想修改的是這一項：\n# config/_default/markup.yaml goldmark: renderer: unsafe: true 當unsafe選項改為true時，markdown中的html標記會直接被保留，才能使用像是\u0026lt;mark\u0026gt;之類的html tag。\n$\\LaTeX$ 作為一個devblog，需要用到數學式子的日子肯定不會少，就算沒有用到也沒關係，我仍然會加入$\\LaTeX$的功能。\n這邊我使用$\\KaTeX$這個Library來達成，將一些script放在我們的html head中，它會自動尋找內文中所有使用delimiter的部分，並將其替換成適當的元素。 KaTeX使用的js function可以自行傳入想要使用的delimiter，這樣我們就可以根據自己的偏好進行設定了。\n\u0026lt;!-- layouts/partials/entend_head.html --\u0026gt; {{ if or .Params.math .Site.Params.math }} \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\u0026#34; integrity=\u0026#34;sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34;\u0026gt; \u0026lt;script defer src=\u0026#34;https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\u0026#34; integrity=\u0026#34;sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script defer src=\u0026#34;https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\u0026#34; integrity=\u0026#34;sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, function() { renderMathInElement(document.body, { // customised options // • auto-render specific keys, e.g.: delimiters: [ {left: \u0026#39;$$\u0026#39;, right: \u0026#39;$$\u0026#39;, display: true}, {left: \u0026#39;$\u0026#39;, right: \u0026#39;$\u0026#39;, display: false}, {left: \u0026#39;\\\\(\u0026#39;, right: \u0026#39;\\\\)\u0026#39;, display: false}, {left: \u0026#39;\\\\[\u0026#39;, right: \u0026#39;\\\\]\u0026#39;, display: true} ], // • rendering keys, e.g.: throwOnError : false }); }); \u0026lt;/script\u0026gt; {{ end }} 將這段html放置在layouts/partials/entend_head.html中，PaperMod會引用這個partial，將它嵌入在所有生成網頁的head中，我們就可以盡情使用$\\LaTeX$了： $$ e^{i\\pi} + 1 = 0 $$\n設定字體樣式 經過一番挑選之後，中文字體我使用Noto Sans TC，英文字體則使用Ubuntu，兩者都可以在Google Fonts上找到， 這邊一樣透過google fonts提供的CDN，將對應的代碼放入html header中即可。\n\u0026lt;!-- layouts/partials/entend_head.html --\u0026gt; \u0026lt;link rel=\u0026#34;preconnect\u0026#34; href=\u0026#34;https://fonts.googleapis.com\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;preconnect\u0026#34; href=\u0026#34;https://fonts.gstatic.com\u0026#34; crossorigin\u0026gt; \u0026lt;link href=\u0026#34;https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900\u0026amp;family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900\u0026amp;family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700\u0026amp;display=swap\u0026#34; rel=\u0026#34;stylesheet\u0026#34;\u0026gt; 然後再透過css設定樣式。除了字體之外，我也加入了文內超連結的hover特效。\nbody { font-family: \u0026#39;Ubuntu\u0026#39;, \u0026#39;Noto Sans TC\u0026#39;, \u0026#39;微軟正黑體\u0026#39;, sans-serif; } mark { background-color: #edff4c; } /* hyperlink hover transition */ .toc a, .post-content a { transition: all .15s ease-out; } .toc a:hover, .post-content a:hover { background-color: white; color: black; transition: all .15s ease-out; } Google Analytics \u0026amp; Google Search Console 這個兩項服務是我參考學長的Blog時得知的，所以我也試著幫我的Blog完成設定。 設定Google Search Console後，Blog就可以出現在Chrome的搜尋引擎當中了，而Google Analytics則能蒐集網站拜訪者的相關資訊，簡單來說就有點像是數據後台啦。\n後記 從我開始看Hugo Docs到寫完這篇文章為止，過了大概五天的時間。其實Hugo的Documentation我覺得有點小凌亂，參考了不少教學影片跟網路文章才把整體架構搞清楚。 之後大概會連載一些在大學端的修課心得，以及課餘時間摸索的東西。\n","permalink":"https://blog.iceyic.dev/post/first-blog/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e升上大學\u003c/strong\u003e之後，開始覺得想要紀錄一下自己學習的軌跡，另一方面也可以將習得的知識寫成文章，日後便可以自己回頭複習檢視，\n甚至讓其他和我一樣遇到相同瓶頸的人得到解決方法。再者，撰寫自己的Blog大概也算是某種品牌經營吧？\n諸如此類，現在這個網站才會誕生啦！\u003c/p\u003e\n\u003ch1 id=\"framework選擇\"\u003eFramework選擇\u003c/h1\u003e\n\u003cp\u003e到2024年3月為止，比較主流的靜態網頁生成框架有：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eJekyll\u003c/li\u003e\n\u003cli\u003eHexo\u003c/li\u003e\n\u003cli\u003eHugo\u003c/li\u003e\n\u003cli\u003ePelican\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e我最後選擇使用\u003cmark\u003eHugo\u003c/mark\u003e來架設這個Blog，主要是它在Github上面的Stars蠻多的，而且很多人都因它在速度上的優勢推薦它。\u003c/p\u003e\n\u003cfigure class=\"align-center \"\u003e\n    \u003cimg loading=\"lazy\" src=\"hugo-logo.svg#center\"/\u003e \u003cfigcaption\u003e\n            \u003cp\u003eLogo of \u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e\u003c/p\u003e\n        \u003c/figcaption\u003e\n\u003c/figure\u003e\n\n\u003ch1 id=\"theme選擇\"\u003eTheme選擇\u003c/h1\u003e\n\u003cp\u003e我選擇使用\u003ca href=\"https://github.com/adityatelange/hugo-PaperMod\"\u003ePaperMod\u003c/a\u003e這個Theme，我個人偏好這種簡潔的版面設計，\n而且調整起來相當簡單，不需要花費太多心力就能了解它提供的\u003ca href=\"https://github.com/adityatelange/hugo-PaperMod/wiki/Features\"\u003eFeatures\u003c/a\u003e。\u003c/p\u003e\n\u003ch1 id=\"設定markup-parser\"\u003e設定Markup Parser\u003c/h1\u003e\n\u003cp\u003eHugo預設使用\u003ca href=\"https://github.com/yuin/goldmark/\"\u003eGoldmark\u003c/a\u003e這個Parser來將以Markdown寫成的文章在生成網頁時轉換為適當的html。\n其中我想修改的是這一項：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# config/_default/markup.yaml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003egoldmark\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erenderer\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eunsafe\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e當unsafe選項改為true時，markdown中的html標記會直接被保留，才能使用像是\u003ccode\u003e\u0026lt;mark\u0026gt;\u003c/code\u003e之類的html tag。\u003c/p\u003e\n\u003ch1 id=\"latex\"\u003e$\\LaTeX$\u003c/h1\u003e\n\u003cp\u003e作為一個devblog，需要用到數學式子的日子肯定不會少，就算沒有用到也沒關係，我仍然會加入$\\LaTeX$的功能。\u003c/p\u003e\n\u003cp\u003e這邊我使用\u003ca href=\"https://katex.org/docs/autorender.html#usage\"\u003e$\\KaTeX$\u003c/a\u003e這個Library來達成，將一些script放在我們的html head中，它會自動尋找內文中所有使用delimiter的部分，並將其替換成適當的元素。\nKaTeX使用的js function可以自行傳入想要使用的delimiter，這樣我們就可以根據自己的偏好進行設定了。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- layouts/partials/entend_head.html --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ if or .Params.math .Site.Params.math }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003elink\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;stylesheet\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eintegrity\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecrossorigin\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;anonymous\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edefer\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eintegrity\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecrossorigin\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;anonymous\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edefer\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eintegrity\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecrossorigin\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;anonymous\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    document.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;DOMContentLoaded\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003erenderMathInElement\u003c/span\u003e(document.\u003cspan style=\"color:#a6e22e\"\u003ebody\u003c/span\u003e, {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#75715e\"\u003e// customised options\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#75715e\"\u003e// • auto-render specific keys, e.g.:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#a6e22e\"\u003edelimiters\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              {\u003cspan style=\"color:#a6e22e\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;$$\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eright\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;$$\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003edisplay\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              {\u003cspan style=\"color:#a6e22e\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;$\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eright\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;$\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003edisplay\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              {\u003cspan style=\"color:#a6e22e\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\\\(\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eright\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\\\)\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003edisplay\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              {\u003cspan style=\"color:#a6e22e\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\\\[\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eright\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\\\]\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003edisplay\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#75715e\"\u003e// • rendering keys, e.g.:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#a6e22e\"\u003ethrowOnError\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e將這段html放置在\u003ccode\u003elayouts/partials/entend_head.html\u003c/code\u003e中，PaperMod會引用這個partial，將它嵌入在所有生成網頁的head中，我們就可以盡情使用$\\LaTeX$了：\n$$\ne^{i\\pi} + 1 = 0\n$$\u003c/p\u003e","title":"使用Hugo架設個人Blog"}]