Series về Docker trong thực thành & sản xuất - Network trong Docker

Series về Docker trong thực thành & sản xuất - Network trong Docker

Vấn đề

Một trong những lý do khiến docker trở nên mạnh mẽ là hệ thống network hỗ trợ kết nối các container với nhau rất tốt, thậm chí chúng còn không quan tâm đến liệu có đang chạy trong môi trường của docker hay không, hay nền tảng máy chủ là gì.

Hiểu nôm na tức là một container có thể kết nối nhau hay đến một ứng dụng đang chạy ngoài docker mà không cần biết là docker for Linux, Windows hay MacOS.

Để tìm hiểu chi tiết hơn, mời các bạn theo dõi tiếp bài viết dưới đây nhé!

Network drivers

Docker cung cấp một số network drivers tuỳ theo mục đích sử dụng khác nhau:

  • bridge: đây là driver mặc định khi tạo một network nếu bạn không chỉ định một driver nào. drive này cho phép các container có thể giao tiếp với nhau nếu chúng đều nằm trong mạng này.
  • host: driver này cho phép các container sử dụng trực tiếp network của máy chủ docker. Tức là container sẽ không dùng network do docker cung cấp nữa mà dùng luôn network của máy chủ.
  • overlay: kết nối nhiều máy chủ docker với nhau thông qua chế độ swarm, các máy chủ docker có thể chạy trên bất kì nền tảng nào nhưng các container bên trong đó đều có thể giao tiếp với nhau.
  • none: đúng như tên gọi driver này đưa container của bạn về thời chưa có internet.

Ngoài ra còn có macvlan và các plugin của bên thứ ba mà trong phạm vi bài viết này tôi sẽ không nói đến. Bạn đọc quan tâm có thể tìm hiểu thêm trên trang chủ của docker.

Tạo một network

Docker cung cấp các lệnh để chúng ta có thể dễ dàng tạo một network.

$ docker network create -d bridge estacks-network --attachable

Tôi vừa tạo một network có tên là estacks-network với driver bridge. Cờ -d cho phép chỉ định các loại driver mà docker hỗ trợ. --attachable cho phép các container có thể kết nối vào network theo cách thủ công (dùng lệnh).

Để tạo mới một container và chỉ định nó kết nối vào network:

$ docker run --network estacks-network --name nginx -d nginx

Để kết nối một container có sẵn từ trước vào network:

$ docker network connect estacks-network mysql

Trong đó mysql là tên của container.

Có rất là nhiều lệnh khác mà mình không thể liệt kê hết ở đây được, các bạn có thể tham khảo thêm ở trang docker network để biết thêm chi tiết và cách sử dụng của chúng.

Cách sử dụng các driver trong mỗi trường hợp

Có một điều chắc chắn rằng các container muốn giao tiếp được với nhau thì chúng phải nằm trong cùng một mạng. Giao tiếp ở đây tức là chúng có khả năng trao đổi dữ liệu với nhau. Vì thế khi triển khai ứng dụng, nếu gặp vấn đề về kết nối thì bạn nên kiểm tra các vấn đề về network có trục trặc gì không.

Có một số cách để triển khai các container và giúp chúng có thể giao tiếp với nhau. Tôi sẽ gợi ý cho bạn một số cách mà tôi hay dùng dưới đây.

Triển khai các container độc lập

Ví dụ bạn chỉ đơn giản là muốn chạy một container mysql để làm cơ sở dữ liệu cho môi trường phát triển thì bạn chỉ cần dùng lệnh docker run:

$ docker run --name mysql -d mysql

Khi không chỉ định network container sẽ mặc định chạy ở bridge, nó được cấp một địa chỉ ip cố định ở bên trong network đó. Bất kì container nào chạy trong bridge sẽ gọi được đến ip của nhau:

$ docker inspect mysql
{
  ...
  "NetworkSettings": {
    ...
    "Networks": {
      "bridge": {
        "IPAMConfig": null,
        "Links": null,
        "Aliases": null,
        "NetworkID": "22a978aa722c09d9636cfcc4e6dedb4c788cf839421201a00a004596e812f0e1",
        "EndpointID": "a72743fdd788e759ed3358d3dd043122be08d5f5dcc8cd20144e55d7161f433c",
        "Gateway": "172.17.0.1",
        "IPAddress": "172.17.0.3",
        "IPPrefixLen": 16,
        "IPv6Gateway": "",
        "GlobalIPv6Address": "",
        "GlobalIPv6PrefixLen": 0,
        "MacAddress": "02:42:ac:11:00:03",
        "DriverOpts": null
      }
    }
  }
}

Mẹo: Sử dụng lệnh docker inspect để xem thông tin chi tiết của một container.

Như trên bạn có thể thấy mysql đang có địa chỉ ip là 172.17.0.3. Bây giờ, giả sử tôi sẽ khởi tạo thêm một container nginx cũng sử dụng bridge, truy cập vào trong container đó để thử ping đến địa chỉ 172.17.0.3 xem có nhận được gì không nhé:

