概述
微服务拆完之后,代码写得再漂亮也会卡在最后一步:怎么把十几个服务、MySQL、Nacos、Redis 一起塞到服务器上跑起来。Docker 这一章就是解决这个问题的,本文是整章的导引,先把地图摊开,再一站一站走。
纲要
- 为什么后端工程师躲不开 Docker:环境一致性、交付形态、微服务部署三笔账
- 本章知识地图:初识 Docker → 基本操作 → 自定义镜像 → DockerCompose → 镜像仓库
- 每一站解决什么问题、对应哪些命令
- 按阶段分组的命令速查表(后续各篇的索引)
- 学习建议:哪些必须动手跑一遍,哪些可以先理解后补
为什么后端必须会 Docker
先看一个几乎每个人都遇到过的场景。本地 mvn package 打出来的 xxx.jar,在自己机器上 java -jar 跑得好好的,扔到测试服务器上直接报错:
text
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter
代码一行没动。差别在于本地是 JDK 8,服务器上是 JDK 11,javax.xml.bind 在 9 之后被移出 JDK 了。这就是依赖兼容问题:同一个 jar,换一套 JDK、换一套 MySQL 客户端库、换一个 glibc 版本,就活了或者死了。
第二笔账是操作系统环境差异 。运维给你的机器装了什么内核参数、时区设成 UTC 还是 Asia/Shanghai、有没有那个特定的 .so 动态库,你控制不了。出了问题排查链路长得离谱,最后发现是服务器 locale 不是 UTF-8,日志里中文全是问号。
第三笔账是部署成本。SpringCloud 里一个业务拆成三四份服务,加上注册中心、网关、数据库,手动部署的流程是:装 JDK、装 MySQL、传 jar、写启动脚本、配开机自启......每台机器来一遍,新同学入职配环境一天就没了。
Docker 的思路很直接:把应用和它需要的一切依赖打包成一个标准单元,从开发机到测试机到生产机,跑的都是同一个东西。环境差异被关进容器里,交付物从"一堆 jar + 一份部署文档"变成"一个镜像"。
#mermaid-svg-cEAIQG7PFzVb3Nt6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .error-icon{fill:#552222;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .marker.cross{stroke:#333333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 p{margin:0;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .cluster-label text{fill:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .cluster-label span{color:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .cluster-label span p{background-color:transparent;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .label text,#mermaid-svg-cEAIQG7PFzVb3Nt6 span{fill:#333;color:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .node rect,#mermaid-svg-cEAIQG7PFzVb3Nt6 .node circle,#mermaid-svg-cEAIQG7PFzVb3Nt6 .node ellipse,#mermaid-svg-cEAIQG7PFzVb3Nt6 .node polygon,#mermaid-svg-cEAIQG7PFzVb3Nt6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .rough-node .label text,#mermaid-svg-cEAIQG7PFzVb3Nt6 .node .label text,#mermaid-svg-cEAIQG7PFzVb3Nt6 .image-shape .label,#mermaid-svg-cEAIQG7PFzVb3Nt6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .rough-node .label,#mermaid-svg-cEAIQG7PFzVb3Nt6 .node .label,#mermaid-svg-cEAIQG7PFzVb3Nt6 .image-shape .label,#mermaid-svg-cEAIQG7PFzVb3Nt6 .icon-shape .label{text-align:center;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .node.clickable{cursor:pointer;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .arrowheadPath{fill:#333333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cEAIQG7PFzVb3Nt6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cEAIQG7PFzVb3Nt6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cEAIQG7PFzVb3Nt6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .cluster text{fill:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .cluster span{color:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cEAIQG7PFzVb3Nt6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .icon-shape,#mermaid-svg-cEAIQG7PFzVb3Nt6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .icon-shape p,#mermaid-svg-cEAIQG7PFzVb3Nt6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .icon-shape .label rect,#mermaid-svg-cEAIQG7PFzVb3Nt6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cEAIQG7PFzVb3Nt6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cEAIQG7PFzVb3Nt6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cEAIQG7PFzVb3Nt6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 微服务拆得越多
部署越麻烦
依赖兼容问题
JDK/库版本不一致
操作系统差异
内核/时区/locale
部署流程重复
每台机器手工装环境
Docker: 应用+依赖一起打包
镜像作为标准交付物
一次构建, 到处运行
本章知识地图
整章是一条从"认识"到"用起来"再到"管起来"的线,五站走完就能自己部署一套微服务集群:
#mermaid-svg-SMadyRrVyEG0BgRQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SMadyRrVyEG0BgRQ .error-icon{fill:#552222;}#mermaid-svg-SMadyRrVyEG0BgRQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SMadyRrVyEG0BgRQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SMadyRrVyEG0BgRQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SMadyRrVyEG0BgRQ .marker.cross{stroke:#333333;}#mermaid-svg-SMadyRrVyEG0BgRQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SMadyRrVyEG0BgRQ p{margin:0;}#mermaid-svg-SMadyRrVyEG0BgRQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ .cluster-label text{fill:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ .cluster-label span{color:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ .cluster-label span p{background-color:transparent;}#mermaid-svg-SMadyRrVyEG0BgRQ .label text,#mermaid-svg-SMadyRrVyEG0BgRQ span{fill:#333;color:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ .node rect,#mermaid-svg-SMadyRrVyEG0BgRQ .node circle,#mermaid-svg-SMadyRrVyEG0BgRQ .node ellipse,#mermaid-svg-SMadyRrVyEG0BgRQ .node polygon,#mermaid-svg-SMadyRrVyEG0BgRQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SMadyRrVyEG0BgRQ .rough-node .label text,#mermaid-svg-SMadyRrVyEG0BgRQ .node .label text,#mermaid-svg-SMadyRrVyEG0BgRQ .image-shape .label,#mermaid-svg-SMadyRrVyEG0BgRQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-SMadyRrVyEG0BgRQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SMadyRrVyEG0BgRQ .rough-node .label,#mermaid-svg-SMadyRrVyEG0BgRQ .node .label,#mermaid-svg-SMadyRrVyEG0BgRQ .image-shape .label,#mermaid-svg-SMadyRrVyEG0BgRQ .icon-shape .label{text-align:center;}#mermaid-svg-SMadyRrVyEG0BgRQ .node.clickable{cursor:pointer;}#mermaid-svg-SMadyRrVyEG0BgRQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SMadyRrVyEG0BgRQ .arrowheadPath{fill:#333333;}#mermaid-svg-SMadyRrVyEG0BgRQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SMadyRrVyEG0BgRQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SMadyRrVyEG0BgRQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SMadyRrVyEG0BgRQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SMadyRrVyEG0BgRQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SMadyRrVyEG0BgRQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SMadyRrVyEG0BgRQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SMadyRrVyEG0BgRQ .cluster text{fill:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ .cluster span{color:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SMadyRrVyEG0BgRQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SMadyRrVyEG0BgRQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-SMadyRrVyEG0BgRQ .icon-shape,#mermaid-svg-SMadyRrVyEG0BgRQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SMadyRrVyEG0BgRQ .icon-shape p,#mermaid-svg-SMadyRrVyEG0BgRQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SMadyRrVyEG0BgRQ .icon-shape .label rect,#mermaid-svg-SMadyRrVyEG0BgRQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SMadyRrVyEG0BgRQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SMadyRrVyEG0BgRQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SMadyRrVyEG0BgRQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 初识 Docker
是什么 / 与虚拟机差别
架构 / 安装
② 基本操作
镜像命令 / 容器命令
数据卷
③ 自定义镜像
镜像结构 / Dockerfile
④ DockerCompose
一键部署多容器
⑤ 镜像仓库
私有仓库 / 推送拉取
| 阶段 | 解决什么问题 | 学完能做什么 | 代表命令 |
|---|---|---|---|
| 初识 Docker | 搞清楚镜像、容器、仓库三者的关系,明白它和虚拟机不是一回事 | 在自己的机器或服务器上把 Docker 装起来并验证 | docker -v、systemctl start docker |
| 基本操作 | 会找镜像、会跑容器、会管理容器里的数据 | 用几条命令把 MySQL、Nginx 跑起来并改配置 | docker pull、docker run、docker exec、docker volume |
| 自定义镜像 | 把写好的 jar 变成可分发的镜像 | 手写 Dockerfile,把微服务打成镜像 | docker build、FROM、COPY、ENTRYPOINT |
| DockerCompose | 十几个容器一个个 run 太痛苦 |
写一个 docker-compose.yml,一条命令拉起整个集群 |
docker compose up -d |
| 镜像仓库 | 镜像越来越多,需要统一存放和分发 | 搭私有仓库,团队内推送、拉取镜像 | docker tag、docker push、docker pull |
第一站:初识 Docker
要回答的问题只有三个------它是什么、它和虚拟机差在哪、它是怎么组织起来干活的。镜像 是只读的模板,容器 是镜像跑起来的实例,DockerHub 是公共的镜像仓库,这三者构成了 Docker 的骨架。另外要理解 Docker 采用 C/S 架构:客户端发指令,dockerd 守护进程负责干活。
和虚拟机的差别是面试高频点,也是理解性能的关键。虚拟机靠 Hypervisor 模拟出一套完整的硬件,每台虚机里跑一个完整的操作系统内核;容器只是宿主机上被 Namespace 隔离、Cgroups 限制的一组进程,共享宿主内核。所以容器启动是秒级甚至毫秒级,虚机是分钟级。
这一站的核心是先把环境装好。装不上的话后面全是空谈。
第二站:基本操作
三块内容,按依赖顺序学:
镜像命令 ------docker pull 拉取、docker images 查看、docker save/docker load 在离线环境里搬运、docker rmi 删除。镜像名的格式要记住:[repository]:[tag],不写 tag 默认是 latest。
容器命令 ------docker run 创建并启动、docker exec 进入容器改文件、docker ps 看运行状态、docker logs 排错、docker stop/start/rm 管理生命周期。容器有三个状态:运行、暂停(进程挂起但内存不释放)、停止(资源回收)。
数据卷 ------这是最容易踩坑的一站。容器删了,里面写的数据也就没了;而且配置文件每次都要 exec 进去手改,改完重启又还原。数据卷把宿主机的目录挂载到容器内路径,解决两件事:数据持久化和配置的灵活修改。
bash
# 关键就是 -v 这一项,宿主机路径:容器内路径
docker run -d --name mysql -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123 \
-v $PWD/mysql/conf:/etc/mysql/conf.d \
-v $PWD/mysql/data:/var/lib/mysql \
mysql:5.7.25
第三站:自定义镜像
官方镜像只能解决"跑现成的软件",我们自己的微服务 jar 得自己打包。这一站先讲镜像结构:镜像由一层层的 Layer 堆叠而成,BaseImage 在最底下,每次指令在上面加一层。理解了分层,才能理解为什么 Dockerfile 里命令的顺序会影响构建缓存。
然后学 Dockerfile 语法,把 jar 变成镜像其实只要三行:
dockerfile
FROM java:8-alpine
COPY ./app.jar /tmp/app.jar
ENTRYPOINT java -jar /tmp/app.jar
docker build 一跑,镜像就有了。这一站是整章的转折点------从"用别人的东西"变成"造自己的东西"。
第四站:DockerCompose
有了镜像不代表部署轻松。网关、用户服务、订单服务、Nacos、MySQL 五个容器,每个都要写一长串 docker run 参数,端口、网络、卷挂载都不能错,还得按顺序启动。Compose 的做法是把这些参数写进一个 yml 文件,一条命令全部拉起:
yaml
version: "3.2"
services:
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123
volumes:
- "$PWD/mysql/data:/var/lib/mysql"
- "$PWD/mysql/conf:/etc/mysql/conf.d/"
userservice:
build: ./user-service
orderservice:
build: ./order-service
gateway:
build: ./gateway
ports:
- "10010:10010"
build 表示现场用 Dockerfile 构建,image 表示直接用现成镜像。注意微服务连 MySQL、Nacos 时要把地址从 localhost 改成服务名(比如 mysql、nacos),因为 Compose 会创建一个自定义网络,容器之间用服务名互相解析------这一步十有八九会配错,报 UnknownHostException。
第五站:镜像仓库
最后一站是管理问题。服务镜像越攒越多,总不能每台服务器都 docker save 成 tar 包手动拷。镜像仓库的作用和 Maven 私服一样:统一存放、版本管理、按需拉取。企业里通常搭一个私有的 registry,推送拉取走 docker tag + docker push + docker pull 三步。
命令速查表
把本章涉及的命令按阶段列出来,后续各篇都从这里检索:
text
① 初识 Docker
docker -v 查看版本,验证安装
docker info 查看 daemon 配置(镜像源、存储驱动)
systemctl start/stop docker 启停守护进程
② 镜像操作
docker pull [repository]:[tag] 拉取镜像
docker images 查看本地镜像
docker save -o xx.tar 镜像名 导出为 tar
docker load -i xx.tar 从 tar 导入
docker rmi 镜像名 删除镜像
docker push 推送(配合镜像仓库)
③ 容器操作
docker run -d --name n -p 80:80 镜像 创建并运行
docker ps / ps -a 查看容器
docker exec -it 容器名 bash 进入容器
docker logs 容器名 查看日志
docker stop / start / restart 容器名 停止 / 启动 / 重启
docker rm / rm -f 容器名 删除(-f 强删)
docker pause / unpause 容器名 暂停 / 恢复
④ 数据卷
docker volume create / ls / inspect / rm / prune
docker run -v 宿主机路径:容器路径 ...
⑤ 自定义镜像
docker build -t 镜像名:tag . 构建镜像
docker history 镜像名 查看分层
⑥ DockerCompose
docker compose up -d / down 启动 / 停止整个集群
docker compose logs -f 服务名 跟踪日志
⑦ 镜像仓库
docker tag 原镜像名 仓库地址/镜像名:tag
docker login 仓库地址
docker push / pull 仓库地址/镜像名:tag
学习建议
分成两批对待,效率差别很大。
必须先动手跑一遍的 :镜像命令和容器命令。这两块是肌肉记忆,光看讲义没有用。docker pull nginx、docker run 起来、docker exec -it 进去改个 index.html、docker ps 看状态、docker rm 删掉,这一套走完,后面所有内容都顺。数据卷也要动手,重点是亲手验证容器删了数据还在,否则挂载路径写错都发现不了。
可以先理解、用到再补的:DockerCompose 和镜像仓库。这两个是工程化能力,语法不难,难在真实项目里的网络和配置。先知道它们解决什么问题、yml 大概长什么样,等真正部署集群时再回来细看。
另外,Dockerfile 部分建议自己写一遍而不是复制。把 FROM、COPY、ENTRYPOINT 换成 ADD、CMD、RUN 分别试试,看构建结果和分层有什么变化,比读十遍文档管用。
官方文档
总结
- 这一章解决的是微服务的部署难题,起因是拆得越细,环境差异和重复部署的成本越高。
- 路径是五站:先认识(初识 Docker),再用起来(基本操作),然后造自己的东西(自定义镜像),接着批量部署(Compose),最后管起来(镜像仓库)。
- 前两站必须动手,后两站可以先建立概念,真实项目里自然会补上。
- 本文是整章导引,每个主题的具体命令、案例和坑都在后续各篇里展开。下一篇从最基础的问题开始:什么是 Docker,它到底比虚拟机强在哪。