6,数据容器卷

6.0容器的理解

在非运行态(静态)下,容器可以看作是多个镜像层加上一个可写层的集合。在这里插入图片描述

可写层的定义

可写层是在基于镜像创建容器时,Docker 自动添加到容器最上层的一个特殊文件系统层。它是容器中唯一可进行写入操作的层,而其下方的镜像层(Read Layer) 都是只读的。

可写层的作用

  • 保存容器运行时的修改:当容器运行时,对文件系统进行的任何操作,比如创建新文件、修改现有文件内容、删除文件等,这些更改都不会影响到下方只读的镜像层,而是被记录在可写层中。例如,在一个基于centos镜像创建的容器里,使用touch命令创建一个新文件,该文件就会被创建在可写层;如果修改了镜像层中已有的文件,那么 Docker 会使用写时复制(Copy - on - Write,COW)技术,先将该文件从镜像层复制到可写层,然后在可写层中进行修改 。
  • 维持容器状态:可写层使得每个容器都能拥有独立于镜像以及其他容器的状态。即使多个容器基于同一个镜像创建,它们各自的可写层也互不干扰,保证了容器数据的独立性和隔离性。

6.1容器数据卷

数据容器卷(Data Volume)是一种用于持久化存储容器数据的机制,它可以独立于容器的生命周期存在,方便数据共享和持久化。

数据容器卷的核心特点

  1. 数据卷可以在容器之间共享和重用
  2. 数据卷数据会持久化,即使删除使用它的容器
  3. 可以直接对数据卷中的数据进行修改
  4. 数据卷的变化不会影响镜像的更新

6.2常用操作命令

创建数据卷

docker volume create mydata

查看所有数据卷

docker volume ls

查看数据卷详情

docker volume inspect mydata

使用数据卷运行容器

# 将数据卷挂载到容器的指定路径
docker run -d --name mycontainer -v mydata:/app/data nginx

绑定挂载主机目录作为数据卷

# 将主机的/path/on/host目录挂载到容器的/container/path目录
docker run -d --name mycontainer -v /path/on/host:/container/path nginx

使用匿名卷

# 创建匿名卷,Docker会自动生成卷名
docker run -d --name mycontainer -v /app/data nginx

删除数据卷

# 删除未被使用的数据卷
docker volume prune

# 删除指定数据卷(需确保没有容器使用)
docker volume rm mydata

测试

docker run -it -v 主机目录:容器内目录 -p 主机端口:容器内端口

docker run -it -v /home/ceshi:/home centos /bin/bash

6.3具名&匿名&绑定挂载

具名挂载(Named Volumes)和匿名挂载(Anonymous Volumes)是两种常用的数据卷挂载方式,它们的主要区别在于是否为数据卷指定了明确的名称。

具名挂载是指为数据卷指定一个明确的名称,便于管理和识别。

# 创建具名卷(可选,运行容器时会自动创建不存在的卷)
docker volume create myappdata

# 使用具名卷挂载
docker run -d \
  --name mycontainer \
  -v myappdata:/app/data \  # 具名卷myappdata挂载到容器的/app/data目录
  nginx

匿名挂载没有为数据卷指定名称,Docker 会自动生成一个随机的唯一标识符作为卷名。

# 使用匿名卷挂载(不指定卷名,只指定容器内路径)
docker run -d \
  --name mycontainer \
  -v /app/data \  # 匿名卷挂载到容器的/app/data目录
  nginx

绑定挂载:格式为 -v 宿主机绝对路径:容器内路径(如你的命令中 /home/ceshi:/home),直接将主机的具体目录 / 文件挂载到容器,不属于 Docker 管理的卷

docker run -it -v /home/ceshi:/home centos /bin/bash

6.4容器共享数据卷

--volumes-from 是 Docker 中用于在容器之间共享数据卷的命令参数,它允许一个容器复用用另一个容器中定义的卷(包括匿名名卷、具名卷或绑定挂载的目录),实现容器间的数据共享。

基本用法

