Sổ tay Docker: Thao tác Docker Network cơ bản

Sổ tay Docker: Thao tác Docker Network cơ bản

Trong phần trước, bạn đã tìm hiểu cách xây dựng ứng dụng JavaScript với Docker. Nếu bỏ lỡ thì bạn có thể xem lại bài viết này ở dưới đây:

Sổ tay Docker: Cách xây dựng ứng dụng JavaScript với Docker
Trong hướng dẫn này, bạn sẽ học cách xây dựng ứng dụng JavaScript với Docker. Tạo image cho ứng dụng, tối ưu image, chạy container, …

Các bài viết trước đây, bạn chỉ làm việc với các container đơn lẻ. Nhưng trong thực tế, phần lớn các dự án mà bạn sẽ phải làm việc sẽ có nhiều hơn một container. Và thành thật mà nói, làm việc với một loạt các container có thể hơi khó khăn nếu bạn không hiểu các khía cạnh của sự cô lập container.

Vì vậy, trong phần này, bạn sẽ làm quen với mạng cơ bản trong Docker và bạn sẽ bắt tay vào thực hiện với một dự án nhỏ có nhiều container.

Bạn đã biết trong phần trước rằng container là môi trường cô lập. Bây giờ hãy xem xét một tình huống trong đó bạn có một ứng dụng notes-api được cung cấp bởi Express.js và một máy chủ cơ sở dữ liệu PostgreSQL đang chạy trong hai container riêng biệt.

Hai container này hoàn toàn cách biệt với nhau và không để ý đến sự tồn tại của nhau. Vì vậy, làm thế nào để bạn kết nối hai container? Đó sẽ không phải là một thách thức?

Bạn có thể nghĩ đến hai giải pháp khả thi cho vấn đề này. Chúng như sau:

  • Truy cập máy chủ cơ sở dữ liệu bằng một cổng.
  • Truy cập máy chủ cơ sở dữ liệu bằng địa chỉ IP và cổng mặc định của nó.

Đầu tiên liên quan đến việc đưa ra một cổng từ container postgresnotes-api sẽ kết nối thông qua đó. Giả sử rằng cổng từ container postgres là 5432. Bây giờ nếu bạn cố gắng kết nối với 127.0.0.1:5432 từ bên trong container notes-api, bạn sẽ thấy rằng notes-api không thể tìm thấy máy chủ cơ sở dữ liệu.

Lý do là khi bạn đang nói 127.0.0.1 bên trong container notes-api, bạn chỉ đơn giản là đang đề cập đến localhost của container đó và chỉ container đó mà thôi. Máy chủ postgres chỉ đơn giản là không tồn tại ở đó. Kết quả là ứng dụng notes-api không kết nối được.

Giải pháp thứ hai mà bạn có thể nghĩ đến là tìm địa chỉ IP chính xác của container postgres bằng lệnh container inspect và sử dụng địa chỉ đó với cổng. Giả sử tên của container postgresnotes-api-db-server, bạn có thể dễ dàng lấy địa chỉ IP bằng cách thực hiện lệnh sau:

docker container inspect --format='{{range .NetworkSettings.Networks}} {{.IPAddress}} {{end}}' notes-api-db-server

#  172.17.0.2

Bây giờ với cổng mặc định postgres5432, bạn có thể rất dễ dàng truy cập máy chủ cơ sở dữ liệu bằng cách kết nối 172.17.0.2:5432 từ container notes-api.

Có nhiều vấn đề trong cách tiếp cận này. Không nên sử dụng địa chỉ IP để tham chiếu đến container. Ngoài ra, nếu container bị hủy và được tạo lại, địa chỉ IP có thể thay đổi. Theo dõi các địa chỉ IP thay đổi này có thể khá bất tiện.

Bây giờ tôi đã loại bỏ các câu trả lời sai có thể xảy ra cho câu hỏi ban đầu, câu trả lời đúng là bạn kết nối chúng bằng cách đặt chúng dưới một mạng cầu nối do người dùng xác định.

Kiến thức cơ bản về Docker Network

Network (mạng) trong Docker cũng là một đối tượng logic giống như một container và image. Cũng giống như hai lệnh kia, có rất nhiều lệnh trong docker network để điều khiển mạng.

Để liệt kê các mạng trong hệ thống của bạn, hãy thực hiện lệnh sau:

docker network ls

# NETWORK ID     NAME      DRIVER    SCOPE
# c2e59f2b96bd   bridge    bridge    local
# 124dccee067f   host      host      local
# 506e3822bf1f   none      null      local

Bạn sẽ thấy ba mạng trong hệ thống của mình. Bây giờ nhìn vào cột DRIVER. Các trình điều khiển này có thể được coi là loại mạng.

