Xung đột Port 80: Tại sao hệ thống Docker của bạn sớm muộn cũng gặp bế tắc
Tất cả chúng ta đều đã từng trải qua cảm giác này. Bạn bắt đầu với một container Docker, map port 8080 sang 80, và mọi thứ hoạt động ổn định. Sau đó, bạn thêm dự án thứ hai, dịch vụ thứ ba, hoặc có thể là một dashboard phân tích nội bộ. Đột nhiên, bạn bị kẹt. Bạn không thể map hai container vào cùng port 80 trên một địa chỉ IP duy nhất. Kết quả là bạn phải ghi nhớ những URL kỳ quặc như your-ip:8081 hay your-ip:8082 chỉ để truy cập vào công cụ của chính mình.
Tiếp đến là cơn ác mộng SSL. Việc quản lý chứng chỉ Let’s Encrypt thủ công bằng Certbot và các cron job cho năm container khác nhau là cách nhanh nhất để làm hỏng hệ thống. Bản thân tôi đã từng mất ngủ nhiều đêm vì chứng chỉ hết hạn chỉ vì một lỗi cú pháp nhỏ trong file cấu hình mà không ai nhận ra. Sự vất vả khi làm thủ công này chính là rào cản lớn nhất để mở rộng home lab hoặc môi trường production của bạn.
Vấn đề: Cấu hình tĩnh trong thế giới Container động
Vấn đề rất đơn giản: chúng ta đang sử dụng các công cụ tĩnh cho những môi trường động. Các proxy truyền thống như Nginx hay HAProxy được xây dựng cho thời đại cũ của các máy chủ “thú cưng” (pet servers) — những cỗ máy có địa chỉ IP cố định trong nhiều năm. Trong Docker, các container có tính chất tạm thời (ephemeral). Chúng được khởi tạo, bị xóa bỏ và thay đổi địa chỉ IP nội bộ mỗi khi bạn cập nhật.
Nếu bạn vẫn trung thành với proxy truyền thống, bạn phải chỉnh sửa file cấu hình thủ công mỗi khi triển khai một dịch vụ mới. Bạn phải chỉ định chính xác cho proxy biết ứng dụng mới nằm ở đâu (ví dụ: 172.18.0.5). Bước thủ công này chính là nơi xảy ra sự chậm trễ trong triển khai và sai lệch cấu hình. Nó biến một công việc chỉ mất 5 phút thành một buổi fix lỗi kéo dài 30 phút.
So sánh các giải pháp: Nginx đối đầu Traefik
Khi tìm cách thoát khỏi mớ hỗn độn này, ba lựa chọn chính thường xuất hiện:
- Nginx/HAProxy thủ công: Đây là những “con quái vật” hiệu năng cao. Tuy nhiên, chúng yêu cầu reload cấu hình thủ công và các sidecar bên ngoài cho SSL. Chúng được xây dựng cho sự ổn định, không phải cho thế giới container thay đổi chóng mặt.
- Nginx Proxy Manager: Cung cấp giao diện web trực quan. Rất tuyệt cho người mới bắt đầu, nhưng vẫn yêu cầu thao tác click thủ công cho mỗi dịch vụ mới. Rất khó để quản lý thông qua kiểm soát phiên bản (GitOps).
- Traefik Proxy: Đây là một proxy “cloud-native”. Thay vì một danh sách backend tĩnh, nó theo dõi Docker socket. Khi một container khởi chạy, Traefik sẽ nhận ra ngay lập tức. Nếu container có các label phù hợp, Traefik sẽ tự động tạo các route và lấy chứng chỉ SSL mà bạn không cần động tay vào.
Giải pháp tối ưu: Service Discovery với Traefik
Traefik là lựa chọn hợp lý cho Docker vì nó coi cấu hình là metadata. Trên thực tế, điều này có nghĩa là bạn có thể triển khai một dịch vụ web hoàn chỉnh, bảo mật và sẵn sàng cho production chỉ bằng cách thêm 6 hoặc 7 dòng vào file docker-compose.yml. Nó chuyển sự phức tạp từ proxy sang chính quá trình triển khai ứng dụng.
Bước 1: Chuẩn bị hạ tầng
Trước tiên, chúng ta cần một mạng dùng chung để proxy có thể giao tiếp với các ứng dụng. Chúng ta cũng cần một file riêng biệt với quyền truy cập nghiêm ngặt để lưu trữ các SSL key.
# Tạo mạng proxy dùng chung
docker network create proxy-net
# Tạo file lưu trữ SSL và giới hạn quyền truy cập
touch acme.json
chmod 600 acme.json
Bước 2: Khởi chạy Traefik Core
File Docker Compose này thiết lập “bộ não” cho việc điều phối của bạn. Nó mở các port 80 và 443, đồng thời cấp cho Traefik quyền chỉ đọc (read-only) vào Docker socket để nó có thể giám sát các container.
version: "3.8"
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: always
security_opt:
- no-new-privileges:true
networks:
- proxy-net
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./acme.json:/acme.json
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.myresolver.acme.email=admin@yourdomain.com"
- "--certificatesresolvers.myresolver.acme.storage=/acme.json"
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=myresolver"
Hãy thay thế email và tên miền placeholder bằng thông tin thực tế của bạn. Khi cấu hình này chạy, Traefik sẽ tự động xử lý quá trình bắt tay (handshake) với Let’s Encrypt thông qua HTTP-01 challenge.
Bước 3: Triển khai ứng dụng không cần cấu hình thủ công
Đây là phần thú vị nhất. Để đưa một ứng dụng mới lên mạng với HTTPS, bạn không cần đụng vào Traefik. Bạn chỉ cần gắn label cho ứng dụng của mình. Dưới đây là ví dụ về một dịch vụ “Whoami” đơn giản:
services:
webapp:
image: traefik/whoami
container_name: web-app
networks:
- proxy-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`app.yourdomain.com`)"
- "traefik.http.routers.webapp.entrypoints=websecure"
- "traefik.http.routers.webapp.tls.certresolver=myresolver"
Khi bạn chạy lệnh docker-compose up -d, Traefik sẽ phát hiện container mới. Nó nhận diện quy tắc app.yourdomain.com, yêu cầu chứng chỉ và bắt đầu điều phối traffic. Quá trình này thường diễn ra trong chưa đầy 10 giây.
Kiểm soát linh hoạt với Middleware
Traefik không chỉ đơn thuần là chuyển hướng traffic; nó còn có thể sửa đổi dữ liệu ngay khi đang truyền tải. Middleware cho phép bạn thêm các tính năng như Basic Auth hoặc rate limiting mà không cần thay đổi một dòng code ứng dụng nào. Ví dụ, để bảo vệ một dashboard nhạy cảm bằng mật khẩu, bạn có thể thêm các label sau:
# Sử dụng htpasswd để tạo mã hash
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$..."
- "traefik.http.routers.dashboard.middlewares=auth"
Điều này cực kỳ hoàn hảo để thêm một lớp bảo mật nhanh chóng cho các công cụ nội bộ vốn không có hệ thống đăng nhập sẵn.
Bảo trì và các quy tắc thực thi tốt nhất
Hãy giữ cấu hình Traefik trong thư mục riêng của nó. Tôi khuyên bạn nên sử dụng cấu trúc trong đó Traefik nằm ở /opt/traefik và các ứng dụng khác nằm trong các thư mục tương ứng của chúng. Việc cô lập này ngăn chặn lỗi đánh máy trong file compose của một ứng dụng làm hỏng toàn bộ hệ thống proxy của bạn.
Một sai lầm phổ biến là quên mở port 80 và 443 trong firewall đám mây (như AWS Security Group hoặc UFW). Nếu port 80 bị chặn, Let’s Encrypt không thể xác thực tên miền của bạn và chứng chỉ SSL sẽ không bao giờ được tạo. Luôn kiểm tra kỹ các bản ghi DNS A trước khi khởi chạy một dịch vụ mới.
Quy trình làm việc mới
- Khởi tạo mạng
proxy-nettoàn cục. - Chạy Traefik một lần và để nó theo dõi Docker socket.
- Triển khai ứng dụng với các label
traefik.enable=truebất cứ khi nào bạn cần. - Chỉ việc rời đi và để Traefik lo phần SSL và điều phối.
Thiết lập này biến hạ tầng của bạn từ một tập hợp các file rời rạc, dễ hỏng thành một hệ thống có khả năng tự phục hồi. Nó giúp tiết kiệm hàng giờ fix lỗi và đảm bảo rằng mọi dịch vụ bạn triển khai đều được bảo mật mặc định ngay từ giây đầu tiên nó hoạt động.

