Whatever has happened is always good.
บันทึกการเดินทาง ญาจาง (Nha Trang, Vietnam) 2024

พอดีมีโอกาส ได้ไปเที่ยวญาจาง ด้วยความที่เห็นรีวิว กันเยอะมากจาก YouTuber .. ว่าเป็นเมืองที่สวยงาม Seafood ราคาถูก ค่าที่พักถูก ค่าครองชีพถูก
จากการที่ไปเอง เมืองเค้าเป็นระเบียบ สวยงามดีมากครับ .. เหมาะสำหรับคนมาพักผ่อน และทำกิจกรรมต่างๆ คนที่ชอบทะเล .. ที่นิยมกัน ก็จะไปสวนสนุก ที่เกาะ VinPearl แล้วก็มีที่เที่ยวที่น่าสนใจ ทางศาสนาและวัฒนธรรมโบราณ อื่นๆ
ตอนเย็น weekend จะมีนักท่องเที่ยว ทั้งชาวเวียดนามเอง เกาหลี รัสเซีย ฝรั่งอื่นๆ เยอะมากครับ (เด็กๆ เยอะมาก)มาเล่นน้ำกัน สนุกสนานดี คนเยอะมาก ในแต่ละจุด ผมว่ามีกว่า 10000 คนได้ หาดเค้าจะยาวมาก .. อีกฝั่งถนนก็จะเป็นที่พัก โรงแรม ร้านรวงต่างๆ ..
ชายหาดจะมีแม่ค้าหาบของปิ้งย่างขาย แต่ตอนผมไป ไม่เจอล็อบสเตอร์ แบบที่เค้ารีวิวกันนะ จะมีขายแต่ในร้านอาหาร ราคาก็ไม่ได้ถูกครับ เมืองท่องเที่ยว ราคาก็จะประมาณนี้ แต่ถ้าเข้าไปในย่าน ที่อยู่อาศัยของคนพื้นที่ ก็จะถูกลงเยอะ ..
การเดินทางที่นี่ จะสะดวกมากครับ จากสนามบินมา ก็นั่ง Airport (City) Bus มาได้ ราคา 65000VND ในเมืองก็เรียกใช้งาน grab ได้ .. รถไม่ติดมาก ไหลๆ สบายๆ มอไซค์ค่อนข้างเยอะ ตามสไตล์เวียดนาม
ด้าน Internet ที่นี่ก็จะเป็น 4G/4G+ นะครับ แต่บางที่ก็ยังเป็น 2G/3G อยู่ .. ตามโรงแรมที่พัก ก็จะมี Free WiFi ให้ใช้งาน แต่จะไม่เร็วเหมือนที่ไทยนะครับ แต่ก็พอใช้งานทั่วไปได้ .. ผมเองด้วยความที่ต้องใช้ Internet ตลอด ก็เลยเปิดใช้งาน Sim2Fly มาเลยจากไทยนะครับ เน้นชัวร์ ลงเครื่อง ก็ใช้งาน Internet ได้เลย ไม่ต้องลุ้น ว่าถ้ามาซื้อ SIM ที่นี่ใช้ จะใช้ค่ายไหนดี +___+
ด้านมืด ก็มีบ้างเรื่องการโกง การหลอกลวง ก็คือจะมีพวกขี่มอไซค์มาชวนไปอย่างว่านะครับ (ปั่มปั้ม) ก็อย่าหลงไปนะครับ .. อย่าเดินคนเดียว อย่าอยู่ในที่เปลี่ยว .. พยายามหาเพื่อนผู้หญิง เดินไปด้วย พวกนี้จะไม่กล้ามารบกวนครับ .. ถ้าไปชายล้วน ยังไงก็ต้องเจอ ไม่ว่าจะเป็นประเทศไทย ลาว จีน ก็มีหมดนะครับ ก็ต้อง Say “No” อย่างเดียวครับ .. แต่คนส่วนใหญ่ที่นี่จะดีครับ ช่วยเหลือนักท่องเที่ยวดีมาก
สำหรับผมเดินทางในเวียดนาม มาหลายปีแล้ว หลายแบบ หลายเมือง .. คิดว่าเวียดนามเค้าเจริญเร็วมาก .. เป็นเมืองแห่งความหวัง ของ Asia เมืองนึงจริงๆ .. ลูกเด็กเล็กแดงเค้าเยอะมาก คนหนุ่มสาว วัยแรงงานเยอะมาก .. ต่อไปไม่นานจะเป็นเมืองพัฒนา แบบสิงคโปร์ (SG) ได้แน่ๆ ครับ ตอนนี้ ก็ใกล้เคียงแล้ว ..
ครบรอบ 1 เดือน ในการกลับมาทำงานอยู่บ้าน
ครบรอบ 1 เดือน ในการย้ายถิ่นฐานจาก กทม. ที่อยู่เป็นหลักมาตั้งแต่สมัยเรียน เพื่อกลับมา WFH ที่บ้าน (จันทบุรี) เลยเขียน blog ไว้สักหน่อยครับ บันทึกความทรงจำ มะก่อนผมก็ชอบเขียน diary ..
เห็นมี ดราม่า ถกเถียงกัน ว่าเข้า office กับ WFH แบบไหนดีกว่ากัน .. ส่วนตัวคิดว่า แล้วแต่ลักษณะงานที่ทำด้วยครับ บางงานก็ต้อง on site 100% .. บางงาน ถ้ามันไม่จำเป็นต้องเข้า office การให้ WFH ก็ดีกว่าในทุกด้านครับ ..
ทั้งนี้ทั้งนั้น ขึ้นอยู่กับตัวบุคคลด้วยครับ ว่ามีความรับผิดชอบ มีความกระตือรือร้น ในการทำงานขนาดไหน tools ต่างๆ เกี่ยวกับ Project Management, Interaction ก็เป็นตัวช่วยทางนึงครับ ..
การกลับมาอยู่บ้าน ก็มีแต่ข้อดีซะเป็นส่วนใหญ่ ข้อเสียก็มีบ้างนิดหน่อย เช่นการเดินทาง ต้องมีรถส่วนตัว ไม่งั้นจะไปไหนมาไหนลำบาก ..
บ้านผมโชคดีหน่อย ที่มีสัญญาณมือถือ เข้าถึงทุกค่าย ทำให้สามารถเลือกใช้ Net SIM ในการทำงานได้ ..
อยู่ในรัศมี ที่สามารถสั่ง 7Deli มาส่งได้ ทำให้สะดวกมาก ในการสั่งกาแฟ จาก All Cafe เพราะถ้าออกไปซื้อเอง คงไม่คุ้มค่าน้ำมันรถ (ผมไม่มีมอไซค์ ปั่นจักรยานก็ไม่ไหวไกลไป รถ Jeep ก็ไม่ไหว ค่าน้ำมันแพงเกิน)
อยู่บ้านวันหยุดได้ทำพวก hobby ที่ชอบ ที่อยากทำได้ เพราะมีพื้นที่และอยู่ห่างไกลจากบ้านอื่น ทำให้เสียงไม่ไปรบกวนใคร ใช้สว่าน ใช้ค้อนได้ สบายใจ เหมือนเรามีห้องทดลอง มี Workshop ส่วนตัว 🙂
Life in 2024
สวัสดีครับทุกท่าน ไม่ได้ update blog มานานมากครับ ตั้งแต่ปี 2023 เกือบจะครบปี เพราะว่าช่วงนั้น จะวุ่นๆ มากด้วย ..
ตอนนี้ ตัดสินใจ คืนคอนโด ที่เช่า มาหลายปี กลับมา WFH อยู่ต่างจังหวัดครับ .. จะได้ลดรายจ่าย ลงไปได้บ้าง จะได้เอาไปโปะบ้าน-คอนโด ให้หมดไวๆ เพราะดอกเบี้ย ช่วงที่ผ่านมาแพงเหลือเกิน (6.xx) ..
อยากกลับมาดูแลพ่อแม่ด้วยครับ เค้าอายุมากแล้ว .. อยากสร้างบ้าน ที่อยู่สบายๆ เหมาะสำหรับคนสูงวัย สักหลัง แนวๆ แบบบ้านญี่ปุ่น (มูจิ) จะได้สะดวก ในการใช้ชีวิตอยู่อาศัย .. กำลังหาแบบ หรือคนออกแบบให้อยู่ครับ ใครมีก็ช่วยแนะนำมาได้ เอางบไม่สูงมาก เพราะแถวนี้ ค่าช่าง ค่าอะไร ราคาค่อนข้างสูง ..
อยู่บ้าน ก็พอมีเวลา ดูแลบ้าน ด้วยครับ ไม่งั้นไม่มีคนอยู่ จะโทรมเร็วมาก .. ได้ปลูกต้นไม้ ได้ทำอะไรที่ชอบ เพราะมีพื้นที่
แต่ก็ไม่รู้ว่าเค้าจะเรียกเข้า office เมื่อไรเหมือนกัน อาจจะต้องไปกลับๆ แบบเพื่อนๆ บางคน บางบริษัท ถ้าแบบนั้นก็ไม่ไหว เหนื่อยกว่าอยู่ กทม. อีก .. บางคนบินไปกลับตลอด กลายเป็นค่าใช้จ่ายสูงกว่าเดิม แต่ก็ยังดีที่บินได้ ไม่เหนื่อยเท่านั่งรถไปกลับ ..
ช่วงนี้ น่าจะพอมีเวลามากขึ้น เพราะไม่ต้องเสียเวลาเดินทางเยอะๆ ไม่ต้องเหนื่อยเดินทาง แบบตอนอยู่ กทม. ก็เลยคิดว่า จะมา update blog เรื่องราวต่างๆ บ่อยๆ หน่อย .. เพราะคิดว่าเรื่องบางอย่างที่ทำอยู่ อาจจะเป็นความรู้ เป็นประโยชน์ ให้คนอื่น ได้บ้าง .. เพราะเจอปัญหาใหม่ๆ ตลอดเวลา กับงานสาย tech ที่ทำอยู่ และพวก hobby ที่ชอบ .. จะทำเป็น YouTube ก็คงไม่ไหว ไม่มีเวลาขนาดนั้น ..
ถ้าใครมีงานพวกเป็นวิทยากร สอนพวก IoT, Robotics, AI, Software Deployment หรืออะไรแนวๆ นี้ ก็ติดต่อมาได้ครับ ถ้าอยู่ใกล้ๆ มารับผมก็พอ เพราะผมไม่สะดวกเดินทางเอง (ไม่มีรถ) .. ไม่คิดเงินครับ (เสาร์-อาทิตย์ วันธรรมดาผมทำงาน) .. ได้แบ่งปันความรู้ ได้แลกเปลี่ยนความรู้ กับคนอื่นบ้างก็ดี .. ไม่งั้นบางทีอยู่บ้านเฉยๆ ก็เบื่อได้เหมือนกัน ..
ปล. เศรษฐกิจแบบนี้ ประหยัดไว้ดีสุด .. หารายได้เพิ่มยากมาก .. แถมปีนี้ น่าจะมีการ layoff กันเยอะทั่วโลก ..
How to Install Magento 2.4.6 on Ubuntu 22.04


