Docker 学习笔记(五):网络与存储核心原理及实战应用

发布于:2025-09-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

Docker

网络

容器通信的三种方式

容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信。

IP 通信

IP 通信就是设备靠IP 地址(如 192.168.108.30)定位,通过网络传数据:

  1. 同局域网(如家里两台电脑):直接发;
  2. 跨网(如连外地服务器):经路由器转发;
  3. 特点:不保证数据不丢 / 不乱序,发前不用先连。
    现在主要用 IPv4,正过渡到地址更多的 IPv6。
Docker DNS Server

Docker DNS Server 是 Docker 内置的 DNS 服务,用于容器之间的域名解析,让容器可以通过容器名或服务名互相访问,无需记住 IP 地址。

示例:

启动两个容器 bbox1 和 bbox2

[root@docker ~]# docker run -it --network my_net2 --name bbox1 busybox
/ # [root@docker ~]#
[root@docker ~]# docker run -it --network my_net2 --name bbox2 busybox
/ # [root@docker ~]#

bbox2 就可以直接 ping 到 bbox1

/ # [root@docker ~]# docker exec -it bbox2 sh
/ # ping -c 3 bbox1
PING bbox1 (172.22.16.4): 56 data bytes
64 bytes from 172.22.16.4: seq=0 ttl=64 time=0.144 ms
64 bytes from 172.22.16.4: seq=1 ttl=64 time=0.101 ms
64 bytes from 172.22.16.4: seq=2 ttl=64 time=0.101 ms

--- bbox1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.101/0.115/0.144 ms
/ #

Docker 的 DNS 服务(容器名解析)仅在用户自定义网络(user-defined network)中生效,默认的 bridge 网络不支持这一功能。

验证这一结论:

创建 bbox3 和 bbox4,均连接到 bridge 网络。

[root@docker ~]# docker run -it --name bbox3 busybox
/ # [root@docker ~]# docker run -it --name bbox4 busybox
/ # [root@docker ~]#

bbox4 无法 ping 到 bbox3

/ # [root@docker ~]# docker exec -it bbox4 sh
/ # ping -c 3 bbox3
ping: bad address 'bbox3'
/ #

Docker DNS Server使用场景:
  • 同一 docker network 中的容器,可直接用 容器名 通信
  • Compose 中定义的服务,可通过 服务名 互相访问(如 db 服务可被 web 服务通过 db 域名访问)
joined 容器

joined 容器(通常称为 “joined container” 或 “容器联合”)是 Docker 中一种特殊的容器运行模式,其核心特点是共享另一个容器的网络命名空间(Network Namespace),实现网络栈的完全共享。

核心特性:
  1. 网络完全共享:joined 容器与被关联的容器共享 IP 地址、端口、网络接口等,相当于 “共用同一个网络身份”。
  2. 进程独立:虽然网络共享,但容器的文件系统、进程空间是独立的。
  3. 通信无网络开销:容器间通信可通过 localhost 直接进行,无需端口映射或网络转发。
示例:

先创建一个 httpd 容器,名字为 web1

[root@docker ~]# docker run -d -it --name web1 httpd
Unable to find image 'httpd:latest' locally
latest: Pulling from library/httpd
ce1261c6d567: Pull complete
aeb6d226161f: Pull complete
4f4fb700ef54: Pull complete
56926e6ce68f: Pull complete
4938babf7b43: Pull complete
307fcc49c641: Pull complete
Digest: sha256:c4edd43414d68d7abd223ef20bef385c95bbbbeaea53b01c6ffef01d62bf6309
Status: Downloaded newer image for httpd:latest
dce76e79f444042c1c9ca0e6c4cdd14e0bda94dd23ed28d18ad8f5ea05161a97

查看一下 web1 的网络:

[root@docker ~]# docker exec -it web1 bash

root@dce76e79f444:/usr/local/apache2# hostname -I
172.17.0.5

或者是:
root@dce76e79f444:/usr/local/apache2# apt-get update

root@dce76e79f444:/usr/local/apache2# apt-get install iproute2 -y

root@dce76e79f444:/usr/local/apache2# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:05 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.5/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
root@dce76e79f444:/usr/local/apache2#

然后创建 busybox 容器并通过 --network=container:web1 指定 jointed 容器为 web1:

[root@docker ~]# docker run -it --network container:web1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:05 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.5/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ #

busybox 和 web1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接 用 127.0.0.1 访问 web1 的 http 服务。

/ # wget 127.0.0.1
Connecting to 127.0.0.1 (127.0.0.1:80)
saving to 'index.html'
index.html           100% |******************************************|    45  0:00:00 ETA
'index.html' saved
/ # cat index.html
<html><body><h1>It works!</h1></body></html>
/ #

joined 容器 使用场景:
  • 拆分单一容器的功能(如将应用和日志收集器分为两个容器,但共享网络)。
  • 为现有容器添加辅助服务(如监控代理),无需修改原容器配置。

容器如何访问外部世界

当前环境下不需要配置docker host和容器也可以访问外网

注意:这里外网指的是容器网络以外的网络环境,并非特指 internet。

[root@docker ~]# ping -c 3 www.baidu.com
PING www.a.shifen.com (180.101.51.73) 56(84) bytes of data.
64 bytes from 180.101.51.73 (180.101.51.73): icmp_seq=1 ttl=128 time=33.8 ms
64 bytes from 180.101.51.73 (180.101.51.73): icmp_seq=2 ttl=128 time=111 ms
64 bytes from 180.101.51.73 (180.101.51.73): icmp_seq=3 ttl=128 time=50.6 ms

--- www.a.shifen.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 33.763/65.204/111.242/33.272 ms
[root@docker ~]# docker run -it --name test1 busybox
/ # ping -c 3 www.baidu.com
PING www.baidu.com (180.101.49.44): 56 data bytes
64 bytes from 180.101.49.44: seq=0 ttl=127 time=60.990 ms
64 bytes from 180.101.49.44: seq=1 ttl=127 time=111.941 ms
64 bytes from 180.101.49.44: seq=2 ttl=127 time=110.252 ms

--- www.baidu.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 60.990/94.394/111.941 ms
/ # [root@docker ~]#

docker host 上的 iptables 规则:

