Làm cách nào để thiết lập liên kết giữa các vùng chứa Docker để việc khởi động lại không làm hỏng nó?


80

Tôi có một vài vùng chứa Docker đang chạy như:

  • Nginx
  • Ứng dụng web 1
  • Ứng dụng web 2
  • PostgreSQL

Vì Nginx cần kết nối với các máy chủ ứng dụng web bên trong ứng dụng web 1 và 2 và các ứng dụng web cần kết nối với PostgreSQL, tôi có các liên kết như sau:

  • Nginx --- liên kết ---> Ứng dụng web 1
  • Nginx --- liên kết ---> Ứng dụng web 2
  • Ứng dụng web 1 --- liên kết ---> PostgreSQL
  • Ứng dụng web 2 --- liên kết ---> PostgreSQL

Điều này hoạt động khá tốt lúc đầu. Tuy nhiên, khi tôi phát triển phiên bản mới của ứng dụng web 1 và ứng dụng web 2, tôi cần phải thay thế chúng. Những gì tôi làm là xóa các vùng chứa ứng dụng web, thiết lập các vùng chứa mới và khởi động chúng.

Đối với vùng chứa ứng dụng web, địa chỉ IP của chúng lúc đầu sẽ giống như:

  • 172.17.0.2
  • 172.17.0.3

Và sau khi tôi thay thế chúng, chúng sẽ có địa chỉ IP mới:

  • 172.17.0.5
  • 172.17.0.6

Giờ đây, các biến môi trường tiếp xúc đó trong vùng chứa Nginx vẫn đang trỏ đến các địa chỉ IP cũ. Đây là vấn đề. Làm cách nào để thay thế một vùng chứa mà không phá vỡ liên kết giữa các vùng chứa? Vấn đề tương tự cũng sẽ xảy ra với PostgreSQL. Nếu tôi muốn nâng cấp phiên bản hình ảnh PostgreSQL, tôi chắc chắn cần phải xóa nó và chạy phiên bản mới, nhưng sau đó tôi cần phải xây dựng lại toàn bộ biểu đồ vùng chứa, vì vậy điều này không lý tưởng cho hoạt động của máy chủ trong đời thực.

Câu trả lời:


53

Hiệu ứng của --linklà tĩnh, vì vậy nó sẽ không hoạt động cho kịch bản của bạn (hiện không có liên kết lại, mặc dù bạn có thể xóa liên kết ).

Chúng tôi đã và đang sử dụng hai cách tiếp cận khác nhau tại dockerize.it để giải quyết vấn đề này, mà không cần liên kết hoặc đại sứ (mặc dù bạn cũng có thể thêm đại sứ).

1) Sử dụng DNS động

Ý tưởng chung là bạn chỉ định một tên duy nhất cho cơ sở dữ liệu của mình (hoặc bất kỳ dịch vụ nào khác) và cập nhật máy chủ DNS tồn tại trong thời gian ngắn với IP thực khi bạn khởi động và dừng vùng chứa.

Chúng tôi bắt đầu với SkyDock . Nó hoạt động với hai bộ chứa docker, máy chủ DNS và một màn hình giúp cập nhật tự động. Sau đó, chúng tôi chuyển sang một cái gì đó tùy chỉnh hơn bằng cách sử dụng Consul (cũng sử dụng phiên bản dockerized: docker -consul ).

Một sự phát triển của điều này (mà chúng tôi chưa thử) sẽ là thiết lập etcd hoặc tương tự và sử dụng API tùy chỉnh của nó để tìm hiểu các IP và cổng. Phần mềm cũng phải hỗ trợ cấu hình lại động.

2) Sử dụng ip cầu docker

Khi hiển thị các cổng container, bạn chỉ có thể liên kết chúng với docker0cầu nối, có (hoặc có thể có) một địa chỉ nổi tiếng.

Khi thay thế vùng chứa bằng phiên bản mới, chỉ cần đặt vùng chứa mới xuất bản cùng một cổng trên cùng một IP.

Điều này đơn giản hơn nhưng cũng hạn chế hơn. Bạn có thể bị xung đột cổng nếu bạn chạy phần mềm tương tự (ví dụ: hai container không thể lắng nghe trên cổng 3306 trên docker0cầu), etcétera… vì vậy yêu thích hiện tại của chúng tôi là tùy chọn 1.


20

Các liên kết dành cho một vùng chứa cụ thể, không dựa trên tên của một vùng chứa. Vì vậy, thời điểm bạn xóa một vùng chứa, liên kết sẽ bị ngắt kết nối và vùng chứa mới (thậm chí có cùng tên) sẽ không tự động thế chỗ.

