Skip to main content

Docker with NGINX

พอได้คอนเซปต์คร่าวๆของ Docker แล้ว ทีนี้ก็ลองมารัน NGINX ดู ที่คิดไว้มีน่าเล่นอยู่ 2 อย่างคือ

  1. รัน NGINX หลายๆ subdomain แบบว่า www.ryyyyyy.com กับ hello.ryyyyyy.com เปิดขึ้นมาแล้วขึ้นคนละหน้าเว็บกัน ถึงจะรันอยู่ใน server เดียวกัน
  2. ปรับให้ NGINX แสดงเว็บ SPA (พวกเว็บ React, Svelte) แล้วไม่ขึ้น not found แบบว่าสามารถเข้าลิงค์ www.ryyyyyy.com/project-detail/10 ตรงๆได้โดยไม่พัง

NOTE: ตอนนี้พี่เซ็ท vm-cherry ไว้แค่ port 80 นะ เอาไว้รับ proxy จาก cloudflare อย่างเดียวก่อน ถ้าเชอรี่อยากลอง connect ด้วยพอร์ตอื่นก็บอกก อยากได้เลขอะไรเดี๋ยวเพิ่มให้5555

NGINX Fundamental

ตัว NGINX เป็น web server นี่แหละ แล้วเราสามารถปรับค่ามันได้ด้วยไฟล์ config เหมือนกัน แต่ถ้าเป็น kmuttBot มันจะอ่าน config ไฟล์เดียวคือ config.yaml ใช่ป่ะ แต่ NGINX มันจะให้เราเขียน config หลายๆไฟล์ได้ โดยมันกำหนดไว้ว่าไฟล์ config จะอยู่ในโฟลเดอร์ /etc/nginx/conf.d/ เสมอ และไฟล์จะต้องมีชื่อลงท้ายด้วย .conf ด้วย (ตามนี้ https://stackoverflow.com/a/22164038)

บางตำราบอกว่า /etc/nginx/sites-available/ ก็ถูกเหมือนกัน แต่อันนั้นจะต้องผ่าน symlink ซึ่งไว้กลับมาเก็บตกดีกว่า5555

โดยแต่ละไฟล์ config จะมีหน้าตาประมาณนี้

server {
        server_name beta.bsthun.com;
        listen 80;

        root /usr/share/nginx/homepage;

        location / {
                try_files $uri /index.html =404;
        }

        location /api/ {
                proxy_pass http://10.5.51.111:3000/api/;
        }

        error_log off;
}

คร่าวๆก็คือว่าเราจะกำหนดว่า (line 2) เข้า config นี้จากโดเมนไหน, (line 3) จาก port อะไร, (line 5) ถ้าเข้าเว็บนี้จะไปโหลดหน้าเว็บจากโฟลเดอร์ไหน และพวก location คือกำหนด rule ตามแต่ละ path ได้ด้วยว่า path ไหนไปไหน ในที่นี้คือกำหนดว่า path / ให้โหลดหน้าเว็บปกติ ถ้า path /api/ ก็จะ reverse proxy ไปหา backend อะไรแบบนี้

(ถ้าสนใจเรื่อง proxy ลองดู https://tountoon.medium.com/dbf095581e4e ได้นะ)

แต่ถ้าเราไม่ config อะไรเลย default ของมันจะไปโหลดหน้าเว็บจากโฟลเดอร์ /usr/share/nginx/html อ่ะ ก็เลยเป็นเหตุผลว่าทำไม docker-compose.yml ของเว็บเชอรี่ตอนนี้เลย mount ./data เข้าไปหา /usr/share/nginx/html แล้วสามารถแสดงผลไฟล์ https://bot.ryyyyyy.com/kmutt-grade.json ได้เลยโดยไม่ต้องมี config

Configuring Service

คอนเซปต์ของการ config nginx multi-site จริงๆ ref จากนี้เลย https://www.learnbestcoding.com/post/15/hosting-multiple-websites-with-nginx แต่เอามาปรับให้เป็นแนว docker อ่ะ

โดยปกติ NGINX มันจะต้อง mount 2 folder ใช่ม้ะ ก็คือโฟลเดอร์ /etc/nginx/conf.d/ (เก็บ config) กับ /usr/share/nginx/ (เก็บหน้าเว็บ) ถ้างั้น step แรกก็คือปรับ volumes ให้ mount 2 โฟลเดอร์ ก็จะได้หน้าตาแบบนี้

version: '3.5'

services:
  web:
    ...
    volumes:
      - type: bind
        source: ./data/
        target: /usr/share/nginx/
        read_only: true
      - type: bind
        source: ./config/
        target: /etc/nginx/conf.d/
        read_only: true
    ...

พอปรับเสร็จแล้วก็ลอง docker compose up ทีนึง มันก็น่าจะ apply change ของเราลง container เรียบร้อยย

ต่อมาก็สร้างไฟล์ config สำหรับ 3 เว็บให้อยู่ในโฟลเดอร์ config ของเรา

  • example1.conf
    server {
    	server_name www.ryyyyyy.com;
    	listen 80;
    	
    	root /usr/share/nginx/something1;
    
    	location / {
    		try_files $uri /index.html =404;
    	}
    }
  • example2.conf
    server {
    	server_name bot.ryyyyyy.com;
    	listen 80;
    	
    	root /usr/share/nginx/something2;
    }
  • example3.conf
    server {
    	server_name ryyyyyy.com;
    	listen 80;
    	
    	return 301 $scheme://www.ryyyyyy.com$request_uri;
    }

    (ให้ redirect ไปที่ www.ryyyyyy.com อะไรงี้)

หน้าตาของ project structure ตอนจบก็น่าจะแบบว่า

  • vm-cherry
    • ...
    • web
      • docker-compose.yml
      • config
        • example1.conf (เปลี่ยนชื่อเลย)
        • example2.conf
        • example3.conf
      • data
        • something1 (ตั้งชื่อให้ที ;-;)
          • index.html
          • favicon.ico
          • static
            • ...
          • ...
        • something2
          • kmutt-grade.json

ส่วนในโฟลเดอร์ data ก็เอาหน้าเว็บแต่ละเว็บมาแปะลงไปตามโฟลเดอร์ได้เลยย

แต่รอบนี้เราไม่ได้ปรับ docker-compose.yml ใช่ป่ะ เราแค่ปรับ nginx config ดังนั้น docker compose up มันจะไม่ take effect ใดๆละ ก็เลยต้องใช้ docker compose restart แทน เอาไว้ restart service ให้ reload config ของมันใหม่ แต่ไม่ได้แก้ docker container ไรงี้

Configuring DNS

รู้สึกตอนนี้เชอรี่ใช้ cloudflare ใช่ป่ะ (ถ้าผิดบอกด้วย555) จริงๆก็แค่เพิ่ม record มาได้เลยว่าอยาก point subdomain อันไหนมาที่เซิฟเวอร์ ซึ่งข้างหลังก็คือ server2e2.ns.bsthun.com เลย

สมมุติอยากให้ ryyyyyy.com กับ www.ryyyyyy.com พอยท์มาที่เซิฟเวอร์พี่ (ซึ่งข้างหลังก็คือ vm-cherry นั่นแหละ) ก็สามารถกด add records เป็น

  1. Type: CNAME, Name: @, Target: server2e2.ns.bsthun.com
  2. Type: CNAME, Name: www, Target: server2e2.ns.bsthun.com
  3. Type: CNAME, Name: bot, Target: server2e2.ns.bsthun.com

แบบนี้ได้เลย พอเราเข้าเว็บที่โดเมนนั้นๆ มันก็จะเชื่อมไปหาเซิฟเวอร์ที่เรารัน nginx เอาไว้ที่ port 80 อะไรงี้

Screenshot 2023-08-02 at 9.00.11 PM.png


ถ้าทำทุก step เรียบร้อยแล้ว ละพอยท์โดเมนมาถูกที่แล้ว พอเข้าเว็บต่างๆมันก็ควรจะขึ้น log ใน nginx ที่เชอรี่รัน แล้วหน้าเว็บก็น่าจะแสดงขึ้นมาปกติเลยย

อันนี้คิดว่าพอเป็น guide ไว้ลองเล่น nginx ได้อยู่ แต่จริงๆมีอะไรให้เล่นอีกเยอะเลย เช่น เราสามารถเก็บ log ได้ ว่าใครเข้ามาดูเว็บเราบ้าง (https://www.digitalocean.com/community/tutorials/nginx-access-logs-error-logs) หรือทำ proxy ไปหา api หลังบ้าน (https://kinsta.com/blog/reverse-proxy/ ตรงล่างๆ) ฯลฯ