<![CDATA[ทดลองเป็นโปรแกรมเมอร์ (หื่น)]]>https://i.dont.works/https://i.dont.works/favicon.pngทดลองเป็นโปรแกรมเมอร์ (หื่น)https://i.dont.works/Ghost 4.48Thu, 16 Feb 2023 07:56:47 GMT60<![CDATA[สร้าง User / Password / Database เพิ่มใน PostgreSQL ที่รันอยู่ใน Docker container]]>เวลาที่เราใช้ PostgreSQL ใน Docker container เราสามารถกำหนดตัวแปรสำหรับสร้าง USER,

]]>
https://i.dont.works/create-user-pass-db-in-pg-docker/600eaba86cd82d000161d821Mon, 25 Jan 2021 12:47:49 GMT

เวลาที่เราใช้ PostgreSQL ใน Docker container เราสามารถกำหนดตัวแปรสำหรับสร้าง USER, PASSWORD, DATABASE ได้เลย

ตัวอย่าง ไฟล์ docker-compose.yml

version: '3'
services:
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_DB: kong
      POSTGRES_USER: kong
      POSTGRES_PASSWORD: kong
    volumes:
    - ./pg_data:/var/lib/postgresql/data

จากตัวอย่างไฟล์ docker-compose.yml ด้านบน จะได้ User / Password / Database มา 1 ชุด สามารถแสดง user ได้ด้วยคำสั่ง

docker-compose exec db psql -U kong -c '\du'
สร้าง User / Password / Database เพิ่มใน PostgreSQL ที่รันอยู่ใน Docker container

ในกรณีที่เราอยากได้  USER, PASSWORD, DATABASE มากกว่า 1 ตั้งแต่แรก เราสามารถที่จะสร้างไฟล์ สำหรับการเริ่มต้นทำงานของ PostgreSQL ใน Docker container ได้ โดยเขียนไฟล์สำหรับสร้าง USER, PASSWORD, DATABASE ไว้ที่ /docker-entrypoint-initdb.d

ตัวอย่างไฟล์ /docker-entrypoint-initdb.d/init-user-db.sh

#!/bin/sh
set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE USER docker;
    CREATE DATABASE docker;
    GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL

จากนั้นแก้ Permission ของไฟล์ chmod +x init-user-db.sh และเพิ่มการแมปไฟล์เข้าไปใน container ทื่ docker-compose.yml

version: '3'
services:
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_DB: kong
      POSTGRES_USER: kong
      POSTGRES_PASSWORD: kong
    volumes:
    - ./pg_data:/var/lib/postgresql/data
    - ./init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh

ถ้าสั่ง docker-compose up -d เพื่อรัน PostgreSQL สิ่งที่คาดว่าจะได้เพิ่มมาคือ

Database = kong,   Username = kong,  Password = kong
Database = docker, Username = docker

แต่ว่า ในกรณีนี้ จะได้แค่

Database = kong,   Username = kong,  Password = kong

เท่าเดิม...

เพราะว่า PostgreSQL มองว่ามีไฟล์ database อยู่แล้ว จะไม่รัน initdb ใหม่ ถ้าอยากได้ 2 user รวมที่อยู่ใน init-user-db.sh ด้วย ต้องลบ pg_data ทิ้ง

docker-compose down
rm -rf pg_data
docker-compose up -d
docker-compose exec db psql -U kong -c '\du'
สร้าง User / Password / Database เพิ่มใน PostgreSQL ที่รันอยู่ใน Docker container

จะเห็นว่า วิธีนี้ มันทำให้เราต้องลบ pg_data ออกก่อนถึงจะใช้ได้

ในกรณีที่ เราอยากได้ User / Password / Database เพิ่มเติม หลังจากที่รัน PostgreSQL ไปแล้ว มีหลายวิธี เช่น

  • ใช้  docker exec คำสั่ง bash/sh ก่อน จากนั้นรันคำสั่ง psql เพื่อเข้าไปใน psql shell แล้วค่อยสร้าง User / Password / Database
  • ใช้ docker exec คำสั่ง psql โดยตรง เพื่อเข้าไปสร้าง User / Password / Database
  • ใช้ docker exec คำสั่ง psql พร้อมกับส่ง sql เข้าไปให้คำสั่ง psql

หลายวิธีด้านบน สามารถทำได้ผลเหมือน ๆ กัน แต่จะใช้หลายคำสั่งในการทำกว่าจะสร้าง User / Password / Database เพิ่มได้ ผมมีเหตุการณ์ ที่อยากจะรันเพียง 1 คำสั่ง เพื่อให้สามารถสร้าง User / Password / Database สำหรับ PostgreSQL ที่รันอยู่ได้

จริง ๆ จะว่าไป มันก็ไม่ได้เป็น 1 คำสั่งซะทีเดียว แต่เป็นการเขียน script ที่มีความยืดหยุ่นมากพอ ที่จะทำให้การรัน script เพียง 1 คำสั่ง สามารถได้สิ่งที่ต้องการครบ คือการสั่ง docker exec คำสั่งในไฟล์ script

ตัวอย่างไฟล์ create-user-db.sh

#!/bin/sh
set -x
POSTGRES="psql --username ${POSTGRES_USER}"
$POSTGRES <<-SQL
CREATE USER ${DB_USER} WITH CREATEDB PASSWORD '${DB_PASSWORD}';
CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};
SQL

จาก script ด้านบน เราสามารถใช้งานร่วมกันกับ docker -e ที่ใช้ตั้งค่า environment variable ได้

ตัวอย่างการใช้งาน

ต้องมี db รันอยู่ก่อนหน้านี้แล้ว

docker-compose exec -T \
  -e DB_USER=hello \
  -e DB_PASSWORD=password \
  -e DB_NAME=world \
  db sh 2>/dev/null < create-user-db.sh
สร้าง User / Password / Database เพิ่มใน PostgreSQL ที่รันอยู่ใน Docker container

จะเห็นว่า วิธีนี้ ทำให้เราไม่ต้องลบ pg_data ออกก่อน ไม่ต้อง รันคำสั่ง docker exec หลาย ๆ คำสั่ง

สำหรับ script ที่สมบูรณ์ สวยกว่าข้างบน แก้ create-user-db.sh ตามนี้

#!/bin/sh

# https://github.com/docker-library/postgres/issues/151

set -x
POSTGRES="psql --username ${POSTGRES_USER}"

echo "Before"
echo "======"
$POSTGRES <<-SQL
\du
SQL

echo -n "[*] Creating database role: ${DB_USER}... "
$POSTGRES <<-SQL
CREATE USER ${DB_USER} WITH CREATEDB PASSWORD '${DB_PASSWORD}';
SQL

echo -n "[*] Creating database ${DB_NAME}... "
$POSTGRES <<-SQL
CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};
SQL

echo
echo "After"
echo "====="
$POSTGRES <<-SQL
\du
SQL

จากนั้นลองสร้าง User / Database / Password เพิ่ม

docker-compose exec -T \
  -e DB_USER=dev \
  -e DB_PASSWORD=devpass \
  -e DB_NAME=dev_db \
  db sh 2>/dev/null < create-user-db.sh
สร้าง User / Password / Database เพิ่มใน PostgreSQL ที่รันอยู่ใน Docker container
]]>
<![CDATA[Kong HTTP Log]]>https://i.dont.works/kong-http-log/5f157262d26f0b0001d25eaaMon, 20 Jul 2020 12:46:37 GMT

การเก็บ Log ของ Kong API Gateway ด้วย HTTP Log Plugin ร่วมกับ Elasticsearch โดยใช้ domecloud/kong-http-log-server เป็น HTTP log server

ถ้าใครยังไม่เคยใช้ Kong API Gateway ให้อ่านตรงนี้ก่อน "Kong API Gateway ฉบับรวบรัด"

Kong Log Plugin มีตัวเลือกให้ใช้หลายตัว เข่น

ในบางกรณี เราไม่สามารถใช้ Log Plugin ใดๆได้ ยกเว้น HTTP Log Plugin อาจจะเพราะว่า Network / Firewall นั้น อนุญาตให้ใช้งานได้แค่ HTTP Protocol เท่านั้น ไม่สามารถใช้ TCP/UDP ได้

Kong HTTP Log

domecloud/kong-http-log-server จะถูกสร้างขึ้นมา เพื่อแก้ปัญหาของการเก็บ Log ของ Kong API Gateway ที่ใช้ HTTP Log Plugin ซึ่งเขียนด้วย Golang ถูกออกแบบมาให้รับ Log ผ่าน HTTP และเก็บ Log ลง Log Backend ได้หลายตัว เช่น Elasticsearch, Loki เป็นต้น

domecloud/kong-http-log-server
HTTP log server for Kong http-log plugin for Elasticsearch and more - domecloud/kong-http-log-server
Kong HTTP Log

เริ่มกันลองใช้กันเลยดีกว่า clone repo มาก่อน

git clone https://github.com/domecloud/kong-http-log-server
cd kong-http-log-server

จากนั้น แก้ไขไฟล์ .env

HOST=0.0.0.0
PORT=8080
ES_HOST=elasticsearch
ES_PORT=9200
INDEX_PATTERN=kong-test-2006-01-02
  • HOST คือ IP Address ที่จะให้ตัว kong-http-log-server binding
  • PORT คือ PORT ที่จะให้ kong-http-log-server listen
  • ES_HOST คือ Elasticsearch host เนื่องจากในตัวอย่าง ใช้ผ่าน Docker + docker-compose เลยใช้ชื่อ service ของ elasticsearch ได้
  • ES_PORT คือ HTTP Port ของ Elasticsearch
  • INDEX_PATTERN คือ index ที่จะเก็บลง Elasticsearch โดยใช้ Golang date-time format (แก้เฉพาะ kong-test ส่วนของ 2006-01-02 ให้คงไว้)

หลังจากแก้ไฟล์ .env และเซฟไฟล์แล้ว เริ่ม start service โดยเริ่มจาก

  • elasticsearch
  • kibana
  • kong-http-log-server
  • kong-database
  • migrations (kong-migration)
  • kong

Deploy Services

docker-compose up -d elasticsearch kibana
docker-compose up -d kong-database
docker-compose up mogrations
docker-compose up -d kong
docker-compose up -d kong-http-log-server

หลังจากที่ start services ทั้งหมดแล้ว ควรมี container รันอยู่ทั้งหมดดังนี้

Kong HTTP Log
docker-compose ps

เพิ่ม Kong Service และ Route

# สร้าง Service
curl http://127.0.0.1:8001/services \
    -d name=httpbin \
    -d url=http://httpbin.org

# สร้าง Route
curl http://127.0.0.1:8001/services/httpbin/routes \
    -d name=httpbin \
    -d paths[]=/
อาจจะใช้ Konga ก็ได้

หลังจากที่ได้ Service และ Route แล้วลองเข้าใช้ผ่าน http://127.0.0./

Kong HTTP Log

เพิ่ม HTTP Log Plugin ให้กับ Kong API Gateway

curl http://127.0.0.1:8001/plugins \
    -d name=http-log \
    -d config.http_endpoint=http://kong-http-log-server:8080/