Tính năng mạng mới cho phép bạn kết nối với các vùng chứa theo tên của chúng, vì vậy nếu bạn tạo một mạng mới, bất kỳ vùng chứa nào được kết nối với mạng đó đều có thể đến các vùng chứa khác theo tên của chúng. Thí dụ:

1) Tạo mạng mới

$ docker network create <network-name>       

2) Kết nối vùng chứa với mạng

$ docker run --net=<network-name> ...

hoặc là

$ docker network connect <network-name> <container-name>

3) Ping container theo tên

docker exec -ti <container-name-A> ping <container-name-B> 

64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

Xem này phần của tài liệu;

Lưu ý: Không giống như kế thừa links, mạng mới sẽ không tạo các biến môi trường cũng như chia sẻ các biến môi trường với các vùng chứa khác.

Tính năng này hiện không hỗ trợ bí danh


Tôi muốn chỉ ra rằng điều này chỉ hoạt động trong phiên bản 1.9 trở lên. Một số bản phân phối vẫn chưa phát hành bản mới nhất.
John Giotta

Một tùy chọn khác là sử dụng bí danh phạm vi mạng thay vì tên vùng chứa (không phải lúc nào cũng phải là duy nhất trên toàn cầu). Nhưng câu trả lời là hoàn toàn chính xác.
Ivan Anishchuk

10

Bạn có thể sử dụng một thùng chứa đại sứ . Nhưng không liên kết vùng chứa đại sứ với khách hàng của bạn, vì điều này tạo ra cùng một vấn đề như trên. Thay vào đó, hãy sử dụng cổng tiếp xúc của vùng chứa đại sứ trên máy chủ docker (thường là 172.17.42.1). Thí dụ:

khối lượng postgres:

$ docker run --name PGDATA -v /data/pgdata/data:/data -v /data/pgdata/log:/var/log/postgresql phusion/baseimage:0.9.10 true

postgres-container:

$ docker run -d --name postgres --volumes-from PGDATA -e USER=postgres -e PASS='postgres' paintedfox/postgresql

đại sứ-container cho bưu chính:

$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador

Giờ đây, bạn có thể bắt đầu một vùng chứa ứng dụng khách postgresql mà không cần liên kết vùng chứa Ambassador và truy cập vào postgresql trên máy chủ cổng (thường là 172.17.42.1):