[root@docker ~]# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P POSTROUTING ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.22.16.0/24 ! -o br-a87bc5c0a709 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-e16b4272602c -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i br-a87bc5c0a709 -j RETURN
-A DOCKER -i br-e16b4272602c -j RETURN
-A DOCKER -i docker0 -j RETURN


# -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
# 含义:
# 如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。

通过 tcpdump 查看地址是如何转换的

先查看 docker host 的路由表:

[root@docker ~]# ip r
default via 192.168.108.2 dev ens160 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-e16b4272602c proto kernel scope link src 172.18.0.1 linkdown
172.22.16.0/24 dev br-a87bc5c0a709 proto kernel scope link src 172.22.16.1
192.168.108.0/24 dev ens160 proto kernel scope link src 192.168.108.30 metric 100

# 默认路由通过 ens160 发出去,所以我们要同时监控 ens160 和 docker0 上的 icmp(ping)数据包。并且使用tcpdump观察现象。

安装抓包软件

[root@docker ~]# yum install -y tcpdump

开两个窗口

[root@docker ~]# tcpdump -i docker0 -n icmp

[root@docker ~]# tcpdump -i ens160 -n icmp

再开一个窗口, busybox ping www.baidu.com:

[root@docker ~]# docker run -it busybox
/ # ping -c 3 www.baidu.com
PING www.baidu.com (180.101.49.44): 56 data bytes
64 bytes from 180.101.49.44: seq=0 ttl=127 time=68.294 ms
64 bytes from 180.101.49.44: seq=1 ttl=127 time=64.250 ms
64 bytes from 180.101.49.44: seq=2 ttl=127 time=71.800 ms

--- www.baidu.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 64.250/68.114/71.800 ms
/ #

在这里插入图片描述
在这里插入图片描述

这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。

在这里插入图片描述

Docker 容器(如 busybox)访问外部网络的核心流程基于默认 bridge 模式的 NAT 机制,可简化为 3 步:

  1. 容器发请求:容器(如 172.17.0.2)将请求发送到 Docker 虚拟网桥 docker0(172.17.0.1,作为容器网关)。
  2. 主机做转换:Docker 主机通过 NAT 技术,将请求源 IP 从容器内网 IP 替换为主机外网 IP(如 192.168.108.130),并记录映射关系。
  3. 响应回传:外部服务(如百度)的响应先到主机,主机再根据 NAT 记录,将响应转发回原容器。

外部世界如何访问容器

通过 端口映射

docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时 通过 -p 参数映射端口:

[root@docker ~]# docker run -d -p 80 httpd

[root@docker ~]# docker ps

[root@docker ~]# docker port gracious_sanderson
80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768

在这里插入图片描述

容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 32768 上,这样就可以通过 :<32768> 访问容器的 web 服务 了。

[root@docker ~]# curl 192.168.108.30:32768
<html><body><h1>It works!</h1></body></html>

去浏览器测试
在这里插入图片描述

在这里插入图片描述

除了映射动态端口,也可在 -p 中指定映射到 host 某个特定端口,例如可将 80 端口映射到 host 的 8080 端口:

[root@docker ~]# docker run -d -p 8080:80 httpd
58d98478b5f0104e71f4ee647a810310bf3c8b8d76f3e7bd22f6319e02c8885d

[root@docker ~]# curl 192.168.108.30:8080
<html><body><h1>It works!</h1></body></html>

[root@docker ~]# ps -elf | grep docker-proxy

在这里插入图片描述

每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量:

在这里插入图片描述

以 0.0.0.0:8080->80/tcp 为例分析整个过程:

在这里插入图片描述

1、外部请求到达 Docker 主机

  • 外部设备(如用户电脑)发起请求,目标地址为 Docker 主机的 IP + 8080 端口(例如 192.168.1.100:8080)。
  • 请求通过物理网络到达 Docker 主机的网卡(如 ens160)。

2、Docker 端口代理(docker-proxy)转发

  • Docker 启动容器时,会在主机上创建 docker-proxy 进程,负责监听 0.0.0.0:8080(即主机所有网卡的 8080 端口)。
  • docker-proxy 接收到外部请求后,根据端口映射规则(8080->80),将请求转发到 Docker 虚拟网桥 docker0(主机内网网关,通常为 172.17.0.1)。

3、网桥路由到目标容器

  • docker0 网桥根据容器的内网 IP(如 172.17.0.2),将请求路由到对应容器。
  • 容器内部的应用监听 80 端口(如 Nginx),接收并处理请求。

4、响应回传

  • 容器处理完请求后,生成响应,按原路返回:先发送到 docker0 网桥,再由 docker-proxy 转发到主机的 8080 端口。
  • 最终响应通过主机网卡发送回外部设备。

实战:安装tomcat

docker hub上面查找tomcat镜像

[root@docker ~]# docker search tomcat

从docker hub上拉取tomcat镜像到本地

[root@docker ~]# docker pull tomcat

在这里插入图片描述

docker images查看是否有拉取到的tomcat

