รัน Redis ด้วย Docker
ทดสอบบน Redis 8.10.2 (
redis:8.10.2-alpine), Docker Desktop บน Mac (Apple silicon), ioredis 6.0.0 ตอนรันจริงใช้ชื่อ container ขึ้นต้นredis-b1-ส่วนในบทใช้ชื่อredisเพื่อให้คัดลอกได้
Docker คือทางลัดที่เร็วที่สุดสู่ Redis บนเครื่อง
หัวข้อที่มีชื่อว่า “Docker คือทางลัดที่เร็วที่สุดสู่ Redis บนเครื่อง”คุณไม่จำเป็นต้องติดตั้ง Redis บนเครื่อง คำสั่ง docker run คำสั่งเดียวจะดึง official image และสตาร์ทเซิร์ฟเวอร์ที่พร้อมใช้ภายในไม่กี่วินาที
อธิบายคำสั่งเริ่มต้น
หัวข้อที่มีชื่อว่า “อธิบายคำสั่งเริ่มต้น”docker run -d --name redis -p 6379:6379 redis:8-alpine| Flag | ทำอะไร |
|---|---|
-d | รันแบบ detached (เบื้องหลัง) เพื่อให้ terminal ยังว่างอยู่ |
--name redis | ตั้งชื่อ container ที่คงที่ เพื่ออ้างถึงในคำสั่งถัด ๆ ไป |
-p 6379:6379 | publish port 6379 ของ container ไปยังเครื่อง host ด้วย port เดียวกัน |
redis:8-alpine | ใช้ official Redis 8 image ที่สร้างบน Alpine Linux — เล็กและเร็ว |
ใช้ tag ลอย หรือ tag ตายตัว?
หัวข้อที่มีชื่อว่า “ใช้ tag ลอย หรือ tag ตายตัว?”redis:8-alpine เป็น tag ที่ เลื่อนได้; redis:8.10.2-alpine คือ release เดียว ในวันที่ตรวจสอบบทนี้ทั้งสองชี้ไปที่ image เดียวกัน:
$ docker image ls redisredis:8-alpine 675f7644831dredis:8.10.2-alpine 675f7644831dimage ID เดียวกัน วันนี้จึงเหมือนกัน แต่เดือนหน้า 8-alpine จะไม่ใช่ ใช้ tag ลอยสำหรับแล็บชั่วคราว และใช้ tag เต็ม (หรือ digest) ในทุกอย่างที่ต้อง reproduce ได้ — bug report, CI job, compose file คอร์สนี้ pin 8.10.2 ในการทดสอบ
อ่าน startup banner
หัวข้อที่มีชื่อว่า “อ่าน startup banner”ตรวจว่าเซิร์ฟเวอร์ขึ้นจริงก่อนจะโทษ client:
docker logs redisนี่คือบรรทัดที่สำคัญ (image ของ Redis 8 ยังพิมพ์อีกหนึ่งบล็อกต่อ module ที่ฝังมา — Bloom, search, time series, JSON — ระหว่าง config กับบรรทัด Server initialized):
1:C 30 Sep 2026 05:42:55.041 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo1:C 30 Sep 2026 05:42:55.041 * Redis version=8.10.2, bits=64, commit=00000000, modified=1, pid=1, just started1:M 30 Sep 2026 05:42:55.042 * Running mode=standalone, port=6379.1:M 30 Sep 2026 05:42:55.048 * Server initialized1:M 30 Sep 2026 05:42:55.048 * Ready to accept connections tcpReady to accept connections tcp คือไฟเขียว และ Redis version= บอกว่าคุณได้เซิร์ฟเวอร์รุ่นไหนจริง ๆ
เชื่อมต่อด้วย redis-cli
หัวข้อที่มีชื่อว่า “เชื่อมต่อด้วย redis-cli”เมื่อ container รันอยู่ ให้เปิด CLI session แบบ interactive ภายใน container:
docker exec -it redis redis-clidocker exec รันคำสั่งภายใน container ที่ทำงานอยู่ -it จัดสรร TTY แบบ interactive เพื่อให้ได้ prompt 127.0.0.1:6379> ที่คุ้นเคย จากนั้นรันคำสั่ง Redis ใดก็ได้
image ทางการสตาร์ทเซิร์ฟเวอร์ด้วย protected-mode ปิด (พิสูจน์บน image 8.10.2) เพราะภายใน container ทุกอย่างเข้าถึง Redis ผ่าน bridge ของ Docker ไม่ใช่ loopback และ protected mode จะปฏิเสธ นั่นทำให้ การ publish port เป็นรั้วเดียวที่คุณมี:
127.0.0.1:6379> CONFIG GET protected-mode1) "protected-mode"2) "no"เพิ่ม persistence ด้วย named volume
หัวข้อที่มีชื่อว่า “เพิ่ม persistence ด้วย named volume”อย่าเชื่อคำว่า “ข้อมูลหายเมื่อลบ container” โดยไม่พิสูจน์ — นี่คือสิ่งที่เกิดขึ้นจริง บน container ที่ไม่มี -v ค่าเริ่มต้น save 3600 1 300 100 60 10000 ของ image จะ snapshot ลง writable layer ของ container เอง (/data/dump.rdb):
$ docker run -d --name redis-b1-nov redis:8.10.2-alpine$ docker exec redis-b1-nov redis-cli SET greeting helloOK$ docker stop redis-b1-nov && docker start redis-b1-nov # same container$ docker exec redis-b1-nov redis-cli GET greetinghello # survived: stop/start keeps the layer$ docker rm -f redis-b1-nov && docker run -d --name redis-b1-nov redis:8.10.2-alpine$ docker exec redis-b1-nov redis-cli GET greeting # (nil): remove + recreate = goneดังนั้น docker stop/start ปลอดภัยแม้ไม่มี volume; docker rm ต่างหากที่ทำลายข้อมูล ต่อ named volume เพื่อให้ข้อมูลอยู่นานกว่า container:
docker run -d --name redis -p 6379:6379 -v redisdata:/data redis:8-alpineทดลองเดิมด้วย -v redisdata:/data: SET greeting hello, docker stop, docker start → DBSIZE เป็น 1; docker exec redis ls -la /data เห็น dump.rdb ขนาด 110 byte; แล้ว docker rm -f และ container ใหม่เอี่ยม บน volume เดิมตอบ GET greeting → hello Docker สร้าง volume redisdata ให้อัตโนมัติ และ /data คือที่ที่ image ทางการเขียนไฟล์ snapshot
สังเกตว่า snapshot มีอยู่เพราะ docker stop ส่ง SIGTERM และ Redis จะ save ก่อนออกเมื่อมีกฎ save ตั้งไว้ แต่ docker kill (SIGKILL) หรือ crash อาจทำให้ข้อมูลตั้งแต่ snapshot ล่าสุดหายหมด — module Persistence วัดเรื่องนี้ให้ดูจริง
วงจรชีวิตของ container
หัวข้อที่มีชื่อว่า “วงจรชีวิตของ container”# Stop the running container (Redis shuts down cleanly)docker stop redis
# Start it again — data in the named volume is preserveddocker start redis
# Remove the container entirely (volume survives unless you also delete it)docker rm redis# Print all logs produced so fardocker logs redis
# Follow logs in real time (Ctrl-C to stop)docker logs -f redisdocker run -d --name redis -p 6379:6379 redis:8-alpine
docker exec -it redis redis-cliรหัสผ่าน และสิ่งที่ client เห็นเมื่อไม่มี
หัวข้อที่มีชื่อว่า “รหัสผ่าน และสิ่งที่ client เห็นเมื่อไม่มี”สตาร์ทเซิร์ฟเวอร์ด้วย --requirepass (เป็น placeholder — ใช้ secret ของคุณเอง และห้าม commit):
docker run -d --name redis -p 127.0.0.1:6379:6379 redis:8.10.2-alpine redis-server --requirepass '<your-password>'reply จริงจาก container นั้น:
$ docker exec redis redis-cli PINGNOAUTH Authentication required.$ docker exec redis redis-cli -a '<your-password>' PINGWarning: Using a password with '-a' or '-u' option on the command line interface may not be safe.PONG-a บน command line รั่วไปใน ps และ shell history จึงมี warning; แนะนำใช้ environment variable REDISCLI_AUTH แทน จากโค้ดแอป error ต่างออกไป เพราะ ioredis 6 เปิด connection ด้วย HELLO ก่อน:
import { Redis } from 'ioredis';
const bad = new Redis('redis://127.0.0.1:6379', { maxRetriesPerRequest: 0 });bad.on('error', () => {});// "NOAUTH HELLO must be called with the client already authenticated, otherwise the// HELLO <proto> AUTH <user> <pass> option can be used ..."console.log('no password ->', await bad.get('k').catch((e: Error) => e.message));bad.disconnect();
const ok = new Redis({ host: '127.0.0.1', port: 6379, password: process.env.REDIS_PASSWORD });console.log('with password ->', await ok.ping()); // PONGok.disconnect();นี่คือข้อความจริงที่ได้ (วัดบน ioredis 6.0.0 กับ Redis 8.10.2) ถ้าเห็น NOAUTH HELLO must be called… ใน log แปลว่าลืมใส่ password ไม่ใช่ปัญหาของ protocol
แบบเดียวกันในรูป compose file
หัวข้อที่มีชื่อว่า “แบบเดียวกันในรูป compose file”อะไรที่เก็บไว้ใช้นาน ให้เขียนลงไฟล์ ไฟล์นี้รันด้วย docker compose up -d, สถานะเป็น healthy หลัง health check แรก, สร้าง appendonlydir ใน volume (เพราะ --appendonly yes) และลบด้วย docker compose down -v:
services: redis: image: redis:8.10.2-alpine command: ["redis-server", "--requirepass", "<your-password>", "--appendonly", "yes"] ports: - "127.0.0.1:6379:6379" volumes: - redisdata:/data healthcheck: test: ["CMD", "redis-cli", "-a", "<your-password>", "--no-auth-warning", "ping"] interval: 5s timeout: 3s retries: 5volumes: redisdata:Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”AI ถูกขอให้ทำ “Redis ที่ข้อมูลไม่หายสำหรับ staging” แล้วตอบว่า:
🐛 หา bug ในโค้ดนี้
docker run -d --name redis -p 6379:6379 redis:8-alpine# data is persisted: Redis saves to /data/dump.rdbdocker rm -f redis && docker run -d --name redis -p 6379:6379 redis:8-alpinedocker exec redis redis-cli GET greetingเฉลย
มีสอง bug ข้อแรก comment จริงแค่ครึ่งเดียว: snapshot ไปที่ /data/dump.rdb จริง แต่นั่นอยู่ ใน writable layer ของ container ดังนั้น docker rm -f ลบมันทิ้ง เรารันลำดับนี้แล้ว GET greeting สุดท้ายได้ (nil) วิธีแก้คือ volume (-v redisdata:/data) — ลำดับเดียวกันเมื่อมี volume ตอบ hello ข้อสอง -p 6379:6379 publish บน ทุก interface ของ host ให้เซิร์ฟเวอร์ที่ protected-mode no และไม่มีรหัสผ่าน บนเครื่อง staging นั่นคือ Redis ที่เปิดโล่ง publish บน loopback (-p 127.0.0.1:6379:6379) หรือใส่ --requirepass และ firewall
docker run -d --name redis -p 127.0.0.1:6379:6379 -v redisdata:/data redis:8.10.2-alpine redis-server --requirepass '<your-password>'Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”container คือ process ที่ใช้แล้วทิ้งพร้อมดิสก์ชั่วคราว ดิสก์ชั่วคราวรอด stop และ start แต่ตายไปกับ rm volume คือดิสก์แยกที่เสียบเพิ่ม และ port ที่ publish คือรูบนกำแพง — เจาะตรงไหน (127.0.0.1: หรือทุก interface) เป็นตัวตัดสินว่าใครเข้าถึง Redis ได้ ต้องการเซิร์ฟเวอร์เดิมพรุ่งนี้ ให้ pin tag
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ตัวเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
| รันโดยไม่ mount volume | เริ่มเร็วที่สุด เหมาะกับการทดลองชั่วคราว | ข้อมูลหายทันทีที่ docker rm container (รอดแค่ stop/start) |
แนบ named volume (-v redisdata:/data) | ข้อมูลรอดหลัง restart/สร้าง container ใหม่ | ต้องดูแลวงจรชีวิตของ volume เอง (backup, cleanup) — ชิ้นส่วนเยอะขึ้น |
tag ลอย (8-alpine) vs ตายตัว (8.10.2-alpine) | tag ลอยได้ patch release ฟรี | tag ลอยเปลี่ยนใต้เท้า; tag ตายตัว reproduce ได้แต่ต้องยกเวอร์ชันเอง |
รัน container ด้วยค่าเริ่มต้นของ docker run | สะดวก เหมาะกับ dev บนเครื่อง | ไม่มีรหัสผ่าน ไม่มี TLS protected-mode no — ไม่ควร deploy ตามนี้ |
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
docker stopทำให้ข้อมูลหาย” ไม่จริง แม้ไม่มี volume: writable layer ของ container เก็บdump.rdbไว้จนกว่าจะdocker rm - “Redis มี protected mode ดังนั้น publish port ปลอดภัย” image ทางการปิด
protected-modeการ map port เป็นเกราะเดียวของคุณ - “
-a passwordใช้ใน script ได้ไม่เป็นไร” มี warning ด้วยเหตุผล มันโผล่ใน process list ใช้REDISCLI_AUTH - “
redis:8กับredis:8.10.2เหมือนกัน” เหมือนแค่ในวันที่ pull
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- ลืมแนบ volume แล้วสงสัยว่าทำไมข้อมูลหาย — ถ้าไม่ mount
/dataกับ named volume ข้อมูลทั้งหมดอยู่แค่ใน writable layer ของ containerdocker rmทำลายมันถาวร - publish
-p 6379:6379สู่อินเทอร์เน็ตโดยไม่มี firewall — image ทางการไม่ได้ตั้ง authentication มาให้ ถ้า bind ที่0.0.0.0โดยไม่กรองเครือข่าย ใครก็เชื่อมต่อและรันคำสั่งได้ - รัน container โดยไม่มี
--requirepass— ใช้ได้เฉพาะทดลองบนเครื่อง ต้องตั้งรหัสผ่านหรือใช้ ACL ก่อนใช้งานจริง