PM2 Process 全部消失、Apache 無回應、MySQL 無法啟動:一次 Server 硬碟爆滿的排查紀錄

最近維護一台 Ubuntu Server 時,遇到一個非常詭異的狀況:

  • PM2 的 process 全部消失
  • Apache 沒有回應
  • MySQL 無法啟動

一開始以為是 記憶體不足導致服務 crash,結果一路排查後才發現真正原因其實很單純:

硬碟滿了…..

這篇記錄完整的排查過程。


系統環境

  • Ubuntu Server
  • Apache2
  • MySQL
  • PM2
  • Docker

第一個症狀:PM2 process 全部消失

登入 server 後,第一件事先檢查 PM2:

pm2 list

結果發現,所有 PM2 process 都消失了。正常情況下,PM2 的 process 不太可能全部消失。第一個直覺是:最近有用 Docker,可能是記憶體不足導致 PM2 被系統 kill。

正當這樣想的時候…

第二個症狀:Apache 也沒有回應

接著測試 Apache

systemctl status apache2

Apache 卻顯示正常運作

也確實有在 listen 80 port:

ss -tlnp | grep :80

但奇怪的是 curl 卻完全沒有回應

curl http://127.0.0.1

request 卡住

* Connected to 127.0.0.1
> GET / HTTP/1.1

完全沒有 response…

重啟後問題更奇怪

這時候還想說會不會重開就沒事了 XD 於是決定 reboot server。

重開後檢查服務:

  • Apache – 一樣顯示有在服務,但就是沒有回應(後來發現等久一點有可能有回應
  • PM2 – 沒有回復上次 saved 的狀態

從瀏覽器連進去看看,發現其實可以連上,只是非常慢。這個時候我進入一個需要 MySql 的頁面,發現連 MySql 也掛了,甚至 SSH 也莫名的慢,才想說搞不好是硬碟滿了 XDDD

檢查硬碟空間

先檢查 disk usage:

df -h

結果看到:

Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G   24G     0 100% /

Root disk 已經滿了。

接下來想撈出大檔並排序:

$ sudo du -ah / | sort -rh | head -20

但 ubuntu 回我:

sort: write failed: /tmp/sortwWBWJH: No space left on device

連一點點空間都沒了,沒辦法做 temp 導致 sort 都掛了…

求救 ChatGPT,先用以下方式釋放出一些空間:

清理 apt cache:

sudo apt clean

清掉系統 Log 只保留 500MB 日誌:

sudo journalctl --vacuum-size=50M

清理 Docker

docker system prune -af
docker volume prune -f

這樣就有約 300mb 可使用。

接下來尋找大型檔案:

sudo find / -xdev -type f -size +100M -exec ls -lh {} \;

結果看到:

/var/lib/docker/.../json.log          308M
/home/xxxxx/.pm2/pm2.log              8.0G
/home/xxxxx/.pm2/logs/...error.log    1.4G

問題立刻浮現… PM2 log 爆掉了….

PM2 log 竟然長到 8GB

PM2 預設會持續寫 log。如果沒有設定 log rotation,log 會一直增加。

這台 server 的情況:

~/.pm2/pm2.log   → 8GB
error log        → 1.4GB

直接把小小的硬碟撐爆。附帶一提這台硬碟只有 20G !

來清垃圾囉

先清空 PM2 log:

pm2 flush

或也可以這樣清空:

truncate -s 0 ~/.pm2/pm2.log

另外也發現 Docker log 有 300 多 mb 順便清一下

sudo truncate -s 0 /var/lib/docker/containers/*/*.log

清理後磁碟狀態

再次檢查:

df -h

結果:

Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G   14G  9.7G  59% /

硬碟使用率從 100% → 59%,釋放將近 10GB 空間。

重啟後,所有服務都能正常啟動了!

PM2 log rotation

為了避免再次發生,我們可以安裝 PM2 log rotation 安裝官方 module:

pm2 install pm2-logrotate

設定

pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 10

Docker log rotation

編輯:

/etc/docker/daemon.json

加入:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

然後重啟 Docker。

結論

這次的事件其實是很基本的問題,只是一開始沒料到硬碟竟然被塞滿(忘記他很小),所以排查的方向一直是錯的。在加大硬碟之前,以後排查 server 問題時,第一件事就先跑:

df -h

有時候問題真的比想像中簡單 XDDD

如何觀察 node.js 的記憶體用量

當程式發生異常的記憶體用量,或甚至 memory leak 導致關閉,我們該如何觀察並找出問題呢?其實有個非常方便的方法,也不需要安裝任何額外的工具。首先打開終端機輸入 node --inspect index.js 來運行程式並監視記憶體。

接著到 chrome 網址列輸入 chrome://inspect/#devices,會出現以下畫面:

Chrome DevTools
閱讀全文 如何觀察 node.js 的記憶體用量

LiveTRA 火車時刻表 UI 設計

LiveTRA 是我的一個 side project,開始的契機是某個時期,因時常搭乘台鐵通勤,而台鐵又經常誤點,想說如果可以在出門前就可以先得知月台上的誤點時間,就可以避免匆匆忙忙趕到車站後才發現原來列車誤點的窘況。

Google了一下馬上就找到了當時由交通部主催的「公共運輸整合資訊流通服務平台 (PTX)」API服務(目前已整合進 「運輸資料流通服務 (TDX)」),很快地就做出了第一版,以下是當時第一版的畫面設計。

舊版車站頁
舊版列車頁

舊版的頁面結構中沒有首頁,以台北車站做為預設車站當作首頁。一旦選擇過車站後,便以上一次選擇的車站當作預設顯示車站。且因為想把車站選擇器順便當作標題使用,因此將車站選擇器放在頁面最上面。

當這個版本運行一陣子後,意外地還蠻多人用的,並且收到了一些反饋,同時也因為想優化整個網站的架構,因此做了改版。

新版首頁與車站頁設計

首頁
車站頁

這個版本出現了首頁,首頁會顯示最後兩次選擇的車站的最近一班列車,以及一些子功能的入口。例如:語系選擇、誤點班次列表、列車誤點賠償規則、最近使用車站、熱門車站⋯等等。

閱讀全文 LiveTRA 火車時刻表 UI 設計

關於 vite 與 webpack 的檔案分割

之前一直很好奇 nuxt 是怎麼做到分割檔案的,但一直沒有時間來好好研究。上網搜尋一下之後,得知原來就跟寫 vue-router 想要 lazy load 的時候一樣,只要不用 import xxx from 'xxx' 匯入,而是改用 import('xxx') 的方式匯入,就會被自動分割,達成在需要的時候才 import 的目的,也就是 component 的 lazy loading。

例如當我們在 vue3 裡要宣告一個 component 的時候可以用:const xxx = defineAsyncComponent(() => import('xxx'))

以下的描述是單純以 vite or webpack 來打包程式碼的情況,而非 nuxt。

因此這個 import() 就是關鍵之處。平常較為人知的 import 是只能在一個檔案的最上頭,並且是以 import xxx from 'xxx' 這樣的形式使用。不過以這種方式 import 的話,全部的 code 將都會被打包在一起,output 到你設定的路徑。

閱讀全文 關於 vite 與 webpack 的檔案分割

[單字筆記] Web工程師相關日文單字

因爲本身是Web前端工程師,常常在查找文件或技術文章時,如果中文英文找不到需要的資訊時,也會試試看用日文來找。這時候就會接觸到一些與程式相關的日文單字,以下整理了一些實用且常看到的單字,之後如果有新增也會在這篇更新。

閱讀全文 [單字筆記] Web工程師相關日文單字

[css筆記] 多行的情況下使用刪節號

上次介紹過如何在表格中使用刪節號,然而之前介紹的這個刪節號的方式,因搭配white-space: nowrap,使得我們使用刪節號會被限制在一行內。這次想要介紹的是在多行的情況下使用刪節號。

這次會使用到的屬性或值會需要加上 webkit prefix,分別是:display: -webkit-box,-webkit-line-clamp: 2, -webkit-box-orient: vertical,因為都還不是標規,亦或者是已經在css level3中有制定,但各家瀏覽器還是暫時只支援加上prefix的版本。例如 line-clamp,我們用caniuse查一下,可以看到他已經制定了,但不管是在edge, firefox, safari, chrome等等的瀏覽器,都還是必須加上prefix。

我們要做的很簡單,就是把 white-space: nowrap 拿掉,原本的 overflow: hidden; text-overflow: ellipsis; 保留,並加上 display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; 即可。

以下來解釋一下個屬性或值的意義。

display: -webkit-box; 其實就是個跟 display: flex 類似的東西,都是讓你可以做彈性編排的屬性值。但在此如果我們換成 flex,刪節號會失效。

-webkit-line-clamp 用於限制內文行數。

-webkit-box-orient: vertical; 以垂直 (vertical) 還是水平 (horizontal) 的方式排列內容

以下是範例程式碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .text {
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
    }
  </style>
</head>
<body>
  <div class="text">
    最後,當他們回到了小咖啡的家中時,天已經亮了。小咖啡感激地對彩虹說:“謝謝你陪我一起冒險,如果沒有你,我可能永遠都找不到回家的路。”而彩虹笑著回答說:“沒關係,朋友就應該互相幫助。”他們相擁而睡,帶著對未來的無限期待。小雨班聽完這個故事後,眼皮漸漸變得沉重,他靜靜地閉上了眼睛,腦海中充滿了對動物冒險的美好幻想。最後,他甜甜地入睡了,做著美好的夢境。
  </div>
</body>
</html>

[css筆記] 在表格中使用刪節號 css text-overflow: ellipsis

下圖是最終我們希望得到的結果:當表格中的 text 2 欄位文字過長時,可以被自動截斷,並加上刪節號。不過在實作的過程中,有遇到一些問題。以下是遇到的問題與解決方式:

首先,我們先做一個表格,外觀與程式碼如下:

閱讀全文 [css筆記] 在表格中使用刪節號 css text-overflow: ellipsis

日文AI歌聲生成NEUTRINO手把手教學

之前介紹過中文歌聲生成AI「雅婷」還沒看過的傳送門在此。今天要介紹的是日文版的雅婷「NEUTRINO」。他的功能基本上就跟雅婷一樣,也完全免費,還有免費的多組聲音model可選,實在是非常佛心來的。但也有個小缺點,就是必須下載,且沒有圖形介面,只能透過終端機 command line 來下指令。

閱讀全文 日文AI歌聲生成NEUTRINO手把手教學

AdSense 新加坡稅務資訊相關申請與設定教學

最近 Google AdSense 開始要台灣的 AdSense 使用者提交新加坡稅務資訊了。這是因為台灣的 AdSense 是透過 Google Asia 付款,而他們的地點設在新加坡。目前新加坡似乎還尚未開始扣稅,但如果之後法律要求預扣稅款,支付的款項可能會扣除較高的扣繳稅額。

先說一下,雖然不是很複雜,但整個過程其實需要點時間,主要是因為需要跟國稅局申請「稅務居住地證明」這個文件,且每年需要重新申請並去Adsense更新。以下就是整個申請與設定流程:

先到財政部稅務入口網,填寫申請表格。可以選要用自然人憑證登入或是以健保卡登入。

閱讀全文 AdSense 新加坡稅務資訊相關申請與設定教學

判斷上傳的圖片格式

當我們想要限制上傳檔案的格式時,最簡單的方式是檢查副檔名或Mime types,不過如果該檔案被更改過副檔名的話,這個檢查就沒意義了。好在我們可以透過各種檔案格式的「魔術數字」來檢查該檔案的內容是否真的符合副檔名所對應的格式。

閱讀全文 判斷上傳的圖片格式

用 color-mix() 處理CSS原生變數的顏色透明度

以往如果我們要在 css 中,對一個顏色變數做透明度處理,第一個想到的應該是透過 sass 的 color module 來處理。但如果我們是要在 runtime 處理一個 css 的原生變數呢?可能會有人想到把 rgb 拆成三個變數,再透過 rgba(var(–r), var(–g), var(–b), 0.5) 這種形式來改變透明度,相同的原理也能套用到 hsla 等等的 css 色彩函式中。

拆開變數的這個做法雖然可行,且在應用上可以很靈活,但同時也增加了變數的數量、造成閱讀與維護時的難度。

這時候不妨試試看 color-mix 函數來對顏色做透明度處理,且除了透明度以外,color-mix 其實本來是用來混合兩個顏色用的。且目前看起來各瀏覽器已經都普遍支援了(除了IE,但現在還有誰管它),請放心使用。

目前已經普遍支援各瀏覽器
閱讀全文 用 color-mix() 處理CSS原生變數的顏色透明度

用 View Transitions API 快速做出有趣的換頁動態吧

如果有在做 Native-like 的 Web Application 的人,應該都曾為了要做到像是 Native 的頁面轉換動態而絞盡腦汁。現在 View Transitions API 提供一種輕鬆創建不同 DOM 狀態之間的動畫轉換的機制。該 API 目前只能用於單一頁面中的 DOM 狀態轉換,但正在計劃支援於同 Domain 的不同頁面間的轉換。目前(2023/09)雖然僅有 Chrome, Edge, Opera 三個瀏覽器支援,但相信未來勢必會成為做動態時很重要的一環。

目前僅Chrome, Edge, Opera 三個瀏覽器支援
閱讀全文 用 View Transitions API 快速做出有趣的換頁動態吧

自動更新 Linode Object Storage SSL/TLS憑證

前一篇文章,我們了解了如何自訂Linode Object Storage的網域,但因Object Storage需要自己手動上傳SSL憑證,且Let’s Encrypt的憑證90天會到期,因此每到即將過期的時間,勢必就得再手動更新一次。因此,這篇就要繼續來了解,如何自動更新憑證。

閱讀全文 自動更新 Linode Object Storage SSL/TLS憑證

Linode Object Storage 自訂網域設定與大阪region Cyberduck設定

Custom Domain 步驟

開好 Linode 的 Object Storage 之後,常常覺得網址有夠長,查了一下自訂網域的方式。跟 AWS 比起來麻煩多了,還要自己用 Certbot 手動建立 ssl 憑證,而這也意味著會遇到無法自動更新憑證的問題。之後會再介紹用 API 更新憑證的方法,這篇的重點就先放在整個設定的步驟。

閱讀全文 Linode Object Storage 自訂網域設定與大阪region Cyberduck設定

[筆記] Critical Render Path 與 Reflow, Repaint

Critical Render Path

讀取 html => 產生 DOM Tree => 產生 CSSOM Tree => 產生 Render Tree => Layout => Paint => Compositing

Layout做的事情

依照樣式表與元素的可視與否,計算每個元素的尺寸、位置

Paint做的事情

繪製每個Layout的文字、顏色、圖片、透明度、transform。也就是把Layout轉為實際視覺上的結果。

Compositing做的事情

將所有的Layout組合起來

閱讀全文 [筆記] Critical Render Path 與 Reflow, Repaint