Container có hai mạng chọn sai cổng mặc định
Hai kết nối, một tuyến mặc định và mạng sai luôn thắng. Ghim lối ra bằng Compose gw_priority và xác nhận từ bên trong container.
Container của bạn vẫn trả lời trên cầu điều khiển, nhưng mọi gói đi ra đều rời mạng qua macvlan của văn phòng. Không có gì hỏng. Hai kết nối mỗi cái cung cấp một cổng mặc định, và tuyến mặc định duy nhất đã đi tới mạng mà bạn không định, nên dịch vụ vẫn chạy trong khi lưu lượng của nó đi đường dài hơn. Khi có người nhận ra, bằng chứng là một bảng định tuyến chứ không phải thông báo lỗi.
Cách giải thích thông thường là Docker đã chọn mạng một cách tình cờ. Trên thực tế quyết định cũ tuân theo thứ tự chữ cái của tên các mạng được gắn, khiến việc định tuyến trở thành hệ quả phụ của một nhãn. gw_priority thay thế bằng một con số bạn chủ động chọn, và việc áp dụng mất khoảng một phút sau khi sơ đồ được ghi ra.
Vì sao tuyến mặc định rơi vào mạng sai
Mỗi mạng mà dịch vụ tham gia đều có thể cung cấp cổng riêng, nhưng container vẫn chỉ có đúng một tuyến mặc định. Trước Compose 2.33.1, thế hòa được giải quyết bằng cách sắp xếp tên mạng, nên một nhãn như control đứng trước office quyết định lưu lượng đi đâu. Không có gì bên trong container ghi lại rằng quyết định đó là tùy tiện, vì vậy nó qua được bước duyệt mã và về sau lộ ra như một tuyến kỳ lạ. Đặt chính sách vào tệp compose là cách duy nhất để lựa chọn có thể kiểm tra được.
Hai thứ thực sự thay đổi mạng nào thắng: các giá trị gw_priority bạn gán và việc mạng nào được gắn vào. Thêm mạng thứ ba thì tập ứng viên lớn hơn; bỏ macvlan thì không còn gì tranh với cầu. Cả hai thao tác đều không hiện trong nhật ký ứng dụng, vì tuyến mặc định sai hiếm khi cắt đứt kết nối hoàn toàn. Nó chỉ khiến các phản hồi đến từ một địa chỉ mà phía bên kia có thể không ngờ tới.
Ghim cổng bằng gw_priority
Compose hiển thị gw_priority trên mỗi kết nối dịch vụ-với-mạng từ phiên bản 2.33.1, và số lớn nhất thắng. Không có khóa cổng riêng cho từng dịch vụ, nên giá trị thuộc khối networks của dịch vụ. Ví dụ dưới đây trao tuyến mặc định cho control trong khi office vẫn truy cập được cho LAN kết nối trực tiếp của nó.
services:
vpn:
networks:
control:
gw_priority: 1
office:
gw_priority: 0
networks:
control:
driver: bridge
ipam:
config:
- subnet: 172.30.50.0/24
office:
driver: macvlan
driver_opts:
parent: eno1.120
macvlan_mode: bridge
ipam:
config:
- subnet: 192.168.120.0/24
ip_range: 192.168.120.192/27
gateway: 192.168.120.1
Hãy đọc khối này thành hai bộ điều khiển riêng: gw_priority quyết định tuyến mặc định, còn phần ipam quyết định địa chỉ. Chỉ đổi subnet thì lựa chọn lối ra vẫn nguyên vẹn, và đó là cách phổ biến nhất khiến bản sửa này chỉ được áp dụng một nửa. Giữ các mức ưu tiên bên cạnh mạng mà chúng chi phối để người đọc sau không phải đi tìm quyết định.
Đừng nhầm gw_priority với thuộc tính priority thông thường bên cạnh. Thuộc tính đó sắp xếp thứ tự tạo kết nối và không nói gì về định tuyến, nên nó không thể chọn cổng mặc định và không sửa tên giao diện. Nếu ai đó khuyên tăng priority để điều hướng lưu lượng, lời khuyên đó sai kể cả khi thứ tự thu được tình cờ trông đúng.
Xác minh mạng nào thực sự mang tuyến mặc định
Kiểm tra bên trong container đang chạy, vì bảng định tuyến của máy chủ mô tả máy chủ chứ không mô tả không gian tên mà dịch vụ dùng. Chạy kiểm tra cấu hình trước để lỗi gõ phím thất bại trước khi khởi động lại, rồi đọc tuyến mà container thực sự đã cài.
docker compose config --quiet
docker exec vpn ip -4 route show default
docker exec vpn cat /proc/net/route
Kết quả đúng nêu thiết bị cầu và 172.30.50.1; kết quả sai nêu 192.168.120.1 và thiết bị macvlan. Nếu image không có gói iproute2, tệp /proc vẫn báo tuyến mặc định, với đích 00000000 và các byte cổng bị đảo. So sánh cột giao diện với docker inspect khi tên thiết bị không rõ ràng.
Macvlan là một cổng vật lý, không phải bản sao của cầu
Macvlan cho container một danh tính thứ hai trên dây, và chính điều đó làm cho việc truy cập thiết bị cục bộ hoạt động. Nó cũng có nghĩa là cổng chuyển mạch và hypervisor phải chấp nhận thêm một địa chỉ MAC, và nhiều mạng đám mây âm thầm từ chối điều đó. Hãy thử một container trước khi lập kế hoạch cho cả đội, vì MAC bị từ chối thường xuất hiện như một liên kết đang bật mà không truyền gì.
Thiết lập macvlan_mode: bridge là một chế độ của trình điều khiển macvlan, không phải cầu phần mềm của Docker, và passthru là một chế độ chuyên dụng riêng chứ không phải mặc định. Linux cũng ngăn container macvlan chạm tới máy chủ của chính nó qua cùng một gốc, và đó là lý do giữ lại cầu điều khiển thay vì mong một kết nối làm cả hai việc.
Đặt tên cho gốc VLAN trước khi Compose nhìn thấy nó
Docker coi một gốc có dấu chấm như eno1.120 là giao diện con 802.1Q và tự tạo thiết bị VLAN. Sự tiện lợi đó có giới hạn: tên suy ra phụ thuộc vào tên giao diện vật lý, và những tên dài có thể đẩy kết quả vượt quá điều mà các công cụ xung quanh mong đợi. Tạo một giao diện VLAN có tên trong cấu hình mạng của máy chủ và trỏ parent tới nó giữ cho cả hai đầu có thể dự đoán được.
Hãy nhớ rằng khóa dưới networks là tên Compose, không phải thiết bị Linux. Tìm trên máy chủ một giao diện tên office sẽ thất bại, vì nhân chỉ thấy giao diện gốc và cặp veth phía sau container.
Dành chỗ cho VPN ở trên
Một ứng dụng khách VPN kết nối sau khi các mạng này tồn tại, nên nó có thể thêm tuyến cụ thể hơn, quy tắc chính sách và bộ lọc bất kể gw_priority. Hãy lập kế hoạch tiền tố từ xa so với cả hai subnet cục bộ, bao gồm các phản hồi mà đường điều khiển cần. Khi một tiền tố từ xa chồng lên LAN văn phòng, hãy sửa kế hoạch địa chỉ thay vì xé toạc cả một dải riêng khỏi đường hầm.
Kiểm tra lại địa chỉ, tuyến và đường đi của dịch vụ sau khi VPN khởi động và một lần nữa sau khi nó kết nối lại, vì chính lúc kết nối lại là lúc một tuyến thêm bằng tay biến mất. Hãy ghi bảng mong đợi trước lần thử đầu tiên để phép so sánh về sau là một diff chứ không phải bài tập trí nhớ. Giữ quyền sở hữu mạng lâu dài trong Compose và để cấu hình VPN chỉ sở hữu chính sách của riêng nó.