Docker学习其二(容器卷,Docker网络,Compose)
文章目录
- 6,数据容器卷
- 6.0容器的理解
- 6.1容器数据卷
- 6.2常用操作命令
- 6.3具名&匿名&绑定挂载
- 6.4容器共享数据卷
- 6.5实例:Mysql数据卷
- 7,DockerFile
- 7.1概述
- 7.2编写规范&指令
- 7.3构建自己的centos镜像
- 7.4部署SpringBoot项目
- 8,Docker网络
- 8.1Docker0
- 基础概念与原理
- 通信机制
- 8.2通过名称通信
- 8.3自定义网络
- 8.4跨网络通信
- 9,Compose
- 9.1Compose的安装
- 9.2docker-compose.yml
- 9.3部署若依项目
- (1)Linux目录
- (2)相关命令
- (3)配置文件
- 解析
- 原配置
6,数据容器卷
6.0容器的理解
在非运行态(静态)下,容器可以看作是多个镜像层加上一个可写层的集合。
可写层的定义
可写层是在基于镜像创建容器时,Docker 自动添加到容器最上层的一个特殊文件系统层。它是容器中唯一可进行写入操作的层,而其下方的镜像层(Read Layer) 都是只读的。
可写层的作用
- 保存容器运行时的修改:当容器运行时,对文件系统进行的任何操作,比如创建新文件、修改现有文件内容、删除文件等,这些更改都不会影响到下方只读的镜像层,而是被记录在可写层中。例如,在一个基于
centos
镜像创建的容器里,使用touch
命令创建一个新文件,该文件就会被创建在可写层;如果修改了镜像层中已有的文件,那么 Docker 会使用写时复制(Copy - on - Write,COW)技术,先将该文件从镜像层复制到可写层,然后在可写层中进行修改 。 - 维持容器状态:可写层使得每个容器都能拥有独立于镜像以及其他容器的状态。即使多个容器基于同一个镜像创建,它们各自的可写层也互不干扰,保证了容器数据的独立性和隔离性。
6.1容器数据卷
数据容器卷(Data Volume)是一种用于持久化存储容器数据的机制,它可以独立于容器的生命周期存在,方便数据共享和持久化。
数据容器卷的核心特点
- 数据卷可以在容器之间共享和重用
- 数据卷数据会持久化,即使删除使用它的容器
- 可以直接对数据卷中的数据进行修改
- 数据卷的变化不会影响镜像的更新
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 新容器镜像
- 作用:新容器会继承 “源容器” 中所有的卷配置(包括挂载路径和数据)。
- 特点
- 无需手动指定卷名或路径,直接复用源容器的卷配置。
- 源容器可以是运行中或已停止的(只要未被删除)。
- 共享的是卷本身,数据会在所有复用该卷的容器间实时同步。
注意事项
- 源容器必须存在:
--volumes-from
依赖源容器的元数据(即使源容器已停止),如果源容器被删除,新容器会挂载失败。 - 卷的生命周期独立:复用卷的容器删除后,卷本身不会被删除(需用
docker volume rm
手动删除)。 - 避免多写冲突:多个容器共享卷时,若同时写入同一文件可能导致数据损坏(如 MySQL 等数据库不支持多实例同时写同一数据文件)。
- 权限问题:不同容器的用户 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_conf
和 mysql_data
) 来持久化数据,即使删除了 mysql01
容器,重新创建新容器时只要挂载相同的卷,数据依然会保留。
如何实现多容器mysql数据共享?
docker run -d \-p 3308:3306 \-e MYSQL_ROOT_PASSWORD=123456 \--name mysql04 \mysql:5.7docker 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
共享数据卷时,必须确保源容器已停止。
那如何实现?
- 通过 MySQL 自身的 主从复制(Master-Slave) 机制,实现数据自动同步,多个容器各自拥有独立的数据文件,但数据保持一致。
- 多容器独立运行,无文件锁冲突
- 主库写入,从库可读,可分担查询压力
- 数据自动同步,延迟低
- 如果是在云环境中,可直接使用 云数据库服务(如 RDS),多个 Docker 容器通过网络连接到同一个数据库实例,无需关心数据文件共享。
这里不再先说mysql的主从复制。
7,DockerFile
7.1概述
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。通过定义一系列命令和参数,Dockerfile 指导 Docker 构建一个自定义的镜像。
7.2编写规范&指令
-
注释格式
使用
#
开头 -
指令大小写
虽然 Docker 对指令大小写不敏感,但行业规范要求指令必须大写(如FROM
、RUN
),参数小写,便于区分指令和内容。 -
执行顺序与缓存
Docker 构建镜像时会按指令顺序从上到下顺序执行,且会缓存每一层的结果。如果某层指令未修改,下次构建会直接使用缓存。因此:-
频繁变动的指令(如复制 COPY 应用代码)应放在文件末尾,减少缓存失效带来的构建时间增加。
-
多个命令尽量合并为一个RUN(用&&连接),减少镜像层数:
# 推荐:合并命令,清理缓存 RUN yum update -y && \yum install -y gcc && \rm -rf /var/cache/yum/*
-
-
关键字(命令)大写字母
指令 | 作用与细节 | 常见误区 |
---|---|---|
FROM | 指定基础镜像(如 FROM centos:7 或 FROM 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 /app → WORKDIR 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 80CMD ["/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 时,会生成两个虚拟网络接口(例如 veth0
和 veth1
)。其中一个接口发送的数据包,会被直接转发到另一个接口(反之亦然),就像物理网线连接的两个网卡。通常会将这两个接口分别放入不同的网络命名空间(如容器的网络命名空间和宿主机的命名空间),从而实现不同命名空间之间的网络互通。
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,核心是通过 services
、networks
、volumes
等关键字定义应用的各个组件。
# 可选:指定 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=3306env_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# 端口,默认为6379port: 6379# 数据库索引database: 0# 密码password:# 连接超时时间timeout: 10s
----------------------------
url: jdbc:mysql://mysql:3306/subsystem useUnicode=true&charac..........username: rootpassword: 220314
流程
- 请求进入宿主机
外部请求(如http://49.232.7.34:8090
)首先到达宿主机的8090
端口,该端口通过docker-compose.yml
中 Nginx 服务的ports: "8090:8090"
映射到 Nginx 容器的8090
端口。 - Docker 端口转发
Docker 守护进程(docker daemon
)负责端口映射,将宿主机8090
端口的请求转发到 Nginx 容器内部的8090
端口。 - Nginx 处理请求
- 若请求是静态资源(如
index.html
、css
、js
),Nginx 直接从容器内的前端静态目录(如/usr/share/nginx/html
)返回文件。 - 若请求是 API 接口(如
/prod-api/user/list
),Nginx 通过配置proxy_pass http://backend:8079/
将请求转发到后端容器。
- 若请求是静态资源(如
- 后端容器处理业务
后端容器(scweb-backend
)接收到请求后:- 通过
jdbc:mysql://mysql:3306/...
连接 MySQL 容器(scweb-mysql
)读写数据。 - 通过
spring.redis.host=redis
连接 Redis 容器(scweb-redis
)操作缓存。
- 通过
- 响应返回
后端处理结果经 Nginx 反向代理返回给外部客户端,完成一次请求。
原配置
frontend
服务本质是前端打包环境(通过 Dockerfile
构建 dist
目录),但最终前端静态文件是通过 Nginx 容器直接提供服务的:
- 目前前端
dist
目录的文件是通过挂载到nginx
容器的(/usr/share/nginx/html
),frontend
容器本身并不参与请求处理。 frontend
容器启动后,既没有暴露端口,也没有被其他服务依赖,属于 “闲置容器”,不影响整体架构运行。
故可以删除相关配置。后者一个一个的启动,留下frontend,正常运行。
docker-compsoe.yml
services:# 后端服务(Spring Boot)backend:build: ./backendcontainer_name: scweb-backendrestart: alwaysenvironment:# 后端连接MySQL和Redis的配置(需与后端application.yml一致)SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/subsystem?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8SPRING_DATASOURCE_USERNAME: rootSPRING_DATASOURCE_PASSWORD: 220314SPRING_REDIS_HOST: redisSPRING_REDIS_PORT: 6379SPRING_REDIS_PASSWORD: # 与Redis配置的密码一致depends_on:- mysql- redisnetworks:- scweb-network# 前端服务(Nginx托管静态静态文件)frontend:build: ./frontendcontainer_name: scweb-frontendrestart: alwaysdepends_on:- backendnetworks:- scweb-network# Nginx反向代理(处理前端请求和API转发)nginx:build: ./nginxcontainer_name: scweb-nginxrestart: alwaysports:- "8090:8090" # 对外暴露80端口depends_on:- frontend- backendnetworks:- scweb-network# 新增:挂载前端 dist 目录到容器内的 Nginx 静态目录volumes:- ./frontend/dist:/usr/share/nginx/html # 宿主机 dist 目录映射到容器内目录# MySQL数据库mysql:image: mysql:8.0container_name: scweb-mysqlrestart: alwaysenvironment:MYSQL_ROOT_PASSWORD: 220314MYSQL_DATABASE: subsystemMYSQL_PASSWORD: 220314volumes:- ./mysql/subsystem.sql:/docker-entrypoint-initdb.d/init.sql- mysql-data:/var/lib/mysqlnetworks:- scweb-networkports:- "3306:3306" command: --default-authentication-plugin=mysql_native_password# Redis缓存(带自定义配置)redis:image: redis:6.2-alpinecontainer_name: scweb-redisrestart: alwaysvolumes:- ./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;}
}