目錄
用 Docker 跑 Redis:設定密碼、資料保存與連線
Redis 是存在記憶體中的鍵值資料庫,常用來當快取、Session 儲存、計數器、佇列。用 Docker 執行只需要幾秒鐘。
基本執行
$ docker run -d --name redis -p 127.0.0.1:6379:6379 redis:8-alpine
Unable to find image 'redis:8-alpine' locally
8-alpine: Pulling from library/redis
Digest: sha256:3811787313eba226a2ef38658c6ccb91cd5e110edc89c37767de373120a0e5a0
Status: Downloaded newer image for redis:8-alpine
6fbb2effcf1bc874eda1064575153984b6223fd6de15ac2452eae0bbf71a74f1
$ docker exec redis redis-cli PING
PONG
$ docker exec redis redis-cli SET greeting hello
OK
$ docker exec redis redis-cli GET greeting
hello
$ docker exec redis redis-cli SET session:abc "user42" EX 60
OK
$ docker exec redis redis-cli TTL session:abc
60
$ docker exec redis redis-server --version
Redis server v=8.10.2 sha=00000000:1 malloc=jemalloc-5.3.0 bits=64 build=6583f6419e33bdeb
$ docker rm -f redis
redis
-p 127.0.0.1:6379:6379:只開放給本機。- 容器內就有
redis-cli,可以直接執行指令。 SET ... EX 60設定 60 秒後自動過期,TTL查看剩餘秒數,常用於 Session 和快取。
互動式使用時執行 docker exec -it redis redis-cli,會出現 127.0.0.1:6379> 提示字元,輸入指令後按 Enter,輸入 exit 離開。
設定密碼
Redis 預設沒有密碼。只要連接埠對外開放,任何人都能讀寫甚至利用它入侵主機,所以:
- 不需要從外部連線時,不要用
-p;需要時只綁定127.0.0.1。 - 設定密碼:
$ docker run -d --name redis redis:8-alpine redis-server --requirepass "change-me"
8dbd1f84d2541f264d93555142cb65f6497e9635111ff4f9544258516e06a0e2
$ docker exec redis redis-cli GET greeting
NOAUTH Authentication required.
$ docker exec redis redis-cli -a "change-me" PING
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
PONG
$ docker exec redis redis-cli -a "change-me" --no-auth-warning PING
PONG
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli PING
PONG
$ docker rm -f redis
redis
- 沒有密碼時得到
NOAUTH Authentication required.。 -a傳入密碼可以執行,但會出現警告,因為密碼會出現在程序列表中。--no-auth-warning只是隱藏警告。- 比較好的做法是用環境變數
REDISCLI_AUTH傳入密碼,redis-cli會自動使用,也沒有警告。
應用程式的連線字串(使用者名稱留空):
redis://:change-me@redis:6379/0
最後的 /0 是資料庫編號(0~15)。
資料持久化
Redis 的資料在記憶體中,要保存到磁碟有兩種方式:RDB 快照和 AOF 日誌。官方映像檔預設只啟用 RDB,檔案存在容器的 /data,所以要掛載 Volume。
實測:RDB 不是即時保存
$ docker run -d --name redis -v redis_data:/data redis:8-alpine
882983e53f8e000fa65faaf8d71bbcbeb57192401b1854621172ce2df95c29a6
$ docker exec redis redis-cli SET visits 100
OK
$ docker rm -f redis
redis
$ docker run -d --name redis -v redis_data:/data redis:8-alpine
5ad1473152a6eafdfdb33df64f68355c73e454f10e6bc135688f7c51686bb059
$ docker exec redis redis-cli GET visits
$ docker exec redis redis-cli CONFIG GET save
save
3600 1 300 100 60 10000
明明掛載了 Volume,docker rm -f 再重建後 visits 卻是空的。CONFIG GET save 顯示預設的保存條件 3600 1 300 100 60 10000,意思是:
| 條件 | 意思 |
|---|---|
3600 1 | 1 小時內至少 1 個 key 變更,就保存 |
300 100 | 5 分鐘內至少 100 個 key 變更,就保存 |
60 10000 | 1 分鐘內至少 10000 個 key 變更,就保存 |
只改了 1 個 key,要等 1 小時才會保存。docker rm -f 直接用 SIGKILL 終止 Redis,來不及保存。
改用 docker stop 正常停止:
$ docker exec redis redis-cli SET visits 100
OK
$ docker stop redis
redis
$ docker logs redis 2>&1 | tail -n 5
1:M 13 Oct 2026 00:57:09.620 * User requested shutdown...
1:M 13 Oct 2026 00:57:09.620 * Saving the final RDB snapshot before exiting.
1:M 13 Oct 2026 00:57:09.621 * BGSAVE done, 1 keys saved, 0 keys skipped, 104 bytes written.
1:M 13 Oct 2026 00:57:09.623 * DB saved on disk
1:M 13 Oct 2026 00:57:09.623 # Redis is now ready to exit, bye bye...
$ docker start redis
redis
$ docker exec redis redis-cli GET visits
100
$ docker exec redis ls -l /data
total 4
-rw------- 1 redis redis 104 Oct 13 00:57 dump.rdb
$ docker rm -f redis
redis
$ docker volume rm redis_data
redis_data
docker stop 送出 SIGTERM,Redis 在結束前執行 Saving the final RDB snapshot before exiting,資料就保存下來了。所以:
- 正常停止(
docker stop、docker compose down、主機正常關機)不會遺失資料。 - 當機、斷電、
docker rm -f、記憶體不足被系統終止時,會遺失上一次快照之後的資料。
AOF:每次寫入都記錄
需要更可靠的保存,開啟 AOF(Append Only File),每個寫入指令都會記錄下來:
$ docker run -d --name redis -v redis_data:/data redis:8-alpine redis-server --appendonly yes
aaac5c67a222018db886e0f3a3306841ec4bba7e8ae0b4b4eca71dc5cede91c9
$ docker exec redis redis-cli SET visits 41
OK
$ docker exec redis redis-cli INCR visits
42
$ docker rm -f redis
redis
$ docker run -d --name redis -v redis_data:/data redis:8-alpine redis-server --appendonly yes
1c880b5c2e8ef54b65baaa1fd99b1b9c976eb42f2d9c52f1eb9188eaeda955e4
$ docker exec redis redis-cli GET visits
42
$ docker exec redis ls /data/appendonlydir
appendonly.aof.1.base.rdb
appendonly.aof.1.incr.aof
appendonly.aof.manifest
$ docker rm -f redis
redis
$ docker volume rm redis_data
redis_data
即使用 docker rm -f 強制刪除,重建後 visits 仍然是 42。/data/appendonlydir 中是 AOF 檔案:base.rdb 是基礎快照,incr.aof 記錄之後的每個寫入,manifest 記錄檔案清單。
| 方式 | 優點 | 缺點 |
|---|---|---|
| RDB(預設) | 檔案小、重啟載入快 | 異常結束時可能遺失上次快照之後的資料 |
| AOF | 預設每秒寫入磁碟一次,最多遺失約 1 秒的資料 | 檔案較大,寫入量大時磁碟負擔較重 |
| 不保存 | 最快 | 重啟後資料全部消失,只適合純快取 |
純快取用途可以完全關閉保存:redis-server --save "" --appendonly no。
使用設定檔與記憶體上限
參數多的時候改用設定檔。redis-demo/redis.conf:
requirepass change-me
appendonly yes
maxmemory 64mb
maxmemory-policy allkeys-lru
當作快取使用時,一定要設定 maxmemory,否則 Redis 會一直佔用記憶體,直到主機記憶體不足。到達上限後依照 maxmemory-policy 處理:
| 策略 | 行為 |
|---|---|
noeviction | 預設值,拒絕新的寫入,回傳錯誤 |
allkeys-lru | 從所有 key 中淘汰最久沒被使用的,快取最常用 |
volatile-lru | 只淘汰有設定過期時間的 key |
allkeys-lfu | 淘汰使用頻率最低的 |
實測:寫入超過上限的資料
用 Redis 內建的 redis-benchmark 寫入 30 萬筆、每筆 500 bytes(約 150 MB)的資料,遠超過 64 MB 的上限:
$ docker run -d --name redis -v "$(pwd)/redis.conf:/usr/local/etc/redis/redis.conf:ro" -v redis_data:/data redis:8-alpine redis-server /usr/local/etc/redis/redis.conf
af187c21e0d6ecb0e9a0299200a6d866dc605d92c715e49df8a7abd850107040
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli CONFIG GET maxmemory*
maxmemory
67108864
maxmemory-clients
0
maxmemory-eviction-tenacity
10
maxmemory-samples
5
maxmemory-policy
allkeys-lru
$ docker exec redis redis-benchmark -a change-me -t set -n 300000 -r 300000 -d 500 -q
SET: 31043.05 requests per second, p50=0.903 msec
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli INFO memory | grep -E "^(used_memory_human|maxmemory_human|maxmemory_policy)"
used_memory_human:63.89M
maxmemory_human:64.00M
maxmemory_policy:allkeys-lru
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli INFO stats | grep evicted_keys
evicted_keys:95511
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli DBSIZE
115038
對應 Windows 的寫法(點選分頁切換):
docker run -d --name redis -v "$(pwd)/redis.conf:/usr/local/etc/redis/redis.conf:ro" -v redis_data:/data redis:8-alpine redis-server /usr/local/etc/redis/redis.conf
docker run -d --name redis -v "${PWD}/redis.conf:/usr/local/etc/redis/redis.conf:ro" -v redis_data:/data redis:8-alpine redis-server /usr/local/etc/redis/redis.conf
docker run -d --name redis -v "%cd%/redis.conf:/usr/local/etc/redis/redis.conf:ro" -v redis_data:/data redis:8-alpine redis-server /usr/local/etc/redis/redis.conf
CONFIG GET maxmemory*確認設定檔生效:67108864bytes 就是 64 MB,策略是allkeys-lru。- 寫入後記憶體停在
63.89M,沒有超過上限。 evicted_keys:95511:有 9 萬多個 key 被自動淘汰,最後留下約 11.5 萬個 key。
同樣的情況改成 noeviction:
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli CONFIG SET maxmemory-policy noeviction
OK
$ docker exec -e REDISCLI_AUTH=change-me redis sh -c 'head -c 2000000 /dev/zero | tr "\\0" x | redis-cli -x SET big-value'
OOM command not allowed when used memory > 'maxmemory'.
$ docker exec -e REDISCLI_AUTH=change-me redis redis-cli GET greeting
記憶體已滿,寫入一個 2 MB 的值時得到 OOM command not allowed when used memory > 'maxmemory',應用程式就會出錯。所以純快取用 allkeys-lru;如果 Redis 裡存的是不能遺失的資料(例如佇列),則用 noeviction 並監控記憶體用量。
在 Compose 中搭配應用程式
做一個 Flask 計數器,每次請求就把 Redis 中的 visits 加 1:
redis-demo/
├── compose.yaml
├── .env
└── app/
├── app.py
├── requirements.txt
└── Dockerfile
app/app.py:
import os
import redis
from flask import Flask
app = Flask(__name__)
r = redis.Redis.from_url(os.environ["REDIS_URL"], decode_responses=True)
@app.get("/")
def index():
visits = r.incr("visits")
return {"message": "Hello from Flask + Redis", "visits": visits}
app/requirements.txt:
flask==3.1.*
gunicorn==23.*
redis==6.*
app/Dockerfile(寫法說明見 Python 應用):
FROM python:3.14-slim
ENV PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
USER nobody
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
.env:
REDIS_PASSWORD=change-this-password
compose.yaml:
services:
app:
build: ./app
ports:
- "8000:8000"
environment:
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0
depends_on:
redis:
condition: service_healthy
redis:
image: redis:8-alpine
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--appendonly", "yes"]
environment:
REDISCLI_AUTH: ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep -q PONG"]
interval: 5s
retries: 10
restart: unless-stopped
volumes:
redis_data:
| 設定 | 說明 |
|---|---|
REDIS_URL 中的 @redis:6379 | 用服務名稱連線 |
command 中的 ${REDIS_PASSWORD} | Compose 從 .env 替換成實際密碼 |
REDISCLI_AUTH | 讓容器內的 redis-cli(包括健康檢查)自動帶入密碼 |
redis 沒有 ports | 只有 app 需要連線,不對外開放 |
$ docker compose up -d --build
[+] Building 0.3s (12/12) FINISHED
=> [internal] load local bake definitions 0.0s
=> => reading from stdin 486B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 263B 0.0s
=> [internal] load metadata for docker.io/library/python:3.14-slim 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/python:3.14-slim@sha256:a2b82f3c48559aa0a8446d9af49826b6e2b2016f4cd2afabfe6013ec53729170 0.0s
=> => resolve docker.io/library/python:3.14-slim@sha256:a2b82f3c48559aa0a8446d9af49826b6e2b2016f4cd2afabfe6013ec53729170 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 93B 0.0s
=> CACHED [2/5] WORKDIR /app 0.0s
=> CACHED [3/5] COPY requirements.txt . 0.0s
=> CACHED [4/5] RUN pip install -r requirements.txt 0.0s
=> CACHED [5/5] COPY . . 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => exporting manifest sha256:2cb1f769137117a44dde49bfb2f8551a8ce989fe8bd0a72696ba37987c29fd13 0.0s
=> => exporting config sha256:88373d2af6030b965f8b4e878d871e906738cd9194ec77bd007c6bbbace7c1b9 0.0s
=> => exporting attestation manifest sha256:c8f570c26323c3e08476544296d44ed0e159d27d9f25b86bf879cd08c6b6e186 0.0s
=> => exporting manifest list sha256:46bd2450b425a935481fb6ef8567c915f5543996f4e35018bdb955ecf858b361 0.0s
=> => naming to docker.io/library/redis-demo-app:latest 0.0s
=> => unpacking to docker.io/library/redis-demo-app:latest 0.0s
=> resolving provenance for metadata file 0.0s
[+] up 5/5
✔ Image redis-demo-app Built 0.4s
✔ Network redis-demo_default Created 0.0s
✔ Volume redis-demo_redis_data Created 0.0s
✔ Container redis-demo-redis-1 Healthy 5.8s
✔ Container redis-demo-app-1 Started 5.9s
$ curl -s http://localhost:8000/
{"message":"Hello from Flask + Redis","visits":1}
$ curl -s http://localhost:8000/
{"message":"Hello from Flask + Redis","visits":2}
$ curl -s http://localhost:8000/
{"message":"Hello from Flask + Redis","visits":3}
Redis 變成 Healthy 之後 app 才啟動,每次請求 visits 就加 1。因為開了 AOF 並掛載 Volume,重建所有容器後計數仍然延續:
$ docker compose up -d --force-recreate
[+] up 2/2
✔ Container redis-demo-redis-1 Healthy 6.1s
✔ Container redis-demo-app-1 Started 6.2s
$ curl -s http://localhost:8000/
{"message":"Hello from Flask + Redis","visits":4}
健康檢查為什麼要 grep PONG
網路上常見的寫法是 test: ["CMD", "redis-cli", "ping"]。實測它的結束代碼:
$ docker compose exec redis redis-cli ping; echo "exit code: $?"
PONG
exit code: 0
$ docker compose exec -e REDISCLI_AUTH=wrong redis redis-cli ping; echo "exit code: $?"
AUTH failed: WRONGPASS invalid username-password pair or user is disabled.
NOAUTH Authentication required.
exit code: 0
$ docker compose exec -e REDISCLI_AUTH=wrong redis sh -c "redis-cli ping | grep -q PONG"; echo "exit code: $?"
AUTH failed: WRONGPASS invalid username-password pair or user is disabled.
exit code: 1
密碼錯誤時,redis-cli 印出了 NOAUTH Authentication required.,但結束代碼仍然是 0。Docker 只看結束代碼判斷健康與否,所以密碼設錯時健康檢查照樣通過,app 啟動後才連線失敗。加上 | grep -q PONG,只有真的收到 PONG 時結束代碼才是 0。
常用 redis-cli 指令
docker exec redis redis-cli INFO memory # 記憶體使用
docker exec redis redis-cli INFO stats # 統計,包含 evicted_keys
docker exec redis redis-cli DBSIZE # key 的數量
docker exec redis redis-cli --scan --pattern 'session:*' | head
docker exec -it redis redis-cli MONITOR # 即時顯示所有指令(除錯用,會影響效能,Ctrl+C 結束)
有設定密碼時,在 docker exec 加上 -e REDISCLI_AUTH=密碼。
避免在正式環境使用
KEYS *,它會掃描所有 key 並阻塞 Redis,key 很多時可能卡住好幾秒。請改用SCAN(redis-cli --scan)。
想用圖形介面瀏覽資料,可以使用官方的 Redis Insight(映像檔 redis/redisinsight,預設連接埠 5540)。第一次開啟時需要閱讀並同意它的授權條款,之後新增連線,主機填 Compose 的服務名稱 redis、連接埠 6379 和密碼即可。
關於 Redis 授權與 Valkey
Redis 在 2024 年把授權改為 RSALv2/SSPL 這類非開源授權,Redis 8 起又加入了 AGPLv3 作為選項。Linux 基金會在 2024 年推出了相容的開源分支 Valkey(BSD 授權),指令與用法幾乎相同:
$ docker run -d --name valkey valkey/valkey:8-alpine
Unable to find image 'valkey/valkey:8-alpine' locally
8-alpine: Pulling from valkey/valkey
Digest: sha256:081c2f5cb575efc901aa80ff9cdbd1ec6a301682fd35e1ebb4b0990a4a4a8507
Status: Downloaded newer image for valkey/valkey:8-alpine
3e7385b8f0d22a999ccf9306938e178c8f82bb4bc1ef9d304bd307917a890087
$ docker exec valkey valkey-cli PING
PONG
$ docker exec valkey redis-cli SET greeting hello
OK
$ docker exec valkey valkey-server --version
Valkey server v=8.1.10 sha=00000000:1 malloc=jemalloc-5.3.0 bits=64 build=fd3b186b1408478b
$ docker rm -f valkey
valkey
Valkey 映像檔同時提供 valkey-cli 和相容的 redis-cli,應用程式的 Redis 函式庫也可以直接連線。一般自行使用 Redis 不受授權變更影響;如果要把 Redis 當作服務提供給他人,請自行確認授權條款。
重點回顧
- Redis 預設沒有密碼,用
--requirepass設定、用REDISCLI_AUTH傳入。 - RDB 不是即時保存,需要可靠性時開
--appendonly yes。 - 當快取時設定
maxmemory與allkeys-lru。 - 健康檢查寫
redis-cli ping | grep -q PONG。
動手練習
練習 1:用 docker exec redis redis-cli 設定一個 30 秒後過期的 key,並查看剩餘時間。
答案
docker exec redis redis-cli SET temp hello EX 30
docker exec redis redis-cli TTL temp
有密碼時加上 -e REDISCLI_AUTH=密碼。
常見問題
Redis 一定要設定密碼嗎?
只要可能被其他人連到就一定要。最好的做法是不對外開放連接埠,並設定密碼。
Redis 重啟後資料不見了?
預設的 RDB 只在達到保存條件或正常停止時寫入磁碟;用 docker rm -f 或當機會遺失資料。需要可靠保存時開啟 AOF。
Valkey 和 Redis 可以互換嗎?
Valkey 是 Redis 的開源分支,指令與協定相容,應用程式通常不需要修改。
更新於 2026年10月14日