Sửa lỗi Docker container không kết nối được PostgreSQL trên VPS
Docker đổi dải IP mạng mỗi lần docker-compose down rồi up, nên PostgreSQL trên host từ chối container. Cách chẩn đoán subnet, mở UFW và pg_hba.conf, rồi cho phép cả dải 172.16.0.0/12 để sửa một lần là xong.
Mục lục
- 1. Tại sao Docker lại đổi IP?
- 2. Chẩn đoán: tìm IP và tên mạng hiện tại
- 3. Xem dải mạng (subnet) và gateway
- 4. Mở firewall (UFW)
- 5. Cập nhật cấu hình PostgreSQL
- 5.1. Sửa pg_hba.conf
- 5.2. Kiểm tra postgresql.conf
- 5.3. Restart PostgreSQL
- 6. Test lại
- 7. Giải pháp Silver Bullet (khuyến nghị)
- Cơ sở lý thuyết
- Bước 1: Sửa pg_hba.conf
- Bước 2: Mở firewall UFW
- Bước 3: Restart PostgreSQL
- Bước 4: Cập nhật connection string trong code
- Tóm tắt nhanh
- Lưu ý
Container đang chạy ngon bỗng không kết nối được PostgreSQL cài trên host? Nguyên nhân
thường gặp: Docker tự động đổi dải IP mạng (dynamic allocation) mỗi khi bạn
docker-compose down rồi up lại, khiến PostgreSQL từ chối kết nối vì IP mới không
nằm trong danh sách được phép. Bài này chỉ cách chẩn đoán, sửa nhanh cho subnet hiện
tại, và giải pháp làm một lần cho mọi subnet. Áp dụng cho PostgreSQL 14/16 trên VPS
Ubuntu/Debian chạy Docker.
Trong bài, myapp là tên container và myapp_default là tên mạng Docker ví dụ - thay
bằng tên thật của bạn. Các đường dẫn dùng PostgreSQL 16, đổi 16 theo phiên bản của
bạn.
1. Tại sao Docker lại đổi IP?
Hãy tưởng tượng Docker quản lý mạng giống như việc xếp phòng trong khách sạn:
- Lần đầu chạy: bạn chạy
docker-compose upcho dự ánmyapp. Docker thấy "Phòng 1" (172.17.x.x) đang trống nên cấp cho dự án của bạn. - Lần sau:
- Bạn cài thêm một dự án khác (ví dụ:
my-blog). Dự án này chiếm "Phòng 1" (172.17.x.x). - Sau đó bạn chạy lại dự án
myapp(docker-compose up). - Docker thấy "Phòng 1" đã có người ở nên tự động đẩy dự án sang "Phòng 2"
(
172.18.x.x) hoặc "Phòng 3" (172.19.x.x).
- Bạn cài thêm một dự án khác (ví dụ:
Hậu quả: lúc trước bạn cấu hình PostgreSQL chỉ cho phép "Phòng 1" (172.17.0.1)
truy cập. Giờ dự án bị đẩy sang "Phòng 2" (172.18.0.1), PostgreSQL thấy IP lạ và chặn
ngay lập tức - lỗi kết nối xuất hiện.
2. Chẩn đoán: tìm IP và tên mạng hiện tại
Chạy lệnh sau để xem container đang nhận IP nào và thuộc mạng nào (thay myapp bằng
tên container của bạn):
# Nếu đã cài jq (đẹp hơn)
docker inspect -f '{{json .NetworkSettings.Networks}}' myapp | jq .
# Nếu chưa cài jq (dùng grep)
docker inspect myapp | grep -A 20 "Networks"Bạn cần tìm hai thông tin:
| Thông tin | Ví dụ |
|---|---|
| Network Name | myapp_default |
| IPAddress | 172.19.0.2 |
3. Xem dải mạng (subnet) và gateway
Sau khi có tên mạng ở bước 2, chạy lệnh sau để xem cấu trúc của mạng đó:
# Thay tên mạng bằng tên bạn tìm được ở bước 2
docker network inspect myapp_defaultKết quả trả về sẽ có đoạn Config như này:
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]Gateway chính là địa chỉ IP mà container dùng để kết nối ra ngoài, bao gồm cả kết nối
tới PostgreSQL trên host.
4. Mở firewall (UFW)
Chạy lệnh này trên VPS để UFW cho phép dải mạng Docker đi qua cổng PostgreSQL (5432):
# Thay 172.21.0.0/16 bằng subnet bạn tìm được ở bước 3
sudo ufw allow from 172.21.0.0/16 to any port 5432
sudo ufw reload5. Cập nhật cấu hình PostgreSQL
5.1. Sửa pg_hba.conf
# Tìm file config (thường ở /etc/postgresql/14/main/ hoặc 16/main/)
sudo nano /etc/postgresql/16/main/pg_hba.confThêm dòng này vào cuối file (thay subnet cho phù hợp):
# Cho phép Docker container kết nối tới PostgreSQL
host all all 172.21.0.0/16 scram-sha-256Nếu bạn dùng xác thực md5 thì đổi scram-sha-256 thành md5. Kiểm tra trong log lỗi
để biết bạn đang dùng loại nào.
5.2. Kiểm tra postgresql.conf
sudo nano /etc/postgresql/16/main/postgresql.confTìm dòng listen_addresses:
- Nếu đã để
listen_addresses = '*'thì không cần sửa gì. - Nếu đang liệt kê từng IP (cách làm trong bài
Cài đặt PostgreSQL trên VPS an toàn) thì bắt
buộc phải thêm gateway của Docker vào danh sách (ví dụ:
,172.21.0.1).
5.3. Restart PostgreSQL
sudo systemctl restart postgresql6. Test lại
Sau khi restart PostgreSQL, restart lại container API:
docker restart myappKiểm tra log để xác nhận kết nối thành công:
docker logs myapp --tail 507. Giải pháp Silver Bullet (khuyến nghị)
Vấn đề với cách sửa ở trên: mỗi lần docker-compose down rồi up lại, Docker có thể
đổi sang dải mạng khác (ví dụ 172.22.x.x). Bạn sẽ phải lặp lại toàn bộ các bước trên.
Giải pháp tối ưu: mở quyền truy cập cho toàn bộ dải IP mà Docker có thể sử dụng, làm một lần duy nhất.
Cơ sở lý thuyết
Theo chuẩn mạng RFC 1918, Docker được phép sử dụng dải IP private class B:
- Từ
172.16.0.0đến172.31.255.255 - Viết gọn theo CIDR:
172.16.0.0/12
Khi cấu hình cho phép 172.16.0.0/12:
| Docker cấp IP | Được phép? |
|---|---|
172.17.0.1 | Có (nằm trong dải) |
172.21.0.1 | Có (nằm trong dải) |
172.30.0.1 | Có (nằm trong dải) |
Bước 1: Sửa pg_hba.conf
sudo nano /etc/postgresql/16/main/pg_hba.confThêm dòng sau (hoặc thay thế dòng cũ):
# Silver Bullet: cho phép bất kỳ IP nào thuộc dải Docker (172.16.x.x -> 172.31.x.x)
host all all 172.16.0.0/12 scram-sha-256Bước 2: Mở firewall UFW
sudo ufw allow from 172.16.0.0/12 to any port 5432
sudo ufw reloadBước 3: Restart PostgreSQL
sudo systemctl restart postgresqlBước 4: Cập nhật connection string trong code
Connection string vẫn phải dùng đúng IP gateway của mạng Docker hiện tại. Gateway luôn
là .1 của subnet:
| Subnet Docker | Host trong connection string |
|---|---|
172.18.x.x | 172.18.0.1 |
172.21.x.x | 172.21.0.1 |
Để không bao giờ phải sửa connection string, bạn có thể dùng host.docker.internal
(trên Mac/Windows) hoặc cấu hình extra_hosts trong docker-compose.yml để map một tên
cố định vào gateway. Tuy nhiên trên Linux VPS, dùng IP gateway trực tiếp là cách đơn
giản và hiệu quả nhất.
Tóm tắt nhanh
# 1. Tìm tên mạng và IP của container
docker inspect myapp | grep -A 20 "Networks"
# 2. Xem subnet và gateway
docker network inspect <tên-mạng>
# 3. Mở firewall (Silver Bullet - làm 1 lần duy nhất)
sudo ufw allow from 172.16.0.0/12 to any port 5432
sudo ufw reload
# 4. Sửa pg_hba.conf - thêm dòng:
# host all all 172.16.0.0/12 scram-sha-256
# 5. Restart PostgreSQL
sudo systemctl restart postgresql
# 6. Restart container
docker restart myappLưu ý
| Triệu chứng | Cách xử lý |
|---|---|
Log báo no pg_hba.conf entry for host "172.x.x.x" | Subnet của container chưa có trong pg_hba.conf - thêm rule cho subnet đó (hoặc 172.16.0.0/12) rồi restart PostgreSQL |
| Rule đã đúng subnet nhưng vẫn lỗi xác thực | Kiểm tra phương thức xác thực trong log lỗi: đổi scram-sha-256 thành md5 nếu server đang dùng md5 |
| Container bị timeout khi kết nối tới port 5432 | UFW chưa mở port 5432 cho subnet Docker - làm lại bước 4 |
Container báo Connection refused | listen_addresses đang liệt kê từng IP mà thiếu gateway của Docker - xem bước 5.2 |
Đang chạy ổn, sau docker-compose down rồi up thì lỗi trở lại | Docker đã cấp subnet mới - áp dụng giải pháp 172.16.0.0/12 và cập nhật IP gateway trong connection string |