$ docker run --rm -t -i paintedfox/postgresql /bin/bash
root@b94251eac8be:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
root@b94251eac8be:/# echo $PGHOST
172.17.42.1
root@b94251eac8be:/#
root@b94251eac8be:/# psql -h $PGHOST --user postgres
Password for user postgres: 
psql (9.3.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.

postgres=#
postgres=# select 6*7 as answer;
 answer 
--------
     42
(1 row)

bpostgres=# 

Bây giờ bạn có thể khởi động lại vùng chứa đại sứ trong khi phải khởi động lại ứng dụng khách.


"-p 5432: 5432" sẽ không để lộ PostgreSQL ra thế giới bên ngoài?
Fang-Pen Lin

2
Nó sẽ được thôi. Nếu bạn không muốn điều này, bạn có thể sử dụng "-p 172.17.42.1:5432:5432".
Swen Thümmler

1
nhân tiện, tại sao bạn cần tạo vùng chứa "PGDATA" đó và liên kết nó với vùng chứa postgresql? Tôi không hiểu, tại sao không chỉ tạo vùng chứa postgresql và ánh xạ trực tiếp ổ đĩa của nó vào một thư mục máy chủ lưu trữ?
Fang-Pen Lin

Vùng chứa PGDATA không cần thiết, tôi sử dụng nó chỉ để tách các mối quan tâm. Khi khởi động vùng chứa posgres, tôi không cần nhớ cách các ổ trong vùng chứa PGDATA được ánh xạ. Tôi đã thêm nó vì đó là cách tôi hiện đang làm. Đó là cơ bản một vấn đề của hương vị - Bản thân tôi chưa chắc chắn cho dù đó là một ý tưởng tốt hay không ...
Swen Thümmler

Thực sự là phương pháp hay nhất để sử dụng một vùng chứa khối lượng dữ liệu như Swen.
derFunk

2

Nếu ai đó vẫn tò mò, bạn phải sử dụng các mục nhập máy chủ trong tệp / etc / hosts của mỗi vùng chứa docker và không nên phụ thuộc vào các biến ENV vì chúng không được cập nhật tự động.

Sẽ có một mục nhập tệp máy chủ cho mỗi vùng chứa được liên kết ở định dạng LINKEDCONTAINERNAME_PORT_PORTNUMBER_TCP, v.v.

Sau đây là từ tài liệu của docker

Lưu ý quan trọng về các biến môi trường Docker

Không giống như các mục nhập máy chủ trong tệp / etc / hosts, các địa chỉ IP được lưu trữ trong các biến môi trường không được cập nhật tự động nếu vùng chứa nguồn được khởi động lại. Chúng tôi khuyên bạn nên sử dụng các mục nhập máy chủ trong / etc / hosts để phân giải địa chỉ IP của các vùng chứa được liên kết.

Các biến môi trường này chỉ được đặt cho quy trình đầu tiên trong vùng chứa. Một số daemon, chẳng hạn như sshd, sẽ làm sạch chúng khi sinh ra vỏ để kết nối.


2

Điều này được bao gồm trong bản dựng thử nghiệm của docker 3 tuần trước, với sự ra đời của các dịch vụ: https://github.com/docker/docker/blob/master/experimental/networking.md

Bạn sẽ có thể có được một liên kết động tại chỗ bằng cách chạy một vùng chứa docker với các --publish-service <name>đối số. Tên này sẽ có thể truy cập được thông qua DNS. Điều này liên tục xảy ra khi khởi động lại vùng chứa (tất nhiên là miễn là bạn khởi động lại vùng chứa với cùng tên dịch vụ)


Làm thế nào để bạn cài đặt phiên bản đó? github.com/docker/docker/releases/tag/v1.8.0-rc1
m59,

1
Xem trang này để biết thêm thông tin: github.com/docker/docker/tree/master/experimental . Phiên bản ngắn: chạy wget -qO- https://experimental.docker.com/ | shđể cài đặt phiên bản thử nghiệm
Laurens Rietveld

1
Câu trả lời này hợp lệ nhưng hiện đã lỗi thời vì docker đã xóa publish-servicetùy chọn thử nghiệm . Bây giờ họ có bí danh phạm vi mạng để thay thế. Về cơ bản là cùng một thứ.
Ivan Anishchuk

1

Bạn có thể sử dụng liên kết dockerlinks với tên để giải quyết vấn đề này.

Hầu hết thiết lập cơ bản trước tiên sẽ là tạo một vùng chứa cơ sở dữ liệu được đặt tên :

$ sudo docker run -d --name db training/postgres

sau đó tạo vùng chứa web kết nối với db:

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

Với điều này, bạn không cần phải kết nối thủ công các vùng chứa với địa chỉ IP của chúng.


2
Hm ... có vẻ như docker bằng cách nào đó sẽ tạo tên máy chủ được liên kết cho bạn, nhưng theo cách của nó, nó tạo tên trong / etc / hosts, nó tĩnh, khi tôi khởi động lại vùng chứa được liên kết, IP sẽ thay đổi, nhưng / etc / hosts vẫn giữ nguyên, vì vậy nó sẽ không hoạt động.
Fang-Pen Lin

2
Vì Docker phiên bản 1.0 sẽ chỉ định địa chỉ IP tích cực hơn. Khi bạn khởi động lại một vùng chứa ( dbtrong trường hợp này), nó sẽ nhận được một địa chỉ IP mới. Vùng chứa khác của bạn (được khởi động lại hoặc không) sẽ giữ lại các giá trị ENV kể từ thời điểm bạn khởi chạy nó và nó vô dụng.
GermanDZ

1
fyi có vẻ như sắp có bản sửa lỗi, / etc / hosts sẽ được cập nhật khi một vùng chứa được liên kết được khởi động lại: github.com/docker/docker/issues/6350
jamhid

1
Vấn đề này dường như đã được khắc phục và phương pháp được đề xuất đang hoạt động đối với tôi.
Jens Piegsa

1
Đây là câu trả lời chính xác nhất ở đây. Vấn đề duy nhất là liên kết là một chiều và thêm sự phụ thuộc giữa các vùng chứa: bạn không thể liên kết chéo hai vùng chứa và bạn không thể dừng vùng chứa được liên kết rồi bắt đầu lại (với các tùy chọn mới hoặc thứ gì đó). Trong bất kỳ trường hợp nào, hãy sử dụng mạng (và hoặc net-aliashoặc tên vùng chứa).
Ivan Anishchuk

1

với cách tiếp cận OpenSVC, bạn có thể giải quyết bằng cách:

  • sử dụng dịch vụ có địa chỉ ip / tên dns của riêng nó (dịch vụ mà người dùng cuối của bạn sẽ kết nối với)
  • yêu cầu docker hiển thị các cổng cho địa chỉ ip cụ thể này (tùy chọn docker "--ip")
  • định cấu hình ứng dụng của bạn để kết nối với địa chỉ ip dịch vụ

mỗi lần bạn thay thế một vùng chứa, bạn chắc chắn rằng nó sẽ kết nối với địa chỉ ip chính xác.

Hướng dẫn tại đây => Docker Multi Containers với OpenSVC

đừng bỏ lỡ phần "điều phối phức tạp" ở cuối hướng dẫn, phần này có thể giúp bạn bắt đầu / dừng vùng chứa theo đúng thứ tự (1 tập con postgresql + 1 tập hợp con ứng dụng web + 1 tập hợp con nginx)

Hạn chế chính là bạn để lộ các cổng webapp và PostgreSQL với địa chỉ công khai, và thực tế chỉ có cổng nginx tcp mới cần được công khai.


1

Bạn cũng có thể thử phương pháp đại sứ để có một vùng chứa trung gian chỉ để giữ nguyên liên kết ... (xem https://docs.docker.com/articles/ambassador_pattern_linking/ ) để biết thêm thông tin


Ambassador là một mẫu tốt nhưng nó gặp phải vấn đề tương tự: địa chỉ ip sẽ không nhất thiết được cập nhật khi khởi động lại. Tuy nhiên, chúng rất tốt cho kết nối giữa các máy chủ. Chà, có thể với bản phát hành docker mới cũng không cần thiết.
Ivan Anishchuk

@IvanAnishchuk đúng, nhưng vào thời điểm đó những nhận xét được đưa ra đây là cách để đi ... (2 năm trước;))
Gekkie

0

Bạn có thể liên kết các cổng kết nối của hình ảnh với các cổng cố định trên máy chủ và định cấu hình các dịch vụ để sử dụng chúng thay thế.

Điều này cũng có những hạn chế, nhưng nó có thể hoạt động trong trường hợp của bạn.


Việc ràng buộc các cổng localhost thực sự có nhược điểm của nó. Kết nối mạng docker mới khiến nó lỗi thời.
Ivan Anishchuk

0

Một thay thế khác là sử dụng --net container:$CONTAINER_IDtùy chọn.

Bước 1: Tạo vùng chứa "mạng"

docker run --name db_net ubuntu:14.04 sleep infinity
docker run --name app1_net --link db_net:db ubuntu:14.04 sleep infinity
docker run --name app2_net --link db_net:db ubuntu:14.04 sleep infinity
docker run -p 80 -p 443 --name nginx_net --link app1_net:app1 --link app2_net:app2 ubuntu:14.04 sleep infinity

Bước 2: Đưa dịch vụ vào vùng chứa "mạng"

docker run --name db --net container:db_net pgsql
docker run --name app1 --net container:app1_net app1
docker run --name app2 --net container:app1_net app2
docker run --name nginx --net container:app1_net nginx

Miễn là bạn không chạm vào vùng chứa "mạng", địa chỉ IP của các liên kết của bạn sẽ không thay đổi.


Mạng cầu nối do người dùng tạo với một cái tên có ý nghĩa có lẽ là một lựa chọn tốt hơn. Không yêu cầu tạo vùng chứa chỉ để sử dụng mạng của họ.
Ivan Anishchuk

0

Bí danh phạm vi mạng là những gì bạn cần trong trường hợp này. Đó là một tính năng khá mới, có thể được sử dụng để "xuất bản" một vùng chứa cung cấp dịch vụ cho toàn mạng, không giống như các bí danh liên kết chỉ có thể truy cập từ một vùng chứa.

Nó không thêm bất kỳ loại phụ thuộc nào giữa các vùng chứa - chúng có thể giao tiếp miễn là cả hai đều đang chạy, bất kể khởi động lại và thứ tự thay thế và khởi chạy. Tôi tin rằng nó sử dụng DNS nội bộ thay vì / etc / hosts

Sử dụng nó như thế này: docker run --net=some_user_definied_nw --net-alias postgres ...và bạn có thể kết nối với nó bằng bí danh đó từ bất kỳ vùng chứa nào trên cùng một mạng.

Không hoạt động trên mạng mặc định, rất tiếc, bạn phải tạo một với docker network create <network>và sau đó sử dụng nó với --net=<network>mọi vùng chứa ( soạn thư cũng hỗ trợ nó ).

Ngoài việc vùng chứa bị hỏng và do đó không thể truy cập bằng bí danh, nhiều vùng chứa cũng có thể chia sẻ một bí danh, trong trường hợp đó, nó không được đảm bảo rằng nó sẽ được giải quyết cho đúng. Nhưng trong một số trường hợp có thể giúp nâng cấp liền mạch, có lẽ.

Tất cả đều chưa được ghi chép đầy đủ, khó có thể tìm ra chỉ bằng cách đọc trang người.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.