หลังจากเพิ่ม HTTP Log plugin แล้ว การจะทำให้เกิด Log ต้องมีการเรียกใช้งาน Web หรือ API ผ่าน Kong

ตั้งค่า Kibana

เปิดเว็บเบราเซอร์ไปที่ http://127.0.0.1:5601

Kong HTTP Log

เลือก Explore on my own แล้วไปที่ Stack Management

Kong HTTP Log

Index pattern > Create index pattern > kong-test-*

Kong HTTP Log

Next step > Time Filter field name > @timestamp > Create  index pattern

Kong HTTP Log

เป็นการเสร็จสิ้น

Kong HTTP Log

ไปที่เมนู Discover

Kong HTTP Log

จากได้หน้า Dashboard สำหรับการดู Log ของ การเรียกใช้ API ผ่าน Kong API gateway

Kong HTTP Log
discover kibana

จบการใช้งาน kong-http-log-server

สิ่งที่ต้องการเพิ่มเติมคือ

Kong HTTP Log

สำหรับใครที่เอา kong-http-log-server ไปใช้ แล้วต้องการ Log Backend แบบอื่นๆ สามารถส่ง Pull Request มาได้ หรือถ้าพบปัญหาการใช้งาน แจ้งปัญหาผ่าน GitHub issuse ได้เลยครับ

]]>
<![CDATA[Nginx Configuration template]]>https://i.dont.works/nginx-configuration-template/5ebd10ae2ab2e8000137e798Thu, 14 May 2020 10:38:29 GMTNginx Configuration templateNginx Configuration template

มีคนไปถามในกลุ่ม Docker in Thai เรื่องการทำ Nginx config template โดยใช้ envsubst คำถามน่าสนใจ

Nginx Configuration template

คำถามคือ

รบกวนสอบถามหน่อยครับ ผมใช้ docker-compose ทำการติดตั้ง nginx ต้องการ copy ไฟล์ site.conf ขึ้นไปและ envsubst ทำการเปลี่ยน environment ในไฟล์ site.conf แต่พอทำการ build แล้วไฟล์ site.conf เป็น 0 ไม่มีข้อมูลข้างในเลย ไม่รู้ว่าใช้คำสั่งถูกไหม ขอคำแนะนำหน่อยครับ
Nginx Configuration template

มีแนบภาพมาในคำถามด้วย

Nginx Configuration template

พร้อม docker-c0mpose ด้วย ดีมาก

Nginx Configuration template

มีพี่ปุ๋ยไปตอบคนแรกด้วย มีสาระ

Nginx Configuration template

อาจารย์เชษฐ์ของผม ก็ไปร่วมแจม

Nginx Configuration template

ผมก็เลยอยากร่วมแจมบ้าง

Nginx Configuration template
https://www.facebook.com/groups/858633044176588/permalink/3170776116295591/

ไงละๆ กด Care กันเลยทีเดียว

ที่ผมไปคอมเม้นแบบนั้น เพราะว่าผมเก่งมาก

Nginx Configuration template

แน่นอน ผมเก่งจริงๆ ไม่เชื่อลองดู

Nginx Configuration template

ท่ดๆ พิมพ์ผิด มีแก้ไข

Nginx Configuration template
Nginx Configuration template

นอกเรื่องมากตั้งไกล ที่จะพูดถึงคือ envsubst แค่นั้นเอง

Nginx Configuration template

envsubst คือ คำสั่งในการแทนที่ค่าใน string หรือข้อความ ด้วยการอ่านค่ามาจาก Environment Variable

ตัวอย่าง

$ export NAME=CHANG
$ echo 'hello $NAME' | envsubst
hello CHANG
Nginx Configuration template

ในตัวอย่างข้างบนคือเรียกใช้คำสั่ง envsubst ตรงๆเลย อาจจะยังไม่เห็นภาพ เดี๋ยวลองใช้ผ่าน Docker และ Nginx ดู

ก่อนอื่นรัน Nginx ใน Docker มาก่อน โดยทำการ map ไดเรคทอรีเข้าไป 2 ตัว คือ conf.d และ html

$ docker run -d --name nginx \
  -v $PWD/conf.d:/etc/nginx/conf.d \
  -v $PWD/html:/var/www/html \
  -p 80:80 \
  nginx:alpine

จะได้คอนเทนเนอร์ และ 2 ไดเรคทอรี่เปล่าๆคือ conf.d, html

Nginx Configuration template

ลองสร้างไฟล์ conf.d/site.tmpl โดยมีค่าตามนี้

server {
    listen $HOST:$PORT;
    server_name $SERVER_NAME;

    root /var/www/html/$SERVER_NAME;

    location / {
    }
}

จากนั้น ลองสร้างไฟล์คอนฟิกใหม่ โดยใช้ conf.d/site.tmpl เป็นต้นแบบ โดยจะตั้งชื่อเซิฟเวอร์ หรือ โดเมนเป็น kop.opop

cat conf.d/site.tmpl | docker exec -i \
  -e HOST=0.0.0.0 \
  -e PORT=80 \
  -e SERVER_NAME=kop.opop \
  nginx sh -c 'envsubst > /etc/nginx/conf.d/$SERVER_NAME.conf'
Nginx Configuration template

จากนั้น เราจะได้ไฟล์ชื่อ conf.d/kop.opop.conf โดยมีคอนฟิกตามนี้

server {
    listen 0.0.0.0:80;
    server_name kop.opop;

    root /var/www/html/kop.opop;

    location / {
    }
}

จากนั้น ก็ใช้มายากล สร้างไฟล์ และไดเรคทอรี่ใน html ให้ตรงกับ SERVER_NAME จะได้

Nginx Configuration template

จะเห็นว่า เราสามารถอ่านค่า Environment Variable ไปใส่ใน Nginx Config ได้ ด้วย envsubst เพิ่มความยืดหยุ่นในการใช้ Nginx ใน Docker ได้ระดับหนึ่งเลย

สุดท้าย ผมบอกแล้วว่า ผมเก่ง Docker, Nginx มาก

Nginx Configuration template

วิดีโอ Live ที่ลืมเอา Terminal ขึ้น เลยมาเขียนบล็อกแทน

Nginx Configurations template with envsubst

Nginx Configurations template with envsubst. // substitutes environment variables in shell format strings อ้าว ลืมแชร์ Terminal

Posted by ทดลองเป็นโปรแกรมเมอร์ - หื่น on Wednesday, 13 May 2020
]]>
<![CDATA[ตัวอย่าง NGINX mutual authentication]]>https://i.dont.works/nginx-mtls-example/5d74bcfe5eeb4e000185754fSun, 08 Sep 2019 09:26:34 GMT

การทำ  Mutual authentication พูดง่ายๆคือ คนที่จะเรียกใช้เว็บ หรือ API ได้ ต้องมีกุญแจ หรือ key ที่ถูกต้อง ถึงจะใช้งานได้

ตัวอย่าง NGINX mutual authentication
Mutual authentication หรือเรียกสั้นๆ mTLS (Mutual TLS authentication)

รัน NGINX ปกติด้วย Docker ง่ายๆ ด้วย Docker Compose

version: '3.7'
services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
    - 80:80
ตัวอย่าง NGINX mutual authentication

ได้ NGINX ง่ายๆแล้ว เพิ่ม HTTPS ให้กับ NGINX โดยใช้ mkcert ในการสร้าง SSL สมมติว่า ชื่อโดเมน bank.api

mkdir -p certs
cd certs
mkcert bank.api

จะได้

Using the local CA at "/Users/USERNAME/Library/Application Support/mkcert" ✨

Created a new certificate valid for the following names 📜
 - "bank.api"

The certificate is at "./bank.api.pem" and the key at "./bank.api-key.pem" ✅

แก้ไฟล์ docker-compose.yml เพื่อตั้งค่า HTTPS

version: '3.7'
services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    volumes:
    - ./certs:/etc/nginx/certs
    - ./conf.d:/etc/nginx/conf.d
    ports:
    - 80:80
    - 443:443

เพิ่มไฟล์ conf.d/bank.api.conf


server {
    listen       443 ssl http2;
    server_name  bank.api;

    ssl_certificate     /etc/nginx/certs/bank.api.pem;
    ssl_certificate_key /etc/nginx/certs/bank.api-key.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

จากนั้น รัน docker-compose up -d ใหม่อีกรอบ แล้วแก้ไฟล์ /etc/hosts เพิ่ม 127.0.0.1 bank.api

# /etc/hosts
127.0.0.1 bank.api

เสร็จแล้วลองเรียกหน้าเว็บด้วย HTTPS https://bank.api

ตัวอย่าง NGINX mutual authentication
https

จะได้หน้าเว็บที่เป็น HTTPS ถ้าใครใช้ mkcert ครั้งแรก แล้วไม่ได้ติดตั้ง Root CA ตอนสร้าง Cert ให้รันคำสั่งนี้

cd certs
mkcert -install

สร้าง Client Certificate สำหรับทำ mTLS

mkcert -client bank.api
Using the local CA at "/Users/USERNAME/Library/Application Support/mkcert" ✨

Created a new certificate valid for the following names 📜
 - "bank.api"

The certificate is at "./bank.api-client.pem" and the key at "./bank.api-client-key.pem" ✅

จากนั้นคัดลอก rootCA.pem มาที่ certs/rootCA.pem

จะได้ไฟล์

.
├── bank.api-client-key.pem
├── bank.api-client.pem
├── bank.api-key.pem
├── bank.api.pem
└── rootCA.pem

แล้วแก้ไฟล์ conf.d/bank.api.conf เพื่อทำการเช็ค client certificate


server {
    listen       443 ssl http2;
    server_name  bank.api;

    ssl_certificate     /etc/nginx/certs/bank.api.pem;
    ssl_certificate_key /etc/nginx/certs/bank.api-key.pem;

    ssl_verify_client on;
    ssl_client_certificate /etc/nginx/certs/rootCA.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

หลังจากนั้น reload nginx เมื่อเข้าผ่านหน้าเว็บ จะได้ error

ตัวอย่าง NGINX mutual authentication

มัน error เพราะไม่ได้ส่ง client certificate ไปด้วย ขี้เกียจติดตั้ง client certificate จะโกงด้วยการใช้ curl

cd  certs
curl -v \
    --key bank.api-client-key.pem \
    --cert bank.api-client.pem \
    --cacert bank.api.pem \
    https://bank.api

จะได้ผลลัพธ์เป็น 200 OK

จบ

ตัวอย่าง NGINX mutual authentication
]]>
<![CDATA[Kong force HTTPS]]>https://i.dont.works/kong-force-https/5d690633e1191f00015065eaFri, 30 Aug 2019 11:43:05 GMT

การทำ Redirect HTTP ไป HTTPS ใน NGINX จะใช้ คอนฟิกประมาณนี้

server {
    listen 80;
    server_name _;
    return 301 https://$host$request_uri;
}

แล้ว Kong ละมันทำยังไง ?