[root@docker ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
tomcat       latest    a79522cb3b39   3 days ago      468MB
httpd        latest    65005131d37e   4 weeks ago     117MB
busybox      latest    0ed463b26dae   11 months ago   4.43MB

使用tomcat镜像创建容器实例(也叫运行镜像)

[root@docker ~]# docker run -itd -p 8080:8080 tomcat
7c6439c04c8cdc8f0ede4b4bb2b777ac010f307836e0dd822bf79a51e7149abc
docker: Error response from daemon: driver failed programming external connectivity on endpoint nostalgic_franklin (2df72cd69455e4f3e5345ea4c2efa6a1c81d72a04e0109b59c14b2ca898d2dd3): Bind for 0.0.0.0:8080 failed: port is already allocated.

# 此时发现端口被占用了
# 解决:

[root@docker ~]# docker rm -f $(docker ps -aq)
[root@docker ~]# docker run -itd -p 8080:8080 tomcat
f818ae9f8b782a3e827fe7d611e516257d7259b6807ad9810c6765fcd06aa2ab

浏览器访问tomcat首页,发现默认页面被修改了

在这里插入图片描述

把webapps.dist目录换成webapps

[root@docker ~]# docker ps

# 进入tomcat容器内部,并启动一个交互式的命令行终端(bash)
[root@docker ~]# docker exec -it f8 bash

# webapps文件夹为空
root@f818ae9f8b78:/usr/local/tomcat# ls webapps
root@f818ae9f8b78:/usr/local/tomcat#

# 文件在webapps.dist
root@f818ae9f8b78:/usr/local/tomcat# ls webapps.dist/
docs examples host-manager manager ROOT

# 用webapps.dist替换webapps
root@f818ae9f8b78:/usr/local/tomcat# rm -r webapps
root@f818ae9f8b78:/usr/local/tomcat# mv webapps.dist webapps

在这里插入图片描述

再次访问tomcat

在这里插入图片描述

存储

Docker的两类存储资源

Docker 为容器提供了两种存放数据的资源:

  • 由 storage driver 管理的镜像层和容器层。

  • Data Volume。

storage driver

Docker 镜像与容器的分层存储结构

在这里插入图片描述

  • 镜像层(Image layers,只读):由多个只读层(如 d3a1f33e8a5ac22013c84729 等)堆叠而成,每个层包含镜像的一部分文件系统内容,这些层不可修改(图中带锁标识)。
  • 容器层(Container layer,可读写):在镜像层之上有一个 thin(精简的)可读写层(Thin R/W layer),容器运行时产生的新文件、对已有文件的修改,都只会记录在这一层,不会影响底层的只读镜像层。这样多个容器可共享同一镜像的只读层,节省空间且便于镜像复用。

优先使用 Linux 发行版默认的 storage driver。

Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。

查看CentOS的默认 driver:

[root@docker ~]# docker info
Client: Docker Engine - Community
 Version:    26.1.3
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.14.0
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.27.0
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 4
  Running: 3
  Paused: 0
  Stopped: 1
 Images: 7
 Server Version: 26.1.3
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89
 runc version: v1.1.12-0-g51d5e94
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
 Kernel Version: 4.18.0-553.6.1.el8.x86_64
 Operating System: CentOS Stream 8
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 3.549GiB
 Name: docker
 ID: 1a80ba28-0749-4316-950f-ba74c81de259
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://faf9c955231a47648fa3dad688d6db6c.mirror.swr.myhuaweicloud.com/
 Live Restore Enabled: false

CentOS Stream 8 用的overlay2,底层文件系统是xfs,各层数据存放在 /var/lib/docker

对于容器存储数据,需分情况:

  • 无状态应用(如 busybox,仅执行临时命令,无需持久化数据),可将数据放在 storage driver 维护的层中,容器删除时数据也删除,下次启动新容器即可;

  • 有状态应用(需持久化数据,希望容器启动加载已有数据、销毁时保留新数据),这种存储方式就不合适。

Data Volume之bind mount

在 Docker 中,Data Volume(数据卷)是一种用于持久化存储容器数据的机制,独立于容器的生命周期,可解决容器数据持久化和共享等问题。

主要特点
  • 独立于容器生命周期:即便容器被删除,数据卷中的数据依然保留 。例如,数据库容器被删除后,若使用了数据卷,数据库中的数据依然存在,下次创建新容器时可继续使用这些数据。
  • 可共享和重用:多个容器可以同时挂载同一个数据卷,实现数据共享。比如,一个 Web 应用容器和日志分析容器,可以挂载同一个数据卷,日志分析容器能直接读取 Web 应用容器写入的数据卷中的日志文件。
  • 读写性能高:数据卷绕过了联合文件系统,直接在宿主机上进行读写操作,相比在容器层中存储数据,读写性能更好。
创建和使用方式
  • 创建数据卷:可以使用 docker volume create [数据卷名] 命令来创建数据卷。例如 docker volume create my - volume ,会创建一个名为 my-volume 的数据卷。
  • 在容器中挂载数据卷:在使用docker run命令启动容器时,通过-v或–mount参数将数据卷挂载到容器内。
    • 使用 -v 参数:格式为 docker run -v [数据卷名]:[容器内挂载路径] [镜像名] 。比如 docker run -v my-volume:/app/data nginx ,表示将名为 my-volume 的数据卷挂载到 nginx 容器内的 /app/data 路径。
    • 使用 --mount 参数:格式更详细,如 docker run --mount type=volume,src=my-volume,dst=/app/data nginxtype 指定类型为数据卷,src 指定数据卷名,dst 指定容器内挂载路径 。
数据卷的优势场景
  • 数据库应用:像 MySQL、PostgreSQL 等数据库容器,使用数据卷可确保数据库数据的持久化,避免因容器的创建、删除影响数据。
  • 日志管理:Web 应用容器可以将日志写入挂载的数据卷,方便日志分析工具读取和分析,同时也不会因为容器的更新或删除丢失历史日志 。
  • 代码和配置共享:在开发环境中,可将开发代码目录挂载到容器内,这样在宿主机修改代码后,容器内的应用能实时感知变化;还可以将配置文件挂载到容器内,实现灵活的配置管理。
bind mount

bind mount 是将 host 上已存在的目录或文件 mount 到容器。

例如 docker host 上有目录 $HOME/htdocs:

[root@docker ~]# mkdir htdocs
[root@docker ~]# cd htdocs/
[root@docker htdocs]# touch index.html
[root@docker htdocs]# vim index.html
[root@docker htdocs]# cd ..
[root@docker ~]# cat htdocs/index.html
<html><body><h1>This is a file in host file system !</h1></body></html>

通过 -v 将其 mount 到 httpd 容器:

[root@docker ~]# docker run -d -p 80:80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
6a7c5e18fe62720955c720f441eb57fb09cf0ab24093ce24e8a8ed543d21c413

# -v 的格式为 <host path>:<container path>
# /usr/local/apache2/htdocs 就是 apache server 存放静态文件的地方

# 当把主机的 $HOME/htdocs 文件夹和它绑在一起后:
#   容器里原来的 /usr/local/apache2/htdocs 里的东西会被 “藏起来”,暂时看不见了
#   取而代之的是主机 $HOME/htdocs 文件夹里的内容
#   在主机上改这个文件夹里的文件,容器里的 Apache 服务器会直接用新内容(不用重新配置容器)
[root@docker ~]# curl 127.0.0.1:80
<html><body><h1>This is a file in host file system !</h1></body></html>

在这里插入图片描述

[root@docker ~]# echo "updated index page!" > ~/htdocs/index.html
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80

在这里插入图片描述

将容器销毁,看看对 bind mount 有什么影响

[root@docker ~]# docker ps

[root@docker ~]# docker stop 6a7c5e18fe62
6a7c5e18fe62
[root@docker ~]# docker rm 6a7c5e18fe62
6a7c5e18fe62
[root@docker ~]# cat ~/htdocs/index.html
updated index page!

# 可见,即使容器没有了,bind mount 也还在。

另外,bind mount 时还可以指定数据的读写权限,默认是可读可写,可指定为只读:

[root@docker ~]# docker run -d -p 80:80 -v ~/htdocs:/usr/local/apache2/htdocs:ro httpd
2b45f726d158baba90d3be053ed2ce39ffad26ec06146cf71e83ba85574d0644
[root@docker ~]#
[root@docker ~]# docker ps

[root@docker ~]# docker exec -it 2b45 bash
root@2b45f726d158:/usr/local/apache2#

root@2b45f726d158:/usr/local/apache2# echo "do some changes" > htdocs/index.html
bash: htdocs/index.html: Read-only file system

ro 设置了只读权限,在容器中是无法对 bind mount 数据进行修改的。只有 host 有权修改数据,提高 了安全性。

除了 bind mount 目录,还可以单独指定一个文件:

[root@docker ~]# docker rm -f $(docker ps -aq)
4a206e27d218
2b45f726d158
f818ae9f8b78
[root@docker ~]# docker run -d -p 80:80 -v ~/htdocs/index.html:/usr/local/apache2/htdocs/new_index.html httpd
fbe505a6ee7d1c8cd723e653abe38004916a7f08edcad58a0545b2cfb9356507
[root@docker ~]# curl 127.0.0.1:80
<html><body><h1>It works!</h1></body></html>
[root@docker ~]# curl 127.0.0.1:80/new_index.html
updated index page!

bind mount 用于向容器添加单个文件(如给 apache 加 html 文件,且不覆盖容器整个目录,还能保留容器原有数据),但宿主端源文件必须存在,否则会被当作新目录挂载。它应用场景丰富,像挂载源代码目录实现代码实时更新、挂载 mysql 数据方便备份迁移。不过,bind mount 需指定宿主特定路径,限制了容器可移植性,容器迁移到其他宿主时,若对应数据或路径不存在,操作会失败。

Data Volume之docker managed volume

ocker managed volume 与 bind mount 在使用上的最大区别是不需要指定 mount 源,指明 mount point 就行了。还是以 httpd 容器为例:

[root@docker ~]# docker rm -f $(docker ps -aq)

[root@docker ~]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
1cea33d01a63749bfbfe64eb343997644290b8c9b3d8c67c66da1bbe7e44095e

# 通过-v告诉docker需要一个data volume并将其mount到 /usr/local/apache2/htdocs。

data volume

[root@docker ~]# docker inspect 1cea
......
"Mounts": [
            {
                "Type": "volume",
                "Name": "458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8",
                "Source": "/var/lib/docker/volumes/458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8/_data",
                "Destination": "/usr/local/apache2/htdocs",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
......

# docker inspect 的输出很多,我们感兴趣的是 Mounts 这部分,这里会显示容器当前使用的所有 data volume,包括 bind mount 和 docker managed volume。
# Source 就是该 volume 在 host 上的目录。
# 每当容器申请 mount docker manged volume 时,docker 都会在 /var/lib/docker/volumes 下生成一个目录
#/var/lib/docker/volumes/458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8/_data,这个目录就是 mount 源。

[root@docker ~]# ls -l /var/lib/docker/volumes/458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8/_data
total 4
-rw-r--r-- 1 501 ftp 45 Jun 12  2007 index.html

[root@docker ~]# curl 127.0.0.1:80
<html><body><h1>It works!</h1></body></html>

# volume 的内容跟容器原有 /usr/local/apache2/htdocs 完全一样
# 因为如果 mount point 指向的是已有目录,原有数据会被复制到 volume 中。
# 但是此时的 /usr/local/apache2/htdocs 已经不再是由 storage driver 管理的层数据了,它已经是一个 data volume。

可以像 bind mount 一样对数据进行操作

示例:

更新数据

[root@docker ~]# echo "update volume from host !" > /var/lib/docker/volumes/458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8/_data/index.html
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80
update volume from host !

docker managed volume 的创建过程:

  • 容器启动时,仅需告知 Docker“需挂载卷到容器内 /abc 目录”,无需指定宿主路径;

  • Docker 自动在宿主的 /var/lib/docker/volumes 下生成随机命名的目录,作为卷的实际存储源;

  • 若容器内 /abc 目录原本已有数据,Docker 会先将这些数据复制到刚生成的宿主卷目录中;

  • 最后将宿主的这个卷目录,挂载到容器内的 /abc 目录,完成卷的创建与关联。

除了通过 docker inspect 查看 volume,也可以用 docker volume 命令查看:

[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
local     f84217e5c24a0bc6d5a60405e7314e0e3985724b53b48678335f680760087831


[root@docker ~]# docker volume inspect 458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
[
    {
        "CreatedAt": "2025-09-09T13:37:05+08:00",
        "Driver": "local",
        "Labels": {
            "com.docker.volume.anonymous": ""
        },
        "Mountpoint": "/var/lib/docker/volumes/458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8/_data",
        "Name": "458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8",
        "Options": null,
        "Scope": "local"
    }
]

# 目前, docker volume 只能查看 docker managed volume,还看不到 bind mount;同时也无法知道 volume 对应的容器,这些信息还得靠 docker inspect 。
两种 data volume 的对比

1、相同点:两者本质都是宿主文件系统中的某个路径,核心作用都是实现容器数据的持久化或共享,避免数据随容器生命周期消亡。

2、不同点:

对比维度 Bind Mount(绑定挂载) Docker Managed Volume(Docker 管理卷)
Volume 位置 可自主指定宿主任意路径(如 /home/user/data 固定在宿主 /var/lib/docker/volumes/ 下(自动生成随机目录)
对已有 mount point 影响 直接隐藏容器内原有目录,用宿主路径内容替换 会先将容器内原有目录的数据,复制到宿主的卷目录中
是否支持单个文件 支持(可挂载单个文件到容器) 不支持,仅能挂载目录
权限控制 可设置只读(如 -v /host/path:/container/path:ro),默认读写 无额外控制,默认均为读写权限
移植性 弱,与宿主特定路径强绑定(迁移容器时需确保目标宿主有对应路径 / 数据) 强,无需手动指定宿主路径(Docker 自动管理),迁移容器时卷可随容器关联

如何共享数据

容器与 host 共享数据

两种类型的 data volume,它们均可实现在容器与 host 之间共享数据,但方式有所区别。

对于 bind mount 是非常明确的:直接将要共享的目录 mount 到容器。

docker managed volume 就要麻烦点。由于 volume 位于 host 中的目录,是在容器启动时才生成,所 以需要将共享数据拷贝到 volume 中。

示例

[root@docker ~]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
c39d0650ec1b2cc2810a83fdd1295319685a50436a0e2c5e0f044f5cfba792bd
[root@docker ~]# curl 127.0.0.1:80
<html><body><h1>It works!</h1></body></html>
[root@docker ~]# docker cp ~/htdocs/index.html c39d0650ec1b2:/usr/local/apache2/htdocs
Successfully copied 2.05kB to c39d0650ec1b2:/usr/local/apache2/htdocs

[root@docker ~]# curl 127.0.0.1:80
updated index page!

docker cp 可以在容器和 host 之间拷贝数据,当然我们也可以直接通过 Linux 的 cp 命令复制到 /var/lib/docker/volumes/xxx。

容器之间共享数据

第一种方法是将共享数据放在 bind mount 中,然后将其 mount 到多个容器。

创建由三个 httpd 容器组成的 web server 集群,它们使用相同的 html 文 件

示例:

# 将 $HOME/htdocs mount 到三个 httpd 容器。
[root@docker ~]# docker run --name web1 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
b248cf04ecfbe3f670285d75e4af3a1cc36430282e4cd089ef3764f10b81933c
[root@docker ~]# docker run --name web2 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
210229c54b4d511cc18eb2a7336173d9db1762a84d59798172db467589d81e55
[root@docker ~]# docker run --name web3 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
4da13957b783ad571ee44161d9924c3626c52a034608ea55a4d804e584a2ad6a

# 查看当前主页内容。
[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED              STATUS              PORTS                                     NAMES
4da13957b783   httpd     "httpd-foreground"   35 seconds ago       Up 34 seconds       0.0.0.0:32771->80/tcp, :::32771->80/tcp   web3
210229c54b4d   httpd     "httpd-foreground"   40 seconds ago       Up 40 seconds       0.0.0.0:32770->80/tcp, :::32770->80/tcp   web2
b248cf04ecfb   httpd     "httpd-foreground"   About a minute ago   Up About a minute   0.0.0.0:32769->80/tcp, :::32769->80/tcp   web1
c39d0650ec1b   httpd     "httpd-foreground"   7 minutes ago        Up 7 minutes        0.0.0.0:80->80/tcp, :::80->80/tcp         nostalgic_cori

[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:32769
updated index page!
[root@docker ~]# curl 127.0.0.1:32770
updated index page!
[root@docker ~]# curl 127.0.0.1:32771
updated index page!
[root@docker ~]#

# 修改 volume 中的主页文件,再次查看并确认所有容器都使用了新的主页。
[root@docker ~]# echo "This is a new index page for web cluster" > ~/htdocs/index.html
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:32769
This is a new index page for web cluster
[root@docker ~]# curl 127.0.0.1:32770
This is a new index page for web cluster
[root@docker ~]# curl 127.0.0.1:32771
This is a new index page for web cluster
[root@docker ~]#

用volume container共享数据

volume container 是专门为其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume。下面我们创建一个 volume container:

[root@docker ~]# docker create --name vc_data -v ~/htdocs/:/usr/local/apache2/htdocs -v /other/userful/tools busybox
caca3f9e514f9eab224b1a256b12cc21cba3b346156e8a13ce0525424be12ca5

# 通过 docker inspect 可以查看到这两个 volume
[root@docker ~]# docker inspect vc_data
......
"Mounts": [
            {
                "Type": "bind",
                "Source": "/root/htdocs",
                "Destination": "/usr/local/apache2/htdocs",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                "Type": "volume",
                "Name": "943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57",
                "Source": "/var/lib/docker/volumes/943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57/_data",
                "Destination": "/other/userful/tools",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
......

# 把之前的123删掉重新run
[root@docker ~]# docker rm -f web1
web1
[root@docker ~]# docker rm -f web2
web2
[root@docker ~]# docker rm -f web3
web3

# 其他容器可以通过 --volumes-from 使用 vc_data 这个 volume container:
[root@docker ~]# docker run --name web1 -d -p 80 --volumes-from vc_data httpd
ec0a7e713634cd82414ce6db4d622bcaa61762609ef8ee09272a2f115e31a576
[root@docker ~]# docker run --name web2 -d -p 80 --volumes-from vc_data httpd
93c6cc1f32711c2d9656cde1659208be7043b2f102096967f2f4ac4b582d334a
[root@docker ~]# docker run --name web3 -d -p 80 --volumes-from vc_data httpd
f6d58d47753733e5d494d694a7c1531764470035cf91ff66dd2be3b3d1adb57d

三个 httpd 容器都使用了 vc_data,看看它们现在都有哪些 volume,以 web1 为例:

[root@docker ~]# docker inspect web1
......
"Mounts": [
            {
                "Type": "volume",
                "Name": "943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57",
                "Source": "/var/lib/docker/volumes/943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57/_data",
                "Destination": "/other/userful/tools",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "bind",
                "Source": "/root/htdocs",
                "Destination": "/usr/local/apache2/htdocs",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
......

web1 容器使用的就是 vc_data 的 volume,而且连 mount point 都是一样的。验证一下数据共享的效 果:

[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS          PORTS                                     NAMES
f6d58d477537   httpd     "httpd-foreground"   6 minutes ago    Up 6 minutes    0.0.0.0:32774->80/tcp, :::32774->80/tcp   web3
93c6cc1f3271   httpd     "httpd-foreground"   6 minutes ago    Up 6 minutes    0.0.0.0:32773->80/tcp, :::32773->80/tcp   web2
ec0a7e713634   httpd     "httpd-foreground"   7 minutes ago    Up 7 minutes    0.0.0.0:32772->80/tcp, :::32772->80/tcp   web1
c39d0650ec1b   httpd     "httpd-foreground"   28 minutes ago   Up 28 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp         nostalgic_cori

[root@docker ~]# echo "1234567890" > ~/htdocs/index.html
[root@docker ~]# curl 127.0.0.1:32772
1234567890
[root@docker ~]# curl 127.0.0.1:32773
1234567890
[root@docker ~]# curl 127.0.0.1:32774
1234567890

# 三个容器已经成功共享了 volume container 中的 volume
总结
实现容器与主机的解耦

用 bind mount 时,每个容器都要显式指定 -v <主机路径>:<容器路径>,这会让容器 “依赖” 主机的具体路径(比如 /home/user/data)。如果换一台主机,路径可能不一样,容器就跑不起来。

而 volume container 把所有挂载路径都 “封装” 在自己内部(比如在数据卷容器里定义 -v /data),其他容器只需通过 --volumes-from 数据卷容器名 关联,完全不用关心主机上的实际存储路径(由 Docker 管理)。
这样一来,容器配置里看不到任何主机路径,实现了 “容器和主机的解耦”—— 容器可以在不同主机间迁移,只要带上数据卷容器的配置,数据就能正常使用。

挂载路径统一,规范但有局限

使用同一个 volume container 的所有应用容器,都会继承相同的挂载点(比如都挂载 /data 目录)。

  • 好处:所有容器的目录结构一致(比如都从 /data/config 读配置,向 /data/logs 写日志),方便团队统一规范(不用每个人记不同的路径),尤其适合多容器协作的场景(比如前后端容器共享静态资源目录)。
  • 局限:如果某个容器需要特殊的挂载路径(比如想把数据存在 /app/data 而不是默认的 /data),就很难灵活调整,必须跟着数据卷容器的定义走,不够灵活。

简单说,volume container 就像一个 “数据配置模板”:统一管理路径,减少容器对主机的依赖,但代价是牺牲了部分灵活性。这也是后来命名卷(Named Volumes)逐渐替代它的原因 —— 命名卷既保留了 “解耦主机路径” 的优点,又能让每个容器灵活指定挂载点,更符合大多数场景的需求。

data-packed volume container

要实现 “数据完全打包到容器中,同时供其他容器共享”,核心是创建 Data-Packed Volume Container(数据打包型卷容器),原理是把数据嵌入镜像,再通过 Docker 管理卷共享。

示例:

[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox:latest
ADD htdocs /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs

# ADD 将静态文件添加到容器目录 /usr/local/apache2/htdocs。
# VOLUME 的作用与 -v 等效,用来创建 docker managed volume,mount point 为 /usr/local/apache2/htdocs,因为这个目录就是 ADD 添加的目录,所以会将已有数据拷贝到 volume 中。

[root@docker ~]# echo "666666666666666" > htdocs/index.html
[root@docker ~]# docker build -t datapacked .
[+] Building 0.1s (7/7) FINISHED                                                                                           docker:default
 => [internal] load build definition from Dockerfile                                                                                 0.0s
 => => transferring dockerfile: 128B                                                                                                 0.0s
 => [internal] load metadata for docker.io/library/busybox:latest                                                                    0.0s
 => [internal] load .dockerignore                                                                                                    0.0s
 => => transferring context: 2B                                                                                                      0.0s
 => [internal] load build context                                                                                                    0.0s
 => => transferring context: 90B                                                                                                     0.0s
 => [1/2] FROM docker.io/library/busybox:latest                                                                                      0.0s
 => [2/2] ADD htdocs /usr/local/apache2/htdocs                                                                                       0.0s
 => exporting to image                                                                                                               0.0s
 => => exporting layers                                                                                                              0.0s
 => => writing image sha256:74c2b6a5c3dbe6fce453235e1a86bdff34c6dd46d55b045fbdf24dc5f2dccc95                                         0.0s
 => => naming to docker.io/library/datapacked                                                                                        0.0s

在这里插入图片描述

用新镜像创建 data-packed volume container:

[root@docker ~]# docker create --name vc_data datapacked
93e2a017de4ab15e4e7b1c5a98eb728bcab688fcf799e19268323b0608d0a102

因为在 Dockerfile 中已经使用了 VOLUME 指令,这里就不需要指定 volume 的 mount point 了。启动 httpd 容器并使用 data-packed volume container:

[root@docker ~]# docker run -d -p 80:80 --volumes-from vc_data httpd
83b5e79df0156d850f3fabce494e27089e6a418633cd41f856daedac243b7644
[root@docker ~]# curl 127.0.0.1:80
666666666666666

Data-Packed Volume Container(数据打包型卷容器)的核心价值可总结为两点:

  1. 数据自包含,不依赖宿主:静态数据(如配置文件、Web 静态文件)直接打包进镜像,容器启动时通过 Docker 管理卷自动加载数据,无需宿主提前准备数据,彻底摆脱对宿主路径的依赖;
  2. 移植性极强:迁移时只需拷贝镜像,在新环境启动卷容器和业务容器,数据就能正常共享,尤其适配 “仅用静态数据” 的场景,无需额外处理数据同步问题。

volume生命周期管理

备份

因为 volume 实际上是 host 文件系统中的目录和文件,所以 volume 的备份实际上是对文件系统的备 份。

[root@docker ~]# docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2

在这里插入图片描述

恢复

volume 的恢复也很简单,如果数据损坏了,直接用之前备份的数据拷贝到 /myregistry 就可以了。

迁移

如果我们想使用更新版本的 Registry,这就涉及到数据迁移,方法是:

  • docker stop 当前 Registry 容器。

  • 启动新版本容器并 mount 原有 volume。

    ​ docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:latest

销毁
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
local     943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57
local     cf88d7bad5be212d960e2dd1dad1c9638e02c9965ea48179ce23b60ba46dbd13
local     ee467975716cdf8c6904126c375ea01259c0bf194c62dec6d96b1c2def72e371
local     f84217e5c24a0bc6d5a60405e7314e0e3985724b53b48678335f680760087831
[root@docker ~]# docker run --name bbox -v /test/data busybox
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
local     898d2a7ac0f8881f0d80878740d7eec692b26d169915dd721e4855877da0472b
local     943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57
local     cf88d7bad5be212d960e2dd1dad1c9638e02c9965ea48179ce23b60ba46dbd13
local     ee467975716cdf8c6904126c375ea01259c0bf194c62dec6d96b1c2def72e371
local     f84217e5c24a0bc6d5a60405e7314e0e3985724b53b48678335f680760087831

容器 bbox 使用的 docker managed volume 可以通过 docker volume ls 查看到。

删除 bbox:

[root@docker ~]# docker rm bbox
bbox
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
local     898d2a7ac0f8881f0d80878740d7eec692b26d169915dd721e4855877da0472b
local     943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57
local     cf88d7bad5be212d960e2dd1dad1c9638e02c9965ea48179ce23b60ba46dbd13
local     ee467975716cdf8c6904126c375ea01259c0bf194c62dec6d96b1c2def72e371
local     f84217e5c24a0bc6d5a60405e7314e0e3985724b53b48678335f680760087831

因为没有使用 -v ,volume 遗留了下来。对于这样的孤儿 volume,可以用 docker volume rm 删除:

[root@docker ~]# docker volume rm 458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
458cc02fa1c3748b302ab8f40ffc449ddb030a54a832ed4858b1c6cb8d3e90c8
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     898d2a7ac0f8881f0d80878740d7eec692b26d169915dd721e4855877da0472b
local     943203e65358f3875cf8cda7e5206e9c8abcca3d2d9d94e092c9249933611b57
local     cf88d7bad5be212d960e2dd1dad1c9638e02c9965ea48179ce23b60ba46dbd13
local     ee467975716cdf8c6904126c375ea01259c0bf194c62dec6d96b1c2def72e371
local     f84217e5c24a0bc6d5a60405e7314e0e3985724b53b48678335f680760087831

# 如果想批量删除孤儿 volume,可以执行:docker volume rm $(docker volume ls -q)

实战:安装mysql

# docker hub上查找mysql镜像
[root@docker ~]# docker search mysql

# 从华为云加速器拉取mysql镜像到本地标签为5.7
[root@docker ~]# docker pull mysql:5.7

使用mysql

方法1(基础)
[root@docker ~]# docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
44a03436dbb25ae4ac3cb3715fa2d88f0ab2891d8847fb8eace9dc751f035066

[root@docker ~]# docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
44a03436dbb2   mysql:5.7    "docker-entrypoint.s…"   10 seconds ago   Up 9 seconds    0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   happy_cray

[root@docker ~]# docker exec -it 44a03436dbb2 bash
bash-4.2# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.44 MySQL Community Server (GPL)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

建库建表插入数据

mysql> CREATE DATABASE db01;
Query OK, 1 row affected (0.00 sec)

mysql> USE db01;
Database changed
mysql> CREATE TABLE tablea(id int,name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO tablea VALUES(1,'yuxb');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tablea;
+------+------+
| id   | name |
+------+------+
|    1 | yuxb |
+------+------+
1 row in set (0.01 sec)

mysql>

打开navicat测试链接

在这里插入图片描述

在这里插入图片描述

插入中文试试

在这里插入图片描述

因为docker上默认字符集不支持中文

mysql> SHOW VARIABLES LIKE 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)


删除容器后,里面的mysql数据就没有了

[root@docker ~]# docker rm -f 44a03436dbb2
44a03436dbb2

在这里插入图片描述

再用同样的方式创建一个mysql,数据还是没有

[root@docker ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
eb4799cbbc51cb71e80774629d064f09eee11c1525b9c17a3a4e78885eee251b

在这里插入图片描述

方法2(进阶)
# 新建mysql实例
[root@docker ~]# docker run -d -p 3306:3306 --privileged=true -v /yuxb/mysql/log:/var/log/mysql -v /yuxb/mysql/data:/var/lib/mysql -v /yuxb/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
b55cc2e8fdedda2c32c9776805c83d43f0867ff54ae27bd051616ef46733446a

# --privileged=true:给容器赋予更高权限(避免某些文件操作因权限不足失败,比如 MySQL 写入数据时)

# 三个 -v 参数(数据持久化):
## /-v /yuxb/mysql/log:/var/log/mysql:把主机的 /yuxb/mysql/log 文件夹,和容器里 MySQL 的日志目录绑定(日志会存在主机上,容器删了日志也在)
## -v /yuxb/mysql/data:/var/lib/mysql:绑定 MySQL 的数据目录(核心!数据库表、数据会存在主机上,容器删了数据不丢)
## -v /yuxb/mysql/conf:/etc/mysql/conf.d:绑定配置文件目录(可以在主机上直接改 MySQL 配置,不用进入容器)

# 具体说:
## 第一个 -v:容器里 MySQL 产生的日志(比如谁连接过、有没有报错),会自动存在你电脑的 /yuxb/mysql/log 文件夹里。就算容器删了,日志还在,方便你查问题。
## 第二个 -v:最重要的!你在 MySQL 里建的表、存的数据,本来是存在容器内部的。加了这个后,会存到你电脑的 /yuxb/mysql/data 里。就算容器不小心删了,只要这个文件夹还在,数据就丢不了,下次再建个新容器还能用这些数据。
## 第三个 -v:MySQL 的配置文件(比如改密码规则、调性能参数),本来要进容器里改。现在你直接在自己电脑的 /yuxb/mysql/conf 里改就行,容器里会自动用你改好的配置,不用费劲进容器操作了。

# 简单说,这三个 -v 就是把容器里 “重要的东西” 挪到自己能直接摸到的地方,既安全(不怕容器没了)又方便(直接在电脑上操作)。


# 查看
[root@docker ~]# cd /yuxb/
[root@docker yuxb]# ls
mysql
[root@docker yuxb]# cd mysql/
[root@docker mysql]# ls
conf  data  log

# 新建my.cnf,通过容器卷同步给mysql容器实例
[root@docker mysql]# cd /yuxb/mysql/conf/
[root@docker conf]# ls
[root@docker conf]# vim my.cnf

# #实现mysql支持中文
[root@docker conf]# cat my.cnf
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8

# 重新启动mysql容器实例再重新进入并查看字符编码
[root@docker conf]# docker restart mysql
mysql
[root@docker conf]# docker exec -it mysql bash
bash-4.2#
bash-4.2# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.44 MySQL Community Server (GPL)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW VARIABLES LIKE 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)

创建库表

mysql> CREATE DATABASE db01;
Query OK, 1 row affected (0.00 sec)

mysql> USE db01;
Database changed
mysql> CREATE TABLE tablea(id int,name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO tablea VALUES(1,'yuxb');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tablea;
+------+------+
| id   | name |
+------+------+
|    1 | yuxb |
+------+------+
1 row in set (0.00 sec)

mysql>

去软件里查看库

在这里插入图片描述

插入中文测试

发现并没有报错

在这里插入图片描述

mysql> SELECT * FROM tablea;
+------+--------+
| id   | name   |
+------+--------+
|    1 | yuxb   |
|    2 | 哈哈   |
+------+--------+
2 rows in set (0.00 sec)

删除测试

[root@docker conf]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
b55cc2e8fded   mysql:5.7   "docker-entrypoint.s…"   7 minutes ago   Up 5 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql
[root@docker conf]# docker rm -f b55
b55

重新创建容器,发现数据还在

[root@docker conf]# docker run -d -p 3306:3306 --privileged=true -v /yuxb/mysql/log:/var/log/mysql -v /yuxb/mysql/data:/var/lib/mysql -v /yuxb/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
543935ff4fd92004d79003639e611193b26081361c9a9b5fac89673d9f61350a

在这里插入图片描述

容器监控

Docker自带的监控子命令

ps

主机上正在运行的程序

[root@docker conf]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
543935ff4fd9   mysql:5.7   "docker-entrypoint.s…"   24 minutes ago   Up 24 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql

top

查看容器的进程

[root@docker conf]# docker top mysql
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
systemd+            11807               11788               0                   15:50               ?                   00:00:01            mysqld

# 命令后面还可以跟上 Linux 操作系统 ps 命令的参数显示特定的信息,比如 -au 。
[root@docker conf]# docker top mysql -au
USER                PID                 %CPU                %MEM                VSZ                 RSS                 TTY                 STAT                START               TIME                COMMAND
systemd+            11807               0.0                 6.1                 1360648             228012              ?                   Ssl                 15:50               0:01                mysqld

stats

列出容器资源使用率

[root@docker conf]# docker stats
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O     PIDS
543935ff4fd9   mysql     0.00%     206.8MiB / 3.549GiB   5.69%     6.13kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O     PIDS
543935ff4fd9   mysql     0.04%     206.8MiB / 3.549GiB   5.69%     6.13kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O     PIDS
543935ff4fd9   mysql     0.04%     206.8MiB / 3.549GiB   5.69%     6.13kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O     PIDS
543935ff4fd9   mysql     0.04%     206.8MiB / 3.549GiB   5.69%     6.13kB / 11.1kB   0B / 30.4MB   30

# 默认会显示一个实时变化的列表,展示每个容器的 CPU 使用率,内存使用量和可用量,网络和磁盘的 IO 数据。

cAdvisor

cAdvisor 是 google 开发的容器监控工具。

[root@docker ~]# docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8080:8080 --detach=true --name=cadvisor google/cadvisor:latest

打开浏览器输入192.168.108.30:8080访问cAdvisor

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结:

  • 缺点:操作界面略显简陋,而且需要在不同页面之间跳转,并且只能监控一个 host。
  • 优点:可以将监控到的数据导出给第三方工具,由这些工具进一步加工处理。

结论:我们把 cAdvisor 定位为一个监控数据收集器,并导出数据给第三方工具,而非展示数据。

容器日志

Docker logs

对于一个运行的容器,Docker 会将日志发送到 容器标准输出设备(STDOUT)和标准错误设备 (STDERR),STDOUT 和 STDERR 实际上就是容器的控制台终端。

示例:

# 运行httpd容器
[root@docker conf]# docker run -p 80:80 httpd
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
[Tue Sep 09 08:38:50.472141 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations
[Tue Sep 09 08:38:50.472442 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'
^C[Tue Sep 09 08:39:02.107747 2025] [mpm_event:notice] [pid 1:tid 1] AH00491: caught SIGTERM, shutting down

# 没有用 -d 参数,httpd 容器以前台方式启动,日志会直接打印在当前的终端窗口。

# 加上 -d 参数以后台方式运行容器,就看不到输出的日志了。
[root@docker conf]# docker run -d -p 80:80 httpd
9349e12a220a57013fbecaf34645f3407d2c407958edc917a884747fcc881fc8

查看日志内容
方法一:

attach 到该容器。

# 先attach然后新开一个窗口,curl localhost就可以输出以下内容
# attach 的终端就会打印出新的日志。
[root@docker conf]# docker attach 9349
172.17.0.1 - - [09/Sep/2025:08:42:20 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [09/Sep/2025:08:42:21 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [09/Sep/2025:08:42:22 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [09/Sep/2025:08:42:22 +0000] "GET / HTTP/1.1" 200 45
......

[root@docker ~]# curl localhost
<html><body><h1>It works!</h1></body></html>
[root@docker ~]# curl localhost
<html><body><h1>It works!</h1></body></html>
[root@docker ~]# curl localhost
<html><body><h1>It works!</h1></body></html>
[root@docker ~]# curl localhost
<html><body><h1>It works!</h1></body></html>

attach 的方法在实际使用中不太方便,因为:

  • 只能看到 attach 之后的日志,以前的日志不可见。
  • 退出 attach 状态比较麻烦(Ctrl+p 然后 Ctrl+q 组合键),一不小心很容器将容器杀掉(比如按下 Ctrl+C)。
方法二:

用 docker logs 命令查看日志。

[root@docker conf]# docker logs 9349
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
[Tue Sep 09 08:39:52.501312 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations
[Tue Sep 09 08:39:52.501443 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'
172.17.0.1 - - [09/Sep/2025:08:42:20 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [09/Sep/2025:08:42:21 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [09/Sep/2025:08:42:22 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [09/Sep/2025:08:42:22 +0000] "GET / HTTP/1.1" 200 45
[Tue Sep 09 08:45:34.041905 2025] [mpm_event:notice] [pid 1:tid 1] AH00491: caught SIGTERM, shutting down

# docker logs 能够打印出自容器启动以来完整的日志,并且 -f 参数可以继续打印出新产生的日志,效果上与 Linux 命令 tail -f 一样。

网站公告

今日签到

点亮在社区的每一天
去签到