Theo mặc định, Docker có năm trình điều khiển mạng. Chúng như sau:

  • bridge – Trình điều khiển mạng mặc định trong Docker. Nó có thể được sử dụng khi nhiều container đang chạy ở chế độ tiêu chuẩn và cần giao tiếp với nhau.
  • host – Loại bỏ hoàn toàn cách ly mạng. Bất kỳ container nào chạy trong một mạng host về cơ bản đều được gắn vào mạng của hệ thống máy chủ.
  • none – Trình điều khiển này vô hiệu hóa hoàn toàn mạng cho các container. Tôi chưa tìm thấy bất kỳ trường hợp sử dụng nào cho loại mạng này.
  • overlay – Trình điều khiển này được sử dụng để kết nối nhiều Docker daemon trên các máy tính và nằm ngoài phạm vi của hướng dẫn này.
  • macvlan – Cho phép gán địa chỉ MAC cho các container, làm cho chúng hoạt động giống như các thiết bị vật lý trong mạng.

Ngoài ra còn có các plugin của bên thứ ba cho phép bạn tích hợp Docker với các ngăn xếp mạng chuyên biệt. Trong số năm loại được đề cập ở trên, bạn sẽ chỉ làm việc với trình điều khiển mạng bridge trong hướng dẫn này.

Cách tạo cầu nối do người dùng xác định trong Docker

Trước khi bạn bắt đầu tạo cầu nối của riêng mình, tôi muốn dành chút thời gian để thảo luận về mạng cầu nối mặc định đi kèm với Docker. Hãy bắt đầu bằng cách liệt kê tất cả các mạng trên hệ thống của bạn:

docker network ls

# NETWORK ID     NAME      DRIVER    SCOPE
# c2e59f2b96bd   bridge    bridge    local
# 124dccee067f   host      host      local
# 506e3822bf1f   none      null      local

Như bạn có thể thấy, Docker đi kèm với một mạng cầu nối mặc định có tên bridge. Bất kỳ container nào bạn chạy sẽ được tự động gắn vào mạng cầu nối này:

docker container run --rm --detach --name hello-dock --publish 8080:80 fhsinchy/hello-dock
# a37f723dad3ae793ce40f97eb6bb236761baa92d72a2c27c24fc7fda0756657d

docker network inspect --format='{{range .Containers}}{{.Name}}{{end}}' bridge
# hello-dock

Các container được gắn với mạng cầu nối mặc định có thể giao tiếp với nhau bằng địa chỉ IP mà tôi đã không khuyến khích trong phần trước.

Tuy nhiên, cầu do người dùng xác định có một số tính năng bổ sung so với cầu mặc định. Theo các tài liệu chính thức về chủ đề này, một số tính năng bổ sung đáng chú ý như sau:

  • Cầu nối do người dùng xác định cung cấp khả năng phân giải DNS tự động giữa các container: Điều này có nghĩa là các container được gắn vào cùng một mạng có thể giao tiếp với nhau bằng cách sử dụng tên container. Vì vậy, nếu bạn có hai container được đặt tên notes-apinotes-db, container API có thể kết nối với container cơ sở dữ liệu bằng cách sử dụng tên notes-db.
  • Các cầu nối do người dùng xác định cung cấp khả năng cách ly tốt hơn: Tất cả các container được gắn vào mạng cầu nối mặc định theo mặc định, điều này có thể gây ra xung đột giữa chúng. Việc gắn các container vào một cầu nối do người dùng xác định có thể đảm bảo cách ly tốt hơn.
  • Các container có thể được gắn vào và tách ra khỏi các mạng do người dùng xác định khi đang di chuyển: Trong thời gian tồn tại của container, bạn có thể kết nối hoặc ngắt kết nối nó khỏi các mạng do người dùng xác định một cách nhanh chóng. Để xóa container khỏi mạng cầu nối mặc định, bạn cần dừng container và tạo lại container đó với các tùy chọn mạng khác nhau.

Bây giờ bạn đã học được khá nhiều về mạng do người dùng xác định, đã đến lúc tạo một mạng cho chính bạn. Một mạng có thể được tạo bằng lệnh network create. Cú pháp chung cho lệnh như sau:

docker network create <network name>

Để tạo một mạng có tên skynet, hãy thực hiện lệnh sau:

docker network create skynet

# 7bd5f351aa892ac6ec15fed8619fc3bbb95a7dcdd58980c28304627c8f7eb070

docker network ls

# NETWORK ID     NAME     DRIVER    SCOPE
# be0cab667c4b   bridge   bridge    local
# 124dccee067f   host     host      local
# 506e3822bf1f   none     null      local
# 7bd5f351aa89   skynet   bridge    local

Như bạn có thể thấy một mạng mới đã được tạo với tên đã cho. Hiện không có container nào được gắn vào mạng này. Trong phần tiếp theo, bạn sẽ tìm hiểu về cách gắn các container vào mạng.

Cách gắn container vào mạng trong Docker