มีคนถามไว้นานแล้ว ที่ Kong Nation ว่า "Redirect Kong non SSL to SSL" เมื่อตอน 20 April 2018 อห. นานแล้วนี่หว่า มีหลายๆคนมาคอนเม้นตอบ วิธีการ work around เช่น แก้ nginx custom template ของ kong หรือใช้ Pre-Function ที่เป็น Serverless Plugin ของ Kong ในการเช็ค scheme ก็ช่วยตัวเองกันไป

Kong ก็ฟังคนใช้แหละ แต่กว่าจะออกมารองรับ ก็ที่ Version 1.2.0 จิ้มไปดู CHANGELOG ได้ที่ https://github.com/Kong/kong/blob/master/CHANGELOG.md#core-6

Kong force HTTPS

จากบล็อกที่แล้ว เรื่อง "KONG DB-LESS HTTPS CONFIG" จะใช้ได้ทั้ง HTTP/HTTPS ถ้าอยากให้ใช้ได้แค่ HTTPS อย่างเดียว ถ้าเข้า HTTP ให้รีไดเรคไป HTTPS แก้คอนฟิกตามนี้

_format_version: '1.1'
services:
- name: httpbin-svc
  url: http://httpbin.org/
  routes:
  - name: httpbin-route
    hosts:
    - kong.dev
    protocols:
    - https
    https_redirect_status_code: 302

certificates:
- cert: "-----BEGIN CERTIFICATE-----..."
  key: "-----BEGIN PRIVATE KEY-----..."
  snis:
  - name: kong.dev

ที่เพิ่มมาคือ protocols และ https_redirect_status_code ซึ่ง 2 ตัวนี้มีค่า default ของมันอยู่ คือ

  • protocols: ["http", "https" ]
  • https_redirect_status_code: 426

ถ้าไม่แก้ protocols ให้เหลืออันเดียว มันจะใช้ได้ทั้ง HTTP/HTTPS ต้องแก้ และถ้าไม่แก้ https_redirect_status_code มันจะไม่รีไดเรค แต่จะได้ error นี้คือ

curl -v kong.dev
* Rebuilt URL to: kong.dev/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to kong.dev (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: kong.dev
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 426
< Date: Fri, 30 Aug 2019 11:33:37 GMT
< Content-Type: application/json; charset=utf-8
< Connection: keep-alive
< Connection: Upgrade
< Upgrade: TLS/1.2, HTTP/1.1
< Content-Length: 39
<
* Connection #0 to host kong.dev left intact
{"message":"Please use HTTPS protocol"}

ถ้าแก้ให้เป็น 301, 302 มันถึงจะรีไดเรคให้

Kong force HTTPS

จบละ เขียนไว้กันลืม

Kong force HTTPS
]]>
<![CDATA[Kong DB-Less HTTPS Config]]>https://i.dont.works/kong-db-less-https-config/5d554187e1191f0001506598Thu, 15 Aug 2019 11:56:31 GMT

เขียนไว้อ่านเอง เดี๋ยวลืม และให้คนอื่นๆได้อ่านด้วย หาตัวอย่างในเว็บของ Kong เองก็ไม่เจอ

สร้าง Certificates ก่อน โดยใช้ mkcert #หิวเป็ดย่าง

 mkcert kong.dev
 mkcert -install

สร้าง Certificates ชื่อ kong.dev จะได้ไฟล์ 2 ไฟล์

  • kong.dev.pem
  • kong.dev-key.pem

และติดตั้ง Certificates ให้เครื่องมันรู้จัก

สร้างไฟล์ kong.yml ใน kong.conf.d

_format_version: '1.1'
services:
- name: test
  url: http://httpbin.org
  routes:
  - name: test
    hosts:
    - kong.dev

certificates:
- cert: "-----BEGIN CERTIFICATE-----..."
  key: "-----BEGIN PRIVATE KEY-----..."
  snis:
  - name: kong.dev

ไฟล์ docker-compose.yml

version: '3.7'
services:
  kong:
    image: kong
    container_name: kong
    environment:
      - KONG_DATABASE=off
      - KONG_DECLARATIVE_CONFIG=/kong.conf.d/kong.yml
    ports:
      - 80:8000
      - 443:8443
    volumes:
      - ./kong.conf.d:/kong.conf.d

สั่งรัน container ด้วย

docker-compose up -d

จากนั้นตั้งค่า /etc/hosts ให้ kong.dev ชี้ไปที่ IP ของเครื่องที่รัน Kong

จะได้หน้าเว็บ HTTPS สวยงาม Trust Certs ด้วย

Kong DB-Less HTTPS Config

จบแล้วจ้า

]]>
<![CDATA[Tech Talk | Kong API Gateway at CODE CRAFT]]>https://i.dont.works/tech-talk-kong-api-gateway-at-code-craft/5d4fde12e1191f000150654cSun, 11 Aug 2019 09:55:33 GMT

เมื่อวันที่ 7 สิงหาคม 2562 | 7 August 2019 ผมได้มีโอกาสไปพูดในงาน Meetup เล็กๆ ที่จัดโดย CODE CRAFT เป็นกิจกรรม ที่จัดขึ้นเพื่อการกินเบียร์ โดยมีข้ออ้าง ที่พอจะมีสาระหน่อย 55555 ต้องขอบคุณ CODE CRAFT ที่จัดกิจกรรมนี้มา โดยผมไปพูดในหัวข้อเรื่อง Kong API Gateway ซึ่งเป็นเรื่องที่ผมคลุกคลีอยู่ในช่วงนี้ ให้พูดเรื่องอื่นๆ ผมน่าจะพูดไม่ได้

ชื่องาน นเรศ พบประชาชน ตอน Kong API Gateway

สไลด์ที่ผมใช้ในงาน

Tech Talk | Kong API Gateway at CODE CRAFT
https://docs.google.com/presentation/d/13vJD02FpWQoebpr2I-JxMWfVJMyOUUWPGqe14RPkwgg/edit?usp=sharing

ในสไลด์อาจจะไม่ละเอียด สิ่งที่ผมพูด และ Demo ในงานจะมี

  • Kong Services
  • Kong Routes
  • Konga
  • Kong Plugins
  • Basic Auth
  • Consumers
  • Request Transformer
  • Response Transformer
  • Rate Limiting
  • Logging
  • Elastic Search
  • Tracing
  • Zipkin

ประมาณนี้ครับ หลังจบงานก็นั่งพูดคุยดื่มเบียร์กัน จนกระทั่ง ส.ส. ได้มาพบนเรศ และประชาชน

Tech Talk | Kong API Gateway at CODE CRAFT
]]>
<![CDATA[ใครไม่ดี KrakenD API Gateway]]>https://i.dont.works/basic-krakend-api-gateway/5d21f8f70d15340001ff4f70Sun, 07 Jul 2019 15:52:48 GMT

เมื่อสัก 2 วันก่อน ผมเห็น KrakenD จากน้องคนนึงโพสต์ใน Facebook บอกว่า มันเร็วกว่า Kong ด้วย ผมตามไปดู เห็นผลการทดสอบ มันเร็วกว่าเยอะเลย จากรูปนี้

ใครไม่ดี KrakenD API Gateway
https://www.krakend.io/docs/benchmarks/overview/

โห เร็วกว่า Kong และ Tyk ด้วย ผมเลยลองรันเล่นดู แต่ว่า เมื่อ 2 วันก่อนนี้ รันแบบรีบๆ ไม่ได้ตั้งใจอ่านเอกสาร เลยยังรันมาเล่นไม่ผ่าน ก็เลยหยุดเล่น แล้วไปทำงานต่อ...

วันนี้ว่าง เลยลองเล่นดูอีกรอบ เพื่อจะดูว่า มันดียังไง เผื่อว่ามีงานที่มันทำได้ดี จะได้มีตัวเปรียบเทียบกับ Kong ถ้าใครติดตามผมใน Facebook จะเห็นว่า ผมใช้ Kong API Gateway และเขียนบล็อก Kong API Gateway ฉบับรวบรัด สำหรับผู้เริ่มต้น แต่พอเห็นตัวนี้เคลมว่าตัวเองเร็วกว่า Kong ผมก็เลยอยากลองดูว่า มันใช้ยังไง

KrakenD API Gateway เขียนด้วยภาษา Golang เหมือนกับ Tyk แต่ผมลอง Tyk แล้วไม่ผ่าน มัน Deploy ดูยาก ๆ ยุ่ง ๆยังไงไม่รู้ มันเป็น Open Source แต่ เวลาใช้ ต้องไปขอ Key มา -_- ผมเลยไม่ลองต่อ ส่วน KrakenD ผมลองแล้ว Deploy ไมผ่านในครั้งแรก และไม่ค่อยชอบเพราะไปเห็นไฟล์คอนฟิกของมัน ที่เป็น JSON ดูแล้วยุ่งๆ อ่านยาก แล้วเอาคอนฟิกไฟล์ตัวอย่างมาลอง แล้วใช้ยากอีก ลาก่อย

วันนี้นั่งร้านกาแฟ และให้เวลากับมัน 2 ขั่วโมง เผื่อหาว่ามันใช้ยังไง พอได้คอนเซ็ปต์ และกลับมากินเบียร์กระป๋องนึง ลื่นไหลเลยทีนี้ พอจะหาข้อดี และชอบมันได้

ไฟล์คอนฟิก ที่เป็น JSON

{
    "version": 2,
    "name": "KrakenD - API Gateway",
    "port": 8000,
    "endpoints": [{
        "endpoint": "/",
        "method": "GET",
        "backend": [{
            "url_pattern": "/ip",
            "host": [
                "http://httpbin.org/"
            ]
        }]
    }]
}

จริง ๆแล้วไฟล์คอนฟิกตัวอย่าง อ่านยากกว่านี้ อันนี้เลย https://github.com/devopsfaith/krakend-ce/blob/master/krakend.json คือผมคิดว่า ถ้าไฟล์คอนฟิกมันสั้นๆ ดูเข้าใจง่าย ผมคงชอบมันในครั้งแรกเห็น 5555 แต่แม่งยาวเฟี้ย กว่าผมจะลดทอน เหลือ JSON ข้างบน หมดกาแฟไป 1 แก้ว เบียร์ 1 กระป๋องเลย

มา Deploy KrakenD กัน

ใช้ Docker + docker-compose แล้วกัน ใครใช้ไม่เป็น ไปหาอ่าน Docker ก่อน หรือซื้อคอร์สผมก็ได้ที่ ask-me.guru | ถามได้ ถ้าจ่ายตังส์

 สร้างไดเรคทอรีสำหรับคอนฟิกไฟล์ ผมใช้ชื่อ konf.d

mkdir -p konf.d

จากนั้นสร้างไฟล์คอนฟิก แล้วใส่ JSON ไฟล์ข้างบน

touch konf.d/krakend.json

ถ้าขี้เกียจเลื่อนไปข้างบน ก๊อปปี้ไปใส่เลย

{
    "version": 2,
    "name": "KrakenD - API Gateway",
    "port": 8000,
    "endpoints": [{
        "endpoint": "/",
        "method": "GET",
        "backend": [{
            "url_pattern": "/ip",
            "host": [
                "http://httpbin.org/"
            ]
        }]
    }]
}

จากนั้น สร้างไฟล์ docker-compose.yml สำหรับรัน KrakenD ใน Docker