$ docker run -d --name nginx nginx
$ docker exec -it nginx bash
root@02d4d43a19fd:/# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.035 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.035 ms
^C
--- 172.17.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 115ms
rtt min/avg/max/mdev = 0.035/0.038/0.045/0.006 ms

Các bạn thấy đấy, do chúng cùng nằm trong mạng bridge nên nếu biết địa chỉ ip thì có thể giao tiếp được với nhau rồi.

Tuy nhiên các container như trên chỉ có thể gọi nhau nếu như chúng cùng nằm trong một mạng, nếu như tôi sử dụng termial thử ping đến mysql thì sẽ không nhận được bất kì phản nào nào. Tại sao vậy?

Bởi vì termial của tôi không được kết nối vào mạng bridge của docker, để làm được điều đó, docker cung cấp một cơ chế mà các bạn có thể đã biết rồi đó là expose port của mysql container ra ngoài máy chủ. Hiểu đơn giản đó là hành động mapping port của mysql ra một port khác trong máy chủ rồi từ port đó có thể kết nối đến container.

$ docker run -d --name mysql2 -p 3306:3307 mysql
$ telnet localhost 3307

Cờ -p 3306:3307 tức là expose cổng 3306 của container ra cổng 3307 của máy chủ, 3307 sẽ là cổng để giao tiếp với mysql trong container. Lúc này tôi có thể sử dụng máy chủ mysql thông qua localhost:3307.

Tôi muốn container dùng luông mạng của máy chủ docker?

Thay vì sử dụng cờ -p để expose từng port có trong docker, tôi có thể chạy container ở host. Khi chỉ định container chạy ở host nó sẽ không bị ràng buộc bởi mạng của docker nữa. Ví dụ:

$ docker run --network host --name mysql -d mysql

Ở terminal tôi thử telnet đến cổng 3306:

$ telnet localhost 3306
Trying ::1...
Connected to localhost.
Escape character is '^]'.

Docker for Windows hay MacOS bản chất là chạy trong một máy ảo Linux, vì thế khi sử dụng host thì network được docker sử dụng ở đây là network của máy ảo. Vì thế giả sử khi bạn dùng Windows mà muốn telnet đến cổng 3306 của container trên mà sử dụng localhost sẽ không được:

$ telnet localhost 3306
Trying ::1...
telnet: connect to address ::1: Connection refused

Thay vì localhost, bạn có thể thử sử dụng địa chỉ IP của máy ảo đang chạy docker.

Container trong docker-compose thì sao?

Tôi đã có một bài viết về Docker compose để khởi động nhiều services.

Hãy để ý trong file docker-compose.yml, mỗi service muốn có thể giao tiếp được với nhau thì chúng phải cần nằm trong networks, networks này là không giới hạn và bạn có thể kết nối các services với nhau tuỳ ý, chỉ cần nhớ một nguyên tắc "nằm trong cùng network" là được.

Networks được khai báo ở trong docker-compose.yml sẽ tồn tại ở trong stacks đó, tức là khi bạn start compose nó sẽ được tạo còn khi down compose thì nó sẽ bị xoá.
Bạn cũng có thể tạo một network sau đó join các services vào network đó.

$ docker network create my-network -d bridge --attachable

Cờ -d bridge chỉ định tạo network với driver bridge, --attachable cho phép các container có thể join vào thủ công. Xem thêm lệnh docker create tại trang của docker.

Trong docker-compose.yml.

version: "3.9"
services:
  mysql:
    ...
    networks:
      - my-network
    ...
  ...

networks:
  my-network:
    external: true

Cuối cùng, overlay có điều gì khác biệt?

Khi docker được triển khai ở chế độ swarm, nỗi node có thể sẽ có địa chỉ ip khác nhau, để làm giảm thiểu sự phức tạp trong giao tiếp giữa các container trên các node thì docker cung cấp overlay driver này.

Hiểu đơn giản là các container trên các node nếu cùng nằm trong một mạng overlay thì chúng có thể giao tiếp được với nhau.

Nếu các bạn chưa biết về docker swarm thì đừng lo, tôi đã lên kế hoạch cho bài viết tiếp theo trong series này.

Tổng kết

Network trong docker giúp cho việc giao tiếp giữa các docker dễ dàng hơn bao giờ hết vì bản chất của các container là độc lập với nhau. Ngoài ra docker cũng cung cấp khá là nhiều tuỳ chọn network driver tuỳ theo nhu cầu sử dụng của mỗi người trong đó có thể kể đến như bridge, host, overlay…

Có một nguyên tắc cần phải nhớ trong triển khai và gỡ lỗi đó là các container muốn giao tiếp được với nhau thì chúng phải cùng nằm trong một network.

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

Bình luận (0)