Docker容器每次重启后容器ip是会发生变化的。
这也意味着如果容器间使用ip地址来进行通信的话,一旦有容器重启,重启的容器将不再能被访问到。
而Docker 网络就能够解决这个问题。
Docker 网络主要有以下两个作用:
容器间的互联和通信以及端口映射
容器IP变动时候可以通过服务名直接网络通信而不受到影响
因此只要是处于同一个Docker 网络下的容器就可以使用服务名进行直接访问,而无需担心重启。
这也是Docker 网络最基本和常用的应用场景。
docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法直接通过Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主机(端口映射),即docker run 创建容器的时候,通过-p或者-P参数来启用。访问容器的时候,就通过【宿主机IP】:【容器端口】访问容器。
二、Docker的四种网络模式
网络模式 | 命令指定方式 | 描述 |
---|---|---|
bridge | –networkbridge | docker0 虚拟网桥上,这也是默认网络模式 |
host | –network host | 容器不会创建自己的网卡,配置 ip 等,而是使用宿主机的 ip 和端口 |
container | –network | 容器名称或id 新创建的容器不会创建自己的网卡和配置自己的ip,而是和一个指定的容器共享ip、端口范围 |
none | –network none | 容器有独立的Network namespace,但并没有对其进行任何网络设置 |
如果觉得–network太长了也可以使用简写-net,效果是一样的
而Docker 安装完成时,一般会自动创建三个网络:
NETWORK ID NAME DRIVER SCOPE
40547f9137a5 bridge bridge local
b40bdb8f0356 host host local
0c7f9938f868 none null local
可以使用下列命令查看:
docker network ls
Docker服务启动 时,默认会创建一个名称为 docker0 网桥(其上有一个名称为 docker0 内部接口)。
该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker会 默认指定docker0 的 ip地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
Docker使用Linux桥接的方式,在宿主机虚拟一个Docker容器网桥(docker0)。
Docker每启动一个容器时会根据Docker网桥的网段分配给容器一个ip地址。
同时Docker网桥是每个容器的默认网关。
同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
docker run创建容器的时,未指定network的容器默认的网络模式就是bridge,使用的就是docker0。
在宿主机ifconfig,就可以看到docker0和自己创建的network:
eth0,eth1……代表网卡一,网卡二……
lo代表127.0.0.1(localhost)
inet addr表示网卡的ip地址
网桥docker0会创建一对对等虚拟设备接口:一个叫veth,另一个叫eth0,成对匹配。
整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,
在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
每个容器实例内部也有一块网卡,每个接口叫eth0;
docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对。
不创建任何网络接口,直接使用宿主机的 ip地址与外界进行通信,不再需要额外进行NAT转换。
在主机模式下不能publish port。
容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。
容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
容器共享宿主机网络ip,这样的好处是外部主机与容器可以直接通信。
Docker启动时指定–network=host或-net=host,如果还指定了-p映射端口,此时就会有如下警告:
NARNING: Published ports are discarded when using host network mode
并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。可以选择无视这个警告或者使用Docker的其他网络模式,例如–network=bridge
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。
新创建的容器不会创建自己的网卡,配置自己的ip,而是和一个指定的容器共享ip、端口范围等。
两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、ip、路由等信息,只有一个lo接口。
lo标识代表禁用网络功能,即:127.0.0.1,本地回环的意思
在学习Docker 各种网络模式前,先要了解Docker 网络的常用命令。
docker network ls
# 基础用法
docker network create 网络名称
# 创建网络时是可以添加一系列参数的:
# --driver:驱动程序类型
# --gateway:主子网的IPV4和IPV6的网关
# --subnet:代表网段的CIDR格式的子网
# mynet:自定义网络名称
docker network create --driver=bridge --gateway=192.168.137.1 --subnet=192.168.137.0/16 mynet
不指定任何选项的时候默认的–driver(网络模式)也是bridge(桥接) 但是gateway和subnet会自动生成
docker network inspect 网络名称
docker network connect 网络名称 容器名称
docker network disconnect 网络名称 容器名称
docker network prune
docker network rm 网络名称
docker network create --driver=bridge --gateway=192.168.137.1 --subnet=192.168.137.0/16 myNet1
docker run --name containerName -p 80:80 -d --network myNet1 myNginx
docker network disconnect myNet1 myNginx
docker network create --driver=bridge --gateway=192.168.137.1 --subnet=192.168.137.0/16 myNet2
# 执行后myNginx容器的网络就变成了myNet2
docker network connect myNet2 myNginx
docker network disconnect myNet2 myNginx
有如下docker-compose.yml文件。
一般来说此时使用如下面命令编排的一组容器时会默认创建一个网络,并且这组容器全部都会加入到网络当中。
docker-compose up -d
这也是这一组容器之间可以直接使用服务名去直接通信的原因。
version: '3'
services:
nginx:
image: nginx:alpine
container_name: nginx-dev
environment:
- TZ=Asia/Shanghai
ports:
- "80:80"
volumes:
- /usr/local/docker/workspace/nginx/html:/usr/share/nginx/html
- /usr/local/docker/workspace/nginx/conf/nginx.conf:/etc/nginx/conf.d/default.conf
mysql:
image: mysql:8
container_name: mysql-dev
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=89225300
- MYSQL_DATABASE=nacos_config
- MYSQL_USER=gddst
- MYSQL_PASSWORD=123456
ports:
- "3306:3306"
volumes:
- /usr/docker/docker/workspace/mysql/data:/var/lib/mysql
- /usr/docker/docker/workspace/mysql/mysql.cnf:/etc/mysql/conf.d/mysql.cnf
- /usr/docker/docker/workspace/mysql/initdb:/docker-entrypoint-initdb.d
但是如果想要显示的指定网络,可以参考如下配置:
networks:
mynet:
driver: bridge
ipam:
config:
- subnet: "192.168.0.101/16"
gateway: 192.168.0.100
配置好网络后,在每个服务下可以指定使用的网络,这里就以nginx为例:
version: '3'
services:
nginx:
image: nginx:alpine
container_name: nginx-dev
environment:
- TZ=Asia/Shanghai
ports:
- "80:80"
volumes:
- /usr/local/docker/workspace/nginx/html:/usr/share/nginx/html
- /usr/local/docker/workspace/nginx/conf/nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- mynet
networks:
mynet:
driver: bridge
ipam:
config:
- subnet: "192.168.0.101/16"
gateway: 192.168.0.100
这样容器编排时,所有容器都会加入到mynet这个自定义网络当中了。
在Docker中,内网和外网映射是非常重要的概念。这篇文章将会为您介绍如何在Docker中进行内网和外网映射。Docker中,每个容器都有自己的IP地址,这个IP地址是在Docker内部使用,不可直接从外部访问。然而,为了在容器内运行的应用程序可以通过网络访问,我们需要对容器的端口进行映射。
对于内网映射,我们可以使用Docker的端口映射功能。这个功能可以将宿主机的端口映射到容器的端口,从而可以在宿主机的网络中访问容器内运行的应用程序。例如,我们可以使用下面的命令将宿主机的端口8080映射到容器的端口80:
docker run -p 8080:80 nginx
这个命令会启动一个NGINX容器,并将它的80端口映射到宿主机的8080端口。现在,我们可以在宿主机的浏览器中通过http://localhost:8080来访问容器内运行的NGINX。
另外,在进行内网映射时,我们也要注意容器内部的防火墙。在某些情况下,需要在容器内部打开端口才能访问容器内的应用程序。例如,我们可以使用下面的命令在容器内部 --expose 参数命令打开容器的端口暴露80
http://<宿主机IP>:80来访问容器内运行的
Docker防火墙通常用于保护Docker容器中的应用程序免遭恶意攻击。防火墙通过过滤流入和流出的网络流量来维护应用程序的安全。在Docker中,防火墙相关的规则都是基于iptables来实现的。因此,使用Docker防火墙之前需要了解iptables的相关知识。
Docker防火墙中的主要配置文daemon.json。
/etc/docker/daemon.json
/etc/docker/conf.d/daemon.json
在这个配置文件中,可以定义防火墙规则和防火墙的默认行为。以下是一些常用的设置:
{
"iptables": false,
"ip-masq": true,
"iptables-snat": true
}
在上面的配置文件中,“iptables”: false 表示关闭iptables,而其他两个选项则是启用iptables功能的设置。
构建Docker防火墙的步骤如下:
首先需要安装iptables,可以使用以下命令进行安装:
sudo apt-get install iptables
接下来需要配置iptables规则,以确保Docker容器中的应用程序可以安全地运行。以下是一个简单的iptables规则示例:
# 允许从所有地址接受TCP和UDP连接
iptables -A INPUT -p tcp -j ACCEPT
iptables -A INPUT -p udp -j ACCEPT
# 允许从特定地址接受SSH连接
iptables -A INPUT -p tcp -s 192.168.0.1 --dport 22 -j ACCEPT
# 允许从特定地址通过HTTP访问
iptables -A INPUT -p tcp -s 192.168.0.0/24 --dport 80 -j ACCEPT
# 默认设置为DROP
iptables -P INPUT DROP
iptables -P FORWARD DROP
在添加规则至daemon.json文件之前,需要先确认防火墙的默认行为是否正确。通常情况下,推荐使用iptables的默认行为DROP。
接下来,需要在daemon.json文件中添加iptables规则:
{
"iptables": true,
"iptables-forward": true,
"iptables-default": "DROP",
"iptables-nat": false,
"iptables-filter": [
{
"name": "allow-all",
"rule": [
"-j ACCEPT"
],
"chain": "DOCKER-USER"
},
{
"name": "allow-ssh",
"rule": [
"-p tcp --dport 22 -j ACCEPT"
],
"chain": "DOCKER-USER"
}
]
}
在上面的配置文件中,“iptables”: true 表示开启iptables,“iptables-default”: “DROP” 表示设置iptables默认行为为DROP,iptables-filter则是针对Docker容器的iptables规则,分别对应"allow-all"和"allow-ssh"两种规则,前者允许所有的流量,而后者允许SSH连接。
使用Docker防火墙是一项非常重要的操作,如果使用不当,可能导致应用程序无法运行或者被攻击。以下是一些使用Docker防火墙的技巧:
如果需要允许特定端口通过Docker防火墙,可以使用以下命令:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
可以使用以下命令来展示所有的iptables规则:
iptables -L
可以使用以下命令来修改iptables规则:
iptables -R INPUT 1 -p tcp --dport 22 -j DROP
可以使用以下命令来清除所有的iptables规则:
iptables -F
维护和优化Docker防火墙是非常重要的,以下是一些可以使用的技巧:
由于iptables规则非常复杂,因此建议定期备份规则以免丢失数据。可以使用以下命令来备份规则:
iptables-save > /root/iptables.save
由于iptables规则非常复杂,建议定期清理旧规则以保持系统的健康。可以使用以下命令来清理旧规则:
iptables -L --line-numbers | grep DROP | awk '{print$1}' | xargs -I{} iptables -D INPUT {}
iptables规则越多,系统负担也就越大。因此建议压缩规则以减少负担,以下是一个简单的压缩规则示例:
iptables-save | python -c "import re, sys; chain = ''; iptables = sys.stdin.readlines(); [print(f'{l.strip()}') if 'ACCEPT' in l or '\n' in l else exec(f'chain = re.match(r\'\:([A-Z]+)\', l).group(1)'); print(l.strip()) for l in iptables if l.strip() != '' ]" | iptables-restore
以上是一些Docker防火墙的维护和优化技巧,可以帮助用户更好地维护系统并优化iptables规则。
telnet ip port
SSH 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议,在linux上可以通过ssh命令来测试端口的连通性,具体用法格式如下:
ssh -v -p port username@ip
说明:
-v 调试模式(会打印日志)
-p 指定端口
username:远程主机的登录用户
ip:远程主机
如果远程主机开通了相应的端口,会有如下图所示的建立成功的提示
curl是利用URL语法在命令行方式下工作的开源文件传输工具。也可以用来测试端口的连通性,具体用法:
curl ip:port
说明:
ip:是测试主机的ip地址
port:是端口,比如80
如果远程主机开通了相应的端口,都会输出信息,如果没有开通相应的端口,则没有任何提示,需要CTRL+C断开。。
wget是一个从网络上自动下载文件的自由工具,支持通过HTTP、HTTPS、FTP三个最常见的TCP/IP协议下载,并可以使用HTTP代理。wget名称的由来是“World
Wide Web”与“get”的结合,它也可以用来测试端口的连通性具体用法:
wget ip:port
说明:
ip:是测试主机的ip地址
port:是端口,比如80
如果远程主机不存在端口则会一直提示连接主机。
如果远程主机存在端口则会看到相应的信息,如下图所示。
vm宿主机无法访问容器;
虚拟机curl localhost也无法访问;
端口映射正常;
然后检查内核版本和docker版本也没问题;
然后重启docker包括容器,kill进程什么都试过了还是无济于事;
systemctl stop docker # 停止docker 服务
pkill docker # 杀掉docker进程
iptables -t nat -F # 清理iptables
ip link set docker0 down # 停止docker0网卡
brctl delbr docker0 # 删除docker0网卡--重点!
systemctl start docker # 启动docker服务
最后证明果然是docker0网卡的问题,而且不删除掉网卡重启都没用,只能删除;