version: '3.7'
services: 
  krakend:
    image: devopsfaith/krakend
    ports: 
      - 80:8000
    volumes: 
      - ./konf.d:/etc/krakend/

ไฟล์จะมีแบบนี้

.
├── docker-compose.yml
└── konf.d
    └── krakend.json

จากนั้นรัน docker-compose เพื่อรัน KrakenD

docker-compose up

จะได้ KranenD รันอยู่ที่ http://127.0.0.1

ใช้ cURL หรือ HTTPie เรียก จะได้ IP ของเครื่องเราเอง เหมือนกับเรียกไปที่ http://httpbin.org/ip

http :80

http http://httpbin.org/ip
ใครไม่ดี KrakenD API Gateway

เห็นไหม ง่ายนิดเดียว ได้ KrakenD API Gateway แล้ว แต่นอกจากนี้ KrakenD ยังมี Designer ชื่อ KrakenDesigner ใช้สำหรับสร้างไฟล์คอนฟิก ที่มีหน้าเว็บให้ใส่ค่าต่างๆ เพื่อความสะดวกสำหรับการเริ่มต้น แต่ผมเองใช้แล้วรู้สึกยุ่งยาก ไฟล์ที่สร้างมาแล้วรันไม่ผ่าน เลยไปอ่านไฟล์คอนฟิกตัวอย่างใน GitHub แล้วก็ตัดตอนเอาให้เหลือคอนฟิกที่น้อยที่สุดที่มันทำงานได้ จนกลายมาเป็น JSON ด้านบนนู้น -_-

ใครไม่ดี KrakenD API Gateway
KrakenD Emulator พัง ทำให้ไม่อยากใช้ Designer

ไม่มี Reload config ใน Production!

KrakenD บอกว่า ออกแบบมาคำนึงถึงประสิทธิ์ภาพเป็นเรื่องแรก! เอ้อ การ Reload config อาจทำให้ประสิทธิ์ภาพลดลงเยอะก็ได้ แต่ถ้าจะทำ น่าจะไปทาง Deploy KrakenD เป็นคลัสเตอร์ แล้วใช้ Load Balancer ค่อยๆกระจายโหลดไปโหนดใหม่ ทำ A/B Testing, Canary, Blue/Green อะไรก็ว่าไป ที่มีคอนฟิกใหม่เอาแทน จะเอาคอนฟิกใหม่ ต้อง Restart KrakenD เอา  ดูเพิ่มเติมได้ที่นี้ จิ้มเลย 👉 Reloading the Krakend configuration with Reflex and Docker

เมื่อก่อนผมไม่ชอบ YAML เลย ขี้เกียจจัดการกับ indent หรือย่อหน้า ชอบ JSON มากกว่า พอมาช่วงหลังๆ การเขียนคอนฟิกไฟล์ จะเป็น YAML เป็นส่วนใหญ่ ตั้งแต่ docker-compose, k8s ก็เป็น YAML หมดเลย หลังๆมา เลยชินกับมัน

KrakenD YAML Config

KrakenD รองรับไฟล์คอนฟิกหลายหลายฟอร์แมตมาก คือ

  • .json
  • .toml
  • .yaml
  • .yml
  • .properties
  • .props
  • .prop
  • .hcl

เยอะเลยแต่ KrakenD แนะนำให้ใช้ JSON เพราะมันแก้ไขผ่าน Designer ได้ แต่อันนี้เป็นตัวอย่าง YAML ที่ทำเหมือน JSON ไฟล์ข้างบน

version: 2
name: KrakenD - API Gateway
port: 8000
output_encoding: json
endpoints:
- endpoint: "/"
  method: "GET"
  backend:
  - host:
    - http://httpbin.org
    url_pattern: "/ip"

แก้ docker-compose.yml ให้เรียกไฟล์ตอนฟิก YAML

version: '3.7'
services: 
  krakend:
    image: devopsfaith/krakend
    working_dir: /etc/krakend/
    command: run -c krakend.yml
    ports: 
      - 80:8000
    volumes: 
      - ./konf.d:/etc/krakend/

Hot-Reload, Check Config, Designer

version: '3.7'
services: 
  designer:
    image: devopsfaith/krakendesigner:latest
    ports:
      - 8787:80
  krakend:
    image: devopsfaith/krakend:config-watcher
    working_dir: /etc/krakend/
    environment: 
      KRAKEND_CONFIG: krakend.yml
    ports: 
      - 80:8000
    volumes: 
      - ./konf.d:/etc/krakend/
  check:
    image: devopsfaith/krakend
    working_dir: /etc/krakend/
    command: check -c krakend.yml
    volumes: 
      - ./konf.d:/etc/krakend

Start KrakenDesiner

docker-compose up -d designer

KrakenDesigner จะรันอยู่ที่ http://127.0.0.1:8787

ใครไม่ดี KrakenD API Gateway

Check config

docker-compose up check
ใครไม่ดี KrakenD API Gateway

แถม Aggregate API

ผมชอบฟีเจอร์นี้มาก ใน Kong API Gateway มันไม่มีมาให้ คือการเรียกไป 1 enpoint แล้วทำการรวม Response มาจากหลายๆที่ เช่น ผมจะเอา 2 API นี้มารวมกัน ใน 1 enpoint

  • http://httpbin.org/ip
  • http://mockbin.org/status/200/Hello

เขียนไฟล์คอนฟิกได้แบบนี้

version: 2
name: KrakenD - API Gateway
port: 8000
output_encoding: json
endpoints:
- endpoint: "/"
  method: "GET"
  backend:
  - host:
    - http://httpbin.org
    url_pattern: "/ip"
- endpoint: "/aggs"
  method: "GET"
  backend:
  - host:
    - "http://httpbin.org"
    url_pattern: "/ip"
  - host:
    - "http://mockbin.org"
    url_pattern: "/status/200/Hello"

ผลที่ได้

$ http :80/aggs

HTTP/1.1 200 OK
Content-Length: 72
Content-Type: application/json; charset=utf-8
Date: Sun, 07 Jul 2019 16:09:32 GMT
X-Krakend: Version 0.9.0
X-Krakend-Completed: true

{
    "code": "200",
    "message": "Hello",
    "origin": "171.99.165.99, 171.99.165.99"
}
ใครไม่ดี KrakenD API Gateway
จบแล้วจ้า

ไฟล์คอนฟิกตัวอย่าง https://github.com/narate/krakend-example

]]>
<![CDATA[Kong API Gateway ฉบับรวบรัด]]>หลายๆคน ที่ผ่านมาอ่านบล็อกนี้ ผมคิดว่า น่าจะเคยอ่านบล

]]>
https://i.dont.works/kong-api-gateway-in-10-minute/5d04ba1f0d15340001ff4dbaSat, 15 Jun 2019 12:09:47 GMT

หลายๆคน ที่ผ่านมาอ่านบล็อกนี้ ผมคิดว่า น่าจะเคยอ่านบล็อกนี้ "ชีวิตดีเพราะมี Kong ช่วยทำ API Gateway" ซึ่งเขียนไว้ได้ดีมาก เห็นภาพรวม และการใช้งาน Kong ได้อย่างเข้าใจง่าย ผมแนะนำให้อ่านประกอบกับบล็อกนี้ไปด้วย

ในบล็อกนี้ ผมจะมาสรุปสั้นๆ เพื่อให้เห็นภาพว่า Kong คืออะไร ทำอะไรได้บ้าง และจะใช้มันทำอะไร

Kong API Gateway ฉบับรวบรัด

จากภาพบน (ด้านซ้าย) แสดงให้เห็นว่า การทำ API แบบเก่าเวลาที่ API เริ่มเยอะขึ้น ฟังก์ชั่นหรือฟีเจอร์ที่ใช้ จะมีซ้ำๆกัน เช่น การ Authentication ก่อนการเรียกใช้ API, การ Limit Request สำหรับการเรียกใช้ API และอื่นๆ ซึ่งแต่ละ API อาจจะมาจากคนละทีมที่เขียนมา แล้วใช้ Reverse Proxy ในการ Route Request ไปยัง API ต่างๆด้านหลัง

ภาพด้านขวาคือ Kong API Gateway จะแสดงให้เห็นว่าจะยุบรวมฟีเจอร์หรือฟังก์ชั่น ที่มีการเรียกใช้งานร่วมกันของ API ต่างๆ ไปอยู่ที่ตัว Kong ทำให้ API ที่เขียนขึ้นมา ไม่ต้องเขียนฟังก์ชั่นเหล่านั้นเอง เช่น Authentication, Rate Limit,  ACL และอื่นๆ

สิ่งที่ทำให้ Kong เป็น API Gateway ที่ได้รับความนิยมมากคือ

  • ใช้ง่ายผ่าน RESTful API
  • Open Source
  • Scale ได้ง่าย
  • ประสิทธิภาพสูง
  • มี Plugin ให้ใช้

Deploy Kong  ในเวลาน้อยกว่า 2 นาที!

ถ้าในเครื่องมี Docker, Docker Compose แล้วนะ :D
git clone https://github.com/narate/kong-docker.git
git checkout pg
time ./start.sh
Kong API Gateway ฉบับรวบรัด
ถ้าไม่มี docker image ในเครื่อง จะขึ้นอยู่กับความเร็วของอินเตอร์เน็ต

จากคำสั่งด้านบน จะได้ Kong API Gateway ที่พร้อมใช้งาน แต่ก่อนจะเริ่มใช้งาน Kong มาทำความเข้าใจพื้นฐานของ Kong ก่อน

หลังจากที่ Deploy Kong ไปแล้วจะได้ 2 Port ที่เปิดเพิ่มมา

  • Kong Admin Port 8001
  • Kong Proxy Port 8000 (แต่ใน docker-compose map port ออกมาเป็น 80 ที่เครื่องโฮสต์)

Kong Admin Port 8001

Kong Admin Port ใช้สำหรับคอนฟิก Kong

Kong Proxy Port 8000

Kong Proxy Port ใช้สำหรับเป็นทางเข้า หรือให้เรียกไปยัง Service ด้านหลัง Kong

Object พื้นฐาน ที่ต้องใช้ใน Kong

  • Services
  • Routes

Kong Services

Kong Services คือ การประกาศไว้ว่า API หรือ Microservice ของเราอยู่ที่ไหน

Kong Routes

Kong Routes คือ ทางเข้าไปยัง Services ที่ประกาศไว้

สร้าง Service แรก


ใช้ cURL ในการสร้าง Service ชื่อ httpbin โดยมี url ของ service เป็น http://httpbin/org

curl 127.0.0.1:8001/services -d 'name=httpbin' -d 'url=http://httpbin.org'

หลังจากรันคำสั่ง จะได้ Respose เป็น JSON กลับมา

{
  "host": "httpbin.org",
  "created_at": 1560595421,
  "connect_timeout": 60000,
  "id": "02fd187e-7976-44ce-88a6-604a707cdc9a",
  "protocol": "http",
  "name": "httpbin",
  "read_timeout": 60000,
  "port": 80,
  "path": null,
  "updated_at": 1560595421,
  "retries": 5,
  "write_timeout": 60000,
  "tags": null
}