Có hai cách để gắn một container vào mạng. Đầu tiên, bạn có thể sử dụng lệnh kết nối mạng để gắn một container vào mạng. Cú pháp chung cho lệnh như sau:

docker network connect <network identifier> <container identifier>

Để kết nối container hello-dock với mạng skynet, bạn có thể thực hiện lệnh sau:

docker network connect skynet hello-dock

docker network inspect --format='{{range .Containers}} {{.Name}} {{end}}' skynet

#  hello-dock

docker network inspect --format='{{range .Containers}} {{.Name}} {{end}}' bridge

#  hello-dock

Như bạn có thể thấy từ đầu ra của hai lệnh network inspect, container hello-dock hiện được gắn vào cả mạng skynet và mạng bridge mặc định .

Cách thứ hai để gắn container vào mạng là sử dụng tùy chọn --network cho các lệnh container run hoặc container create. Cú pháp chung cho tùy chọn như sau:

--network <network identifier>

Để chạy một container hello-dock khác được gắn vào cùng một mạng, bạn có thể thực hiện lệnh sau:

docker container run --network skynet --rm --name alpine-box -it alpine sh

# lands you into alpine linux shell

/ # ping hello-dock

# PING hello-dock (172.18.0.2): 56 data bytes
# 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.191 ms
# 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.103 ms
# 64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.139 ms
# 64 bytes from 172.18.0.2: seq=3 ttl=64 time=0.142 ms
# 64 bytes from 172.18.0.2: seq=4 ttl=64 time=0.146 ms
# 64 bytes from 172.18.0.2: seq=5 ttl=64 time=0.095 ms
# 64 bytes from 172.18.0.2: seq=6 ttl=64 time=0.181 ms
# 64 bytes from 172.18.0.2: seq=7 ttl=64 time=0.138 ms
# 64 bytes from 172.18.0.2: seq=8 ttl=64 time=0.158 ms
# 64 bytes from 172.18.0.2: seq=9 ttl=64 time=0.137 ms
# 64 bytes from 172.18.0.2: seq=10 ttl=64 time=0.145 ms
# 64 bytes from 172.18.0.2: seq=11 ttl=64 time=0.138 ms
# 64 bytes from 172.18.0.2: seq=12 ttl=64 time=0.085 ms

--- hello-dock ping statistics ---
13 packets transmitted, 13 packets received, 0% packet loss
round-trip min/avg/max = 0.085/0.138/0.191 ms

Như bạn có thể thấy, chạy ping hello-dock từ bên trong container alpine-box hoạt động vì cả hai container đều nằm trong cùng một mạng cầu nối do người dùng xác định và phân giải DNS tự động đang hoạt động.

Tuy nhiên, hãy nhớ rằng để phân giải DNS tự động hoạt động, bạn phải gán tên tùy chỉnh cho các container. Sử dụng tên được tạo ngẫu nhiên sẽ không hoạt động.

Cách tách container khỏi mạng trong Docker

Trong phần trước, bạn đã học về cách gắn container vào mạng. Trong phần này, bạn sẽ tìm hiểu về cách tách chúng ra.

Bạn có thể sử dụng lệnh network disconnect cho tác vụ này. Cú pháp chung cho lệnh như sau:

docker network disconnect <network identifier> <container identifier>

Để tách container hello-dock khỏi mạng skynet, bạn có thể thực hiện lệnh sau:

docker network disconnect skynet hello-dock

Cũng giống như lệnh network connect, lệnh network disconnect không đưa ra bất kỳ đầu ra nào.

Cách xóa mạng trong Docker

Cũng giống như các đối tượng logic khác trong Docker, các mạng có thể được xóa bằng lệnh network rm. Cú pháp chung cho lệnh như sau:

docker network rm <network identifier>

Để xóa mạng skynet khỏi hệ thống của bạn, bạn có thể thực hiện lệnh sau:

docker network rm skynet

Bạn cũng có thể sử dụng lệnh network prune để xóa bất kỳ mạng nào không sử dụng khỏi hệ thống của mình. Lệnh cũng có tùy chọn -f hay --force-a hay --all.

Trong hướng dẫn tiếp theo, bạn sẽ học cách chạy ứng dụng trên nhiều container trong Docker.

Sổ tay Docker: Cách chạy ứng dụng JavaScript trên nhiều Container
Trong hướng dẫn này, bạn sẽ học cách triển khai ứng dụng trên nhiều container trong Docker.

Bài viết này được dịch từ cuốn sách The Docker Handbook của Farhan Hasin Chowdhury:

The Docker Handbook – 2021 Edition
The concept of containerization itself is pretty old. But the emergence of the Docker Engine [https://docs.docker.com/get-started/overview/#docker-engine] in2013 has made it much easier to containerize your applications. According to the Stack Overflow Developer Survey – 2020[https://insights.stackoverflow.com/survey/2020#overview…

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *