[{"data":1,"prerenderedAt":6445},["ShallowReactive",2],{"blogs-2026-08-03-grafana-alloy-loki":3,"next-blog-2026-08-03-grafana-alloy-loki":1887,"prev-blog-2026-08-03-grafana-alloy-loki":1887,"recommended-blog-2026-08-03-grafana-alloy-loki":1898,"related-blog-2026-08-03-grafana-alloy-loki":2140},{"id":4,"title":5,"body":6,"date":1882,"description":1883,"draft":1884,"extension":1885,"meta":1886,"navigation":131,"next":1887,"ogTitle":1888,"path":1889,"pin":1884,"prev":1887,"recommends":1890,"seo":1892,"sitemap":1893,"stem":1894,"tags":1895,"updated":1887,"__hash__":1897},"content/blogs/2026-08-03-grafana-alloy-loki.md","ลองใช้ Grafana Alloy เก็บ log เข้า Loki",{"type":7,"value":8,"toc":1869},"minimark",[9,14,23,31,34,49,53,56,59,62,94,102,105,108,181,184,200,211,214,253,257,264,391,413,416,458,461,478,481,566,577,581,588,594,606,610,613,729,732,735,749,830,849,852,895,898,1123,1129,1182,1188,1191,1197,1200,1250,1256,1260,1267,1298,1305,1380,1386,1389,1413,1417,1424,1523,1526,1745,1757,1760,1763,1766,1807,1810,1813,1816,1822,1826,1865],[10,11,13],"h2",{"id":12},"introduction","Introduction",[15,16,17,18,22],"p",{},"สวัสดีครับ พอดีที่บ้านผมมี container รันอยู่หลายตัว แล้วเวลาจะดู log ทีก็ต้อง ",[19,20,21],"code",{},"docker logs"," ไล่ทีละตัว ซึ่งพอมันเยอะขึ้นเรื่อย ๆ ก็เริ่มไม่ไหวแล้วครับ เลยคิดว่าถึงเวลารวม log ไว้ที่เดียวสักที",[15,24,25,26,30],{},"ตัวที่คนใช้กันมานานคือ Promtail แต่พอไปเปิดเอกสารดูก็เจอบรรทัดนี้ครับ \"Promtail is end of life (EOL) as of March 2, 2026\" คือหมดอายุไปตั้งแต่ต้นปีแล้ว ไม่มี update ไม่มี security fix อีกต่อไป (Grafana Agent ก็หมดอายุไปก่อนหน้านั้นตั้งแต่ 1 พฤศจิกายน 2025 เหมือนกัน) ตัวที่ Grafana ให้ย้ายไปคือ ",[27,28,29],"strong",{},"Alloy"," ผมเลยข้ามไปลองตัวนี้เลยดีกว่า",[15,32,33],{},"บทความนี้ผมรันจริงทั้งหมดบนเครื่องตัวเองด้วย docker นะครับ output ที่แปะไว้ก๊อปมาจาก terminal ตรง ๆ ไม่ได้พิมพ์ขึ้นมาเอง เวอร์ชันที่ใช้คือ Alloy v1.18.0 กับ Loki 3.7.4",[15,35,36,37,40,41,44,45,48],{},"ของที่ต้องมีก่อนคือ docker กับ ",[19,38,39],{},"jq"," (เอาไว้อ่าน JSON ที่ Loki ตอบกลับมา ถ้ายังไม่มีก็ ",[19,42,43],{},"brew install jq"," หรือ ",[19,46,47],{},"apt install jq",") แล้วก็ต้องมี port 3100 กับ 12345 ว่างอยู่ด้วย",[10,50,52],{"id":51},"alloy-คืออะไร","Alloy คืออะไร",[15,54,55],{},"Alloy คือ distribution ของ OpenTelemetry Collector ที่ Grafana ทำขึ้นมาเองครับ (OpenTelemetry Collector คือตัวกลางมาตรฐานที่คอยรับข้อมูลพวก log, metrics, traces จากที่นึงแล้วส่งต่อไปอีกที่นึง) เอาไว้เก็บ log, metrics, traces แล้วก็ profiles ส่งไปยังปลายทางที่เราต้องการ พูดง่าย ๆ คือของเดิมที่เคยแยกเป็น Promtail (log) กับ Prometheus agent (metrics) ตอนนี้รวมมาอยู่ในตัวเดียวแล้ว",[15,57,58],{},"ที่ผมชอบคือมันเขียน config เป็นก้อน ๆ เรียกว่า component แล้วเอา output ของก้อนนึงต่อเข้า input ของอีกก้อนได้เลย ไม่ได้เขียนเป็น YAML ก้อนใหญ่ ๆ ที่ต้องมานั่งไล่ว่า key ไหนอยู่ใต้ key ไหน เดี๋ยวจะได้เห็นกันครับ",[15,60,61],{},"เช็คเวอร์ชันได้แบบนี้",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-sh shiki shiki-themes material-theme-lighter one-dark-pro github-dark","docker run --rm grafana/alloy:latest --version\n","sh","",[19,70,71],{"__ignoreMap":68},[72,73,76,80,84,88,91],"span",{"class":74,"line":75},"line",1,[72,77,79],{"class":78},"sosa_","docker",[72,81,83],{"class":82},"sAlJX"," run",[72,85,87],{"class":86},"sEoV_"," --rm",[72,89,90],{"class":82}," grafana/alloy:latest",[72,92,93],{"class":86}," --version\n",[63,95,100],{"className":96,"code":98,"language":99},[97],"language-text","alloy, version v1.18.0 (branch: HEAD, revision: a435563)\n  build user:       root@buildkitsandbox\n  build date:       2026-07-20T09:02:30Z\n  go version:       go1.26.5\n  platform:         linux/arm64\n  tags:             gore2regex,netgo,embedalloyui,promtail_journal_enabled\n","text",[19,101,98],{"__ignoreMap":68},[10,103,104],{"id":104},"วิธีการติดตั้ง",[15,106,107],{},"ผมยก Loki ขึ้นมาก่อนเพื่อให้มีปลายทางให้ส่ง แล้วค่อยตาม Alloy ทีหลัง สร้าง network ให้สองตัวคุยกันได้ด้วย",[63,109,111],{"className":65,"code":110,"language":67,"meta":68,"style":68},"docker network create alloy-net\n\ndocker run -d --name loki-demo \\\n  --network alloy-net \\\n  -p 3100:3100 \\\n  grafana/loki:latest\n",[19,112,113,126,133,153,164,175],{"__ignoreMap":68},[72,114,115,117,120,123],{"class":74,"line":75},[72,116,79],{"class":78},[72,118,119],{"class":82}," network",[72,121,122],{"class":82}," create",[72,124,125],{"class":82}," alloy-net\n",[72,127,129],{"class":74,"line":128},2,[72,130,132],{"emptyLinePlaceholder":131},true,"\n",[72,134,136,138,140,143,146,149],{"class":74,"line":135},3,[72,137,79],{"class":78},[72,139,83],{"class":82},[72,141,142],{"class":86}," -d",[72,144,145],{"class":86}," --name",[72,147,148],{"class":82}," loki-demo",[72,150,152],{"class":151},"sicAX"," \\\n",[72,154,156,159,162],{"class":74,"line":155},4,[72,157,158],{"class":86},"  --network",[72,160,161],{"class":82}," alloy-net",[72,163,152],{"class":151},[72,165,167,170,173],{"class":74,"line":166},5,[72,168,169],{"class":86},"  -p",[72,171,172],{"class":82}," 3100:3100",[72,174,152],{"class":151},[72,176,178],{"class":74,"line":177},6,[72,179,180],{"class":82},"  grafana/loki:latest\n",[15,182,183],{},"Loki ใช้เวลาบูตพอสมควรครับ ของผมรอไป 17 วินาทีกว่าจะพร้อมรับ query เช็คได้แบบนี้",[63,185,187],{"className":65,"code":186,"language":67,"meta":68,"style":68},"curl -s http://localhost:3100/ready\n",[19,188,189],{"__ignoreMap":68},[72,190,191,194,197],{"class":74,"line":75},[72,192,193],{"class":78},"curl",[72,195,196],{"class":86}," -s",[72,198,199],{"class":82}," http://localhost:3100/ready\n",[15,201,202,203,206,207,210],{},"ถ้ายังไม่พร้อมจะได้ ",[19,204,205],{},"Ingester not ready: waiting for 15s after being ready"," กลับมา รอจนกว่าจะขึ้นคำว่า ",[19,208,209],{},"ready"," แล้วค่อยไปต่อ",[15,212,213],{},"จากนั้นเตรียมโฟลเดอร์กับไฟล์ log ตัวอย่างที่จะให้ Alloy อ่าน",[63,215,217],{"className":65,"code":216,"language":67,"meta":68,"style":68},"mkdir -p ~/alloy/logs\necho '{\"level\":\"info\",\"msg\":\"service started\"}' > ~/alloy/logs/app.log\n",[19,218,219,230],{"__ignoreMap":68},[72,220,221,224,227],{"class":74,"line":75},[72,222,223],{"class":78},"mkdir",[72,225,226],{"class":86}," -p",[72,228,229],{"class":82}," ~/alloy/logs\n",[72,231,232,236,240,243,246,250],{"class":74,"line":128},[72,233,235],{"class":234},"sJZfL","echo",[72,237,239],{"class":238},"snfNA"," '",[72,241,242],{"class":82},"{\"level\":\"info\",\"msg\":\"service started\"}",[72,244,245],{"class":238},"'",[72,247,249],{"class":248},"sbGrh"," >",[72,251,252],{"class":82}," ~/alloy/logs/app.log\n",[10,254,256],{"id":255},"เขียน-config","เขียน config",[15,258,259,260,263],{},"สร้างไฟล์ ",[19,261,262],{},"~/alloy/config.alloy"," ขึ้นมาครับ จะใช้ editor เปิดเขียนเองก็ได้ หรือก๊อปทั้งก้อนนี้ไปวางใน terminal เลยก็ได้",[63,265,267],{"className":65,"code":266,"language":67,"meta":68,"style":68},"cat > ~/alloy/config.alloy \u003C\u003C'EOF'\nlocal.file_match \"applog\" {\n  path_targets = [{\n    __path__ = \"/var/log/demo/*.log\",\n    job      = \"demo-app\",\n    host     = \"homelab\",\n  }]\n}\n\nloki.source.file \"applog\" {\n  targets    = local.file_match.applog.targets\n  forward_to = [loki.write.default.receiver]\n}\n\nloki.write \"default\" {\n  endpoint {\n    url = \"http://loki-demo:3100/loki/api/v1/push\"\n  }\n}\nEOF\n",[19,268,269,286,291,296,301,306,311,317,323,328,334,340,346,351,356,362,368,374,380,385],{"__ignoreMap":68},[72,270,271,274,276,279,282],{"class":74,"line":75},[72,272,273],{"class":78},"cat",[72,275,249],{"class":248},[72,277,278],{"class":82}," ~/alloy/config.alloy",[72,280,281],{"class":248}," \u003C\u003C",[72,283,285],{"class":284},"s2KDN","'EOF'\n",[72,287,288],{"class":74,"line":128},[72,289,290],{"class":82},"local.file_match \"applog\" {\n",[72,292,293],{"class":74,"line":135},[72,294,295],{"class":82},"  path_targets = [{\n",[72,297,298],{"class":74,"line":155},[72,299,300],{"class":82},"    __path__ = \"/var/log/demo/*.log\",\n",[72,302,303],{"class":74,"line":166},[72,304,305],{"class":82},"    job      = \"demo-app\",\n",[72,307,308],{"class":74,"line":177},[72,309,310],{"class":82},"    host     = \"homelab\",\n",[72,312,314],{"class":74,"line":313},7,[72,315,316],{"class":82},"  }]\n",[72,318,320],{"class":74,"line":319},8,[72,321,322],{"class":82},"}\n",[72,324,326],{"class":74,"line":325},9,[72,327,132],{"emptyLinePlaceholder":131},[72,329,331],{"class":74,"line":330},10,[72,332,333],{"class":82},"loki.source.file \"applog\" {\n",[72,335,337],{"class":74,"line":336},11,[72,338,339],{"class":82},"  targets    = local.file_match.applog.targets\n",[72,341,343],{"class":74,"line":342},12,[72,344,345],{"class":82},"  forward_to = [loki.write.default.receiver]\n",[72,347,349],{"class":74,"line":348},13,[72,350,322],{"class":82},[72,352,354],{"class":74,"line":353},14,[72,355,132],{"emptyLinePlaceholder":131},[72,357,359],{"class":74,"line":358},15,[72,360,361],{"class":82},"loki.write \"default\" {\n",[72,363,365],{"class":74,"line":364},16,[72,366,367],{"class":82},"  endpoint {\n",[72,369,371],{"class":74,"line":370},17,[72,372,373],{"class":82},"    url = \"http://loki-demo:3100/loki/api/v1/push\"\n",[72,375,377],{"class":74,"line":376},18,[72,378,379],{"class":82},"  }\n",[72,381,383],{"class":74,"line":382},19,[72,384,322],{"class":82},[72,386,388],{"class":74,"line":387},20,[72,389,390],{"class":284},"EOF\n",[15,392,393,394,397,398,401,402,405,406,409,410,412],{},"ขั้นตอนนี้ห้ามข้ามนะครับ ถ้ายังไม่มีไฟล์นี้แล้วไปสั่ง ",[19,395,396],{},"docker run"," เลย docker จะสร้าง",[27,399,400],{},"โฟลเดอร์","ชื่อ ",[19,403,404],{},"config.alloy"," ให้แทน แล้ว container จะพังตั้งแต่ยังไม่ทันขึ้น พร้อม error ที่งงมากว่า ",[19,407,408],{},"not a directory"," แถม ",[19,411,21],{}," ก็ว่างเปล่าไม่มีอะไรให้ดูด้วย",[15,414,415],{},"อ่าน config จากบนลงล่างได้เลยครับ",[417,418,419,434,452],"ul",{},[420,421,422,425,426,429,430,433],"li",{},[19,423,424],{},"local.file_match"," คือตัวบอกว่าจะอ่านไฟล์ไหนบ้าง ตรง ",[19,427,428],{},"job"," กับ ",[19,431,432],{},"host"," คือ label ที่จะติดไปกับ log ทุกบรรทัด เอาไว้ค้นทีหลัง",[420,435,436,439,440,443,444,447,448,451],{},[19,437,438],{},"loki.source.file"," คือตัวอ่านไฟล์ สังเกตว่า ",[19,441,442],{},"targets"," ชี้ไปที่ผลลัพธ์ของบล็อกแรกตรง ๆ (",[19,445,446],{},"local.file_match.applog.targets",") แล้ว ",[19,449,450],{},"forward_to"," ก็ชี้ต่อไปยังบล็อกที่สาม",[420,453,454,457],{},[19,455,456],{},"loki.write"," คือตัวส่งออก",[15,459,460],{},"การต่อกันแบบนี้แหละครับที่เป็นจุดขายของ Alloy คือ output ของ component นึงเสียบเข้า input ของอีกตัวได้เลย",[15,462,463,464,466,467,470,471,474,475,477],{},"อีกเรื่องที่ควรรู้ไว้คือ ",[19,465,438],{}," มีบล็อก ",[19,468,469],{},"file_match"," ในตัวอยู่แล้ว ใส่ glob ลงไปใน ",[19,472,473],{},"__path__"," ตรง ๆ ได้โดยไม่ต้องมี ",[19,476,424],{}," แยกออกมา แต่ผมเลือกเขียนแยกเพราะอยากให้เห็นภาพว่า component ต่อกันยังไง แล้วถ้าวันหลังมี component อื่นอยากใช้ targets ชุดเดียวกันก็ดึงไปใช้ต่อได้",[15,479,480],{},"ทีนี้รันได้แล้ว",[63,482,484],{"className":65,"code":483,"language":67,"meta":68,"style":68},"docker run -d --name alloy-demo \\\n  --network alloy-net \\\n  -p 12345:12345 \\\n  -v ~/alloy/config.alloy:/etc/alloy/config.alloy \\\n  -v ~/alloy/logs:/var/log/demo \\\n  grafana/alloy:latest \\\n    run --server.http.listen-addr=0.0.0.0:12345 \\\n    --storage.path=/var/lib/alloy/data \\\n    /etc/alloy/config.alloy\n",[19,485,486,501,509,518,528,537,544,554,561],{"__ignoreMap":68},[72,487,488,490,492,494,496,499],{"class":74,"line":75},[72,489,79],{"class":78},[72,491,83],{"class":82},[72,493,142],{"class":86},[72,495,145],{"class":86},[72,497,498],{"class":82}," alloy-demo",[72,500,152],{"class":151},[72,502,503,505,507],{"class":74,"line":128},[72,504,158],{"class":86},[72,506,161],{"class":82},[72,508,152],{"class":151},[72,510,511,513,516],{"class":74,"line":135},[72,512,169],{"class":86},[72,514,515],{"class":82}," 12345:12345",[72,517,152],{"class":151},[72,519,520,523,526],{"class":74,"line":155},[72,521,522],{"class":86},"  -v",[72,524,525],{"class":82}," ~/alloy/config.alloy:/etc/alloy/config.alloy",[72,527,152],{"class":151},[72,529,530,532,535],{"class":74,"line":166},[72,531,522],{"class":86},[72,533,534],{"class":82}," ~/alloy/logs:/var/log/demo",[72,536,152],{"class":151},[72,538,539,542],{"class":74,"line":177},[72,540,541],{"class":82},"  grafana/alloy:latest",[72,543,152],{"class":151},[72,545,546,549,552],{"class":74,"line":313},[72,547,548],{"class":82},"    run",[72,550,551],{"class":86}," --server.http.listen-addr=0.0.0.0:12345",[72,553,152],{"class":151},[72,555,556,559],{"class":74,"line":319},[72,557,558],{"class":86},"    --storage.path=/var/lib/alloy/data",[72,560,152],{"class":151},[72,562,563],{"class":74,"line":325},[72,564,565],{"class":82},"    /etc/alloy/config.alloy\n",[15,567,568,569,572,573,576],{},"ตรง ",[19,570,571],{},"--server.http.listen-addr=0.0.0.0:12345"," อย่าลืมใส่นะครับ ถ้าไม่ใส่มันจะไป bind ที่ ",[19,574,575],{},"127.0.0.1"," ข้างใน container แล้วเราจะเข้าหน้า UI จากข้างนอกไม่ได้เลย",[10,578,580],{"id":579},"หน้า-ui-ของ-alloy","หน้า UI ของ Alloy",[15,582,583,584,587],{},"เปิด ",[19,585,586],{},"http://localhost:12345"," แล้วไปที่เมนู Graph ได้เลยครับ (หน้าแรกที่เด้งขึ้นมาจะเป็น Components) มันจะวาด pipeline ให้ดูว่า component ไหนต่อกับอันไหน",[15,589,590],{},[591,592],"img",{"alt":68,"src":593},"/images/2026-08-03-grafana-alloy-loki/alloy-ui.png",[15,595,596,597,599,600,602,603,605],{},"นี่แหละครับคือสิ่งที่อธิบายไปเมื่อกี้ ",[19,598,424],{}," ยิงไปหา ",[19,601,438],{}," แล้วต่อไป ",[19,604,456],{}," ตรงตาม config ที่เขียนไว้เป๊ะ ๆ เส้นสีเขียวคือ log ถ้ามี metrics หรือ traces ด้วยก็จะเป็นคนละสี อันนี้มีประโยชน์มากตอน config เริ่มใหญ่ เพราะดูรูปทีเดียวรู้เลยว่าอะไรต่อกับอะไร",[10,607,609],{"id":608},"เช็คว่า-log-เข้าจริง","เช็คว่า log เข้าจริง",[15,611,612],{},"ลองเขียน log เพิ่มเข้าไปในไฟล์",[63,614,616],{"className":65,"code":615,"language":67,"meta":68,"style":68},"for i in 1 2 3; do\n  echo \"{\\\"level\\\":\\\"info\\\",\\\"msg\\\":\\\"handled request $i\\\"}\" >> ~/alloy/logs/app.log\ndone\necho '{\"level\":\"error\",\"msg\":\"database connection refused\"}' >> ~/alloy/logs/app.log\n",[19,617,618,647,709,714],{"__ignoreMap":68},[72,619,620,624,628,631,634,637,640,644],{"class":74,"line":75},[72,621,623],{"class":622},"s36IK","for",[72,625,627],{"class":626},"sLHrO"," i",[72,629,630],{"class":622}," in",[72,632,633],{"class":82}," 1",[72,635,636],{"class":82}," 2",[72,638,639],{"class":82}," 3",[72,641,643],{"class":642},"stlKB",";",[72,645,646],{"class":622}," do\n",[72,648,649,652,655,658,661,664,666,669,671,674,676,679,681,684,686,688,690,693,696,698,701,704,707],{"class":74,"line":128},[72,650,651],{"class":234},"  echo",[72,653,654],{"class":238}," \"",[72,656,657],{"class":82},"{",[72,659,660],{"class":151},"\\\"",[72,662,663],{"class":82},"level",[72,665,660],{"class":151},[72,667,668],{"class":82},":",[72,670,660],{"class":151},[72,672,673],{"class":82},"info",[72,675,660],{"class":151},[72,677,678],{"class":82},",",[72,680,660],{"class":151},[72,682,683],{"class":82},"msg",[72,685,660],{"class":151},[72,687,668],{"class":82},[72,689,660],{"class":151},[72,691,692],{"class":82},"handled request ",[72,694,695],{"class":626},"$i",[72,697,660],{"class":151},[72,699,700],{"class":82},"}",[72,702,703],{"class":238},"\"",[72,705,706],{"class":248}," >>",[72,708,252],{"class":82},[72,710,711],{"class":74,"line":135},[72,712,713],{"class":622},"done\n",[72,715,716,718,720,723,725,727],{"class":74,"line":155},[72,717,235],{"class":234},[72,719,239],{"class":238},[72,721,722],{"class":82},"{\"level\":\"error\",\"msg\":\"database connection refused\"}",[72,724,245],{"class":238},[72,726,706],{"class":248},[72,728,252],{"class":82},[15,730,731],{},"รอสัก 2-3 วินาทีให้ Alloy อ่านไฟล์แล้วส่งเข้า Loki ก่อนนะครับ ถ้ายิง query ทันทีจะเห็นแค่บรรทัดแรกบรรทัดเดียวแล้วนึกว่าพัง",[15,733,734],{},"ถามก่อนว่า Loki รู้จัก label อะไรบ้าง",[63,736,738],{"className":65,"code":737,"language":67,"meta":68,"style":68},"curl -s http://localhost:3100/loki/api/v1/labels\n",[19,739,740],{"__ignoreMap":68},[72,741,742,744,746],{"class":74,"line":75},[72,743,193],{"class":78},[72,745,196],{"class":86},[72,747,748],{"class":82}," http://localhost:3100/loki/api/v1/labels\n",[63,750,754],{"className":751,"code":752,"language":753,"meta":68,"style":68},"language-json shiki shiki-themes material-theme-lighter one-dark-pro github-dark","{ \"status\": \"success\", \"data\": [\"filename\", \"host\", \"job\", \"service_name\"] }\n","json",[19,755,756],{"__ignoreMap":68},[72,757,758,760,763,767,769,771,773,776,778,780,782,785,787,789,792,794,797,799,801,803,805,807,809,811,813,815,817,819,822,824,827],{"class":74,"line":75},[72,759,657],{"class":642},[72,761,654],{"class":762},"sObbn",[72,764,766],{"class":765},"sVeMc","status",[72,768,703],{"class":762},[72,770,668],{"class":642},[72,772,654],{"class":238},[72,774,775],{"class":82},"success",[72,777,703],{"class":238},[72,779,678],{"class":642},[72,781,654],{"class":762},[72,783,784],{"class":765},"data",[72,786,703],{"class":762},[72,788,668],{"class":642},[72,790,791],{"class":642}," [",[72,793,703],{"class":238},[72,795,796],{"class":82},"filename",[72,798,703],{"class":238},[72,800,678],{"class":642},[72,802,654],{"class":238},[72,804,432],{"class":82},[72,806,703],{"class":238},[72,808,678],{"class":642},[72,810,654],{"class":238},[72,812,428],{"class":82},[72,814,703],{"class":238},[72,816,678],{"class":642},[72,818,654],{"class":238},[72,820,821],{"class":82},"service_name",[72,823,703],{"class":238},[72,825,826],{"class":642},"]",[72,828,829],{"class":642}," }\n",[15,831,832,429,834,836,837,839,840,842,843,845,846,848],{},[19,833,428],{},[19,835,432],{}," คือที่เราใส่ไว้ใน config เอง ส่วนอีกสองตัวมาคนละที่กันครับ ",[19,838,796],{}," เป็นของ Alloy ที่เอา ",[19,841,473],{}," มาแปะให้ทุกบรรทัด ส่วน ",[19,844,821],{}," เป็นของ Loki ที่ map มาจาก label ",[19,847,428],{}," ให้อัตโนมัติตอน ingest",[15,850,851],{},"ทีนี้ลองดึง log ออกมาดูจริง ๆ",[63,853,855],{"className":65,"code":854,"language":67,"meta":68,"style":68},"curl -sG http://localhost:3100/loki/api/v1/query_range \\\n  --data-urlencode 'query={job=\"demo-app\"}' \\\n  --data-urlencode 'limit=10'\n",[19,856,857,869,883],{"__ignoreMap":68},[72,858,859,861,864,867],{"class":74,"line":75},[72,860,193],{"class":78},[72,862,863],{"class":86}," -sG",[72,865,866],{"class":82}," http://localhost:3100/loki/api/v1/query_range",[72,868,152],{"class":151},[72,870,871,874,876,879,881],{"class":74,"line":128},[72,872,873],{"class":86},"  --data-urlencode",[72,875,239],{"class":238},[72,877,878],{"class":82},"query={job=\"demo-app\"}",[72,880,245],{"class":238},[72,882,152],{"class":151},[72,884,885,887,889,892],{"class":74,"line":135},[72,886,873],{"class":86},[72,888,239],{"class":238},[72,890,891],{"class":82},"limit=10",[72,893,894],{"class":238},"'\n",[15,896,897],{},"ที่ได้กลับมาคือ JSON ก้อนเดียวยาว ๆ หน้าตาประมาณนี้",[63,899,901],{"className":751,"code":900,"language":753,"meta":68,"style":68},"{\"status\":\"success\",\"data\":{\"resultType\":\"streams\",\"result\":[{\"stream\":{\"detected_level\":\"error\",\"filename\":\"/var/log/demo/app.log\",\"host\":\"homelab\",\"job\":\"demo-app\",\"service_name\":\"demo-app\"},\"values\":[[\"1785760821561036812\",\"{\\\"level\\\":\\\"error\\\",\\\"msg\\\":\\\"database connection refused\\\"}\"]]},...\n",[19,902,903],{"__ignoreMap":68},[72,904,905,907,909,911,913,915,917,919,921,923,925,927,929,932,934,938,940,942,944,947,949,951,953,956,958,961,963,967,969,971,973,977,979,981,983,986,988,990,992,994,996,998,1000,1003,1005,1007,1009,1011,1013,1015,1017,1020,1022,1024,1026,1028,1030,1032,1034,1037,1039,1041,1043,1045,1047,1049,1051,1053,1055,1058,1060,1063,1065,1068,1070,1073,1075,1077,1079,1081,1083,1085,1087,1089,1091,1093,1095,1097,1099,1101,1103,1105,1107,1110,1112,1114,1116,1119],{"class":74,"line":75},[72,906,657],{"class":642},[72,908,703],{"class":762},[72,910,766],{"class":765},[72,912,703],{"class":762},[72,914,668],{"class":642},[72,916,703],{"class":238},[72,918,775],{"class":82},[72,920,703],{"class":238},[72,922,678],{"class":642},[72,924,703],{"class":762},[72,926,784],{"class":765},[72,928,703],{"class":762},[72,930,931],{"class":642},":{",[72,933,703],{"class":762},[72,935,937],{"class":936},"sLise","resultType",[72,939,703],{"class":762},[72,941,668],{"class":642},[72,943,703],{"class":238},[72,945,946],{"class":82},"streams",[72,948,703],{"class":238},[72,950,678],{"class":642},[72,952,703],{"class":762},[72,954,955],{"class":936},"result",[72,957,703],{"class":762},[72,959,960],{"class":642},":[{",[72,962,703],{"class":762},[72,964,966],{"class":965},"sxkdo","stream",[72,968,703],{"class":762},[72,970,931],{"class":642},[72,972,703],{"class":762},[72,974,976],{"class":975},"s9IQt","detected_level",[72,978,703],{"class":762},[72,980,668],{"class":642},[72,982,703],{"class":238},[72,984,985],{"class":82},"error",[72,987,703],{"class":238},[72,989,678],{"class":642},[72,991,703],{"class":762},[72,993,796],{"class":975},[72,995,703],{"class":762},[72,997,668],{"class":642},[72,999,703],{"class":238},[72,1001,1002],{"class":82},"/var/log/demo/app.log",[72,1004,703],{"class":238},[72,1006,678],{"class":642},[72,1008,703],{"class":762},[72,1010,432],{"class":975},[72,1012,703],{"class":762},[72,1014,668],{"class":642},[72,1016,703],{"class":238},[72,1018,1019],{"class":82},"homelab",[72,1021,703],{"class":238},[72,1023,678],{"class":642},[72,1025,703],{"class":762},[72,1027,428],{"class":975},[72,1029,703],{"class":762},[72,1031,668],{"class":642},[72,1033,703],{"class":238},[72,1035,1036],{"class":82},"demo-app",[72,1038,703],{"class":238},[72,1040,678],{"class":642},[72,1042,703],{"class":762},[72,1044,821],{"class":975},[72,1046,703],{"class":762},[72,1048,668],{"class":642},[72,1050,703],{"class":238},[72,1052,1036],{"class":82},[72,1054,703],{"class":238},[72,1056,1057],{"class":642},"},",[72,1059,703],{"class":762},[72,1061,1062],{"class":965},"values",[72,1064,703],{"class":762},[72,1066,1067],{"class":642},":[[",[72,1069,703],{"class":238},[72,1071,1072],{"class":82},"1785760821561036812",[72,1074,703],{"class":238},[72,1076,678],{"class":642},[72,1078,703],{"class":238},[72,1080,657],{"class":82},[72,1082,660],{"class":151},[72,1084,663],{"class":82},[72,1086,660],{"class":151},[72,1088,668],{"class":82},[72,1090,660],{"class":151},[72,1092,985],{"class":82},[72,1094,660],{"class":151},[72,1096,678],{"class":82},[72,1098,660],{"class":151},[72,1100,683],{"class":82},[72,1102,660],{"class":151},[72,1104,668],{"class":82},[72,1106,660],{"class":151},[72,1108,1109],{"class":82},"database connection refused",[72,1111,660],{"class":151},[72,1113,700],{"class":82},[72,1115,703],{"class":238},[72,1117,1118],{"class":642},"]]},",[72,1120,1122],{"class":1121},"sg798","...\n",[15,1124,1125,1126,1128],{},"อ่านไม่ไหวครับ ต่อ ",[19,1127,39],{}," เอาเฉพาะบรรทัด log ดีกว่า",[63,1130,1132],{"className":65,"code":1131,"language":67,"meta":68,"style":68},"curl -sG http://localhost:3100/loki/api/v1/query_range \\\n  --data-urlencode 'query={job=\"demo-app\"}' \\\n  --data-urlencode 'limit=10' | jq -r '.data.result[].values[][1]'\n",[19,1133,1134,1144,1156],{"__ignoreMap":68},[72,1135,1136,1138,1140,1142],{"class":74,"line":75},[72,1137,193],{"class":78},[72,1139,863],{"class":86},[72,1141,866],{"class":82},[72,1143,152],{"class":151},[72,1145,1146,1148,1150,1152,1154],{"class":74,"line":128},[72,1147,873],{"class":86},[72,1149,239],{"class":238},[72,1151,878],{"class":82},[72,1153,245],{"class":238},[72,1155,152],{"class":151},[72,1157,1158,1160,1162,1164,1166,1169,1172,1175,1177,1180],{"class":74,"line":135},[72,1159,873],{"class":86},[72,1161,239],{"class":238},[72,1163,891],{"class":82},[72,1165,245],{"class":238},[72,1167,1168],{"class":248}," |",[72,1170,1171],{"class":78}," jq",[72,1173,1174],{"class":86}," -r",[72,1176,239],{"class":238},[72,1178,1179],{"class":82},".data.result[].values[][1]",[72,1181,894],{"class":238},[63,1183,1186],{"className":1184,"code":1185,"language":99},[97],"{\"level\":\"error\",\"msg\":\"database connection refused\"}\n{\"level\":\"info\",\"msg\":\"handled request 3\"}\n{\"level\":\"info\",\"msg\":\"handled request 2\"}\n{\"level\":\"info\",\"msg\":\"handled request 1\"}\n{\"level\":\"info\",\"msg\":\"service started\"}\n",[19,1187,1185],{"__ignoreMap":68},[15,1189,1190],{},"ครบทั้ง 5 บรรทัดที่เขียนลงไฟล์ไป",[15,1192,1193,1194,1196],{},"กลับไปดูใน JSON ก้อนบนอีกทีจะเห็น ",[19,1195,976],{}," โผล่มาด้วย อันนี้ผมไม่ได้ใส่ไว้ใน config เลยครับ เป็น Loki ที่อ่านเนื้อ log แล้วแยกให้เองว่าบรรทัดไหน error บรรทัดไหน info ทั้งที่ผมไม่ได้เขียน parser อะไรไว้สักตัว",[15,1198,1199],{},"ถ้าอยากกรองเฉพาะ error ก็ใส่ filter ต่อท้าย query ได้",[63,1201,1203],{"className":65,"code":1202,"language":67,"meta":68,"style":68},"curl -sG http://localhost:3100/loki/api/v1/query_range \\\n  --data-urlencode 'query={job=\"demo-app\"} |= \"error\"' \\\n  --data-urlencode 'limit=10' | jq -r '.data.result[].values[][1]'\n",[19,1204,1205,1215,1228],{"__ignoreMap":68},[72,1206,1207,1209,1211,1213],{"class":74,"line":75},[72,1208,193],{"class":78},[72,1210,863],{"class":86},[72,1212,866],{"class":82},[72,1214,152],{"class":151},[72,1216,1217,1219,1221,1224,1226],{"class":74,"line":128},[72,1218,873],{"class":86},[72,1220,239],{"class":238},[72,1222,1223],{"class":82},"query={job=\"demo-app\"} |= \"error\"",[72,1225,245],{"class":238},[72,1227,152],{"class":151},[72,1229,1230,1232,1234,1236,1238,1240,1242,1244,1246,1248],{"class":74,"line":135},[72,1231,873],{"class":86},[72,1233,239],{"class":238},[72,1235,891],{"class":82},[72,1237,245],{"class":238},[72,1239,1168],{"class":248},[72,1241,1171],{"class":78},[72,1243,1174],{"class":86},[72,1245,239],{"class":238},[72,1247,1179],{"class":82},[72,1249,894],{"class":238},[63,1251,1254],{"className":1252,"code":1253,"language":99},[97],"{\"level\":\"error\",\"msg\":\"database connection refused\"}\n",[19,1255,1253],{"__ignoreMap":68},[10,1257,1259],{"id":1258},"ตรวจ-config-ก่อนรัน","ตรวจ config ก่อนรัน",[15,1261,1262,1263,1266],{},"Alloy มีคำสั่ง ",[19,1264,1265],{},"validate"," ให้ใช้ครับ ไม่ต้องรันจริงแล้วมานั่งดู log ว่าพังตรงไหน",[63,1268,1270],{"className":65,"code":1269,"language":67,"meta":68,"style":68},"docker run --rm -v ~/alloy/config.alloy:/c.alloy \\\n  grafana/alloy:latest validate /c.alloy\n",[19,1271,1272,1288],{"__ignoreMap":68},[72,1273,1274,1276,1278,1280,1283,1286],{"class":74,"line":75},[72,1275,79],{"class":78},[72,1277,83],{"class":82},[72,1279,87],{"class":86},[72,1281,1282],{"class":86}," -v",[72,1284,1285],{"class":82}," ~/alloy/config.alloy:/c.alloy",[72,1287,152],{"class":151},[72,1289,1290,1292,1295],{"class":74,"line":128},[72,1291,541],{"class":82},[72,1293,1294],{"class":82}," validate",[72,1296,1297],{"class":82}," /c.alloy\n",[15,1299,1300,1301,1304],{},"ถ้าผ่านจะไม่มีอะไรขึ้นมาเลย ทีนี้ลองทำให้มันพังดูบ้าง ผมสร้างไฟล์ ",[19,1302,1303],{},"bad.alloy"," ที่ใส่ attribute มั่ว ๆ เข้าไปหนึ่งตัว",[63,1306,1308],{"className":65,"code":1307,"language":67,"meta":68,"style":68},"cat > ~/alloy/bad.alloy \u003C\u003C'EOF'\nloki.write \"default\" {\n  endpoint {\n    url = \"http://loki-demo:3100/loki/api/v1/push\"\n  }\n  bogus_arg = 1\n}\nEOF\n\ndocker run --rm -v ~/alloy/bad.alloy:/bad.alloy \\\n  grafana/alloy:latest validate /bad.alloy\n",[19,1309,1310,1323,1327,1331,1335,1339,1344,1348,1352,1356,1371],{"__ignoreMap":68},[72,1311,1312,1314,1316,1319,1321],{"class":74,"line":75},[72,1313,273],{"class":78},[72,1315,249],{"class":248},[72,1317,1318],{"class":82}," ~/alloy/bad.alloy",[72,1320,281],{"class":248},[72,1322,285],{"class":284},[72,1324,1325],{"class":74,"line":128},[72,1326,361],{"class":82},[72,1328,1329],{"class":74,"line":135},[72,1330,367],{"class":82},[72,1332,1333],{"class":74,"line":155},[72,1334,373],{"class":82},[72,1336,1337],{"class":74,"line":166},[72,1338,379],{"class":82},[72,1340,1341],{"class":74,"line":177},[72,1342,1343],{"class":82},"  bogus_arg = 1\n",[72,1345,1346],{"class":74,"line":313},[72,1347,322],{"class":82},[72,1349,1350],{"class":74,"line":319},[72,1351,390],{"class":284},[72,1353,1354],{"class":74,"line":325},[72,1355,132],{"emptyLinePlaceholder":131},[72,1357,1358,1360,1362,1364,1366,1369],{"class":74,"line":330},[72,1359,79],{"class":78},[72,1361,83],{"class":82},[72,1363,87],{"class":86},[72,1365,1282],{"class":86},[72,1367,1368],{"class":82}," ~/alloy/bad.alloy:/bad.alloy",[72,1370,152],{"class":151},[72,1372,1373,1375,1377],{"class":74,"line":336},[72,1374,541],{"class":82},[72,1376,1294],{"class":82},[72,1378,1379],{"class":82}," /bad.alloy\n",[63,1381,1384],{"className":1382,"code":1383,"language":99},[97],"Error: /bad.alloy:5:3: unrecognized attribute name \"bogus_arg\"\n\n4 |   }\n5 |   bogus_arg = 1\n  |   ^^^^^^^^^^^^^\n6 | }\nError: validation failed\n2026/08/03 13:32:02 collector server run finished with error: validation failed\n",[19,1385,1383],{"__ignoreMap":68},[15,1387,1388],{},"ชี้ให้เลยว่าบรรทัด 5 ตัวอักษรที่ 3 พร้อมขีดเส้นใต้ให้ด้วย",[15,1390,1391,1392,1395,1396,1398,1399,1402,1403,1406,1407,1409,1410,1412],{},"ระวังอย่าไปสับสนกับคำสั่ง ",[19,1393,1394],{},"fmt"," นะครับ สองตัวนี้ตรวจคนละชั้นกัน ",[19,1397,1394],{}," ดูแค่ syntax คือวงเล็บปิดครบไหม โครงสร้างถูกไหม (ผมลองเอา config ที่ลืมปิดวงเล็บไปให้ มันฟ้อง ",[19,1400,1401],{},"expected }, got EOF"," แล้ว exit 1) แต่มันไม่เช็คว่าชื่อ attribute หรือ component นั้นมีอยู่จริงหรือเปล่า ตัว ",[19,1404,1405],{},"bogus_arg"," ข้างบนผ่าน ",[19,1408,1394],{}," ฉลุยด้วย exit 0 แถมยังพ่น config ที่จัดหน้าเรียบร้อยแล้วออกมาให้อีก ต้องใช้ ",[19,1411,1265],{}," ถึงจะจับได้",[10,1414,1416],{"id":1415},"ย้ายจาก-promtail","ย้ายจาก Promtail",[15,1418,1419,1420,1423],{},"ใครที่มี config Promtail อยู่แล้วไม่ต้องนั่งแปลมือครับ Alloy มีคำสั่ง ",[19,1421,1422],{},"convert"," ให้ ผมลองเอา config สั้น ๆ แบบนี้ไปแปลงดู",[63,1425,1427],{"className":65,"code":1426,"language":67,"meta":68,"style":68},"cat > ~/alloy/promtail.yaml \u003C\u003C'EOF'\nclients:\n  - url: http://localhost:3100/loki/api/v1/push\nscrape_configs:\n  - job_name: system\n    static_configs:\n      - targets: [localhost]\n        labels:\n          job: varlogs\n          __path__: /var/log/*.log\nEOF\n\ndocker run --rm -v ~/alloy/promtail.yaml:/p.yaml \\\n  grafana/alloy:latest convert --source-format=promtail /p.yaml\n",[19,1428,1429,1442,1447,1452,1457,1462,1467,1472,1477,1482,1487,1491,1495,1510],{"__ignoreMap":68},[72,1430,1431,1433,1435,1438,1440],{"class":74,"line":75},[72,1432,273],{"class":78},[72,1434,249],{"class":248},[72,1436,1437],{"class":82}," ~/alloy/promtail.yaml",[72,1439,281],{"class":248},[72,1441,285],{"class":284},[72,1443,1444],{"class":74,"line":128},[72,1445,1446],{"class":82},"clients:\n",[72,1448,1449],{"class":74,"line":135},[72,1450,1451],{"class":82},"  - url: http://localhost:3100/loki/api/v1/push\n",[72,1453,1454],{"class":74,"line":155},[72,1455,1456],{"class":82},"scrape_configs:\n",[72,1458,1459],{"class":74,"line":166},[72,1460,1461],{"class":82},"  - job_name: system\n",[72,1463,1464],{"class":74,"line":177},[72,1465,1466],{"class":82},"    static_configs:\n",[72,1468,1469],{"class":74,"line":313},[72,1470,1471],{"class":82},"      - targets: [localhost]\n",[72,1473,1474],{"class":74,"line":319},[72,1475,1476],{"class":82},"        labels:\n",[72,1478,1479],{"class":74,"line":325},[72,1480,1481],{"class":82},"          job: varlogs\n",[72,1483,1484],{"class":74,"line":330},[72,1485,1486],{"class":82},"          __path__: /var/log/*.log\n",[72,1488,1489],{"class":74,"line":336},[72,1490,390],{"class":284},[72,1492,1493],{"class":74,"line":342},[72,1494,132],{"emptyLinePlaceholder":131},[72,1496,1497,1499,1501,1503,1505,1508],{"class":74,"line":348},[72,1498,79],{"class":78},[72,1500,83],{"class":82},[72,1502,87],{"class":86},[72,1504,1282],{"class":86},[72,1506,1507],{"class":82}," ~/alloy/promtail.yaml:/p.yaml",[72,1509,152],{"class":151},[72,1511,1512,1514,1517,1520],{"class":74,"line":353},[72,1513,541],{"class":82},[72,1515,1516],{"class":82}," convert",[72,1518,1519],{"class":86}," --source-format=promtail",[72,1521,1522],{"class":82}," /p.yaml\n",[15,1524,1525],{},"ได้ผลออกมาแบบนี้",[63,1527,1531],{"className":1528,"code":1529,"language":1530,"meta":68,"style":68},"language-hcl shiki shiki-themes material-theme-lighter one-dark-pro github-dark","loki.source.file \"system\" {\n    targets = [{\n        __address__ = \"localhost\",\n        __path__    = \"/var/log/*.log\",\n        job         = \"varlogs\",\n    }]\n    forward_to = [loki.write.default.receiver]\n\n    file_match {\n        enabled = true\n    }\n    legacy_positions_file = \"/var/log/positions.yaml\"\n}\n\nloki.write \"default\" {\n    endpoint {\n        url = \"http://localhost:3100/loki/api/v1/push\"\n    }\n    external_labels = {}\n}\n","hcl",[19,1532,1533,1560,1574,1589,1603,1620,1628,1638,1642,1649,1660,1665,1680,1684,1688,1706,1713,1727,1731,1741],{"__ignoreMap":68},[72,1534,1535,1539,1542,1545,1547,1550,1552,1555,1557],{"class":74,"line":75},[72,1536,1538],{"class":1537},"sZDfs","loki",[72,1540,1541],{"class":248},".",[72,1543,1544],{"class":626},"source",[72,1546,1541],{"class":248},[72,1548,1549],{"class":626},"file",[72,1551,654],{"class":238},[72,1553,1554],{"class":82},"system",[72,1556,703],{"class":238},[72,1558,1559],{"class":642}," {\n",[72,1561,1562,1565,1569,1571],{"class":74,"line":128},[72,1563,1564],{"class":626},"    targets",[72,1566,1568],{"class":1567},"sMTEB"," =",[72,1570,791],{"class":1537},[72,1572,1573],{"class":642},"{\n",[72,1575,1576,1579,1581,1584,1586],{"class":74,"line":135},[72,1577,1578],{"class":1537},"        __address__ = ",[72,1580,703],{"class":238},[72,1582,1583],{"class":82},"localhost",[72,1585,703],{"class":238},[72,1587,1588],{"class":1537},",\n",[72,1590,1591,1594,1596,1599,1601],{"class":74,"line":155},[72,1592,1593],{"class":1537},"        __path__    = ",[72,1595,703],{"class":238},[72,1597,1598],{"class":82},"/var/log/*.log",[72,1600,703],{"class":238},[72,1602,1588],{"class":1537},[72,1604,1605,1608,1611,1613,1616,1618],{"class":74,"line":166},[72,1606,1607],{"class":626},"        job",[72,1609,1610],{"class":1567},"         =",[72,1612,654],{"class":238},[72,1614,1615],{"class":82},"varlogs",[72,1617,703],{"class":238},[72,1619,1588],{"class":1537},[72,1621,1622,1625],{"class":74,"line":177},[72,1623,1624],{"class":642},"    }",[72,1626,1627],{"class":1537},"]\n",[72,1629,1630,1633,1635],{"class":74,"line":313},[72,1631,1632],{"class":626},"    forward_to",[72,1634,1568],{"class":1567},[72,1636,1637],{"class":1537}," [loki.write.default.receiver]\n",[72,1639,1640],{"class":74,"line":319},[72,1641,132],{"emptyLinePlaceholder":131},[72,1643,1644,1647],{"class":74,"line":325},[72,1645,1646],{"class":1537},"    file_match ",[72,1648,1573],{"class":642},[72,1650,1651,1654,1656],{"class":74,"line":330},[72,1652,1653],{"class":626},"        enabled",[72,1655,1568],{"class":1567},[72,1657,1659],{"class":1658},"scCEF"," true\n",[72,1661,1662],{"class":74,"line":336},[72,1663,1664],{"class":642},"    }\n",[72,1666,1667,1670,1672,1674,1677],{"class":74,"line":342},[72,1668,1669],{"class":626},"    legacy_positions_file",[72,1671,1568],{"class":1567},[72,1673,654],{"class":238},[72,1675,1676],{"class":82},"/var/log/positions.yaml",[72,1678,1679],{"class":238},"\"\n",[72,1681,1682],{"class":74,"line":348},[72,1683,322],{"class":642},[72,1685,1686],{"class":74,"line":353},[72,1687,132],{"emptyLinePlaceholder":131},[72,1689,1690,1692,1694,1697,1699,1702,1704],{"class":74,"line":358},[72,1691,1538],{"class":1537},[72,1693,1541],{"class":248},[72,1695,1696],{"class":626},"write",[72,1698,654],{"class":238},[72,1700,1701],{"class":82},"default",[72,1703,703],{"class":238},[72,1705,1559],{"class":642},[72,1707,1708,1711],{"class":74,"line":364},[72,1709,1710],{"class":1537},"    endpoint ",[72,1712,1573],{"class":642},[72,1714,1715,1718,1720,1722,1725],{"class":74,"line":370},[72,1716,1717],{"class":626},"        url",[72,1719,1568],{"class":1567},[72,1721,654],{"class":238},[72,1723,1724],{"class":82},"http://localhost:3100/loki/api/v1/push",[72,1726,1679],{"class":238},[72,1728,1729],{"class":74,"line":376},[72,1730,1664],{"class":642},[72,1732,1733,1736,1738],{"class":74,"line":382},[72,1734,1735],{"class":626},"    external_labels",[72,1737,1568],{"class":1567},[72,1739,1740],{"class":642}," {}\n",[72,1742,1743],{"class":74,"line":387},[72,1744,322],{"class":642},[15,1746,1747,1748,1750,1751,1753,1754,1756],{},"สังเกตว่ามันเลือกใช้ ",[19,1749,469],{}," block ในตัวของ ",[19,1752,438],{}," แทนที่จะแยก ",[19,1755,424],{}," ออกมาเหมือนที่ผมเขียนไว้ข้างบน ซึ่งก็ถูกทั้งคู่ครับ",[15,1758,1759],{},"ใช้ได้เลยแต่แนะนำให้อ่านทวนก่อนสักรอบ อย่าเอาไปใช้ทั้งดุ้นเลยทีเดียว",[10,1761,1762],{"id":1762},"ลบของทิ้ง",[15,1764,1765],{},"ลองเสร็จแล้วเก็บกวาดด้วยนะครับ",[63,1767,1769],{"className":65,"code":1768,"language":67,"meta":68,"style":68},"docker rm -f alloy-demo loki-demo\ndocker network rm alloy-net\nrm -rf ~/alloy\n",[19,1770,1771,1786,1796],{"__ignoreMap":68},[72,1772,1773,1775,1778,1781,1783],{"class":74,"line":75},[72,1774,79],{"class":78},[72,1776,1777],{"class":82}," rm",[72,1779,1780],{"class":86}," -f",[72,1782,498],{"class":82},[72,1784,1785],{"class":82}," loki-demo\n",[72,1787,1788,1790,1792,1794],{"class":74,"line":128},[72,1789,79],{"class":78},[72,1791,119],{"class":82},[72,1793,1777],{"class":82},[72,1795,125],{"class":82},[72,1797,1798,1801,1804],{"class":74,"line":135},[72,1799,1800],{"class":78},"rm",[72,1802,1803],{"class":86}," -rf",[72,1805,1806],{"class":82}," ~/alloy\n",[10,1808,1809],{"id":1809},"สรุป",[15,1811,1812],{},"ที่ลองมาผมว่า Alloy เข้าใจง่ายกว่า Promtail อยู่พอสมควรครับ ตรงที่ config เป็น component ต่อกันเป็นท่อ แล้วมีหน้า UI ให้ดูว่าท่อมันต่อถูกไหม เวลาพังจะหาต้นตอง่ายกว่าเยอะ",[15,1814,1815],{},"ที่เขียนในบทความนี้เป็นแค่ log อย่างเดียวนะครับ จริง ๆ Alloy ทำ metrics กับ traces ได้ด้วยในตัวเดียวกัน ถ้ามีโอกาสจะมาเขียนต่อ",[15,1817,1818,1819,1821],{},"ส่วนใครที่ยังใช้ Promtail อยู่ ถึงตอนนี้มันยังทำงานได้ปกติก็จริง แต่ EOL ไปแล้วตั้งแต่เดือนมีนาคมครับ แปลว่าถ้ามีช่องโหว่โผล่มาก็จะไม่มีใครแก้ให้แล้ว ควรวางแผนย้ายไว้แต่เนิ่น ๆ ดีกว่า และมีคำสั่ง ",[19,1820,1422],{}," ช่วยอยู่แล้วด้วย",[10,1823,1825],{"id":1824},"references","References",[417,1827,1828,1837,1844,1851,1858],{},[420,1829,1830],{},[1831,1832,1836],"a",{"href":1833,"rel":1834},"https://grafana.com/docs/alloy/latest/",[1835],"nofollow","Grafana Alloy documentation",[420,1838,1839],{},[1831,1840,1843],{"href":1841,"rel":1842},"https://grafana.com/docs/alloy/latest/set-up/install/docker/",[1835],"Install Alloy on Docker",[420,1845,1846],{},[1831,1847,1850],{"href":1848,"rel":1849},"https://grafana.com/docs/alloy/latest/reference/components/loki/loki.source.file/",[1835],"loki.source.file component",[420,1852,1853],{},[1831,1854,1857],{"href":1855,"rel":1856},"https://grafana.com/docs/loki/latest/send-data/promtail/",[1835],"Promtail EOL notice",[420,1859,1860],{},[1831,1861,1864],{"href":1862,"rel":1863},"https://grafana.com/docs/agent/latest/",[1835],"Grafana Agent EOL announcement",[1866,1867,1868],"style",{},"html pre.shiki code .sosa_, html code.shiki .sosa_{--shiki-light:#E2931D;--shiki-default:#61AFEF;--shiki-dark:#B392F0}html pre.shiki code .sAlJX, html code.shiki .sAlJX{--shiki-light:#91B859;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .sEoV_, html code.shiki .sEoV_{--shiki-light:#91B859;--shiki-default:#D19A66;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sicAX, html code.shiki .sicAX{--shiki-light:#90A4AE;--shiki-default:#56B6C2;--shiki-dark:#79B8FF}html pre.shiki code .sJZfL, html code.shiki .sJZfL{--shiki-light:#6182B8;--shiki-default:#56B6C2;--shiki-dark:#79B8FF}html pre.shiki code .snfNA, html code.shiki .snfNA{--shiki-light:#39ADB5;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .sbGrh, html code.shiki .sbGrh{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#F97583}html pre.shiki code .s2KDN, html code.shiki .s2KDN{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#9ECBFF}html pre.shiki code .s36IK, html code.shiki .s36IK{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#C678DD;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sLHrO, html code.shiki .sLHrO{--shiki-light:#90A4AE;--shiki-default:#E06C75;--shiki-dark:#E1E4E8}html pre.shiki code .stlKB, html code.shiki .stlKB{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#E1E4E8}html pre.shiki code .sObbn, html code.shiki .sObbn{--shiki-light:#39ADB5;--shiki-default:#E06C75;--shiki-dark:#79B8FF}html pre.shiki code .sVeMc, html code.shiki .sVeMc{--shiki-light:#9C3EDA;--shiki-default:#E06C75;--shiki-dark:#79B8FF}html pre.shiki code .sLise, html code.shiki .sLise{--shiki-light:#E2931D;--shiki-default:#E06C75;--shiki-dark:#79B8FF}html pre.shiki code .sxkdo, html code.shiki .sxkdo{--shiki-light:#F76D47;--shiki-default:#E06C75;--shiki-dark:#79B8FF}html pre.shiki code .s9IQt, html code.shiki .s9IQt{--shiki-light:#E53935;--shiki-default:#E06C75;--shiki-dark:#79B8FF}html pre.shiki code .sg798, html code.shiki .sg798{--shiki-light:#90A4AE;--shiki-light-font-style:inherit;--shiki-default:#FFFFFF;--shiki-default-font-style:inherit;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sZDfs, html code.shiki .sZDfs{--shiki-light:#90A4AE;--shiki-default:#ABB2BF;--shiki-dark:#E1E4E8}html pre.shiki code .sMTEB, html code.shiki .sMTEB{--shiki-light:#39ADB5;--shiki-default:#56B6C2;--shiki-dark:#F97583}html pre.shiki code .scCEF, html code.shiki .scCEF{--shiki-light:#39ADB5;--shiki-default:#D19A66;--shiki-dark:#79B8FF}",{"title":68,"searchDepth":128,"depth":128,"links":1870},[1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881],{"id":12,"depth":128,"text":13},{"id":51,"depth":128,"text":52},{"id":104,"depth":128,"text":104},{"id":255,"depth":128,"text":256},{"id":579,"depth":128,"text":580},{"id":608,"depth":128,"text":609},{"id":1258,"depth":128,"text":1259},{"id":1415,"depth":128,"text":1416},{"id":1762,"depth":128,"text":1762},{"id":1809,"depth":128,"text":1809},{"id":1824,"depth":128,"text":1825},"2026-08-03T00:00:00.000Z","แนะนำ Grafana Alloy ตัวเก็บ telemetry รุ่นใหม่ของ Grafana ตั้งแต่ติดตั้งด้วย docker เขียน config ส่ง log เข้า Loki ไปจนถึงคำสั่ง validate และ convert จาก Promtail",false,"md",{},null,"Grafana Alloy With Loki","/blogs/2026-08-03-grafana-alloy-loki",[1891],"2025-05-18-from-old-notebook-to-home-lab-server.md",{"title":5,"description":1883},{"loc":1889},"blogs/2026-08-03-grafana-alloy-loki",[1896],"Homelab","4ri5cnRtp0DQtmGT_GJ7z6bV1eWEI5iph1oBfohJLtY",[1899],{"id":1900,"title":1901,"body":1902,"date":2127,"description":2128,"draft":1884,"extension":1885,"meta":2129,"navigation":131,"next":1887,"ogTitle":2130,"path":2131,"pin":1884,"prev":1887,"recommends":2132,"seo":2135,"sitemap":2136,"stem":2137,"tags":2138,"updated":1887,"__hash__":2139},"content/blogs/2025-05-18-from-old-notebook-to-home-lab-server.md","เปลี่ยน Notebook (เก่า) ที่ไม่ได้ใช้งานเป็น Home Lab Server",{"type":7,"value":1903,"toc":2120},[1904,1909,1916,1920,1923,1944,1950,1962,1967,1970,1973,1993,1998,2009,2014,2020,2025,2028,2032,2050,2055,2058,2074,2078,2081,2083,2086,2089,2109,2114,2117],[15,1905,1906],{},[591,1907],{"alt":68,"src":1908},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-f67e4b1b-db83-40d3-82ed-3fbdc5562404.jpg",[15,1910,1911,1912,1915],{},"สวัสดีครับ พอดีผมมีโปรเจคนึงที่ต้องการใช้งาน Server ขนาดเล็ก ๆ เครื่องนึง กำลังจะหาซื้อเครื่อง Mini PC ใหม่มาเพิ่มพอดี แต่โปรเจคนี้ก็ยังไม่มั่นใจว่าจะไปต่อได้แค่ไหนก่อนที่จะซื้อ hardware มาใหม่ เลยจะลองหยิบเอา Notebook เครื่องเก่ามาลองรันดูก่อนครับ ไหน ๆ ก็ละเลยมาเขียนเป็นบทความสั้น ๆ เอาไว้แชร์เป็นไอเดียสำหรับคนที่ต้องการจะลองเล่น Home Lab แต่ยังไม่อยากจะซื้อเครื่องใหม่ จริง ๆ แล้ว ",[27,1913,1914],{},"notebook"," นายเองก็เป็นได้นะ server อะ !",[10,1917,1919],{"id":1918},"ข้อดีของการนำ-notebook-มาใช้งาน","ข้อดีของการนำ notebook มาใช้งาน",[15,1921,1922],{},"หลาย ๆ คนคงจะคุ้นชินกับการใช้งาน notebook ในแบบปกติคือการใช้งาน windows ทั่ว ๆ ไป แต่ก็อาจจะมีหลาย ๆ คนที่เป็น advanced user ขึ้นมาหน่อยจับมาลง Linux ตัวต่าง ๆ ใช้งาน วันนี้จะลองพามาเปลี่ยนจาก notebook ให้กลายเป็น server ขนาดเล็ก ๆ กันครับ แล้วมันเหมาะสมยังไง",[417,1924,1925,1928,1938,1941],{},[420,1926,1927],{},"มีแบตเตอร์รี่ในตัว ปกติแล้ว server หรือ home server ที่ดีควรจะมี UPS เพื่อช่วยสำรองไฟในกรณีที่ไฟตกหรือไฟดับจะช่วยทำให้อุปกรณ์เสียหายได้ยากขึ้น ซึ่ง notebook (ในสภาพปกติ) จะมีแบตเตอร์รี่ในตัวอยู่แล้ว",[420,1929,1930,1931,1935],{},"กินไฟน้อย ถ้าเป็น notebook ที่ไม่ใช้ gaming notebook นะครับ ปกติแล้วจะกินไฟน้อย อันนี้คือจังหวะที่กำลังชาร์จแบต + idle อยู่นะครับกินไฟอยู่ที่ประมาณ 30W เอง (อันนี้ใช้ performance mode ถ้าปรับเป็น balanced mode จะลงไปอยู่ที่ราว ๆ 15 W) (แต่ก็เยอะถ้าเทียบกับตระกูล mini PC ที่มี CPU N100) ",[1932,1933,1934],"em",{},"เดี๋ยวจะมาอัพเดทอีกทีหลังผ่านไป 1 เดือนนะครับว่ากินไฟไปกี่หน่วย",[591,1936],{"alt":68,"src":1937},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-661534a0-e4a4-4d14-9238-67f04d5de3a7.jpg",[420,1939,1940],{},"มีจอ, trackpad, keyboard ในตัว เวลาเครื่องมีปัญหาก็จะสามารถตรวจสอบได้เลย ไม่ต้องหาอุปกรณ์มาต่อเพิ่ม",[420,1942,1943],{},"ประหยัดค่าใช้จ่าย หลาย ๆ คนอยากจะเล่น Home Lab เป็นงานอดิเรกนะครับ ซึ่งการเอาอุปกรณ์ที่มีอยู่แล้วมาใช้งานก็จะช่วยประหยัดไปได้อีก",[15,1945,1946,1947],{},"ตอนที่ไม่ได้มีการใช้งานเปิดเครื่องทิ้งไว้เฉย ๆ กินไฟ 3W เอง (น้อยมากก)\n",[591,1948],{"alt":68,"src":1949},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-0bafd825-77ca-4f2c-b5ac-8ab9394dfb8f.jpg",[15,1951,1952,1953,1957,1958,1961],{},"ผมขอข้ามการติดตั้ง OS ไปนะครับ (ผมติดตั้งเป็น Ubuntu version 24 ไปกดรับ version pro ได้นะ ",[1831,1954,1956],{"href":1955},"/blogs/ubuntu-pro","วิธีรับ Ubuntu Pro",") เพราะทำแบบปกติทั่ว ๆ ไปเลยไม่ได้มีอะไรผิดปกติ แนะนำว่าให้เสียบสายแลนแทนการใช้งาน Wifi นะครับ เผื่อความ stable ของการใช้งาน หลังจากนั้นก็สามารถใช้ ",[19,1959,1960],{},"ssh"," เพื่อเชื่อมต่อเข้าไปได้เลย (IP address แนะนำให้ fixed ไว้ตอนติดตั้งนะครับ ไม่แนะนำให้ใช้ DHCP จะปวดหัวเอาทีหลัง)",[15,1963,1964],{},[591,1965],{"alt":68,"src":1966},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-1.png",[10,1968,1969],{"id":1969},"วิธีการทำให้หน้าจอของเครื่องดับ",[15,1971,1972],{},"ปกติแล้วในการใช้งาน ubuntu server จะไม่ได้ใช้งานหน้าจอหรืออุปกรณ์โดยตรงนะครับ ซึ่งตรงนี้เราจะสามารถปิดการใช้หลาย ๆ function ออกไปได้ ซึ่งในการปิดหน้าจอสามารถทำได้ตามนี้เลยครับ",[63,1974,1976],{"className":65,"code":1975,"language":67,"meta":68,"style":68},"sudo vim /etc/default/grub # หรือจะใช้ nano ก็ได้\n",[19,1977,1978],{"__ignoreMap":68},[72,1979,1980,1983,1986,1989],{"class":74,"line":75},[72,1981,1982],{"class":78},"sudo",[72,1984,1985],{"class":82}," vim",[72,1987,1988],{"class":82}," /etc/default/grub",[72,1990,1992],{"class":1991},"sDKbk"," # หรือจะใช้ nano ก็ได้\n",[15,1994,1995],{},[591,1996],{"alt":68,"src":1997},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-33c75f2a-a1f7-4294-a2d8-7b769330b6c6.jpg",[15,1999,2000,2001,2004,2005,2008],{},"โดยเพิ่ม ",[19,2002,2003],{},"consoleblank=120"," ไปยัง ",[19,2006,2007],{},"GRUB_CMDLINE_LINUX_DEFAULT="," สามารถปรับเวลาที่ต้องการได้เลย หน่วยเป็นวินาที",[15,2010,2011],{},[591,2012],{"alt":68,"src":2013},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-fd057b5b-b310-4d8c-b7e0-088c6481403f.jpg",[15,2015,2016,2017],{},"จากนั้นออกมาที่ terminal เหมือมเดิมแล้วรัน ",[19,2018,2019],{},"sudo update-grub",[15,2021,2022],{},[591,2023],{"alt":68,"src":2024},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-cad0302a-9626-4746-99da-7ef8acb21a57.jpg",[15,2026,2027],{},"จากนั้น reboot เครื่องแล้วรอตามเวลาที่ตั้งไว้ได้เลย ว่าหน้าจอจะดับหรือไม่ ของผมตั้งไว้ 120 ก็คือรอ 2 นาทีครับ",[10,2029,2031],{"id":2030},"ตรวจสอบว่า-sleep-mode-ถูกปิดใช้งานอยู่","ตรวจสอบว่า sleep mode ถูกปิดใช้งานอยู่",[63,2033,2035],{"className":65,"code":2034,"language":67,"meta":68,"style":68},"sudo systemctl status sleep.target\n",[19,2036,2037],{"__ignoreMap":68},[72,2038,2039,2041,2044,2047],{"class":74,"line":75},[72,2040,1982],{"class":78},[72,2042,2043],{"class":82}," systemctl",[72,2045,2046],{"class":82}," status",[72,2048,2049],{"class":82}," sleep.target\n",[15,2051,2052],{},[591,2053],{"alt":68,"src":2054},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-0f23e13d-d614-4174-852e-6257096028a1.jpg",[15,2056,2057],{},"วิธีการปิดใช้งาน",[63,2059,2061],{"className":65,"code":2060,"language":67,"meta":68,"style":68},"sudo systemctl mask sleep.target\n",[19,2062,2063],{"__ignoreMap":68},[72,2064,2065,2067,2069,2072],{"class":74,"line":75},[72,2066,1982],{"class":78},[72,2068,2043],{"class":82},[72,2070,2071],{"class":82}," mask",[72,2073,2049],{"class":82},[10,2075,2077],{"id":2076},"วิธีการปิดไฟ-black-light-ของ-keyboard","วิธีการปิดไฟ black light ของ keyboard",[15,2079,2080],{},"อันนี้จะขึ้นอยู่กับแต่ละรุ่นนะครับว่ารองรับการปิดใช้มั้ย ต้องเข้าไปดูใน bios เลยครับ ซึ่งของผมอันนี้ไม่รองรับ ต้องเดินไปกดปิดไฟหน้าเครื่องเอง (sad) ไว้จะมาลองหาวิธีอีกทีครับ",[10,2082,1809],{"id":1809},[15,2084,2085],{},"อันนี้ก็เป็นไอเดียนะครับว่าเราสามารถจับเอา notebook มาทำเป็น home server ขนาดเล็ก ๆ ได้ แต่อาจจะต้องปรับเปลี่ยนการตั้งค่านิดหน่อยเพื่อให้เหมาะสมกับการเป็น server จากนี้ก็ขึ้นอยู่กับจุดประสงค์ว่าจะเอาไปทำอะไรต่อ เช่น ลง docker + portainer ทำเป็น web server หรือจะลองลง proxmox ก็ได้นะ ผมมีเครื่อง notebook ที่ลองลง proxmox ไว้เหมือนกัน",[15,2087,2088],{},"สิ่งที่ทำเพิ่มได้",[417,2090,2091,2094,2097,2100,2103,2106],{},[420,2092,2093],{},"ติดตั้ง docker",[420,2095,2096],{},"ติดตั้ง zsh เพื่อปรับแต่ง terminal",[420,2098,2099],{},"ลง license เพื่อใช้งาน ubuntu pro (เคยเขียนบทความไว้แล้ว)",[420,2101,2102],{},"ติดตั้ง tailscale เพื่อใช้งานเครื่องนี้จากภายนอก",[420,2104,2105],{},"ติดตั้ง coder เพื่อทำเป็น remote dev server",[420,2107,2108],{},"ทำระบบ backup",[15,2110,2111],{},[591,2112],{"alt":68,"src":2113},"/images/2025-05-18-from-old-notebook-to-home-lab-server/images-ff8a1e4b-de92-477b-9e8a-a93b64ee8878.jpg",[15,2115,2116],{},"ขอบคุณที่อ่านจนจบครับบ เย่ ~",[1866,2118,2119],{},"html pre.shiki code .sosa_, html code.shiki .sosa_{--shiki-light:#E2931D;--shiki-default:#61AFEF;--shiki-dark:#B392F0}html pre.shiki code .sAlJX, html code.shiki .sAlJX{--shiki-light:#91B859;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .sDKbk, html code.shiki .sDKbk{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#7F848E;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":68,"searchDepth":128,"depth":128,"links":2121},[2122,2123,2124,2125,2126],{"id":1918,"depth":128,"text":1919},{"id":1969,"depth":128,"text":1969},{"id":2030,"depth":128,"text":2031},{"id":2076,"depth":128,"text":2077},{"id":1809,"depth":128,"text":1809},"2025-05-18T00:00:00.000Z","อยากลองเล่น Home Lab แต่ไม่อยากซื้ออุปกรณ์ใหม่ ลองเอา notebook เครื่องเก่ามาลองใช้ก่อนได้นะ",{},"Home Lab Server","/blogs/2025-05-18-from-old-notebook-to-home-lab-server",[2133,2134],"2025-04-15-ubuntu-pro.md","2024-01-14-setup-ubuntu-server.md",{"title":1901,"description":2128},{"loc":2131},"blogs/2025-05-18-from-old-notebook-to-home-lab-server",[1896],"h9IeFWVHTQTbxdgOil0HF02queMa9aGWkJO1uV6H9sk",[2141,2997,3428,3621,3840,4005,4447,4562,4651,4741],{"id":2142,"title":2143,"body":2144,"date":2986,"description":2987,"draft":1884,"extension":1885,"meta":2988,"navigation":131,"next":1887,"ogTitle":2989,"path":2990,"pin":1884,"prev":1887,"recommends":2991,"seo":2992,"sitemap":2993,"stem":2994,"tags":2995,"updated":1887,"__hash__":2996},"content/blogs/2024-01-14-setup-ubuntu-server.md","ตั้งค่า Ubuntu Server",{"type":7,"value":2145,"toc":2970},[2146,2148,2151,2154,2159,2188,2195,2213,2235,2252,2256,2283,2287,2322,2326,2332,2359,2366,2372,2410,2416,2455,2458,2469,2473,2480,2522,2526,2576,2587,2670,2677,2736,2742,2753,2756,2797,2808,2860,2863,2867,2924,2927,2967],[10,2147,13],{"id":12},[15,2149,2150],{},"รวม command ที่ต้องใช้บ่อย ๆ เมื่อมีการ Setup เครื่อง Ubuntu ใหม่ (Version 20, 22, 24)",[10,2152,2153],{"id":2153},"หมวดหมู่การตั้งค่าพื้นฐาน",[2155,2156,2158],"h3",{"id":2157},"update-packages-ต่าง-ๆ-ที่ติดตั้งมาใน-os-ให้เป็น-version-ล่าสุด","Update Packages ต่าง ๆ ที่ติดตั้งมาใน OS ให้เป็น Version ล่าสุด",[63,2160,2162],{"className":65,"code":2161,"language":67,"meta":68,"style":68},"sudo apt update && sudo apt upgrade -y\n",[19,2163,2164],{"__ignoreMap":68},[72,2165,2166,2168,2171,2174,2177,2180,2182,2185],{"class":74,"line":75},[72,2167,1982],{"class":78},[72,2169,2170],{"class":82}," apt",[72,2172,2173],{"class":82}," update",[72,2175,2176],{"class":642}," &&",[72,2178,2179],{"class":78}," sudo",[72,2181,2170],{"class":82},[72,2183,2184],{"class":82}," upgrade",[72,2186,2187],{"class":86}," -y\n",[2155,2189,2191,2192],{"id":2190},"แก้ไข-timezone-ให้เป็น-asiabangkok","แก้ไข timezone ให้เป็น ",[19,2193,2194],{},"Asia/Bangkok",[63,2196,2198],{"className":65,"code":2197,"language":67,"meta":68,"style":68},"sudo timedatectl set-timezone Asia/Bangkok\n",[19,2199,2200],{"__ignoreMap":68},[72,2201,2202,2204,2207,2210],{"class":74,"line":75},[72,2203,1982],{"class":78},[72,2205,2206],{"class":82}," timedatectl",[72,2208,2209],{"class":82}," set-timezone",[72,2211,2212],{"class":82}," Asia/Bangkok\n",[2155,2214,2216,2217,2220,2221,2224,2225,2224,2228,2224,2231,2234],{"id":2215},"ติดตั้ง-packages-ที่สำคัญสำหรับการพัฒนา-development-โดยภายใน-build-essential-จะมีการรวมเอา-packages-ที่สำคัญเอาไว้เช่น-gcc-c-make-lib6-dev-และอื่น-ๆ-อีกมากมาย","ติดตั้ง Packages ที่สำคัญสำหรับการพัฒนา (Development) โดยภายใน ",[19,2218,2219],{},"Build Essential"," จะมีการรวมเอา Packages ที่สำคัญเอาไว้เช่น ",[19,2222,2223],{},"gcc",", ",[19,2226,2227],{},"c++",[19,2229,2230],{},"make",[19,2232,2233],{},"lib6-dev"," และอื่น ๆ อีกมากมาย",[63,2236,2238],{"className":65,"code":2237,"language":67,"meta":68,"style":68},"sudo apt install build-essential\n",[19,2239,2240],{"__ignoreMap":68},[72,2241,2242,2244,2246,2249],{"class":74,"line":75},[72,2243,1982],{"class":78},[72,2245,2170],{"class":82},[72,2247,2248],{"class":82}," install",[72,2250,2251],{"class":82}," build-essential\n",[2155,2253,2255],{"id":2254},"แก้ไข-hostname","แก้ไข Hostname",[63,2257,2259],{"className":65,"code":2258,"language":67,"meta":68,"style":68},"sudo hostnamectl set-hostname \u003Chostname>\n",[19,2260,2261],{"__ignoreMap":68},[72,2262,2263,2265,2268,2271,2274,2277,2280],{"class":74,"line":75},[72,2264,1982],{"class":78},[72,2266,2267],{"class":82}," hostnamectl",[72,2269,2270],{"class":82}," set-hostname",[72,2272,2273],{"class":248}," \u003C",[72,2275,2276],{"class":82},"hostnam",[72,2278,2279],{"class":1537},"e",[72,2281,2282],{"class":248},">\n",[2155,2284,2286],{"id":2285},"ปิดการใช้งาน-cloud-init-network","ปิดการใช้งาน Cloud-init network",[63,2288,2290],{"className":65,"code":2289,"language":67,"meta":68,"style":68},"sudo nano  /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg\n\n# เอาไปใส่ในไฟล์\nnetwork: {config: disabled}\n",[19,2291,2292,2302,2306,2311],{"__ignoreMap":68},[72,2293,2294,2296,2299],{"class":74,"line":75},[72,2295,1982],{"class":78},[72,2297,2298],{"class":82}," nano",[72,2300,2301],{"class":82},"  /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg\n",[72,2303,2304],{"class":74,"line":128},[72,2305,132],{"emptyLinePlaceholder":131},[72,2307,2308],{"class":74,"line":135},[72,2309,2310],{"class":1991},"# เอาไปใส่ในไฟล์\n",[72,2312,2313,2316,2319],{"class":74,"line":155},[72,2314,2315],{"class":78},"network:",[72,2317,2318],{"class":82}," {config:",[72,2320,2321],{"class":82}," disabled}\n",[2155,2323,2325],{"id":2324},"ขั้นตอนการติดตั้ง-docker-ใน-version-daemon","ขั้นตอนการติดตั้ง docker ใน version daemon",[15,2327,2328,2329,2331],{},"ติดตั้ง ",[19,2330,193],{}," สำหรับการเรียกใช้งาน Script",[63,2333,2335],{"className":65,"code":2334,"language":67,"meta":68,"style":68},"sudo apt update\nsudo apt install curl -y\n",[19,2336,2337,2346],{"__ignoreMap":68},[72,2338,2339,2341,2343],{"class":74,"line":75},[72,2340,1982],{"class":78},[72,2342,2170],{"class":82},[72,2344,2345],{"class":82}," update\n",[72,2347,2348,2350,2352,2354,2357],{"class":74,"line":128},[72,2349,1982],{"class":78},[72,2351,2170],{"class":82},[72,2353,2248],{"class":82},[72,2355,2356],{"class":82}," curl",[72,2358,2187],{"class":86},[15,2360,2361,2362,2365],{},"ติดตั้งโดยใช้ script จาก ",[19,2363,2364],{},"get.docker.com"," ซึ่งจะช่วยในการติดตั้ง docker แบบง่ายมาก ๆ",[2367,2368,2369],"blockquote",{},[15,2370,2371],{},"script นี้จำเป็นต้องใช้ root หรือ sudo ในการทำงานนะครับ",[63,2373,2375],{"className":65,"code":2374,"language":67,"meta":68,"style":68},"cd /tmp\ncurl -fsSL https://get.docker.com -o get-docker.sh\nsudo sh get-docker.sh\n",[19,2376,2377,2385,2401],{"__ignoreMap":68},[72,2378,2379,2382],{"class":74,"line":75},[72,2380,2381],{"class":234},"cd",[72,2383,2384],{"class":82}," /tmp\n",[72,2386,2387,2389,2392,2395,2398],{"class":74,"line":128},[72,2388,193],{"class":78},[72,2390,2391],{"class":86}," -fsSL",[72,2393,2394],{"class":82}," https://get.docker.com",[72,2396,2397],{"class":86}," -o",[72,2399,2400],{"class":82}," get-docker.sh\n",[72,2402,2403,2405,2408],{"class":74,"line":135},[72,2404,1982],{"class":78},[72,2406,2407],{"class":82}," sh",[72,2409,2400],{"class":82},[15,2411,2412,2413,2415],{},"หากต้องการรัน docker โดยไม่ต้องรัน ",[19,2414,1982],{}," นำหน้าสามารถเพิ่ม user ดังกล่าวเข้าไปใน group docker ได้ครับ",[63,2417,2419],{"className":65,"code":2418,"language":67,"meta":68,"style":68},"# run docker without sudo\nsudo gpasswd -a $USER docker\nsudo service docker restart\n",[19,2420,2421,2426,2442],{"__ignoreMap":68},[72,2422,2423],{"class":74,"line":75},[72,2424,2425],{"class":1991},"# run docker without sudo\n",[72,2427,2428,2430,2433,2436,2439],{"class":74,"line":128},[72,2429,1982],{"class":78},[72,2431,2432],{"class":82}," gpasswd",[72,2434,2435],{"class":86}," -a",[72,2437,2438],{"class":626}," $USER",[72,2440,2441],{"class":82}," docker\n",[72,2443,2444,2446,2449,2452],{"class":74,"line":135},[72,2445,1982],{"class":78},[72,2447,2448],{"class":82}," service",[72,2450,2451],{"class":82}," docker",[72,2453,2454],{"class":82}," restart\n",[15,2456,2457],{},"ทดสอบเรียกใช้งาน",[63,2459,2461],{"className":65,"code":2460,"language":67,"meta":68,"style":68},"docker --version\n",[19,2462,2463],{"__ignoreMap":68},[72,2464,2465,2467],{"class":74,"line":75},[72,2466,79],{"class":78},[72,2468,93],{"class":86},[10,2470,2472],{"id":2471},"ติดตั้ง-helm","ติดตั้ง helm",[15,2474,2475,2476],{},"Credit: ",[1831,2477,2478],{"href":2478,"rel":2479},"https://helm.sh/docs/intro/install/#from-script",[1835],[63,2481,2483],{"className":65,"code":2482,"language":67,"meta":68,"style":68},"cd /tmp\ncurl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3\nchmod 700 get_helm.sh\n./get_helm.sh\n",[19,2484,2485,2491,2505,2517],{"__ignoreMap":68},[72,2486,2487,2489],{"class":74,"line":75},[72,2488,2381],{"class":234},[72,2490,2384],{"class":82},[72,2492,2493,2495,2497,2499,2502],{"class":74,"line":128},[72,2494,193],{"class":78},[72,2496,2391],{"class":86},[72,2498,2397],{"class":86},[72,2500,2501],{"class":82}," get_helm.sh",[72,2503,2504],{"class":82}," https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3\n",[72,2506,2507,2510,2514],{"class":74,"line":135},[72,2508,2509],{"class":78},"chmod",[72,2511,2513],{"class":2512},"snOyU"," 700",[72,2515,2516],{"class":82}," get_helm.sh\n",[72,2518,2519],{"class":74,"line":155},[72,2520,2521],{"class":78},"./get_helm.sh\n",[10,2523,2525],{"id":2524},"ติดตั้ง-zsh-p10k-configure-เพื่อให้ใช้งาน-terminal-ได้ง่ายยิ่งขึ้น","ติดตั้ง ZSH + P10k Configure เพื่อให้ใช้งาน terminal ได้ง่ายยิ่งขึ้น",[63,2527,2529],{"className":65,"code":2528,"language":67,"meta":68,"style":68},"sudo apt install curl wget git zsh -y\n\nsh -c \"$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\"\n\n",[19,2530,2531,2552,2556],{"__ignoreMap":68},[72,2532,2533,2535,2537,2539,2541,2544,2547,2550],{"class":74,"line":75},[72,2534,1982],{"class":78},[72,2536,2170],{"class":82},[72,2538,2248],{"class":82},[72,2540,2356],{"class":82},[72,2542,2543],{"class":82}," wget",[72,2545,2546],{"class":82}," git",[72,2548,2549],{"class":82}," zsh",[72,2551,2187],{"class":86},[72,2553,2554],{"class":74,"line":128},[72,2555,132],{"emptyLinePlaceholder":131},[72,2557,2558,2560,2563,2566,2568,2570,2573],{"class":74,"line":135},[72,2559,67],{"class":78},[72,2561,2562],{"class":86}," -c",[72,2564,2565],{"class":238}," \"$(",[72,2567,193],{"class":78},[72,2569,2391],{"class":86},[72,2571,2572],{"class":82}," https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh",[72,2574,2575],{"class":238},")\"\n",[15,2577,2578,2579,2582,2583,2586],{},"ติดตั้ง plugins เพิ่มเติมโดยแนะนำเป็น ",[19,2580,2581],{},"zsh-syntax-highlighting"," เพื่อช่วยในการ highlight command และ ",[19,2584,2585],{},"zsh-autosuggestions"," ซึ่งจะเป็นการนำเอา history มาแสดงให้ขณะพิมพ์ command",[63,2588,2590],{"className":65,"code":2589,"language":67,"meta":68,"style":68},"# Install plugins\ngit clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting\ngit clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions\n",[19,2591,2592,2597,2638],{"__ignoreMap":68},[72,2593,2594],{"class":74,"line":75},[72,2595,2596],{"class":1991},"# Install plugins\n",[72,2598,2599,2602,2605,2608,2611,2614,2617,2620,2623,2625,2628,2630,2633,2635],{"class":74,"line":128},[72,2600,2601],{"class":78},"git",[72,2603,2604],{"class":82}," clone",[72,2606,2607],{"class":82}," https://github.com/zsh-users/zsh-syntax-highlighting.git",[72,2609,2610],{"class":642}," ${",[72,2612,2613],{"class":626},"ZSH_CUSTOM",[72,2615,2616],{"class":248},":-",[72,2618,2619],{"class":1537},"~",[72,2621,2622],{"class":248},"/",[72,2624,1541],{"class":1537},[72,2626,2627],{"class":626},"oh-my-zsh",[72,2629,2622],{"class":248},[72,2631,2632],{"class":626},"custom",[72,2634,700],{"class":642},[72,2636,2637],{"class":82},"/plugins/zsh-syntax-highlighting\n",[72,2639,2640,2642,2644,2647,2649,2651,2653,2655,2657,2659,2661,2663,2665,2667],{"class":74,"line":135},[72,2641,2601],{"class":78},[72,2643,2604],{"class":82},[72,2645,2646],{"class":82}," https://github.com/zsh-users/zsh-autosuggestions",[72,2648,2610],{"class":642},[72,2650,2613],{"class":626},[72,2652,2616],{"class":248},[72,2654,2619],{"class":1537},[72,2656,2622],{"class":248},[72,2658,1541],{"class":1537},[72,2660,2627],{"class":626},[72,2662,2622],{"class":248},[72,2664,2632],{"class":626},[72,2666,700],{"class":642},[72,2668,2669],{"class":82},"/plugins/zsh-autosuggestions\n",[15,2671,2672,2673,2676],{},"แก้ไข ",[19,2674,2675],{},".zshrc"," file เพื่อทำการเพิ่ม plugins เข้าไป",[63,2678,2680],{"className":65,"code":2679,"language":67,"meta":68,"style":68},"nano ~/.zshrc\n\n# Update plugins\nplugins=(\n    git\n    zsh-syntax-highlighting # \u003C- Add\n    zsh-autosuggestions     # \u003C- Add\n)\n",[19,2681,2682,2690,2694,2699,2710,2715,2723,2731],{"__ignoreMap":68},[72,2683,2684,2687],{"class":74,"line":75},[72,2685,2686],{"class":78},"nano",[72,2688,2689],{"class":82}," ~/.zshrc\n",[72,2691,2692],{"class":74,"line":128},[72,2693,132],{"emptyLinePlaceholder":131},[72,2695,2696],{"class":74,"line":135},[72,2697,2698],{"class":1991},"# Update plugins\n",[72,2700,2701,2704,2707],{"class":74,"line":155},[72,2702,2703],{"class":626},"plugins",[72,2705,2706],{"class":1567},"=",[72,2708,2709],{"class":642},"(\n",[72,2711,2712],{"class":74,"line":166},[72,2713,2714],{"class":82},"    git\n",[72,2716,2717,2720],{"class":74,"line":177},[72,2718,2719],{"class":82},"    zsh-syntax-highlighting",[72,2721,2722],{"class":1991}," # \u003C- Add\n",[72,2724,2725,2728],{"class":74,"line":313},[72,2726,2727],{"class":82},"    zsh-autosuggestions",[72,2729,2730],{"class":1991},"     # \u003C- Add\n",[72,2732,2733],{"class":74,"line":319},[72,2734,2735],{"class":642},")\n",[15,2737,2738,2739,2741],{},"จากนั้น reload ",[19,2740,2675],{}," file เพื่อทำการ reload การตั้งค่า",[63,2743,2745],{"className":65,"code":2744,"language":67,"meta":68,"style":68},"source ~/.zshrc\n",[19,2746,2747],{"__ignoreMap":68},[72,2748,2749,2751],{"class":74,"line":75},[72,2750,1544],{"class":234},[72,2752,2689],{"class":82},[15,2754,2755],{},"ลง P10K เพิ่มเพื่อทำให้ terminal ดูสวยงามมากยิ่งขึ้น",[63,2757,2759],{"className":65,"code":2758,"language":67,"meta":68,"style":68},"git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k\n\n",[19,2760,2761],{"__ignoreMap":68},[72,2762,2763,2765,2767,2770,2773,2775,2777,2779,2782,2784,2786,2788,2790,2792,2794],{"class":74,"line":75},[72,2764,2601],{"class":78},[72,2766,2604],{"class":82},[72,2768,2769],{"class":86}," --depth=1",[72,2771,2772],{"class":82}," https://github.com/romkatv/powerlevel10k.git",[72,2774,2610],{"class":642},[72,2776,2613],{"class":626},[72,2778,2616],{"class":248},[72,2780,2781],{"class":626},"$HOME",[72,2783,2622],{"class":248},[72,2785,1541],{"class":1537},[72,2787,2627],{"class":626},[72,2789,2622],{"class":248},[72,2791,2632],{"class":626},[72,2793,700],{"class":642},[72,2795,2796],{"class":82},"/themes/powerlevel10k\n",[15,2798,2799,2800,2803,2804,2807],{},"จากนั้นแก้ไข ",[19,2801,2802],{},"ZSH_THEME"," ให้เป็น ",[19,2805,2806],{},"powerlevel10k/powerlevel10k"," เพื่อ load theme ดังกล่าวเข้าไป",[63,2809,2811],{"className":65,"code":2810,"language":67,"meta":68,"style":68},"# Update .zshrc file\nnano ~/.zshrc\n\n# Update Theme\nZSH_THEME=\"powerlevel10k/powerlevel10k\"\n\n# Reload\nsource ~/.zshrc\n",[19,2812,2813,2818,2824,2828,2833,2845,2849,2854],{"__ignoreMap":68},[72,2814,2815],{"class":74,"line":75},[72,2816,2817],{"class":1991},"# Update .zshrc file\n",[72,2819,2820,2822],{"class":74,"line":128},[72,2821,2686],{"class":78},[72,2823,2689],{"class":82},[72,2825,2826],{"class":74,"line":135},[72,2827,132],{"emptyLinePlaceholder":131},[72,2829,2830],{"class":74,"line":155},[72,2831,2832],{"class":1991},"# Update Theme\n",[72,2834,2835,2837,2839,2841,2843],{"class":74,"line":166},[72,2836,2802],{"class":626},[72,2838,2706],{"class":1567},[72,2840,703],{"class":238},[72,2842,2806],{"class":82},[72,2844,1679],{"class":238},[72,2846,2847],{"class":74,"line":177},[72,2848,132],{"emptyLinePlaceholder":131},[72,2850,2851],{"class":74,"line":313},[72,2852,2853],{"class":1991},"# Reload\n",[72,2855,2856,2858],{"class":74,"line":319},[72,2857,1544],{"class":234},[72,2859,2689],{"class":82},[2861,2862],"hr",{},[10,2864,2866],{"id":2865},"ติดตั้ง-nodejs-v22","ติดตั้ง NodeJS (v.22)",[63,2868,2870],{"className":65,"code":2869,"language":67,"meta":68,"style":68},"curl -sL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh\nsudo bash /tmp/nodesource_setup.sh\nsudo apt install nodejs\n\n# Verify\nnode -v\n",[19,2871,2872,2887,2896,2907,2911,2916],{"__ignoreMap":68},[72,2873,2874,2876,2879,2882,2884],{"class":74,"line":75},[72,2875,193],{"class":78},[72,2877,2878],{"class":86}," -sL",[72,2880,2881],{"class":82}," https://deb.nodesource.com/setup_22.x",[72,2883,2397],{"class":86},[72,2885,2886],{"class":82}," /tmp/nodesource_setup.sh\n",[72,2888,2889,2891,2894],{"class":74,"line":128},[72,2890,1982],{"class":78},[72,2892,2893],{"class":82}," bash",[72,2895,2886],{"class":82},[72,2897,2898,2900,2902,2904],{"class":74,"line":135},[72,2899,1982],{"class":78},[72,2901,2170],{"class":82},[72,2903,2248],{"class":82},[72,2905,2906],{"class":82}," nodejs\n",[72,2908,2909],{"class":74,"line":155},[72,2910,132],{"emptyLinePlaceholder":131},[72,2912,2913],{"class":74,"line":166},[72,2914,2915],{"class":1991},"# Verify\n",[72,2917,2918,2921],{"class":74,"line":177},[72,2919,2920],{"class":78},"node",[72,2922,2923],{"class":86}," -v\n",[15,2925,2926],{},"ติดตั้ง yarn เพิ่มเติม",[63,2928,2930],{"className":65,"code":2929,"language":67,"meta":68,"style":68},"# Install yarn\nsudo npm install -g yarn\n\n# Verify\nyarn --version\n",[19,2931,2932,2937,2952,2956,2960],{"__ignoreMap":68},[72,2933,2934],{"class":74,"line":75},[72,2935,2936],{"class":1991},"# Install yarn\n",[72,2938,2939,2941,2944,2946,2949],{"class":74,"line":128},[72,2940,1982],{"class":78},[72,2942,2943],{"class":82}," npm",[72,2945,2248],{"class":82},[72,2947,2948],{"class":86}," -g",[72,2950,2951],{"class":82}," yarn\n",[72,2953,2954],{"class":74,"line":135},[72,2955,132],{"emptyLinePlaceholder":131},[72,2957,2958],{"class":74,"line":155},[72,2959,2915],{"class":1991},[72,2961,2962,2965],{"class":74,"line":166},[72,2963,2964],{"class":78},"yarn",[72,2966,93],{"class":86},[1866,2968,2969],{},"html pre.shiki code .sosa_, html code.shiki .sosa_{--shiki-light:#E2931D;--shiki-default:#61AFEF;--shiki-dark:#B392F0}html pre.shiki code .sAlJX, html code.shiki .sAlJX{--shiki-light:#91B859;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .stlKB, html code.shiki .stlKB{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#E1E4E8}html pre.shiki code .sEoV_, html code.shiki .sEoV_{--shiki-light:#91B859;--shiki-default:#D19A66;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbGrh, html code.shiki .sbGrh{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#F97583}html pre.shiki code .sZDfs, html code.shiki .sZDfs{--shiki-light:#90A4AE;--shiki-default:#ABB2BF;--shiki-dark:#E1E4E8}html pre.shiki code .sDKbk, html code.shiki .sDKbk{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#7F848E;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sJZfL, html code.shiki .sJZfL{--shiki-light:#6182B8;--shiki-default:#56B6C2;--shiki-dark:#79B8FF}html pre.shiki code .sLHrO, html code.shiki .sLHrO{--shiki-light:#90A4AE;--shiki-default:#E06C75;--shiki-dark:#E1E4E8}html pre.shiki code .snOyU, html code.shiki .snOyU{--shiki-light:#F76D47;--shiki-default:#D19A66;--shiki-dark:#79B8FF}html pre.shiki code .snfNA, html code.shiki .snfNA{--shiki-light:#39ADB5;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .sMTEB, html code.shiki .sMTEB{--shiki-light:#39ADB5;--shiki-default:#56B6C2;--shiki-dark:#F97583}",{"title":68,"searchDepth":128,"depth":128,"links":2971},[2972,2973,2983,2984,2985],{"id":12,"depth":128,"text":13},{"id":2153,"depth":128,"text":2153,"children":2974},[2975,2976,2978,2980,2981,2982],{"id":2157,"depth":135,"text":2158},{"id":2190,"depth":135,"text":2977},"แก้ไข timezone ให้เป็น Asia/Bangkok",{"id":2215,"depth":135,"text":2979},"ติดตั้ง Packages ที่สำคัญสำหรับการพัฒนา (Development) โดยภายใน Build Essential จะมีการรวมเอา Packages ที่สำคัญเอาไว้เช่น gcc, c++, make, lib6-dev และอื่น ๆ อีกมากมาย",{"id":2254,"depth":135,"text":2255},{"id":2285,"depth":135,"text":2286},{"id":2324,"depth":135,"text":2325},{"id":2471,"depth":128,"text":2472},{"id":2524,"depth":128,"text":2525},{"id":2865,"depth":128,"text":2866},"2024-01-14T00:00:00.000Z","โน๊ตนี้คือ commands ที่มักจะใช้บ่อย ๆ เมื่อต้อง setup ubuntu server (20.04, 22.04)",{},"Setup Ubuntu Server","/blogs/2024-01-14-setup-ubuntu-server",[2133],{"title":2143,"description":2987},{"loc":2990},"blogs/2024-01-14-setup-ubuntu-server",[1896],"5oBy7u1bHxX4cEDFPSQBKPIWtGVACmqhxjEqyYarFoc",{"id":2998,"title":2999,"body":3000,"date":3417,"description":3418,"draft":1884,"extension":1885,"meta":3419,"navigation":131,"next":1887,"ogTitle":2999,"path":3420,"pin":1884,"prev":1887,"recommends":3421,"seo":3422,"sitemap":3423,"stem":3424,"tags":3425,"updated":3426,"__hash__":3427},"content/blogs/2024-01-19-setup-uptime-kuma.md","Setup Uptime Kuma",{"type":7,"value":3001,"toc":3409},[3002,3004,3007,3017,3022,3026,3077,3081,3088,3182,3186,3275,3281,3286,3290,3367,3379,3390,3394,3406],[10,3003,13],{"id":12},[15,3005,3006],{},"โน๊ตนี้คือ วิธีการ setup uptime kuma แบบไม่ใช้ docker",[417,3008,3009],{},[420,3010,3011,3012],{},"สำหรับข้อมูลฉบับเต็มสามารถดูได้ที่ ",[1831,3013,3016],{"href":3014,"rel":3015},"https://github.com/louislam/uptime-kuma",[1835],"louislam/uptime-kuma",[2367,3018,3019],{},[15,3020,3021],{},"อัปเดต 2026-08-09 ตอนที่เขียนโน๊ตนี้ครั้งแรกยังเป็น 1.23.x อยู่ ตอนนี้ 2.x ออกมาแล้วนะ\nเลยกลับมาแก้ส่วนของการอัปเดตให้ตรงกับของจริง",[10,3023,3025],{"id":3024},"_1-basic-setup","1. Basic setup",[63,3027,3029],{"className":65,"code":3028,"language":67,"meta":68,"style":68},"sudo apt update && sudo apt upgrade -y\nsudo timedatectl set-timezone Asia/Bangkok\n\nsudo apt install curl git -y\n",[19,3030,3031,3049,3059,3063],{"__ignoreMap":68},[72,3032,3033,3035,3037,3039,3041,3043,3045,3047],{"class":74,"line":75},[72,3034,1982],{"class":78},[72,3036,2170],{"class":82},[72,3038,2173],{"class":82},[72,3040,2176],{"class":642},[72,3042,2179],{"class":78},[72,3044,2170],{"class":82},[72,3046,2184],{"class":82},[72,3048,2187],{"class":86},[72,3050,3051,3053,3055,3057],{"class":74,"line":128},[72,3052,1982],{"class":78},[72,3054,2206],{"class":82},[72,3056,2209],{"class":82},[72,3058,2212],{"class":82},[72,3060,3061],{"class":74,"line":135},[72,3062,132],{"emptyLinePlaceholder":131},[72,3064,3065,3067,3069,3071,3073,3075],{"class":74,"line":155},[72,3066,1982],{"class":78},[72,3068,2170],{"class":82},[72,3070,2248],{"class":82},[72,3072,2356],{"class":82},[72,3074,2546],{"class":82},[72,3076,2187],{"class":86},[10,3078,3080],{"id":3079},"_2-install-nodejs-v22-and-pm2","2. Install nodejs (v.22) and pm2",[15,3082,3083,3084,3087],{},"Uptime Kuma 2.x บังคับ nodejs ตั้งแต่ 20.4.0 ขึ้นไป (ดูได้จาก ",[19,3085,3086],{},"engines"," ใน package.json ของ repo) ลง v.22 ไปเลยก็ได้ครับ",[63,3089,3091],{"className":65,"code":3090,"language":67,"meta":68,"style":68},"curl -sL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh\n\nsudo bash /tmp/nodesource_setup.sh\n\nsudo apt install nodejs\n\n# Verify\nnode -v\n\n# Install pm2\nsudo npm install -g pm2\n\n# Verify\npm2 --version\n",[19,3092,3093,3105,3109,3117,3121,3131,3135,3139,3145,3149,3154,3167,3171,3175],{"__ignoreMap":68},[72,3094,3095,3097,3099,3101,3103],{"class":74,"line":75},[72,3096,193],{"class":78},[72,3098,2878],{"class":86},[72,3100,2881],{"class":82},[72,3102,2397],{"class":86},[72,3104,2886],{"class":82},[72,3106,3107],{"class":74,"line":128},[72,3108,132],{"emptyLinePlaceholder":131},[72,3110,3111,3113,3115],{"class":74,"line":135},[72,3112,1982],{"class":78},[72,3114,2893],{"class":82},[72,3116,2886],{"class":82},[72,3118,3119],{"class":74,"line":155},[72,3120,132],{"emptyLinePlaceholder":131},[72,3122,3123,3125,3127,3129],{"class":74,"line":166},[72,3124,1982],{"class":78},[72,3126,2170],{"class":82},[72,3128,2248],{"class":82},[72,3130,2906],{"class":82},[72,3132,3133],{"class":74,"line":177},[72,3134,132],{"emptyLinePlaceholder":131},[72,3136,3137],{"class":74,"line":313},[72,3138,2915],{"class":1991},[72,3140,3141,3143],{"class":74,"line":319},[72,3142,2920],{"class":78},[72,3144,2923],{"class":86},[72,3146,3147],{"class":74,"line":325},[72,3148,132],{"emptyLinePlaceholder":131},[72,3150,3151],{"class":74,"line":330},[72,3152,3153],{"class":1991},"# Install pm2\n",[72,3155,3156,3158,3160,3162,3164],{"class":74,"line":336},[72,3157,1982],{"class":78},[72,3159,2943],{"class":82},[72,3161,2248],{"class":82},[72,3163,2948],{"class":86},[72,3165,3166],{"class":82}," pm2\n",[72,3168,3169],{"class":74,"line":342},[72,3170,132],{"emptyLinePlaceholder":131},[72,3172,3173],{"class":74,"line":348},[72,3174,2915],{"class":1991},[72,3176,3177,3180],{"class":74,"line":353},[72,3178,3179],{"class":78},"pm2",[72,3181,93],{"class":86},[10,3183,3185],{"id":3184},"_3-install-uptime-kuma","3. Install Uptime Kuma",[63,3187,3189],{"className":65,"code":3188,"language":67,"meta":68,"style":68},"git clone https://github.com/louislam/uptime-kuma.git\n\ncd uptime-kuma\n\nnpm run setup\n\npm2 install pm2-logrotate\n\npm2 start server/server.js --name uptime-kuma\n\npm2 save && pm2 startup\n",[19,3190,3191,3200,3204,3211,3215,3225,3229,3238,3242,3256,3260],{"__ignoreMap":68},[72,3192,3193,3195,3197],{"class":74,"line":75},[72,3194,2601],{"class":78},[72,3196,2604],{"class":82},[72,3198,3199],{"class":82}," https://github.com/louislam/uptime-kuma.git\n",[72,3201,3202],{"class":74,"line":128},[72,3203,132],{"emptyLinePlaceholder":131},[72,3205,3206,3208],{"class":74,"line":135},[72,3207,2381],{"class":234},[72,3209,3210],{"class":82}," uptime-kuma\n",[72,3212,3213],{"class":74,"line":155},[72,3214,132],{"emptyLinePlaceholder":131},[72,3216,3217,3220,3222],{"class":74,"line":166},[72,3218,3219],{"class":78},"npm",[72,3221,83],{"class":82},[72,3223,3224],{"class":82}," setup\n",[72,3226,3227],{"class":74,"line":177},[72,3228,132],{"emptyLinePlaceholder":131},[72,3230,3231,3233,3235],{"class":74,"line":313},[72,3232,3179],{"class":78},[72,3234,2248],{"class":82},[72,3236,3237],{"class":82}," pm2-logrotate\n",[72,3239,3240],{"class":74,"line":319},[72,3241,132],{"emptyLinePlaceholder":131},[72,3243,3244,3246,3249,3252,3254],{"class":74,"line":325},[72,3245,3179],{"class":78},[72,3247,3248],{"class":82}," start",[72,3250,3251],{"class":82}," server/server.js",[72,3253,145],{"class":86},[72,3255,3210],{"class":82},[72,3257,3258],{"class":74,"line":330},[72,3259,132],{"emptyLinePlaceholder":131},[72,3261,3262,3264,3267,3269,3272],{"class":74,"line":336},[72,3263,3179],{"class":78},[72,3265,3266],{"class":82}," save",[72,3268,2176],{"class":642},[72,3270,3271],{"class":78}," pm2",[72,3273,3274],{"class":82}," startup\n",[15,3276,3277,3280],{},[19,3278,3279],{},"npm run setup"," จะ checkout ไปที่ tag ล่าสุดให้เองครับ ไม่ต้องระบุ version เอง ตอนนี้ (2026-08-09) คือ 2.5.0",[15,3282,3283],{},[19,3284,3285],{},"http://ip:3001",[10,3287,3289],{"id":3288},"_4-update-uptime-kuma","4. Update Uptime Kuma",[63,3291,3293],{"className":65,"code":3292,"language":67,"meta":68,"style":68},"cd uptime-kuma\n\ngit fetch --all\ngit checkout 2.5.0 --force\n\nnpm ci --omit dev\nnpm run download-dist\n\npm2 restart uptime-kuma\n",[19,3294,3295,3301,3305,3315,3328,3332,3345,3354,3358],{"__ignoreMap":68},[72,3296,3297,3299],{"class":74,"line":75},[72,3298,2381],{"class":234},[72,3300,3210],{"class":82},[72,3302,3303],{"class":74,"line":128},[72,3304,132],{"emptyLinePlaceholder":131},[72,3306,3307,3309,3312],{"class":74,"line":135},[72,3308,2601],{"class":78},[72,3310,3311],{"class":82}," fetch",[72,3313,3314],{"class":86}," --all\n",[72,3316,3317,3319,3322,3325],{"class":74,"line":155},[72,3318,2601],{"class":78},[72,3320,3321],{"class":82}," checkout",[72,3323,3324],{"class":2512}," 2.5.0",[72,3326,3327],{"class":86}," --force\n",[72,3329,3330],{"class":74,"line":166},[72,3331,132],{"emptyLinePlaceholder":131},[72,3333,3334,3336,3339,3342],{"class":74,"line":177},[72,3335,3219],{"class":78},[72,3337,3338],{"class":82}," ci",[72,3340,3341],{"class":86}," --omit",[72,3343,3344],{"class":82}," dev\n",[72,3346,3347,3349,3351],{"class":74,"line":313},[72,3348,3219],{"class":78},[72,3350,83],{"class":82},[72,3352,3353],{"class":82}," download-dist\n",[72,3355,3356],{"class":74,"line":319},[72,3357,132],{"emptyLinePlaceholder":131},[72,3359,3360,3362,3365],{"class":74,"line":325},[72,3361,3179],{"class":78},[72,3363,3364],{"class":82}," restart",[72,3366,3210],{"class":82},[15,3368,3369,3370,3373,3374],{},"เลข version ตรง ",[19,3371,3372],{},"git checkout"," เปลี่ยนตามอันล่าสุดได้เลย ดูได้ที่ ",[1831,3375,3378],{"href":3376,"rel":3377},"https://github.com/louislam/uptime-kuma/releases",[1835],"Releases",[2367,3380,3381],{},[15,3382,3383,3384,3389],{},"ถ้าอัปจาก 1.x มา 2.x อันนี้เป็น major version มี breaking changes อยู่ ทาง repo แนะนำให้สำรองข้อมูลก่อน\nแล้วอ่าน ",[1831,3385,3388],{"href":3386,"rel":3387},"https://github.com/louislam/uptime-kuma/wiki/Migration-From-v1-To-v2",[1835],"Migration From v1 To v2"," ให้จบก่อนนะ",[10,3391,3393],{"id":3392},"ref","Ref",[417,3395,3396,3401],{},[420,3397,3398],{},[1831,3399,3016],{"href":3014,"rel":3400},[1835],[420,3402,3403],{},[1831,3404,3378],{"href":3376,"rel":3405},[1835],[1866,3407,3408],{},"html pre.shiki code .sosa_, html code.shiki .sosa_{--shiki-light:#E2931D;--shiki-default:#61AFEF;--shiki-dark:#B392F0}html pre.shiki code .sAlJX, html code.shiki .sAlJX{--shiki-light:#91B859;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .stlKB, html code.shiki .stlKB{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#E1E4E8}html pre.shiki code .sEoV_, html code.shiki .sEoV_{--shiki-light:#91B859;--shiki-default:#D19A66;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sDKbk, html code.shiki .sDKbk{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#7F848E;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sJZfL, html code.shiki .sJZfL{--shiki-light:#6182B8;--shiki-default:#56B6C2;--shiki-dark:#79B8FF}html pre.shiki code .snOyU, html code.shiki .snOyU{--shiki-light:#F76D47;--shiki-default:#D19A66;--shiki-dark:#79B8FF}",{"title":68,"searchDepth":128,"depth":128,"links":3410},[3411,3412,3413,3414,3415,3416],{"id":12,"depth":128,"text":13},{"id":3024,"depth":128,"text":3025},{"id":3079,"depth":128,"text":3080},{"id":3184,"depth":128,"text":3185},{"id":3288,"depth":128,"text":3289},{"id":3392,"depth":128,"text":3393},"2024-01-19T00:00:00.000Z","วิธีติดตั้ง Uptime Kuma บน Ubuntu แบบไม่ใช้ docker ด้วย nodejs กับ pm2 พร้อมวิธีอัปเดตเป็น version 2.x",{},"/blogs/2024-01-19-setup-uptime-kuma",[],{"title":2999,"description":3418},{"loc":3420},"blogs/2024-01-19-setup-uptime-kuma",[1896],"2026-08-09T00:00:00.000Z","fNs_-CirZm8l5v2KDuNENJEJekQvNA8T3eMSnkyPnrc",{"id":3429,"title":3430,"body":3431,"date":3610,"description":3611,"draft":1884,"extension":1885,"meta":3612,"navigation":131,"next":1887,"ogTitle":3613,"path":3614,"pin":1884,"prev":1887,"recommends":3615,"seo":3616,"sitemap":3617,"stem":3618,"tags":3619,"updated":1887,"__hash__":3620},"content/blogs/2025-04-15-ubuntu-pro.md","วิธีการติดตั้ง Ubuntu Version Pro แบบฟรี",{"type":7,"value":3432,"toc":3605},[3433,3436,3446,3450,3515,3525,3529,3544,3550,3553,3558,3563,3573,3578,3585,3587,3590],[15,3434,3435],{},"หลายคนอาจเคยเห็นว่า Ubuntu มีเวอร์ชัน Pro ให้ใช้งาน ไม่ว่าจะในหน้าจอติดตั้งระบบ หรือเป็นหน้าต่างแจ้งเตือนเมื่อเข้าสู่ระบบปฏิบัติการแล้ว",[15,3437,3438,3439,3442,3443],{},"ทาง Ubuntu เปิดให้ใช้งานเวอร์ชัน Pro ได้ฟรี สำหรับผู้ใช้แบบส่วนตัวสูงสุด ",[19,3440,3441],{},"5 active machines"," และสำหรับสมาชิก Ubuntu community สูงสุด ",[19,3444,3445],{},"50 active machines",[10,3447,3449],{"id":3448},"ความแตกต่างหลักระหว่าง-ubuntu-ปกติ-และ-ubuntu-pro","ความแตกต่างหลักระหว่าง Ubuntu ปกติ และ Ubuntu Pro",[3451,3452,3453,3469],"table",{},[3454,3455,3456],"thead",{},[3457,3458,3459,3463,3466],"tr",{},[3460,3461,3462],"th",{},"คุณสมบัติ",[3460,3464,3465],{},"Ubuntu ปกติ",[3460,3467,3468],{},"Ubuntu Pro (ฟรี)",[3470,3471,3472,3484,3495,3504],"tbody",{},[3457,3473,3474,3478,3481],{},[3475,3476,3477],"td",{},"ระยะเวลารับอัปเดต",[3475,3479,3480],{},"5 ปี",[3475,3482,3483],{},"10 ปี",[3457,3485,3486,3489,3492],{},[3475,3487,3488],{},"Livepatch",[3475,3490,3491],{},"-",[3475,3493,3494],{},"✓",[3457,3496,3497,3500,3502],{},[3475,3498,3499],{},"การ hardening ระบบ",[3475,3501,3491],{},[3475,3503,3494],{},[3457,3505,3506,3509,3512],{},[3475,3507,3508],{},"จำนวนเครื่องที่รองรับ",[3475,3510,3511],{},"ไม่จำกัด",[3475,3513,3514],{},"5 เครื่อง (ส่วนตัว)",[2367,3516,3517],{},[15,3518,3519,3520,3524],{},"สามารถอ่านเพิ่มเติมได้ที่ ",[1831,3521,3522],{"href":3522,"rel":3523},"https://ubuntu.com/pricing/pro",[1835]," ตรง Ubuntu Pro for personal users",[10,3526,3528],{"id":3527},"ขั้นตอนการกดรับ-license","ขั้นตอนการกดรับ License",[3530,3531,3532],"ol",{},[420,3533,3534,3535,3539,3540],{},"สมัคร account ที่ ",[1831,3536,3537],{"href":3537,"rel":3538},"https://ubuntu.com",[1835]," ให้เรียบร้อยจากนั้นไปที่หน้า ",[1831,3541,3542],{"href":3542,"rel":3543},"https://ubuntu.com/pro",[1835],[15,3545,3546],{},[591,3547],{"alt":3548,"src":3549},"Ubuntu Pro","/images/2025-04-15-ubuntu-pro/image-1.png",[15,3551,3552],{},"แล้วกดที่ปุ่ม \"Get Ubuntu Pro now\" ได้เลยครับ",[3530,3554,3555],{"start":128},[420,3556,3557],{},"จากนั้นเลือกเป็น \"Myself\" ได้เลยครับ จะเป็นการกดรับ license free กด continue to card ได้เลย",[15,3559,3560],{},[591,3561],{"alt":3548,"src":3562},"/images/2025-04-15-ubuntu-pro/image-2.png",[3530,3564,3565],{"start":135},[420,3566,3567,3568,3572],{},"เมื่อเสร็จสิ้นขั้นตอนให้ไปยัง ",[1831,3569,3570],{"href":3570,"rel":3571},"https://ubuntu.com/pro/dashboard",[1835]," ตรงเมนู \"Your subscriptions\" จะเจอกับ Token ซึ่งสามารถนำไปใช้งานได้เลยครับ",[15,3574,3575],{},[591,3576],{"alt":3548,"src":3577},"/images/2025-04-15-ubuntu-pro/image-3.png",[15,3579,3580,3581,3584],{},"หากเป็นการติดตั้งระบบปฏิบัติการใหม่ สามารถกรอก Token ได้ในขั้นตอนติดตั้ง แต่หากติดตั้ง Ubuntu ไปแล้ว สามารถใช้คำสั่ง ",[19,3582,3583],{},"sudo pro attach \u003CTOKEN>"," เพื่อเปิดใช้งาน License ได้เลย",[2861,3586],{},[10,3588,3589],{"id":3589},"แหล่งข้อมูลเพิ่มเติม",[417,3591,3592,3599],{},[420,3593,3594],{},[1831,3595,3598],{"href":3596,"rel":3597},"https://ubuntu.com/legal/ubuntu-pro/personal",[1835],"Ubuntu Pro for Personal Use",[420,3600,3601],{},[1831,3602,3604],{"href":3522,"rel":3603},[1835],"Ubuntu Pro Pricing",{"title":68,"searchDepth":128,"depth":128,"links":3606},[3607,3608,3609],{"id":3448,"depth":128,"text":3449},{"id":3527,"depth":128,"text":3528},{"id":3589,"depth":128,"text":3589},"2025-04-15T00:00:00.000Z","วิธีขอและเปิดใช้ Ubuntu Pro ฟรีสำหรับ personal use สูงสุด 5 เครื่อง ได้อัปเดตความปลอดภัยยาว 10 ปี",{},"Ubuntu Pro Version","/blogs/2025-04-15-ubuntu-pro",[2134],{"title":3430,"description":3611},{"loc":3614},"blogs/2025-04-15-ubuntu-pro",[1896],"4URY48rjIDwOv4p9KyD2cdd-LNS--_uYloigKRGb5UY",{"id":3622,"title":3623,"body":3624,"date":3829,"description":3830,"draft":1884,"extension":1885,"meta":3831,"navigation":131,"next":1887,"ogTitle":3832,"path":3833,"pin":1884,"prev":1887,"recommends":3834,"seo":3835,"sitemap":3836,"stem":3837,"tags":3838,"updated":1887,"__hash__":3839},"content/blogs/2025-05-12-next-dns.md","ผมย้ายจาก Pihole & Adguard มาใช้ NextDNS",{"type":7,"value":3625,"toc":3816},[3626,3631,3635,3638,3641,3644,3648,3653,3663,3666,3670,3673,3677,3680,3683,3687,3690,3693,3696,3701,3704,3707,3713,3716,3719,3724,3727,3738,3741,3747,3750,3755,3759,3764,3767,3771,3774,3779,3783,3786,3791,3793,3796,3799,3806,3813],[15,3627,3628],{},[591,3629],{"alt":68,"src":3630},"/images/2025-05-12-next-dns/images-7cc8fbe7-61fd-422c-90fd-f1f87ff3201e.jpg",[10,3632,3634],{"id":3633},"nextdns-คือ","NextDNS คือ",[15,3636,3637],{},"DNS Server ที่สามารถช่วยในการป้องกัน security threads, block โฆษณา หรือ block การ tracking ของ website หรือ application ต่าง ๆ โดยที่เราสามารถตั้งค่าได้ว่าในแต่ละ device จะใช้ policy ใดในการป้องกัน",[15,3639,3640],{},"โดยวิธีการทำงานตัว NextDNS จะมี list ของ domain ที่เป็นอันตราย (สามารถใช้ Blacklist มาตรฐานทั่ว ๆ ไปของ Adguard หรือ PiHole ได้) เมื่อมีการพยายาม query ข้อมูลที่เป็นอันตรายจาก domain ที่อยู่ใน blacklist ตัว NextDNS ก็จะทำการ block คำขอดังกล่าวแทน",[15,3642,3643],{},"วิธีการใช้งานตั้งค่าจะคล้าย ๆ กับ Pihole หรือ Adguard เลยครับ แต่ต่างตรงที่เราไม่ต้อง self-hosted เอง, สามารถจัดการเองได้บน website \"my.nextdns.io\" ครับ",[10,3645,3647],{"id":3646},"pricing","Pricing",[15,3649,3650],{},[591,3651],{"alt":68,"src":3652},"/images/2025-05-12-next-dns/images-21282f92-db55-4b79-aa63-67fc9aa7303a.jpg",[15,3654,3655,3658,3659,3662],{},[1932,3656,3657],{},"ข้อมูล ณ วันที่ 12/5/68","\nจะมีให้ใช้ 4 Plan หลัก ๆ ด้วยกันโดยที่มี version free ให้ใช้ด้วยครับ สามารถตั้งค่าได้เหมือนกับ version เสียตัง แต่จะแตกต่างตรงที่หากครบ 300,000 queries ต่อเดือนแล้วหลังจากนั้น",[27,3660,3661],{},"จะไม่มีการ block query"," (จะกลายเป็นเหมือน DNS ธรรมดาทั่ว ๆ ไป) เลยไม่ต้องกังวลไปนะครับว่าหลังจากครบโควต้าฟรีแล้วจะใช้งาน DNS ไม่ได้",[15,3664,3665],{},"ส่วนตัวผมเลือกเป็น Version Pro ครับ ปีละ 590 บาท (ทดลองใช้ตัวฟรีประมาณ 1 สัปดาห์ก็ตัดสินใจจ่ายเงินเลยครับ)",[10,3667,3669],{"id":3668},"ควรเลือก-plan-ไหนดี","ควรเลือก Plan ไหนดี",[15,3671,3672],{},"สำหรับผมแล้ว 300,000 queries ต่อเดือน บอกเลยครับว่า \"ไม่พอ\" 5555 เลยตัดสินใจ subscription เป็นตัว pro แทน แต่ก็แนะนำว่าให้ลองใช้ดูก่อนก็ได้ครับ ว่าเข้ากับ lifestyle ของตัวเองรึเปล่า ถ้าถูกใจค่อยจ่ายตังก็ได้ครับ เพราะตัว free มี featureเหมือนกับตัว pro หรือตัวเสียตังเลย",[10,3674,3676],{"id":3675},"เอาเงินค่า-subscription-ไปเช่า-server-แล้ว-self-hosted-เองจะดีกว่ามั้ย","เอาเงินค่า subscription ไปเช่า server แล้ว self-hosted เองจะดีกว่ามั้ย",[15,3678,3679],{},"เครื่อง server 1 ปีในราคา 590 บาทถ้าถามว่ามีมั้ย ก็ต้องบอกว่าพอจะหาได้ครับ ผมเองก็เป็นสาย Self-hosted, Home Lab ที่ชอบการ self-hosted อะไรหลาย ๆ อย่าง แต่ผมมีความคิดนึงครับว่าจะมีอยู่ไม่กี่อย่างที่จะไม่ self-hosted เองได้แก่ Email Server, Bitwarden (password manager) และ DNS เพราะสิ่งเหล่านี้การดูแลรักษาให้ใช้งานได้และปลอดภัยถือว่าเป็น challenge มากครับ ถ้าใช้งานไม่ได้ขึ้นมางานหยาบทันที ดังนั้นยอมเสียตังหน่อยเพื่อใช้ความ stable ตรงนี้ดีกว่าครับ (อันนี้มุมมองผมนะ)",[15,3681,3682],{},"แล้วก็เคยมีประสบการณ์ที่ Internal ช้าเพราะว่า Setup Pihole & Adguard ไม่ดีทำให้จังหวะ query มันแปลก ๆ กว่าจะหาเหตุผลเจอว่าเป็นที่การตั้งค่าของตัวเอง ก็โทษ ISP อยู่หลายวัน 55555 (อันนี้ยอมรับว่าเป็น skill issue ของตัวเอง) แต่ก็อยากแชร์เป็น use-case ให้ฟังครับ",[10,3684,3686],{"id":3685},"dns-over-https","DNS over HTTPS",[15,3688,3689],{},"อันนี้เป็นอีกเหตุผลนึงที่ผมย้ายมาใช้ NextDNS เพราะว่ามี DoH ให้ใช้ อธิบายให้เห็นภาพง่าย ๆ ให้มองว่าถ้าเราใช้ DNS แบบปกติทางผู้ให้บริการ internal หรือผู้ดูแลระบบจะสามารถดู DNS query ได้ (DNS query จะไม่ได้มีการเข้ารหัสไว้) ซึ่งการมาใช้ DoH จะทำให้มี Privacy ในส่วนนี้มากยิ่งขึ้น จะเป็นการ encryption DNS query อีกทั้งยังสามารถป้องกันการโดน DNS Proxy จากผู้ดูแลได้อีกด้วย (สำหรับคนที่กังวลในเรื่อง Privacy แบบผมอันนี้คือจุดสำคัญเลยครับ)",[10,3691,3692],{"id":3692},"วิธีการใช้งาน",[15,3694,3695],{},"ต้องบอกว่าสามารถใช้งานได้ในแทบทุกอุปกรณ์เลย ส่วนตัวผมเองใช้งานบน Windows, MacOS, IPAD OS, iOS โดยจะมีการตั้งค่าหลายวิธีเลยครับ แต่แนะนำว่าให้ใช้วิธีการติดตั้ง Application ดีกว่า ตัวอย่างเช่น iOS",[15,3697,3698],{},[591,3699],{"alt":68,"src":3700},"/images/2025-05-12-next-dns/images-44fe4e0e-4f38-4ed5-abd5-c165444b4de7.jpg",[15,3702,3703],{},"ซึ่งสามารถใช้งานได้ 2 วิธีคือการลง Profile กับติดตั้ง Application บอกตรง ๆ ว่าผมไม่กล้าเลือกการติดตั้งโดยใช้ Profile ครับ เลยเลือกเป็น Download Application จาก App Store แทน",[15,3705,3706],{},"วิธีการใช้งานง่ายมากจริง ๆ อ่านตรงข้างล่างก็ทำตามได้แล้วครับ",[15,3708,3709,3710],{},"ผมเองใช้งานทั้งในรูปแบบของการติดตั้งบน end devices และติดตั้งบน router ที่บ้านเลยครับ แต่ถ้าเป็นการติดตั้งบน router ที่ไม่ได้รองรับการ setup next dns (อุุปกรณ์ที่รองรับเช่น Mikrotik, Tailscale, pfSense) จำเป็นต้องตั้งค่า DNS แบบปกติ (เอา IP มาใส่) แล้ว Update ตรง \"Linked IP\"\n",[591,3711],{"alt":68,"src":3712},"/images/2025-05-12-next-dns/images-5da350b0-f339-429e-a108-edfefb194df2.jpg",[15,3714,3715],{},"ให้ IP เป็นของบ้านเรา แล้วจะสามารถใช้ Profile นั้น ๆ ได้ จะใช้เป็นตั้งค่า DDNS หรือทำ scheduler เพื่อไป update ip ก็ได้ครับ (ผมใช้ n8n ทำ workflows ให้ loop fetch เรื่อย ๆ)",[10,3717,3718],{"id":3718},"ป้องกันอะไรได้บ้าง",[15,3720,3721],{},[591,3722],{"alt":68,"src":3723},"/images/2025-05-12-next-dns/images-0eae9706-37a8-4bc3-ab47-0e7f86206438.jpg",[15,3725,3726],{},"ภายใน Tab Security จะมี options ให้ตั้งค่าได้ครับว่า Profile นี้จะให้มีการป้องกันอะไรบ้าง เช่น",[417,3728,3729,3732,3735],{},[420,3730,3731],{},"Threat Intelligence Feeds: ป้องกัน domain ที่เป็นอันตรายเช่น Phishing, c2 server",[420,3733,3734],{},"DNS Rebinding Protection: ป้องกัน DNS ประเภทที่ Mapping เป็น Private IP",[420,3736,3737],{},"Typosquatting Protection: ป้องกัน domain ประเภทที่เจตนาเล่นกับการพิมพ์ผิด เช่น gooogle.com เป็นต้น (เว็บจริงคือ google.com)",[15,3739,3740],{},"และอื่น ๆ อีกตั้งค่าได้เยอะประมาณนึงเลยครับ",[15,3742,3743,3744],{},"เลือก Blacklists เองได้ เหมือนกับ Adguard & Pihole เลย\n",[591,3745],{"alt":68,"src":3746},"/images/2025-05-12-next-dns/images-1d87500f-04aa-4aaa-b6f8-d46c4f0b7da4.jpg",[15,3748,3749],{},"มี Parental Control ใช้สำหรับการสร้าง Profile เพื่อป้องกันเด็ก ๆ ที่บ้านดูในสิ่งที่ไม่ควรจะดูได้ สามารถ block การเข้าถึง application ต่าง ๆ ได้ ตั้งเวลาในการเข้าใช้งาน domain ต่าง ๆ ได้ครับ",[15,3751,3752],{},[591,3753],{"alt":68,"src":3754},"/images/2025-05-12-next-dns/images-890aa801-ef39-44e6-bfa4-d13f15d97025.jpg",[10,3756,3758],{"id":3757},"รองรับหลาย-profile","รองรับหลาย Profile",[15,3760,3761],{},[591,3762],{"alt":68,"src":3763},"/images/2025-05-12-next-dns/images-76b242e0-e379-4af6-ad5e-75eec5ac5c51.jpg",[15,3765,3766],{},"ในบางกรณีจะนำไปใช้ในการควบคุมการเข้าถึงของบุคคลอื่น ๆ เช่นผู้สูงอายุที่บ้าน, เด็ก ๆ ที่บ้าน ทาง NextDNS เองก็มีการให้ตั้งค่า Profile ได้เช่นเดียวกัน โดยที่แต่ละ Profile จะมี ID ของตัวเองและตั้งค่าแยกกันโดยอิสระ ในเครื่องที่ต้องการใช้ Profile อื่นก็ให้นำ ID ใน Tab Setup ไปใส่ได้เลยครับ",[10,3768,3770],{"id":3769},"ดู-query-logs-ได้","ดู query logs ได้",[15,3772,3773],{},"สามารถดู Logs การ query domain ต่าง ๆ ได้ครับ สิ่งจะเห็นทุก ๆ อุปกรณ์ที่ใช้ Profile ID นี้ แต่ตรงนี้สามารถปิดได้นะครับ (ปกติก็ผมจะปิดไว้)",[15,3775,3776],{},[591,3777],{"alt":68,"src":3778},"/images/2025-05-12-next-dns/images-aaeef8ce-564a-4afb-abb2-e9fdbbd29f9b.jpg",[10,3780,3782],{"id":3781},"server-มีที่ไหนบ้าง","Server มีที่ไหนบ้าง",[15,3784,3785],{},"มีหลาย Server มากครับโดยใกล้สุดคืออยู่ที่กรุงเทพ ซึ่ง Ping เพียงแค่ \"7ms\" น้อยมาก ๆ อันนี้เลยเป็นอีกเหตุผลที่ผมเลือกใช้",[15,3787,3788],{},[591,3789],{"alt":68,"src":3790},"/images/2025-05-12-next-dns/images-509d5eaf-58e3-46b7-a425-01b7b562443d.jpg",[10,3792,1809],{"id":1809},[15,3794,3795],{},"ผมใช้งานมาได้ประมาณ 1 เดือนแล้วครับ ตอนนี้ก็เอา Adgaurd ออกจาก Home Server แล้ว จากที่ใช้มายังไม่เจอเหตุการณ์ที่ Server Down แล้วทำให้ใช้งานไม่ได้เลยครับ ส่วนตัวให้ 9/10 หัก 1 คะแนนตรงที่ ถ้ามี Custom DNS record ให้ใช้จะดีเลยครับ แต่อันนี้ไม่มี",[15,3797,3798],{},"สนใจสมัครได้ที่",[15,3800,3801,3802],{},"(affiliate link): ",[1831,3803,3804],{"href":3804,"rel":3805},"https://nextdns.io/?from=2ggah5wn",[1835],[15,3807,3808,3809],{},"(link ปกติ): ",[1831,3810,3811],{"href":3811,"rel":3812},"https://my.nextdns.io/",[1835],[15,3814,3815],{},"กดสมัครผ่าน affiliate link เพื่อเป็นกำลังใจเล็ก ๆ น้อย ๆ ได้นะครับ ~",{"title":68,"searchDepth":128,"depth":128,"links":3817},[3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828],{"id":3633,"depth":128,"text":3634},{"id":3646,"depth":128,"text":3647},{"id":3668,"depth":128,"text":3669},{"id":3675,"depth":128,"text":3676},{"id":3685,"depth":128,"text":3686},{"id":3692,"depth":128,"text":3692},{"id":3718,"depth":128,"text":3718},{"id":3757,"depth":128,"text":3758},{"id":3769,"depth":128,"text":3770},{"id":3781,"depth":128,"text":3782},{"id":1809,"depth":128,"text":1809},"2025-05-12T00:00:00.000Z","การมี DNS ที่ช่วยในการ Block โฆษณาหรือการ Tracking ในยุคสมัยนี้ถือว่าเป็นสิ่งที่ควรจะมี",{},"NextDNS","/blogs/2025-05-12-next-dns",[],{"title":3623,"description":3830},{"loc":3833},"blogs/2025-05-12-next-dns",[1896],"AxQCd5onmd9TH4k-Y4t4pP2k8lbRUHexZW547MjeeKY",{"id":1900,"title":1901,"body":3841,"date":2127,"description":2128,"draft":1884,"extension":1885,"meta":4000,"navigation":131,"next":1887,"ogTitle":2130,"path":2131,"pin":1884,"prev":1887,"recommends":4001,"seo":4002,"sitemap":4003,"stem":2137,"tags":4004,"updated":1887,"__hash__":2139},{"type":7,"value":3842,"toc":3993},[3843,3847,3851,3853,3855,3869,3873,3879,3883,3885,3887,3901,3905,3911,3915,3919,3923,3925,3927,3941,3945,3947,3961,3963,3965,3967,3969,3971,3985,3989,3991],[15,3844,3845],{},[591,3846],{"alt":68,"src":1908},[15,3848,1911,3849,1915],{},[27,3850,1914],{},[10,3852,1919],{"id":1918},[15,3854,1922],{},[417,3856,3857,3859,3865,3867],{},[420,3858,1927],{},[420,3860,1930,3861,3863],{},[1932,3862,1934],{},[591,3864],{"alt":68,"src":1937},[420,3866,1940],{},[420,3868,1943],{},[15,3870,1946,3871],{},[591,3872],{"alt":68,"src":1949},[15,3874,1952,3875,1957,3877,1961],{},[1831,3876,1956],{"href":1955},[19,3878,1960],{},[15,3880,3881],{},[591,3882],{"alt":68,"src":1966},[10,3884,1969],{"id":1969},[15,3886,1972],{},[63,3888,3889],{"className":65,"code":1975,"language":67,"meta":68,"style":68},[19,3890,3891],{"__ignoreMap":68},[72,3892,3893,3895,3897,3899],{"class":74,"line":75},[72,3894,1982],{"class":78},[72,3896,1985],{"class":82},[72,3898,1988],{"class":82},[72,3900,1992],{"class":1991},[15,3902,3903],{},[591,3904],{"alt":68,"src":1997},[15,3906,2000,3907,2004,3909,2008],{},[19,3908,2003],{},[19,3910,2007],{},[15,3912,3913],{},[591,3914],{"alt":68,"src":2013},[15,3916,2016,3917],{},[19,3918,2019],{},[15,3920,3921],{},[591,3922],{"alt":68,"src":2024},[15,3924,2027],{},[10,3926,2031],{"id":2030},[63,3928,3929],{"className":65,"code":2034,"language":67,"meta":68,"style":68},[19,3930,3931],{"__ignoreMap":68},[72,3932,3933,3935,3937,3939],{"class":74,"line":75},[72,3934,1982],{"class":78},[72,3936,2043],{"class":82},[72,3938,2046],{"class":82},[72,3940,2049],{"class":82},[15,3942,3943],{},[591,3944],{"alt":68,"src":2054},[15,3946,2057],{},[63,3948,3949],{"className":65,"code":2060,"language":67,"meta":68,"style":68},[19,3950,3951],{"__ignoreMap":68},[72,3952,3953,3955,3957,3959],{"class":74,"line":75},[72,3954,1982],{"class":78},[72,3956,2043],{"class":82},[72,3958,2071],{"class":82},[72,3960,2049],{"class":82},[10,3962,2077],{"id":2076},[15,3964,2080],{},[10,3966,1809],{"id":1809},[15,3968,2085],{},[15,3970,2088],{},[417,3972,3973,3975,3977,3979,3981,3983],{},[420,3974,2093],{},[420,3976,2096],{},[420,3978,2099],{},[420,3980,2102],{},[420,3982,2105],{},[420,3984,2108],{},[15,3986,3987],{},[591,3988],{"alt":68,"src":2113},[15,3990,2116],{},[1866,3992,2119],{},{"title":68,"searchDepth":128,"depth":128,"links":3994},[3995,3996,3997,3998,3999],{"id":1918,"depth":128,"text":1919},{"id":1969,"depth":128,"text":1969},{"id":2030,"depth":128,"text":2031},{"id":2076,"depth":128,"text":2077},{"id":1809,"depth":128,"text":1809},{},[2133,2134],{"title":1901,"description":2128},{"loc":2131},[1896],{"id":4006,"title":4007,"body":4008,"date":4437,"description":4438,"draft":1884,"extension":1885,"meta":4439,"navigation":131,"next":1887,"ogTitle":4440,"path":4441,"pin":1884,"prev":1887,"recommends":1887,"seo":4442,"sitemap":4443,"stem":4444,"tags":4445,"updated":1887,"__hash__":4446},"content/blogs/2025-08-26-xponology-on-old-pc.md","ติดตั้ง Xpenology บน PC เครื่องเก่ากัน",{"type":7,"value":4009,"toc":4429},[4010,4014,4017,4020,4023,4027,4030,4044,4047,4052,4055,4063,4066,4069,4079,4084,4087,4092,4103,4106,4109,4122,4127,4146,4151,4154,4158,4161,4166,4169,4174,4177,4182,4185,4190,4193,4198,4201,4206,4209,4214,4217,4222,4225,4230,4233,4238,4241,4246,4249,4254,4259,4262,4267,4270,4275,4278,4283,4286,4290,4298,4303,4306,4311,4316,4321,4324,4329,4334,4339,4342,4347,4350,4355,4358,4363,4368,4371,4376,4379,4384,4389,4392,4397,4400,4405,4408,4413,4415],[10,4011,4013],{"id":4012},"xpenology-คืออะไร","Xpenology คืออะไร?",[15,4015,4016],{},"Xpenology เป็นส่วนของซอฟต์แวร์แบบ “bootloader” ที่ทำให้ระบบปฏิบัติการของ Synology ซึ่งเรียกว่า DSM (DiskStation Manager) สามารถรันบน hardware ทั่วไปได้ แทนที่จะต้องใช้เฉพาะเครื่อง NAS ของ Synology เท่านั้น",[15,4018,4019],{},"DSM เป็นระบบปฏิบัติการที่ Synology พัฒนาบน Linux ออกแบบมาเพื่อทำงานบนเครื่อง NAS มีฟีเจอร์ที่จำเป็นสำหรับการเก็บข้อมูล แชร์ไฟล์ steaming และอื่น ๆ อีกมากมาย",[15,4021,4022],{},"ซึ่งการติดตั้ง Xpenology พูดง่าย ๆ คือการที่จะทำให้เราสามารถใช้งาน Synology OS บน Hardware ทั่ว ๆ ไปได้นั่นเอง โดยในวันนี้ผมจะมาติดตั้งบน PC เครื่องเก่ากันนะครับ",[10,4024,4026],{"id":4025},"hardware-วันนี้","Hardware วันนี้",[15,4028,4029],{},"โดยเครื่องนี้จะเป็น",[417,4031,4032,4035,4038,4041],{},[420,4033,4034],{},"CPU: I3 7100",[420,4036,4037],{},"Ram: 8GB DDR4",[420,4039,4040],{},"HDD: 1TB 2 ลูก",[420,4042,4043],{},"SSD: 256GB สำหรับติดตั้ง OS",[15,4045,4046],{},"โดย PC เครื่องนี้ผมซื้อมือ 2 มาจาก facebook marketplace โดยตอนซื้อมาจะมีแค่ CPU, RAM, HDD 1 ลูก โดยเงื่อนไขของการทำ DIY NAS ของผมคือต้องใส่ HDD อย่างน้อยได้ 2 ลูก เพื่อทำ RAID 1 (RAID Software ก็ได้)",[15,4048,4049],{},[591,4050],{"alt":68,"src":4051},"/images/2025-08-26-xponology-on-old-pc/images-e8de1f9e-b6b3-4964-9299-d404f3f70541.jpg",[15,4053,4054],{},"ปัญหาต่อมาคืออยากจะใช้ SSD เพื่อทำให้ OS ทำงานได้ไวขึ้น มี 2 ทางเลือกครับ (บอร์ดรุ่นเก่าไม่มีช่อง M.2 ให้เสียบ sadddd)",[417,4056,4057,4060],{},[420,4058,4059],{},"ซื้อ SSD แบบ SATA มาเสียบเพิ่ม เพราะช่อง SATA มี 4 ช่อง (ใช้ไปแล้ว 2)",[420,4061,4062],{},"ซื้อตัวแปร PCI-E เป็น M.2 แล้วเสียบ SSD แบบ M.2",[15,4064,4065],{},"ตอนแรกทางเลือกแรกคือทางเลือกที่ดีสำหรับผม แต่แล้วเมื่อตรวจสอบสายไฟจาก POWER แล้วก็ทำให้น้ำตาไหล เพราะว่ามีสายไฟสำหรับ HDD แค่ 2 ช่อง (ใช้ครบแล้ว) เลยทำให้ต้องไปทางเลือกที่ 2 นั่นก็คือซื้อ PCE to M.2",[15,4067,4068],{},"สิ่งที่ลุ้นและขี้เกียจก็คือซื้อมาแล้ว Mainboard จะรองรับมั้ย ซึ่งตามหลัก Engineer ก็คือต้องเปิด Datasheet เพื่อหาข้อมูล แต่ไม่ทำ (5555) ซื้อเลย ได้ไม่ได้ค่อยว่ากันอีกที เพราะตั้งใจจะทำ DIY อยู่แล้ว เลยไปเจอใน shopee มาครับเป็นตัวนี้",[15,4070,4071,4074,4075],{},[27,4072,4073],{},"JEYI PCIe X4 to M.2 NVMe",": ",[1831,4076,4077],{"href":4077,"rel":4078},"https://s.shopee.co.th/VwlrEE0hL",[1835],[15,4080,4081],{},[591,4082],{"alt":68,"src":4083},"/images/2025-08-26-xponology-on-old-pc/images-1183b11d-997c-413e-aa3a-f0caf0a40d9d.jpg",[15,4085,4086],{},"เมื่อประกอบ SSD เข้าไปจะหน้าตาประมาณนี้ครับ",[15,4088,4089],{},[591,4090],{"alt":68,"src":4091},"/images/2025-08-26-xponology-on-old-pc/images-43be6af3-b251-4c21-86fd-37d63fa4d4aa.jpg",[417,4093,4094,4097,4100],{},[420,4095,4096],{},"โดย Adapter ตัวนี้จะเสียบลงไปที่ช่อง PCI-E ได้ทั้ง x4 x8 x16 เลย",[420,4098,4099],{},"SSD ตัวนี้เป็น M.2 NVME Gen 4 ความเร็วก็คือวิ่งได้ที่ระดับ Gen 4 เลยครับ",[420,4101,4102],{},"รองรับ mainboard ในหลาย ๆ รุ่นจากที่ลองหาข้อมูลดูนะครับ ตัวอย่างผมซื้อมาใช้กับ Asrock H11M-K รุ่นค่อนข้างเก่าเลย แต่ก็ยังใช้งานได้",[15,4104,4105],{},"ต่อไปมาเริ่มติดตั้ง OS กันครับ ~",[10,4107,4108],{"id":4108},"เตรียมการก่อนเริ่มติดตั้ง",[3530,4110,4111],{},[420,4112,4113,4114,4118,4119],{},"download img จาก ",[1831,4115,4116],{"href":4116,"rel":4117},"https://github.com/AuxXxilium/arc/releases/",[1835]," โดยเลือกเป็น .img.zip นะครับ โดยในตัวอย่างผมติดตั้งเป็น version ",[1932,4120,4121],{},"2.6.5",[15,4123,4124],{},[591,4125],{"alt":68,"src":4126},"/images/2025-08-26-xponology-on-old-pc/images-d9342298-65c4-4b3d-925d-b18be0afcfdb.jpg",[3530,4128,4129,4140],{"start":128},[420,4130,4131,4132,4136,4137],{},"สำหรับโปรแกรมที่ใช้การสร้าง bootloader ผมจะใช้เป็น ตัวนี้นะครับ สามารถโหลดได้จาก ",[1831,4133,4134],{"href":4134,"rel":4135},"https://etcher.balena.io",[1835]," ผมใช้ตัวนี้เป็นประจำ หากมีเครื่องมือตัวอื่นที่ถนัดก็สามารถใช้ได้เหมือนกัน",[591,4138],{"alt":68,"src":4139},"/images/2025-08-26-xponology-on-old-pc/images-be9017e4-edbe-41bc-8b98-67ae7ec9e1f2.jpg",[420,4141,4142,4143],{},"ต้องมี Flashdrive ขนาด 4GB++ ด้วยนะครับ โดยเลือก image เป็น ARC ส่วนของ Target Drive ก็เลือกเป็น Flashdrive แล้วนั่งรอได้เลย ",[591,4144],{"alt":68,"src":4145},"/images/2025-08-26-xponology-on-old-pc/images-a68d5af0-d0c5-46e5-92f6-e5b6cb141946.jpg",[15,4147,4148],{},[591,4149],{"alt":68,"src":4150},"/images/2025-08-26-xponology-on-old-pc/images-dce47f43-fc8c-41e3-880b-ee970b3c54ce.jpg",[15,4152,4153],{},"เมื่อเสร็จแล้วสามารถนำไปเสียบกับ PC แล้วเลือกให้ Boot จาก Flashdrive ได้เลย (วิธีการขึ้นอยู่กับ Hardware โดยปกติก็เข้า bios แล้วปรับให้ boot จาก flashdrive ก่อนได้เลย)",[10,4155,4157],{"id":4156},"ติดตั้ง-xpenology","ติดตั้ง Xpenology",[15,4159,4160],{},"ของผมจะเป็น Mainboard ASUS เมื่อเข้ามา Bios ได้แล้วให้กด F8 แล้วเลือก Flashdrive",[15,4162,4163],{},[591,4164],{"alt":68,"src":4165},"/images/2025-08-26-xponology-on-old-pc/images-a9e1330c-551a-48b3-9be2-44cef6cfc9af.jpg",[15,4167,4168],{},"กด Arc Config Mode เพื่อเริ่มการตั้งค่าได้เลย (ผมเองก็ยังมือใหม่นะ ลองผิดลองถูกมาหลายรอบเหมือนกัน 5555)",[15,4170,4171],{},[591,4172],{"alt":68,"src":4173},"/images/2025-08-26-xponology-on-old-pc/images-684d99e1-c4dc-4992-929c-1f2c43c233d3.jpg",[15,4175,4176],{},"Choose Model",[15,4178,4179],{},[591,4180],{"alt":68,"src":4181},"/images/2025-08-26-xponology-on-old-pc/images-103a577d-9d02-497d-b32e-f0134880c992.jpg",[15,4183,4184],{},"ผมจะเลือกเป็น DS920+ (แนะนำให้เลือกเป็นตัวที่มี + จะทำอะไรได้เยอะกว่า)",[15,4186,4187],{},[591,4188],{"alt":68,"src":4189},"/images/2025-08-26-xponology-on-old-pc/images-4ec24e02-3a07-4e8d-9715-ade4c1fc5a13.jpg",[15,4191,4192],{},"ขั้นตอนนี้ผมจะเลือกเป็น No นะเพื่อดูการตั้งค่าเพิ่มเติม",[15,4194,4195],{},[591,4196],{"alt":68,"src":4197},"/images/2025-08-26-xponology-on-old-pc/images-50b1276f-aebd-4e3d-b2d8-c2e422ac2d65.jpg",[15,4199,4200],{},"เลือกเป็น \"Use random SN/Mac (Reduced DSM Features)\"",[15,4202,4203],{},[591,4204],{"alt":68,"src":4205},"/images/2025-08-26-xponology-on-old-pc/images-168b3668-464c-4bdc-b74a-55377d806109.jpg",[15,4207,4208],{},"ติดตั้ง Addons ผมจะเลือกเป็น default ไปเลยครับ",[15,4210,4211],{},[591,4212],{"alt":68,"src":4213},"/images/2025-08-26-xponology-on-old-pc/images-86186fc1-f851-4d02-b009-3ad9ca1bf2cb.jpg",[15,4215,4216],{},"ในส่วนของ Power option จะเลือกเป็น conservative ตามคำแนะนำเลย เพราะจะ balance ระหว่าง performance และการประหยัดไฟ แต่ถ้าใครอยากประหยัดไฟสุด ๆ ก็เลือกเป็น powersave ได้เหมือนกันนะ",[15,4218,4219],{},[591,4220],{"alt":68,"src":4221},"/images/2025-08-26-xponology-on-old-pc/images-7d83a70d-3bea-40d7-9c1a-1fcbc8417e19.jpg",[15,4223,4224],{},"ผมจะยังไม่ build ครับ จะไปแก้ไขการตั้งค่าเพิ่มเติมอีกนิด",[15,4226,4227],{},[591,4228],{"alt":68,"src":4229},"/images/2025-08-26-xponology-on-old-pc/images-adab944f-3d00-4f12-ab3c-cb24e08f2fee.jpg",[15,4231,4232],{},"จากนั้นผมจะ Format Disk ตัวหลักสำหรับลง OS ไป โดยเลือกในหัวข้อ \"Show Loader Options\" แล้วไปที่ \"Format Disks\"",[15,4234,4235],{},[591,4236],{"alt":68,"src":4237},"/images/2025-08-26-xponology-on-old-pc/images-9fa91ef7-7702-473e-afd2-9001bf83ea29.jpg",[15,4239,4240],{},"เลือก drive ที่ต้องการ format ในที่นี้ผมจะ format /dev/nvme0 นะครับ",[15,4242,4243],{},[591,4244],{"alt":68,"src":4245},"/images/2025-08-26-xponology-on-old-pc/images-ccd646d7-e3cb-4478-a9af-43162f3393c2.jpg",[15,4247,4248],{},"จากนั้นผมจะทำการ Clone bootloader จาก flashdrive ไปยัง SSD ที่ได้มีการ format ในขั้นตอนก่อนหน้า",[15,4250,4251],{},[591,4252],{"alt":68,"src":4253},"/images/2025-08-26-xponology-on-old-pc/images-972237ff-6eb8-4809-b141-86dd087ac4a2.jpg",[15,4255,4256],{},[591,4257],{"alt":68,"src":4258},"/images/2025-08-26-xponology-on-old-pc/images-a2e1e489-d07d-45c1-a477-aaf84a95f1cf.jpg",[15,4260,4261],{},"จากนั้นก็นั่งรอระบบทำการ Clone ให้ครับ",[15,4263,4264],{},[591,4265],{"alt":68,"src":4266},"/images/2025-08-26-xponology-on-old-pc/images-81d93c17-e52b-4e7c-ba52-f6a58d8a456e.jpg",[15,4268,4269],{},"เมื่อเสร็จแล้วก็ให้ถอด flashdrive ออกได้เลยย แล้วก็ reboot ได้เลยครับ",[15,4271,4272],{},[591,4273],{"alt":68,"src":4274},"/images/2025-08-26-xponology-on-old-pc/images-baf17eec-c164-4342-a35c-06d5f8d2fc14.jpg",[15,4276,4277],{},"ระบบจะ boot แล้วกลับมาหน้านี้ (อย่าลืมถอด flashdrive ก่อนนะ ไม่งั้นอาจจะยังเป็นการ boot จาก flashdrive อยู่)",[15,4279,4280],{},[591,4281],{"alt":68,"src":4282},"/images/2025-08-26-xponology-on-old-pc/images-55c9740c-54ab-4047-bf63-3d32566019f5.jpg",[15,4284,4285],{},"ถ้ายังใช้ไม่ได้ให้กด Arc Config Mode -> Build อีกทีดูนะครับ",[10,4287,4289],{"id":4288},"setup-synology","Setup Synology",[15,4291,4292,4293,4297],{},"เปิด Browser เข้าไปที่ ",[1831,4294,4295],{"href":4295,"rel":4296},"http://finds.synology.com/",[1835]," จากนั้นจะเจอกับ synology เครื่องใหม่ของเราครับ ตรง model name อาจจะยังไม่ตรงอยู่ไม่เป็นไร",[15,4299,4300],{},[591,4301],{"alt":68,"src":4302},"/images/2025-08-26-xponology-on-old-pc/images-f8a9ccd4-c797-4619-8fef-2f3ebcb2b754.jpg",[15,4304,4305],{},"จากนั้นก็ Next Next Next",[15,4307,4308],{},[591,4309],{"alt":68,"src":4310},"/images/2025-08-26-xponology-on-old-pc/images-53c51d89-810e-4606-8fb5-9a9764e17bd5.jpg",[15,4312,4313],{},[591,4314],{"alt":68,"src":4315},"/images/2025-08-26-xponology-on-old-pc/images-5d0ad6a6-f349-4b30-8e49-a72a2d74576c.jpg",[15,4317,4318],{},[591,4319],{"alt":68,"src":4320},"/images/2025-08-26-xponology-on-old-pc/images-466bd21f-e423-4a16-860c-f83d875cc79f.jpg",[15,4322,4323],{},"แล้วก็นั่งรอแบบ จ๊อง จ๊อง",[15,4325,4326],{},[591,4327],{"alt":68,"src":4328},"/images/2025-08-26-xponology-on-old-pc/images-fde692f4-1653-4fb8-a25d-45f786e58ea1.jpg",[15,4330,4331],{},[591,4332],{"alt":68,"src":4333},"/images/2025-08-26-xponology-on-old-pc/images-b0d91faa-8ab0-42b5-ba03-36dd055ec016.jpg",[15,4335,4336],{},[591,4337],{"alt":68,"src":4338},"/images/2025-08-26-xponology-on-old-pc/images-04e06b92-7e9c-498a-a82b-3d35afd09afa.jpg",[15,4340,4341],{},"ตรงนี้ก็ตั้ง user/password ได้เลย บาง step ผมจะข้าม ๆ ไปนะ ใช้ sense ได้เลยครับ",[15,4343,4344],{},[591,4345],{"alt":68,"src":4346},"/images/2025-08-26-xponology-on-old-pc/images-af6b4954-f9d3-4073-896d-35efcbf2acff.jpg",[15,4348,4349],{},"ต่อมาจะเป็นการตั้งค่า storage นะ",[15,4351,4352],{},[591,4353],{"alt":68,"src":4354},"/images/2025-08-26-xponology-on-old-pc/images-41946753-83ef-4480-b1c7-394b639a4540.jpg",[15,4356,4357],{},"โดยผมจะเลือกเป็น RAID 1 คือ 2 ลูก mirror กัน",[15,4359,4360],{},[591,4361],{"alt":68,"src":4362},"/images/2025-08-26-xponology-on-old-pc/images-a7392138-9df9-499e-8feb-3f4c0febc9f9.jpg",[15,4364,4365],{},[591,4366],{"alt":68,"src":4367},"/images/2025-08-26-xponology-on-old-pc/images-a7b843e5-fdf7-4fc6-905f-f385b3e7ca26.jpg",[15,4369,4370],{},"ขั้นตอนนี้จะเป็นการตรวจสอบสภาพของ HDD แนะนำให้ทำก่อนก็ดีครับ จะได้มั่นใจว่าข้อมูลจะไม่หาย",[15,4372,4373],{},[591,4374],{"alt":68,"src":4375},"/images/2025-08-26-xponology-on-old-pc/images-2df346bb-85da-430e-8746-fd2fe051f056.jpg",[15,4377,4378],{},"โดยความจุของ RAID 1 จะเป็นไปตามขนาดของ Disk ที่เล็กที่สุด ให้กดปุ่ม Max เพื่อตั้งค่าได้เลย",[15,4380,4381],{},[591,4382],{"alt":68,"src":4383},"/images/2025-08-26-xponology-on-old-pc/images-31528bb1-07aa-4938-919f-b54efcc45b23.jpg",[15,4385,4386],{},[591,4387],{"alt":68,"src":4388},"/images/2025-08-26-xponology-on-old-pc/images-f7f0e1e1-2a27-4835-91a5-3460b11d78fb.jpg",[15,4390,4391],{},"ตรงนี้แนะนำให้เปิด Disk Encryption ด้วยครับ",[15,4393,4394],{},[591,4395],{"alt":68,"src":4396},"/images/2025-08-26-xponology-on-old-pc/images-b5ee381d-566a-4e8c-9295-056fbd02ebe7.jpg",[15,4398,4399],{},"แล้วรอนั่งรอตัวระบบทำการตรวจสอบ Health ของ Disk ครับ",[15,4401,4402],{},[591,4403],{"alt":68,"src":4404},"/images/2025-08-26-xponology-on-old-pc/images-c1934bff-cdb7-47de-89ea-cfa4e381613b.jpg",[15,4406,4407],{},"เรียบร้อยแล้วครับ เพียงเท่านี้เราก็จะมี Synology ใช้บน Hardware ที่เราเลือกเองได้ ผิดถูกตรงไหนขออภัยด้วยครับ หวังว่าจะพอเป็นแนวทางให้ได้ หากต้องการใช้งานจริงจังแนะนำอ่าน Docs ของทาง Xpenology เพิ่มเติมจะได้ข้อมูลที่แม่นยำมากยิ่งขึ้นฮะ",[15,4409,4410],{},[591,4411],{"alt":68,"src":4412},"/images/2025-08-26-xponology-on-old-pc/images-12cdc78c-2d2e-4b00-9fac-a475d6b2404a.jpg",[10,4414,1825],{"id":1824},[417,4416,4417,4423],{},[420,4418,4419],{},[1831,4420,4421],{"href":4421,"rel":4422},"https://github.com/AuxXxilium/arc",[1835],[420,4424,4425],{},[1831,4426,4427],{"href":4427,"rel":4428},"https://xpenology.org/",[1835],{"title":68,"searchDepth":128,"depth":128,"links":4430},[4431,4432,4433,4434,4435,4436],{"id":4012,"depth":128,"text":4013},{"id":4025,"depth":128,"text":4026},{"id":4108,"depth":128,"text":4108},{"id":4156,"depth":128,"text":4157},{"id":4288,"depth":128,"text":4289},{"id":1824,"depth":128,"text":1825},"2025-08-26T00:00:00.000Z","วิธีลง Xpenology เพื่อให้ Synology DSM รันบน PC เครื่องเก่า กลายเป็น NAS ไว้ใช้เองที่บ้าน",{},"How To Setup Xpenology","/blogs/2025-08-26-xponology-on-old-pc",{"title":4007,"description":4438},{"loc":4441},"blogs/2025-08-26-xponology-on-old-pc",[1896],"eUiaA4iK3ITfQ4vjCMiMaj8zw7Xwx63qfeD0sIUmigE",{"id":4448,"title":4449,"body":4450,"date":4551,"description":4552,"draft":1884,"extension":1885,"meta":4553,"navigation":131,"next":1887,"ogTitle":4554,"path":4555,"pin":1884,"prev":1887,"recommends":4556,"seo":4557,"sitemap":4558,"stem":4559,"tags":4560,"updated":1887,"__hash__":4561},"content/blogs/2025-09-19-chuwi-minibook-x.md","Chuwi Minibook X Linux แบบพกพาง่าย",{"type":7,"value":4451,"toc":4546},[4452,4455,4460,4463,4467,4470,4481,4484,4487,4490,4495,4500,4514,4519,4530,4537,4540,4543],[15,4453,4454],{},"เบื่อไหมกับการที่ต้องพก Notebook ขนาดใหญ่ไปไหนมาไหนแล้วมันไม่สะดวก หรือจะเป็นการที่อยากได้ Notebook ขนาดเล็ก ๆ เอาไว้พกพาไปใช้งาน (ขำ ๆ) วันนี้ผมเจอ Notebook รุ่นนึงมานำเสนอครับ นั่นก็คืออออ \"Chuwi minibook X\" นั่นเอง",[15,4456,4457],{},[591,4458],{"alt":68,"src":4459},"/images/2025-09-19-chuwi-minibook-x/preview.jpg",[15,4461,4462],{},"เอาจริง ๆ ผมเองก็เพิ่งเคยได้ยินชื่อแบรนนี้เมื่อไม่กี่วันก่อนมาจากใน Reddit ครับ ด้วยความที่กำลังมองหา Notebook ขนาดเล็ก เอามาลง Linux เพื่อใช้งานตอนที่ไม่ได้จริงจังมาก ก็เลยได้ยินคนในกลุ่มแนะนำรุ่นนี้มา เลยลองมาหาข้อมูลดูก็พบว่ามีขายในไทยด้วย !!",[10,4464,4466],{"id":4465},"spec","Spec",[15,4468,4469],{},"สิ่งที่ทำให้ผมตัดสินใจซื้อคือตัว CPU ครับ ส่วนตัวผมเองชอบ CPU ตระกูล N ของ Intel อยู่แล้ว เช่น N95, N100, N150 ซึ่งเป็น CPU ที่ประหยัดพลังงาน แต่ก็ยังคงประสิทธิภาพไว้อยู่ (ผมมี Mini PC ที่เป็น N100 รันอยู่ปีกว่าแล้วครับนิ่ง ๆ เลย) โดย Notebook รุ่นนี้ที่ผมได้มาเป็น Spec",[417,4471,4472,4475,4478],{},[420,4473,4474],{},"CPU: N150",[420,4476,4477],{},"Ram: 16GB",[420,4479,4480],{},"SSD: 512GB",[15,4482,4483],{},"จริง ๆ Spec ลองไปดูใน website ก็ได้นะครับ ในส่วนอื่น ๆ อีกอย่างที่ผมชอบคือขนาดเพียง 10\" นิด ๆ แต่มี Keyboard + Trackpad มาให้ด้วย รุ่นนี้เป็นจอ Touch Screen พลิกกลับไปเป็น Tablet ได้ด้วยนะะ",[10,4485,4486],{"id":4486},"รีวิวส่วนตัว",[15,4488,4489],{},"จากที่ผมลองใช้งานมานะครับ ผมขอสรุปเป็นข้อดีข้อเสียดังนี้",[15,4491,4492],{},[591,4493],{"alt":68,"src":4494},"/images/2025-09-19-chuwi-minibook-x/btop.jpg",[15,4496,4497],{},[27,4498,4499],{},"ข้อดี",[417,4501,4502,4505,4508,4511],{},[420,4503,4504],{},"ขนาดเล็ก พกพาง่าย",[420,4506,4507],{},"CPU N150 ทำงานได้ค่อนข้างดี เหมาะกับงานที่ไม่ได้หนักมาก",[420,4509,4510],{},"มี Keyboard และ Trackpad ซึ่งความรู้สึกในการใช้งานคือดีเลย ไม่ก๊องแก๊ง",[420,4512,4513],{},"Port การเชื่อมต่อ Type-C ชาร์จได้ ต่อจอได้ (ผมลองเสียบกับจอ Dell ของผมที่เป็น Type C แบบ one-wire ใช้งานได้ปกติ)",[15,4515,4516],{},[27,4517,4518],{},"ข้อเสีย",[417,4520,4521,4524,4527],{},[420,4522,4523],{},"นิ้วเบียด พิมพ์ผิดบ่อย ด้วยความที่ keyboard ขนาดเล็กครับ ฝั่งซ้ายมันจะเล็ก (zone: Tab, Capslock, Shift) เลยทำให้จังหวะแรกที่พิมพ์ผมมักพิมพ์ผิดบ่อย ๆ",[420,4525,4526],{},"เครื่องร้อนง่าย (แต่พอเปลี่ยนซิริโคลนแล้วดีขึ้นนะครับ)",[420,4528,4529],{},"UI ตอน Boot จะบัค ถ้าเอามาลง Linux",[15,4531,4532,4533],{},"แต่โดยรวม ๆ ผมให้ 8/10 ครับ ตอนที่ตัดสินใจซื้อมาจริง ๆ แล้วก็ไม่ได้คาดหวังว่ามันจะออกมา perfect หรอกครับ แต่แค่นี้ผมก็รู้สึกไม่ผิดหวังแล้ว ราคาใน Shopee ปกติจะอยู่ที่ประมาณ 12XXX บาทครับ แต่ผมได้ช่วง Flash Sell + 9.9 มาเหลือประมาณ 89XX บาท (ถูกมากกก) ผมกดจากอันนี้ฮะ ",[1831,4534,4535],{"href":4535,"rel":4536},"https://s.shopee.co.th/qaEfmoOmu",[1835],[10,4538,4539],{"id":4539},"สุดท้าย",[15,4541,4542],{},"ความตั้งใจของผมคือเอาพกติดกระเป๋าเอาไว้ใช้งานตอนออกไปข้างนอก เผื่อต้อง remote ไปทำอะไรที่มันด่วน ๆ จะได้ทำได้เลย เพราะเครื่องนี้น้ำหนักประมาณ 1KG ครับ เบามาก ก่อนหน้านี้เคยลองซื้อ IPAD Mini 7 + Keyboard Case มา แต่ก็ไม่ไหวพกพาไม่สะดวกเลยครับ พอมาเจอตัวนี้ ง่ายขึ้นเยอะะะะ",[15,4544,4545],{},"ตัดสินใจลงเป็น Linux Ubuntu เอาไว้ ต้องมานั่ง Custom พอสมควรเลยเช่น Tablet mode, Touch Screen ใช้เวลาพอสมควรเลย แต่ก็สนุกดีครับ หาอะไรลองเล่นไปเรื่อยยยย ~",{"title":68,"searchDepth":128,"depth":128,"links":4547},[4548,4549,4550],{"id":4465,"depth":128,"text":4466},{"id":4486,"depth":128,"text":4486},{"id":4539,"depth":128,"text":4539},"2025-09-19T00:00:00.000Z","รีวิว Notebook พกพาขนาดเล็กกก เอามาเป็นเครื่องไว้ใช้งานสำหรับ Remote โดยการติดตั้ง Ubuntu Desktop",{},"Chuwi Miniboox X","/blogs/2025-09-19-chuwi-minibook-x",[2134,2133],{"title":4449,"description":4552},{"loc":4555},"blogs/2025-09-19-chuwi-minibook-x",[1896],"u_pP94b9jrUXYQ3SDuMCCkmg6epEOhOmgIeQo-V3mwM",{"id":4563,"title":4564,"body":4565,"date":4641,"description":4642,"draft":1884,"extension":1885,"meta":4643,"navigation":131,"next":1887,"ogTitle":4644,"path":4645,"pin":1884,"prev":1887,"recommends":1887,"seo":4646,"sitemap":4647,"stem":4648,"tags":4649,"updated":1887,"__hash__":4650},"content/blogs/2025-10-30-3d-printer-bambu-lab-a1.md","มือใหม่หัดเล่น 3D Printer (bambu lab a1) ปริ้นอะไรดี ?",{"type":7,"value":4566,"toc":4631},[4567,4571,4575,4583,4586,4590,4598,4601,4604,4612,4615,4619,4623],[10,4568,4570],{"id":4569},"แนะนำของที่ต้องมี-ด้วยการซื้อ","แนะนำของที่ต้องมี (ด้วยการซื้อ)",[10,4572,4574],{"id":4573},"_3d-printer-bambu-lab-a1","3D Printer Bambu Lab A1",[417,4576,4577],{},[420,4578,4579],{},[1831,4580,4581],{"href":4581,"rel":4582},"https://s.shopee.co.th/6psUuYdq3G",[1835],[15,4584,4585],{},"แนะนำตัวนี้เพราะว่า Plate มีขนาดถึง 26x26x26 เป็นขนาดที่พอเหมาะ ไม่เล็กและไม่ใหญ่จนเกินไป (ถ้าไปตัวเล็กกว่านี้จะพิมพ์อะไรได้น้อยกว่า)",[2155,4587,4589],{"id":4588},"เส้น-3d-esun-pla","เส้น 3D ESun PLA",[417,4591,4592],{},[420,4593,4594],{},[1831,4595,4596],{"href":4596,"rel":4597},"https://s.shopee.co.th/2g2vwxaUCS",[1835],[15,4599,4600],{},"ลองเปลี่ยนมาหลายรุ่นแล้ว สุดท้ายก็มาจบที่ตัวนี้ครับ (มี profile บน bambu lab ด้วยนะเอออ)",[2155,4602,4603],{"id":4603},"หัวพิมพ์สำรอง",[417,4605,4606],{},[420,4607,4608],{},[1831,4609,4610],{"href":4610,"rel":4611},"https://s.shopee.co.th/gHrZLuAGC",[1835],[15,4613,4614],{},"อันนี้ผมซื้อเอาไว้สำรองครับ เผื่อหัวหลักมีปัญหา เลยลองเอารุ่นทดแทนมาใช้ (ไม่ใช่ของแท้)",[10,4616,4618],{"id":4617},"ป้ายยา-model-3d-ที่เคยพิมพ์มา","ป้ายยา Model 3D ที่เคยพิมพ์มา",[2155,4620,4622],{"id":4621},"นกฮูกสำหรับวางแว่นสุด-cute","นกฮูกสำหรับวางแว่นสุด Cute",[417,4624,4625],{},[420,4626,4627],{},[1831,4628,4629],{"href":4629,"rel":4630},"https://makerworld.com/en/models/580423-owl-glasses-holder",[1835],{"title":68,"searchDepth":128,"depth":128,"links":4632},[4633,4634,4638],{"id":4569,"depth":128,"text":4570},{"id":4573,"depth":128,"text":4574,"children":4635},[4636,4637],{"id":4588,"depth":135,"text":4589},{"id":4603,"depth":135,"text":4603},{"id":4617,"depth":128,"text":4618,"children":4639},[4640],{"id":4621,"depth":135,"text":4622},"2025-10-30T00:00:00.000Z","รวม item ที่ควรมีและ model ที่เคยพิมพ์จริงจากการเล่น 3D Printer Bambu Lab A1",{},"Bambu Lab A1","/blogs/2025-10-30-3d-printer-bambu-lab-a1",{"title":4564,"description":4642},{"loc":4645},"blogs/2025-10-30-3d-printer-bambu-lab-a1",[1896],"-U1ObIEXRmu5LJReIqk17ZUkWHOQom57hwXN9p4O5XE",{"id":4652,"title":4653,"body":4654,"date":4731,"description":4732,"draft":1884,"extension":1885,"meta":4733,"navigation":131,"next":1887,"ogTitle":4734,"path":4735,"pin":1884,"prev":1887,"recommends":1887,"seo":4736,"sitemap":4737,"stem":4738,"tags":4739,"updated":3426,"__hash__":4740},"content/blogs/2025-11-09-nanokvm-kvm.md","รีวิว nanokvm - kvm ขนาดเล็กพกพาง่าย",{"type":7,"value":4655,"toc":4724},[4656,4663,4668,4672,4675,4678,4681,4684,4687,4690,4693,4698,4703,4706,4709,4714,4716],[15,4657,4658,4659],{},"แวะมาป้ายยา item สำหรับสาย homelab ที่มีชื่อว่า nanovkm usb ",[1831,4660,4661],{"href":4661,"rel":4662},"https://wiki.sipeed.com/hardware/en/kvm/NanoKVM_USB/introduction.html",[1835],[15,4664,4665],{},[591,4666],{"alt":68,"src":4667},"/images/2025-11-09-nanokvm-kvm/nano_kvm_01.jpg",[10,4669,4671],{"id":4670},"nanokvm-usb-คืออะไร","NanoKVM USB คืออะไร",[15,4673,4674],{},"โดยความสามารถของอุปกรณ์ตัวนี้คือการเชื่อมสามารถใช้ type C 1 เส้น ในการแสดงผลภาพและส่ง mouse keyboard ของ macbook ไปยังเครื่องปลายทางได้ครับ",[10,4676,4677],{"id":4677},"วิธีการต่อ",[15,4679,4680],{},"ฝั่ง Host: เสียบ type C 1 เส้นเข้ากับอุปกรณ์ เช่น notebook, macbook ได้เลย",[15,4682,4683],{},"ฝั่ง Target: เสียบ HDMI, USB เพื่อส่งภาพและ virtual mouse keyboard ไปยังเครื่องปลายทาง",[10,4685,4686],{"id":4686},"ราคาและการสั่งซื้อ",[15,4688,4689],{},"โดยอุปกรณ์นี้ผมได้มาในราคาประมาณ 1200 บาท สามารถสั่งเว็บตรงของทางผู้ผลิตได้เลย ใช้เวลาจัดส่งประมาณ 2-3 สัปดาห์ครับ หลังจากได้ที่มาชอบมากกก เพราะทำให้ตอนที่ต้อง connect minipc, server, raspberry pi ไม่จำเป็นต้องเสียบหน้าจออีกต่อไป สามารถใช้ macbook ควบคุมได้เลย",[15,4691,4692],{},"จริง ๆ ยังมีอีกหลายรุ่นนะครับ แต่ผมกดรุ่นนี้มาเพราะรูปทรง + features เหมาะกับการใช้งานของผมพอดี",[15,4694,4695],{},[591,4696],{"alt":68,"src":4697},"/images/2025-11-09-nanokvm-kvm/nano_kvm_02.jpg",[15,4699,4700],{},[591,4701],{"alt":68,"src":4702},"/images/2025-11-09-nanokvm-kvm/nano_kvm_03.jpg",[10,4704,4705],{"id":4705},"หน้าตาตอนใช้งาน",[15,4707,4708],{},"สามารถใช้งานผ่าน web browser ได้เลย",[15,4710,4711],{},[591,4712],{"alt":68,"src":4713},"/images/2025-11-09-nanokvm-kvm/nano_kvm_web.jpg",[10,4715,3393],{"id":3392},[417,4717,4718],{},[420,4719,4720],{},[1831,4721,4723],{"href":4661,"rel":4722},[1835],"NanoKVM USB - Sipeed Wiki",{"title":68,"searchDepth":128,"depth":128,"links":4725},[4726,4727,4728,4729,4730],{"id":4670,"depth":128,"text":4671},{"id":4677,"depth":128,"text":4677},{"id":4686,"depth":128,"text":4686},{"id":4705,"depth":128,"text":4705},{"id":3392,"depth":128,"text":3393},"2025-11-09T00:00:00.000Z","รีวิว NanoKVM USB ตัวเล็ก ๆ ที่ใช้ type C เส้นเดียวคุม minipc, server, raspberry pi ได้โดยไม่ต้องเสียบจอ",{},"NanoKVM","/blogs/2025-11-09-nanokvm-kvm",{"title":4653,"description":4732},{"loc":4735},"blogs/2025-11-09-nanokvm-kvm",[1896],"-I5iAm_Q9XVMxcXqJEaUp8kve-C_oSv0cDLP-yqPn08",{"id":4742,"title":4743,"body":4744,"date":6433,"description":6434,"draft":1884,"extension":1885,"meta":6435,"navigation":131,"next":1887,"ogTitle":6436,"path":6437,"pin":1884,"prev":1887,"recommends":6438,"seo":6440,"sitemap":6441,"stem":6442,"tags":6443,"updated":1887,"__hash__":6444},"content/blogs/2026-08-07-node-exporter-mtls-basic-auth.md","ตั้งค่า node_exporter ให้ปลอดภัยด้วย mTLS และ Basic auth",{"type":7,"value":4745,"toc":6417},[4746,4748,4751,4768,4774,4787,4791,4794,4832,4835,4877,4883,4886,4926,4932,4935,4938,4954,4958,4961,4968,4974,4977,4983,4988,4995,5001,5005,5008,5281,5287,5309,5315,5319,5331,5362,5365,5371,5382,5386,5396,5462,5480,5491,5494,5497,5551,5554,5557,5562,5565,5588,5591,5607,5613,5616,5630,5636,5639,5658,5664,5671,5679,5682,5716,5722,5725,5787,5793,5797,5800,5947,5954,5960,5967,5982,5989,6021,6027,6034,6075,6081,6088,6092,6098,6150,6156,6159,6165,6168,6175,6179,6182,6188,6221,6227,6243,6264,6267,6287,6301,6311,6313,6315,6375,6377,6383,6386,6389,6391,6414],[10,4747,13],{"id":12},[15,4749,4750],{},"สวัสดีครับ วันนี้มาแชร์วิธีทำให้ node_exporter ปลอดภัยขึ้นกัน ด้วยการเปิด Basic auth กับ mTLS ครับ เพราะโดยปกติแล้ว Port ดังกล่าวจะสามารถดึงข้อมูลได้ทันที",[15,4752,4753,4754,4757,4758,4763,4764,4767],{},"ระหว่างทางผมไปเจอเรื่องที่ทำให้งงอยู่พักนึงด้วย คือไฟล์ ",[19,4755,4756],{},"web-config.yml"," ที่ปกติเราเขียนให้ Prometheus (",[1831,4759,4762],{"href":4760,"rel":4761},"https://prometheus.io/docs/guides/basic-auth/",[1835],"Securing Prometheus API and UI endpoints using basic auth",") ",[27,4765,4766],{},"เอามาใช้กับ node_exporter ได้เลย"," ทั้งที่ผมเข้าใจมาตลอดว่ามันเป็น config ของ Prometheus อย่างเดียว",[15,4769,4770,4771,4773],{},"บทความนี้ผมรันด้วย docker ทั้งหมดนะครับ เพราะลองแล้วลบทิ้งได้ง่ายดี output ทุกก้อนก๊อปมาจาก terminal ตรง ๆ (มีแก้แค่ชื่อเครื่องกับ arch ให้เป็นค่าทั่วไปนะ) เวอร์ชันที่ใช้คือ node_exporter 1.12.1 กับ Prometheus 3.13.2 (ใครรันแบบ bare metal อยู่แล้วก็อ่านข้ามส่วน ",[19,4772,396],{}," ไปได้ ตัว config เหมือนกันหมด เดี๋ยวมีบอกท้ายบทความว่าแก้ systemd ตรงไหน)",[15,4775,4776,4777,4779,4780,429,4783,4786],{},"ของที่ต้องมีคือ docker กับ ",[19,4778,39],{}," ส่วน ",[19,4781,4782],{},"htpasswd",[19,4784,4785],{},"promtool"," เดี๋ยวเรียกผ่าน docker เอา ไม่ต้องลงเพิ่ม",[10,4788,4790],{"id":4789},"ปัญหาของพอร์ต-9100","ปัญหาของพอร์ต 9100",[15,4792,4793],{},"ลองยกตัวที่ไม่ได้ตั้งอะไรไว้เลยขึ้นมาดูก่อนครับ",[63,4795,4797],{"className":65,"code":4796,"language":67,"meta":68,"style":68},"docker network create mon-net\ndocker run -d --name ne-open --network mon-net quay.io/prometheus/node-exporter:latest\n",[19,4798,4799,4810],{"__ignoreMap":68},[72,4800,4801,4803,4805,4807],{"class":74,"line":75},[72,4802,79],{"class":78},[72,4804,119],{"class":82},[72,4806,122],{"class":82},[72,4808,4809],{"class":82}," mon-net\n",[72,4811,4812,4814,4816,4818,4820,4823,4826,4829],{"class":74,"line":128},[72,4813,79],{"class":78},[72,4815,83],{"class":82},[72,4817,142],{"class":86},[72,4819,145],{"class":86},[72,4821,4822],{"class":82}," ne-open",[72,4824,4825],{"class":86}," --network",[72,4827,4828],{"class":82}," mon-net",[72,4830,4831],{"class":82}," quay.io/prometheus/node-exporter:latest\n",[15,4833,4834],{},"แล้วยิงเข้าไปเฉย ๆ โดยไม่ต้องมี credential",[63,4836,4838],{"className":65,"code":4837,"language":67,"meta":68,"style":68},"docker run --rm --network mon-net curlimages/curl:latest \\\n  -s http://ne-open:9100/metrics | grep '^node_uname_info'\n",[19,4839,4840,4857],{"__ignoreMap":68},[72,4841,4842,4844,4846,4848,4850,4852,4855],{"class":74,"line":75},[72,4843,79],{"class":78},[72,4845,83],{"class":82},[72,4847,87],{"class":86},[72,4849,4825],{"class":86},[72,4851,4828],{"class":82},[72,4853,4854],{"class":82}," curlimages/curl:latest",[72,4856,152],{"class":151},[72,4858,4859,4862,4865,4867,4870,4872,4875],{"class":74,"line":128},[72,4860,4861],{"class":86},"  -s",[72,4863,4864],{"class":82}," http://ne-open:9100/metrics",[72,4866,1168],{"class":248},[72,4868,4869],{"class":78}," grep",[72,4871,239],{"class":238},[72,4873,4874],{"class":82},"^node_uname_info",[72,4876,894],{"class":238},[63,4878,4881],{"className":4879,"code":4880,"language":99},[97],"node_uname_info{domainname=\"(none)\",machine=\"x86_64\",nodename=\"homelab-01\",sysname=\"Linux\",version=\"#1 SMP PREEMPT Fri Jul 31 00:11:41 UTC 2026\"} 1\n",[19,4882,4880],{"__ignoreMap":68},[15,4884,4885],{},"ได้ชื่อเครื่อง สถาปัตยกรรม และเวอร์ชัน kernel มาครบ ลองนับทั้งหมดดู",[63,4887,4889],{"className":65,"code":4888,"language":67,"meta":68,"style":68},"docker run --rm --network mon-net curlimages/curl:latest \\\n  -s http://ne-open:9100/metrics | grep -c '^node_'\n",[19,4890,4891,4907],{"__ignoreMap":68},[72,4892,4893,4895,4897,4899,4901,4903,4905],{"class":74,"line":75},[72,4894,79],{"class":78},[72,4896,83],{"class":82},[72,4898,87],{"class":86},[72,4900,4825],{"class":86},[72,4902,4828],{"class":82},[72,4904,4854],{"class":82},[72,4906,152],{"class":151},[72,4908,4909,4911,4913,4915,4917,4919,4921,4924],{"class":74,"line":128},[72,4910,4861],{"class":86},[72,4912,4864],{"class":82},[72,4914,1168],{"class":248},[72,4916,4869],{"class":78},[72,4918,2562],{"class":86},[72,4920,239],{"class":238},[72,4922,4923],{"class":82},"^node_",[72,4925,894],{"class":238},[63,4927,4930],{"className":4928,"code":4929,"language":99},[97],"906\n",[19,4931,4929],{"__ignoreMap":68},[15,4933,4934],{},"906 บรรทัดครับ มีทั้ง mount point ที่มีอยู่ พื้นที่ดิสก์ที่เหลือ interface ที่ต่ออยู่ แล้วก็เวอร์ชัน kernel ที่บอกกลาย ๆ ว่าเครื่องนี้แพตช์ล่าสุดเมื่อไหร่ ข้อมูลพวกนี้อย่างเดียวไม่ได้ทำให้ใครเข้าเครื่องได้ แต่มันก็นับว่าเป็นข้อมูลที่สำคัญในการนำไปหาข้อมูลต่อได้",[15,4936,4937],{},"ลบตัวทดลองทิ้งก่อนไปต่อ",[63,4939,4941],{"className":65,"code":4940,"language":67,"meta":68,"style":68},"docker rm -f ne-open\n",[19,4942,4943],{"__ignoreMap":68},[72,4944,4945,4947,4949,4951],{"class":74,"line":75},[72,4946,79],{"class":78},[72,4948,1777],{"class":82},[72,4950,1780],{"class":86},[72,4952,4953],{"class":82}," ne-open\n",[10,4955,4957],{"id":4956},"config-ที่ใช้ร่วมกัน","config ที่ใช้ร่วมกัน",[15,4959,4960],{},"ทีนี้มาถึงเรื่องที่ผมเข้าใจผิดมาตลอดครับ",[15,4962,4963,4964,4967],{},"พอเปิด ",[19,4965,4966],{},"--help"," ของ node_exporter ดู มันชี้ไปที่ repo ของ exporter-toolkit ตรง ๆ เลย",[63,4969,4972],{"className":4970,"code":4971,"language":99},[97],"--web.config.file=\"\"       Path to configuration file that can\n                           enable TLS or authentication. See:\n                           https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md\n",[19,4973,4971],{"__ignoreMap":68},[15,4975,4976],{},"ในเอกสารตัวนั้นก็เขียนไว้ตรง ๆ ว่า \"Exporters and services instrumented with the Exporter Toolkit share the same web configuration file format.\"",[15,4978,4979,4980],{},"แปลว่า Prometheus, node_exporter, Alertmanager และ exporter ตัวอื่น ๆ ในตระกูลนี้ใช้ library ตัวเดียวกันจัดการเรื่อง TLS กับ auth ไฟล์ config เลยหน้าตาเหมือนกันหมด flag ที่ใช้เปิดก็ชื่อเดียวกันคือ ",[19,4981,4982],{},"--web.config.file",[15,4984,4985],{},[591,4986],{"alt":68,"src":4987},"/images/2026-08-07-node-exporter-mtls-basic-auth/shared-config.png",[15,4989,4990,4991,4994],{},"ตอนรัน node_exporter จะเห็นใน log ด้วยว่ามันเรียก ",[19,4992,4993],{},"tls_config.go"," ซึ่งเป็นไฟล์ของ exporter-toolkit ไม่ใช่โค้ดของ node_exporter เอง",[63,4996,4999],{"className":4997,"code":4998,"language":99},[97],"level=INFO source=tls_config.go:415 msg=\"TLS is enabled.\" http2=true address=[::]:9100\n",[19,5000,4998],{"__ignoreMap":68},[10,5002,5004],{"id":5003},"สร้าง-cert","สร้าง cert",[15,5006,5007],{},"mTLS แปลว่าทั้งสองฝั่งต้องยืนยันตัวตนกัน ไม่ใช่แค่ฝั่ง server เพราะฉะนั้นต้องมี CA ของตัวเองไว้เซ็นให้ทั้งคู่",[63,5009,5011],{"className":65,"code":5010,"language":67,"meta":68,"style":68},"mkdir -p ~/node-exporter/certs && cd ~/node-exporter/certs\n\n# CA ของเราเอง\nopenssl req -x509 -newkey rsa:2048 -nodes -days 365 \\\n  -keyout ca.key -out ca.crt -subj \"/CN=homelab-ca\"\n\n# cert ฝั่ง node_exporter\nopenssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr \\\n  -subj \"/CN=node-exporter\"\nprintf \"subjectAltName=DNS:node-exporter,DNS:localhost,IP:127.0.0.1\" > san.ext\nopenssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \\\n  -out server.crt -days 365 -extfile san.ext\n\n# cert ฝั่ง Prometheus\nopenssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr \\\n  -subj \"/CN=prometheus\"\nopenssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \\\n  -out client.crt -days 365\n",[19,5012,5013,5030,5034,5039,5067,5091,5095,5100,5125,5137,5154,5184,5201,5205,5210,5234,5245,5269],{"__ignoreMap":68},[72,5014,5015,5017,5019,5022,5024,5027],{"class":74,"line":75},[72,5016,223],{"class":78},[72,5018,226],{"class":86},[72,5020,5021],{"class":82}," ~/node-exporter/certs",[72,5023,2176],{"class":642},[72,5025,5026],{"class":234}," cd",[72,5028,5029],{"class":82}," ~/node-exporter/certs\n",[72,5031,5032],{"class":74,"line":128},[72,5033,132],{"emptyLinePlaceholder":131},[72,5035,5036],{"class":74,"line":135},[72,5037,5038],{"class":1991},"# CA ของเราเอง\n",[72,5040,5041,5044,5047,5050,5053,5056,5059,5062,5065],{"class":74,"line":155},[72,5042,5043],{"class":78},"openssl",[72,5045,5046],{"class":82}," req",[72,5048,5049],{"class":86}," -x509",[72,5051,5052],{"class":86}," -newkey",[72,5054,5055],{"class":82}," rsa:2048",[72,5057,5058],{"class":86}," -nodes",[72,5060,5061],{"class":86}," -days",[72,5063,5064],{"class":2512}," 365",[72,5066,152],{"class":151},[72,5068,5069,5072,5075,5078,5081,5084,5086,5089],{"class":74,"line":166},[72,5070,5071],{"class":86},"  -keyout",[72,5073,5074],{"class":82}," ca.key",[72,5076,5077],{"class":86}," -out",[72,5079,5080],{"class":82}," ca.crt",[72,5082,5083],{"class":86}," -subj",[72,5085,654],{"class":238},[72,5087,5088],{"class":82},"/CN=homelab-ca",[72,5090,1679],{"class":238},[72,5092,5093],{"class":74,"line":177},[72,5094,132],{"emptyLinePlaceholder":131},[72,5096,5097],{"class":74,"line":313},[72,5098,5099],{"class":1991},"# cert ฝั่ง node_exporter\n",[72,5101,5102,5104,5106,5108,5110,5112,5115,5118,5120,5123],{"class":74,"line":319},[72,5103,5043],{"class":78},[72,5105,5046],{"class":82},[72,5107,5052],{"class":86},[72,5109,5055],{"class":82},[72,5111,5058],{"class":86},[72,5113,5114],{"class":86}," -keyout",[72,5116,5117],{"class":82}," server.key",[72,5119,5077],{"class":86},[72,5121,5122],{"class":82}," server.csr",[72,5124,152],{"class":151},[72,5126,5127,5130,5132,5135],{"class":74,"line":325},[72,5128,5129],{"class":86},"  -subj",[72,5131,654],{"class":238},[72,5133,5134],{"class":82},"/CN=node-exporter",[72,5136,1679],{"class":238},[72,5138,5139,5142,5144,5147,5149,5151],{"class":74,"line":330},[72,5140,5141],{"class":234},"printf",[72,5143,654],{"class":238},[72,5145,5146],{"class":82},"subjectAltName=DNS:node-exporter,DNS:localhost,IP:127.0.0.1",[72,5148,703],{"class":238},[72,5150,249],{"class":248},[72,5152,5153],{"class":82}," san.ext\n",[72,5155,5156,5158,5161,5164,5167,5169,5172,5174,5177,5179,5182],{"class":74,"line":336},[72,5157,5043],{"class":78},[72,5159,5160],{"class":82}," x509",[72,5162,5163],{"class":86}," -req",[72,5165,5166],{"class":86}," -in",[72,5168,5122],{"class":82},[72,5170,5171],{"class":86}," -CA",[72,5173,5080],{"class":82},[72,5175,5176],{"class":86}," -CAkey",[72,5178,5074],{"class":82},[72,5180,5181],{"class":86}," -CAcreateserial",[72,5183,152],{"class":151},[72,5185,5186,5189,5192,5194,5196,5199],{"class":74,"line":342},[72,5187,5188],{"class":86},"  -out",[72,5190,5191],{"class":82}," server.crt",[72,5193,5061],{"class":86},[72,5195,5064],{"class":2512},[72,5197,5198],{"class":86}," -extfile",[72,5200,5153],{"class":82},[72,5202,5203],{"class":74,"line":348},[72,5204,132],{"emptyLinePlaceholder":131},[72,5206,5207],{"class":74,"line":353},[72,5208,5209],{"class":1991},"# cert ฝั่ง Prometheus\n",[72,5211,5212,5214,5216,5218,5220,5222,5224,5227,5229,5232],{"class":74,"line":358},[72,5213,5043],{"class":78},[72,5215,5046],{"class":82},[72,5217,5052],{"class":86},[72,5219,5055],{"class":82},[72,5221,5058],{"class":86},[72,5223,5114],{"class":86},[72,5225,5226],{"class":82}," client.key",[72,5228,5077],{"class":86},[72,5230,5231],{"class":82}," client.csr",[72,5233,152],{"class":151},[72,5235,5236,5238,5240,5243],{"class":74,"line":364},[72,5237,5129],{"class":86},[72,5239,654],{"class":238},[72,5241,5242],{"class":82},"/CN=prometheus",[72,5244,1679],{"class":238},[72,5246,5247,5249,5251,5253,5255,5257,5259,5261,5263,5265,5267],{"class":74,"line":370},[72,5248,5043],{"class":78},[72,5250,5160],{"class":82},[72,5252,5163],{"class":86},[72,5254,5166],{"class":86},[72,5256,5231],{"class":82},[72,5258,5171],{"class":86},[72,5260,5080],{"class":82},[72,5262,5176],{"class":86},[72,5264,5074],{"class":82},[72,5266,5181],{"class":86},[72,5268,152],{"class":151},[72,5270,5271,5273,5276,5278],{"class":74,"line":376},[72,5272,5188],{"class":86},[72,5274,5275],{"class":82}," client.crt",[72,5277,5061],{"class":86},[72,5279,5280],{"class":2512}," 365\n",[15,5282,568,5283,5286],{},[19,5284,5285],{},"subjectAltName"," สำคัญมากครับ ต้องใส่ชื่อหรือ IP ที่ Prometheus จะใช้เรียกจริง ๆ ถ้าใส่ไม่ตรงจะ verify ไม่ผ่านตอน scrape เช็คได้ด้วย",[63,5288,5290],{"className":65,"code":5289,"language":67,"meta":68,"style":68},"openssl verify -CAfile ca.crt server.crt client.crt\n",[19,5291,5292],{"__ignoreMap":68},[72,5293,5294,5296,5299,5302,5304,5306],{"class":74,"line":75},[72,5295,5043],{"class":78},[72,5297,5298],{"class":82}," verify",[72,5300,5301],{"class":86}," -CAfile",[72,5303,5080],{"class":82},[72,5305,5191],{"class":82},[72,5307,5308],{"class":82}," client.crt\n",[63,5310,5313],{"className":5311,"code":5312,"language":99},[97],"server.crt: OK\nclient.crt: OK\n",[19,5314,5312],{"__ignoreMap":68},[10,5316,5318],{"id":5317},"เปิด-basic-auth","เปิด basic auth",[15,5320,5321,5322,5324,5325,5328,5329],{},"รหัสผ่านใน ",[19,5323,4756],{}," ต้องเป็น bcrypt hash ใส่เป็นข้อความเปล่าไม่ได้ (ถ้าใส่ไป node_exporter จะขึ้นมาแป๊บนึงแล้วดับ พร้อม log ว่า ",[19,5326,5327],{},"hashedSecret too short to be a bcrypted password",") สร้าง hash ด้วย ",[19,5330,4782],{},[63,5332,5334],{"className":65,"code":5333,"language":67,"meta":68,"style":68},"docker run --rm -it httpd:2.4-alpine htpasswd -nBC 12 prometheus\n",[19,5335,5336],{"__ignoreMap":68},[72,5337,5338,5340,5342,5344,5347,5350,5353,5356,5359],{"class":74,"line":75},[72,5339,79],{"class":78},[72,5341,83],{"class":82},[72,5343,87],{"class":86},[72,5345,5346],{"class":86}," -it",[72,5348,5349],{"class":82}," httpd:2.4-alpine",[72,5351,5352],{"class":82}," htpasswd",[72,5354,5355],{"class":86}," -nBC",[72,5357,5358],{"class":2512}," 12",[72,5360,5361],{"class":82}," prometheus\n",[15,5363,5364],{},"มันจะถามรหัสให้พิมพ์สองรอบ แล้วพ่นออกมาแบบนี้",[63,5366,5369],{"className":5367,"code":5368,"language":99},[97],"prometheus:$2y$12$\u003Chash 53 ตัวอักษร>\n",[19,5370,5368],{"__ignoreMap":68},[15,5372,5373,5374,5377,5378,5381],{},"ที่ไม่ใช้ ",[19,5375,5376],{},"-b"," แล้วพิมพ์รหัสต่อท้ายคำสั่งไปเลย เพราะรหัสจะไปค้างใน shell history แล้วก็โผล่ใน ",[19,5379,5380],{},"ps"," ด้วยครับ",[10,5383,5385],{"id":5384},"เปิด-mtls","เปิด mTLS",[15,5387,5388,5389,5392,5393],{},"เอา hash ที่ได้มาใส่ แล้วเติม ",[19,5390,5391],{},"tls_server_config"," เข้าไปในไฟล์เดียวกัน สร้างที่ ",[19,5394,5395],{},"~/node-exporter/web-config.yml",[63,5397,5399],{"className":65,"code":5398,"language":67,"meta":68,"style":68},"cat > ~/node-exporter/web-config.yml \u003C\u003C'EOF'\ntls_server_config:\n  cert_file: /etc/node_exporter/certs/server.crt\n  key_file: /etc/node_exporter/certs/server.key\n  client_auth_type: RequireAndVerifyClientCert\n  client_ca_file: /etc/node_exporter/certs/ca.crt\n  min_version: TLS12\n\nbasic_auth_users:\n  prometheus: $2y$12$\u003Chash ที่ได้จากขั้นตอนที่แล้ว>\nEOF\n",[19,5400,5401,5414,5419,5424,5429,5434,5439,5444,5448,5453,5458],{"__ignoreMap":68},[72,5402,5403,5405,5407,5410,5412],{"class":74,"line":75},[72,5404,273],{"class":78},[72,5406,249],{"class":248},[72,5408,5409],{"class":82}," ~/node-exporter/web-config.yml",[72,5411,281],{"class":248},[72,5413,285],{"class":284},[72,5415,5416],{"class":74,"line":128},[72,5417,5418],{"class":82},"tls_server_config:\n",[72,5420,5421],{"class":74,"line":135},[72,5422,5423],{"class":82},"  cert_file: /etc/node_exporter/certs/server.crt\n",[72,5425,5426],{"class":74,"line":155},[72,5427,5428],{"class":82},"  key_file: /etc/node_exporter/certs/server.key\n",[72,5430,5431],{"class":74,"line":166},[72,5432,5433],{"class":82},"  client_auth_type: RequireAndVerifyClientCert\n",[72,5435,5436],{"class":74,"line":177},[72,5437,5438],{"class":82},"  client_ca_file: /etc/node_exporter/certs/ca.crt\n",[72,5440,5441],{"class":74,"line":313},[72,5442,5443],{"class":82},"  min_version: TLS12\n",[72,5445,5446],{"class":74,"line":319},[72,5447,132],{"emptyLinePlaceholder":131},[72,5449,5450],{"class":74,"line":325},[72,5451,5452],{"class":82},"basic_auth_users:\n",[72,5454,5455],{"class":74,"line":330},[72,5456,5457],{"class":82},"  prometheus: $2y$12$\u003Chash ที่ได้จากขั้นตอนที่แล้ว>\n",[72,5459,5460],{"class":74,"line":336},[72,5461,390],{"class":284},[15,5463,5464,5465,5468,5469,5471,5472,5475,5476,5479],{},"path ใน config เป็น path ",[27,5466,5467],{},"ข้างใน container"," นะครับ เดี๋ยวตอน ",[19,5470,396],{}," เรา mount ",[19,5473,5474],{},"~/node-exporter/certs"," ไปไว้ที่ ",[19,5477,5478],{},"/etc/node_exporter/certs"," ให้ตรงกัน ถ้ารันแบบ bare metal ก็เปลี่ยนเป็น path จริงบนเครื่องได้เลย",[15,5481,5482,5483,5486,5487,5490],{},"หัวใจอยู่ที่ ",[19,5484,5485],{},"client_auth_type: RequireAndVerifyClientCert"," ครับ อันนี้แหละที่ทำให้เป็น mTLS จริง ๆ คือ server จะบังคับให้ client ส่ง cert มาด้วย แล้วตรวจว่าเซ็นด้วย CA ใน ",[19,5488,5489],{},"client_ca_file"," หรือเปล่า",[15,5492,5493],{},"เอกสารของ exporter-toolkit เตือนไว้ตรง ๆ ว่า \"If you want to enable client authentication, you need to use RequireAndVerifyClientCert. Other values are insecure.\" อย่าใส่ค่าอื่นแม้ชื่อจะดูคล้ายกันนะครับ",[15,5495,5496],{},"รันได้แล้ว",[63,5498,5500],{"className":65,"code":5499,"language":67,"meta":68,"style":68},"docker run -d --name node-exporter --network mon-net \\\n  -v ~/node-exporter/web-config.yml:/etc/node_exporter/web-config.yml:ro \\\n  -v ~/node-exporter/certs:/etc/node_exporter/certs:ro \\\n  quay.io/prometheus/node-exporter:latest \\\n  --web.config.file=/etc/node_exporter/web-config.yml\n",[19,5501,5502,5521,5530,5539,5546],{"__ignoreMap":68},[72,5503,5504,5506,5508,5510,5512,5515,5517,5519],{"class":74,"line":75},[72,5505,79],{"class":78},[72,5507,83],{"class":82},[72,5509,142],{"class":86},[72,5511,145],{"class":86},[72,5513,5514],{"class":82}," node-exporter",[72,5516,4825],{"class":86},[72,5518,4828],{"class":82},[72,5520,152],{"class":151},[72,5522,5523,5525,5528],{"class":74,"line":128},[72,5524,522],{"class":86},[72,5526,5527],{"class":82}," ~/node-exporter/web-config.yml:/etc/node_exporter/web-config.yml:ro",[72,5529,152],{"class":151},[72,5531,5532,5534,5537],{"class":74,"line":135},[72,5533,522],{"class":86},[72,5535,5536],{"class":82}," ~/node-exporter/certs:/etc/node_exporter/certs:ro",[72,5538,152],{"class":151},[72,5540,5541,5544],{"class":74,"line":155},[72,5542,5543],{"class":82},"  quay.io/prometheus/node-exporter:latest",[72,5545,152],{"class":151},[72,5547,5548],{"class":74,"line":166},[72,5549,5550],{"class":86},"  --web.config.file=/etc/node_exporter/web-config.yml\n",[10,5552,5553],{"id":5553},"ทดสอบทีละชั้น",[15,5555,5556],{},"ตรงนี้สนุกครับ เพราะขาดชั้นไหนไป error ที่ได้ก็คนละแบบกัน ใช้ไล่หาสาเหตุได้เลย",[15,5558,5559],{},[591,5560],{"alt":68,"src":5561},"/images/2026-08-07-node-exporter-mtls-basic-auth/auth-layers.png",[15,5563,5564],{},"ผมตั้ง alias สั้น ๆ ไว้ก่อน จะได้ไม่ต้องพิมพ์ยาว",[63,5566,5568],{"className":65,"code":5567,"language":67,"meta":68,"style":68},"alias c='docker run --rm --network mon-net -v ~/node-exporter/certs:/c:ro curlimages/curl:latest'\n",[19,5569,5570],{"__ignoreMap":68},[72,5571,5572,5576,5579,5581,5583,5586],{"class":74,"line":75},[72,5573,5575],{"class":5574},"sOHk9","alias",[72,5577,5578],{"class":626}," c",[72,5580,2706],{"class":1567},[72,5582,245],{"class":238},[72,5584,5585],{"class":82},"docker run --rm --network mon-net -v ~/node-exporter/certs:/c:ro curlimages/curl:latest",[72,5587,894],{"class":238},[15,5589,5590],{},"เริ่มจากยิง http เปล่า ๆ แบบเดิม",[63,5592,5594],{"className":65,"code":5593,"language":67,"meta":68,"style":68},"c -sS http://node-exporter:9100/metrics\n",[19,5595,5596],{"__ignoreMap":68},[72,5597,5598,5601,5604],{"class":74,"line":75},[72,5599,5600],{"class":78},"c",[72,5602,5603],{"class":86}," -sS",[72,5605,5606],{"class":82}," http://node-exporter:9100/metrics\n",[63,5608,5611],{"className":5609,"code":5610,"language":99},[97],"Client sent an HTTP request to an HTTPS server.\n",[19,5612,5610],{"__ignoreMap":68},[15,5614,5615],{},"เปลี่ยนเป็น https แต่ยังไม่บอก CA",[63,5617,5619],{"className":65,"code":5618,"language":67,"meta":68,"style":68},"c -sS https://node-exporter:9100/metrics\n",[19,5620,5621],{"__ignoreMap":68},[72,5622,5623,5625,5627],{"class":74,"line":75},[72,5624,5600],{"class":78},[72,5626,5603],{"class":86},[72,5628,5629],{"class":82}," https://node-exporter:9100/metrics\n",[63,5631,5634],{"className":5632,"code":5633,"language":99},[97],"curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)\nMore details here: https://curl.se/docs/sslcerts.html\n",[19,5635,5633],{"__ignoreMap":68},[15,5637,5638],{},"ใส่ CA แล้วแต่ยังไม่มี client cert",[63,5640,5642],{"className":65,"code":5641,"language":67,"meta":68,"style":68},"c -sS --cacert /c/ca.crt https://node-exporter:9100/metrics\n",[19,5643,5644],{"__ignoreMap":68},[72,5645,5646,5648,5650,5653,5656],{"class":74,"line":75},[72,5647,5600],{"class":78},[72,5649,5603],{"class":86},[72,5651,5652],{"class":86}," --cacert",[72,5654,5655],{"class":82}," /c/ca.crt",[72,5657,5629],{"class":82},[63,5659,5662],{"className":5660,"code":5661,"language":99},[97],"curl: (56) OpenSSL SSL_read: OpenSSL/3.5.7: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0\n",[19,5663,5661],{"__ignoreMap":68},[15,5665,5666,5667,5670],{},"บรรทัด ",[19,5668,5669],{},"alert certificate required"," นี่แหละครับคือหลักฐานว่า mTLS ทำงานอยู่ ฝั่ง server ไม่ยอมคุยต่อเพราะเราไม่ได้ส่ง cert ไปให้",[2367,5672,5673],{},[15,5674,5675,5676,5678],{},"ข้อความพวกนี้มาจาก curl ที่ build กับ OpenSSL นะครับ ถ้ายิงจาก macOS ที่ curl ใช้ LibreSSL จะได้เลข exit code กับข้อความคนละแบบ และจะไม่มีคำว่า ",[19,5677,5669],{}," ให้เห็น ผมเลยยิงผ่าน container เอาเพื่อให้ทุกคนได้ผลเหมือนกัน",[15,5680,5681],{},"ใส่ client cert แล้วแต่ยังไม่ใส่ user กับ password",[63,5683,5685],{"className":65,"code":5684,"language":67,"meta":68,"style":68},"c -sS --cacert /c/ca.crt --cert /c/client.crt --key /c/client.key \\\n  https://node-exporter:9100/metrics\n",[19,5686,5687,5711],{"__ignoreMap":68},[72,5688,5689,5691,5693,5695,5697,5700,5703,5706,5709],{"class":74,"line":75},[72,5690,5600],{"class":78},[72,5692,5603],{"class":86},[72,5694,5652],{"class":86},[72,5696,5655],{"class":82},[72,5698,5699],{"class":86}," --cert",[72,5701,5702],{"class":82}," /c/client.crt",[72,5704,5705],{"class":86}," --key",[72,5707,5708],{"class":82}," /c/client.key",[72,5710,152],{"class":151},[72,5712,5713],{"class":74,"line":128},[72,5714,5715],{"class":82},"  https://node-exporter:9100/metrics\n",[63,5717,5720],{"className":5718,"code":5719,"language":99},[97],"Unauthorized\n",[19,5721,5719],{"__ignoreMap":68},[15,5723,5724],{},"คราวนี้ผ่านชั้น TLS มาได้แล้ว มาตกที่ basic auth แทน (HTTP 401) ใส่ให้ครบทั้งหมด",[63,5726,5728],{"className":65,"code":5727,"language":67,"meta":68,"style":68},"c -sS -u prometheus:'รหัสที่ตั้งไว้' \\\n  --cacert /c/ca.crt --cert /c/client.crt --key /c/client.key \\\n  https://node-exporter:9100/metrics | grep -E '^node_(boot_time_seconds|uname_info)'\n",[19,5729,5730,5751,5768],{"__ignoreMap":68},[72,5731,5732,5734,5736,5739,5742,5744,5747,5749],{"class":74,"line":75},[72,5733,5600],{"class":78},[72,5735,5603],{"class":86},[72,5737,5738],{"class":86}," -u",[72,5740,5741],{"class":82}," prometheus:",[72,5743,245],{"class":238},[72,5745,5746],{"class":82},"รหัสที่ตั้งไว้",[72,5748,245],{"class":238},[72,5750,152],{"class":151},[72,5752,5753,5756,5758,5760,5762,5764,5766],{"class":74,"line":128},[72,5754,5755],{"class":86},"  --cacert",[72,5757,5655],{"class":82},[72,5759,5699],{"class":86},[72,5761,5702],{"class":82},[72,5763,5705],{"class":86},[72,5765,5708],{"class":82},[72,5767,152],{"class":151},[72,5769,5770,5773,5775,5777,5780,5782,5785],{"class":74,"line":135},[72,5771,5772],{"class":82},"  https://node-exporter:9100/metrics",[72,5774,1168],{"class":248},[72,5776,4869],{"class":78},[72,5778,5779],{"class":86}," -E",[72,5781,239],{"class":238},[72,5783,5784],{"class":82},"^node_(boot_time_seconds|uname_info)",[72,5786,894],{"class":238},[63,5788,5791],{"className":5789,"code":5790,"language":99},[97],"node_boot_time_seconds 1.786004256e+09\nnode_uname_info{domainname=\"(none)\",machine=\"x86_64\",nodename=\"homelab-01\",...} 1\n",[19,5792,5790],{"__ignoreMap":68},[10,5794,5796],{"id":5795},"ฝั่ง-prometheus","ฝั่ง Prometheus",[15,5798,5799],{},"ทีนี้ต้องบอก Prometheus ว่าให้ส่งอะไรไปบ้างตอน scrape",[63,5801,5803],{"className":65,"code":5802,"language":67,"meta":68,"style":68},"cat > ~/node-exporter/prometheus.yml \u003C\u003C'EOF'\nglobal:\n  scrape_interval: 15s\n\nscrape_configs:\n  - job_name: 'node'\n    scheme: https\n    static_configs:\n      - targets: ['node-exporter:9100']\n    basic_auth:\n      username: prometheus\n      password: 'รหัสที่ตั้งไว้'\n    tls_config:\n      ca_file: /etc/prometheus/certs/ca.crt\n      cert_file: /etc/prometheus/certs/client.crt\n      key_file: /etc/prometheus/certs/client.key\nEOF\n\ndocker run -d --name prometheus --network mon-net -p 9090:9090 \\\n  -v ~/node-exporter/prometheus.yml:/etc/prometheus/prometheus.yml:ro \\\n  -v ~/node-exporter/certs:/etc/prometheus/certs:ro \\\n  prom/prometheus:latest\n",[19,5804,5805,5818,5823,5828,5832,5836,5841,5846,5850,5855,5860,5865,5870,5875,5880,5885,5890,5894,5898,5922,5931,5941],{"__ignoreMap":68},[72,5806,5807,5809,5811,5814,5816],{"class":74,"line":75},[72,5808,273],{"class":78},[72,5810,249],{"class":248},[72,5812,5813],{"class":82}," ~/node-exporter/prometheus.yml",[72,5815,281],{"class":248},[72,5817,285],{"class":284},[72,5819,5820],{"class":74,"line":128},[72,5821,5822],{"class":82},"global:\n",[72,5824,5825],{"class":74,"line":135},[72,5826,5827],{"class":82},"  scrape_interval: 15s\n",[72,5829,5830],{"class":74,"line":155},[72,5831,132],{"emptyLinePlaceholder":131},[72,5833,5834],{"class":74,"line":166},[72,5835,1456],{"class":82},[72,5837,5838],{"class":74,"line":177},[72,5839,5840],{"class":82},"  - job_name: 'node'\n",[72,5842,5843],{"class":74,"line":313},[72,5844,5845],{"class":82},"    scheme: https\n",[72,5847,5848],{"class":74,"line":319},[72,5849,1466],{"class":82},[72,5851,5852],{"class":74,"line":325},[72,5853,5854],{"class":82},"      - targets: ['node-exporter:9100']\n",[72,5856,5857],{"class":74,"line":330},[72,5858,5859],{"class":82},"    basic_auth:\n",[72,5861,5862],{"class":74,"line":336},[72,5863,5864],{"class":82},"      username: prometheus\n",[72,5866,5867],{"class":74,"line":342},[72,5868,5869],{"class":82},"      password: 'รหัสที่ตั้งไว้'\n",[72,5871,5872],{"class":74,"line":348},[72,5873,5874],{"class":82},"    tls_config:\n",[72,5876,5877],{"class":74,"line":353},[72,5878,5879],{"class":82},"      ca_file: /etc/prometheus/certs/ca.crt\n",[72,5881,5882],{"class":74,"line":358},[72,5883,5884],{"class":82},"      cert_file: /etc/prometheus/certs/client.crt\n",[72,5886,5887],{"class":74,"line":364},[72,5888,5889],{"class":82},"      key_file: /etc/prometheus/certs/client.key\n",[72,5891,5892],{"class":74,"line":370},[72,5893,390],{"class":284},[72,5895,5896],{"class":74,"line":376},[72,5897,132],{"emptyLinePlaceholder":131},[72,5899,5900,5902,5904,5906,5908,5911,5913,5915,5917,5920],{"class":74,"line":382},[72,5901,79],{"class":78},[72,5903,83],{"class":82},[72,5905,142],{"class":86},[72,5907,145],{"class":86},[72,5909,5910],{"class":82}," prometheus",[72,5912,4825],{"class":86},[72,5914,4828],{"class":82},[72,5916,226],{"class":86},[72,5918,5919],{"class":82}," 9090:9090",[72,5921,152],{"class":151},[72,5923,5924,5926,5929],{"class":74,"line":387},[72,5925,522],{"class":86},[72,5927,5928],{"class":82}," ~/node-exporter/prometheus.yml:/etc/prometheus/prometheus.yml:ro",[72,5930,152],{"class":151},[72,5932,5934,5936,5939],{"class":74,"line":5933},21,[72,5935,522],{"class":86},[72,5937,5938],{"class":82}," ~/node-exporter/certs:/etc/prometheus/certs:ro",[72,5940,152],{"class":151},[72,5942,5944],{"class":74,"line":5943},22,[72,5945,5946],{"class":82},"  prom/prometheus:latest\n",[15,5948,5949,5950,5953],{},"จุดที่คนพลาดกันบ่อยคือลืม ",[19,5951,5952],{},"scheme: https"," ครับ ค่าเริ่มต้นของ Prometheus คือ http ผมลองลบบรรทัดนั้นออกดูแล้ว หน้า target จะขึ้นแบบนี้",[63,5955,5958],{"className":5956,"code":5957,"language":99},[97],"url       = http://node-exporter:9100/metrics\nhealth    = down\nlastError = server returned HTTP status 400 Bad Request\n",[19,5959,5957],{"__ignoreMap":68},[15,5961,5962,5963,5966],{},"สังเกตว่ามันไม่ได้บอกว่า ",[19,5964,5965],{},"Client sent an HTTP request to an HTTPS server"," เหมือนตอนที่เรายิง curl เองนะครับ ข้อความนั้นเป็นแค่ body ที่ Prometheus ทิ้งไป เหลือให้เห็นแค่ 400 เฉย ๆ เพราะฉะนั้นถ้าเจอ 400 กับ target ที่เป็น node_exporter ให้สงสัยเรื่อง scheme ไว้ก่อนเลย",[15,5968,5969,5970,5973,5974,5977,5978,5981],{},"ส่วนที่ไม่ได้ใส่ไว้ในตัวอย่างคือ ",[19,5971,5972],{},"server_name"," ครับ มันเป็นตัว override ชื่อที่ใช้ตรวจ cert จำเป็นตอนที่ address ของ target ไม่ตรงกับ SAN เช่นเวลา scrape ด้วย IP ในที่นี้ ",[19,5975,5976],{},"node-exporter:9100"," ตรงกับ ",[19,5979,5980],{},"DNS:node-exporter"," อยู่แล้วเลยไม่ต้องใส่",[15,5983,5984,5985,5988],{},"รอสัก 20 วินาทีให้ scrape รอบแรกทำงานก่อนนะครับ แล้วค่อยเช็ค ถ้าถามเร็วเกินไปจะเห็น health เป็น ",[19,5986,5987],{},"unknown"," แล้วนึกว่าพัง",[63,5990,5992],{"className":65,"code":5991,"language":67,"meta":68,"style":68},"curl -s http://localhost:9090/api/v1/targets \\\n  | jq -r '.data.activeTargets[] | \"\\(.labels.job)  \\(.health)  \\(.scrapeUrl)\"'\n",[19,5993,5994,6005],{"__ignoreMap":68},[72,5995,5996,5998,6000,6003],{"class":74,"line":75},[72,5997,193],{"class":78},[72,5999,196],{"class":86},[72,6001,6002],{"class":82}," http://localhost:9090/api/v1/targets",[72,6004,152],{"class":151},[72,6006,6007,6010,6012,6014,6016,6019],{"class":74,"line":128},[72,6008,6009],{"class":248},"  |",[72,6011,1171],{"class":78},[72,6013,1174],{"class":86},[72,6015,239],{"class":238},[72,6017,6018],{"class":82},".data.activeTargets[] | \"\\(.labels.job)  \\(.health)  \\(.scrapeUrl)\"",[72,6020,894],{"class":238},[63,6022,6025],{"className":6023,"code":6024,"language":99},[97],"node  up  https://node-exporter:9100/metrics\n",[19,6026,6024],{"__ignoreMap":68},[15,6028,6029,6030,6033],{},"หรือจะ query ค่า ",[19,6031,6032],{},"up"," ตรง ๆ ก็ได้",[63,6035,6037],{"className":65,"code":6036,"language":67,"meta":68,"style":68},"curl -sG http://localhost:9090/api/v1/query --data-urlencode 'query=up{job=\"node\"}' \\\n  | jq -r '.data.result[] | \"up = \\(.value[1])  instance=\\(.metric.instance)\"'\n",[19,6038,6039,6060],{"__ignoreMap":68},[72,6040,6041,6043,6045,6048,6051,6053,6056,6058],{"class":74,"line":75},[72,6042,193],{"class":78},[72,6044,863],{"class":86},[72,6046,6047],{"class":82}," http://localhost:9090/api/v1/query",[72,6049,6050],{"class":86}," --data-urlencode",[72,6052,239],{"class":238},[72,6054,6055],{"class":82},"query=up{job=\"node\"}",[72,6057,245],{"class":238},[72,6059,152],{"class":151},[72,6061,6062,6064,6066,6068,6070,6073],{"class":74,"line":128},[72,6063,6009],{"class":248},[72,6065,1171],{"class":78},[72,6067,1174],{"class":86},[72,6069,239],{"class":238},[72,6071,6072],{"class":82},".data.result[] | \"up = \\(.value[1])  instance=\\(.metric.instance)\"",[72,6074,894],{"class":238},[63,6076,6079],{"className":6077,"code":6078,"language":99},[97],"up = 1  instance=node-exporter:9100\n",[19,6080,6078],{"__ignoreMap":68},[15,6082,6083,6084,6087],{},"ได้ ",[19,6085,6086],{},"1"," แปลว่า scrape ผ่าน mTLS สำเร็จแล้วครับ",[10,6089,6091],{"id":6090},"ตรวจ-config","ตรวจ config",[15,6093,6094,6095,6097],{},"อันนี้เป็นของแถมที่ผมชอบมาก ",[19,6096,4785],{}," ของ Prometheus ตรวจไฟล์ของ node_exporter ได้ด้วย เพราะมันเป็น format เดียวกันนั่นแหละ",[63,6099,6101],{"className":65,"code":6100,"language":67,"meta":68,"style":68},"docker run --rm --entrypoint promtool \\\n  -v ~/node-exporter/web-config.yml:/w.yml:ro \\\n  -v ~/node-exporter/certs:/etc/node_exporter/certs:ro \\\n  prom/prometheus:latest check web-config /w.yml\n",[19,6102,6103,6119,6128,6136],{"__ignoreMap":68},[72,6104,6105,6107,6109,6111,6114,6117],{"class":74,"line":75},[72,6106,79],{"class":78},[72,6108,83],{"class":82},[72,6110,87],{"class":86},[72,6112,6113],{"class":86}," --entrypoint",[72,6115,6116],{"class":82}," promtool",[72,6118,152],{"class":151},[72,6120,6121,6123,6126],{"class":74,"line":128},[72,6122,522],{"class":86},[72,6124,6125],{"class":82}," ~/node-exporter/web-config.yml:/w.yml:ro",[72,6127,152],{"class":151},[72,6129,6130,6132,6134],{"class":74,"line":135},[72,6131,522],{"class":86},[72,6133,5536],{"class":82},[72,6135,152],{"class":151},[72,6137,6138,6141,6144,6147],{"class":74,"line":155},[72,6139,6140],{"class":82},"  prom/prometheus:latest",[72,6142,6143],{"class":82}," check",[72,6145,6146],{"class":82}," web-config",[72,6148,6149],{"class":82}," /w.yml\n",[63,6151,6154],{"className":6152,"code":6153,"language":99},[97],"/w.yml SUCCESS\n",[19,6155,6153],{"__ignoreMap":68},[15,6157,6158],{},"ลองพิมพ์ค่าผิดดูว่ามันจับได้ไหม",[63,6160,6163],{"className":6161,"code":6162,"language":99},[97],"/w.yml FAILED: invalid ClientAuth: RequireAnyClientCert2\n",[19,6164,6162],{"__ignoreMap":68},[15,6166,6167],{},"จับได้ครับ ควรรันทุกครั้งก่อน restart service เพราะถ้า config พังแล้ว service ไม่ขึ้น กว่าจะรู้ตัวก็ตอน metrics หายไปแล้ว",[15,6169,6170,6171,6174],{},"อย่างเดียวที่ต้องระวังคือมันเปิดไฟล์ cert ตาม path ใน config ไปด้วย เพราะฉะนั้นต้องรันบนเครื่องที่มีไฟล์พวกนั้นอยู่จริง (หรือ mount เข้าไปให้ตรง path เหมือนคำสั่งข้างบน) ไม่งั้นจะได้ ",[19,6172,6173],{},"failed to read cert_file"," ทั้งที่ config ถูกทุกอย่าง",[10,6176,6178],{"id":6177},"ถ้ารันแบบ-bare-metal","ถ้ารันแบบ bare metal",[15,6180,6181],{},"ที่ทำมาทั้งหมดใช้กับ systemd ได้เหมือนกันครับ ต่างแค่สองอย่าง",[15,6183,6184,6185,6187],{},"หนึ่ง เปลี่ยน path ใน ",[19,6186,4756],{}," เป็น path จริงบนเครื่อง แล้วก๊อป cert ไปไว้ตรงนั้น",[63,6189,6191],{"className":65,"code":6190,"language":67,"meta":68,"style":68},"sudo mkdir -p /etc/node_exporter\nsudo cp -r ~/node-exporter/certs ~/node-exporter/web-config.yml /etc/node_exporter/\n",[19,6192,6193,6205],{"__ignoreMap":68},[72,6194,6195,6197,6200,6202],{"class":74,"line":75},[72,6196,1982],{"class":78},[72,6198,6199],{"class":82}," mkdir",[72,6201,226],{"class":86},[72,6203,6204],{"class":82}," /etc/node_exporter\n",[72,6206,6207,6209,6212,6214,6216,6218],{"class":74,"line":128},[72,6208,1982],{"class":78},[72,6210,6211],{"class":82}," cp",[72,6213,1174],{"class":86},[72,6215,5021],{"class":82},[72,6217,5409],{"class":82},[72,6219,6220],{"class":82}," /etc/node_exporter/\n",[15,6222,6223,6224],{},"สอง เติม flag เข้าไปใน unit ที่ ",[19,6225,6226],{},"/etc/systemd/system/node_exporter.service",[63,6228,6232],{"className":6229,"code":6230,"language":6231,"meta":68,"style":68},"language-ini shiki shiki-themes material-theme-lighter one-dark-pro github-dark","ExecStart=/usr/local/bin/node_exporter \\\n  --web.config.file=/etc/node_exporter/web-config.yml\n","ini",[19,6233,6234,6239],{"__ignoreMap":68},[72,6235,6236],{"class":74,"line":75},[72,6237,6238],{},"ExecStart=/usr/local/bin/node_exporter \\\n",[72,6240,6241],{"class":74,"line":128},[72,6242,5550],{},[15,6244,6245,6246,6249,6250,6252,6253,6255,6256,6259,6260,6263],{},"แล้ว ",[19,6247,6248],{},"sudo systemctl daemon-reload && sudo systemctl restart node_exporter"," ตามปกติ ส่วน ",[19,6251,4785],{}," จะติดมากับ tarball ของ Prometheus อยู่แล้ว และ ",[19,6254,4782],{}," อยู่ในแพ็กเกจ ",[19,6257,6258],{},"apache2-utils"," (Debian/Ubuntu) หรือ ",[19,6261,6262],{},"httpd-tools"," (RHEL)",[10,6265,6266],{"id":6266},"ข้อควรระวัง",[15,6268,6269,6272,6273,6275,6276,6279,6280,6283,6284,6286],{},[27,6270,6271],{},"รหัสผ่านอยู่หลายที่ ต้องเปลี่ยนพร้อมกัน"," ตอนหมุนรหัสต้องแก้ทั้ง ",[19,6274,4756],{}," ฝั่ง node_exporter และ ",[19,6277,6278],{},"basic_auth"," ใน ",[19,6281,6282],{},"prometheus.yml"," ถ้าแก้ไม่ครบ target จะกลายเป็น down แล้วอาการที่เห็นมีอย่างเดียวคือ ",[19,6285,6032],{}," เป็น 0",[15,6288,6289,6292,6293,6296,6297,6300],{},[27,6290,6291],{},"cert มีวันหมดอายุ"," ที่ผมตั้งไว้คือ 365 วัน พอครบแล้ว target ตัวนั้นจะ down ทันที (ตัวอื่นที่ไม่ได้ใช้ cert ยังทำงานปกติ) ที่สำคัญคือ ",[27,6294,6295],{},"ต้องจำวันหมดอายุเอง"," เพราะสแตกที่ตั้งในบทความนี้ไม่มีตัวไหนคอย monitor cert ให้ ผมลอง query ",[19,6298,6299],{},"probe_ssl_earliest_cert_expiry"," ที่หลายคนแนะนำกันดูแล้ว ได้ผลลัพธ์ว่างเปล่า เพราะ metric ตัวนั้นเป็นของ blackbox_exporter ซึ่งไม่ได้อยู่ในบทความนี้ ถ้าจะใช้จริงต้องลง blackbox_exporter เพิ่มแล้วตั้ง module ให้ใส่ client cert ไปด้วย ไม่งั้นมันก็ทะลุ mTLS เข้าไปวัดไม่ได้เหมือนกัน ทางที่ง่ายกว่าคือจดวันหมดอายุใส่ปฏิทินไว้เลยครับ",[15,6302,6303,6306,6307,6310],{},[27,6304,6305],{},"ถ้า Prometheus อยู่เครื่องเดียวกับ node_exporter"," ผูก ",[19,6308,6309],{},"--web.listen-address=127.0.0.1:9100"," ไปเลยง่ายกว่าเยอะครับ ไม่ต้องยุ่งกับ cert เลยสักนิด mTLS มีประโยชน์ตอนที่ต้องวิ่งข้ามเครื่องข้ามวงจริง ๆ",[10,6312,1762],{"id":1762},[15,6314,1765],{},[63,6316,6318],{"className":65,"code":6317,"language":67,"meta":68,"style":68},"docker rm -f node-exporter prometheus\ndocker network rm mon-net\ndocker rmi quay.io/prometheus/node-exporter:latest prom/prometheus:latest curlimages/curl:latest\nrm -rf ~/node-exporter\nunalias c\n",[19,6319,6320,6332,6342,6358,6367],{"__ignoreMap":68},[72,6321,6322,6324,6326,6328,6330],{"class":74,"line":75},[72,6323,79],{"class":78},[72,6325,1777],{"class":82},[72,6327,1780],{"class":86},[72,6329,5514],{"class":82},[72,6331,5361],{"class":82},[72,6333,6334,6336,6338,6340],{"class":74,"line":128},[72,6335,79],{"class":78},[72,6337,119],{"class":82},[72,6339,1777],{"class":82},[72,6341,4809],{"class":82},[72,6343,6344,6346,6349,6352,6355],{"class":74,"line":135},[72,6345,79],{"class":78},[72,6347,6348],{"class":82}," rmi",[72,6350,6351],{"class":82}," quay.io/prometheus/node-exporter:latest",[72,6353,6354],{"class":82}," prom/prometheus:latest",[72,6356,6357],{"class":82}," curlimages/curl:latest\n",[72,6359,6360,6362,6364],{"class":74,"line":155},[72,6361,1800],{"class":78},[72,6363,1803],{"class":86},[72,6365,6366],{"class":82}," ~/node-exporter\n",[72,6368,6369,6372],{"class":74,"line":166},[72,6370,6371],{"class":234},"unalias",[72,6373,6374],{"class":82}," c\n",[10,6376,1809],{"id":1809},[15,6378,6379,6380,6382],{},"เรื่องที่ผมได้จากรอบนี้ไม่ใช่แค่วิธีเปิด mTLS ครับ แต่เป็นการรู้ว่า ",[19,6381,4756],{}," มันเป็นไฟล์ที่ใช้ร่วมกันได้กับทุกตัวในตระกูล Prometheus ซึ่งแปลว่าถ้าเคยตั้งให้ตัวไหนไปแล้วตัวนึง ที่เหลือก็ก๊อปแล้วแก้ path เอาได้เลย ไม่ต้องไปเรียนรู้ใหม่",[15,6384,6385],{},"ส่วนใครที่ยังปล่อย node_exporter โล่งอยู่ ถ้าอยู่ในวงบ้านที่มีแต่เราคนเดียวก็อาจจะยังไม่ต้องรีบ แต่ถ้ามีเครื่องอื่นหรือคนอื่นอยู่ในวงด้วยก็ควรทำอะไรสักอย่างแล้วครับ",[15,6387,6388],{},"มีเรื่องนึงที่ต้องบอกไว้ด้วย ถ้าใส่แค่ basic auth โดยไม่เปิด TLS รหัสผ่านจะวิ่งไปแบบ base64 ซึ่งถอดกลับได้ทันที ใครดักอ่านในวงเดียวกันก็เห็นตั้งแต่ scrape รอบแรก มันพอกันคนที่บังเอิญเปิดเจอได้ก็จริง แต่ไม่ได้กันคนที่ตั้งใจดัก เพราะฉะนั้นถ้าจะใส่แค่ basic auth ก่อน อย่าเอารหัสที่ใช้ที่อื่นมาใช้ซ้ำนะครับ ตัวที่ปกป้องรหัสจริง ๆ คือ TLS",[10,6390,1825],{"id":1824},[417,6392,6393,6400,6407],{},[420,6394,6395],{},[1831,6396,6399],{"href":6397,"rel":6398},"https://github.com/prometheus/node_exporter",[1835],"node_exporter",[420,6401,6402],{},[1831,6403,6406],{"href":6404,"rel":6405},"https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md",[1835],"exporter-toolkit — web configuration",[420,6408,6409],{},[1831,6410,6413],{"href":6411,"rel":6412},"https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config",[1835],"Prometheus — tls_config",[1866,6415,6416],{},"html pre.shiki code .sosa_, html code.shiki .sosa_{--shiki-light:#E2931D;--shiki-default:#61AFEF;--shiki-dark:#B392F0}html pre.shiki code .sAlJX, html code.shiki .sAlJX{--shiki-light:#91B859;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .sEoV_, html code.shiki .sEoV_{--shiki-light:#91B859;--shiki-default:#D19A66;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sicAX, html code.shiki .sicAX{--shiki-light:#90A4AE;--shiki-default:#56B6C2;--shiki-dark:#79B8FF}html pre.shiki code .sbGrh, html code.shiki .sbGrh{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#F97583}html pre.shiki code .snfNA, html code.shiki .snfNA{--shiki-light:#39ADB5;--shiki-default:#98C379;--shiki-dark:#9ECBFF}html pre.shiki code .stlKB, html code.shiki .stlKB{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#E1E4E8}html pre.shiki code .sJZfL, html code.shiki .sJZfL{--shiki-light:#6182B8;--shiki-default:#56B6C2;--shiki-dark:#79B8FF}html pre.shiki code .sDKbk, html code.shiki .sDKbk{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#7F848E;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .snOyU, html code.shiki .snOyU{--shiki-light:#F76D47;--shiki-default:#D19A66;--shiki-dark:#79B8FF}html pre.shiki code .s2KDN, html code.shiki .s2KDN{--shiki-light:#39ADB5;--shiki-default:#ABB2BF;--shiki-dark:#9ECBFF}html pre.shiki code .sOHk9, html code.shiki .sOHk9{--shiki-light:#9C3EDA;--shiki-default:#C678DD;--shiki-dark:#F97583}html pre.shiki code .sLHrO, html code.shiki .sLHrO{--shiki-light:#90A4AE;--shiki-default:#E06C75;--shiki-dark:#E1E4E8}html pre.shiki code .sMTEB, html code.shiki .sMTEB{--shiki-light:#39ADB5;--shiki-default:#56B6C2;--shiki-dark:#F97583}",{"title":68,"searchDepth":128,"depth":128,"links":6418},[6419,6420,6421,6422,6423,6424,6425,6426,6427,6428,6429,6430,6431,6432],{"id":12,"depth":128,"text":13},{"id":4789,"depth":128,"text":4790},{"id":4956,"depth":128,"text":4957},{"id":5003,"depth":128,"text":5004},{"id":5317,"depth":128,"text":5318},{"id":5384,"depth":128,"text":5385},{"id":5553,"depth":128,"text":5553},{"id":5795,"depth":128,"text":5796},{"id":6090,"depth":128,"text":6091},{"id":6177,"depth":128,"text":6178},{"id":6266,"depth":128,"text":6266},{"id":1762,"depth":128,"text":1762},{"id":1809,"depth":128,"text":1809},{"id":1824,"depth":128,"text":1825},"2026-08-07T00:00:00.000Z","วิธีเปิด Basic auth และ mTLS ให้ node_exporter ด้วย web-config.yml ไฟล์เดียวกับที่ Prometheus ใช้",{},"Secure node_exporter With mTLS","/blogs/2026-08-07-node-exporter-mtls-basic-auth",[1891,6439],"2026-08-03-grafana-alloy-loki.md",{"title":4743,"description":6434},{"loc":6437},"blogs/2026-08-07-node-exporter-mtls-basic-auth",[1896],"S2mEz-GLvX5MgME4zSqk7wsBT3DXANv58iytOHzgooY",1789544695482]