หลังจากที่สร้าง Service แล้ว จะยังเข้าถึง Service ไม่ได้ เพราะยังไม่มี Routes หรือทางเข้าไปยัง Services ถ้าเข้าไปยัง Proxy Port  http://127.0.0.1/

curl 127.0.0.1

จะได้ error

{
  "message": "no Route matched with those values"
}

สร้าง Routes เพื่อให้เรียกหา Service ได้

curl 127.0.0.1:8001/services/httpbin/routes -d 'paths[]=/'

จะได้ JSON Response

{
  "id": "4eaef1d0-b0e1-49d6-a160-81f9456933e7",
  "tags": null,
  "paths": [
    "/"
  ],
  "destinations": null,
  "protocols": [
    "http",
    "https"
  ],
  "created_at": 1560595643,
  "snis": null,
  "hosts": null,
  "name": null,
  "preserve_host": false,
  "regex_priority": 0,
  "strip_path": true,
  "sources": null,
  "updated_at": 1560595643,
  "https_redirect_status_code": 426,
  "service": {
    "id": "02fd187e-7976-44ce-88a6-604a707cdc9a"
  },
  "methods": null
}

ถ้าเรียกหน้าเว็บ http://127.0.0.1 จะได้หน้าเว็บเดียวกับ  http://httpbin.org

Kong API Gateway ฉบับรวบรัด

หรือว่าทดลองเรียกใช้ด้วย cURL เพื่อเช็ค IP ของเครื่องเรา

curl 127.0.0.1/ip

จะได้ IP ของเครื่องเรา

{
  "origin": "192.168.32.99, 184.22.153.99, 192.168.32.99"
}

จะเห็นว่า เราสามารถสร้าง API Gateway ได้ใน 2 คำสั่ง ซึ่งใช้เวลาในไม่กี่นาที เราก็ได้ API Gateway ที่เรียกไปยัง Microservice ข้างหลังได้ ถ้าจะเพิ่ม Services, Routes ใหม่ ก็จะเพิ่มได้ในไม่กี่นาที

Konga Kong Admin GUI Dashboard

Kong ไม่ได้ให้ระบบ Admin GUI Dashboard มาให้ในเวอร์ชั่น CE หรือ Community Edition แต่ก็มีคนสร้าง Admin GUI Dashboard เวอร์ชั่น Open source มาให้ใช้ https://github.com/pantsel/konga

ในไฟล์ docker-compose ที่ clone ลงมา จะมี service ให้อยู่แล้ว แต่ยังไม่ได้รันขึ้นมา ต้องรันขึ้นมาก่อน โดยต้องรัน 2 service คือ mongo, konga

docker-compose up -d mongo konga

หลังจาก run เสร็จแล้ว konga จะรันอยู่ที่ Port 1337 การเข้าใช้งานครั้งแรก จะต้องสร้าง username/password ก่อนเข้าใช้งาน

Kong API Gateway ฉบับรวบรัด

หลังจากที่สร้าง username/password แล้ว และ Login เข้าไปในระบบของ Konga จะได้หน้าให้ config admin url

Kong API Gateway ฉบับรวบรัด
  • Name ตั้งชื่ออะไรก็ได้
  • Kong Admin URL ให้ใช้ http://kong:8001/ เพราะรันอยู่ใน docker-compose สามารถเรียกด้วยชื่อ service ใน docker-compose ได้

จะได้หน้า Admin GUI

Kong API Gateway ฉบับรวบรัด

Kong Plugin

สิ่งที่ทำให้ Kong มีความยืดหยุ่นคือ มีระบบ Plugin (แต่ ยังไม่มี plugin market) ตัวอย่าง คือ จะเพิ่มระบบ Authentication ให้กับ API ที่สร้างขึ้นมา โดยจะใช้ Basic Auth ที่ต้องใส่ username/password ก่อน ถึงจะสามารถเรียก API ได้

เมนู Service

ในเมนู Service จะเห็นว่า มี Service ที่เราสร้างไว้ก่อนหน้านี้ผ่าน command line

Kong API Gateway ฉบับรวบรัด

กดเข้าไปใน httpbin จะเห็นรายละเอียดของ Service ที่สร้างไว้

Kong API Gateway ฉบับรวบรัด

จากนั้นให้ไปที่เมนู Plugins

Kong API Gateway ฉบับรวบรัด

กด Add Plugin ให้เลือก Basic Auth ไม่ต้องใส่ค่าอะไร

Kong API Gateway ฉบับรวบรัด

หลังจากที่เพิ่ม Plugin ไปแล้ว เวลาเรียกใช้ API จะต้องใส่ username/password

Kong API Gateway ฉบับรวบรัด
แต่ เรายังไม่มี username/password

สร้าง Kong Consumer

Consumer คือ คนที่จะมาเรียกใช้ API เวลาเรา Enable Plugin ที่เกี่ยวกับการ Authentication แล้ว จำเป็นที่จะต้องสร้าง Consumer ก่อน

ไปที่เมนู Consumers และสร้าง Consumer

Kong API Gateway ฉบับรวบรัด

จากนั้นสร้าง Credentials Basic Auth ให้กับ Consumer

Kong API Gateway ฉบับรวบรัด

หลังจากสร้าง Credentials แล้ว จะเข้าใช้เว็บหรือ API ได้

จบ

เดี๋ยวๆๆ ลืมขายของ

ที่เขียนมาทั้งหมด สามารถหาอ่านได้จากที่อื่นๆได้ ไม่ได้เป็นเรื่องยากมากสำหรับ Dev/Sys Admin  เท่าไหร่ แต่ สิ่งที่ Kong มีความยืดหยุ่นมากกว่านี้คือ เราสามารถที่จะเขียน Plugin ขึ้นมาได้เอง โดยใช้ภาษา Lua ที่เขียนอยู่บน OpenResty สำหรับบริษัทที่สนใจ อยากใช้ Kong ซึ่ง Kong มี 2 เวอร์ชั่นคือ EE (Enterprise Edition) และ CE (Community Edition) ทางบริษัท DomeCloud เป็น Partner กับ Kong Inc. ในการขาย Kong EE และนอกจากนี้ DomeCloud ยังรับ Implement Kong CE Plugin ด้วยติดต่อได้ทาง Fcebook page DomeCloud

]]>
<![CDATA[มาลองใช้ GoTTY ส่ง Terminal ออกไปยัง Web Browser]]>จากงาน DevOps BKK 2018 ใน Session ของอาจารย์ชาญวิทย์ แก้วกสิ นั้นมีปัญหาเร

]]>
https://i.dont.works/gotty/5b951d7948f15800017d5e77Sun, 09 Sep 2018 13:36:37 GMT

จากงาน DevOps BKK 2018 ใน Session ของอาจารย์ชาญวิทย์ แก้วกสิ นั้นมีปัญหาเรื่องของการต่อจอออกโปรเจคเตอร์ เลยใช้การ bind terminal ออกไปยังเว็บเบราเซอร์แทน โดยใช้ GoTTY และมีการถ่ายทอดสด ประเด็นคือว่า มีคนเปิดเว็บเบราเซอร์ไปที่ IP:PORT ที่อาจารย์ใช้อยู่ ก็เลยสามารถเข้าถึง shell ของเครื่อง demo ได้


ดูวิดีโอย้อนหลัง


หลังจากจบงาน ผมเพิ่งรู้ทีหลังว่ามีคนเข้าไปพิมพ์ว่าออกจาก vim ไม่ได้ ผมออกจากห้องก่อน เพราะต้องไปบรรยายใน session ถัดไปอีกห้องหนึ่ง จริงๆผมเคยใช้ GoTTY มาก่อนแล้ว ในการเอาตัว logs ของ kubernetes pods ออกไปดู

มาลองใช้ GoTTY ส่ง Terminal ออกไปยัง Web Browser
GoTTY Kubernetes Pods Logs 

วันนี้เลยลองทำ demo การใช้ GoTTY ให้ดู เผื่อว่าจะมีใครเอาไปใช้ต่อในโอกาสถัดไป


จบในส่วนของ GoTTY ฝาก session ที่ผมพูดไปในงาน DevOps BKK 2018 ด้วย https://github.com/narate/DevOps-BKK-2018

]]>
<![CDATA[มา ๆ พัง MariaDB Galera Cluster เล่นกัน]]>

ในปี 2018 ใครๆก็พูดถึง Kubernetes กัน หลายๆที่ ใช้กันเป็นปกติ เราก็ใช

]]>
https://i.dont.works/mariadb-galera-cluster/5b951c8a48f15800017d5e43Mon, 30 Jul 2018 10:37:11 GMT
มา ๆ พัง MariaDB Galera Cluster เล่นกัน

ในปี 2018 ใครๆก็พูดถึง Kubernetes กัน หลายๆที่ ใช้กันเป็นปกติ เราก็ใช้ที่ DomeCloud แต่ในบางงาน ที่รันอยู่มานานแล้ว ไม่ได้ย้ายไปอยู่บน Kubernetes แต่ก็ยังรันบน Docker อยู่ดี มีหลายๆเซอร์วิส ที่ยังคงทำงานได้ดีอยู่ จนกระทั่งเจอพญานาค

ไม่ว่าพญานาคที่ว่า จะมาจากไหน ช่างมัน วันนี้ ผมจะมาเล่าถึงการทำ MariaDB Cluster ด้วย Galera

มา ๆ พัง MariaDB Galera Cluster เล่นกัน

มา ๆ พัง MariaDB Galera Cluster เล่นกัน

บล็อกนี้ ผมจะมาเล่าถึงการทำ MariaDB Galera Cluster เพื่อแบ่งปันกันครับ ใครมีข้อแนะนำอะไร ส่งข้อความมาให้ผมแก้ได้เลยที่ https://m.me/koonnarate

เริ่มเลยดีกว่า

ตัว MariaDB รองรับการทำ Galera Cluster ในตัว ตั้งแต่เวอร์ชั่น 10.1 (Changes & Improvements in MariaDB 10.1)

ในบล็อกนี้ ผมจะทำตัวอย่าง การทำ Cluster ด้วย MariaDB 10.2 บน Docker แต่จะเป็น Docker บนเครื่องเดียวกัน "ไม่สามารถ เอาไปใช้งานจริงได้" ถ้าจะเอาไปใช้งานจริง ต้องแยกให้แต่ละโหนด อยู่คนละเครื่องเซิฟเวอร์ จะใช้ผ่าน Docker network macvlan หรือ จะใช้ Default bridge network แล้วรันด้วยการ map port ก็ได้เช่นเดียวกัน แต่ ควรที่จะรันให้อยู่ภายใต้ Private Network

ถ้าใครจะใช้การ map port ต้อง map port ตามนี้

  • 3306
  • 4567
  • 4568
  • 4444

แต่ละ Port คืออะไรบ้าง ตามไปดู Galera Cluster FIREWALL SETTINGS

ย้ำ บล็อกนี้ เทสให้ดูบน Docker ที่รันอยู่บนเครื่องเดียวกัน

แรกสุด สร้าง Network สำหรับ Docker ใช้ Bridge Driver และใส่ subnet เป็น 192.168.10.0/16 ชื่อ db-net

docker network create -d bridge \
    --subnet=192.168.10.0/16 \
    db-net

จะสร้าง Database 3 containers ชื่อ node0, node1, node2 โดยจะมี Galera config แบบนี้ สำหรับ node0

[mysqld]
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0

# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so

# Galera Cluster Configuration
wsrep_cluster_name="galera_cluster"
wsrep_cluster_address="gcomm://192.168.10.10,192.168.10.11,192.168.10.12"

# Galera Synchronization Configuration
wsrep_sst_method=rsync

# Galera Node Configuration
wsrep_node_address="192.168.10.10"
wsrep_node_name="node0"

สิ่งที่โหนดอื่นๆ จะต่างกัน จะมีส่วนนี้

# Galera Node Configuration
wsrep_node_address="192.168.10.10"
wsrep_node_name="node0"

นอกนั้นคอนฟิกเหมือนกัน

  • wsrep_cluster_address="gcomm://" คือ IP ของโหนดที่จะเข้ามา Join Cluster คั่นด้วย comma
  • wsrep_node_address คือ IP ของโหนดนั้นๆ
  • wsrep_node_name คือ ชื่อของโหนด

ในคอนฟิก ผมมี IP 3 IP คือ

  • 192.168.10.10 [node0]
  • 192.168.10.11 [node1]
  • 192.168.10.12 [node2]

รัน node0 ด้วย docker

docker run --rm -it --name node0 \
    --net=db-net \
    --ip=192.168.10.10 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node0/mysql:/var/lib/mysql \
    mariadb:10.2 --wsrep-new-cluster

สังเกตว่า ผมใช้ --rm -it เดี๋ยวจะมาเล่าให้ฟังทีหลัง

ที่ node0 รันขึ้นมาด้วย Image mariadb:10.2 และมี parameter --wsrep-new-cluster คือการบอกว่า เครื่องนี้ จะเป็น Primary

หลังจากรันคำสั่งข้างบนไป จะได้

Initializing database
... # ข้าม

Database initialized
MySQL init process in progress...
... # ข้าม
... view(view_id(PRIM,c4673b0e,1) memb {
        c4673b0e,0
} joined {
} left {
} partitioned {
})
... # ข้าม
 WSREP: Quorum results:
        version    = 4,
        component  = PRIMARY,
        conf_id    = 0,
        members    = 1/1 (joined/total),
        act_id     = 0,
        last_appl. = -1,
        protocols  = 0/8/3 (gcs/repl/appl),
        group UUID = c468f36c-93da-11e8-9cba-678b3ddd89a6
... # ข้าม
[Note] mysqld: ready for connections.
...

รันเสร็จ เราจะได้ MariaDB Cluster 1 node ที่เป็น Primary ค้างหน้านี้ไว้ก่อน ให้รัน node1 ต่อ ก่อนรัน node1 อย่าลืมแก้ Galera config ให้เป็นของ node1

docker run -d --name node1 \
    --restart=always \
    --net=db-net \
    --ip=192.168.10.11 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node1.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node1/mysql:/var/lib/mysql \
    mariadb:10.2

ที่ Logs ของ node0 จะมี

... # ข้าม
...view(view_id(PRIM,79991dba,2) memb {
        79991dba,0
        d2d34f12,0
} joined {
} left {
} partitioned {
})
... # ข้าม
2018-07-30  9:32:28 139968731133696 [Note] WSREP: Quorum results:
        version    = 4,
        component  = PRIMARY,
        conf_id    = 1,
        members    = 1/2 (joined/total),
        act_id     = 7176,
        last_appl. = 0,
        protocols  = 0/8/3 (gcs/repl/appl),
        group UUID = c468f36c-93da-11e8-9cba-678b3ddd89a6

เมื่อรัน node1 เสร็จแล้ว เช็คจำนวนโหนดใน Cluster ที่ node0

MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 2     |
+--------------------+-------+
1 row in set (0.00 sec)

ต่อไปก็รันโหนด node2

docker run -d --name node2 \
    --restart=always \
    --net=db-net \
    --ip=192.168.10.12 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node2.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node2/mysql:/var/lib/mysql \
    mariadb:10.2

หลังจากรันเสร็จแล้ว เช็คจำนวนโหนดใน Cluster อีกที

MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+
1 row in set (0.00 sec)

ตอนนี้ เราได้ Cluster ที่มี 3 node โดยเราสามารถเขียนลงที่โหนดไหนก็ได้

# สร้าง Database ชื่อ node0 ที่ node0
mysql -u root -p -h node0 -e "CREATE DATABASE node0;"
# แสดงรายชื่อ Database ที่ node2
mysql -u root -p -h node2 -e "SHOW DATABASES;"

# สร้าง และ แสดงชื่อ Database ที่โหนดต่างๆ
mysql -u root -p -h node1 -e "CREATE DATABASE node2;"
mysql -u root -p -h node1 -e "SHOW DATABASES;"
mysql -u root -p -h node2 -e "CREATE DATABASE node1;"
mysql -u root -p -h node0 -e "SHOW DATABASES;"

ต่อมา ลองลบ node0 container และข้อมูลที่ node0 มีอยู่ ออกทั้งหมด

docker rm -f node0
rm -rf data/node0

ถ้าดู Logs ที่โหนดอื่นๆ จะเห็น

WSREP: Quorum results:
	version    = 4,
	component  = PRIMARY,
	conf_id    = 5,
	members    = 2/2 (joined/total),
	act_id     = 7179,
	last_appl. = 0,
	protocols  = 0/8/3 (gcs/repl/appl),
	group UUID = c468f36c-93da-11e8-9cba-678b3ddd89a6

และก็ลบ node1 ทิ้งด้วย

docker rm -f node1
rm -rf data/node1

ตอนนี้ จะเหลือแต่ node2 เท่านั้น ที่ยังอยู่

MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 1     |
+--------------------+-------+
1 row in set (0.00 sec)

Database ทั้งหมด

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| node0              |
| node1              |
| node2              |
| performance_schema |
+--------------------+
6 rows in set (0.01 sec)

ต่อมา รัน node0 กลับคืนมา

docker run -d --name node0 \
    --restart=always \
    --net=db-net \
    --ip=192.168.10.10 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node0/mysql:/var/lib/mysql \
    mariadb:10.2

สังเกตว่า ไม่ได้ใช้ --rm -it แล้ว เดี๋ยวมาเล่า

และก็รัน node1

docker run -d --name node1 \
    --restart=always \
    --net=db-net \
    --ip=192.168.10.11 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node1.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node1/mysql:/var/lib/mysql \
    mariadb:10.2

ลองดู Database ที่มีอยู่ จะเท่ากับเท่าเดิม


MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| node0              |
| node1              |
| node2              |
| performance_schema |
+--------------------+
6 rows in set (0.02 sec)

หลังจากที่ node0, node1 หายไป แต่ node2 ยังอยู่ ข้อมูลก็ยังอยู่ อันนี้ เรายังมีข้อมูลอยู่ที่ โหนดอื่นๆ ไม่ได้ดับ หรือ โหนดหายไปพร้อมกัน ทั้งหมด แล้ว ถ้ามันหายไปพร้อมกันทั้งหมด เช่น เซิฟเวอร์ถูก reboot จะเป็นยังไงน่ะ ไหน ลองซิ...

ผมจะลอง restart docker เพื่อจำลองการ reboot เซิฟเวอร์

ลุย...

มา ๆ พัง MariaDB Galera Cluster เล่นกัน

Restart docker เสร็จแล้ว โหนดก็กลับมา แล้วข้อมูลยังอยู่ไหมน่ะ

มา ๆ พัง MariaDB Galera Cluster เล่นกัน

ฉิบ หาย แล้ว

หลังจากที่ เซิฟเวอร์ถูก reboot แล้ว login ไม่ได้ ซวยแล้วๆๆๆๆๆ พอลองดูที่ /var/lib/mysql ยังมีข้อมูลอยู่

มา ๆ พัง MariaDB Galera Cluster เล่นกัน

ในรูปคือ ที่ node0 พอลองกับโหนดอื่นๆ ก็ได้ error เหมือนกัน login ไม่ได้

เช็ค Logs ที่ node0 ก็จะเห็นประมาณนี้

 [Warning] WSREP: Quorum: No node with complete state:

	Version      : 4
	Flags        : 0x5
	Protocols    : 0 / 8 / 3
	State        : NON-PRIMARY
	Desync count : 0
	Prim state   : NON-PRIMARY
	Prim UUID    : 00000000-0000-0000-0000-000000000000
	Prim  seqno  : -1
	First seqno  : -1
	Last  seqno  : -1
	Prim JOINED  : 0
	State UUID   : c4aaf165-93df-11e8-bfcf-d6db3193273e
	Group UUID   : 00000000-0000-0000-0000-000000000000
	Name         : 'node1'
	Incoming addr: '192.168.10.11:3306'

	Version      : 4
	Flags        : 0x4
	Protocols    : 0 / 8 / 3
	State        : NON-PRIMARY
	Desync count : 0
	Prim state   : NON-PRIMARY
	Prim UUID    : 00000000-0000-0000-0000-000000000000
	Prim  seqno  : -1
	First seqno  : -1
	Last  seqno  : -1
	Prim JOINED  : 0
	State UUID   : c4aaf165-93df-11e8-bfcf-d6db3193273e
	Group UUID   : 00000000-0000-0000-0000-000000000000
	Name         : 'node0'
	Incoming addr: '192.168.10.10:3306'

	Version      : 4
	Flags        : 0x4
	Protocols    : 0 / 8 / 3
	State        : NON-PRIMARY
	Desync count : 0
	Prim state   : NON-PRIMARY
	Prim UUID    : 00000000-0000-0000-0000-000000000000
	Prim  seqno  : -1
	First seqno  : -1
	Last  seqno  : -1
	Prim JOINED  : 0
	State UUID   : c4aaf165-93df-11e8-bfcf-d6db3193273e
	Group UUID   : 00000000-0000-0000-0000-000000000000
	Name         : 'node2'
	Incoming addr: '192.168.10.12:3306'

[Warning] WSREP: No re-merged primary component found.
[Note] WSREP: Bootstrapped primary 00000000-0000-0000-0000-000000000000 found: 3.
[Note] WSREP: Quorum results:
	version    = 4,
	component  = PRIMARY,
	conf_id    = -1,
	members    = 3/3 (joined/total),
	act_id     = -1,
	last_appl. = -1,
	protocols  = 0/8/3 (gcs/repl/appl),
	group UUID = 00000000-0000-0000-0000-000000000000

ก็มี 3 members แล้วนี่หว่า แต่มันไม่เจอ Primary เราต้อง หาทางเอา Primary กลับมา ผมลองรัน --wsrep-new-cluster ที่ node0 ใหม่ โดยลบ node0 ออกก่อน

docker rm -f node0
docker run --rm -it --name node0 \
    --net=db-net \
    --ip=192.168.10.10 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node0/mysql:/var/lib/mysql \
    mariadb:10.2 --wsrep-new-cluster

