Lỗi
Bạn khởi động lại server, đưa các container trở lại, rồi gặp phải lỗi này:
Error response from daemon: driver failed programming external connectivity on endpoint mycontainer: iptables: No chain/target/match by that name.
Mọi thứ vẫn hoạt động tốt trước khi reboot. Các container không thay đổi gì. Vậy mà Docker từ chối khởi động chúng. Thật bực bội vì nhìn bề ngoài không có gì có vẻ bị hỏng cả.
Chuyện gì thực sự đang xảy ra
Docker quản lý mạng container bằng cách chèn các rule vào iptables — cụ thể là vào các chain như DOCKER, DOCKER-USER và DOCKER-ISOLATION-STAGE-1. Docker tạo ra các chain này khi daemon khởi động.
Sau khi reboot, nếu có gì đó tải lại hoặc reset các rule firewall sau khi Docker đã khởi động, các chain đó sẽ bị xóa sạch. Các rule của Docker biến mất, nhưng Docker không biết điều đó. Khi khởi động container, Docker cố gắng thêm rule vào các chain không còn tồn tại — đó là lý do xuất hiện "No chain/target/match by that name."
Các nguyên nhân thường gặp:
firewalldhoặcufwtải lại sau khi Docker khởi động và flush iptables- Một script
iptables -Fhoặciptables --flushtùy chỉnh chạy lúc boot - Race condition trong thứ tự systemd: Docker khởi động trước khi firewall sẵn sàng, sau đó firewall reset mọi thứ
- Chuyển đổi giữa
iptables-legacyvàiptables-nft— rất phổ biến trên Debian/Ubuntu 20.04 trở lên
Fix 1: Khởi động lại Docker Daemon (Sửa nhanh)
Cách khôi phục nhanh nhất ngay lúc này: restart Docker. Điều này buộc Docker phải tạo lại các chain iptables từ đầu.
sudo systemctl restart docker
Sau đó khởi động container của bạn:
docker start mycontainer
# hoặc
docker compose up -d
Hoạt động ngay trong hầu hết các trường hợp. Nhưng lỗi sẽ quay lại ở lần reboot tiếp theo nếu nguyên nhân gốc rễ chưa được giải quyết — vì vậy hãy đọc tiếp.
Fix 2: Kiểm tra Backend iptables (Ubuntu/Debian)
Trên Ubuntu 20.04 trở lên và Debian 10 trở lên, hệ thống có thể đang dùng iptables-nft (backend nftables) trong khi Docker mong đợi iptables-legacy. Khi chúng không khớp, việc tra cứu chain thất bại âm thầm trong quá trình khởi động Docker.
Kiểm tra backend nào đang hoạt động:
sudo update-alternatives --display iptables
Nếu kết quả hiển thị iptables-nft là lựa chọn hiện tại, hãy chuyển sang legacy:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
Khởi động lại Docker:
sudo systemctl restart docker
Chỉ riêng bước này đã giải quyết được lỗi trên phần lớn các máy Debian/Ubuntu hiện đại. Hãy thử cách này trước tiên trên những hệ thống đó.
Fix 3: Sửa Thứ tự Service systemd (firewalld)
Bạn đang chạy firewalld? Đó có thể là thủ phạm. Firewalld tải lại bộ rule của chính nó trong quá trình boot — và nếu việc tải lại đó xảy ra sau khi Docker đã thiết lập xong các chain, firewalld sẽ xóa sạch chúng.
Xác nhận firewalld đang hoạt động:
sudo systemctl status firewalld
sudo firewall-cmd --list-all
Yêu cầu systemd Docker phải chờ firewalld khởi động xong trước khi bắt đầu:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/after-firewalld.conf <<EOF
[Unit]
After=firewalld.service
EOF
Áp dụng thay đổi:
sudo systemctl daemon-reload
sudo systemctl restart docker
Trên các dev server mà bạn thực sự không cần firewalld, chỉ cần tắt nó đi:
sudo systemctl disable --now firewalld
Fix 4: Ngăn Script Flush iptables Chạy Sau Docker
Bạn có script tùy chỉnh flush các rule iptables — chẳng hạn trong /etc/rc.local hoặc một systemd unit riêng? Hãy kiểm tra thời điểm chạy của nó so với Docker khi boot.
Flush toàn bộ chain thủ công, rồi để Docker tự xây dựng lại:
# Xóa toàn bộ rule iptables
sudo iptables -F
sudo iptables -X
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
# Xây dựng lại các chain của Docker
sudo systemctl restart docker
Cần giữ lại script flush? Thêm lệnh systemctl restart docker vào cuối script đó. Hoặc cấu hình thứ tự systemd để Docker luôn khởi động sau cùng.
Kiểm tra
Sau khi áp dụng fix, xác nhận các chain của Docker tồn tại:
sudo iptables -L DOCKER --line-numbers
Bạn sẽ thấy kết quả tương tự như sau:
Chain DOCKER (1 references)
num target prot opt source destination
Nếu vẫn thấy iptables: No chain/target/match by that name ở đây, có nghĩa là các chain của Docker vẫn chưa được tạo. Hãy restart daemon và kiểm tra lại.
Xác nhận container khởi động bình thường:
docker start mycontainer
docker ps # container phải hiển thị trạng thái Up
Thực hiện reboot toàn bộ để xác nhận fix hoạt động bền vững:
sudo reboot
# Sau khi kết nối lại:
docker ps -a # kiểm tra trạng thái container
journalctl -u docker -n 50 # xem log Docker để tìm lỗi
Phòng ngừa
Một vài điều đáng thiết lập để vấn đề này không tái diễn:
- Cố định backend iptables. Chuyển sang
iptables-legacysẽ được giữ nguyên qua các lần reboot — nhưng hãy kiểm tra lại sau các lần nâng cấp OS lớn. Lệnhapt upgradetrên Ubuntu có thể âm thầm đổi lại thành nft. - Chỉ dùng
--iptables=falsekhi bạn biết mình đang làm gì. Đặt"iptables": falsetrong/etc/docker/daemon.jsonđể tự quản lý hoàn toàn — nhưng điều đó có nghĩa là bạn phải tự viết mọi rule NAT và forwarding. Thiếu một rule là container mất kết nối internet. - Trực quan hóa thứ tự boot. Chạy
systemd-analyze plot > boot.svgvà mở file SVG trong trình duyệt. Bạn sẽ thấy chính xác Docker và firewalld khởi động tại thời điểm nào trên timeline — race condition trở nên rõ ràng ngay lập tức. - Lên kế hoạch cho các subnet Docker. Khi chạy nhiều mạng Docker với subnet tùy chỉnh, xung đột CIDR với mạng host gây ra một lớp lỗi khác nhưng cũng khó hiểu không kém. Subnet Calculator trên ToolCraft rất tiện để kiểm tra nhanh xem subnet bridge có xung đột với các route hiện có hay không trước khi gán.
Tham Chiếu Nhanh
# Trình tự fix phổ biến nhất:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo systemctl restart docker
# Xác nhận các chain Docker đã hoạt động:
sudo iptables -L DOCKER
# Kiểm tra log Docker nếu vẫn còn lỗi:
journalctl -u docker -n 100 --no-pager

