為 Docker 容器和 Docker-Compose 分配靜態 IP
一、概述
當我們運行 Docker 容器時,它使用 IP 地址連接到虛擬網絡。因此,我們希望服務能夠動態獲取配置。但是,我們可能希望使用靜態 IP 而不是自動 IP 分配。
在本教程中,我們將看到內置配置和為容器分配手動 IP 之間的區別。最後,我們將添加一些帶有測試的 Docker Compose 示例。
2.DHCP和DNS
讓我們看看使用 DHCP 和 DNS 為容器分配 Docker 內置 IP 以解析主機名。
2.1。 Docker 如何分配 IP
Docker 首先為每個容器分配一個IP ,充當 DHCP 服務器。此外,還有多個 DNS 服務器。
然後容器使用dockerd
內的服務器處理 DNS 請求,該服務器識別同一內部網絡上其他容器的名稱。這樣,容器可以在不知道其內部 IP 地址的情況下進行通信。儘管每次應用程序啟動時內部 IP 地址可能不同,但由於dockerd
內部的內部 DNS 服務器,容器仍然可以輕鬆地以人類可讀的名稱連接。
然後, dockerd
將名稱查找發送到CoreDNS (來自CNCF )。最後,請求會根據域名移動到主機。
域docker.internal
有一個側面案例。它包括解析為當前主機的有效 IP 地址的 DNS 名稱host.docker.internal
。它允許容器聯繫這些主機服務,而不必擔心硬編碼 IP 地址。雖然不推薦,但它可以方便地用於開發目的。
2.2.網絡示例
例如,我們可以為 MySQL 服務運行一個容器。讓我們看看 Docker Compose YAML 定義:
services:
db:
image: mysql:latest
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_ROOT_HOST=localhost
ports:
- 3306:3306
volumes:
- db:/var/lib/mysql
networks:
- network
volumes:
db:
driver: local
networks:
network:
driver: bridge
像往常一樣,我們運行我們的容器:
docker-compose up -d
讓我們從容器的角度檢查網絡,使用jq
format
語法來獲取 JSON 輸出:
docker inspect --format='{{json .NetworkSettings.Networks}}' 2d3f4c69a213 | jq .
Docker Compose 根據當前目錄分配網絡名稱。例如,如果我們在project
目錄中,我們可以看到類似的輸出:
{
"project_network": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"project-db-1",
"db",
"2d3f4c69a213"
],
"NetworkID": "39ffbd8155d11ba03d0b548307f549f06790fe045e121a6d862b070d4fb67fa7",
"EndpointID": "0eba235239b06f7e0cb5065b7f2ebd83e7d227f8cfad4df8de73260472737500",
"Gateway": "172.19.0.1",
"IPAddress": "172.19.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:13:00:02",
"DriverOpts": null
}
}
容器從網絡創建的子網中獲取私有172.19.0.2
IP 地址。
最重要的是,我們可以看到有關IPAMConfig
的信息,即 IP 地址管理。當我們靜態分配 IP 時,這將是相關的。
現在,我們可以檢查網絡:
docker inspect network project_network
這一次,我們對網絡有了更深入的了解:
[
{
"Name": "project_network",
"Id": "39ffbd8155d11ba03d0b548307f549f06790fe045e121a6d862b070d4fb67fa7",
"Created": "2022-09-09T16:19:26.27396468+02:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"2d3f4c69a2139dea9089a6d42907fdc085282c5df176b39bf7c20f5d0780179d": {
"Name": "project-db-1",
"EndpointID": "7447fe2550afb3f980f36449673724e9ed6dd16f41a085cc20ada3074a0d8e54",
"MacAddress": "02:42:ac:13:00:02",
"IPv4Address": "172.19.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "network",
"com.docker.compose.project": "project",
"com.docker.compose.version": "2.10.2"
}
}
]
值得注意的是,Docker Compose網絡從版本 2 開始就可用。
3.靜態IP
了解更多關於自動 IP 分配的知識後,我們現在將創建我們的網絡子網。然後我們可以為我們的服務分配我們喜歡的 IP。
3.1。分配靜態 IP
如果我們使用 Docker CLI,我們將通過首先創建子網來實現此結果:
docker network create --subnet=10.5.0.0/16 mynet
然後,我們使用靜態 IP 運行容器,再次使用 MySQL 服務:
docker run --net mynet --ip 10.5.0.1 -p 3306:3306 --mount source=db,target=/var/lib/mysql -e MYSQL_ROOT_PASSWORD=password mysql:latest
我們可以使用 Docker Compose 完成一個完整的示例:
services:
db:
container_name: mysql_db
image: mysql:latest
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_ROOT_HOST=10.5.0.1
ports:
- 3306:3306
volumes:
- db:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
network:
ipv4_address: 10.5.0.5
volumes:
db:
driver: local
networks:
network:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
現在,我們已經通過ipam
關鍵字定義了network的子網,並為服務分配了 IPv4 地址。為了改變,我們使用10.5.0.5
。 172.* 和 10.* IP 地址通常用於專用網絡。我們也可以使用IPv6地址,它的地址長度為 128 位,由於效率更高,它將取代 IPv4。
按照建議,我們將網關地址分配給數據庫主機MYSQL_ROOT_HOST
。
最後,我們添加一個 SQL 腳本來創建用戶、數據庫和表:
CREATE DATABASE IF NOT EXISTS test;
CREATE USER 'db_user'@'10.5.0.1' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'db_user'@'10.5.0.1' WITH GRANT OPTION;
FLUSH PRIVILEGES;
use test;
CREATE TABLE IF NOT EXISTS TEST_TABLE (id int, name varchar(255));
INSERT INTO TEST_TABLE VALUES (1, 'TEST_1');
INSERT INTO TEST_TABLE VALUES (2, 'TEST_2');
INSERT INTO TEST_TABLE VALUES (3, 'TEST_3');
我們只想讓用戶訪問該特定地址的數據庫。
容器啟動後,我們可以用docker ps
查看它的定義:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
97812e199512 mysql:latest "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql_db
我們現在可以通過輸入密碼連接到數據庫。我們使用容器名稱或 ID,因為它們解析為 DNS 的別名:
mysql --host=mysql_db -u db_user -p
現在,使用status
命令,我們可以測試我們的 MySQL 主機是否解析為容器 ID:
Connection id: 10
Current database: test
Current user: [email protected]
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server: MySQL
Server version: 8.0.30 MySQL Community Server - GPL
Protocol version: 10
Connection: 97812e199512 via TCP/IP
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: utf8mb3
Conn. characterset: utf8mb3
TCP port: 3306
3.2.與內置 Docker IP 管理的區別
讓我們檢查一下容器。在靜態 IP 的情況下,我們可以看到IPAM
配置現在有一個 IPv4 地址:
{
"project_network": {
"IPAMConfig": {
"IPv4Address": "10.5.0.5"
},
"Links": null,
"Aliases": [
"project_db",
"db",
"122c0c6bfcf9"
],
"NetworkID": "7ac7a1d9e33dffc65bc867aee4db04b9b8fecaeb3bbb91c74c2f72e4611c6955",
"EndpointID": "84145191a0327b777b6a31bacb2a0260d9a31e8c22cbfca1923775b3649b1d7e",
"Gateway": "10.5.0.1",
"IPAddress": "10.5.0.5",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:0a:05:00:05",
"DriverOpts": null
}
}
從容器的角度來看,這是主要區別。
如果我們需要一個靜態的私有 IP 地址,我們應該考慮是否需要使用一個。大多數時候,我們希望靜態 IP 從另一個容器或主機與一個容器通信。 Docker 的內置網絡已經可以處理這個問題。
但是,我們可能想要手動指定一個私有 IP 地址,例如,直接從主機訪問容器。
值得注意的是使用Docker Swarm自定義網絡的可能性。
4。結論
在本文中,我們了解了 Docker 如何管理 IP 分配以及如何向容器添加靜態地址。我們還看到了運行 MySQL 服務的 Docker Compose 配置示例,有或沒有靜態 IP。
與往常一樣,我們可以在 GitHub 上找到工作代碼示例。