ปรากฏว่า Container ตาย แต่ถ้าดู Logs ดีๆ จะเห็นว่า

[Note] WSREP: Start replication
[Note] WSREP: 'wsrep-new-cluster' option used, bootstrapping the cluster
[Note] WSREP: Setting initial position to 00000000-0000-0000-0000-000000000000:-1
[ERROR] WSREP: It may not be safe to bootstrap the cluster from this node. It was not the last one to leave the cluster and may not contain all the updates. To force cluster bootstrap with this node, edit the grastate.dat file manually and set safe_to_bootstrap to 1 .

อันนี้ edit the grastate.dat file manually and set safe_to_bootstrap to 1

ผมแก้ไฟล์ grastate.dat

จาก

# GALERA saved state
version: 2.1
uuid:    c468f36c-93da-11e8-9cba-678b3ddd89a6
seqno:   -1
safe_to_bootstrap: 0

แก้

safe_to_bootstrap: 0

เป็น

safe_to_bootstrap: 1

แล้วรันใหม่

docker run --rm -it --name node0 \
    --net=db-net \
    --ip=192.168.10.10 \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
    -v $(pwd)/data/node0/mysql:/var/lib/mysql \
    mariadb:10.2 --wsrep-new-cluster

แล้วก็เข้าไปดูใน Container จะเห็น Cluster size เป็น 1 อยู่ แต่ Database ยังอยู่ครบ

มา ๆ พัง MariaDB Galera Cluster เล่นกัน

จากนั้นก็ restart node1, node2

docker restart node1 node2

จากนั้นก็เป็นการเสร็จพิธี

มา ๆ พัง MariaDB Galera Cluster เล่นกัน


อ่านเพิ่มเติม Introducing the “Safe-To-Bootstrap” feature in Galera Cluster

จบแล้ว เท่านี้ ถ้าใครอ่านแล้วถูกใจ อยากบริจาคค่าเบียร์ ค่ากาแฟ กดบริจาคผ่าน PayPal ได้ที่ GHOST_URL/donate/ กดหลายๆแก้วก็ได้


Source code : https://github.com/narate/mariadb-galera-cluster

]]>
<![CDATA[Monitor OpenResty ด้วย Prometheus]]>

Screen-Shot-2561-01-29-at-01.29.00-2

TLDR; Monitor Nginx Connections ด้วย Prometheus และแสดงกราฟด้วย Grafana

ขี้เกียจเขียนเยอะ เอาสั้นๆ

]]>
https://i.dont.works/nginx-prometheus-metrics-example/5b951c8a48f15800017d5e40Sun, 28 Jan 2018 19:49:10 GMTMonitor OpenResty ด้วย Prometheus

Monitor OpenResty ด้วย Prometheus

TLDR; Monitor Nginx Connections ด้วย Prometheus และแสดงกราฟด้วย Grafana

ขี้เกียจเขียนเยอะ เอาสั้นๆพอ

Prometheus คือตัว ระบบมอนิเตอร์ และ Time Series Database มันจะไปดูดสิ่งที่เรียกว่า Metrics มาเก็บไว้ที่ตัวมัน ทุกๆ x วินาที ตั้งได้

เอารูปไปดู 1 รูปพอ

Monitor OpenResty ด้วย Prometheus

เราอยากรู้ข้อมูลอะไร เราก็เอาโปรแกรมไปรันสำหรับสร้าง Metrics เช่น node_exporter ที่มีในเว็บของ Prometheus เองก็เอาไว้ส่ง Metrics ของเครื่องนั้นๆ เช่น Disk, RAM, Network

อีกอย่างที่ต้องรันคือ Prometheus มันเป็นไบนารีมาให้รันเลย เขียนคอนฟิกเพิ่มนิดหน่อย เพื่อบอกว่า ไปดูด Metrics จากที่ไหนบ้างมาเก็บ

จริงๆตัว Prometheus มันมีระบบสร้างกราฟให้แหละ แต่ไม่เท่ ใช้ Grafana cool กว่า

จะไม่ทำ How to ที่ละเอียด เพราะขี้เกียจ ผมจะมโนว่า ทุกคนที่อ่าน ใช้ OpenResty เป็นแล้ว

ใช้ Nginx Prometheus Metrics วิธีการคือ โหลดไฟล์ หรือโคลนมันมาเก็บไว้ จากนั้นเพิ่มคอนฟิกใน nginx ให้รู้จัก package.path แบบนี้ เขียนไว้ใน http context น่ะ นอก server context

ดูไฟล์ nginx.conf ก่อน

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    #include       mime.types;
    default_type  text/html;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location / {
            content_by_lua_block {
                ngx.say("hello world")
            }
        }
    }
    include *.vhost;
}

เชี้ยย ก๊อปมายาวเลย มันจะมี *.vhost เอาไฟล์ไว้ข้างๆ nginx.conf เดี๋ยวมันจะหากันไม่เจอ แล้วดูไฟล์ metrics.vhost 2 บรรทัดบนก่อน เดี๋ยวค่อยแยกมาดู

lua_shared_dict prometheus_metrics 10M;
lua_package_path '/usr/local/openresty/luajit/lib/?.lua;;';
...

lua_shared_dict prometheus_metrics 10M; อันนี้คือ Shared dict ที่ไว้ให้ Lua เอาไปเก็บค่าต่างๆ ต้องเขียนไว้ใน nginx config file ด้วย ส่วน lua_package_path คือบอกว่า lua library ไปหาได้จากที่ไหนบ้าง เอาไฟล์จาก repo ข้างบนไปใส่ตามในนี้ /usr/local/openresty/luajit/lib/

กลับมาดูไฟล์ metrics.vhost ต่อ ผมจะข้าม init_by_lua_block, log_by_lua_block ไปลอกมาใส่ หรือ เอาไฟล์มาใช้เลย ก๊อปๆมา (บาปส์) มาดูส่วนของ server เลยส่วนนี้คือการสร้าง virtual host สำหรับให้ Prometheus มาดูด metics ไปเก็บ โดยเปิดพอร์ท 9527 ไว้

server {
    listen 9527;
    #allow 192.168.0.0/16;
    #deny all;
    location /metrics {
        content_by_lua_block {
            if ngx.var.connections_active ~= nil then
                http_connections:set(ngx.var.connections_active, {"active"})
                http_connections:set(ngx.var.connections_reading, {"reading"})
                http_connections:set(ngx.var.connections_waiting, {"waiting"})
                http_connections:set(ngx.var.connections_writing, {"writing"})
            end
            prometheus:collect()
        }
    }
}

เมื่อใส่ไฟล์ครบแล้ว reload nginx แล้วเปิดไปที่ IP:9527/metrics จะได้ข้อความหน้าตาแบบนี้

Monitor OpenResty ด้วย Prometheus

ต่อมาให้รัน Prometheus โดยไปเลือกดาวน์โหลดได้จากที่นี่ https://prometheus.io/download/ เลือก prometheus-2.x.-ARCH.tar.gz ตามแพลตฟอร์มที่ใช้ แล้วแตกไฟล์มา ให้แก้ไฟล์ prometheus.yml แล้วเพิ่ม targets ไปยัง IP:9527 ที่รัน OpenResty ไว้

# ข้าม
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ['localhost:9090']
      - targets: ['IP:9527']

เสร็จแล้วรัน ./prometheus เพื่อเริ่มทำงาน แล้วเปิดไปดูที่ localhost:9090 จะได้หน้าเว็บสำหรับ Query ข้อมูล

Monitor OpenResty ด้วย Prometheus

เลือก nginx_http_connections ก็จะได้ข้อมูล และสร้างกราฟได้

Monitor OpenResty ด้วย Prometheus

ต่อมาใช้ Grafana กัน

เอา docker รันขึ้นมาเลย

docker run -d --name=grafana -p 3000:3000 grafana/grafana

รันเสร็จ เปิดไปที่ http://localhost:3000 ใช้ Default username/password admin/admin แล้วไปตรง Datasource ใส่ URL ของ Prometheus ที่รันไว้

Monitor OpenResty ด้วย Prometheus

กดเซฟ แล้วไปสร้าง Dashboard กด Add Row เลือก Graph

Monitor OpenResty ด้วย Prometheus

ในหน้าสร้างกราฟ เลือก Datasource และใส่ Query

nginx_http_connections{instance="IP:9527"}

Monitor OpenResty ด้วย Prometheus

ต่อไปเพิ่มตัว Singlestat ใส่ Query

nginx_http_connections{instance="IP:9527", state="active"}

Monitor OpenResty ด้วย Prometheus

จะได้แบบนี้

Monitor OpenResty ด้วย Prometheus

จบ

]]>
<![CDATA[แฮกกัญเมาๆ EP2 | OWASP TOP 10 A1-Injection]]>

หลังจาก แฮกกัญเมาๆ EP1 - CHMOD 777 (ชามด) ผ่านไป 1 อาทิตย์ ผมกับมิตรสห

]]>
https://i.dont.works/owasp-top-10-a1-injection/5b951c8a48f15800017d5e3fMon, 16 Oct 2017 04:05:50 GMT
แฮกกัญเมาๆ EP2 | OWASP TOP 10 A1-Injection

หลังจาก แฮกกัญเมาๆ EP1 - CHMOD 777 (ชามด) ผ่านไป 1 อาทิตย์ ผมกับมิตรสหายท่านหนึ่ง ก็ทำคลิป แฮกกัญเมาๆ EP2 | OWASP TOP 10 A1-Injection ตามที่ได้วางแผนกันเอาไว้ คือจะทำอาทิตย์ละ 1 คลิป ในช่วงแรกๆ จะพูดถึง OWASP TOP 10

แฮกกัญเมาๆ EP2 | OWASP TOP 10 A1-Injection
Photo by Ashim D’Silva / Unsplash

OSASP TOP 10 มีอะไรบ้าง ตามไปดูได้ที่ OWASP TOP 10 - 2017

ในตอนนี้จะเขียนเรื่องแรกก่อน A1-Injection

Injection ก็คงคุ้นเคยดี จะมี SQL, LDAP, OS (Command) injection ในคลิปข้างล่างนี้ จะพูดถึง 2 ตัวอย่างคือ LDAP, OS (Command) Injection โดยตัว LDAP จะยกตัวอย่างเคสของ Joomla! ที่มีช่องโหว่ LDAP injection "Joomla! 3.7.5 - Takeover in 20 Seconds with LDAP Injection"

และส่วนของ OS (Command) injection จะยกตัวอย่างการเขียนโค้ด PHP ที่เรียกใช้ shell_exec โดยไม่กรองค่า หรือ ไม่ตรวจสอบ input ให้ดีก่อนทำงาน ทำให้ attacker สามารถส่งโค้ดไปรันบนเครื่องและยึดเครื่องเซิฟเวอร์ได้ ไม่ว่าจะเป็นการดาวน์โหลดมัลแวร์ไปรัน หรือทำการ reverse shell ก็สามารถทำได้ และถ้าเซิฟเวอร์เครื่องนั้น โชคร้าย ใช้ OS ที่มีบั๊กอยู่ attacker ยังสามารถยกสิทธิ์ตัวเองเป็น root ได้ด้วย