docker run --volumes-from 源容器名称/ID 新容器镜像
  • 作用:新容器会继承 “源容器” 中所有的卷配置(包括挂载路径和数据)。
  • 特点
    • 无需手动指定卷名或路径,直接复用源容器的卷配置。
    • 源容器可以是运行中或已停止的(只要未被删除)。
    • 共享的是卷本身,数据会在所有复用该卷的容器间实时同步。

注意事项

  1. 源容器必须存在--volumes-from 依赖源容器的元数据(即使源容器已停止),如果源容器被删除,新容器会挂载失败。
  2. 卷的生命周期独立:复用卷的容器删除后,卷本身不会被删除(需用 docker volume rm 手动删除)。
  3. 避免多写冲突:多个容器共享卷时,若同时写入同一文件可能导致数据损坏(如 MySQL 等数据库不支持多实例同时写同一数据文件)。
  4. 权限问题:不同容器的用户 ID(UID)可能不同,可能导致文件权限冲突,需确保容器内用户对共享卷有正确的读写权限。

6.5实例:Mysql数据卷

# 创建具名卷
docker volume create mysql_data
docker volume create mysql_conf

# 使用具名卷启动
docker run -d \
  -p 3307:3306 \
  -v mysql_conf:/etc/mysql/conf.d \
  -v mysql_data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  --name mysql01 \
  mysql:5.7

