概述
一个微服务在本地跑得好好的,推到测试环境直接报 glibc 版本不匹配------这类问题 Docker 靠一个动作就堵死了:把应用依赖的系统函数库一起打包进镜像。本文拆开讲它为什么能这么干,隔离是怎么来的,以及它和虚拟机究竟差在哪一层。
纲要
- 部署的两个老问题
- 依赖兼容:不同组件抢同一套函数库,版本互相打架
- 环境差异:开发用 Ubuntu、生产用 CentOS,换个系统就跑不起来
- Docker 的两板斧
- 打包:应用 + Libs 函数库 + Deps 依赖 + 配置 → 一个可移植镜像
- 隔离:每个应用跑在自己的沙箱容器里,互相不可见
- 操作系统分层结构:硬件 → Linux 内核 → 系统应用/函数库 → 应用
- 跨系统的关键:镜像自带函数库,只依赖宿主机的 Linux 内核
- 镜像 vs 容器:只读文件包 vs 运行起来的隔离进程
- Docker 与虚拟机的差别:分层对比图 + 量化对比表
- 动手 :
docker run、docker exec、Dockerfile、docker-compose.yml - 在 CI/CD 与微服务中的角色:一次构建,到处运行
部署到底难在哪
先别急着谈 Docker,把问题摆清楚,后面每一条设计都能对上号。
一个分布式项目拆完之后,组件数量是指数级涨的:前端依赖 Node.js,服务端要 MySQL、Redis、RabbitMQ、Nacos,后面还有 Elasticsearch、MinIO......每一个都有自己的依赖和函数库要求。
#mermaid-svg-J3H1kEPKt14hR6Qt{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-J3H1kEPKt14hR6Qt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-J3H1kEPKt14hR6Qt .error-icon{fill:#552222;}#mermaid-svg-J3H1kEPKt14hR6Qt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-J3H1kEPKt14hR6Qt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-J3H1kEPKt14hR6Qt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-J3H1kEPKt14hR6Qt .marker.cross{stroke:#333333;}#mermaid-svg-J3H1kEPKt14hR6Qt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-J3H1kEPKt14hR6Qt p{margin:0;}#mermaid-svg-J3H1kEPKt14hR6Qt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt .cluster-label text{fill:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt .cluster-label span{color:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt .cluster-label span p{background-color:transparent;}#mermaid-svg-J3H1kEPKt14hR6Qt .label text,#mermaid-svg-J3H1kEPKt14hR6Qt span{fill:#333;color:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt .node rect,#mermaid-svg-J3H1kEPKt14hR6Qt .node circle,#mermaid-svg-J3H1kEPKt14hR6Qt .node ellipse,#mermaid-svg-J3H1kEPKt14hR6Qt .node polygon,#mermaid-svg-J3H1kEPKt14hR6Qt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-J3H1kEPKt14hR6Qt .rough-node .label text,#mermaid-svg-J3H1kEPKt14hR6Qt .node .label text,#mermaid-svg-J3H1kEPKt14hR6Qt .image-shape .label,#mermaid-svg-J3H1kEPKt14hR6Qt .icon-shape .label{text-anchor:middle;}#mermaid-svg-J3H1kEPKt14hR6Qt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-J3H1kEPKt14hR6Qt .rough-node .label,#mermaid-svg-J3H1kEPKt14hR6Qt .node .label,#mermaid-svg-J3H1kEPKt14hR6Qt .image-shape .label,#mermaid-svg-J3H1kEPKt14hR6Qt .icon-shape .label{text-align:center;}#mermaid-svg-J3H1kEPKt14hR6Qt .node.clickable{cursor:pointer;}#mermaid-svg-J3H1kEPKt14hR6Qt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-J3H1kEPKt14hR6Qt .arrowheadPath{fill:#333333;}#mermaid-svg-J3H1kEPKt14hR6Qt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-J3H1kEPKt14hR6Qt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-J3H1kEPKt14hR6Qt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J3H1kEPKt14hR6Qt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-J3H1kEPKt14hR6Qt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J3H1kEPKt14hR6Qt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-J3H1kEPKt14hR6Qt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-J3H1kEPKt14hR6Qt .cluster text{fill:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt .cluster span{color:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt 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-J3H1kEPKt14hR6Qt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-J3H1kEPKt14hR6Qt rect.text{fill:none;stroke-width:0;}#mermaid-svg-J3H1kEPKt14hR6Qt .icon-shape,#mermaid-svg-J3H1kEPKt14hR6Qt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J3H1kEPKt14hR6Qt .icon-shape p,#mermaid-svg-J3H1kEPKt14hR6Qt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-J3H1kEPKt14hR6Qt .icon-shape .label rect,#mermaid-svg-J3H1kEPKt14hR6Qt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J3H1kEPKt14hR6Qt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-J3H1kEPKt14hR6Qt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-J3H1kEPKt14hR6Qt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 微服务集群
Node.js 前端
MySQL
Redis
RabbitMQ
Nacos 注册中心
函数库 lib-A v1.2
函数库 lib-B v3.0
版本冲突 / 符号找不到
部署失败
麻烦分两层:
| 层次 | 具体问题 | 典型报错 |
|---|---|---|
| 依赖层 | 多个组件共用同一函数库但版本不同,装 A 就崩 B | version 'GLIBC_2.29' not found |
| 系统层 | 开发 Ubuntu、测试 Debian、生产 CentOS,系统函数库各不相同 | error while loading shared libraries: libxxx.so.0 |
更烦的是第二层:你花了两天把开发环境调通,测试环境、预发布环境、生产环境还得重来一遍。操作系统版本一旦不一致,前面那两天的经验基本不可用。
Docker 的两板斧
Docker 的解法说白了就两件事。
打包:把依赖装进镜像
既然每个应用都要自己的依赖和函数库,那就别让它们去操作系统上找了------自己带。
#mermaid-svg-Xnt8OxdvIbvoCljD{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-Xnt8OxdvIbvoCljD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xnt8OxdvIbvoCljD .error-icon{fill:#552222;}#mermaid-svg-Xnt8OxdvIbvoCljD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xnt8OxdvIbvoCljD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xnt8OxdvIbvoCljD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xnt8OxdvIbvoCljD .marker.cross{stroke:#333333;}#mermaid-svg-Xnt8OxdvIbvoCljD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xnt8OxdvIbvoCljD p{margin:0;}#mermaid-svg-Xnt8OxdvIbvoCljD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD .cluster-label text{fill:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD .cluster-label span{color:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD .cluster-label span p{background-color:transparent;}#mermaid-svg-Xnt8OxdvIbvoCljD .label text,#mermaid-svg-Xnt8OxdvIbvoCljD span{fill:#333;color:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD .node rect,#mermaid-svg-Xnt8OxdvIbvoCljD .node circle,#mermaid-svg-Xnt8OxdvIbvoCljD .node ellipse,#mermaid-svg-Xnt8OxdvIbvoCljD .node polygon,#mermaid-svg-Xnt8OxdvIbvoCljD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xnt8OxdvIbvoCljD .rough-node .label text,#mermaid-svg-Xnt8OxdvIbvoCljD .node .label text,#mermaid-svg-Xnt8OxdvIbvoCljD .image-shape .label,#mermaid-svg-Xnt8OxdvIbvoCljD .icon-shape .label{text-anchor:middle;}#mermaid-svg-Xnt8OxdvIbvoCljD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Xnt8OxdvIbvoCljD .rough-node .label,#mermaid-svg-Xnt8OxdvIbvoCljD .node .label,#mermaid-svg-Xnt8OxdvIbvoCljD .image-shape .label,#mermaid-svg-Xnt8OxdvIbvoCljD .icon-shape .label{text-align:center;}#mermaid-svg-Xnt8OxdvIbvoCljD .node.clickable{cursor:pointer;}#mermaid-svg-Xnt8OxdvIbvoCljD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Xnt8OxdvIbvoCljD .arrowheadPath{fill:#333333;}#mermaid-svg-Xnt8OxdvIbvoCljD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xnt8OxdvIbvoCljD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xnt8OxdvIbvoCljD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xnt8OxdvIbvoCljD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xnt8OxdvIbvoCljD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xnt8OxdvIbvoCljD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Xnt8OxdvIbvoCljD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xnt8OxdvIbvoCljD .cluster text{fill:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD .cluster span{color:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD 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-Xnt8OxdvIbvoCljD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xnt8OxdvIbvoCljD rect.text{fill:none;stroke-width:0;}#mermaid-svg-Xnt8OxdvIbvoCljD .icon-shape,#mermaid-svg-Xnt8OxdvIbvoCljD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xnt8OxdvIbvoCljD .icon-shape p,#mermaid-svg-Xnt8OxdvIbvoCljD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Xnt8OxdvIbvoCljD .icon-shape .label rect,#mermaid-svg-Xnt8OxdvIbvoCljD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xnt8OxdvIbvoCljD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Xnt8OxdvIbvoCljD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Xnt8OxdvIbvoCljD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用代码
docker build
Libs 函数库
Deps 依赖 + 配置文件
镜像 Image 只读
容器 1
容器 2
容器 N
打出来的这个包叫镜像 。它落到任何一台机器上都不需要再 apt install 任何东西,自然也就不存在"A 装完 B 崩了"这种事。
隔离:沙箱从哪来的
有人会问:一堆应用塞同一台机器,依赖不打架了吗?不会------Docker 给每个应用套了一个隔离容器,用的是 Namespace + Cgroups 这套内核机制:
- Namespace 负责"看不见":进程、网络、挂载点、用户各自一套视图,容器里
ps只能看到自己的进程 - Cgroups 负责"抢不到":限制 CPU、内存用量,一个容器内存泄漏不会拖垮整机
这就是常说的沙箱机制。容器内改文件、杀进程,都影响不到宿主机和其他容器。
为什么应用不能跨系统运行
要理解 Docker 怎么跨系统,得先看 Linux 发行版的结构。所有 Linux 发行版(CentOS、Ubuntu、Fedora、Debian)都是两层:
#mermaid-svg-jhfnBoGtpot7kCDa{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-jhfnBoGtpot7kCDa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jhfnBoGtpot7kCDa .error-icon{fill:#552222;}#mermaid-svg-jhfnBoGtpot7kCDa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jhfnBoGtpot7kCDa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jhfnBoGtpot7kCDa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jhfnBoGtpot7kCDa .marker.cross{stroke:#333333;}#mermaid-svg-jhfnBoGtpot7kCDa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jhfnBoGtpot7kCDa p{margin:0;}#mermaid-svg-jhfnBoGtpot7kCDa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jhfnBoGtpot7kCDa .cluster-label text{fill:#333;}#mermaid-svg-jhfnBoGtpot7kCDa .cluster-label span{color:#333;}#mermaid-svg-jhfnBoGtpot7kCDa .cluster-label span p{background-color:transparent;}#mermaid-svg-jhfnBoGtpot7kCDa .label text,#mermaid-svg-jhfnBoGtpot7kCDa span{fill:#333;color:#333;}#mermaid-svg-jhfnBoGtpot7kCDa .node rect,#mermaid-svg-jhfnBoGtpot7kCDa .node circle,#mermaid-svg-jhfnBoGtpot7kCDa .node ellipse,#mermaid-svg-jhfnBoGtpot7kCDa .node polygon,#mermaid-svg-jhfnBoGtpot7kCDa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jhfnBoGtpot7kCDa .rough-node .label text,#mermaid-svg-jhfnBoGtpot7kCDa .node .label text,#mermaid-svg-jhfnBoGtpot7kCDa .image-shape .label,#mermaid-svg-jhfnBoGtpot7kCDa .icon-shape .label{text-anchor:middle;}#mermaid-svg-jhfnBoGtpot7kCDa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jhfnBoGtpot7kCDa .rough-node .label,#mermaid-svg-jhfnBoGtpot7kCDa .node .label,#mermaid-svg-jhfnBoGtpot7kCDa .image-shape .label,#mermaid-svg-jhfnBoGtpot7kCDa .icon-shape .label{text-align:center;}#mermaid-svg-jhfnBoGtpot7kCDa .node.clickable{cursor:pointer;}#mermaid-svg-jhfnBoGtpot7kCDa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jhfnBoGtpot7kCDa .arrowheadPath{fill:#333333;}#mermaid-svg-jhfnBoGtpot7kCDa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jhfnBoGtpot7kCDa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jhfnBoGtpot7kCDa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jhfnBoGtpot7kCDa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jhfnBoGtpot7kCDa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jhfnBoGtpot7kCDa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jhfnBoGtpot7kCDa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jhfnBoGtpot7kCDa .cluster text{fill:#333;}#mermaid-svg-jhfnBoGtpot7kCDa .cluster span{color:#333;}#mermaid-svg-jhfnBoGtpot7kCDa 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-jhfnBoGtpot7kCDa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jhfnBoGtpot7kCDa rect.text{fill:none;stroke-width:0;}#mermaid-svg-jhfnBoGtpot7kCDa .icon-shape,#mermaid-svg-jhfnBoGtpot7kCDa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jhfnBoGtpot7kCDa .icon-shape p,#mermaid-svg-jhfnBoGtpot7kCDa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jhfnBoGtpot7kCDa .icon-shape .label rect,#mermaid-svg-jhfnBoGtpot7kCDa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jhfnBoGtpot7kCDa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jhfnBoGtpot7kCDa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jhfnBoGtpot7kCDa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Linux 发行版分层
调用函数库
封装为内核指令
驱动
应用层:MySQL / Redis / Java 服务
系统应用层:函数库 glibc 等,对内核指令的封装
系统内核层:Linux Kernel,提供内核指令
计算机硬件层:CPU / 内存 / 磁盘 / 网卡
关键差异只在系统应用层。Ubuntu 和 CentOS 的内核都是 Linux,但上层的函数库版本、名字、路径都可能不同。Ubuntu 22.04 带的是 glibc 2.35,CentOS 7 只有 glibc 2.17。
于是把一个 Ubuntu 上编译的 MySQL 搬到 CentOS 跑:
Linux内核 CentOS系统函数库 MySQL应用 Linux内核 CentOS系统函数库 MySQL应用 #mermaid-svg-Zc9nSZ6saH6CL1LJ{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-Zc9nSZ6saH6CL1LJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .error-icon{fill:#552222;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .marker.cross{stroke:#333333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Zc9nSZ6saH6CL1LJ p{margin:0;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Zc9nSZ6saH6CL1LJ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Zc9nSZ6saH6CL1LJ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .sequenceNumber{fill:white;}#mermaid-svg-Zc9nSZ6saH6CL1LJ #sequencenumber{fill:#333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .messageText{fill:#333;stroke:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .labelText,#mermaid-svg-Zc9nSZ6saH6CL1LJ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .loopText,#mermaid-svg-Zc9nSZ6saH6CL1LJ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Zc9nSZ6saH6CL1LJ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .noteText,#mermaid-svg-Zc9nSZ6saH6CL1LJ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .actorPopupMenu{position:absolute;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Zc9nSZ6saH6CL1LJ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Zc9nSZ6saH6CL1LJ .actor-man circle,#mermaid-svg-Zc9nSZ6saH6CL1LJ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Zc9nSZ6saH6CL1LJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 代码里写死了函数库版本,迁移即失败 调用 glibc 2.35 的符号 找不到 / 版本不匹配 进程退出并报错
注意这里的因果关系:不是内核不兼容,是上层函数库对不上。
Docker 怎么跨系统
既然缺的是函数库,那就把函数库也打包。Docker 在打包时把「用户程序 + 它要调用的系统函数库」一起塞进镜像,运行时直接调自己带的函数库,再往下由宿主机的 Linux 内核接手:
#mermaid-svg-pxUkj8FZG8voMVts{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-pxUkj8FZG8voMVts .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pxUkj8FZG8voMVts .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pxUkj8FZG8voMVts .error-icon{fill:#552222;}#mermaid-svg-pxUkj8FZG8voMVts .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pxUkj8FZG8voMVts .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pxUkj8FZG8voMVts .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pxUkj8FZG8voMVts .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pxUkj8FZG8voMVts .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pxUkj8FZG8voMVts .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pxUkj8FZG8voMVts .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pxUkj8FZG8voMVts .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pxUkj8FZG8voMVts .marker.cross{stroke:#333333;}#mermaid-svg-pxUkj8FZG8voMVts svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pxUkj8FZG8voMVts p{margin:0;}#mermaid-svg-pxUkj8FZG8voMVts .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pxUkj8FZG8voMVts .cluster-label text{fill:#333;}#mermaid-svg-pxUkj8FZG8voMVts .cluster-label span{color:#333;}#mermaid-svg-pxUkj8FZG8voMVts .cluster-label span p{background-color:transparent;}#mermaid-svg-pxUkj8FZG8voMVts .label text,#mermaid-svg-pxUkj8FZG8voMVts span{fill:#333;color:#333;}#mermaid-svg-pxUkj8FZG8voMVts .node rect,#mermaid-svg-pxUkj8FZG8voMVts .node circle,#mermaid-svg-pxUkj8FZG8voMVts .node ellipse,#mermaid-svg-pxUkj8FZG8voMVts .node polygon,#mermaid-svg-pxUkj8FZG8voMVts .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pxUkj8FZG8voMVts .rough-node .label text,#mermaid-svg-pxUkj8FZG8voMVts .node .label text,#mermaid-svg-pxUkj8FZG8voMVts .image-shape .label,#mermaid-svg-pxUkj8FZG8voMVts .icon-shape .label{text-anchor:middle;}#mermaid-svg-pxUkj8FZG8voMVts .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pxUkj8FZG8voMVts .rough-node .label,#mermaid-svg-pxUkj8FZG8voMVts .node .label,#mermaid-svg-pxUkj8FZG8voMVts .image-shape .label,#mermaid-svg-pxUkj8FZG8voMVts .icon-shape .label{text-align:center;}#mermaid-svg-pxUkj8FZG8voMVts .node.clickable{cursor:pointer;}#mermaid-svg-pxUkj8FZG8voMVts .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pxUkj8FZG8voMVts .arrowheadPath{fill:#333333;}#mermaid-svg-pxUkj8FZG8voMVts .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pxUkj8FZG8voMVts .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pxUkj8FZG8voMVts .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pxUkj8FZG8voMVts .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pxUkj8FZG8voMVts .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pxUkj8FZG8voMVts .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pxUkj8FZG8voMVts .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pxUkj8FZG8voMVts .cluster text{fill:#333;}#mermaid-svg-pxUkj8FZG8voMVts .cluster span{color:#333;}#mermaid-svg-pxUkj8FZG8voMVts 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-pxUkj8FZG8voMVts .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pxUkj8FZG8voMVts rect.text{fill:none;stroke-width:0;}#mermaid-svg-pxUkj8FZG8voMVts .icon-shape,#mermaid-svg-pxUkj8FZG8voMVts .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pxUkj8FZG8voMVts .icon-shape p,#mermaid-svg-pxUkj8FZG8voMVts .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pxUkj8FZG8voMVts .icon-shape .label rect,#mermaid-svg-pxUkj8FZG8voMVts .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pxUkj8FZG8voMVts .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pxUkj8FZG8voMVts .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pxUkj8FZG8voMVts :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 宿主机
镜像内部 自带一切
发起内核指令
完全绕过
MySQL 应用
打包进来的 Ubuntu 函数库
系统应用 CentOS 自己的函数库 不参与
Linux 内核
硬件
所以 Docker 的准则是:一个镜像可以跑在任意 Linux 内核的操作系统上。它依赖的只有内核,不依赖上层的系统应用。
两条问题对应两条答案:
| 原始问题 | Docker 的解法 |
|---|---|
| 不同组件依赖冲突 | 应用 + 依赖 + 函数库 + 配置打包成镜像,自给自足 |
| 开发/测试/生产环境有差异 | 镜像包含完整运行环境,只依赖 Linux 内核 |
| 多应用互相干扰 | 沙箱机制隔离,容器之间互不可见 |
| 部署启动慢 | 一条 docker run 拉起,一条 docker rm 清掉 |
Docker 和虚拟机差在哪
很多人第一反应是"这不就是虚拟机吗"。差别很大,看分层:
#mermaid-svg-APosZ97IZdNOrQVh{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-APosZ97IZdNOrQVh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-APosZ97IZdNOrQVh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-APosZ97IZdNOrQVh .error-icon{fill:#552222;}#mermaid-svg-APosZ97IZdNOrQVh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-APosZ97IZdNOrQVh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-APosZ97IZdNOrQVh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-APosZ97IZdNOrQVh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-APosZ97IZdNOrQVh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-APosZ97IZdNOrQVh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-APosZ97IZdNOrQVh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-APosZ97IZdNOrQVh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-APosZ97IZdNOrQVh .marker.cross{stroke:#333333;}#mermaid-svg-APosZ97IZdNOrQVh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-APosZ97IZdNOrQVh p{margin:0;}#mermaid-svg-APosZ97IZdNOrQVh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-APosZ97IZdNOrQVh .cluster-label text{fill:#333;}#mermaid-svg-APosZ97IZdNOrQVh .cluster-label span{color:#333;}#mermaid-svg-APosZ97IZdNOrQVh .cluster-label span p{background-color:transparent;}#mermaid-svg-APosZ97IZdNOrQVh .label text,#mermaid-svg-APosZ97IZdNOrQVh span{fill:#333;color:#333;}#mermaid-svg-APosZ97IZdNOrQVh .node rect,#mermaid-svg-APosZ97IZdNOrQVh .node circle,#mermaid-svg-APosZ97IZdNOrQVh .node ellipse,#mermaid-svg-APosZ97IZdNOrQVh .node polygon,#mermaid-svg-APosZ97IZdNOrQVh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-APosZ97IZdNOrQVh .rough-node .label text,#mermaid-svg-APosZ97IZdNOrQVh .node .label text,#mermaid-svg-APosZ97IZdNOrQVh .image-shape .label,#mermaid-svg-APosZ97IZdNOrQVh .icon-shape .label{text-anchor:middle;}#mermaid-svg-APosZ97IZdNOrQVh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-APosZ97IZdNOrQVh .rough-node .label,#mermaid-svg-APosZ97IZdNOrQVh .node .label,#mermaid-svg-APosZ97IZdNOrQVh .image-shape .label,#mermaid-svg-APosZ97IZdNOrQVh .icon-shape .label{text-align:center;}#mermaid-svg-APosZ97IZdNOrQVh .node.clickable{cursor:pointer;}#mermaid-svg-APosZ97IZdNOrQVh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-APosZ97IZdNOrQVh .arrowheadPath{fill:#333333;}#mermaid-svg-APosZ97IZdNOrQVh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-APosZ97IZdNOrQVh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-APosZ97IZdNOrQVh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-APosZ97IZdNOrQVh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-APosZ97IZdNOrQVh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-APosZ97IZdNOrQVh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-APosZ97IZdNOrQVh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-APosZ97IZdNOrQVh .cluster text{fill:#333;}#mermaid-svg-APosZ97IZdNOrQVh .cluster span{color:#333;}#mermaid-svg-APosZ97IZdNOrQVh 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-APosZ97IZdNOrQVh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-APosZ97IZdNOrQVh rect.text{fill:none;stroke-width:0;}#mermaid-svg-APosZ97IZdNOrQVh .icon-shape,#mermaid-svg-APosZ97IZdNOrQVh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-APosZ97IZdNOrQVh .icon-shape p,#mermaid-svg-APosZ97IZdNOrQVh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-APosZ97IZdNOrQVh .icon-shape .label rect,#mermaid-svg-APosZ97IZdNOrQVh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-APosZ97IZdNOrQVh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-APosZ97IZdNOrQVh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-APosZ97IZdNOrQVh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Docker方案
容器 A
Docker Engine 系统进程
容器 B
宿主机 OS
硬件
虚拟机方案
App A
Guest OS 完整操作系统 数 GB
App B
Hypervisor 模拟硬件
宿主机 OS
硬件
虚拟机是在操作系统里再跑一个操作系统:Hypervisor 先把 CPU、内存、磁盘整套硬件模拟出来,Guest OS 装在上面,应用再装在 Guest OS 里。每个 VM 都要背一份完整内核,CPU 指令还要过一层虚拟化翻译。
Docker 只封装函数库,容器就是宿主机上的一个普通进程,靠内核的 Namespace/Cgroups 做隔离,没有硬件模拟,也没有第二个内核。
| 对比项 | Docker 容器 | 虚拟机 |
|---|---|---|
| 本质 | 宿主机上的一个系统进程 | 操作系统中的操作系统 |
| 隔离粒度 | 进程级,共享宿主机内核 | OS 级,独享 Guest 内核 |
| 体积 | 镜像几十到几百 MB,alpine 版 5 MB 起 |
镜像数 GB |
| 启动速度 | 秒级,docker run nginx 通常 1 秒内端口就绪 |
分钟级,要走完整开机流程 |
| 性能损耗 | 接近原生,主要损耗在网络 NAT | 有可感知损耗,尤其 IO 和内存 |
| 单机密度 | 同一台机器跑上百个容器很常见 | 十几个 VM 就吃紧 |
| 隔离强度 | 较弱,共享内核,内核漏洞会穿透 | 强,Guest 之间完全独立 |
| 跨平台 | 只能跑 Linux 容器,Windows 上要靠 WSL2 | 可以跑完全不同的 OS |
最后两行是选型时的真实取舍:要强隔离、要跑异构 OS,用虚拟机;要密度、要启动速度、要 CI/CD 里快速起环境,用 Docker。生产上常见的做法是 VM 做外层硬隔离,Docker 跑在 VM 里做应用级隔离。
镜像与容器
这两个词最容易混,一句话区分:
- 镜像 Image :应用在硬盘上的文件 + 运行环境 + 部分系统函数库,打包成的只读文件包
- 容器 Container :镜像运行起来形成的进程,被 Docker 隔离,对外不可见
类比一下:镜像是你下载的 QQ 安装包,容器是你双开、三开之后桌面上的那几个 QQ 进程。一个镜像可以启动任意多次,各容器之间互不影响。
#mermaid-svg-zqarOR3aC0pwkHQi{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-zqarOR3aC0pwkHQi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zqarOR3aC0pwkHQi .error-icon{fill:#552222;}#mermaid-svg-zqarOR3aC0pwkHQi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zqarOR3aC0pwkHQi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zqarOR3aC0pwkHQi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zqarOR3aC0pwkHQi .marker.cross{stroke:#333333;}#mermaid-svg-zqarOR3aC0pwkHQi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zqarOR3aC0pwkHQi p{margin:0;}#mermaid-svg-zqarOR3aC0pwkHQi defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-zqarOR3aC0pwkHQi g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-zqarOR3aC0pwkHQi g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-zqarOR3aC0pwkHQi g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-zqarOR3aC0pwkHQi g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-zqarOR3aC0pwkHQi g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-zqarOR3aC0pwkHQi .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-zqarOR3aC0pwkHQi .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-zqarOR3aC0pwkHQi .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-zqarOR3aC0pwkHQi .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-zqarOR3aC0pwkHQi .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-zqarOR3aC0pwkHQi .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-zqarOR3aC0pwkHQi .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-zqarOR3aC0pwkHQi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zqarOR3aC0pwkHQi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zqarOR3aC0pwkHQi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zqarOR3aC0pwkHQi .edgeLabel .label text{fill:#333;}#mermaid-svg-zqarOR3aC0pwkHQi .label div .edgeLabel{color:#333;}#mermaid-svg-zqarOR3aC0pwkHQi .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-zqarOR3aC0pwkHQi .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-zqarOR3aC0pwkHQi .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-zqarOR3aC0pwkHQi .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-zqarOR3aC0pwkHQi .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-zqarOR3aC0pwkHQi .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zqarOR3aC0pwkHQi .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zqarOR3aC0pwkHQi #statediagram-barbEnd{fill:#333333;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zqarOR3aC0pwkHQi .cluster-label,#mermaid-svg-zqarOR3aC0pwkHQi .nodeLabel{color:#131300;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-zqarOR3aC0pwkHQi .note-edge{stroke-dasharray:5;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-note text{fill:black;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram-note .nodeLabel{color:black;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagram .edgeLabel{color:red;}#mermaid-svg-zqarOR3aC0pwkHQi #dependencyStart,#mermaid-svg-zqarOR3aC0pwkHQi #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-zqarOR3aC0pwkHQi .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zqarOR3aC0pwkHQi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} docker run
docker pause
docker unpause
docker stop
docker start
docker rm
docker rm -f
运行
暂停
停止
注意 pause 和 stop 不是一回事:pause 是冻结进程,CPU 停了但内存还占着;stop 是终止进程,内存和 CPU 都回收。排查内存问题时别搞混。
动手:一条命令跑起来
安装与启动
CentOS 7 上装 Docker CE,内核要求不低于 3.10:
bash
# 卸载旧版本,可选
yum remove docker docker-client docker-common docker-latest \
docker-logrotate docker-engine docker-ce
# 装 yum 工具
yum install -y yum-utils device-mapper-persistent-data lvm2 --skip-broken
# 配阿里云镜像源,官方源在国内太慢
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
yum makecache fast
yum install -y docker-ce
docker -v
启动前先关防火墙,Docker 要用一堆端口,逐个放行太折腾:
bash
systemctl stop firewalld
systemctl disable firewalld
systemctl start docker # 启动
systemctl restart docker # 重启
docker -v # 输出 Docker version 20.10.x, build xxxxx
跑一个 Nginx
bash
# 拉镜像
docker pull nginx
# 创建并运行容器
docker rm -f mn 2>/dev/null # 清掉上一篇残留的同名容器
docker run --name mn -p 80:80 -d nginx
参数逐个说:
| 参数 | 作用 |
|---|---|
--name mn |
容器命名为 mn,后续操作用它指代 |
-p 80:80 |
端口映射,冒号左是宿主机端口,右是容器端口 |
-d |
后台运行,不占用当前终端 |
nginx |
镜像名,不写 tag 默认 latest |
-p 这步不能省。容器默认是隔离网络,直接访问宿主机 80 端口摸不到容器里的 Nginx,映射之后宿主机 80 的流量才会被转发进容器的 80。
常用运维命令:
bash
docker ps # 只看运行中的容器
docker ps -a # 含已停止的
docker logs mn # 看日志
docker logs -f mn # 持续跟踪日志,排查启动失败很好用
docker exec -it mn bash # 进容器内部
docker stop mn # 停止
docker start mn # 重新拉起
docker rm -f mn # 强制删除
有个坑:官方镜像为了瘦身,容器里常常没有 vi。改文件用 sed 凑合:
bash
docker exec -it mn bash
cd /usr/share/nginx/html
sed -i 's#Welcome to nginx#Hello Docker#g' index.html
真正的项目别这么干------容器内改的数据会随容器一起消失。正确做法是用数据卷 -v 把目录挂到宿主机。
打包一个 Java 微服务
这是 cloud-demo 的真实目录结构:
text
cloud-demo/
├── docker-compose.yml
├── gateway/
│ ├── Dockerfile
│ └── app.jar
├── order-service/
│ ├── Dockerfile
│ └── app.jar
├── user-service/
│ ├── Dockerfile
│ └── app.jar
└── mysql/
├── conf/
│ └── hmy.cnf
└── data/
被打包的 user-service 是一个最小 Spring Boot 应用。启动类:
java
package cn.itcast.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 用户服务启动类。Dockerfile 里的 ENTRYPOINT 最终执行的就是这个 main 方法。
*/
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
对外接口:
java
package cn.itcast.user.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 用户查询接口,用于验证容器是否正常提供服务。
*/
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public String queryById(@PathVariable("id") Long id) {
// 容器里查数据库用的是服务名 mysql,不是 localhost
return "user-" + id;
}
}
pom.xml 里把产物固定成 app.jar,和 Dockerfile 的 COPY 对上:
xml
<build>
<!-- 产物名固定为 app.jar,Dockerfile 直接 COPY ./app.jar -->
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Dockerfile 三行就够:
dockerfile
# 基础镜像用 alpine 版 JRE,体积比完整 JDK 小一个数量级
FROM java:8-alpine
# 把 jar 拷进容器
COPY ./app.jar /tmp/app.jar
# 容器启动时执行的命令
ENTRYPOINT java -jar /tmp/app.jar
镜像分层结构长这样:
#mermaid-svg-uWUHbS7BZBVj03iM{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-uWUHbS7BZBVj03iM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uWUHbS7BZBVj03iM .error-icon{fill:#552222;}#mermaid-svg-uWUHbS7BZBVj03iM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uWUHbS7BZBVj03iM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uWUHbS7BZBVj03iM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uWUHbS7BZBVj03iM .marker.cross{stroke:#333333;}#mermaid-svg-uWUHbS7BZBVj03iM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uWUHbS7BZBVj03iM p{margin:0;}#mermaid-svg-uWUHbS7BZBVj03iM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uWUHbS7BZBVj03iM .cluster-label text{fill:#333;}#mermaid-svg-uWUHbS7BZBVj03iM .cluster-label span{color:#333;}#mermaid-svg-uWUHbS7BZBVj03iM .cluster-label span p{background-color:transparent;}#mermaid-svg-uWUHbS7BZBVj03iM .label text,#mermaid-svg-uWUHbS7BZBVj03iM span{fill:#333;color:#333;}#mermaid-svg-uWUHbS7BZBVj03iM .node rect,#mermaid-svg-uWUHbS7BZBVj03iM .node circle,#mermaid-svg-uWUHbS7BZBVj03iM .node ellipse,#mermaid-svg-uWUHbS7BZBVj03iM .node polygon,#mermaid-svg-uWUHbS7BZBVj03iM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uWUHbS7BZBVj03iM .rough-node .label text,#mermaid-svg-uWUHbS7BZBVj03iM .node .label text,#mermaid-svg-uWUHbS7BZBVj03iM .image-shape .label,#mermaid-svg-uWUHbS7BZBVj03iM .icon-shape .label{text-anchor:middle;}#mermaid-svg-uWUHbS7BZBVj03iM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uWUHbS7BZBVj03iM .rough-node .label,#mermaid-svg-uWUHbS7BZBVj03iM .node .label,#mermaid-svg-uWUHbS7BZBVj03iM .image-shape .label,#mermaid-svg-uWUHbS7BZBVj03iM .icon-shape .label{text-align:center;}#mermaid-svg-uWUHbS7BZBVj03iM .node.clickable{cursor:pointer;}#mermaid-svg-uWUHbS7BZBVj03iM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uWUHbS7BZBVj03iM .arrowheadPath{fill:#333333;}#mermaid-svg-uWUHbS7BZBVj03iM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uWUHbS7BZBVj03iM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uWUHbS7BZBVj03iM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uWUHbS7BZBVj03iM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uWUHbS7BZBVj03iM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uWUHbS7BZBVj03iM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uWUHbS7BZBVj03iM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uWUHbS7BZBVj03iM .cluster text{fill:#333;}#mermaid-svg-uWUHbS7BZBVj03iM .cluster span{color:#333;}#mermaid-svg-uWUHbS7BZBVj03iM 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-uWUHbS7BZBVj03iM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uWUHbS7BZBVj03iM rect.text{fill:none;stroke-width:0;}#mermaid-svg-uWUHbS7BZBVj03iM .icon-shape,#mermaid-svg-uWUHbS7BZBVj03iM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uWUHbS7BZBVj03iM .icon-shape p,#mermaid-svg-uWUHbS7BZBVj03iM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uWUHbS7BZBVj03iM .icon-shape .label rect,#mermaid-svg-uWUHbS7BZBVj03iM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uWUHbS7BZBVj03iM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uWUHbS7BZBVj03iM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uWUHbS7BZBVj03iM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 基础镜像 java:8-alpine
COPY 层 app.jar
ENTRYPOINT 层 启动命令
userservice 镜像 只读
运行中的容器进程
每一层都是只读的,容器启动时在最上面加一层可写层。所以同一个镜像起两个容器,各自的可写层互不干扰,底层文件却是共享的------这是 Docker 省磁盘的原因。
多服务一起起,用 compose 编排:
yaml
version: "3.2"
services:
nacos:
image: nacos/nacos-server
environment:
MODE: standalone
ports:
- "8848:8848"
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"
bash
docker-compose up -d # 后台启动全部服务
docker-compose ps # 看各服务状态
docker-compose logs -f userservice
docker-compose down # 停掉并清理
两个细节值得记住:
mysql挂了两个卷,conf放hmy.cnf,data放数据文件。不挂 data 卷,容器一删数据就没了。- compose 里服务之间用服务名 互访,配置文件里写
mysql:3306而不是127.0.0.1:3306。这是新手最常踩的坑------每个容器都有自己的 network namespace,localhost 指的是容器自己。
hmy.cnf 内容:
ini
[mysqld]
skip-name-resolve
character_set_server=utf8
datadir=/var/lib/mysql
server-id=1000
Docker 在 CI/CD 与微服务中的角色
Docker 真正的价值不在"本地跑个 MySQL 方便",而在于它把交付物 统一了。以前 CI 产出的是 jar、war、静态资源,部署还要对一遍环境;现在 CI 产出的是镜像,镜像里已经含了运行时,部署动作退化成 docker pull + docker run。
生产服务器 镜像仓库 CI 流水线 代码仓库 开发者 生产服务器 镜像仓库 CI 流水线 代码仓库 开发者 #mermaid-svg-b619Ufy2Ic8UPSNQ{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-b619Ufy2Ic8UPSNQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b619Ufy2Ic8UPSNQ .error-icon{fill:#552222;}#mermaid-svg-b619Ufy2Ic8UPSNQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b619Ufy2Ic8UPSNQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b619Ufy2Ic8UPSNQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b619Ufy2Ic8UPSNQ .marker.cross{stroke:#333333;}#mermaid-svg-b619Ufy2Ic8UPSNQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b619Ufy2Ic8UPSNQ p{margin:0;}#mermaid-svg-b619Ufy2Ic8UPSNQ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b619Ufy2Ic8UPSNQ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-b619Ufy2Ic8UPSNQ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-b619Ufy2Ic8UPSNQ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-b619Ufy2Ic8UPSNQ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-b619Ufy2Ic8UPSNQ .sequenceNumber{fill:white;}#mermaid-svg-b619Ufy2Ic8UPSNQ #sequencenumber{fill:#333;}#mermaid-svg-b619Ufy2Ic8UPSNQ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-b619Ufy2Ic8UPSNQ .messageText{fill:#333;stroke:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b619Ufy2Ic8UPSNQ .labelText,#mermaid-svg-b619Ufy2Ic8UPSNQ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .loopText,#mermaid-svg-b619Ufy2Ic8UPSNQ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-b619Ufy2Ic8UPSNQ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-b619Ufy2Ic8UPSNQ .noteText,#mermaid-svg-b619Ufy2Ic8UPSNQ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-b619Ufy2Ic8UPSNQ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b619Ufy2Ic8UPSNQ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b619Ufy2Ic8UPSNQ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b619Ufy2Ic8UPSNQ .actorPopupMenu{position:absolute;}#mermaid-svg-b619Ufy2Ic8UPSNQ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-b619Ufy2Ic8UPSNQ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b619Ufy2Ic8UPSNQ .actor-man circle,#mermaid-svg-b619Ufy2Ic8UPSNQ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-b619Ufy2Ic8UPSNQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} git push 触发构建 mvn package 产出 app.jar docker build 生成镜像 docker push 打标签推送 推送完成 docker pull 指定版本 docker-compose up -d 滚动替换 部署完成 版本可追溯
微服务场景下这套流程的收益被放大了:
- 一个项目十几个服务,每个服务独立镜像、独立版本,可以单独升级回滚
- 扩容变成
docker-compose up --scale userservice=3,不用再为新机器装环境 - 镜像 tag 就是版本号,回滚到上一个 tag 即可,不用回忆"上次那版 JDK 打的还是 OpenJDK 11"
镜像仓库除了 DockerHub,国内常用的有阿里云镜像服务和网易云镜像中心。私有仓库可以直接起 registry 容器:
bash
docker run -d \
--restart=always \
--name registry \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
registry
私有仓库走 http 默认不被 Docker 信任,要在 /etc/docker/daemon.json 加 insecure-registries,然后 systemctl daemon-reload && systemctl restart docker。忘了这步会报:
text
http: server gave HTTP response to HTTPS client
命令速览
| 命令 | 作用 |
|---|---|
docker pull nginx |
从仓库拉镜像 |
docker images |
看本地镜像 |
docker rmi nginx:latest |
删镜像 |
docker save -o nginx.tar nginx:latest |
导出镜像为文件 |
docker load -i nginx.tar |
从文件导入镜像 |
docker run --name mn -p 80:80 -d nginx |
创建并运行容器 |
docker ps / docker ps -a |
查看容器 |
docker logs -f mn |
跟踪容器日志 |
docker exec -it mn bash |
进入容器 |
docker stop / docker start / docker restart |
停止 / 启动 / 重启 |
docker rm -f mn |
强制删除容器 |
docker build -t userservice:1.0 . |
按 Dockerfile 构建镜像 |
docker-compose up -d |
按 compose 文件批量启动 |
官方文档
总结
- Docker 解决的是两件事:依赖兼容性靠打包 ,环境差异靠连系统函数库一起打包从而只依赖 Linux 内核。
- 容器不是虚拟机。它是宿主机上的进程,靠 Namespace 做视图隔离、Cgroups 做资源限额,没有硬件模拟,所以体积小、启动快。
- 镜像是只读文件包,容器是它跑起来的进程;一个镜像可以起多个容器,共享底层只读层。
-p不写就访问不到,-v不写数据就会丢,compose 里互访要用服务名------这三个是上手阶段最常踩的坑。- 真正让它值钱的不是本地开发方便,而是把交付物从 jar 换成了镜像,让 CI/CD 的最后一个环节变成一条
docker run。