ที่พิมพ์มาทั้งหมด มีพูดอยู่ในคลิป เชิญชม

id
uid=0(root) gid=0(root) groups=0(root)
]]>
<![CDATA[แฮกกัญเมาๆ EP1 - CHMOD 777 (ชามด)]]>

อัดคลิปกับมิตรสหายท่านหนึ่ง พูดเรื่อง Linux file permission แบบเมาๆ เมาด

]]>
https://i.dont.works/basic-linux-file-permission/5b951c8a48f15800017d5e3eThu, 12 Oct 2017 04:14:53 GMT
แฮกกัญเมาๆ EP1 - CHMOD 777 (ชามด)

อัดคลิปกับมิตรสหายท่านหนึ่ง พูดเรื่อง Linux file permission แบบเมาๆ เมาดิบ เพราะว่า อัดคลิปกัญตอนราวๆตอนเที่ยง สรุปคือ ผมเมาตลอดเวลา

แฮกกัญเมาๆ EP1 - CHMOD 777 (ชามด)

TL;DR

4 = r = read
2 = w = write
1 = x = execute
0 = no permission

Linux file permission

ตัวอย่าง

-rw-r--r--  1 narate  staff       97 Oct  5 17:46 x.txt

- ตัวแรกคือชนิดไฟล์ เป็นไปได้คือ ไฟล์ทั่วไป (-), ไดเรคทอรี่ (d) หรือ อาจจะเป็น link (l)
rw- 3 ตัวถัดมาคือ user (u) หรือ เจ้าของไฟล์
r-- 3 ตัวถัดมาคือ group (g) หรือ กลุ่ม
r-- 3 ตัวถัดมาคือ other (o) หรือ คนอื่นๆ

กลุ่มที่เป็น 3 ตัว เป็นไปได้คือ rwx เรียงตามนี้ อันนี้ไม่มีค่า ให้เป็น - ค่ารวมกัน จะได้ 0-7 ในแต่ละกลุ่ม

แบบจำไว้ใช้

ค่า สิทธ์
4 r, read
2 w, write
1 x, execute
0 no permission

ที่มาของตัวเลข

rwx = 4 + 2 + 1 = 7
rw- = 4 + 2 + 0 = 6
...

777 คืออะไร

-rwxrwxrwx = 4 + 2 + 1, 4 + 2 + 1, 4 + 2 + 1 = 7,7,7

ค่า 4, 2, 1, 0 มาจากไหน มีพูดในคลิป

พิมพ์ 777 แล้วจะโชคดี


                             ,,s#@@####W%%%W###@@Mm,,
                        ,#@#WT7`,,ssmM%@#5@#KMmmg,;|75%@Mw
                    a##%^,,s#7@Q,#M45j"""335"5%=#@,@#"Mw,|7@@w
                 s@M7 s4@Q,Mjk     \[.]',"J," #    `,"%Q,@#m,^%@W
              s@#\,s"@#*}-2;,j.,-  ` ,W4k=W=4`'`^\,,'^,`W4*#@7%m/%@m
            #@C`xC7#Cy4,]`%,jx       @.......b      ,w,,M`,-=3@#7@,"@m
          #@C #`]M^~. Vb|x",<,4 `d```[`b ,w`~,*qb|^]^^%@^@q^@k ;@c #^@c , v~`-^,s^v b } a"w,".`~" * 7@7@q"@m @#^,%@m k \x^\ \ | ` ^ ^\.` v"@5@ %@ ,@# #@#\f;< " ,. m^[ [,=",`" .~ q@#wq7@q ,@b @m#;..x-#,`m, v w {,,b *"^` `[" ,b,,<[ .%^ w-d.\%#@m^@q @,#{<"k.c ,^ '". q)w `j="Q2@emmm@QK=#" .@ ( -^^p}'ok.m"%1,#k'@q @# #7# \^{w^^ q `- "-o mq#qk^``, `"="3WQWQ"">~^ ,~`  y^,V\<<,}1^7m^@ @# {## ` j ".="," `- .km\,4aq<. f`j ~ `'` "qw5, ]x7, ,-` .\-^` ] @#@p@@ @b @eb `~ \, ;;#^ _ooooo_ "jqk .m^ .' b @@@ @p {@ @,@]-, ,.@`#{q~ o8888888o lk%^|k. \.-}@p@~@q #"b[,,qp[]~ l, ]q^ 88" . "88 dbb g4, ;>Qw-jjC7bj@
 @# @@bb~,ck[] .......,@`@@]{        (| -_- |)        }@]`2D.......|  ~Qw=^j@@# @
 @# Wsbb",,b[| ,,,,,,},, }@|         O\  =  /O        ]@] ,,o,,,,,,|  ~k,a dks# @
 @# #"b@]j,b[| -------.4 7]d      ____/`---'\____     L##`},-~~~~--|  ~k;" j^"#j@
 @@ @#Q|7=C           b , b@}   .'  \\|     |//  `.  /@]^. ,>           >*3{%@b{#
 j@pjM@    b          -`:, w@  /  \\|||  :  |||//  \ /#,b,k `-         [   @@@ @b
                              /  _||||| -:- |||||_  \
                              |   | \\\  -  /'| |   |
                              | \_|  `\`---'//  |_/ |
                              \  .-\__ `-. -'__/-.  /
                            ___`. .'  /--.--\  `. .'___
                         ."" '<  `.___\_<|>_/___.' _> \"".
                        | | :  `- \`. ;`. _/; .'/ /  .' ; |
                        \  \ `-.   \_\_`. _.'_/_/  -' _.' /
              ===========`-.`___`-.__\ \___  /__.-'_.'_.-'================
                           `=--=-'                    hjw
^[ [^ascii ^art ^generator](http://my.asciiart.club) ^] 

]]>
<![CDATA[รีวิว API Docฯ Genฯ แบบลวกๆ เร็วกว่าต้มมาม่า]]>

สั้นๆ จบย่อหน้าเดียว ลูกพี่สั่ง ให้เขียนเอกสารประกอบ API

]]>
https://i.dont.works/quick-review-api-docs-gen/5b951c8a48f15800017d5e3dWed, 20 Sep 2017 20:06:49 GMT
รีวิว API Docฯ Genฯ แบบลวกๆ เร็วกว่าต้มมาม่า

สั้นๆ จบย่อหน้าเดียว ลูกพี่สั่ง ให้เขียนเอกสารประกอบ API โดยตอนแรก ลูกพี่ส่งตัว MkDocs และถามผมว่าจะใช้อะไรเขียน ผมบอกว่าใช้ Swagger ลองๆเขียนไปสักพัก ใช้ยากฉิบหาย สุดท้าย ผมเลือก Slate จบ

รีวิว API Docฯ Genฯ แบบลวกๆ เร็วกว่าต้มมาม่า
Photo by Samuel Zeller / Unsplash

รีวิวแบบยาวหน่อยๆ

บอกไว้ก่อน ทั้งหมดที่กำลังจะอ่านนี้ ผมเพิ่งลองใช้ได้ไม่นาน และไม่ได้อ่านเอกสารอย่างถ้วนถี่ๆๆๆๆ แฮ่กๆๆๆ

เริ่มเลย

ดาวบน GitHub

ชื่อ จำนวนดาว
MkDocs https://github.com/mkdocs/mkdocs/ 4,469
Swagger Editor https://github.com/swagger-api/swagger-editor 2,717
Slate https://github.com/lord/slate 20,022

Slate ชนะ ไสย์ๆ

ภาษาที่ใช้สร้าง

ชื่อ ภาษา
MkDocs Python
Swagger JavaScript/NodeJS
Slate Ruby

ข้อนี้ ผมชอบ Python, NodeJS ตามลำดับ ผมไม่ได้ลอง MkDocs เลยไม่รู้ว่ามันทำงานดีไหม ตัว Swagger Editor มันแก้แล้วเห็นแบบทันที ส่วน Slate แก้เสร็จแล้วต้องเซฟไฟล์และ Reload หน้าเว็บ ซึ่ง โหลดช้า Ruby เร็วส์... ส่วน Swagger Editor แม่งเซฟไฟล์หน้าเว็บไม่ได้ดาวน์โหลดได้อย่างเดียว งั้นให้ Slate ชนะไปแล้วกัน

ภาษา หรือ Syntax ที่ใช้เขียน Docฯ

ชื่อ ภาษา
MkDocs ยาแมว (YAML) ใช้สร้าง Layout, ใช้ Markdown เขียนเอกสาร
Swagger ใช้ยาแมว (YAML) เขียน (default) และ Import/Export JSON
Slate Markdown 1 ไฟล์ จบ

เห็น MkDocs แล้วกดปิดแทบไม่ทัน มันยุ่งไป ต้องสร้าง Layout เอง เขียนยาแมว แล้วก็เขียน Markdown ของแต่ละหน้าอีกที ส่วนตัว Swagger Editor มันมาเป็นยาแมว แล้วแม่ง ผมเขียนไม่เป็น กด tab ผิดฟ้อง error รัวๆเลย มาจบที่ Slate เขียน Markdown ไฟล์เดียวเลย หรือจะแยกไฟล์ก็ได้ Slate ชนะไปอีก 1 เม็ด

ฟีเจอร์เด็ดของแต่ละอัน

ชื่อ ฟีเจอร์
MkDocs ไม่ได้ลอง
Swagger gen client ได้, ลองรันโค้ดสร้าง request ผ่านหน้าเว็บได้เลย
Slate เขียนโค้ดตัวอย่าง ให้เลือกดูได้หลายๆภาษา

ฟีเจอร์ของ Swagger ดี ชนะตรงมันรันทดสอบได้เลย แต่ส่วนของ Slate ก็ดีตรงที่ มันเขียนโค้ดตัวอย่างให้เลือกได้ สรุป ด้วยความเอนเอียง Slate ชนะ

สรุป

ข้อดี

  • Swagger
    แก้ YAML หน้าเว็บและเห็นผลเลย รันทดสอบเรียกใช้ API ได้ผ่านหน้าเว็บ

  • Slate
    เขียนโค้ดตัวอย่างได้ เขียนเอกสารง่าย เพราะมันคือ Markdown

ข้อเสีย

  • Swagger
    ไม่ใช่ข้อเสียของ Swagger แต่ผมเขียน YAML ไม่เป็น
  • Slate
    แก้โค้ดเสร็จแล้ว ต้องโหลดหน้าเว็บใหม่ ซึ่งโหลดช้า ขนาดอยู่ที่ localhost น่ะ

จบท้าย ด้วยรูป

ผู้ชนะอันดับ 1 Slate

https://github.com/lord/slate

รีวิว API Docฯ Genฯ แบบลวกๆ เร็วกว่าต้มมาม่า

รองอันดับ 1 Swagger Editor

https://swagger.io/swagger-editor/

รีวิว API Docฯ Genฯ แบบลวกๆ เร็วกว่าต้มมาม่า

ที่โหล่

http://www.mkdocs.org/#overview

รีวิว API Docฯ Genฯ แบบลวกๆ เร็วกว่าต้มมาม่า

a+rwx

]]>