<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by PumsDev on Medium]]></title>
        <description><![CDATA[Stories by PumsDev on Medium]]></description>
        <link>https://medium.com/@pumsdev?source=rss-8d41a1cba321------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*1CW_xzTJ6O3-VZIgtf3Gcw.png</url>
            <title>Stories by PumsDev on Medium</title>
            <link>https://medium.com/@pumsdev?source=rss-8d41a1cba321------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 22 May 2026 13:51:03 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@pumsdev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Setup Kafbat for monitor Kafka]]></title>
            <link>https://medium.com/@pumsdev/setup-kafbat-for-monitor-kafka-4eafb4676410?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/4eafb4676410</guid>
            <category><![CDATA[apache-kafka]]></category>
            <category><![CDATA[kafbat]]></category>
            <category><![CDATA[kafka]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Mon, 08 Dec 2025 10:56:12 GMT</pubDate>
            <atom:updated>2026-01-22T07:39:16.061Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/656/1*841aWk-aW-jbUMTXF7VrBw.png" /></figure><p>สวัสดีครับ Blog นี้จะพามารู้จักกับ Kafbat ซึ่งเป็น Tools เพื่อเอามาใช้ในการจัดการ Kafka cluster กันครับ</p><p><strong>Kafbat</strong> คือ Tool ที่เป็น Opensource ตัวนึงที่สามารถนำมาใช้ Monitor และ จัดการ Kafka Cluster หลายๆ cluster ในตัวเดียวได้ ซึ่งถ้าคุ้นเคยกับ Conduktor Tools ตัวนี้ก็มีความสามารถเบื้องต้นใกล้เคียงกันครับผม</p><h3>วิธี Setup Kafbat เพื่อ run โดยใช้ Jar ไฟล์</h3><p>สิ่งที่จำเป็นต้องมีเพื่อ run Kafbat ขึ้นมาที่เครื่องเรา</p><h4>Kafbat</h4><p>สำหรับ Kafbat ที่เป็น Jar ไฟล์ สามารถ download ได้จาก <a href="https://github.com/kafbat/kafka-ui/releases">https://github.com/kafbat/kafka-ui/releases</a> ซึ่งปัจจุบันวันที่เขียน blog นี้ จะเป็น version <strong>1.4.2</strong></p><h4>JDK</h4><p>เนื่องจากเรา download Jar มาเลยจำเป็นต้องมี JDK หรือ JRE เพื่อให้สามารถ run java ที่เครื่องได้ โดย Kafbat version <strong>1.4.2</strong> นั้น Minimum Java version คือ<strong> 21</strong> สามารถ Download ได้จาก <a href="https://www.oracle.com/java/technologies/downloads/#jdk21-windows">https://www.oracle.com/java/technologies/downloads/#jdk21-windows</a></p><h4>Application properties</h4><p>ตัว application properties เป็น configuration ไฟล์ ซึ่งสามารถดูตัวอย่างได้จาก <a href="https://github.com/kafbat/kafka-ui/blob/main/api/src/main/resources/application-local.yml">https://github.com/kafbat/kafka-ui/blob/main/api/src/main/resources/application-local.yml</a></p><p>แต่สำหรับการทดลองใช้งานครั้งแรก แนะนำให้ลอง setup แบบง่าย ๆ เพื่อลองเชื่อมต่อกับ Kafka ที่ run อยู่ที่ localhost:9092 ดูก่อน โดย properties จะเหลือประมาณนี้</p><pre>logging:<br>  level:<br>    root: INFO<br>    io.kafbat.ui: DEBUG<br>    #org.springframework.http.codec.json.Jackson2JsonEncoder: DEBUG<br>    #org.springframework.http.codec.json.Jackson2JsonDecoder: DEBUG<br>    reactor.netty.http.server.AccessLog: INFO<br>    org.springframework.security: DEBUG<br><br><br>kafka:<br>  clusters:<br>    - name: local<br>      bootstrapServers: localhost:9092<br></pre><p>เมื่อเรา Download ทุกไฟล์เรียบร้อยแล้ว ให้เอามารวมกันไว้ที่ folder เดียวกันจะได้ประมาณนี้</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/670/1*7qoHdI445AeCk42uhSH0ew.png" /></figure><p>ทดสอบการ run Kafbat ด้วยการเปิด terminal ไปที่ folder ที่เราเก็บไฟล์ไว้แล้ว ต่อด้วย command</p><pre>&lt;java_file&gt; -Dspring.config.additional-location=&lt;properties_file&gt; --add-opens java.rmi/javax.rmi.ssl=ALL-UNNAMED -jar &lt;kafbat_jar&gt;</pre><ul><li>&lt;java_file&gt; → แทนที่ด้วย java execution file เช่น ./jdk-21.0.9/bin/java.exe และสำหรับ Mac OS Path ก็จะประมาณนี้ ./jdk-21.0.9.jdk/Contents/Home/bin/java</li><li>&lt;properties_file&gt; → แทนที่ด้วย properties file เช่น ./application.yaml</li><li>&lt;kafbat_jar&gt; → แทนที่ด้วย kafbat jar file เช่น ./api-v1.4.2.jar</li></ul><p>หลังจาก run command เรียบร้อยแล้วก็ให้เปิด web browser แล้วเข้าไปที่ localhost:8080 เพื่อไปยังหน้า UI ของ Kafbat</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4eafb4676410" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apache Kafka Components Explained in One Picture]]></title>
            <link>https://medium.com/odds-team/apache-kafka-components-explained-in-one-picture-6080b03285d9?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/6080b03285d9</guid>
            <category><![CDATA[apache-kafka]]></category>
            <category><![CDATA[kafka]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Wed, 03 Dec 2025 14:56:13 GMT</pubDate>
            <atom:updated>2025-12-04T04:09:07.409Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1T-RAFps-5Z0q8T2nD5Fbw.png" /></figure><blockquote>Apache Kafka is a distributed event streaming platform</blockquote><p>Kafka คือ แพลตฟอร์มที่ถูกออกแบบมาเพื่อใช้ในงานในระบบที่ต้องจัดการกับข้อมูลที่ไหลเข้ามาแบบต่อเนื่อง (Event streaming) โดย Kafka จะทำหน้าที่เป็นตัวกลางที่คอยรับและเก็บข้อมูลเหล่านั้นชั่วคราว ก่อนที่ระบบปลายทางหลายๆ ระบบจะมาดึงข้อมูลจาก Kafka ไปใช้งานต่อ เช่น การรับข้อมูล log จากพฤติกรรมผู้ใช้งานบนเว็บไซต์มาเก็บไว้ที่ Kafka ก่อนแล้วระบบต่าง ๆ ที่เกี่ยวข้อง เช่น ระบบวิเคราะห์ข้อมูล ระบบแจ้งเตือน และระบบ Dashboard สามารถดึงข้อมูลเดียวกันนี้ไปใช้งานต่อได้พร้อมกันแบบ real-time โดยไม่รบกวนกัน</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lwDxF7CsUuBL5y04jk1r-A.png" /></figure><p>ในบทความนี้ เราจะมาทำความรู้จักกับองค์ประกอบหลักของ Kafka และทำความเข้าใจว่าทำไมมันถึงเป็นหัวใจสำคัญของ Event-Driven Architecture โดยส่วนประกอบหลักของ Kafka เพื่อความเข้าใจง่ายแล้วผมขอแบ่งออกเป็น 5 ส่วนหลัก ๆ อันได้แก่ Topic, Producer, Consumer, Broker และ Manager</p><p>เรามาทำความรู้จักกับแต่ละส่วนกันเลย</p><h3>1. Topic</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2xCevJ2WJ7j7m7tHeYwMeQ.png" /></figure><p>Topic ใน Kafka คือส่วนที่ไว้ใช้สำหรับเก็บข้อมูล โดยถ้าเปรียบเทียบกับ Relational Database ก็จะเปรียบเหมือนกับ Table แต่…</p><h4>ข้อแตกต่างระหว่าง Kafka Topic และ Table</h4><p><strong>ไม่มี Query แบบ Traditional Database</strong></p><ul><li>Kafka ตัวเองไม่มี query engine</li><li>ไม่สามารถ random access หรือ index-based lookup</li><li>แต่สามารถใช้ <strong>ksqlDB</strong> หรือ <strong>Kafka Streams</strong> เพื่อ query real-time ได้</li></ul><p><strong>Immutable (แก้ไขไม่ได้)</strong></p><ul><li>เขียนลงไปแล้วไม่สามารถ Update หรือ Delete ได้</li><li>เหมือนการบันทึกประวัติ เมื่อเกิดขึ้นแล้วข้อมูลที่เขียนลงไปใน Topic แล้วจะไม่สามารถแก้ไขได้</li></ul><p><strong>การลบใช้ Retention Policy</strong></p><ul><li>ไม่ได้ลบทีละ Record แต่จะเป็นลบตามเวลาหรือขนาดที่ตั้งไว้ (Retention)</li><li>ตั้ง Retention เช่น “เก็บ 7 วัน” หรือ “เก็บ 100GB”</li><li>Kafka ลบข้อมูลเก่าอัตโนมัติตาม policy</li></ul><h4>ส่วนประกอบของ Topic</h4><h4><strong>1.1 Partition</strong></h4><p>ภายใน Topic จะมี Partition ไว้สำหรับช่วยกระจายการทำงาน ทำให้ในหนึ่ง Topic สามารถรองรับ Service ที่เข้ามาเขียนและอ่านข้อมูลแบบคู่ขนานกัน โดยการกำหนดจำนวน Partition ให้เหมาะสมกับจำนวน Event ที่เข้ามาใน Topic จะช่วยเพิ่มความสามารถในการเขียนและอ่านข้อมูลจาก Topic ได้รวดเร็วมากยิ่งขึ้น</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cx1aWHluqhSnsJ4qFbQiGQ.png" /></figure><h4><strong>1.2 Offset</strong></h4><p>จะเป็นตัวแสดงถึงตำแหน่งปัจจุบันในแต่ละ Partition ว่าแต่ละ Consumer อ่านไปล่าสุดถึงตรงไหนแล้ว เพื่อป้องกันตอนที่ Consumer นั้น ๆ เกิดพังไป แล้วมี Consumer ใหม่มาแทนที่ ก็จะได้อ่านข้อมูลในลำดับถัดไปต่อได้เลย</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/712/1*IKmdAwYXhqTdXPKQmxQioA.png" /></figure><h4><strong>1.3 Replication factor</strong></h4><p>เป็น Configuration ที่เอาไว้ เพื่อการเพิ่มความมั่นใจในการเก็บข้อมูลของแต่ละ Partition ว่าจะมีการทำสำเนาเก็บไว้กี่ชุด โดยถ้า set ไว้เท่ากับ 1 หมายความว่าจะมีข้อมูลของ Partition แค่ 1 ชุดเท่านั้น (ไม่มีสำเนา)</p><h4><strong>1.4 Retention</strong></h4><p>ตัวเลขที่ระบุถึงอายุที่จะเก็บข้อมูลไว้ใน Kafka มีไว้เพื่อช่วยในการจัดการการใช้พื้นที่บน Disk ของ Kafka เพื่อที่จะได้ลบข้อมูลที่ไม่มีคนใช้งานแล้วออกไป โดยเราสามารถตั้งเงื่อนไข ได้ 2 แบบคือ ตามอายุ (millisecond) กับ ตามขนาด (byte)</p><h4><strong>1.5 In-sync replicas</strong></h4><p>คือ List รายชื่อของ Kafka Server (Broker) ทั้งหมดที่กำลังทำหน้าที่เก็บสำเนาข้อมูลใน Partition นี้อยู่</p><ul><li>การ config min.insync.replicas เป็นอีกหนึ่งใน Configuration ที่ไม่ควรมองข้าม เพราะเป็นตัวกำหนดถึงจำนวน Kafka Server (Broker) ขึ้นต่ำที่ต้องทำสำเนาข้อมูลของแต่ละ Partition ไว้เพื่อช่วยป้องกันเวลาที่มี Server พังเราจะได้มีข้อมูลสำรองไว้ใช้งาน</li></ul><h3>2. Producer</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*v9uUbkouqOaa6YO83Q4RJg.png" /></figure><p>Producer คือ Application หรือ Service ที่ส่งข้อมูลไปยัง Kafka Topic โดยในการที่จะสร้างข้อมูลเข้าไปยัง Topic ได้นั้น จะต้องมีการ Setup เบื้องต้นดังนี้</p><h4><strong>2.1 Producer Configuration</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7keqv8QgfxDfhu2zbcF6mw.png" /></figure><p><strong>2.1.1) Bootstrap server</strong> เป็น Configuration ที่ระบุถึง Kafka Server (Broker) ซึ่ง จะใช้ตอนที่ Service ของ Producer กำลังเริ่มต้นเชื่อมต่อไปหา Kafka Server เพื่อดึงข้อมูลรายละเอียดของ Kafka Cluster (Metadata) เพื่อใช้ต่อในตอนที่จะส่ง Event ไปเก็บใน Kafka</p><p><strong>2.1.2) Acknowledgement</strong> เป็น Configuration ที่บอกว่า Kafka Server (Broker) จะตอบกลับเมื่อไหร่ โดยมีทั้งหมด 3 แบบ คือ All, Wait leader และ No wait</p><ul><li>All (acks=all หรือ -1) : รอจนกว่า Brokers ทั้งหมดจะเขียนข้อมูลเสร็จค่อยตอบกลับ โดย default จะเป็นค่านี้</li><li>Wait leader (acks=1) : รอเฉพาะ Broker ที่ทำหน้าที่เป็น partition leader ของ partition นั้น ๆ เขียนข้อมูลเสร็จ แล้วจึงตอบกลับ</li><li>No wait (acks=0) : ตอบกลับทันที ไม่ต้องรอ</li></ul><h4><strong>2.2 Producer Message</strong></h4><p>คือ ข้อมูลที่จะถูกส่งเข้ามาเพื่อเก็บใน Kafka โดยโครงสร้างคร่าว ๆ จะประกอบด้วย</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/681/1*hyYNGlJfN6bEG_RtRpZlmQ.png" /></figure><p><strong>2.2.1) Key</strong> คือ ข้อมูลที่จะใช้สำหรับการเลือก Partition โดยเราสามารถระบุ Key ให้กับ Event เพื่อให้ Event ที่มี Key เดียวกันได้ถูก Produce ไปลง Partition เดิมเสมอ แต่ถ้าหากเราไม่ได้ระบุ Message Key โดย default ตัว Event จะถูก Produce ไปยัง Partition โดยใช้วิธี Sticky partitioner ในการช่วยเลือก Partition ซึ่งถ้าใครสนใจเรื่อง Sticky partitioner สามารถอ่านรายละเอียดได้จาก <a href="https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/">ที่นี่</a></p><p><strong>2.2.2) Value</strong> คือ ข้อมูลที่เราต้องการจะเก็บไว้ใน Kafka โดยอาจจะเป็นรูปแบบ Plain text, Photobuf, CSV และอื่น ๆ</p><p><strong>2.2.3) Header </strong>คือ Key-Value pairs เพื่อใส่ข้อมูล additional ที่อาจจะต้องใช้งานในฝั่ง Consumer อย่างเช่น tracing, correlation-id เป็นต้น</p><p>อีกหนึ่งสิ่งที่สำคัญของ Producer Message คือ จะต้องมีการระบุ Serializer ให้กับ Key และ Value เสมอ เพื่อให้ Producer สามารถแปลงข้อมูลไปเป็น Binary ได้ถูกต้องตรง Format เพราะว่าข้อมูลที่เก็บไว้ภายใน Kafka จะเป็นรูปแบบ Binary</p><h3>3. Consumer</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I138t0Sa40WEAlsyMDpCjg.png" /></figure><p>Consumer คือ Application หรือ Service ที่เข้ามาดึงข้อมูลจาก Kafka ไปใช้งานต่อ โดยการที่จะเข้ามายัง Kafka Server เพื่อดึงข้อมูลไปใช้งานต่อได้ก็จะต้องมีการ Setup เบื้องต้นดังนี้</p><h4>3.1 Consumer Configuration</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_643EGkKNxIfakjO-Y1sLA.png" /></figure><p><strong>3.1.1) Bootstrap server</strong> เป็น Configuration ที่ระบุถึง Kafka Server (Broker) เช่นเดียวกับ Bootstreap server ของฝั่ง Producer โดยฝั่งของ Consumer เองก็จะมีการเชื่อมต่อไปหา Kafka Server เพื่อดึงรายละเอียดของ Kafka Cluster (metadata) เพื่อใช้ต่อในการที่จะดึง Event มาใช้งานต่อ</p><p><strong>3.1.2) Consumer group </strong>เป็น Configuration ที่จะช่วยเพิ่มความสามารถในการประมวลผลแบบขนานกันได้ โดยแทนที่จะมี Consumer คนเดียวมาดึงข้อมูลจาก Kafka Topic เราสามารถเพิ่ม Consumer หลายๆ ตัวมาช่วยกันได้ โดยจะมีกฏสำคัญข้อนึงคือ ภายใน Group</p><ul><li>1 Partition จะถูกดึงข้อมูลโดย 1 Consumer เท่านั้น</li><li>เมื่อ Consumer ตัวใดตัวหนึ่งล่มไป Kafka จะย้ายการดึงข้อมูลของ Partition ที่มันเคยดูแลอยู่ ไปให้ Consumer ตัวอื่น ๆ ที่ยังทำงานได้อยู่แทนโดยอัตโนมัติ</li></ul><p><strong>3.1.3) Delivery semantic</strong> เป็น Configuration ที่ไว้บอกว่าข้อมูลจะถูกประมวลผลจาก Consumer กี่ตัวก่อนที่จะ Commit เพื่อจะขยับ Offset ที่เก็บไว้ใน Kafka Server ไปยังเลขถัดไป โดยจะมีทั้งหมด 3 รูปแบบ คือ</p><ul><li>At lease once : ประมวลผลข้อมูลให้เสร็จก่อนแล้วค่อย Commit Offset</li><li>At most once : Commit Offset ก่อนประมวลผล</li><li>Exactly once : ใช้ Transaction หรือ Idempotent Consumer ในการจัดการ เพื่อให้มั่นใจว่าข้อมูลจะถูกประมวลผลแล้วค่อย Commit Offset</li></ul><h4><strong>3.2 Consumer Message</strong></h4><p>คือ ข้อมูลที่ดึงออกมาจาก Kafka Topic โดยจะมีโครงสร้างเดียวกันกับ Producer Message แตกต่างกันเฉพาะฝั่ง Consumer จะเป็นฝั่งของการดึงข้อมูลมาใช้งาน ดังนั้น จะต้องมีการระบุ Deserializer ให้กับ Key และ Value ด้วยเพื่อให้สามารถแปลง Binary ที่อยู่ใน Kafka Topic กลับมาเป็นข้อมูลได้ถูกต้อง</p><h3>4. Broker</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9__9IoQUFYcP-9pFWzGSsA.png" /></figure><p>Broker หรือ Kafka Server คือศูนย์กลางที่ทำหน้าที่ในการจัดเก็บและจัดการข้อมูลต่าง ๆ ภายใน Cluster โดยมีหน้าที่คร่าว ๆ ดังนี้</p><ul><li>เก็บและจัดการข้อมูล Event</li><li>เป็นตัวกลางในการรับข้อมูลจาก Producer</li><li>เปิดช่องทางให้ Consumer เข้ามาดึงข้อมูลไปใช้งานต่อ</li><li>ทำการ Replication Data เพื่อเพิ่มความปลอดภัยของข้อมูล</li><li>จัดการข้อมูลของ Topic, Partition และ Offset</li></ul><p>โดย Broker ทุกตัวเราจะต้องมีการ Setup เบื้องต้นได้แก่</p><h4>4.1 Role</h4><p>ใน Kafka Cluster จะมีการกำหนด Role ให้กับ Broker โดยสามารถแบ่งออกได้เป็น สองแบบคือ Controller และ Broker</p><p><strong>Role: Broker</strong></p><p>คือ เครื่อง Broker ที่ทำหน้าที่ในการบันทึกและจัดการข้อมูลของแต่ละ Partition โดยแบ่งย่อยเป็น 2 แบบคือ</p><ul><li>Leader คือ Broker ที่คอยรับข้อมูลจาก Producer มาและบันทึกเข้าไปใน Kafka และเปิดให้ Consumer มาดึงข้อมูลไปใช้งานต่อได้</li><li>Follower คือ Broker ที่ Repilcation ข้อมูลของ Partition ไว้ เผื่อ Leader พังไปจะมีโอกาสถูกเลือกขึ้นเป็นเครื่อง Leader ได้</li></ul><p><strong>Role: Controller</strong></p><p>คือ เครื่อง Broker ที่ทำหน้าในการจัดการ Metadata ของ Cluster และคอย Monitor และเลือกว่าใครจะเป็น Leader ของแต่ละ Partition และเมื่อมีการเพิ่มลดจำนวน Broker ตัว Controller ก็จะเป็นคน Rebalance Partition ให้เหมาะกับจำนวน Broker ตัว Role ของฝั่ง Controller เองก็สามารถแบ่งเป็น 2 แบบคือ</p><ul><li>Active Controller คือ ตัวหลักที่ทำหน้าที่เป็น Controller อยู่ในปัจจุบัน ซึ่งมีได้แค่ 1 ตัวเท่านั้น</li><li>Passive Controller คือ ตัวสำรองที่คอย Replication ข้อมูลของ Controller อยู่เพื่อตอนที่ Active Controller พังไป จะได้ขึ้นเป็น Active ตัวใหม่ได้ทันทีโดยคนที่ทำหน้าที่เป็นคนเลือก Controller คือ Manager</li></ul><h4>4.2 In-Sync replicas</h4><p>คือ List รายชื่อของ Kafka Server (Broker) ทั้งหมดที่กำลังทำหน้าที่เก็บสำเนาข้อมูลใน Partition นี้อยู่ ซึ่งจะเหมือนกับที่อธิบายไปตอนต้นที่หัวข้อ Topic ซึ่งถ้ามีการ Setup ไว้ทั้งที่ Topic และ Broker ก็จะยึดจากที่ Topic เป็นหลัก เพราะ Priority ของ Topic สูงกว่า</p><h4><strong>5. Manager</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/740/1*8P4jlMX6yX2SQcNYhMSb4Q.png" /></figure><p>มาถึงส่วนสุดท้ายกันแล้ว นั่นคือ Manager ซึ่งโดย Document ของ Kafka เองไม่ได้เรียกส่วนนี้ว่าเป็น Manager และก็ยังไม่เจอชื่อเรียกที่ชัดเจนนัก แต่ด้วยหน้าที่ที่เป็นคนคอยประสานงานให้การทำงานภายใน Cluster เป็นไปได้อย่างราบรื่นผมเลยเปรียบตัว Zookeeper และ KRaft เป็น Manager แทนนะครับ</p><h4>5.1 Zookeeper</h4><p>Zookeeper คือ Service ที่ไว้ใช้จัดการ Configuration และ Metadata ต่าง ๆ ของ Kafka และยังเป็นตัวที่คอยบอกทางให้กับ Client เพื่อตามหา Broker ที่ทำหน้าที่เป็น Leader ปัจจุบันของแต่ละ Partition ด้วย รวมทั้งคอยจัดการการ Broker คอย Monitor, เลือก Broker ที่จะทำหน้าที่เป็น Controller</p><h4><strong>5.2 KRaft</strong></h4><p>KRaft เป็นโหมดใหม่ของ Kafka ที่ถูกออกแบบมาแทนที่การใช้งาน Zookeeper ในการจัดการ Metadata ทั้งหมดของ Kafka ทำให้ไม่ต้องติดตั้งและดูแล Zookeeper Cluster แยกต่างหากอีกต่อไป ช่วยลดความซับซ้อน และปัญหา Latency ระหว่าง Kafka และ Zookeeper</p><p><strong>ข้อจำกัด Zookeeper</strong></p><ul><li>ต้องติดตั้งและดูแล Zookeeper Cluster แยกต่างหาก</li><li>Metadata operation มี latency ที่สูงเพราะต้อง round-trip ไป Zookeeper</li><li>การเลือก Controller จะทำผ่าน Zookeeper ทำให้ Failover ช้าขึ้น</li></ul><p>KRaft มาแก้ปัญหาเหล่านี้โดยการเพิ่มการเก็บและจัดการ Metadata มากับ Kafka Broker เลยไม่ต้อง Setup หรือสร้าง Cluster แยกอีกต่อไป และเพิ่มการใช้ Kafka’s Raft ซึ่งเป็น Consensus Algorithm ที่พัฒนาต่อจาก Raft มาเป็นตัวช่วยในการตัดสินใจเลือกว่าจะให้ Broker ไหนรับ Role เป็น Active Controller ได้อย่างรวดเร็วและเสถียร</p><blockquote>KRaft ได้รับการประกาศว่าเป็น Production ready ตั้งแต่ Apache Kafka 3.3 เป็นต้นไป โดยคาดว่า Zookeeper จะถูก deprecate (เลิกใช้) ทั้งหมดใน version 4.0 ขึ้นไป</blockquote><h3>Kafka in Advanced</h3><p>นอกจากส่วนประกอบหลักที่กล่าวถึงไปแล้ว Kafka ยังมี Components เสริม ที่ออกแบบมาเพื่อตอบโจทย์ระบบ Always-On ซึ่งต้องทำงานต่อเนื่องและรองรับปริมาณข้อมูลขนาดใหญ่ ตัวอย่างที่น่าสนใจ ได้แก่</p><ul><li><strong>Kafka Streams — </strong>Library สำหรับการประมวลผลข้อมูลแบบ real-time</li><li><strong>Kafka Connect</strong> —Framework สำหรับเชื่อมต่อ Kafka กับระบบภายนอก เช่น Databases, File systems, หรือ Messaging systems</li></ul><h3>Course</h3><p><a href="https://www.coursera.org/learn/packt-apache-kafka-series-learn-apache-kafka-for-beginners-v3-cjher">https://www.coursera.org/learn/packt-apache-kafka-series-learn-apache-kafka-for-beginners-v3-cjher</a></p><h3>Reference</h3><ul><li><a href="https://kafka.apache.org/documentation/">Apache Kafka</a></li><li><a href="https://learn.conduktor.io/kafka/what-is-apache-kafka/">Home - Conduktor documentation</a></li><li><a href="https://raft.github.io/">Raft Consensus Algorithm</a></li><li><a href="https://zookeeper.apache.org/">Apache ZooKeeper</a></li><li><a href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-833%3A+Mark+KRaft+as+Production+Ready">KIP-833: Mark KRaft as Production Ready</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6080b03285d9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/odds-team/apache-kafka-components-explained-in-one-picture-6080b03285d9">Apache Kafka Components Explained in One Picture</a> was originally published in <a href="https://medium.com/odds-team">odds.team</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[วิธีปรับหน้าเว็บเป็นสีขาวดำ สำหรับใช้ช่วงไว้อาลัย]]></title>
            <link>https://medium.com/@pumsdev/%E0%B8%A7%E0%B8%B4%E0%B8%98%E0%B8%B5%E0%B8%9B%E0%B8%A3%E0%B8%B1%E0%B8%9A%E0%B8%AB%E0%B8%99%E0%B9%89%E0%B8%B2%E0%B9%80%E0%B8%A7%E0%B9%87%E0%B8%9A%E0%B9%80%E0%B8%9B%E0%B9%87%E0%B8%99%E0%B8%AA%E0%B8%B5%E0%B8%82%E0%B8%B2%E0%B8%A7%E0%B8%94%E0%B8%B3-%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A%E0%B9%83%E0%B8%8A%E0%B9%89%E0%B8%8A%E0%B9%88%E0%B8%A7%E0%B8%87%E0%B9%84%E0%B8%A7%E0%B9%89%E0%B8%AD%E0%B8%B2%E0%B8%A5%E0%B8%B1%E0%B8%A2-588611a6ed60?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/588611a6ed60</guid>
            <category><![CDATA[css]]></category>
            <category><![CDATA[css-filters]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Sun, 26 Oct 2025 09:21:37 GMT</pubDate>
            <atom:updated>2025-10-26T09:21:37.653Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/750/1*Goix71WtQbrzSWk5PIXcRQ.png" /></figure><p>สำหรับหน้าเว็บ เรามีวิธีง่าย ๆ ในการปรับเว็บเราให้เป็นโทนสีขาวดำ โดยใช้แค่ CSS บรรทัดเดียวเพื่อทำให้หน้าเว็บของเรากลายเป็นสีขาวดำแทนสีเดิมที่เราเคยใส่ไว้ โดย CSS ที่ต้องเพิ่มเข้าไปคือ filter: grayscale(1)</p><pre>html,<br>body {<br>  /* Mourning grayscale filter */<br>  filter: grayscale(1);<br>}</pre><p>ซึ่งโดยปกติแล้วเราใช้ filter ในการ blur หรือการปรับสีให้กับ element ของเราโดยปกติแล้วความสามารถของ filter หลัก ๆ ก็จะมี</p><ul><li>blur</li><li>contrast</li><li>grayscale</li><li>hue-rotate</li><li>drop-shadow</li><li>opacity</li></ul><p>ซึ่งถ้าอยากรู้ว่าแต่ละแบบต่างกันยังไง แนะนำให้เข้าไปลองเล่นจาก <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter">https://developer.mozilla.org/en-US/docs/Web/CSS/filter</a> ซึ่งมีให้กดเล่นเพื่อให้เห็นภาพชัดเจนมาก ๆ</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xBgHRgiL-K4RVRXh9Gj24A.png" /></figure><p>ด้วยความสามารถที่ filter ทำได้ บวกกับโครงสร้างของเว็บที่เรามี global.css เพื่อเอาไว้คุม theme หลักๆ ของ web เราไว้อยู่แล้ว เราก็แค่ add ตัว filter เข้าไปใน tag html หรือ html + body แบบในรูปตัวอย่างแรกได้เลย</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=588611a6ed60" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Anatomy of a JWT]]></title>
            <link>https://medium.com/@pumsdev/anatomy-of-a-jwt-b8f7abd2926a?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/b8f7abd2926a</guid>
            <category><![CDATA[jwt-authentication]]></category>
            <category><![CDATA[jwt]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Sat, 31 May 2025 05:28:03 GMT</pubDate>
            <atom:updated>2025-06-02T19:05:25.695Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VRYMMYUTtoVeRBC8vv_hIg.png" /></figure><p><a href="https://jwt.io/">JSON Web Tokens</a> are an open, industry standard <a href="https://tools.ietf.org/html/rfc7519"><strong>RFC 7519</strong></a> method for representing claims securely between two parties.</p><p>สาเหตุที่เรามีการนำ JWT มาใช้ก็เพื่อการส่งข้อมูลระหว่างกันของ Service ไม่ว่าจะเป็น Frontend to Backend หรือ Backend to Another Backend ก็ดี ก็เพื่อให้มีความปลอดภัยมากขึ้น เพราะทุกอย่างถูกมัดรวมกันไว้เป็นก้อนเดียว และมี Signature ทับอีกชั้นเพื่อรับรองว่าข้อมูลด้านในไม่ได้ถูกปลอมแปลง และยังสามารถกำหนดอายุของ Token ไว้ได้ด้วย โดยเราจะพบ JWT ได้บ่อยในการใช้กับเรื่องของการส่งข้อมูลสิทธิ์การใช้งานของ User แปะไว้ใน Request Header ที่ชื่อ Authorization และมี value อยู่ในรูปแบบ Bearer &lt;Token&gt;</p><h3>โครงสร้างของ JWT</h3><p><strong>ตัวอย่าง JWT</strong></p><blockquote><em>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</em></blockquote><p>JWT จะประกอบไปด้วย 3 ส่วน ซึ่งคั่นกันตัวเครื่องหมาย “.” ซึ่งถ้าดูให้ง่ายขึ้น JWT จะอยู่ในรูปแบบนี้ คือ</p><blockquote><em>hhhhhhhhh.ppppppppp.sssssssss</em></blockquote><p>hhhhhhhhh =&gt; คือส่วนของ Header เอาไว้ใช้บอกประเภทของ Token และ algorithm ในการเข้ารหัสของ signature</p><p>pppppppp =&gt; คือส่วนของข้อมูล หรือที่เรียกว่า Payload หรือเรียกอีกชื่อว่า Claims</p><p>sssssssss =&gt; คือส่วนของ Signature</p><h3>เรามาแกะ JWT เป็นส่วน ๆ กัน</h3><p><strong>ส่วนแรก</strong> คือ Header ซึ่งจาก JWT ตัวอย่าง เราจะได้ค่า base64 ของ Header คือ</p><blockquote><em>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</em></blockquote><p>เมื่อเรานำไป decode base64 จะได้ JSON หน้าตาแบบนี้</p><pre>{&quot;alg&quot;:&quot;HS256&quot;,&quot;typ&quot;:&quot;JWT&quot;}</pre><p><strong>ส่วนที่สอง</strong> คือ Payload</p><blockquote><em>eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ</em></blockquote><p>เมื่อเรานำไป decode base64 จะได้ JSON หน้าตาแบบนี้</p><pre>{&quot;sub&quot;:&quot;1234567890&quot;,&quot;name&quot;:&quot;John Doe&quot;,&quot;iat&quot;:1516239022}</pre><p><strong>ส่วนสุดท้าย</strong> คือ Signature</p><blockquote><em>SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</em></blockquote><p>ซึ่งส่วนนี้เราจะไม่ได้นำไปเเกะเป็น base64 แต่เราจะเอาไปเปรียบเทียบ หรือ verify โดยขึ้นอยู่กับ algoritm ที่ใช้ซึ่งระบุอยู่ใน Header โดยถ้าเป็น Algorithm แบบ Symetric เราจะสร้าง Signature ด้วยท่าแบบด้านล่าง แล้วเอาไปเข้า Base64 และเปรียบเทียบกับ Signature เดิม ถ้าเท่ากันก็ผ่าน</p><pre>HMACSHA256(<br>  base64UrlEncode(header) + &quot;.&quot; + base64UrlEncode(payload),<br>  your-256-bit-secret<br>)</pre><p>ส่วนถ้าเป็น Asymetric จะใช้เป็นการ verify signature แทน ซึ่งการ verify signature หากต้องการ ศึกษาเพิ่มเติมสามารถอ่านจากแหล่งเหล่านี้ได้เลย</p><ul><li><a href="https://auth0.com/docs/secure/tokens/json-web-tokens/validate-json-web-tokens">Validate JSON Web Tokens</a></li><li><a href="https://jwt.io/introduction">JSON Web Token Introduction - jwt.io</a></li></ul><p><strong>เพิ่มเติม ข้อสังเกตุของ JWT</strong></p><p>เนื่องจากข้อมูลที่อยู่ใน Payload เป็นเพียงการเอา JSON ไป encode ด้วย base64 เท่านั้น ข้อมูลที่เป็น sensitive data ถ้าจะใส่เข้าไปควร encrypt ไว้ด้วย และขนาดของ Payload ไม่ควรเยอะเกินไปเพราะส่งผลให้ Token มีขนาดใหญ่ตามไปด้วย</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b8f7abd2926a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mock Http Server for load test scenario API timeout with Javascript]]></title>
            <link>https://medium.com/@pumsdev/mock-http-server-for-load-test-scenario-api-timeout-with-javascript-0b0d1527cd05?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/0b0d1527cd05</guid>
            <category><![CDATA[mock-http-server]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[loadtest]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Wed, 30 Apr 2025 01:43:14 GMT</pubDate>
            <atom:updated>2025-04-30T01:48:20.918Z</atom:updated>
            <content:encoded><![CDATA[<p>มาสร้าง mock http server ไว้ใช้สำหรับยิงทดสอบ scenario API timeout ด้วย Javascript กัน</p><p>เริ่มต้นจากการ initial project ด้วย npm กันก่อน</p><pre>mkdir test-mock-server<br>cd test-mock-server<br>npm init</pre><p>จากนั้นติดตั้ง express.js ซึ่งเป็น web application framework</p><pre>npm install express</pre><p>ต่อมาสร้าง file สำหรับ javascript application อย่างง่ายของเรากัน</p><pre>// test-timeout.js<br>// Express Timeout Test Server<br>const express = require(&#39;express&#39;);<br>const app = express();<br>const PORT = 10443;<br><br>// Middleware to parse JSON body<br>app.use(express.json());<br><br>// Default route - responds immediately<br><br>// POST endpoint with timeout<br>app.post(&#39;/&#39;, (req, res) =&gt; {<br>  const delay = 120000;<br>  <br>  console.log(`POST request received, will respond after ${delay}ms`);<br>  <br>  setTimeout(() =&gt; {<br>    res.json({<br>      message: `POST response after ${delay}ms delay`,<br>      receivedData: req.body,<br>      timestamp: new Date().toISOString()<br>    });<br>  }, delay);<br>});<br><br>// Start the server<br>app.listen(PORT, () =&gt; {<br>  console.log(`Timeout test server running on port ${PORT}`);<br>});</pre><p>อธิบาย code เพิ่มเติม</p><p>PORT คือ http server port ที่เราอยากให้ server เปิดขึ้นตอนที่ running</p><p>app.post(&#39;/&#39; จะเป็นส่วนของการ config http method และ endpoint สามารถปรับแก้เป็น endpoint อื่น ๆ ตามที่เราต้องการได้ เผื่อว่าในฝั่ง client ที่เรียกมาที่ตัว mock http server ตัวนี้มี endpoint อื่น ๆ ที่กำหนดไว้อยู่แล้ว</p><p>const delay = 1200000; เป็นการตั้ง delay ให้กับการทำงานของ function นี้ โดยจากใน code ตัวอย่างจะ delay 2 นาที หรือก็คือ 120,000 วินาที</p><p>หลังจากนั้น save file js ของเรา โดยผมจะ save เป็น test-timeout.js</p><p>จากนั้น run js file ของเราด้วย node ผ่าน command</p><pre>node test-timeout.js</pre><p>เท่านี้ mock http server ของเราก็พร้อมใช้งานแล้ว</p><p>จากนั้นทดสอบด้วย curl</p><pre>curl -X POST localhost:10443/</pre><p>ผลลัพธ์จะออกกมาหน้าตาประมาณนี้ เป็นตาม log ที่เราตั้งไว้</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7lt7bWDKXzvsHoOxl4KNcQ.png" /></figure><p>เพียงเท่านี้เราก็จะมี mock http server เพื่อไว้ใช้ ทดสอบเราสำหรับ timeout แล้ว</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0b0d1527cd05" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cynefin Framework]]></title>
            <link>https://medium.com/@pumsdev/cynefin-framework-557d7890f9ab?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/557d7890f9ab</guid>
            <category><![CDATA[cynefin-framework]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Sun, 13 Apr 2025 05:51:41 GMT</pubDate>
            <atom:updated>2025-04-13T05:51:41.124Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Hse3ccfaiJASpuRk3M9rKg.png" /></figure><p>ในบทนำของหนังสือ <strong>7 Rules Positive Productive Change</strong> พูดถึงการตัดสินใจในการแก้ปัญหาบนสถานะการณ์ต่าง ๆ ที่มีความซับซ้อนต่างกัน โดยได้อ้างอิงถึง Cynefin Framework ขึ้นมาและน่าสนใจมากเลยอยากจะมาแชร์สิ่งนี้ต่อ</p><p>Cynefin Framework (คัน-นาฟ-อิน) คือ decision making framework ที่สร้างโดย Dave Snowden โดยใน framework ของเค้าได้แบ่งความซับซ้อนของปัญหาโดยดูจากความสัมพันธ์ของเหตุและผล ออกเป็น 5 แบบคือ</p><p><strong>1.Obvious, Simple</strong> ⇒ ชัดแจ้ง คือมีวิธีการที่ดีในการแก้ปัญหาแบบนี้อยู่แล้วเเละสามารถนำไปใช้ได้เลย วิธีการคิดเมื่ออยู่ในปัญหาที่เป็น Simple คือ ใช้การตัดสินใจจากการแบ่ง Categorize ของปัญหาก่อนแล้วค่อยแล้วตัดสินใจตามวิธีการของ Categorize นั้น ๆ ที่เคยมีผลลัพธ์ที่แน่นอนปรากฏให้เห็นมาแล้ว</p><p><strong>2. Complicated</strong> ⇒ คือ ยังไม่มีหลักฐานชัดเจนเกี่ยวกับวิธีการในการแก้ปัญหาต้องใช้การวิเคราห์เพิ่มเติมก่อนจะแก้ปัญหา หรือหาผู้เชี่ยวชาญมาช่วยคิด วิธีคิดเมื่ออยู่ในปัญหาแบบ Complicated คือ ใช้การตัดสินใจด้วยการ Analyze หาข้อมูลเพิ่มเติมให้เพียงพอ หรือหาคนที่เชี่ยวชาญมาช่วยหาทางออกที่เหมาะสม</p><p><strong>3. Complex</strong> ⇒ สถานะการณ์ที่ต้องทดลองเสาะหาทางออกใหม่ ๆ เพื่อเเก้ปัญหานี้ วิธีคิดในเมื่ออยู่ในปัญหาแบบ Complex คือ คิดหาวิธีการขึ้นมาใหม่ๆ มาแล้วอาจจะทดสอบดูก่อนที่จะตัดสินใจว่าจะเลือกใช้วิธีการไหน</p><p><strong>4. Chaotic </strong>⇒ ทำยังไก็ได้ กำจัดความวุ่นวายออกไปก่อน วิธีการคิดเมื่ออยู่ในปัญหาแบบ Chaotic คือ การตัดสินใจหาการกระทำบางอย่างที่น่าจะทำให้ความวุ่นวายลดลง หรือควบคุมได้ง่ายขึ้นมาก่อนเป็นอันดับเเรก หลังจากที่ความวุ่นวายสงบลงแล้วบางที่ปัญหาของเราอาจจะกลายเป็น Simple ไปเลยก็ได้</p><p><strong>5. Disorder</strong> ⇒ ไม่รู้ว่าสิ่งที่กำลังเจออยู่นี้คืออะไร สิ่งที่ต้องทำคือระบุให้ได้ก่อนว่าคืออะไร แล้วค่อยคิดถึงความซับซ้อนอีกที</p><p>สำหรับผู้ที่สนใจอยากได้ข้อมูลเพิ่มเติมสามารถดูได้จาก reference ด้านล่างต่อได้ครับ …</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FN7oz366X0-8%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DN7oz366X0-8&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FN7oz366X0-8%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/65a988fcad6afbb2e04a033ada57ba0e/href">https://medium.com/media/65a988fcad6afbb2e04a033ada57ba0e/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F_waoADNcaBU%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D_waoADNcaBU&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F_waoADNcaBU%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/354ed897b985aa8dcff0f5709d8e61e5/href">https://medium.com/media/354ed897b985aa8dcff0f5709d8e61e5/href</a></iframe><p><a href="https://hbr.org/2007/11/a-leaders-framework-for-decision-making">A Leader&#39;s Framework for Decision Making</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=557d7890f9ab" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[มาใช้ FNM ในการจัดการ Node Version กันเถอะ]]></title>
            <link>https://medium.com/@pumsdev/%E0%B8%A1%E0%B8%B2%E0%B9%83%E0%B8%8A%E0%B9%89-fnm-%E0%B9%83%E0%B8%99%E0%B8%81%E0%B8%B2%E0%B8%A3%E0%B8%88%E0%B8%B1%E0%B8%94%E0%B8%81%E0%B8%B2%E0%B8%A3-node-version-%E0%B8%81%E0%B8%B1%E0%B8%99%E0%B9%80%E0%B8%96%E0%B8%AD%E0%B8%B0-b1d7e74a6e18?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/b1d7e74a6e18</guid>
            <category><![CDATA[fnm]]></category>
            <category><![CDATA[node-version-manager]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Sun, 06 Apr 2025 03:54:14 GMT</pubDate>
            <atom:updated>2025-04-06T03:54:14.757Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bvSDwI1d1pqibLqWc9HTNA.png" /></figure><p>ในการใช้งาน Node JS บางครั้งเราจะเจอปัญหาว่าในแต่ละ Project ที่นั้นใช้ Node version ที่แตกต่างกัน พอสลับ project ก็อาจจะ run ไม่ได้เนื่องจาก Node version ที่เครื่องใช้อยู่ใหม่กว่า version ที่ project ที่เราพึ่งสลับไป เลยอาจจะต้องมาคอยลบแล้วก็ติดตั้ง Node JS ใหม่ให้ตรงกับที่ project ใช้งานอยู่</p><h4>ปัญหาเหล่านี้จะหมดไปถ้าเราใช้ Node manager</h4><p>Node manager คือ เครื่องมือที่เกิดมาช่วยให้การจัดการ version ของ Node JS นั้นง่ายเเละรวดเร็วมากขึ้นเพียงไม่กี่ command ก็ได้ Node version ที่ต้องการ พร้อมสำหรับการ run Project เราอย่างง่ายดาย</p><h4>เร็วกว่า NVM ยังไง</h4><p>เครื่องมือที่มีการใช้งานแพร่หลายในการทำ Node manager ชื่อแรกที่จะขึ้นมาคือ NVM (Node Version Manager) ซึ่งถูกเขียนขึ้นด้วย Bash script ในขณะที่ FNM (Fast Node Manager) นั้นถูกเขียนขึ้นด้วย Rust จึงมีข้อได้เปรียบกว่า NVM หลายข้อตั้งแต่พื้นฐานของภาษาที่สามารถ compiled ไปเป็น machine code ก่อนได้ รวมไปจนถึงความสามารถในการ optimization ต่างๆ เช่น การทำ parallel เป็นต้น</p><h4>การติดตั้ง</h4><p>การติดตั้งสามารถทำได้หลายวิธีโดยอ้างอิงจาก <a href="https://github.com/Schniz/fnm?tab=readme-ov-file">document</a> หลักได้เลย ส่วนผมจะใช้ brew ในการติดตั้งเพื่อความง่าย โดยเริ่มจาก</p><pre>brew install fnm</pre><p>จากนั้นก็ต้องเพิ่ม command ด้านล่างนี้เข้าไปใน .zshrc</p><pre>eval &quot;$(fnm env --use-on-cd --shell zsh)&quot;</pre><p>ทดสอบใช้งานด้วยการลอง fnm -V เพื่อตรวจสอบ version ของ fnm ถ้ามีการตอบกลับมาแสดงว่าใช้งานได้แล้ว</p><h4>การใช้งาน</h4><p>การค้นหาว่า Node version ปัจจุบันมี version ไหนอยู่บ้างสามารถทำได้โดยใช้</p><pre>fnm list-remote<br># ถ้าต้องการ version LTS สามารถใส่ --lts เพิ่มเข้าไป<br>fnm list-remote --lts</pre><p>การติดตั้ง Node JS</p><pre>fnm install v22.13.0<br># ถ้าต้องการติดตั้ง version ที่เป็น latest LTS<br>fnm install --lts</pre><p>หลังจากติดตั้ง เราสามารถเรียกดู Node version ที่ติดตั้งไว้ในเครื่องโดยใช้</p><pre>fnm list</pre><p>สั่งให้ fnm ตั้งค่า Node version เป็นตัวที่เราต้องการโดยใช้คำสั่ง</p><pre>fnm use v22.13.0</pre><p>จากนั้นลองเรียกดู version ของ Node โดยใช้ node -v เราก็จะได้ version เป็น v22.13.0 เป็นที่เรียบร้อย</p><p>สามารถดู Node version ปัจจุบันของ fnm ได้ด้วย command</p><pre>fnm current</pre><p>เท่านี้การปรับ Node version ก็เป็นเรื่องง่ายดายแล้ว แต่เดี๋ยวก่อน เราลองไปเปิด terminal ใหม่อีกสักตัวนึงแล้วลอง node -v ดูสิ๊ว่าจะได้ version อะไร ปรากฏว่าที่ terminal อื่น ๆ จะได้เป็น version ที่เป็น Node ที่มาจาก system หรืออาจจะไม่เจอเลย กรณีที่ไม่เคยลง Node มาก่อน สาเหตุที่เป็นแบบนี้เพราะว่า การเรียกใช้ fnm use … นั้นจะมีผมเฉพาะ session ของ terminal ที่เราเปิดอยู่เท่านั้น ถ้าปิด terminal ไปหรือเปิด terminal ใหม่ระบบจะไปหาตัว default มาแทน</p><p>ดังนั้นในเครื่องเราก็ควรตั้งค่า version ที่เป็น default ไว้ด้วย โดยใช้ command</p><pre>fnm install v22.14.0<br>fnm default v22.14.0</pre><p>เพียงเท่านี้ เราก็จะมีทั้ง version default (v22.14.0) และ version ที่เราตั้งค่าไว้ที่ terminal เรา (v22.13.0) ซึ่งถ้าเราเปิด terminal ใหม่ Node version ของ terminal นั้นก็จะเป็นค่า default (v22.14.0)</p><p>command อื่น ๆ สามารถดูได้จาก <a href="https://github.com/Schniz/fnm/blob/master/docs/commands.md">document</a> หรือ fnm -h ก็ได้</p><p>เพียงเท่านี้ ชีวิตเราก็จะง่ายขึ้นเพียงเลือกใช้ tools ที่เหมาะสมกับการใช้งานของเรา</p><p>Reference</p><ul><li><a href="https://github.com/Schniz/fnm">GitHub - Schniz/fnm: 🚀 Fast and simple Node.js version manager, built in Rust</a></li><li><a href="https://github.com/nvm-sh/nvm/tree/master">GitHub - nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b1d7e74a6e18" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kubernetes Volumes Mount Propagation]]></title>
            <link>https://medium.com/@pumsdev/kubernetes-volumes-mount-propagation-3b2f360b2492?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/3b2f360b2492</guid>
            <category><![CDATA[volume-mounting]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Fri, 04 Apr 2025 02:52:51 GMT</pubDate>
            <atom:updated>2025-04-04T02:52:51.262Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KjShQdgkbk0FD3JZjHp5gw.png" /></figure><p>ในการใช้งาน Kubernetes นั้นมักมีโจทย์ที่หลากหลายในเรื่องของการเก็บข้อมูลของ Pod บางครั้งเราอาจจำเป็นต้องเก็บข้อมูลที่ถูกสร้างด้วย Pod นั้นไว้ถาวรหรือบางครั้งก็จำเป็นที่จะต้องใช้ข้อมูลร่วมกับ Pod อื่น ๆ หรือในบางครั้งก็อยากให้ ทุก Pod ที่เกิดขึ้นมาใน Kubernetes นั้นสามารถเข้าถึงข้อมูลที่ path เดิมได้เสมอเเม้ Pod จะถูกสร้างใหม่ก็ตาม ในบทความนี้จะพาไปลงลึกในเรื่อง Mount Propagation ซึ่งเป็น Feature ที่อาจจะไม่ได้ถูกพูดถึงและไม่ค่อยเจอในการใช้งานปกติมากนัก แต่มันก็เป็นส่ิงที่มีอยู่ใน Kubernetes Volumes</p><p>Volumes ที่ได้ใช้งานหลักๆ ใน Kubernetes นั้นสามารถแบ่งย่อยได้เป็น 2 ประเภท</p><ul><li><strong>Ephemeral Volume</strong> หรือ volume ชั่วคราวซึ่งจะมีอายุเท่ากับ Pod ตัวอย่างเช่น Configmaps, Secret, emptyDir.</li><li><strong>Persistent Volumes</strong> เป็น volume ที่อยู่ไปตลอด จนกว่าจะโดนทับ หรือมีรอบมาลบ volume นั้นๆ ออกไป ตัวอย่างเช่น hostPath, local (local storage device that mounted on nodes), nfs.</li></ul><p>ทำไมถึงพูดถึงด้านบนก่อน ก็เพราะว่าในบางครั้งเราอาจจะจำเป็นต้องแชร์ Persistent Volume ที่เป็น hostPath หรือ local ร่วมกันกับ Pod อื่นที่อยู่ใน Host เดียวกัน ในจุดนี้เเหละที่เราอาจจะต้องการให้ Mount Propagation จะเข้ามาช่วยในการควบคุมการมองเห็นข้อมูลที่จะ mount จาก host เข้าไปยัง Pod</p><p><strong>Mount Propagation Mode</strong></p><ul><li>None ( default ) =&gt; โดย default mode จะเป็น private volume ที่สร้างมาและมองเห็นเฉพาะที่ pod ที่สร้างเท่านั้น</li><li>HostToContainer =&gt; เป็นการ mount ข้อมูลจาก host ไปทับแทนที่ข้อมูลที่ path ของ container เลย (ถ้ามีอยู่ก่อนหน้า)</li><li>Bidirectional =&gt; การ mount 2 ทาง ก็คือ merge รวม data ทั้งใน container และ host เข้าด้วยกัน แต่มีข้อจำกัดคือ ตัว container ต้องมีสิทธิ์ในการเข้าถึง host files ด้วย</li></ul><p>ถ้าหากต้องการดูตัวอย่าง configuration สามารถอ้างอิงจากใน Document หลักของ Kubernetes ได้เลยครับ</p><p><a href="https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation">https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b2f360b2492" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Encoding vs Encryption vs Hashing เบื้องต้น]]></title>
            <link>https://medium.com/@pumsdev/encoding-vs-encryption-vs-hashing-%E0%B9%80%E0%B8%9A%E0%B8%B7%E0%B9%89%E0%B8%AD%E0%B8%87%E0%B8%95%E0%B9%89%E0%B8%99-f20a88a5111c?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/f20a88a5111c</guid>
            <category><![CDATA[hashing]]></category>
            <category><![CDATA[encoding]]></category>
            <category><![CDATA[encryption]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Wed, 19 Mar 2025 16:31:01 GMT</pubDate>
            <atom:updated>2025-03-20T15:47:48.506Z</atom:updated>
            <content:encoded><![CDATA[<p>3 สิ่งนี้คือกระบวนการในการเปลี่ยนรูปแบบของข้อมูล โดยมีจุดประสงค์ที่แตกต่างกัน</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*krLCL6_g3b6X9eCPxAkVTw.png" /></figure><h3>Encoding</h3><h4>จุดประสงค์</h4><ul><li>เพื่อเปลี่ยนรูปแบบข้อมูลไปเป็นอีกรูปแบบนึงที่เป็น เพื่อให้สามารถจัดเก็บหรือส่งต่อได้ง่ายขึ้น</li><li>ไม่จำเป็นต้องมี secret key ในการ encoding</li><li>ง่ายต้องการแปลงข้อมูลกลับ (ถ้ารู้ว่า Encoding ไปด้วยเทคนิคไหน)</li></ul><h4>ตัวอย่างการ Encoding</h4><ul><li>URL Encoding เพื่อปรับเปลี่ยนอักษรพิเศษให้อยู่บน URL ได้</li><li>Base64 Encoding เปลี่ยน Binary data ไปเป็น ASCII text</li><li>UTF-8 ตัวอย่างที่เราคุ้นเคยก็คือเปลี่ยนการเปลี่ยนข้อมูลภาษาไทยของเราเป็น Unicode characters เพื่อบันทึกลง Database หรือ เพื่อการเเสดงผลภาษาไทยให้ไม่ผิดเพี้ยนไป</li></ul><h3>Encryption</h3><h4>จุดประสงค์</h4><ul><li>เพื่อเปลี่ยนข้อมูลไปเป็นรูปแบบที่ไม่สามารถอ่านทำความเข้าใจได้</li><li>ต้องมี Key ในการ encrypt และ decrypt</li><li>เพื่อรักษาข้อมูลที่เป็นความลับ ให้ถูกเปิดอ่านได้เฉพาะคนที่กำหนดเท่านั้น</li><li>คนที่สามารถเปิดอ่านข้อมูลได้จะต้องมี Key ที่ถูกต้องเท่านั้น</li></ul><h4>ตัวอย่างการ Encryption</h4><ul><li>AES (symmetric encryption)</li><li>RSA (asymmetric encryption)</li><li>TLS/SSL ตัวนี้เราจะเห็นบ่อยในการทำ security สำหรับเว็บ browser</li></ul><h3>Hashing</h3><h4>จุดประสงค์</h4><ul><li>เพื่อสร้าง “ตัวแทน” ของข้อมูล</li><li>One-way ไม่สามารถแปลง “ตัวแทน” กลับมาเป็นข้อมูลเดิมได้</li><li>เพื่อให้ได้ข้อมูลที่มีขนาดคงที่</li></ul><h4>ตัวอย่างการ Hashing</h4><ul><li>SHA-256</li><li>Bcrypt จะใช้บ่อยสำหรับการเก็บ password ลงใน database</li><li>MD5 จะเจอในการทำ checksums เพื่อตรวจสอบความสมบูรณ์ของข้อมูลที่ส่งผ่าน internet</li></ul><h3>Reference</h3><ul><li><a href="https://auth0.com/blog/encoding-encryption-hashing/">Encoding, Encryption, and Hashing</a></li><li><a href="https://www.codecademy.com/article/hashing-encryption-encoding-obfuscation">Hashing vs. Encryption vs. Encoding vs. Obfuscation | Codecademy</a></li><li><a href="https://medium.com/swlh/the-difference-between-encoding-encryption-and-hashing-878c606a7aff">The Difference Between Encoding, Encryption, and Hashing.</a></li><li><a href="https://www.geeksforgeeks.org/encryption-encoding-hashing/">Encryption vs Encoding vs Hashing - GeeksforGeeks</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f20a88a5111c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[จดไว้สำหรับ Prompt Engineer]]></title>
            <link>https://medium.com/@pumsdev/%E0%B8%88%E0%B8%94%E0%B9%84%E0%B8%A7%E0%B9%89%E0%B8%AA%E0%B8%B3%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A-prompt-engineer-a564daa50983?source=rss-8d41a1cba321------2</link>
            <guid isPermaLink="false">https://medium.com/p/a564daa50983</guid>
            <category><![CDATA[prompt-engineer]]></category>
            <dc:creator><![CDATA[PumsDev]]></dc:creator>
            <pubDate>Wed, 12 Mar 2025 16:02:44 GMT</pubDate>
            <atom:updated>2025-03-14T06:38:17.164Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VVj1qu61KP_feQB5p-zCUQ.png" /></figure><p>Prompt engineer is the systematic design and development of prompts for LLMs</p><p>Techniques</p><ul><li>Domain expertise</li><li>Bias mitigation</li><li>Framing</li><li>Task specification</li></ul><p>Approachs</p><ul><li>Interview pattern</li><li>Cognitive building</li><li>Tree-of-Thoughs</li><li>Chain-of-Thoughs</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a564daa50983" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>