Khái niệm Network Bridge trong Docker và cách kết nối các container cùng phục vụ
- 30-08-2024
- Toanngo92
- 0 Comments
Mục lục
Network trong Docker, liên kết mạng các container
Docker cho phép bạn tạo ra một network (giao tiếp mạng), sau đó các container khác nhau cùng kết nối vào network để phục vụ. Khi các container cùng nằm trong một network thì chúng có thể giao tiếp với nhau nhanh chóng qua tên của container và cổng (port) được lắng nghe của container trên mạng đó.
Cú pháp liệt kê các container đang có sẵn (cả đang chạy và đang tắt)
docker network ls
Các network được tạo ra theo một driver nào đó như bridge, none, overlay, macvlan, trong đó:
Trong Docker, các loại mạng như bridge
, none
, overlay
, và macvlan
đại diện cho các kiểu mạng khác nhau mà bạn có thể tạo và quản lý để kết nối các container. Dưới đây là mô tả chi tiết cho từng loại:
1. Bridge Network
- Bridge là kiểu mạng mặc định mà Docker tạo ra khi bạn khởi động một container nếu bạn không chỉ định loại mạng nào khác.
- Các container trên cùng một bridge network có thể giao tiếp với nhau qua địa chỉ IP.
- Bridge hoạt động trên máy chủ cục bộ, nghĩa là các container kết nối với cùng một bridge network có thể giao tiếp với nhau, nhưng không thể giao tiếp với các container trên các máy chủ khác.
- Đây là loại mạng thường được sử dụng khi bạn có một vài container cần giao tiếp với nhau nhưng không cần phải tiếp xúc trực tiếp với bên ngoài.
2. None Network
- None là một loại mạng đặc biệt mà khi bạn khởi tạo một container với nó, Docker sẽ không gán bất kỳ giao diện mạng nào cho container đó.
- Container này hoàn toàn bị cô lập về mặt mạng, bạn sẽ cần tự cấu hình kết nối mạng nếu cần thiết.
- Loại mạng này hữu ích khi bạn muốn kiểm soát hoàn toàn cấu hình mạng của container.
3. Overlay Network
- Overlay là một mạng ảo hoạt động trên nhiều máy chủ Docker (nodes) trong một cụm Docker Swarm hoặc Kubernetes.
- Nó cho phép các container trên các máy chủ khác nhau có thể giao tiếp với nhau như thể chúng nằm trong cùng một mạng cục bộ.
- Overlay network thường được sử dụng khi bạn cần triển khai ứng dụng phân tán mà các container chạy trên nhiều máy chủ khác nhau.
4. Macvlan Network
- Macvlan là loại mạng cho phép bạn gán một địa chỉ MAC cho mỗi container, biến mỗi container thành một thiết bị mạng trực tiếp trên mạng vật lý.
- Mỗi container có địa chỉ IP của riêng nó và xuất hiện trên mạng vật lý như một thiết bị vật lý thực sự.
- Macvlan thích hợp cho các ứng dụng cần có địa chỉ IP cố định và cần giao tiếp trực tiếp với các thiết bị trên mạng vật lý.
Mỗi loại mạng này có mục đích và ứng dụng riêng tùy thuộc vào nhu cầu cụ thể của ứng dụng và cơ sở hạ tầng mà bạn đang triển khai. Trong nội dung bài viết này, chúng ta demo bridge network để giao tiếp giữa các container.
Khởi tạo một bridge network có tên là hvc-network
docker network create --driver bridge hvc-network
Khi tạo một container, chỉ định cho container nối vào network có tên là my-network thì thêm vào tham số –network my-network trong lệnh docker run. Khi một container có tên my-container đã được tạo, để thiết lập nó nối vào network có tên my-network.
docker network connect my-network my-container
Các kết nối mạng thông qua các cổng, để thiết lập cấu hình các cổng của container cần lưu ý: có cổng bên trong container, có cổng mở ra bên ngoài (public), có giao thức của cổng (tpc, udp). Khi chạy container (tạo) cần thiết lập cổng thì đưa vào lệnh docker run tham số với cú pháp như sau:
docker run -p public-port:target-port/protocol ...
Trong đó
public-port
: cổng public ra ngoài (ví dụ80
,8080
…), các kết nối không cùng network đến container phải thông qua cổng này.target-port
: cổng bên trong container, cổng public-port sẽ ánh xạ vào cổng này. Nếu các container cùng network có thể kết nối với nhau thông qua cổng này.
Thực hành cài đặt Network trong Docker
Dưới đây, chúng ta sẽ tạo một network với 3 container chạy các dịch vụ khác nhau cùng phục vụ là PHP, APACHE, MARIADB
Bước 1: Tạo container chạy PHP
Trong tình huống này, mình lựa chọn cài đặt php:8.2-fpm, mặc định cho phép gọi tới php thông qua cổng 9000.
Ví dụ:
docker run -d --name container-php -h php -v "/path/to/folder/php":/home/php php:8.2-fpm
Trong đó:
-d
: container sau khi tạo chạy luôn ở chế độ nền.--name c-php
: container tạo ra và đặt luôn cho nó tên làc-php
(Tương tác với container dễ đọc hơn khi sử dụng tên thay vì phải sử dụng mã hash id, nếu không đặt tên thì docker sinh ra tên ngẫu nhiên).-h php
đặt tên HOSTNAME của container làphp
-v "/path/to/folder/php":/home/php
thư mục trên máy host
(với Windows đường dẫn theo OS này như/path/to/folder/php
"d:\myapp\php"
) được gắn vào container ở đường dẫn/home/php
. (Thông thường, thư mục này sẽ là thư mục lưu trữ mã nguồn)php:8.2-fpm
là image khởi tạo ra container, nếu image này chưa có, nó sẽ được tự động tải về.
Sau lệnh này, một container đã tạo container có tên đặt là c-php
từ image php:8.2-fpm
. Kiểm tra lại danh sách container trong docker với lệnh:
docker ps -a
Như hình trên, một container có tên container-php
được tạo ra, chạy với lệnh script mặc định docker-php-entrypoint
, dịch vụ PHP-FPM
chạy và lắng nghe ở cổng 9000
. Script mặc định này mô tả trong một file Dockerfile
, loại file này sẽ tìm hiểu sau. Ở đây bạn có thể xem thông tin file này kèm trong image bằng cú pháp:
docker inspect image_name_or_id
Ví dụ:
docker inspect php:8.2-fpm
Ví dụ Output:
[
{
"Id": "sha256:6b1a35f72eede59dee0e276642a15caadd6793f6e91ad8409fcff4670455ac10",
"RepoTags": [
"php:8.2-fpm"
],
"RepoDigests": [
"php@sha256:9de16d11baa9141d0f7893cfc16de6aac34826b0df11f6dcb5e17590e4edc3b7"
],
"Parent": "",
"Comment": "",
"Created": "2024-08-13T02:06:35.090697732Z",
"DockerVersion": "23.0.11",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"9000/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PHPIZE_DEPS=autoconf \t\tdpkg-dev \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkg-config \t\tre2c",
"PHP_INI_DIR=/usr/local/etc/php",
"PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64",
"PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64",
"PHP_LDFLAGS=-Wl,-O1 -pie",
"GPG_KEYS=39B641343D8C104B2B146DC3F9C39DC0B9698544 E60913E4DF209907D8E30D96659A97C9CF2A795A 1198C0117593497A5EC5C199286AF1F9897469DC",
"PHP_VERSION=8.2.22",
"PHP_URL=https://www.php.net/distributions/php-8.2.22.tar.xz",
"PHP_ASC_URL=https://www.php.net/distributions/php-8.2.22.tar.xz.asc",
"PHP_SHA256=8566229bc88ad1f4aadc10700ab5fbcec81587c748999d985f11cf3b745462df"
],
"Cmd": [
"php-fpm"
],
"Image": "sha256:fede637165c15a2d9b9b23e59bd7415d29a309fb32231f84b4798025ccc4834f",
"Volumes": null,
"WorkingDir": "/var/www/html",
"Entrypoint": [
"docker-php-entrypoint"
],
"OnBuild": null,
"Labels": null,
"StopSignal": "SIGQUIT"
},
"Architecture": "amd64",
"Os": "linux",
"Size": 494197848,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/93c09c4b1a5fe05edf1f2588c83b7b3df2a1e984c4915306cc6833426645d372/diff:/var/lib/docker/overlay2/42935b1cb343677f195b4e53063bf13eeb66125bd8595f473fc09bca3c948e07/diff:/var/lib/docker/overlay2/9a02a632ef24bc82cd4b95cd1c359f2e5037e39fb39def5d1327ce83db48f4a3/diff:/var/lib/docker/overlay2/f2477b637e29519b4819e56cf26b0859f10bfb36f7dca1136084de3347d4d17e/diff:/var/lib/docker/overlay2/f2ea86174a122663a13ae9115eb27a369784219391d188480d14159b28c438bc/diff:/var/lib/docker/overlay2/74357a1af2072eb940e87c502acc5a061f83292ad4756d0afea5b360b49f9ef1/diff:/var/lib/docker/overlay2/0eda781ed227b0e511486609ab120794af043baa2083f94d87cb69806feab752/diff:/var/lib/docker/overlay2/082dca6b654df48bdf92e5ca0bb58246ac57347b370cfea9c6722bd1b611c0de/diff:/var/lib/docker/overlay2/8268df7066eda1a894e0c1b0d1fcb81cfd1f1d12f3d586616f1a037322b6c42d/diff",
"MergedDir": "/var/lib/docker/overlay2/b390a8f78a4371c0725ef9ae1cd089915092305611b3b948e578239e1a8a9475/merged",
"UpperDir": "/var/lib/docker/overlay2/b390a8f78a4371c0725ef9ae1cd089915092305611b3b948e578239e1a8a9475/diff",
"WorkDir": "/var/lib/docker/overlay2/b390a8f78a4371c0725ef9ae1cd089915092305611b3b948e578239e1a8a9475/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034",
"sha256:4cae4ea97049ffe77607650a058d069bcfd37d79c2d0b8907ec937bc31f53af6",
"sha256:7f0d23b78477cf960f0e870890d4e7f57107b2c21120832cb8a3ff59a84eb7cc",
"sha256:8f42af1dd50e140a19f623041d48ce4937c89d5661ba6ee5adfae38d300c883a",
"sha256:8fcaac4cd769f5612962e71cb7da6554284e56037cf1f07a5958853edc400035",
"sha256:488cab6e3311f5e331d611d37caaf24dc38b0434f7c8b6da8b1b35d319f56496",
"sha256:b388f30331d33d120d4513313b70e6af841d5c06d901576227f9001d967854ee",
"sha256:b21fa37becf3eb98bb9c641f3a72f805ac75f2dd69abab90386c5c63486e6ab9",
"sha256:b524bf39853bc387f702276c28b779d0d60af18584d339abc7313792907cce80",
"sha256:b1faa2ae7f5cae84971010de1384986a34fb50ad5a167cb2c30eba8536e80f0f"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
Entrypoint
quy định các lệnh chạy mặc định khi tạo – chạy container, nên bạn thấy script docker-php-entrypoint
được chạy.
Lưu ý: trong một số image, Entrypoint chạy script kết thúc ngay dẫn đến container kết thúc ngay, kể cả khi khởi chạy lại container bằng
docker start
thì nó cũng kết thúc luôn. Những loại image như vậy, cần tiến trình hoạt động dài để giữ cho container sống. Để khắc phục, thêm vào nó tham số-i
ở lệnhdocker run
Tương tác với container
Để truy cập vào container có tên container-php vừa tạo và tương tác, thao tác như sau:
docker exec -it container-php bash
Kiểm tra php version trong container:
Test thử php chạy trên môi trường xem có ổn định không:
echo "<?php echo 'Hello World';" > helloworld.php
Chạy thử tệp:
php ./helloworld.php
Truy cập thư mục map với host, bạn có thể sửa tệp tại host, container sẽ được tự động cập nhật theo.
Ngoài ra, để cài thêm các extension trong PHP, chúng ta có thể cần cài thêm một số extension như docker-php-ext-configure
và docker-php-ext-install
để bạn cấu hình, cài các Extension cho PHP.
Ví dụ cài opcache:
docker-php-ext-configure opcache --enable-opcache \
&& docker-php-ext-install opcache
Ví dụ cài mysqli:
docker-php-ext-configure opcache --enable-mysqli \
&& docker-php-ext-install mysqli
Ví dụ cài PDO:
docker-php-ext-configure opcache --enable-pdo_mysql \
&& docker-php-ext-install pdo_mysql
Cấu hình php.ini:
cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
vi /usr/local/etc/php/php.ini
Sau khi cài đặt thành công, khởi động lại php:
docker restart container-php
Bước 2: Tạo docker chạy Apache HTTPD
Tải image httpd:
docker pull httpd
Tạo container httpd đặt tên là container-httpd
docker run -di -p 8080:80 -p 443:443 --name c-httpd -h httpd -v "/path/to/folder/php":/home/php httpd
Sau khi truy cập trình duyệt sẽ trả ra giao diện:
Cấu hình lại tệp config httpd.conf
- File config tại:
/usr/local/apache2/conf/httpd.conf
- Nếu muốn cài trình soạn thảo nano gõ lệnh
apt-get update & apt-get install nano
Vậy để soạn thảo config bạn gõ
vi /usr/local/apache2/conf/httpd.conf
Thêm cấu hình sau vào httpd.conf để thiết lập php-handler (trình xử lý php cho httpd), trong đó các bạn để ý tên proxy là container-php (tên container):
AddHandler "proxy:fcgi://container-php:9000" .php
Do Handler này gọi PHP thông qua proxy, nên Apache phải kích hoạt module liên quan đến proxy, mở file httpd.conf
và bỏ commnet trên các dòng:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
Khởi động lại apache để các cấu hình được cập nhật:
apachectl -k restart
Liên kết giữa Apache HTTPD và PHP-FPM
Để hai container container-php
và container-httpd
giao tiếp được với nhau (ví dụ từ httpd với proxy:fcgi://c-php:9000), thì ta thiết lập chúng cùng một mạng.
Liệt kê danh sách các network trong docker:
docker network ls
Tạo một bridget tên www_bridge
docker network create --driver bridge www_bridge
Cấy hình container container-php
và container-httpd
nối vào mạng này.
docker network connect www_bridge container-php
docker network connect www_bridge container-httpd
Kiểm tra thông tin network:
docker network inspect www_bridge
Output:
[
{
"Name": "www_bridge",
"Id": "31f4e3a9f9a7ff4b2a8947b0198e5d8a2a0f4a3df8387419c614f9d01ffb2fb2",
"Created": "2024-08-30T13:54:19.382240527Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"8d80f1928d9ea0eb61e346be53c2582a3884ac8460ed045a645ff4fde598eff4": {
"Name": "container-php",
"EndpointID": "f1f24994812d61a39e79574a23e5ac98a966af839a7d3ed31bf0d53cab3b49df",
"MacAddress": "02:42:ac:13:00:02",
"IPv4Address": "172.19.0.2/16",
"IPv6Address": ""
},
"ec17d4c0d238cf46467d2a26b2318f34af3a093978b58e93dfdbba889ff6404f": {
"Name": "container-httpd",
"EndpointID": "f8d4d365e9e8094e71c9f30f6aa184efaf2071d95f16eabd0dd8844d23b3b016",
"MacAddress": "02:42:ac:13:00:03",
"IPv4Address": "172.19.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
Sau cấu hình này thì Apache đã giao tiếp được với PHP, chúng ta sẽ tiếp tục cấu hình và chạy một file php. Trước tiên, mở httpd.conf
và chỉnh sửa thư mục gốc mặc định nằm ở /home/php
. Tìm đến đoạn:
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
Thay nội dung thành:
DocumentRoot "/home/php"
<Directory "/home/php">
Bước tiếp theo, truy cập container php, tạo file /home/php/test.php
vi /home/php/test.php
Đưa nội dung vào tệp:
<php
phpinfo();
Chạy thử http://localhost:8080 để kiểm tra kết quả:
Bước 3: cài đặt MariaDB
Tạo, chạy container từ image mariadb (image này nếu chưa có ở local, tự tải từ repository về), đặt tên là container-mysql
docker run -it --network www_bridge --name container-mariadb -h mariadb -v "/path/to/code/db":/var/lib/mysql -e MYSQL_ROOT_PASSWORD=abc123 mariadb
Lệnh này sẽ tạo và chạy một container MariaDB với các chi tiết sau:
- Nó sử dụng mạng Docker
www_bridge
. - Container được đặt tên là
container-mariadb
. - Hostname bên trong container được đặt là
mariadb
. - Thư mục trên host
/path/to/db
được gắn vào/var/lib/mysql
bên trong container, đây là nơi MariaDB lưu trữ dữ liệu của nó. - Mật khẩu root cho MariaDB được đặt là
abc123
.
Sau lệnh này, MariaDB cùng mạng với PHP, APACHE và cổng mở là 3306
, file cấu hình ở /etc/mysql/my.cnf
, có thể thoát khỏi terminal đang chạy với tổ hợp phím CTRL + P, CTRL + Q
Tiếp theo, truy cập my.cnf để cấu hình:
docker exec -it c-mysql bash
apt-get update
apt-get install nano
nano /etc/mysql/my.cnf
Thêm nội dung vào tệp:
[mysqld]
default-authentication-plugin=mysql_native_password
Ra khỏi container và khởi động lại docker restart container-mariadb
, giờ thì đã có server MariaDB đang chạy, các ứng dụng cùng mạng muốn sử dụng có thể kết nối đến nó qua cổng 3306
Test tạo dabase với container:
docker exec -it c-mysql bash #truy cập vào container
mariadb -uroot -pabc123 #kết nối vào mariadb
#Từ dấu nhắc MySQL, Tạo một user tên testuser với password là testpass
CREATE USER 'testuser'@'%' IDENTIFIED BY 'testpass';
#Tạo db có tên db_testdb
create database db_testdb;
#Cấp quyền cho user testuser trên db - db_testdb
GRANT ALL PRIVILEGES ON db_testdb.* TO 'testuser'@'%';
flush privileges;
show database; #Xem các database đang có, kết quả có db bạn vừa tạo
exit; #Ra khỏi mariadb
Bài viết có tham khảo từ Mạng network bridge trong Docker kết nối các container với nhau (xuanthulab.net)