สวัสดีครับ วันนี้ จะมาพูดถึงการ Install Magento 2 (Adobe Commerce) นะครับ
เป็น eCommerce Software ตัวนึง ที่น่าใช้มากๆ มาพร้อมกับ feature ที่ครบครัน ..
รายละเอียดเพิ่มเติม https://business.adobe.com/products/magento/magento-commerce.html
รายละเอียดของ System ประมาณนี้ครับ
- OS: Ubuntu 22.04
- Magento: 2.4.6-p2 (Open Source)
- Apache: 2.4.x
- PHP: 8.1.2
- Composer: 2.2.6
- MySQL: 8.0.34
- Elasticsearch: 7.17.13
มาเริ่มกันเลย ..
Step 1: Update Operating System
# apt update && apt upgrade -y
Step 2: Install Apache Web Server
# apt install apache2
Step 3: Install PHP and PHP extensions
# apt install php php-common libapache2-mod-php php-cli php-fpm php-mysql php-json php-opcache php-gmp php-curl php-intl php-mbstring php-xmlrpc php-gd php-xml php-zip php-soap php-bcmath php-apcu
Modify php.ini file (/etc/php/8.1/cli/php.ini)
memory_limit = 1GB
upload_max_filesize = 256M
zlib.output_compression = On
max_execution_time = 600
max_input_time = 900
date.timezone = Asia/Bangkok
Step 4: Install the MySQL server
# apt install mysql-server
# mysql_secure_installation
Step 5: Create a Magento Database
# mysql -u root -p
mysql> CREATE DATABASE magento;
mysql> CREATE USER 'magento'@'localhost' IDENTIFIED BY 'Str0ngPa$$w0rd';
mysql> GRANT ALL PRIVILEGES ON magento.* TO 'magento'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;
Step 6. Install Elasticsearch
# curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
# echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
# apt update && apt install elasticsearch
# systemctl start elasticsearch
# systemctl enable elasticsearch
Verify Elasticsearch
curl -X GET "localhost:9200"
{
"name" : "magento-01",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "-Or5raP6T5uEG3bUG1JYHw",
"version" : {
"number" : "7.17.13",
"build_flavor" : "default",
"build_type" : "deb",
"build_hash" : "2b211dbb8bfdecaf7f5b44d356bdfe54b1050c13",
"build_date" : "2023-08-31T17:33:19.958690787Z",
"build_snapshot" : false,
"lucene_version" : "8.11.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
Step 7: Install Composer
# apt install composer
Step 8: Install Magento
Goto Magento Marketplace https://marketplace.magento.com/
Get Access Keys
My profile > Marketplace > My products > Access Keys
# composer global config http-basic.repo.magento.com Your-Public-Key Your-Private-Key
# composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition=2.4.6-p2 /var/www/magento
# cd /var/www/magento
# bin/magento setup:install \
--base-url=http://your-domain.com \
--db-host=localhost \
--db-name=magento \
--db-user=magento \
--db-password=Str0ngPa$$w0rd \
--admin-firstname=Admin \
--admin-lastname=User \
--admin-email=admin@your-domain.com \
--admin-user=admin \
--admin-password=admin123 \
--language=en_US \
--currency=USD \
--timezone=Asia/Bangkok \
--use-rewrites=1
# chown -R www-data: /var/www/magento
Step 9: Setup Cron jobs
# sudo -u www-data bin/magento cron:install
Step 10: Configure Apache for Magento
Create /etc/apache2/sites-available/magento.conf
<VirtualHost *:80>
ServerAdmin admin@your_domain.com
DocumentRoot /var/www/magento/pub
ServerName your_domain.com
ServerAlias www.your_domain.com
<Directory /var/www/magento>
AllowOverride All
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# a2ensite magento.conf
# a2enmod rewrite
# systemctl restart apache2
Step 11: Access your Magento installation
Store Front: http://your_domain.com
Admin Dashboard: http://your_domain.com/admin_xxxxxx
Step 12: Diasable 2FA
# bin/magento mod:dis Magento_AdminAdobeImsTwoFactorAuth Magento_TwoFactorAuth
# bin/magento setup:di:compile
Options: Tuning Magento
https://experienceleague.adobe.com/docs/commerce-operations/performance-best-practices/software.html
Setup IKEA VINDRIKTNING + WeMos D1 mini (Tasmota Firmware)
IoT 101 ของเรา วันนี้ จะมาพูดถึงวิธีการ Setup IKEA VINDRIKTNING + WeMos D1 mini (Tasmota Firmware) กันครับ
โดยมีขั้นตอนต่างๆ ประมาณนี้
1. เกาะ WiFi (AP): tasmota-XXXXXX-XXXX
2. เปิด Web Browser เข้า http://192.168.4.1
3. ทำการ Setup WiFi ที่ต้องการใช้งาน
4. จากนั้นเข้าผ่าน IP ใหม่ ของ Tasmota ที่ได้จาก WiFi ที่คุณใช้งาน
5. คลิก Configuration > Configure Module เลือก Generic (0) กด Save แล้วรอ Tasmota Restart
6. คลิกเข้า Configure Module อีกครั้ง
– PIN: D1 GPIO5 เลือก VINDRIKTNING
– PIN: D6 GPIO12 เลือก DHT11
กด Save แล้วรอ Tasmota Restart
7. เมื่อเรา config ถูกต้อง ก็จะได้ค่า Temperature, Humidity, Dew point และ PM 2.5 ตามรูปครับ
8. คลิก Configuration > Configure MQTT
– Host: mqtt.tono.tech
– Port: 1883
– Client: DVES_%06X (ค่า default)
– User: mqtt
– Password: XXXXXX
– Topic: tasmota_%06X (ค่า default)
* จากตัวอย่าง จะได้ค่า tasmota_1AB59B ตามเลข MAC Address ของตัว ESP8266
– Full Topic: %prefix%/%topic%/ (ค่า default)
กด Save แล้วรอ Tasmota Restart
9. คลิก Console จะเห็น Logs ที่ Tasmota ประมาณนี้ เป็นอันใช้ได้แล้ว
07:48:21.453 MQT: tele/tasmota_1AB59B/SENSOR = {"Time":"2023-03-21T07:48:21","DHT11":{"Temperature":24.7,"Humidity":72.0,"DewPoint":19.3},"VINDRIKTNING":{"PM2.5":7},"TempUnit":"C"}
10. Download IoT MQTT Panel จาก Play Store
11. เลือก Backup and Restore
จากนั้นเลือก RESTORE จาก pm25dashboard.json นี้ หรือจะเลือกสร้าง Dashboard ใหม่เองก็ได้
เปลี่ยนชื่อ topic เป์นของ Tasmota Device ของเรา
อย่างเช่นของผมจะเป็น tele/tasmota_1AB59B/SENSOR
pm25dashboard.json
{"dataVersion":120,"globalSettings":{"runInBackground":true,"appTheme":"dark"},"connections":[{"connectionName":"Tasmota PM2.5","clientId":"ub5bvpTlZA","host":"mqtt.tono.tech","port":1883,"connectionType":"tcp","username":"mqtt","password":"XXXXXX","connectionTimeout":30,"keepAlive":60,"autoConnect":true,"disableHostnameCheck":false,"caCertificateFileName":"","clientCertificateFileName":"","clientKeyFileName":"","sslClientPassword":"","enableWillMessage":false,"willTopic":"","willPayload":"","willRetain":false,"willQoS":0,"defaultDashboard":"dashboard_0","connectionId":"connection_0","notifyOnDisconnect":false}],"dashboards":[{"connectionId":"connection_0","dashboardId":"dashboard_0","dashboardName":"PM 2.5","dashboardPrefixTopic":"","lockPanels":true}],"panels":[{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_7","panelName":"WiFi Signal","type":"progress","mqttType":"sub","topic":"tele\/tasmota_1AB59B\/STATE","payloadMin":0,"payloadMax":100,"qos":0,"defaultColor":"success","dynamicColor":true,"firstColor":"danger","secondColor":"warning","thirdColor":"success","firstColorEnd":50,"secondColorEnd":80,"disableDashboardPrefix":true,"panelWidth":"100","panelMergeClass":"","showReceivedTimeStamp":true,"unit":"%","orientation":"horizontal","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.Wifi.RSSI","messageFactor":1,"step":1},{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_1","panelName":"Temp","type":"gauge","mqttType":"sub","topic":"tele\/tasmota_1AB59B\/SENSOR","payloadMin":0,"payloadMax":100,"qos":0,"disableDashboardPrefix":false,"panelWidth":"33","firstColor":"#009600","secondColor":"#fac800","thirdColor":"#ff5722","firstColorEnd":33.33,"secondColorEnd":66.67,"panelMergeClass":"","showReceivedTimeStamp":false,"unit":"°C","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.DHT11.Temperature","messageFactor":"","step":1},{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_2","panelName":"Humidity","type":"gauge","mqttType":"sub","topic":"tele\/tasmota_1AB59B\/SENSOR","payloadMin":0,"payloadMax":100,"qos":0,"disableDashboardPrefix":false,"panelWidth":"33","firstColor":"#009600","secondColor":"#fac800","thirdColor":"#ff5722","firstColorEnd":70,"secondColorEnd":100,"panelMergeClass":"","showReceivedTimeStamp":false,"unit":"%","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.DHT11.Humidity","messageFactor":1,"step":1},{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_3","panelName":"PM 2.5","type":"gauge","mqttType":"sub","topic":"tele\/tasmota_1AB59B\/SENSOR","payloadMin":0,"payloadMax":1000,"qos":0,"disableDashboardPrefix":false,"panelWidth":"33","firstColor":"#009600","secondColor":"#fac800","thirdColor":"#ff5722","firstColorEnd":35,"secondColorEnd":85,"panelMergeClass":"","showReceivedTimeStamp":false,"unit":"ug\/m3","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.VINDRIKTNING.['PM2.5']","messageFactor":1,"step":10},{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_0","panelName":"PM 2.5","type":"line-graph","mqttType":"sub","qos":0,"comboItemList":[{"topic":"tele\/tasmota_1AB59B\/SENSOR","label":"ug\/m3","color":"#7b00f5","showArea":true,"showPoints":true,"unit":"","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.VINDRIKTNING.['PM2.5']","messageFactor":1}],"disableDashboardPrefix":false,"panelWidth":"100","panelMergeClass":"","maxPersistence":50,"smoothCurve":true},{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_4","panelName":"Temp","type":"line-graph","mqttType":"sub","qos":0,"comboItemList":[{"topic":"tele\/tasmota_1AB59B\/SENSOR","label":"°C","color":"#99ff99","showArea":true,"showPoints":true,"unit":"°C","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.DHT11.Temperature","messageFactor":1}],"disableDashboardPrefix":false,"panelWidth":"100","panelMergeClass":"","maxPersistence":50,"smoothCurve":true},{"connectionId":"connection_0","dashboardId":"dashboard_0","panelId":"panel_5","panelName":"Humidity ","type":"line-graph","mqttType":"sub","qos":0,"comboItemList":[{"topic":"tele\/tasmota_1AB59B\/SENSOR","label":"%","color":"#007bf5","showArea":true,"showPoints":true,"unit":"%","enableNotification":false,"notificationFilter":"all","notificationMin":"","notificationMax":"","notificationValue":"","notificationRegex":"","notificationMessage":"","isJSONPayload":true,"jsonPath":"$.DHT11.Humidity","messageFactor":1}],"disableDashboardPrefix":false,"panelWidth":"100","panelMergeClass":"","maxPersistence":50,"smoothCurve":true}]}
12. ถ้าเสร็จเรียบร้อย เราก็จะได้ Dashboard ไว้ใช้งานแล้วครับ

* เพิ่มเติมครับ วิธีการ Hard Reset Tasmota กลับไปเป็นค่า default
1. ถอดสาย USB ออก 30s
2. เสียบสาย ถอดสาย สลับกัน 7 ครั้ง (ครั้งละไม่เกิน 5s)
เป็นอย่างไรกันบ้างครับ ไม่ยากเลยใช่ไหมครับ ในการทำ Dashboard ดีๆ ไว้ดูคุณภาพอากาศ สำหรับช่วง PM 2.5 ประเทศเรา เป็นผู้นำอันดับโลก แบบนี้ 🙂
ถ้าสงสัยหรืออยากสอบถามเพิ่มเติม comment ไว้ตรงนี้ หรือ add LINE ID: pornpasok เข้ามาพูดคุยกันได้ครับ ..
Filebeat Lightweight shipper for logs from k8s to Elasticsearch
สวัสดีครับ DevOps 101 ของเรา วันนี้ จะมาพูดถึง Filebeat นะครับ ..
Filebeat คืออะไร?
Filebeat = Lightweight shipper for logs
“Whether you’re collecting from security devices, cloud, containers, hosts, or OT, Filebeat helps you keep the simple things simple by offering a lightweight way to forward and centralize logs and files.”
ถ้าเอาแบบเข้าใจง่ายๆ ก็คือ ตัวกวาด logs จากที่ต่างๆ เข้ามาที่ centralize logs ในที่นี้ ผมจะพูดถึง k8s cluster (EKS) logs ไปเก็บที่ Elasticsearch ละกันนะครับ ..
จริงๆ ยังมี ตัวอื่นๆ อีก นะครับ ที่ทำงานคล้ายๆ Filebeat เช่น Fluentd, Fluentbit
ในเมื่อเราใช้ ELK Stack ใน Ecosystem ของเราแล้ว การที่เราอยาก search logs ต่างๆ ที่เกิดจาก container ของเรา ได้ง่ายสุด ก็คือการ ship logs จาก k8s cluster ของเรา ไปเก็บไว้บน Elasticsearch แล้วทำการ search ผ่าน Kibana ..
วิธีการ Install Filebeat ใน k8s cluster
ทำได้หลายวิธีครับ ในที่นี้ ผมจะใช้วิธีง่ายๆ ผ่าน kubectl ดังต่อไปนี้
1. Create Secret โดยใส่ค่าที่จำเป็น พวกนี้ลงไป
– ELASTICSEARCH_HOST = Endpoint ของ Elasticsearch เรา
– ELASTICSEARCH_PORT = Port ที่ Elasticsearch เราทำงานอยู่
– ELASTICSEARCH_USERNAME = Username ของ Elasticsearch
– ELASTICSEARCH_PASSWORD = Password ของ Elasticsearch
– ELASTIC_CLOUD_ID = Cloud ID ในกรณีที่เราใช้ผ่าน Cloud Service ของ Elastic.co
– ELASTIC_CLOUD_AUTH = Username:Password ของ Elasticsearch
filebeat-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: filebeat
namespace: kube-system
type: Opaque
data:
ELASTICSEARCH_HOST: aHR0cDovL2xvY2FsaG9zdA==
ELASTICSEARCH_PASSWORD:
ELASTICSEARCH_PORT: OTIwMA==
ELASTICSEARCH_USERNAME:
ELASTIC_CLOUD_AUTH:
ELASTIC_CLOUD_ID:
จากนั้น สั่ง create secret
kubectl create -f filebeat-secret.yaml
2. Create Deployment และ Resource อื่นๆ
filebeat-kubernetes.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: kube-system
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
# To enable hints based autodiscover, remove `filebeat.inputs` configuration and uncomment this:
filebeat.autodiscover:
providers:
- type: kubernetes
node: ${NODE_NAME}
hints.enabled: true
hints.default_config:
type: container
paths:
- /var/log/containers/*${data.kubernetes.container.id}.log
# Filter by container.name
# filebeat.autodiscover:
# providers:
# - type: kubernetes
# node: ${NODE_NAME}
# templates:
# - condition:
# contains:
# kubernetes.container.name: "container01"
# config:
# - type: container
# paths:
# - "/var/log/containers/*-${data.kubernetes.container.id}.log"
# - condition:
# contains:
# kubernetes.container.name: "container02"
# config:
# - type: container
# paths:
# - "/var/log/containers/*-${data.kubernetes.container.id}.log"
processors:
- add_cloud_metadata:
- add_host_metadata:
cloud.id: ${ELASTIC_CLOUD_ID}
cloud.auth: ${ELASTIC_CLOUD_AUTH}
output.elasticsearch:
hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
#index: "%{[fields.my_type]}-%{[agent.version]}-%{+yyyy.MM.dd}"
username: ${ELASTICSEARCH_USERNAME}
password: ${ELASTICSEARCH_PASSWORD}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: kube-system
labels:
k8s-app: filebeat
spec:
selector:
matchLabels:
k8s-app: filebeat
template:
metadata:
labels:
k8s-app: filebeat
spec:
serviceAccountName: filebeat
terminationGracePeriodSeconds: 30
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:8.6.2
args: ["-c", "/etc/filebeat.yml", "-e"]
env:
- name: ELASTICSEARCH_HOST
valueFrom:
secretKeyRef:
name: filebeat
key: ELASTICSEARCH_HOST
- name: ELASTICSEARCH_PORT
valueFrom:
secretKeyRef:
name: filebeat
key: ELASTICSEARCH_PORT
- name: ELASTICSEARCH_USERNAME
valueFrom:
secretKeyRef:
name: filebeat
key: ELASTICSEARCH_USERNAME
- name: ELASTICSEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: filebeat
key: ELASTICSEARCH_PASSWORD
- name: ELASTIC_CLOUD_ID
valueFrom:
secretKeyRef:
name: filebeat
key: ELASTIC_CLOUD_ID
- name: ELASTIC_CLOUD_AUTH
valueFrom:
secretKeyRef:
name: filebeat
key: ELASTIC_CLOUD_AUTH
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
runAsUser: 0
# If using Red Hat OpenShift uncomment this:
#privileged: true
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml
- name: data
mountPath: /usr/share/filebeat/data
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: config
configMap:
defaultMode: 0640
name: filebeat-config
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log
# data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart
- name: data
hostPath:
# When filebeat runs as non-root user, this directory needs to be writable by group (g+w).
path: /var/lib/filebeat-data
type: DirectoryOrCreate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: filebeat
subjects:
- kind: ServiceAccount
name: filebeat
namespace: kube-system
roleRef:
kind: ClusterRole
name: filebeat
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: filebeat
namespace: kube-system
subjects:
- kind: ServiceAccount
name: filebeat
namespace: kube-system
roleRef:
kind: Role
name: filebeat
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: filebeat-kubeadm-config
namespace: kube-system
subjects:
- kind: ServiceAccount
name: filebeat
namespace: kube-system
roleRef:
kind: Role
name: filebeat-kubeadm-config
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: filebeat
labels:
k8s-app: filebeat
rules:
- apiGroups: [""] # "" indicates the core API group
resources:
- namespaces
- pods
- nodes
verbs:
- get
- watch
- list
- apiGroups: ["apps"]
resources:
- replicasets
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources:
- jobs
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: filebeat
# should be the namespace where filebeat is running
namespace: kube-system
labels:
k8s-app: filebeat
rules:
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs: ["get", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: filebeat-kubeadm-config
namespace: kube-system
labels:
k8s-app: filebeat
rules:
- apiGroups: [""]
resources:
- configmaps
resourceNames:
- kubeadm-config
verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: filebeat
namespace: kube-system
labels:
k8s-app: filebeat
---
จากนั้น สั่ง create deployment
kubectl create -f filebeat-kubernetes.yaml
3. Search k8s cluster logs ผ่าน Kibana
ถ้า config ทุกอย่างเราถูกต้อง container ทำงานได้ เราก็จะได้ logs ของ k8s cluster เรา ไปเก็บบน Elasticsearch และทำการ search ผ่าน Kibana ได้เลย
* เราสามารถ filter input ของ Logs ที่จะ ship ไปเก็บ ที่ Elasticsearch ได้
ตัวอย่างอยู่ใน filebeat-kubernetes.yaml ที่ comment ไว้
เป็นอย่างไรกันบ้างครับ ไม่ยากเลยใช่ไหมครับ สำหรับการ ship logs จาก k8s cluster ของเรา ไปเก็บบน Elasticsearch 🙂
Git Repo: https://github.com/pornpasok/k8s-logs-es-filebeat
Ref: https://www.elastic.co/beats/filebeat
Happy New Year 2023 สวัสดีปีใหม่ 2566

สวัสดีปีใหม่ 2023 ครับ อาจจะช้าไปสักนิด พอดีช่วงหยุดปีใหม่ ที่ผ่านมา
ผมเองมีภารกิจ กลับไปทำ checklist ที่ list ไว้ว่าจะไปทำที่บ้าน
เพราะว่า นานๆ กลับที ไม่ได้กลับบ่อย ก็เลยต้องไปทำให้เสร็จครับ ..
จริงๆ list ไว้หลายอย่างมาก แต่ทำไม่สำเร็จ เพราะว่า ไม่มีเวลาพอ
บางอย่าง ก็ทำเองไม่ได้ทั้งหมด +___+
ที่ทำเสร็จไปแล้ว ก็มีประมาณนี้
– ติดม่านกันแสง UV หน้าต่าง ประตู ทั่วบ้าน หลังจากปล่อยโล่งๆ มา 8 ปี
– ติดกล้องวงจรปิดใหม่ ครอบคลุม และมีประสิทธิภาพมากขึ้น
– ติดระบบไฟ Solar รอบบ้าน ให้แสงสว่าง ประหยัดพลังงาน
– ซ่อมแซม เครื่องมือเครื่องใช้ต่างๆ ให้ทำงานได้ปกติ เหมือนเดิม
– ซ่อมแซม และทำระบบเพิ่ม ทั้ง IoT และ Weather Station
– ซ่อมแซม ตรวจสอบ Jeep ให้ทำงานได้ปกติ (ยังเหลือ switch ประตู และระบบไฟ)
– อื่นๆ ก็ทำนั้นทำนี่ ให้ดีขึ้น ให้สมบูรณ์ขึ้น
สำหรับปี 2022 ที่ผ่านมา ถือว่าเป็นปีที่เกิดการเปลี่ยนแปลง อย่างยิ่งเหมือนกันสำหรับผม
– เข้ามาอยู่ กทม. อีกรอบ ช่วงโควิด (Dec 2021) เช่าคอนโด อยู่ใจกลางเมือง ย่านรัชโยธิน
– งานส่วนใหญ่ ก็ยังเป็น เกี่ยวกับ DevOps ทำ Infrastructure, CI/CD Tools, Monitoring Tools ให้ Developers ใช้งาน
– ได้เข้าไปทำงาน ในระบบธุรกิจใหม่ๆ ที่ไม่เคยทำมาก่อน ก็สนุกดี ได้เจออะไรแปลกๆ ใหม่ๆ เยอะดี
– บริษัทที่ทำงานด้วย ดีมาก ไม่ได้เน้นระเบียบ หรือกระบวนการ ที่ไม่จำเป็น ทำให้ทำงานได้แบบมีประสิทธิภาพและคุณภาพ มากขึ้น, ไม่ต้องเข้า office ทุกวัน
– เดินทาง ท่องเที่ยว มากขึ้น เพราะโควิด เริ่มไม่รุนแรง
– รายได้ ของปีนี้ เริ่มดีขึ้น ทุกอย่างเริ่มลงตัวขึ้น
– หันมาออกกำลังกาย ช่วงปลายปี เพื่อสุขภาพ
– งดดื่ม ถ้าไม่จำเป็น +____+
– ผลตรวจสุขภาพ ประจำปี แบบละเอียด (package 5500 ของ รพ.สมิติเวช) พบว่าสุขภาพดีขึ้น จากปีก่อนๆ ที่ร่างกายหักโหม ดื่มหนัก ไม่ได้ออกกำลังกาย
– ได้มีเวลา หยุดยาว ช่วงปลายปี กลับไปอยู่บ้าน ที่จันทบุรี
– ได้ทะเบียนใหม่ มาใส่ Jeep ละครับ กย 9666 จันทบุรี ผลรวม = 36 ก็ถือว่าดีมาก สำหรับเลขนี้ ผลรวมนี้ ส่วนตัวผม เลขนี้ ก็มีความหมายของมันเอง สำหรับรถ ก็คือรถปี 1996 แล้วก็ทะเบียน ได้มาใช้ปี 2566 อีกด้วย ลงตัวเป๊ะ 🙂
สำหรับปี 2023 ที่คาดหวังไว้ และอยากทำ มีประมาณนี้
– ก็ยังคงเหมือนปี 2022 ที่ยังทำไม่ได้ทั้งหมด +___+
– ออกกำลังกาย มากขึ้น เลือกกินอาหาร ที่ดีต่อร่างกาย เน้นเรื่องสุขภาพเป็นหลัก
– ลดน้ำหนัก ให้เหลือ <= 75 (ปัจจุบัน 81-82)
– ทำงานที่ได้รับมอบหมาย ให้มีประสิทธิภาพ ดีที่สุด ให้เกินกว่าที่ต้องทำ ในทุกมิติ
– ศึกษาหาความรู้ เพิ่มเติมเสมอ
– เดินทาง ท่องเที่ยว ให้มากขึ้น ไปที่ใหม่ๆ ที่ไม่เคยไปบ้าง
– ใช้จ่ายอย่างประหยัด ใช้เท่าที่จำเป็น, ลงทุน หลายๆ ทางที่พอมีความเป็นไปได้
– ทำธุรกิจ เกี่ยวกับ technology ด้านการเกษตร (Agritech) จริงๆ จังๆ
– อยากทำ มอเตอร์ไซค์ ให้เช่าที่ปาย และเมืองท่องเที่ยวอื่นๆ
– เก็บสะสม รถที่ชอบ และราคาไม่น่าจะตกแล้ว
– กลับบ้านให้บ่อยขึ้น
– ช่วยเหลือสังคม เท่าที่กำลังกาย และกำลังทรัพย์ พอจะทำได้ 🙂
Setup Elasticsearch and Kibana OpenID Connect with Azure AD

สวัสดีครับ DevOps 101 ของเรา วันนี้มีเรื่องมาแนะนำ การ setup Single Sign-On (SSO) ของ App เรา .. ด้วย Azure AD โดยที่เรา ไม่จำเป็นต้องมีสิทธิเป็น admin ของ org นั้นๆ .. เป็น user ธรรมดา ก็สามารถ ทำได้
ในที่นี้ จะใช้ในส่วนของ OpenID Connect (OIDC) นะครับ ซึ่งสามารถ ใช้งานทั้งกับ Google, GitHub, AWS Cognito และอื่นๆ ได้ .. แต่ในที่นี้ผมจะใช้เป็น Azure AD นะครับ
จากความต้องการขององค์กร ที่ต้องการให้ พนักงานทุกคน login ด้วย Azure AD หรือถ้าพูดให้เข้าใจง่าย ก็คือใช้ email บริษัท ในทุกๆ App ที่ใช้งาน เนื่องจากเราใช้ Office365 อยู่แล้ว .. จะได้สะดวกในการจัดการ พนักงาน ที่มีการเข้าออก เปลี่ยนแปลงอยู่เสมอ ไม่ต้อง add แบบ local user ..
ในที่นี้ผมจะยกตัวอย่างการ นำ Azure AD (OpenID Connect) มาใช้กับ ELK Stack โดยสามารถใช้ได้ กับ ELK Stack ที่เรา hosted เอง และใช้ได้กับ elastic.co นะครับ ..
วิธีการ Setup OpenID Connect เราสามารถทำตามขั้นตอน ใน docs ของ elastic.co ได้เลยดังนี้
https://www.elastic.co/guide/en/cloud/current/ec-securing-clusters-oidc-op.html#ec-securing-clusters-oidc-op
จากนั้น เราสามาถทำ Role Mappings ได้ใน Kibana อย่างในที่นี้ ผมจะให้ user ที่ login ผ่าน Azure AD (OIDC) ทั้งหมด มี สิทธิแค่ viewer ก็สามารถทำได้ประมาณนี้
User field: realm.name
Type: text
Value: oidc1 (ชื่อ realm ที่เรา config ในหน้า login ของ Kibana)

หรือถ้าเรา ต้องการสร้าง Role mapping เฉพาะเจาะจงว่า user (email) คนไหน สามารถทำอะไรเพิ่มเติมได้ บ้าง เราก็สามารถ สร้างเพิ่มได้ ประมาณนี้
User field: username
Type: text
Value: user@email.com (email ของ user ที่เราต้องการ)

จะเห็นว่า เราสามารถนำ Azure AD (OpenID Connect) มาใช้กับ Kibana ของเราได้ ทำให้เราสะดวก ในการจัดการ user และการ ทำ Role Mappings ก็ช่วยให้เรา สามารถกำหนด permission ของแต่ละ user ให้แตกต่างกันได้ .. เราไม่ต้องไป add local user บน Elasticsearch และเราสามารถนำ OpenID Connect ไปใช้กับ App อื่นๆ ของเราได้ด้วยเช่นกันครับ .. ไม่ว่าจะเป็น App ที่เราเขียนเอง หรือ OpenSource ต่างๆ ก็รองรับ OpenID Connect กันเป็นส่วนใหญ่ 🙂
ถ้าเพื่อนๆ สงสัยตรงไหน comment กันเข้ามาถามได้นะครับ ไม่ยากครับ ลองไปทำเล่นดู elastic.co เอง ใช้งานได้ฟรี 14 วันครับ หรือถ้าจะ Hosted เองด้วย docker-compose ง่ายๆ ก็ได้ครับ 🙂
ตัวอย่าง: https://github.com/pornpasok/docker-elk
ลองใช้ Argo CD deploy application บน EKS

DevOps 101 ของเราวันนี้ จะมาทดลองใช้งาน Argo CD กันนะครับ .. ว่ามันดียังไง ทำอะไรได้บ้าง และเหมาะกับใคร? Logo ของ Argo CD ก็น่ารักดีครับ เป็นรูป มนุษย์ต่างดาว หรือ หมึก ก็ไม่รู้ ..
นิยามที่ทาง Argo CD เค้าบอกไว้ ในเว็บเค้าเองก็คือ ..
What Is Argo CD?
“Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.”
ถ้าสรุปให้เข้าใจได้ง่ายๆ ก็คือ เป็น Tools สำหรับทำ CD (Continuous Delivery) ที่ตรงไปตรงมา
ไม่ซับซ้อน สำหรับ deploy Application บน k8s ด้วยการใช้หลักการของ GitOps
รายละเอียดเพิ่มเติม : https://argo-cd.readthedocs.io/
เท่าที่ลองเล่นดู หลังจากที่เรามี k8s cluster แล้ว (ในที่นี่ผมใช้ EKS) เราก็จะต้อง Install เจ้า Argo CD ใน k8s cluster ของเรา จากนั้น เราก็จะใช้ความสามารถของ Argo CD ได้ ด้วยการผูกกับ Git Repository ที่ใช้ในการ deploy Application ของเรา เท่านี้ก็เป็นอันเรียบร้อยครับ .. เมื่อเรามีการเปลี่ยนแปลง configuration บน git เจ้า Argo CD ก็จะมา sync config ไป deploy บน k8s cluster ให้เรา ..
ดูรายละเอียดเพิ่มเติมที่ YouTube ของ Nana ได้ที่นี่ครับ : https://www.youtube.com/watch?v=MeU5_k9ssrs
สรุปความคิดเห็นส่วนตัว ที่ลองใช้งาน (ยังไม่ได้ใช้จริงๆ จังๆ บน Production)
ข้อดี
– Install ง่ายมาก ไม่เจอปัญหาอะไรเลย ใช้ resource น้อยมาก
– ไม่ต้องวุ่นวาย เรื่องการ Install kubectl เพราะอยู่ใน k8s cluster อยู่แล้ว
– ไม่ต้องวุ่นวาย เรื่องการจัดการ Credentials ในการเข้าถึง k8s cluster
– มี Dashboard ที่แสดงรายละเอียด ของการ deploy Application ได้ละเอียด ครบถ้วน
– Sync manifest จาก Git Repository ได้รวดเร็ว
– หน้าตาดูดี สวยงาม
ข้อเสีย
– Deploy (CD) บน k8s cluster ได้เท่านั้น
– ในส่วนของ CI (Continuous Integration) ยังต้องใช้ 3rd-party อื่นช่วย ไม่ว่าจะเป็น GitHub Action, GitLab CI, Jenkins
– ถ้าดูพวก Metrics ได้ด้วย น่าจะดีเลย แต่คิดว่า คงออกแบบมา ให้ simple ทำงาน CD ได้อย่างดี ก็พอ