现在使用了具名卷(mysql_confmysql_data 来持久化数据,即使删除了 mysql01 容器,重新创建新容器时只要挂载相同的卷,数据依然会保留。

如何实现多容器mysql数据共享

  docker run -d \
  -p 3308:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  --name mysql04 \
  mysql:5.7
  
  docker run -d -p 3309:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql05 --volumes-from mysql03 mysql:5.7

mysql04正常启动但没有03的数据,05无法启动,docker log mysql05会有错误日志。原因:MySQL 数据文件不支持多实例同时读写,使用 --volumes-from 共享数据卷时,必须确保源容器已停止。

那如何实现?

  1. 通过 MySQL 自身的 主从复制(Master-Slave) 机制,实现数据自动同步,多个容器各自拥有独立的数据文件,但数据保持一致。
    • 多容器独立运行,无文件锁冲突
    • 主库写入,从库可读,可分担查询压力
    • 数据自动同步,延迟低
  2. 如果是在云环境中,可直接使用 云数据库服务(如 RDS),多个 Docker 容器通过网络连接到同一个数据库实例,无需关心数据文件共享。

这里不再先说mysql的主从复制。

7,DockerFile

7.1概述

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。通过定义一系列命令和参数,Dockerfile 指导 Docker 构建一个自定义的镜像。

7.2编写规范&指令

  1. 注释格式

    使用 # 开头

  2. 指令大小写
    虽然 Docker 对指令大小写不敏感,但行业规范要求指令必须大写(如 FROMRUN),参数小写,便于区分指令和内容。

  3. 执行顺序与缓存
    Docker 构建镜像时会按指令顺序从上到下顺序执行,且会缓存每一层的结果。如果某层指令未修改,下次构建会直接使用缓存。因此:

    • 频繁变动的指令(如复制 COPY 应用代码)应放在文件末尾,减少缓存失效带来的构建时间增加。

    • 多个命令尽量合并为一个RUN(用&&连接),减少镜像层数:

      # 推荐:合并命令,清理缓存
      RUN yum update -y && \
          yum install -y gcc && \
          rm -rf /var/cache/yum/*
      
  4. 关键字(命令)大写字母

指令 作用与细节 常见误区
FROM 指定基础镜像(如 FROM centos:7FROM scratch 构建空镜像)。 必须是 Dockerfile 第一条指令;尽量使用官方精简镜像(如 alpine 版本)减小体积。
MAINTAINER 标注维护者信息(已过时,推荐用 LABEL)。 示例:LABEL maintainer="dev@example.com" version="1.0"
RUN 构建时执行命令(如安装软件、配置环境)。 避免单独使用 RUN yum update(无意义且增大镜像体积);及时清理缓存(如 apt clean)。
ADD 复制文件到镜像,支持自动解压本地 tar 包和下载网络文件。 不推荐用于下载网络文件(建议用 RUN wget 替代,更可控);避免解压不必要的文件。
COPY 仅复制本地文件到镜像,功能更纯粹(推荐优先使用)。 路径必须是构建上下文内的文件(不能用 ../ 访问外部文件)。
CMD 容器启动时执行的命令(可被 docker run 后的命令覆盖)。 一个 Dockerfile 中仅最后一个 CMD 生效;推荐使用数组格式:CMD ["nginx", "-g", "daemon off;"]
ENV 设置环境变量(如 ENV JAVA_HOME /usr/local/jdk),容器运行时可继承。 变量可在后续指令中引用:RUN echo $JAVA_HOME
EXPOSE 声明容器对外暴露的端口(仅文档作用,不实际映射)。 需配合 docker run -p 才能真正暴露端口;可声明多个端口:EXPOSE 80 443
VOLUME 定义匿名卷(持久化数据),避免容器删除时数据丢失。 运行时可通过 -v 覆盖为具名卷或绑定目录:docker run -v mydata:/data ...
WORKDIR 设置后续指令的工作目录(类似 cd,但更推荐,避免 RUN cd 的临时生效)。 可多次使用,路径相对于前一次的工作目录:WORKDIR /appWORKDIR config 最终为 /app/config

7.3构建自己的centos镜像

**核心流程:**编写 DockerFile → docker build 构建镜像 → docker run 运行容器 → docker push 发布镜像(到 DockerHub 或私有仓库)

因为centos镜像并没有vim指令,这里将vim指令加上去然后构建自己的镜像。

1,编写DockerFile

vim DockerFile
------------------编写---------------
FROM centos:7

#维护者信息
LABEL maintainer="saber@123456@qq.com"

#设置工作目录
ENV MYPATH /usr/local
WORKDIR &MYPATH

# 更换yum源为阿里云(解决官方源失效问题)
 RUN rm -f /etc/yum.repos.d/*.repo && \
     curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \
     yum clean all && \
     yum makecache
# 安装vim编辑器
RUN yum install -y vim && \
    yum clean all

#暴露端口
EXPOSE 80

CMD ["/bin/bash"]

在 Dockerfile 的 CMD ["/bin/bash"] 中,中括号 [] 的作用,第一个元素是要执行的命令,后续元素是该命令的参数。例如:

CMD ["echo", "Hello Docker"]

2,构建镜像

# 在 Dockerfile 所在目录执行,-t 指定镜像名称和标签
# . :指定 Dockerfile 所在的路径为当前目录。
docker build -t my-centos:with-vim .
#-f指定文件名称
docker build -f dockerfile-test-cmd -t cmd-test:0.1 .

3,运行容器

docker run -it --name my-centos01 my-centos:with-vim

测试,使用vim指令成功。

7.4部署SpringBoot项目

基于docker原生方式 部署我们的springboot项目。

1,导入jar包

位置Dockerfile 所在目录nginx-cross-study-1.0-SNAPSHOT.jar

如果是Dockerfile 所在目录下的app/nginx-cross-study-1.0-SNAPSHOT.jar,那下面就改成app/nginx-cross-study-1.0-SNAPSHOT.jar就可以。

2,配置dockerFile(这里的名字是springboot-test)

# 基础镜像:使用官方JDK 8(推荐使用openjdk,体积更小)
FROM openjdk:8-jdk-alpine

# 维护者信息
LABEL maintainer="saber"

# 临时目录挂载(Spring Boot 内嵌Tomcat默认使用/tmp作为工作目录)
VOLUME /tmp

# 将本地Jar包复制到容器中(重命名为app.jar,简化命令)
# 注意:Jar包路径需与Dockerfile相对应(若不在同一目录,需写相对路径)
COPY nginx-cross-study-1.0-SNAPSHOT.jar app.jar

# 修复容器内随机数生成慢的问题(加速Spring Boot启动)
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]

# 暴露项目端口(与Spring Boot配置的server.port一致,默认8080)
EXPOSE 8089

3,构建

docker build -f springboot-test -t springboot-test:nginx .

4,启动

docker run -p 8089:8089 -it --name springboot-test-nginx02 springboot-test:nginx

8,Docker网络

Docker 网络是 Docker 容器与外部世界(包括其他容器、主机、外部网络)通信的基础,它通过虚拟化技术为容器提供独立的网络环境,并支持多种网络模式以满足不同场景的需求。

8.1Docker0

docker0 是 Docker 在安装时自动创建的一个虚拟网桥(Bridge)设备,它是 Docker 容器默认网络模式(bridge 模式)下实现网络连接的关键组件。简而言之,docker0 主要作用是为容器提供与宿主机、容器与容器之间通信的基础网络能力

基础概念与原理

  • 虚拟网桥角色:从本质上来说,docker0 是一个基于 Linux 内核的虚拟网络设备,属于网桥类型。它类似于一个软件交换机,能够连接多个网络接口,在容器和宿主机之间,以及容器与容器之间转发数据包。当 Docker 创建一个容器时,会同时创建一对 veth-pair 接口(虚拟以太网对),其中一个接口放置在容器内作为 eth0(容器的网卡),另一个接口则连接到 docker0 网桥上,通过这种方式,容器就能够接入到 docker0 所构建的虚拟网络中。
  • IP 地址分配:docker0 默认会被分配一个本地未被占用的私有 IP 地址,例如常见的 172.17.42.1,子网掩码通常为 255.255.0.0 (对应网段为 172.17.0.0/16)。当容器以默认的 bridge 模式启动时,Docker 会从这个网段中为容器分配一个唯一的 IP 地址,从而使得容器之间、容器与宿主机之间能够基于 IP 进行通信。

什么是veth-pair?

它是 Linux 内核提供的一种虚拟网络设备技术,常用于在不同网络命名空间(Network Namespace)之间建立点对点的网络连接。

veth-pair 本质上是一对 “虚拟网卡”,它们像一根 “虚拟网线” 的两端。当创建一对 veth-pair 时,会生成两个虚拟网络接口(例如 veth0veth1)。其中一个接口发送的数据包,会被直接转发到另一个接口(反之亦然),就像物理网线连接的两个网卡。通常会将这两个接口分别放入不同的网络命名空间(如容器的网络命名空间和宿主机的命名空间),从而实现不同命名空间之间的网络互通。

veth-pair 是容器网络(如 Docker、Kubernetes)的核心技术之一,比如:

容器与宿主机的通信
Docker 容器默认运行在独立的网络命名空间中,通过 veth-pair 连接容器内的 eth0 接口和宿主机的虚拟接口(如 vethxxxx),并将宿主机侧的接口挂载到 docker0 网桥,使容器能与宿主机及其他容器通信。

示意图:

容器命名空间          宿主机命名空间
+------------+        +------------+
|  eth0      |<------>| vethxxxx   |
+------------+        +------------+
                          |
                          v
                     +------------+
                     | docker0 网桥 |
                     +------------+

在这里插入图片描述

通信机制

  • 容器与容器通信:处于同一 docker0 网桥上的容器,它们在网络层面处于同一个广播域。只要容器之间的防火墙等安全策略允许,容器可以直接通过对方的 IP 地址进行通信。比如,容器 A 的 IP 是 172.17.0.2,容器 B 的 IP 是 172.17.0.3,容器 A 就可以使用 ping 172.17.0.3 等方式与容器 B 通信,docker0 网桥会根据 MAC 地址表来转发数据包。
  • 容器与宿主机通信:容器通过 veth-pair 连接到 docker0,docker0 再通过宿主机的物理网卡与外部网络通信。同时,宿主机可以通过 iptables 等工具进行端口映射,将宿主机的端口映射到容器的端口上,从而实现从宿主机访问容器内的服务。例如,使用 docker run -p 8080:80 nginx 命令,就将宿主机的 8080 端口映射到了容器内的 80 端口,此时在宿主机上访问localhost:8080就相当于访问容器内的 Nginx 服务。
$ docker exec -it 容器id

$ ip addr
# 查看容器内部网络地址 发现容器启动的时候会得到一个 eth0@if551 ip地址,docker分配!
550: eth0@if551: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

# 思考? linux能不能ping通容器内部! 可以 容器内部可以ping通外界吗? 可以!
$ ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.074 ms

8.2通过名称通信

思考一个场景:我们编写了一个微服务,database url=ip: 项目不重启,数据ip换了,我们希望可以处理这个问题,可以通过名字来进行访问容器?

$ docker exec -it tomcat02 ping tomca01 # ping不通
ping: tomca01: Name or service not known
# 运行一个tomcat03 --link tomcat02
$ docker run -d -P --name tomcat03 --link tomcat02 tomcat
5f9331566980a9e92bc54681caaac14e9fc993f14ad13d98534026c08c0a9aef
# 用tomcat03 ping tomcat02 可以ping通
$ docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.115 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.080 ms
# 用tomcat02 ping tomcat03 ping不通

–-link 本质就是在hosts配置中添加映射,现在使用Docker已经不建议使用–link了!

–link劣势:

  • 但这种方式是单向的(tomcat03 能访问 tomcat02,反之不行)
  • 不支持动态更新,当被链接的容器 IP 变化时,链接方不会自动更新
  • 扩展性差,不适合多容器复杂网络环境

建议自定义网络,不适用docker0!docker0问题:不支持容器名连接访问!

8.3自定义网络

Docker 容器的网络连接基于 Linux 桥接技术,宿主机的 docker0 网桥是容器网络的核心枢纽,而 veth-pair 技术则像 “连接线” 一样,将每个容器接入到这个网桥上,从而实现了容器与宿主机、容器与容器之间的网络互通。这种设计保证了容器网络的隔离性与连通性的平衡。

所有容器不指定网络的情况下,默认docker路由,接着docker给容器分配ip。

网络模式

bridge :桥接 docker(默认,自己创建也是用bridge模式)

none :容器启动时不配置任何网络,没有网卡、IP、路由,仅保留lo(本地回环)接口,完全与网络隔离。

  • 极致隔离,容器内无法访问外部网络,外部也无法访问容器。
  • 仅能通过docker exec在容器内部操作,适合纯本地计算场景(如数据处理脚本,无需网络交互)。

host :和所主机共享网络。容器不创建独立网络命名空间,直接共享宿主机的网络栈(使用宿主机的 IP、端口、路由等)。容器内的服务端口直接占用宿主机端口,无需端口映射。

  • 网络性能最优(无网桥转发开销),但完全没有网络隔离(容器端口与宿主机端口冲突风险高)。
  • 容器内的localhost指向宿主机的localhost
  • 对网络性能要求极高的场景(如高频通信的服务)

container :容器联网模式(用得少!局限很大)新容器不创建独立网络命名空间,而是共享另一个已存在容器的网络栈(IP、端口、网卡等均与被共享容器一致)。两个容器通过lo接口直接通信,外部网络访问需通过被共享容器的端口映射。

# 我们直接启动的命令,会有一个默认参数 --net bridge,而这个就是docker0
# bridge就是docker0
$ docker run -d -P --name tomcat01 tomcat
等价于 => docker run -d -P --name tomcat01 --net bridge tomcat

# docker0,特点:默认,域名不能访问。 --link可以打通连接,但是很麻烦!
# 我们可以 自定义一个网络
$ docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network create \
  --driver bridge \          # 指定网络驱动类型为桥接模式
  --subnet 192.168.0.0/16 \  # 指定网络的子网网段
  --gateway 192.168.0.1 \    # 指定网络的网关地址
  mynet                      # 自定义网络的名称

如果不指定 --gateway,Docker 会自动分配一个网关 IP(通常是网段的第一个可用 IP,如 192.168.0.1),并非 “没有网关”。

#查看docker网络
docker network ls

#查看自己创建的网络
docker network inspect mynet;

--------------测试----------
[root@VM-8-4-centos ~]# docker run -d --name tomcat-net-01 --network mynet tomcat
9d71fc7db5c7abb118bffab1dbb170a574b59a4934daf879901989d2bd78e6e6
[root@VM-8-4-centos ~]# docker run -d --name tomcat-net-02 --network mynet tomcat
e77c412dd11aa1578a59860bf2f6032d0065d18b7baa77ebdf0af067d0862b61
[root@VM-8-4-centos ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "096589a184cc8eb875beb1a42a18fdd17d2591fd5a86e08c569d07e1cfe89ab8",
        "Created": "2025-08-01T16:26:08.811562225+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/24",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "9d71fc7db5c7abb118bffab1dbb170a574b59a4934daf879901989d2bd78e6e6": {
                "Name": "tomcat-net-01",
                "EndpointID": "f66e15c145d261441f1ac5c6e980050bb081f93e6ca78514072fa0e6a0585410",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/24",
                "IPv6Address": ""
            },
            "e77c412dd11aa1578a59860bf2f6032d0065d18b7baa77ebdf0af067d0862b61": {
                "Name": "tomcat-net-02",
                "EndpointID": "44903cc353e977dbf4dbb590cc1c84a6e4f549e587359b7c0005c0bfc83f789e",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/24",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
---------------tomcat镜像(尤其是官方精简版)默认没有安装ping命令-----------
#进入容器内部
#exec用于在运行中的容器内部执行指定命令。
docker exec -it tomcat-net-01 bash

# 更新apt源(首次安装需要)
apt-get update

# 安装ping工具
apt-get install -y iputils-ping

# 在tomcat-net-01容器内ping tomcat-net-02
ping tomcat-net-02

在自定义的网络下,服务可以互相ping通,不用使用–link。自定义网络(如bridge驱动的网络)会自动维护一个 DNS 解析服务,同一网络内的容器无需配置,即可通过容器名(或--name指定的名称)直接通信。

我们自定义的网络docker当我们维护好了对应的关系,推荐我们平时这样使用网络!

好处:

redis -不同的集群使用不同的网络,保证集群是安全和健康的

mysql-不同的集群使用不同的网络,保证集群是安全和健康的

8.4跨网络通信

默认情况下,一个容器只能属于它启动时指定的网络(通过 --network 参数)。而 docker network connect 可以让容器额外加入其他网络,实现:

  • 同一个容器与多个网络中的服务通信
  • 跨网络的容器间互联互通(突破默认的网络隔离)

基础用法

docker network connect [网络名/网络ID] [容器名/容器ID]
docker network disconnect [网络名/网络ID] [容器名/容器ID]

9,Compose

9.1Compose的安装

安装Docker Compose

github太慢了,该指令源自38-docker compose 是什么以及如何安装_哔哩哔哩_bilibili

curl -SL https://bmshare.oss-cn-beijing.aliyuncs.com/docker/docker-compose/v2.29.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

docker-compose 文件添加可执行权限

chmod +x /usr/local/bin/docker-compose

检测是否安装成功,如果输出版本信息证明安装成功。

docekr-compose -v

9.2docker-compose.yml

docker-compose.yml 是用于定义和运行多容器 Docker 应用的配置文件,其语法基于 YAML,核心是通过 servicesnetworksvolumes 等关键字定义应用的各个组件。

# 可选:指定 Compose 文件版本(v2/v3 是主流,v3.8 是较新稳定版)
# 注意:最新 Docker 版本已淡化版本差异,可省略
version: '3.8'

# 定义所有服务(容器)
services:
  服务1名称:
    # 服务1的配置...
  服务2名称:
    # 服务2的配置...

# 定义网络(可选,用于服务间通信)
networks:
  网络名称:
    # 网络配置...

# 定义数据卷(可选,用于数据持久化)
volumes:
  卷名称:
    # 卷配置...

每个服务对应一个容器,常用配置如下:

1. 镜像相关

services:
  app:
    image: nginx:alpine  # 直接使用现有镜像(名称:标签)
    # 或通过 Dockerfile 构建镜像(二选一)
    build:
      context: ./app     # Dockerfile 所在目录
      dockerfile: Dockerfile  # 自定义 Dockerfile 文件名(默认 Dockerfile)
      args:              # 构建时参数(对应 Dockerfile 中的 ARG)
        - ENV=production

2. 容器配置

services:
  app:
    container_name: my-app  # 自定义容器名称(默认:项目名_服务名_序号)
    restart: always         # 重启策略:always(总是)、on-failure(失败时)、no(默认)
    command: ["nginx", "-g", "daemon off;"]  # 覆盖容器启动命令
    entrypoint: /app/start.sh  # 覆盖容器入口点

3. 网络配置

services:
  app:
    networks:
      - my-network  # 加入自定义网络(需在 networks 中定义)
    ports:
      - "8080:80"   # 端口映射:宿主机端口:容器内端口(格式:[宿主机IP:]宿主机端口:容器端口)
      - "9000-9002:9000-9002"  # 端口范围映射
    expose:
      - 8080  # 暴露容器端口(仅允许内部网络访问,不映射到宿主机)

4. 环境变量

services:
  app:
    environment:  # 直接指定环境变量(键值对)
      - DB_HOST=mysql
      - DB_PORT=3306
    env_file:     # 从文件加载环境变量(每行格式:KEY=VALUE)
      - .env      # 相对路径或绝对路径

5. 数据持久化(卷挂载)

services:
  app:
    volumes:
      - ./data:/app/data  # 绑定挂载:宿主机目录:容器内目录(双向同步)
      - my-volume:/app/logs  # 命名卷:使用 volumes 中定义的卷(数据持久化)
      - /app/temp  # 匿名卷:仅容器内使用,容器删除后数据丢失

9.3部署若依项目

(1)Linux目录

shuangchauang/
├── docker-compose.yml       # 容器编排主配置
├── backend/                 # 后端服务
│   ├── Dockerfile           # 后端镜像构建文件
│   └── xxx.jar       # Spring Boot 打包的 JAR
├── frontend/                # 前端服务
│   ├── Dockerfile           # 前端镜像构建文件
│   └── dist/                # 前端打包好的静态文件(Vue/React 等)
├── nginx/                   # Nginx 配置
│   ├── Dockerfile           # Nginx 镜像构建文件
│   └── nginx.conf           # Nginx 反向代理配置
├── mysql/                   # MySQL 配置
│   └── init.sql             # 数据库初始化脚本
└── redis/                   # Redis 自定义配置
    └── redis.conf           # Redis 自定义配置文件怎么改变

(2)相关命令

--------------防火墙相关-------
# 临时开放80端口(重启防火墙后失效)
sudo firewall-cmd --zone=public --add-port=80/tcp

# 永久开放80端口(需要重新加载规则)
sudo firewall-cmd --zone=public --add-port=80/tcp --permanent

# 重新加载防火墙规则,使设置生效
sudo firewall-cmd --reload

# 验证是否已开放
sudo firewall-cmd --zone=public --list-ports | grep 80

#查看相应端口占用情况
sudo netstat -tulpn | egrep '80|3306|6379|8079'
----------docker-compose相关-----------
#停止并删除所有通过当前docker-compose.yml启动的容器、网络,同时保留数据卷
docker-compose down

#第一次启动,构建并启动
docker-compose up -d

# 重新构建并启动
docker-compose up -d --build

#检查容器状态
docker-compose ps

#eg:打印后端日志
docker-compose logs backend

#只启动mysql服务
docker-compose up -d mysql
docker-compose down mysql

(3)配置文件

解析

后端的application.yml连接的数据库ip地址换成mysql和redis.

不需要配置ip的原因:Docker的容器间通信(在docker-compose.yml中也不需要配置端口映射)

# redis 配置
redis:
  # 地址
  # 地址:使用 Docker 网络中的 Redis 服务名
  host: redis
  # 端口,默认为6379
  port: 6379
  # 数据库索引
  database: 0
  # 密码
  password:
  # 连接超时时间
  timeout: 10s
----------------------------
url: jdbc:mysql://mysql:3306/subsystem useUnicode=true&charac..........
    username: root
    password: 220314

流程

  1. 请求进入宿主机
    外部请求(如 http://49.232.7.34:8090)首先到达宿主机的 8090 端口,该端口通过 docker-compose.yml 中 Nginx 服务的 ports: "8090:8090" 映射到 Nginx 容器的 8090 端口。
  2. Docker 端口转发
    Docker 守护进程(docker daemon)负责端口映射,将宿主机 8090 端口的请求转发到 Nginx 容器内部的 8090 端口。
  3. Nginx 处理请求
    • 若请求是静态资源(如 index.htmlcssjs),Nginx 直接从容器内的前端静态目录(如 /usr/share/nginx/html)返回文件。
    • 若请求是 API 接口(如 /prod-api/user/list),Nginx 通过配置 proxy_pass http://backend:8079/ 将请求转发到后端容器。
  4. 后端容器处理业务
    后端容器(scweb-backend)接收到请求后:
    • 通过 jdbc:mysql://mysql:3306/... 连接 MySQL 容器(scweb-mysql)读写数据。
    • 通过 spring.redis.host=redis 连接 Redis 容器(scweb-redis)操作缓存。
  5. 响应返回
    后端处理结果经 Nginx 反向代理返回给外部客户端,完成一次请求。

在这里插入图片描述

原配置

frontend 服务本质是前端打包环境(通过 Dockerfile 构建 dist 目录),但最终前端静态文件是通过 Nginx 容器直接提供服务的:

  • 目前前端 dist 目录的文件是通过挂载到 nginx 容器的(/usr/share/nginx/html),frontend 容器本身并不参与请求处理。
  • frontend 容器启动后,既没有暴露端口,也没有被其他服务依赖,属于 “闲置容器”,不影响整体架构运行。

故可以删除相关配置。后者一个一个的启动,留下frontend,正常运行。

docker-compsoe.yml

services:
  # 后端服务(Spring Boot)
  backend:
    build: ./backend
    container_name: scweb-backend
    restart: always
    environment:
      # 后端连接MySQL和Redis的配置(需与后端application.yml一致)
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/subsystem?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: 220314
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PORT: 6379
      SPRING_REDIS_PASSWORD:  # 与Redis配置的密码一致
    depends_on:
      - mysql
      - redis
    networks:
      - scweb-network

  # 前端服务(Nginx托管静态静态文件)
  frontend:
    build: ./frontend
    container_name: scweb-frontend
    restart: always
    depends_on:
      - backend
    networks:
      - scweb-network

  # Nginx反向代理(处理前端请求和API转发)
  nginx:
    build: ./nginx
    container_name: scweb-nginx
    restart: always
    ports:
      - "8090:8090"  # 对外暴露80端口
    depends_on:
      - frontend
      - backend
    networks:
      - scweb-network
    # 新增:挂载前端 dist 目录到容器内的 Nginx 静态目录
    volumes:
      - ./frontend/dist:/usr/share/nginx/html  # 宿主机 dist 目录映射到容器内目录

  # MySQL数据库
  mysql:
    image: mysql:8.0
    container_name: scweb-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 220314
      MYSQL_DATABASE: subsystem
      MYSQL_PASSWORD: 220314
    volumes:
      - ./mysql/subsystem.sql:/docker-entrypoint-initdb.d/init.sql
      - mysql-data:/var/lib/mysql
    networks:
      - scweb-network
    ports:
      - "3306:3306" 
    command: --default-authentication-plugin=mysql_native_password

  # Redis缓存(带自定义配置)
  redis:
    image: redis:6.2-alpine
    container_name: scweb-redis
    restart: always
    volumes:
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf  # 挂载自定义配置
      - redis-data:/data  # 数据持久化
    command: redis-server /usr/local/etc/redis/redis.conf  # 使用自定义配置启动
    networks:
      - scweb-network
    # 添加端口映射:宿主机端口:容器端口
    ports:
      - "6379:6379"

# 数据卷(持久化数据)
volumes:
  mysql-data:
  redis-data:

# 自定义网络(服务间通信)
networks:
  scweb-network:
    driver: bridge

backend–Dockerfile

FROM nginx:alpine
# 替换Nginx默认配置为自定义配置
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 8090

frontend–Dockerfile

FROM nginx:alpine
# 将前端打包的dist目录复制到Nginx默认静态文件目录
COPY dist/ /usr/share/nginx/html/
# 暴露80端口(内部端口,通过nginx服务转发)
EXPOSE 80

nginx–Dockerfile

FROM nginx:alpine
# 替换Nginx默认配置为自定义配置
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 8090

nginx.conf

server {
    listen 8090;
    server_name xx.xxx.x.xx;  # 替换为你的服务器公网IP或域名

    # 双创项目前端 - 主配置
    location / {
        root   /usr/share/nginx/html;
        index  index.html;
        try_files $uri $uri/ /index.html;
    }

    # 双创项目后端API代理
    location /prod-api/ {
        proxy_pass http://backend:8079/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