Docker学习其二(容器卷,Docker网络,Compose)

文章目录

6,数据容器卷

6.0容器的理解

在非运行态(静态)下,容器可以看作是多个镜像层加上一个可写层的集合。

可写层的定义

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

可写层的作用

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

6.1容器数据卷

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

数据容器卷的核心特点

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

6.2常用操作命令

创建数据卷

bash 复制代码
docker volume create mydata

查看所有数据卷

bash 复制代码
docker volume ls

查看数据卷详情

bash 复制代码
docker volume inspect mydata

使用数据卷运行容器

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

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

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

使用匿名卷

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

删除数据卷

bash 复制代码
# 删除未被使用的数据卷
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 中用于在容器之间共享数据卷的命令参数,它允许一个容器复用用另一个容器中定义的卷(包括匿名名卷、具名卷或绑定挂载的目录),实现容器间的数据共享。

基本用法

bash 复制代码
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,构建镜像

bash 复制代码
# 在 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 网桥,使容器能与宿主机及其他容器通信。

示意图:

plaintext 复制代码
容器命名空间          宿主机命名空间
+------------+        +------------+
|  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. 镜像相关

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

2. 容器配置

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

3. 网络配置

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

4. 环境变量

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

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

yaml 复制代码
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;
    }
}
相关推荐
心一信息27 分钟前
如何在Ubuntu上部署excalidraw
linux·运维·ubuntu
人生匆匆41 分钟前
linux ext4缩容home,扩容根目录
linux·运维·服务器
鱼骨不是鱼翅41 分钟前
自动化框架pytest
运维·自动化·pytest
-Xie-1 小时前
JVM学习日记(十三)Day13
jvm·学习
IT成长日记1 小时前
【自动化运维神器Ansible】YAML支持的数据类型详解:构建高效Playbook的基石
运维·自动化·ansible·数据类型·yaml·playbook
IT成长日记1 小时前
【自动化运维神器Ansible】YAML语法详解:Ansible Playbook的基石
运维·自动化·ansible·yaml
潘多编程2 小时前
云原生三剑客:Kubernetes + Docker + Spring Cloud 实战指南与深度整合
docker·云原生·kubernetes
Hello_Embed2 小时前
嵌入式 C 语言入门:循环结构学习笔记 —— 从语法到实用技巧
c语言·笔记·stm32·学习
来自于狂人3 小时前
CentOS 镜像源配置与 EOL 后的应对策略
linux·运维·centos