Hệ thống --link
là một di sản của Docker, kế thừa từ các phiên bản trước. Bất chấp thực tế rằng chức năng này có thể được hủy bỏ bất kỳ lúc này, nhiều nền tảng phát triển phần mềm hiện tại vẫn đang dựa trên nó (chẳng hạn cấu hình service
của GitLab CI).
Link là một chức năng gắn liền với mạng Docker Bridge mặc định. Trước khi các chức năng Docker Network ra đời, link là giải pháp để các container nhận thức và giao nhận thông tin với nhau một cách an toàn.
Bài viết này sẽ làm rõ bức tranh toàn cảnh về link.
Kết nối container thông qua port-mapping
Đặt vấn đề với một ứng dụng Python Flash đơn giản chạy trong một container:
$ docker run -d -P training/webapp python app.py
nostalgic_morse
Các container có network và IP nội bộ của chúng, cờ -P
được sử dụng để tự động ánh xạ bất cứ port mở nào của container tới một port ngẫu nhiên nằm trong dải ephemeral ports của máy host. Sự ánh xạ này có thể thấy được khi chạy docker ps
, trong ví dụ dưới đây port 5000
của container đã được dẫn tới port 49155
của host:
$ docker ps nostalgic_morse
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
Ánh xạ có thể được cấu hình thủ công thông qua cờ -p
. Ví dụ dưới đây ánh xạ port 5000
của container tới port 80
của host:
$ docker run -d -p 80:5000 training/webapp python app.py
Nếu việc ánh xạ vào một port cố định gây phát sinh vấn đề, thì có thể tự định nghĩa một dải port riêng để Docker chọn ngẫu nhiên:
$ docker run -d -p 8000-9000:5000 training/webapp python app.py
Mặc định, ánh xạ được thực hiện trên tất cả các interface của máy host. Nếu cần thiết thì có thể chỉ định interface được ánh xạ, trong ví dụ dưới đây, port 5000
được ánh xạ vào port 80
, nhưng chỉ trên interface localhost
:
$ docker run -d -p 127.0.0.1:80:5000 training/webapp python app.py
Hoặc một kết hợp khác, vào một port bất kỳ trong dải ephemeral ports, chỉ trên interface localhost
:
$ docker run -d -p 127.0.0.1::5000 training/webapp python app.py
Thậm chí có thể chỉ định port UDP hay SCTP bằng cách sử dụng hậu tố /udp
or /sctp
:
$ docker run -d -p 127.0.0.1:80:5000/udp training/webapp python app.py
Lệnh docker port
có thể được sử dụng để hiển thị port đang được ánh xạ:
$ docker port nostalgic_morse 5000
127.0.0.1:49155
Kết nối thông qua linking system
Lưu ý: nội dung ở phần này liên quan rất chặt chẽ đến mạng bridge mặc định.
Docker phân giải các liên kết dựa theo tên của các container. Tên có thể được Docker tạo ra một cách tự động, mà trong bài viết này là nostalgic_morse
, hoặc được đặt thủ công. Tên tạo ra hai chức năng hữu ích:
- Làm rõ ý nghĩa về mặt chức năng của container, chẳng hạn đặt tên container chứa web application là
web
. - Cung cấp cho Docker một điểm tham chiếu đến container, chẳng hạn container
web
có thể liên kết đến containerdb
thông qua tên. Chức năng này đến từ đặc tính đơn nhất của tên container.
Tên có thể được đặt thủ công bằng cờ --name
chẳng hạn:
$ docker run -d -P --name web training/webapp python app.py
Tên của container có thể được kiểm tra bằng cách sử dụng câu lệnh docker ps
:
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web
Giao tiếp thông qua các link
Các link cho phép các container nhận thức được sự tồn tại của các container khác và giao vận thông tin một cách an toàn từ container này tới container kia. Khi cài đặt một link, một đường dẫn sẽ được tạo ra giữa container nguồn và container nhận. Bên nhận có thể truy cập dữ liệu từ bên nguồn. Để tạo một link, sử dụng cờ --link
. Dưới đây là ví dụ với một container có tên db
được tạo ra từ một ảnh có chứa CSDL PostgreSQL:
$ docker run -d --name db training/postgres
Container web
được tạo ra và liên kết với db
thông qua một link:
$ docker run -d -P --name web --link db:db training/webapp python app.py
Cú pháp sử dụng cờ --link
có dạng sau:
--link <name or id>:alias
Trong đó name
là tên của container được liên kết tới, và alias
là bí danh cho link. Bí danh có thể được đặt tắt:
--link <name or id>
Trong trường hợp này link nhận bí danh giống với tên của container. Ví dụ ở trên có thể được viết lại như sau:
$ docker run -d -P --name web --link db training/webapp python app.py
Thử inspect container web
:
$ docker inspect -f "{{ .HostConfig.Links }}" web
[/db:/web/db]
Kết quả cho thấy container web
đã được liên kết tới container db
, cho phép web
truy xuất dữ liệu của db
.
Điều mà link thực sự làm là gì? Docker tạo ra một sercure tunnel giữa hai container mà không yêu cầu bất kỳ thao tác mở port nào; vậy là web
có thể truy cập db
, mà không yêu cầu db
phải phơi bày bất kỳ thứ gì với mạng.
Docker expose thông tin kết nối của bên nguồn với bên nhận bằng hai đường:
- Các biến môi trường
- Cập nhật tập tin
/etc/hosts
Các biến môi trường
Docker tạo ra một vài biến môi trường trên container bên nhận khi thực hiện link. Đầu tiên là các biến môi trường đến từ tham số của cờ --link
. Phần còn lại đến từ:
- các lệnh
ENV
từ Dockerfile của container bên nguồn - các tùy chọn
-e
,--env
và--env-file
của câu lệnhdocker run
đã khởi động container nguồn
Cơ chế này cho phép lập trình để nhận thức được các thông tin cần thiết của container nguồn từ container đích.
Cảnh báo: phải nắm thật rõ ràng cơ chế expose biến môi trường của container được link. Điều này có thể gây rò rỉ dữ liệu nhạy cảm.
Docker đặt một biến môi trường <alias>_NAME
cho mỗi container đích của cờ --link
. Lấy ví dụ, container web
được link tới container db
bởi --link db:webdb
, vậy Docker sẽ tạo ra biến môi trường WEBDB_NAME=/web/webdb
vào container web
.
Docker cũng định nghĩa một tập các biến môi trường cho mỗi port được expose bởi container nguồn. Mỗi biến có một tiền tố đơn nhất theo dạng <name>_PORT_<port>_<protocol>
, trong đó:
- bí danh
<name>
được chỉ định bởi cờ--link
(ví dụwebdb
) <port>
thể hiện port được expose<protocol>
chỉ định giao thức TCP hay UDP
Docker sử dụng định dạng tiền tố này để định nghĩa ba biến môi trường khác nhau cho mỗi port:
- Biến
prefix_ADDR
chứa địa chỉ IP từ URL, ví dụWEBDB_PORT_5432_TCP_ADDR=172.17.0.82
. - Biến
prefix_PORT
chứa số hiệu của port từ URL, ví dụWEBDB_PORT_5432_TCP_PORT=5432
. - Biết
prefix_PROTO
chứa giao thức từ URL, ví dụWEBDB_PORT_5432_TCP_PROTO=tcp
.
Ngoài ra, Docker cũng tạo một biến môi trường có tên theo định dạng <alias>_PORT
. Biến này chứa URL của port đầu tiên được container nguồn expose, trong đó port “đầu tiên” nghĩa là port có số hiệu thấp nhất. Ví dụ, WEBDB_PORT=tcp://172.17.0.82:5432
. Nếu port được sử dụng trên cả giao thức tcp và udp thì giao thức tcp sẽ được chọn.
Cuối cùng, Docker cũng expose mỗi biến môi trường bắt nguồn từ container nguồn thành các biến môi trường tương ứng tại container đích. Các biến tại container đích được đặt tên theo định dạng <alias>_EVN_<name>
, trong đó <name>
là tên biến nguyên thủy. Giá trị của biến sẽ lấy tự giá trị được sử dụng khi khởi động container nguồn.
Lệnh env
có thể được sử dụng để liệt kê danh sách các biến môi trường của container chỉ định
$ docker run --rm --name web2 --link db:db training/webapp env
. . .
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
. . .
Trong ví dụ trên là một loạt biến môi trường với các thông tin hữu dụng về container nguồn. Tất cả đều bắt đầu với tiền tố DB_
– vốn là alias
của --link
. Các biến này có thể được sử dụng để kết nối ứng dụng trong container đích đến CSDL tại container db
. Sự kết nối là riêng tư và được bảo mật; chỉ container web
có thể giao tiếp với db
theo cách này.
Lưu ý quan trọng về các biến môi trường
Không giống như các bản ghi của tập tin /etc/hosts
, các địa chỉ IP được chứa trong biến môi trường sẽ không được cập nhật nếu container nguồn khởi động lại. Nên sử dụng bản ghi của /etc/hosts
để phân giải địa chỉ IP của container được liên kết.
Các biến môi trường chỉ được đặt cho process đầu tiên thực thi trong container. Một vài daemon, chẳng hạn sshd
, sẽ không nhìn thấy các biến này.
Cập nhật /etc/hosts
Bên cạnh các biến môi trường, Docker bổ sung một bản ghi cho mỗi container nguồn vào /etc/hosts
. Chẳng hạn như dưới đây là bản ghi mà container web
nắm giữ:
$ docker run -t -i --rm --link db:webdb training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
. . .
172.17.0.5 webdb 6e5cdeb2d300 db
Ở trên có hai bản ghi. Bản ghi đầu tiên để phân giải hostname (chính là ID) của container web
. Bản ghi thứ hai phân giải bí danh, tên, và hostname của container db
:
root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping webdb
PING webdb (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms
Ví dụ trên đã cài đặt công cụ và sau đó ping đến db
thông qua bí danh. Bí danh này có thể được dùng để cấu hình cho ứng dụng chạy trong web
truy cập đến db
.
Nếu container db
được khởi động lại, tập tin /etc/hosts
trên container web
sẽ được tự động cập nhật.
$ docker restart db
db
$ docker run -t -i --rm --link db:db training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
. . .
172.17.0.9 db
Tài nguyên tham khảo
- Docker docs: Legacy container links