Java 项目部署之 Docker工具快速入门: Docker 是什么以及它如何解决部署环境问题

概述

一个微服务在本地跑得好好的,推到测试环境直接报 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。
相关推荐
老木避暑研究所1 小时前
从原始数据到洞察:一次完整的避暑房环境数据采集、清洗与可视化分析
java·开发语言
程序喵大人1 小时前
【C++入门】编译链接模型 - 05 ODR 为什么会让重复定义变成工程炸点
开发语言·c++·编译链接·odr
weixin199701080161 小时前
《淘宝TOP API:落地方案、接口边界与业务踩坑 —— 聚石塔内外价差 10 倍的真相》(附 Python 源码)
开发语言·python
前端 贾公子1 小时前
LangGraph == 图的状态(State)管理 (上)
java·开发语言·数据库
huaweichenai2 小时前
spring boot 实现file文件上传
java·spring boot·后端
AIGC大时代2 小时前
LLM Agent 执行沙箱闸门:失败含义与当天最小实验
docker·沙箱·闸门·bubblewrap·llm agent
VX_bysjlw9852 小时前
数码设备销售网站设计与实现39138-计算机毕设原创(免费领源码+带部署教程)
java·vue.js·spring boot·mysql·tomcat·mybatis·idea
荣码2 小时前
从0到1搭一个生产级RAG系统:串联前面21篇所有知识
java·python
xuxigifxfh2 小时前
牛客:HJ170 01序列
java·开发语言·算法·华为机考