docker run -d redis chạy được ngay. Bài này đo xem cấu hình mặc định đó thật sự cho bạn cái gì.
Mặc định của ảnh redis chính thức
maxmemory 0 không giới hạn
maxmemory-policy noeviction đầy thì TỪ CHỐI GHI
requirepass (trống) không mật khẩu
bind * -::* nghe mọi giao diện
protected-mode no TẮT
timeout 0 không đóng kết nối nhàn rỗi
appendonly no chỉ có RDB, mất tối đa 1 giờ
Dòng thứ năm là dòng đáng chú ý nhất: ảnh chính thức tự tắt protected-mode.
protected-mode là cơ chế Redis thêm vào từ 3.2 để chặn kết nối từ ngoài khi chưa đặt mật khẩu. Ảnh Docker tắt nó — hợp lý cho việc phát triển, vì container luôn được truy cập từ ngoài, nhưng nó gỡ bỏ lớp bảo vệ cuối cùng.
Tôi chạy FLUSHALL từ một container khác
PING -> PONG
CONFIG GET -> maxmemory 0
FLUSHALL -> OK
Không mật khẩu, không cấu hình gì. Bất kỳ container nào trên cùng mạng đều xoá sạch dữ liệu được.
Với mạng Docker nội bộ thì đây là rủi ro chấp nhận được. Nhưng chỉ cần một dòng -p 6379:6379 là cổng đó ra tới giao diện mạng của máy chủ, và nếu máy chủ có IP công khai thì bạn vừa đặt một Redis mở lên Internet.
Đây là nguyên nhân của phần lớn các vụ Redis bị xâm nhập — không phải mật khẩu yếu, mà là không có mật khẩu nào cả.
--memory của Docker không bảo vệ được Redis
Container --memory 128m, nạp 600.000 khoá:
| Cấu hình | Kết quả |
|---|---|
maxmemory 0 |
OOMKilled, ExitCode 137 |
maxmemory 80mb, nạp trong Lua |
OOMKilled |
maxmemory 80mb, nạp từ ngoài |
Sống |
Dòng cuối, chi tiết:
used_memory 80,00M
used_memory_rss 88,06M
đuổi 319.257 khoá, giữ 280.743
container 83,99 MiB / 128 MiB
Redis không đọc giới hạn cgroup. Nó không biết container bị giới hạn 128 MB; nó chỉ biết maxmemory, và nếu bạn không khai thì nó lớn tới lúc kernel giết tiến trình.
Đặt --memory mà không đặt maxmemory chỉ thay đổi cách Redis chết: thay vì máy chủ hết RAM, container bị giết. Dữ liệu mất như nhau.
Dòng thứ hai: Lua vẫn vượt giới hạn
maxmemory 80mb mà nạp bằng một script Lua vẫn bị OOMKilled.
Lý do nối lại với phần 14: Redis kiểm maxmemory một lần ở đầu script, không kiểm từng lệnh bên trong. 600.000 lệnh SET trong một script chạy hết mà không đuổi khoá nào — và vượt hẳn giới hạn container.
Nạp cùng lượng dữ liệu từ ngoài, mỗi lệnh là một lệnh riêng, thì maxmemory được kiểm mỗi lần và LRU đuổi đều đặn: 319.257 khoá bị đuổi, container ổn định ở 84/128 MiB.
Bài học: script Lua dài có thể phá vỡ mọi giới hạn bộ nhớ bạn đặt. Đây là một lý do nữa để giữ script ngắn, bên cạnh chuyện chặn máy chủ đã đo ở phần 14.
Cấu hình tối thiểu cho môi trường thật
services:
redis:
image: redis:7-alpine
command: >
redis-server
--requirepass ${REDIS_PASSWORD}
--maxmemory 800mb
--maxmemory-policy allkeys-lru
--appendonly yes
--appendfsync everysec
--timeout 300
--save ""
volumes:
- redis-data:/data
mem_limit: 1200m
sysctls:
net.core.somaxconn: 1024
ports:
- "127.0.0.1:6379:6379"
Từng dòng có lý do đo được trong sê-ri:
requirepass— vìprotected-modeđã tắt.maxmemory 800mbvớimem_limit 1200m— chừa 50% cho copy-on-write khifork(phần 18), bộ đệm khách (phần 30), và phân mảnh (phần 10). Tỷ lệ 2:3 là điểm khởi đầu an toàn.allkeys-lruthay chonoeviction— trừ khi Redis là kho dữ liệu chính (phần 9).appendonly yes— vì RDB mặc định mất tối đa một giờ (phần 20).timeout 300— vì mặc định0để kết nối rò rỉ sống mãi (phần 31).127.0.0.1:6379:6379— chỉ nghe trên loopback của máy chủ, không phải mọi giao diện.
Bốn điều khác đáng biết
Volume là bắt buộc nếu bật lưu trữ. Không có volume, /data nằm trong lớp ghi của container và biến mất khi container bị xoá — kể cả khi appendonly yes.
CONFIG SET không bền. Như đo ở phần 19: CONFIG SET appendonly yes mất khi khởi động lại container. Mọi thứ phải nằm trong command hoặc tệp cấu hình.
Cảnh báo lúc khởi động đáng đọc. Ảnh này in cảnh báo về somaxconn và overcommit_memory nếu máy chủ chưa chỉnh. Cái thứ hai đặc biệt quan trọng: vm.overcommit_memory=0 có thể làm fork cho BGSAVE thất bại, và đó là bản sao lưu của bạn.
Kiểm tra tình trạng nên gọi PING chứ đừng chỉ xem cổng mở. Redis đang nạp RDB vẫn mở cổng nhưng trả LOADING cho mọi lệnh — như đo ở phần 18.
Thử ba mươi giây
Kiểm container Redis của bạn:
docker exec <container> redis-cli config get \
maxmemory maxmemory-policy requirepass appendonly protected-mode timeout | paste - -
docker inspect <container> -f 'mem_limit={{.HostConfig.Memory}} ports={{.HostConfig.PortBindings}}'
docker inspect <container> -f 'mounts={{range .Mounts}}{{.Destination}} {{end}}'
Bốn dấu hiệu:
requirepasstrống và cổng ánh xạ ra0.0.0.0— Redis của bạn đang mở.maxmemory 0vớimem_limitkhác 0 — container sẽ bị OOMKilled, không phải Redis từ chối ghi.appendonly yesmà không có mount/data— lưu trữ đang ghi vào một nơi sẽ biến mất.maxmemorybằng hoặc gầnmem_limit— chưa chừa chỗ choforkvà bộ đệm khách.
Phần sau: khi nào không nên dùng Redis — và dùng gì thay thế.