<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Docker 与容器 on 扎塔-Zata</title><link>https://www.zata.cc/tags/docker-%E4%B8%8E%E5%AE%B9%E5%99%A8/</link><description>Recent content in Docker 与容器 on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>扎塔-Zata</copyright><lastBuildDate>Thu, 24 Sep 2026 17:09:06 +0800</lastBuildDate><atom:link href="https://www.zata.cc/tags/docker-%E4%B8%8E%E5%AE%B9%E5%99%A8/index.xml" rel="self" type="application/rss+xml"/><item><title>Docker Swarm 实战（二）：Traefik 反向代理部署</title><link>https://www.zata.cc/p/docker-swarm-traefik-deployment/</link><pubDate>Sun, 12 Apr 2026 11:00:00 +0800</pubDate><guid>https://www.zata.cc/p/docker-swarm-traefik-deployment/</guid><description>&lt;img src="https://www.zata.cc/p/docker-swarm-traefik-deployment/images/index/index.png" alt="Featured image of post Docker Swarm 实战（二）：Traefik 反向代理部署" />&lt;blockquote>
&lt;p>记录一次真实的生产环境部署过程，包含踩过的坑和解决方案。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>环境说明&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>服务器：阿里云 ECS（Ubuntu）&lt;/li>
&lt;li>应用栈：FastAPI 后端 + Next.js 前端 + Celery + Redis&lt;/li>
&lt;li>反向代理：Traefik v2.11&lt;/li>
&lt;li>容器编排：Docker Swarm&lt;/li>
&lt;li>镜像仓库：阿里云 ACR&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;strong>第一步：安装 Docker&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://get.docker.com &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>国内服务器如果无法访问，通过 DaoCloud 镜像拉取：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 拉取镜像示例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker pull docker.m.daocloud.io/traefik:v2.11
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>验证安装：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;strong>第二步：初始化 Docker Swarm&lt;/strong>&lt;/p>
&lt;p>单机部署时，本机既是 manager 也是 worker：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker swarm init --advertise-addr &amp;lt;服务器公网IP&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>执行后会输出一个 &lt;code>docker swarm join&lt;/code> 命令，如果以后需要加入 worker 节点可以用它。&lt;/p>
&lt;hr>
&lt;p>&lt;strong>第三步：创建 Traefik 公共网络&lt;/strong>&lt;/p>
&lt;p>Traefik 通过 overlay 网络发现各服务，这个网络必须在部署任何 stack 之前创建好：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker network create --driver overlay --attachable traefik-public
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>重要&lt;/strong>：应用的 &lt;code>docker-compose.stack.yml&lt;/code> 中 &lt;code>traefik-public&lt;/code> 网络标记为 &lt;code>external: true&lt;/code>，如果这个网络不存在，&lt;code>docker stack deploy&lt;/code> 会直接报错失败。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;p>&lt;strong>第四步：准备 Traefik 配置文件&lt;/strong>&lt;/p>
&lt;p>将以下 &lt;code>stack.yml&lt;/code> 上传到服务器 &lt;code>/opt/myapp/traefik/stack.yml&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">traefik&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">docker.m.daocloud.io/traefik:v2.11&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">providers.docker=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">providers.docker.swarmMode=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">providers.docker.endpoint=unix:///var/run/docker.sock&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">providers.docker.exposedByDefault=false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">providers.docker.network=traefik-public&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">entrypoints.web.address=:80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">entrypoints.websecure.address=:443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">entrypoints.web.http.redirections.entrypoint.to=websecure&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">entrypoints.web.http.redirections.entrypoint.scheme=https&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">certificatesresolvers.letsencrypt.acme.storage=/var/lib/traefik/acme/acme.json&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- --&lt;span class="l">ping=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">published&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">protocol&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mode&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">host&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">published&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">protocol&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mode&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">host&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">/var/run/docker.sock:/var/run/docker.sock:ro&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">/opt/myapp/traefik/acme.json:/var/lib/traefik/acme/acme.json&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">traefik-public&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">healthcheck&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;CMD&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;traefik&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;healthcheck&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--ping&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">30s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">timeout&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">retries&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mode&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">replicated&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">replicas&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">placement&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">constraints&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">node.role==manager&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">traefik-public&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">external&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">traefik-public&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;strong>第五步：准备 ACME 证书存储文件&lt;/strong>&lt;/p>
&lt;p>Traefik 会自动申请 Let&amp;rsquo;s Encrypt 证书并写入这个文件，&lt;strong>文件必须提前创建且权限为 600&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir -p /opt/myapp/traefik
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">touch /opt/myapp/traefik/acme.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod &lt;span class="m">600&lt;/span> /opt/myapp/traefik/acme.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>文件内容是空的完全没问题，Traefik 首次启动后会自动填充。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;p>&lt;strong>第六步：部署 Traefik&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> /opt/myapp/traefik
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TRAEFIK_ACME_EMAIL&lt;/span>&lt;span class="o">=&lt;/span>你的邮箱@example.com &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>docker stack deploy -c stack.yml traefik
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>验证是否正常运行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stack services traefik
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 应看到 REPLICAS 为 1/1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service logs traefik_traefik --tail &lt;span class="m">20&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 应看到 &amp;#34;Configuration loaded from flags.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;strong>踩坑记录&lt;/strong>&lt;/p>
&lt;p>&lt;strong>坑 1：Traefik v3 的 swarm provider 与 Docker 29 不兼容&lt;/strong>&lt;/p>
&lt;p>&lt;strong>现象&lt;/strong>：日志持续报错 &lt;code>client version 1.24 is too old. Minimum supported API version is 1.40&lt;/code>，服务始终 &lt;code>0/1&lt;/code>。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：Traefik v3.3.5 的 swarm provider 内部 Docker 客户端使用了旧版 API（1.24），而 Docker 29.x daemon 最低只支持 API 1.40。设置 &lt;code>DOCKER_API_VERSION&lt;/code> 环境变量无效，因为 Traefik 的 swarm provider 没有使用该变量。&lt;/p>
&lt;p>&lt;strong>解决方案&lt;/strong>：降级到 Traefik v2.11，v2 的 Docker provider 对 Swarm 支持成熟稳定。&lt;/p>
&lt;p>同时注意 v2 和 v3 的命令参数不同：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>功能&lt;/th>
&lt;th>v3&lt;/th>
&lt;th>v2&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>启用 Swarm&lt;/td>
&lt;td>&lt;code>--providers.swarm=true&lt;/code>&lt;/td>
&lt;td>&lt;code>--providers.docker.swarmMode=true&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>端点&lt;/td>
&lt;td>&lt;code>--providers.swarm.endpoint&lt;/code>&lt;/td>
&lt;td>&lt;code>--providers.docker.endpoint&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>默认不暴露&lt;/td>
&lt;td>&lt;code>--providers.swarm.exposedByDefault&lt;/code>&lt;/td>
&lt;td>&lt;code>--providers.docker.exposedByDefault&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>坑 2：静态配置文件与 CLI 参数冲突&lt;/strong>&lt;/p>
&lt;p>&lt;strong>现象&lt;/strong>：同时使用 &lt;code>--configFile=/etc/traefik/traefik.yml&lt;/code> 和 CLI 参数时，ACME 存储路径用了配置文件里的旧路径，导致 &lt;code>open /letsencrypt/acme.json: no such file or directory&lt;/code>。&lt;/p>
&lt;p>&lt;strong>解决方案&lt;/strong>：二选一，不要混用。推荐只用 CLI 参数，去掉 &lt;code>--configFile&lt;/code> 和 &lt;code>configs&lt;/code> 挂载。&lt;/p>
&lt;p>&lt;strong>坑 3：SCP 上传目录时目标路径不存在&lt;/strong>&lt;/p>
&lt;p>&lt;strong>现象&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">scp&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">realpath&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">opt&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">myapp&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">traefik&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">No&lt;/span> &lt;span class="n">such&lt;/span> &lt;span class="n">file&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">scp&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">upload&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="n">canonicalization&lt;/span> &lt;span class="n">failed&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>解决方案&lt;/strong>：上传前先在服务器上创建目录：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ssh root@your-server &lt;span class="s2">&amp;#34;mkdir -p /opt/myapp/traefik&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>坑 4：host-mode 端口被占用导致 Pending&lt;/strong>&lt;/p>
&lt;p>&lt;strong>现象&lt;/strong>：&lt;code>docker service ps&lt;/code> 看到 &lt;code>host-mode port already in use on 1 node&lt;/code>，新任务处于 Pending 状态。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：滚动更新时旧容器仍在运行占用端口，新容器无法启动。等旧容器退出后会自动恢复正常，不需要手动干预。&lt;/p>
&lt;hr>
&lt;p>&lt;strong>部署顺序总结&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1. 安装 Docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. docker swarm init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. docker network create --driver overlay --attachable traefik-public
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 准备 acme.json（touch + chmod 600）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5. docker stack deploy -c stack.yml traefik ← Traefik 先于应用部署
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6. docker stack deploy -c docker-compose.stack.yml &amp;lt;app-stack-name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Traefik 会监听 Docker Swarm 的服务变化，自动根据 &lt;code>deploy.labels&lt;/code> 中的配置生成路由规则和申请 HTTPS 证书，无需重启。&lt;/p></description></item><item><title>Docker Swarm 实战（一）：核心概念与集群管理</title><link>https://www.zata.cc/p/docker-swarm-guide/</link><pubDate>Sun, 12 Apr 2026 10:00:00 +0800</pubDate><guid>https://www.zata.cc/p/docker-swarm-guide/</guid><description>&lt;img src="https://www.zata.cc/p/docker-swarm-guide/images/index/index.png" alt="Featured image of post Docker Swarm 实战（一）：核心概念与集群管理" />&lt;h2 id="系列文章">系列文章
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>篇&lt;/th>
&lt;th>主题&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>一（本篇）&lt;/td>
&lt;td>核心概念与集群管理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="../docker-swarm-traefik-deployment/" >二&lt;/a>&lt;/td>
&lt;td>Traefik 反向代理部署&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="为什么选择-docker-swarm">为什么选择 Docker Swarm
&lt;/h2>&lt;p>在容器编排领域，Kubernetes 是行业标准，但对于中小规模项目，Docker Swarm 往往是更务实的选择：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>零额外依赖&lt;/strong>：Docker 自带，无需单独安装&lt;/li>
&lt;li>&lt;strong>学习曲线平缓&lt;/strong>：&lt;code>docker-compose.yml&lt;/code> 稍加改造就能用于 Swarm&lt;/li>
&lt;li>&lt;strong>运维成本低&lt;/strong>：单机也能跑，按需扩展到多节点&lt;/li>
&lt;li>&lt;strong>足够稳定&lt;/strong>：生产可用，社区成熟&lt;/li>
&lt;/ul>
&lt;p>如果你的业务规模不需要 K8s 的复杂调度能力，Swarm 是一个省心的选择。&lt;/p>
&lt;hr>
&lt;h2 id="核心概念">核心概念
&lt;/h2>&lt;h3 id="node节点">Node（节点）
&lt;/h3>&lt;p>Swarm 集群由多个 Docker 主机（Node）组成，分两类角色：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>角色&lt;/th>
&lt;th>职责&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Manager&lt;/strong>&lt;/td>
&lt;td>维护集群状态、调度任务、处理 API 请求&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Worker&lt;/strong>&lt;/td>
&lt;td>执行容器任务，不参与调度决策&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>Manager 节点同时也承担 Worker 职责（可配置为纯 Manager）&lt;/li>
&lt;li>生产环境建议部署奇数个 Manager（3 或 5），保证 Raft 共识算法的容错能力&lt;/li>
&lt;li>单机部署时，本机同时充当 Manager 和 Worker&lt;/li>
&lt;/ul>
&lt;h3 id="service服务">Service（服务）
&lt;/h3>&lt;p>Service 是 Swarm 的基本部署单元，描述&amp;quot;运行什么、运行几份、如何更新&amp;quot;：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── replicated mode：固定副本数，调度器负责在节点间分配
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── global mode：每个节点各运行一个副本（适合日志收集、监控 agent 等）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="task任务">Task（任务）
&lt;/h3>&lt;p>Task 是 Service 的最小执行单元，对应一个运行中的容器实例。调度器将 Task 分配到合适的节点上执行。&lt;/p>
&lt;h3 id="stack栈">Stack（栈）
&lt;/h3>&lt;p>Stack 是一组相关 Service 的集合，通过 &lt;code>docker-compose.yml&lt;/code>（需加 &lt;code>deploy&lt;/code> 字段）定义，用 &lt;code>docker stack deploy&lt;/code> 一键部署。&lt;/p>
&lt;h3 id="overlay-network覆盖网络">Overlay Network（覆盖网络）
&lt;/h3>&lt;p>Swarm 使用 overlay 网络让不同节点上的容器互相通信，底层通过 VXLAN 封装实现跨主机网络。&lt;/p>
&lt;hr>
&lt;h2 id="安装与初始化">安装与初始化
&lt;/h2>&lt;h3 id="安装-docker">安装 Docker
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://get.docker.com &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>国内网络可通过 DaoCloud 镜像安装：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -sSL https://get.daocloud.io/docker &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="初始化-swarm">初始化 Swarm
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化，指定对外通告的 IP（通常为服务器公网 IP 或内网 IP）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm init --advertise-addr &amp;lt;IP&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>初始化成功后会输出加入集群的命令，记录备用：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">To add a worker to this swarm, run the following command:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> docker swarm join --token SWMTKN-1-xxx &amp;lt;IP&amp;gt;:2377
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">To add a manager to this swarm, run &amp;#39;docker swarm join-token manager&amp;#39; and follow the instructions.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="查看集群状态">查看集群状态
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker node ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">abc123 * node-1 Ready Active Leader
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">def456 node-2 Ready Active
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ghi789 node-3 Ready Active Reachable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="节点管理">节点管理
&lt;/h2>&lt;h3 id="添加节点">添加节点
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在 Manager 节点上获取加入 token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm join-token worker &lt;span class="c1"># 获取 worker 加入命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm join-token manager &lt;span class="c1"># 获取 manager 加入命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在目标机器上执行输出的 join 命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm join --token &amp;lt;TOKEN&amp;gt; &amp;lt;MANAGER_IP&amp;gt;:2377
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="更改节点角色">更改节点角色
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Worker 升级为 Manager&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node promote &amp;lt;NODE_ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Manager 降级为 Worker&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node demote &amp;lt;NODE_ID&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="节点维护与下线">节点维护与下线
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将节点置为维护模式（停止接受新任务，现有任务迁移到其他节点）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node update --availability drain &amp;lt;NODE_ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复为可调度状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node update --availability active &amp;lt;NODE_ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 从集群中删除节点（需先 drain）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node rm &amp;lt;NODE_ID&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="服务管理">服务管理
&lt;/h2>&lt;h3 id="创建服务">创建服务
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 基本格式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service create &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --name my-web &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --replicas &lt;span class="m">3&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --publish &lt;span class="nv">published&lt;/span>&lt;span class="o">=&lt;/span>8080,target&lt;span class="o">=&lt;/span>&lt;span class="m">80&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> nginx:alpine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>常用参数：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>--replicas N&lt;/code>&lt;/td>
&lt;td>副本数量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--publish&lt;/code>&lt;/td>
&lt;td>端口映射&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--network&lt;/code>&lt;/td>
&lt;td>加入指定网络&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--env KEY=VAL&lt;/code>&lt;/td>
&lt;td>环境变量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--constraint&lt;/code>&lt;/td>
&lt;td>调度约束（如 &lt;code>node.role==manager&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--limit-cpu / --limit-memory&lt;/code>&lt;/td>
&lt;td>资源限制&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--restart-condition&lt;/code>&lt;/td>
&lt;td>重启策略&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--update-parallelism&lt;/code>&lt;/td>
&lt;td>滚动更新并发数&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="查看服务">查看服务
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service ls &lt;span class="c1"># 列出所有服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service ps &amp;lt;SERVICE&amp;gt; &lt;span class="c1"># 查看服务的任务列表（各副本状态）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service inspect &amp;lt;SERVICE&amp;gt; &lt;span class="c1"># 查看服务详细配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service inspect --pretty &amp;lt;SERVICE&amp;gt; &lt;span class="c1"># 易读格式&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="扩缩容">扩缩容
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service scale my-web&lt;span class="o">=&lt;/span>&lt;span class="m">5&lt;/span> &lt;span class="c1"># 将副本数调整为 5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service scale &lt;span class="nv">svc1&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">3&lt;/span> &lt;span class="nv">svc2&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">2&lt;/span> &lt;span class="c1"># 同时调整多个服务&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="滚动更新">滚动更新
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 更新镜像版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service update --image nginx:1.25 my-web
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 控制更新节奏&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service update &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --update-parallelism &lt;span class="m">2&lt;/span> &lt;span class="se">\ &lt;/span> &lt;span class="c1"># 每批更新 2 个副本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --update-delay 10s &lt;span class="se">\ &lt;/span> &lt;span class="c1"># 每批之间等待 10 秒&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --image nginx:1.25 my-web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>更新失败时回滚：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service rollback my-web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="删除服务">删除服务
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service rm my-web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="stack-部署">Stack 部署
&lt;/h2>&lt;p>Stack 是推荐的生产部署方式，用 Compose 文件统一描述整个应用栈。&lt;/p>
&lt;h3 id="compose-文件关键差异">Compose 文件关键差异
&lt;/h3>&lt;p>与普通 &lt;code>docker-compose.yml&lt;/code> 相比，Stack 文件需要增加 &lt;code>deploy&lt;/code> 字段，并且&lt;strong>不支持&lt;/strong> &lt;code>build&lt;/code>（镜像必须提前构建好）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">api&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">myregistry/api:latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-net&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">replicas&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">update_config&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">parallelism&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">delay&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">10s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">failure_action&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">rollback&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart_policy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">on&lt;/span>-&lt;span class="l">failure&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">delay&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">max_attempts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">limits&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpus&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.5&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">512M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.api.rule=Host(`api.example.com`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app-net&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">driver&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">overlay&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="部署与更新">部署与更新
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 部署（stack 不存在则创建，已存在则更新）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stack deploy -c docker-compose.stack.yml myapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 带环境变量&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ENV_VAR&lt;/span>&lt;span class="o">=&lt;/span>value docker stack deploy -c stack.yml myapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看 stack 中的服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stack services myapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看 stack 中所有任务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stack ps myapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 删除整个 stack&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stack rm myapp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="网络管理">网络管理
&lt;/h2>&lt;h3 id="创建-overlay-网络">创建 overlay 网络
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 基本创建&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker network create --driver overlay my-network
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 可附加（允许独立容器加入，非 Swarm service 也能连接）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker network create --driver overlay --attachable my-network
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="网络模式对比">网络模式对比
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>模式&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>overlay&lt;/code>&lt;/td>
&lt;td>Swarm service 之间的跨节点通信（默认）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>host&lt;/code>&lt;/td>
&lt;td>直接使用宿主机网络栈，性能最高，但端口冲突风险&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ingress&lt;/code>&lt;/td>
&lt;td>Swarm 内置的负载均衡网络（自动创建，无需手动管理）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="端口发布模式">端口发布模式
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">published&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">protocol&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">tcp&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mode&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">host &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># host 模式：直接绑定到宿主机，绕过 Swarm 的 ingress 负载均衡&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;80:80&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># ingress 模式（默认）：通过 Swarm routing mesh 转发，任意节点均可接收流量&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="配置与密钥管理">配置与密钥管理
&lt;/h2>&lt;h3 id="secret敏感数据">Secret（敏感数据）
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建 secret&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;my_db_password&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> docker secret create db_password -
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker secret create ssl_cert ./cert.pem
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在 service 中使用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service create &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --name db &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --secret db_password &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> postgres:15
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 Compose 文件中：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres:15&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">secrets&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">db_password&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">secrets&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db_password&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">external&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用已存在的 secret&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Secret 会以文件形式挂载到容器的 &lt;code>/run/secrets/&amp;lt;secret_name&amp;gt;&lt;/code>。&lt;/p>
&lt;h3 id="config非敏感配置">Config（非敏感配置）
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建 config&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker config create nginx_conf ./nginx.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在 service 中使用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service create &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --name web &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --config &lt;span class="nv">source&lt;/span>&lt;span class="o">=&lt;/span>nginx_conf,target&lt;span class="o">=&lt;/span>/etc/nginx/nginx.conf &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> nginx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="常用运维操作">常用运维操作
&lt;/h2>&lt;h3 id="查看服务日志">查看服务日志
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service logs &amp;lt;SERVICE&amp;gt; &lt;span class="c1"># 输出所有副本的日志&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service logs -f &amp;lt;SERVICE&amp;gt; &lt;span class="c1"># 实时跟踪&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service logs --tail &lt;span class="m">50&lt;/span> &amp;lt;SERVICE&amp;gt; &lt;span class="c1"># 最近 50 行&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="强制重新部署不改配置">强制重新部署（不改配置）
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service update --force &amp;lt;SERVICE&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="查看任务失败原因">查看任务失败原因
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service ps --no-trunc &amp;lt;SERVICE&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>--no-trunc&lt;/code> 会显示完整的错误信息，方便排查启动失败的原因。&lt;/p>
&lt;h3 id="清理已停止的任务">清理已停止的任务
&lt;/h3>&lt;p>Swarm 默认保留已完成的任务记录，可通过以下方式清理：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker system prune &lt;span class="c1"># 清理未使用的容器、网络、镜像&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker system prune --volumes &lt;span class="c1"># 同时清理 volume&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="常见问题">常见问题
&lt;/h2>&lt;p>&lt;strong>Q：&lt;code>docker stack deploy&lt;/code> 报错 &lt;code>network not found&lt;/code>？&lt;/strong>&lt;/p>
&lt;p>A：&lt;code>external: true&lt;/code> 的网络必须在 deploy 之前手动创建，否则 Swarm 不会自动创建它：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker network create --driver overlay --attachable traefik-public
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Q：服务副本一直是 &lt;code>0/1&lt;/code>，&lt;code>docker service ps&lt;/code> 显示 &lt;code>pending&lt;/code>？&lt;/strong>&lt;/p>
&lt;p>A：常见原因：&lt;/p>
&lt;ul>
&lt;li>节点资源不足（CPU/内存超出限制）&lt;/li>
&lt;li>调度约束无法满足（如 &lt;code>node.role==manager&lt;/code> 但 manager 不可用）&lt;/li>
&lt;li>host-mode 端口冲突（等旧容器退出自动恢复）&lt;/li>
&lt;li>镜像拉取失败（检查仓库权限和网络）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Q：滚动更新后服务异常，如何回滚？&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker service rollback &amp;lt;SERVICE&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Q：单机 Swarm 和 docker compose up 有什么区别？&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特性&lt;/th>
&lt;th>docker compose&lt;/th>
&lt;th>docker stack (Swarm)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>高可用&lt;/td>
&lt;td>无&lt;/td>
&lt;td>副本自动重启、跨节点调度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>滚动更新&lt;/td>
&lt;td>无&lt;/td>
&lt;td>内置，支持回滚&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Secret 管理&lt;/td>
&lt;td>无&lt;/td>
&lt;td>原生支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>扩缩容&lt;/td>
&lt;td>手动&lt;/td>
&lt;td>&lt;code>docker service scale&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>适用场景&lt;/td>
&lt;td>本地开发&lt;/td>
&lt;td>生产部署&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="下一步">下一步
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="../docker-swarm-traefik-deployment/" >Docker Swarm 实战（二）：Traefik 反向代理部署&lt;/a> — 在 Swarm 上部署 Traefik v2，实现 HTTPS 自动证书和路由转发&lt;/li>
&lt;/ul></description></item><item><title>Docker 私有镜像仓库registry</title><link>https://www.zata.cc/p/docker-%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93registry/</link><pubDate>Mon, 23 Feb 2026 00:20:36 +0800</pubDate><guid>https://www.zata.cc/p/docker-%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93registry/</guid><description>&lt;img src="https://www.zata.cc/p/docker-%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93registry/images/index/index.png" alt="Featured image of post Docker 私有镜像仓库registry" />&lt;h1 id="安装和配置">安装和配置
&lt;/h1>&lt;h2 id="dokploy-搭建自带自动清理功能的私有-docker-镜像仓库-----20260225">Dokploy ：搭建自带“自动清理”功能的私有 Docker 镜像仓库 &amp;mdash; 20260225
&lt;/h2>&lt;p>在个人服务器上搭建私有 Docker Registry（镜像仓库）是很多开发者的刚需。使用 Dokploy 部署虽然简单，但很多人很快会遇到一个棘手的问题：&lt;strong>磁盘空间爆炸&lt;/strong>。&lt;/p>
&lt;p>原生的 Docker Registry 像个“只进不出”的貔貅，默认既不支持删除镜像，删除了也不释放空间。&lt;/p>
&lt;p>本文将带你通过 Dokploy 部署一个&lt;strong>带 Web 管理界面&lt;/strong>、&lt;strong>支持删除&lt;/strong>且&lt;strong>每晚自动释放磁盘空间&lt;/strong>的完美私有仓库。&lt;/p>
&lt;hr>
&lt;p>&lt;strong>1. 为什么 Registry 的清理这么麻烦？&lt;/strong>&lt;/p>
&lt;p>在开始配置之前，我们需要理解为什么 Docker 官方把 Registry 设计得这么“难用”。很多人发现，即便开启了删除功能，删除了镜像，磁盘占用依然没变。&lt;/p>
&lt;p>这是因为 Registry 的清理机制分为两步：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>软删除 (Soft Delete)&lt;/strong>：当你通过 API 或 UI 点击删除时，Registry 只是删除了&lt;strong>清单（Manifest）&lt;/strong>。这就像你在 Windows 上把文件拖进了回收站，文件其实还在磁盘上。&lt;/li>
&lt;li>&lt;strong>垃圾回收 (Garbage Collection)&lt;/strong>：这是真正的物理删除。因为 Docker 的镜像是由很多“层（Layers）”组成的，且不同镜像会&lt;strong>共享&lt;/strong>底层。Registry 必须通过扫描全盘，计算引用关系，确认某一层真的没有被任何镜像使用后，才敢真正删除它。&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>此外，官方默认禁用删除操作（&lt;code>REGISTRY_STORAGE_DELETE_ENABLED: false&lt;/code>）主要基于以下考量：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>层共享保护&lt;/strong>：防止误删被其他镜像依赖的基础层。&lt;/li>
&lt;li>&lt;strong>并发安全&lt;/strong>：防止在清理时，刚好有新镜像正在推送（Push）导致数据损坏。&lt;/li>
&lt;li>&lt;strong>不可变基础设施&lt;/strong>：发布的镜像应当被视为不可变的历史存档。&lt;/li>
&lt;/ul>
&lt;p>但在个人或小团队场景下，为了省钱（服务器磁盘贵啊！），我们需要它能够清理。&lt;/p>
&lt;hr>
&lt;p>&lt;strong>2. 编写 &lt;code>docker-compose.yml&lt;/code>&lt;/strong>&lt;/p>
&lt;p>我们将通过 Dokploy 部署三个服务：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Registry&lt;/strong>：核心仓库服务。&lt;/li>
&lt;li>&lt;strong>Auto-Auth&lt;/strong>：一个辅助容器，启动时生成账号密码文件，然后自动退出。&lt;/li>
&lt;li>&lt;strong>Registry UI&lt;/strong>：一个轻量级的 Web 界面，方便我们查看和删除镜像。&lt;/li>
&lt;/ol>
&lt;p>在 Dokploy 的 &lt;code>Compose&lt;/code> 选项卡中，填入以下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 定义数据卷，持久化存储镜像和认证信息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry-auth-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 使用 Dokploy 预置的外部网络&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">dokploy-network&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">external&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 1. 认证辅助服务：生成账号 admin / 密码 123456&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 启动一次生成文件后就会退出，这是正常的&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">auto-setup-auth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">httpd:alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/bin/sh -c &amp;#34;htpasswd -Bbn admin 123456 &amp;gt; /auth/htpasswd&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">registry-auth-data:/auth&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 2. 核心 Registry 服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">registry:2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-private-registry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">always&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">dokploy-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 映射端口方便本地调试，生产环境主要靠 Traefik 转发&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;5000:5000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 认证配置&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_AUTH&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">htpasswd&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_AUTH_HTPASSWD_REALM&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Registry Realm&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_AUTH_HTPASSWD_PATH&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/auth/htpasswd&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 反向代理配置&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_X_FORWARDED_FOR&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_X_FORWARDED_PROTO&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 【关键配置】开启删除权限&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_STORAGE_DELETE_ENABLED&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 【跨域配置】允许 UI 界面通过浏览器调用 API&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_ACCESS_CONTROL_ALLOW_ORIGIN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;[https://registry-ui.zata.cafe]&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_ACCESS_CONTROL_ALLOW_METHODS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;[HEAD,GET,OPTIONS,DELETE]&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_ACCESS_CONTROL_ALLOW_HEADERS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;[Authorization,Accept,Cache-Control]&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_ACCESS_CONTROL_EXPOSE_HEADERS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;[Docker-Content-Digest]&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">registry-data:/var/lib/registry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">registry-auth-data:/auth&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">auto-setup-auth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_completed_successfully&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Traefik 标签：配置域名和 HTTPS&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry.rule=Host(`registry.zata.cafe`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry.entrypoints=websecure&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry.tls.certresolver=letsencrypt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.services.registry.loadbalancer.server.port=5000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 取消请求体大小限制，防止 Push 大镜像失败&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 3. Web UI 管理界面&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry-ui&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">joxit/docker-registry-ui:latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">registry-ui&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">always&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">dokploy-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 删除 REGISTRY_URL，改用下面这两个变量：&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">NGINX_PROXY_PASS_URL=http://my-private-registry:5000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">SINGLE_REGISTRY=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">DELETE_IMAGES=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">REGISTRY_TITLE=Zata Registry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry-ui.rule=Host(`registry-ui.zata.cafe`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry-ui.entrypoints=websecure&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry-ui.tls.certresolver=letsencrypt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.services.registry-ui.loadbalancer.server.port=80&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>注意&lt;/strong>：请将代码中的 &lt;code>zata.cafe&lt;/code> 替换为你自己的域名，并确保 DNS 解析已指向你的服务器。&lt;/p>
&lt;/blockquote>
&lt;p>点击 &lt;strong>Deploy&lt;/strong> 部署应用。&lt;/p>
&lt;hr>
&lt;p>&lt;strong>3. 实现自动化清理 (GC)&lt;/strong>&lt;/p>
&lt;p>部署完成后，你现在可以通过 &lt;code>registry-ui.zata.cafe&lt;/code> 访问界面，并手动删除不需要的镜像 Tag。但这只是“软删除”。为了释放磁盘空间，我们需要在&lt;strong>宿主机&lt;/strong>上配置定时任务。&lt;/p>
&lt;p>&lt;strong>步骤 1：创建清理脚本&lt;/strong>&lt;/p>
&lt;p>SSH 登录到你的服务器，创建一个脚本文件：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">nano /root/clean_registry.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>写入以下内容：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Starting Registry Garbage Collection: &lt;/span>&lt;span class="k">$(&lt;/span>date&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 调用容器内的 registry 工具执行垃圾回收&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -m 参数表示同时删除不再被引用的 manifest（清单）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> my-private-registry /bin/registry garbage-collect /etc/docker/registry/config.yml -m
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Garbage Collection Finished: &lt;/span>&lt;span class="k">$(&lt;/span>date&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>保存并退出（Ctrl+O, Enter, Ctrl+X），然后赋予执行权限：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">chmod +x /root/clean_registry.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>步骤 2：配置定时任务 (Crontab)&lt;/strong>&lt;/p>
&lt;p>我们希望在每天凌晨（比如 4 点）没人使用的时候执行清理。&lt;/p>
&lt;p>输入命令编辑定时任务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">crontab -e
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在文件末尾添加一行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 每天凌晨 4:00 执行清理，并将日志输出到文件以便排查&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">0&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">root&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">clean_registry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sh&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nb">log&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">registry_gc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">log&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;strong>4. 最终工作流&lt;/strong>&lt;/p>
&lt;p>完成以上配置后，你的私有仓库工作流变成了这样：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>日常开发&lt;/strong>：正常 &lt;code>docker push&lt;/code> 推送镜像。&lt;/li>
&lt;li>&lt;strong>手动维护&lt;/strong>：觉得某个镜像版本太老了？打开 &lt;code>registry-ui&lt;/code> 网页，点一下垃圾桶图标删除它（此时空间未释放）。&lt;/li>
&lt;li>&lt;strong>自动瘦身&lt;/strong>：不用管了，去睡觉吧。凌晨 4 点，服务器会自动运行 GC，扫描并彻底删除那些你标记为删除的文件块，第二天早上起来，磁盘空间就回来了。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;p>&lt;strong>总结&lt;/strong>&lt;/p>
&lt;p>通过 Dokploy 配合 Traefik，我们不仅拥有了一个 HTTPS 安全的私有仓库，还加上了可视化管理界面。再配合简单的 Shell 脚本和 Crontab，解决了 Docker Registry 最令人头疼的磁盘占用问题。&lt;/p>
&lt;p>Happy Coding! 🐳&lt;/p>
&lt;h2 id="docker-和-traefik-搭建私有镜像仓库及踩坑记录----20260223">Docker 和 Traefik 搭建私有镜像仓库（及踩坑记录）&amp;mdash; 202602/23
&lt;/h2>&lt;p>在 DevOps 流程中，拥有一个私有的 Docker Registry（镜像仓库）是必不可少的。虽然 Docker Hub 很好用，但出于隐私、速度和成本的考虑，自建仓库往往是更好的选择。本文将手把手教你如何使用 Docker Compose 和 Traefik 反向代理搭建一个带 HTTPS 和基础认证的轻量级私有仓库，并重点分析部署过程中最容易遇到的 “Gateway Timeout” 和 “Connection Refused” 等网络问题。&lt;/p>
&lt;p>&lt;strong>为什么选择这个方案？&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>轻量级&lt;/strong>：基于官方 &lt;code>registry:2&lt;/code> 镜像，资源占用极小。&lt;/li>
&lt;li>&lt;strong>安全&lt;/strong>：通过 Traefik 自动管理 SSL 证书（Let&amp;rsquo;s Encrypt），并配置 &lt;code>htpasswd&lt;/code> 基础认证。&lt;/li>
&lt;li>&lt;strong>易维护&lt;/strong>：所有配置通过一个 &lt;code>docker-compose.yml&lt;/code> 文件管理。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>准备工作&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>一台安装了 Docker 和 Docker Compose 的 Linux 服务器。&lt;/li>
&lt;li>一个域名并解析到服务器 IP（例如：&lt;code>registry.example.com&lt;/code>）。&lt;/li>
&lt;li>&lt;strong>Traefik 已经在运行中&lt;/strong>（如果你使用的是 Dokploy、Coolify 等面板，Traefik 通常是内置好的）。&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>核心配置：docker-compose.yml&lt;/strong>&lt;/p>
&lt;p>为了方便演示，我们使用一个技巧：利用临时容器自动生成密码文件，避免手动安装 &lt;code>htpasswd&lt;/code> 工具的麻烦。请创建一个目录 &lt;code>my-registry&lt;/code>，并在其中新建 &lt;code>docker-compose.yml&lt;/code>，内容如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 定义数据卷，持久化存储镜像和认证信息&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry-auth-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 【重点】定义网络&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 这里必须使用 external: true，表示使用外部已经存在的 Traefik 网络&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 如果你用的是 Dokploy，网络名通常是 dokploy-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 如果你是自己部署的 Traefik，通常叫 traefik_public 或 proxy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">dokploy-network&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">external&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 1. 辅助服务：自动生成账号密码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 启动后会生成密码文件并存入卷中，然后自动退出&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">auto-setup-auth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">httpd:alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 账号: admin, 密码: 123456 (生产环境请修改！)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/bin/sh -c &amp;#34;htpasswd -Bbn admin 123456 &amp;gt; /auth/htpasswd&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">registry-auth-data:/auth&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 2. 核心仓库服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">registry&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">registry:2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-private-registry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">always&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 【关键点 1】加入 Traefik 所在的网络&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">dokploy-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 端口映射（可选，仅用于本地调试，生产环境可注释掉）&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;5000:5000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 启用认证&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_AUTH&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">htpasswd&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_AUTH_HTPASSWD_REALM&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Registry Realm&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_AUTH_HTPASSWD_PATH&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/auth/htpasswd&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 告诉 Registry 它运行在 HTTPS 反向代理后面&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_X_FORWARDED_FOR&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REGISTRY_HTTP_HEADERS_X_FORWARDED_PROTO&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">registry-data:/var/lib/registry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">registry-auth-data:/auth&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">auto-setup-auth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_completed_successfully&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 【关键点 2】Traefik 标签配置&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 修改为你的实际域名&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry.rule=Host(`registry.example.com`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 指定入口点为 HTTPS&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry.entrypoints=websecure&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 启用 TLS (自动申请证书)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.registry.tls.certresolver=letsencrypt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 【至关重要】告诉 Traefik 容器内部端口是 5000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.services.registry.loadbalancer.server.port=5000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 取消请求体大小限制（防止 docker push 大镜像失败）&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置完成后，使用 &lt;code>docker-compose up -d&lt;/code> 命令启动服务。&lt;/p>
&lt;p>&lt;strong>常见问题排查（Troubleshooting）&lt;/strong>&lt;/p>
&lt;p>在部署过程中，你可能会遇到以下几种错误，请根据现象进行排查：&lt;/p>
&lt;p>&lt;strong>1. 504 Gateway Timeout (网关超时)&lt;/strong>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker-%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93registry/images/index/image.png"
width="3404"
height="2050"
srcset="https://www.zata.cc/p/docker-%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93registry/images/index/image_hu6010604155192436420.png 480w, https://www.zata.cc/p/docker-%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93registry/images/index/image_hu14076917848918260733.png 1024w"
loading="lazy"
alt="网关超时"
class="gallery-image"
data-flex-grow="166"
data-flex-basis="398px"
>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>现象&lt;/strong>：浏览器访问域名/v2/，加载很久后显示 &lt;code>504 Gateway Timeout&lt;/code>。&lt;/li>
&lt;li>&lt;strong>原因&lt;/strong>：这说明 Traefik 接到了请求，但是&lt;strong>无法连接到后端的 Registry 容器&lt;/strong>。这通常是因为 Traefik 和 Registry &lt;strong>不在同一个 Docker 网络&lt;/strong> 中。Traefik 就像大楼的前台，它想转接电话给 Registry，但发现线路不通。&lt;/li>
&lt;li>&lt;strong>解决方案&lt;/strong>：检查 &lt;code>docker-compose.yml&lt;/code> 中的 &lt;code>networks&lt;/code> 部分。必须确保 Registry 加入了 Traefik 所在的那个网络（通过 &lt;code>docker network ls&lt;/code> 查看网络名称）。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>2. Context Deadline Exceeded (连接超时)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>现象&lt;/strong>：在使用 Dokploy 或命令行登录时，提示 &lt;code>Client.Timeout exceeded while awaiting headers&lt;/code>。&lt;/li>
&lt;li>&lt;strong>原因&lt;/strong>：这通常是&lt;strong>网络层面的物理隔绝&lt;/strong>。你的请求根本没有到达服务器，被防火墙拦在了外面。&lt;/li>
&lt;li>&lt;strong>解决方案&lt;/strong>：
&lt;ol>
&lt;li>检查云服务商（AWS/阿里云/腾讯云）的安全组，&lt;strong>必须开放 TCP 443 和 80 端口&lt;/strong>。&lt;/li>
&lt;li>检查服务器内部防火墙（如 &lt;code>ufw&lt;/code> 或 &lt;code>iptables&lt;/code>）。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>3. 登录失败或 404 Not Found&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>现象&lt;/strong>：访问域名能通，但是 docker login 总是失败，或者页面显示 404。&lt;/li>
&lt;li>&lt;strong>原因&lt;/strong>：可能是 Traefik 把流量转发到了错误的端口。Registry 默认监听 &lt;strong>5000&lt;/strong>，而 Traefik 默认转发到 80。&lt;/li>
&lt;li>&lt;strong>解决方案&lt;/strong>：确保 &lt;code>labels&lt;/code> 中包含指定端口的配置：&lt;code>- &amp;quot;traefik.http.services.registry.loadbalancer.server.port=5000&amp;quot;&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>4. 推送镜像失败 (Blob Upload Unknown)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>现象&lt;/strong>：登录成功，但在 &lt;code>docker push&lt;/code> 大文件时失败并重试。&lt;/li>
&lt;li>&lt;strong>原因&lt;/strong>：Traefik 或 Nginx 默认会限制上传文件的大小。&lt;/li>
&lt;li>&lt;strong>解决方案&lt;/strong>：添加中间件配置取消限制：&lt;code>- &amp;quot;traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=0&amp;quot;&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>验证与使用&lt;/strong>&lt;/p>
&lt;p>部署成功后，建议按以下步骤进行测试：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>浏览器访问&lt;/strong>：打开 &lt;code>https://registry.example.com/v2/&lt;/code>，应该弹出登录框，输入 &lt;code>admin&lt;/code> / &lt;code>123456&lt;/code>，页面显示 &lt;code>{}&lt;/code> 即为成功。&lt;/li>
&lt;li>&lt;strong>Docker 登录&lt;/strong>：在终端执行 &lt;code>docker login registry.example.com&lt;/code>，输入配置的用户名和密码。&lt;/li>
&lt;li>&lt;strong>推送镜像&lt;/strong>：
&lt;ul>
&lt;li>给本地镜像打标签：&lt;code>docker tag my-image:latest registry.example.com/my-image:latest&lt;/code>&lt;/li>
&lt;li>推送至仓库：&lt;code>docker push registry.example.com/my-image:latest&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>总结&lt;/strong>&lt;/p>
&lt;p>搭建私有仓库并不难，难点通常在于&lt;strong>网络配置&lt;/strong>。记住一句话：&lt;strong>“Traefik 是网关，防火墙是门卫，网络是内部通道。”&lt;/strong> 只有这三者都配置正确，你的镜像仓库才能顺畅运行。希望这篇指南能帮你避开常见的陷阱。&lt;/p>
&lt;h1 id="概念">概念
&lt;/h1>&lt;h2 id="镜像仓库命名空间概念详解">镜像仓库命名空间概念详解
&lt;/h2>&lt;p>在容器化技术日益普及的今天，镜像仓库成为了存储和管理 Docker 镜像的核心基础设施。而在镜像仓库的体系中，&lt;strong>命名空间（Namespace）&lt;/strong> 是一个至关重要却又容易被忽视的概念。本文将带您深入了解命名空间的定义、作用以及最佳实践。&lt;/p>
&lt;p>&lt;strong>什么是命名空间&lt;/strong>&lt;/p>
&lt;p>命名空间可以理解为镜像仓库中的一种逻辑隔离机制。它类似于文件系统中的文件夹，用于对镜像进行分类和组织。在一个公共或私有的镜像仓库中，命名空间允许不同的用户、团队或项目拥有独立的区域来存储他们的镜像，从而避免命名冲突。&lt;/p>
&lt;p>例如，一个完整的镜像地址通常遵循以下格式：&lt;/p>
&lt;ul>
&lt;li>registry/namespace/image-name:tag&lt;/li>
&lt;/ul>
&lt;p>其中，namespace 部分就是命名空间。对于个人用户，命名空间通常等同于用户名；对于组织用户，则是组织名称。&lt;/p>
&lt;p>&lt;strong>为什么需要命名空间&lt;/strong>&lt;/p>
&lt;p>引入命名空间主要为了解决以下几个核心问题：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>资源隔离与权限管理&lt;/strong>
不同的命名空间可以绑定不同的访问控制策略。管理员可以为特定命名空间分配独立的读写权限，确保只有授权人员才能推送或拉取特定镜像，从而实现多租户环境下的安全隔离。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>避免命名冲突&lt;/strong>
在没有命名空间的情况下，所有镜像都必须拥有全局唯一的名称。这显然是不现实的。通过命名空间，不同的团队可以使用相同的镜像名称（例如 web-app），只要它们位于不同的命名空间下（例如 team-a/web-app 和 team-b/web-app），就不会发生冲突。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>逻辑组织与分类&lt;/strong>
随着微服务架构的演进，一个项目可能包含数十甚至上百个镜像。通过合理的命名空间规划，可以将镜像按业务线、环境（开发、测试、生产）或团队进行归类，极大地提升了运维效率。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>命名空间的使用场景&lt;/strong>&lt;/p>
&lt;p>在实际操作中，命名空间的应用非常广泛：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>个人开发者&lt;/strong>：通常直接使用个人账户作为命名空间，用于存储个人实验性或开源项目镜像。&lt;/li>
&lt;li>&lt;strong>企业团队&lt;/strong>：会创建专门的组织命名空间，如 company-backend、company-frontend，以便区分不同职能团队的产物。&lt;/li>
&lt;li>&lt;strong>环境隔离&lt;/strong>：有些团队会使用命名空间来区分环境，例如 proj-dev、proj-prod，虽然更常见的做法是使用 Tag 或不同的仓库实例，但命名空间也是一种可行的隔离手段。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>最佳实践建议&lt;/strong>&lt;/p>
&lt;p>为了更高效地管理镜像仓库，建议遵循以下规范：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>保持命名简洁&lt;/strong>：使用小写字母、数字和连字符，避免特殊字符，确保兼容性和易读性。&lt;/li>
&lt;li>&lt;strong>统一命名规范&lt;/strong>：团队内部应制定明确的命名规则，例如 业务线 - 服务名，防止随意创建导致混乱。&lt;/li>
&lt;li>&lt;strong>定期清理&lt;/strong>：定期检查命名空间下的镜像，删除不再使用的旧版本，节省存储空间并减少安全隐患。&lt;/li>
&lt;li>&lt;strong>权限最小化&lt;/strong>：仅为必要的人员分配命名空间的写入权限，降低误操作风险。&lt;/li>
&lt;/ul>
&lt;p>综上所述，命名空间是镜像仓库管理中不可或缺的一部分。合理使用命名空间不仅能提升工作效率，还能增强系统的安全性和可维护性。希望本文能帮助您更好地规划和使用镜像仓库中的命名空间。&lt;/p></description></item><item><title>Docker Compose(dev\test\prod)</title><link>https://www.zata.cc/p/docker-composedev%5Ctest%5Cprod/</link><pubDate>Wed, 17 Dec 2025 23:05:55 +0800</pubDate><guid>https://www.zata.cc/p/docker-composedev%5Ctest%5Cprod/</guid><description>&lt;img src="https://www.zata.cc/p/docker-composedev%5Ctest%5Cprod/images/index/index.png" alt="Featured image of post Docker Compose(dev\test\prod)" />&lt;h1 id="docker-compose-全生命周期环境治理深度研究报告">Docker Compose 全生命周期环境治理深度研究报告
&lt;/h1>&lt;h2 id="从开发工作流到生产级交付的架构实践">从开发工作流到生产级交付的架构实践
&lt;/h2>&lt;h3 id="1-引言容器编排在软件生命周期中的演进">1. 引言：容器编排在软件生命周期中的演进
&lt;/h3>&lt;p>在云原生技术栈日益成熟的今天，Docker Compose 已超越了其作为“本地开发工具”的初始定位。随着 Docker Compose V2 规范的标准化，它成为定义多容器应用架构的通用语言。本报告旨在剖析如何利用 Compose 的分层合并机制、变量控制体系及不可变基础设施原则，构建一套覆盖 Dev、Test、Prod 的全生命周期治理方案。&lt;/p>
&lt;hr>
&lt;h3 id="2-docker-compose-配置管理的架构哲学">2. Docker Compose 配置管理的架构哲学
&lt;/h3>&lt;h4 id="21-核心矛盾一致性-vs-差异性">2.1 核心矛盾：一致性 vs 差异性
&lt;/h4>&lt;p>架构设计的核心挑战在于维持&lt;strong>基础拓扑的一致性&lt;/strong>（服务依赖、网络别名）与&lt;strong>环境配置的差异性&lt;/strong>（资源限制、暴露端口、卷挂载）。&lt;/p>
&lt;h4 id="22-覆盖模式override-pattern的数学逻辑">2.2 覆盖模式（Override Pattern）的数学逻辑
&lt;/h4>&lt;p>Docker Compose 使用文件名顺序（&lt;code>-f&lt;/code> 参数）来决定配置的最终形态。其合并算法（Merge Logic）如下：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>标量（Scalars）&lt;/strong>：直接替换。后加载的文件覆盖先加载的文件（如 &lt;code>image&lt;/code> tag, &lt;code>restart&lt;/code> 策略）。&lt;/li>
&lt;li>&lt;strong>映射（Maps）&lt;/strong>：键值合并。新键追加，旧键覆盖（如 &lt;code>environment&lt;/code>, &lt;code>labels&lt;/code>）。&lt;/li>
&lt;li>&lt;strong>序列（Sequences）&lt;/strong>：&lt;strong>追加（Union）&lt;/strong>。这是最容易出错的地方。例如，如果在 Base 中定义了端口 80，Override 中定义了 8080，结果是&lt;strong>两个端口都暴露&lt;/strong>。
&lt;ul>
&lt;li>&lt;em>例外&lt;/em>：挂载卷（Volumes）如果容器内路径相同，则为覆盖。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>最佳实践：基座极简原则（Minimalist Base Principle）&lt;/strong>
&lt;code>compose.yaml&lt;/code> 应只包含所有环境共有的配置。凡是可能在生产环境需要移除的配置（如调试端口、源代码 Bind Mount），&lt;strong>绝对不要&lt;/strong>写在 Base 文件中。&lt;/p>
&lt;hr>
&lt;h3 id="3-环境变量env的生态系统与优先级矩阵">3. 环境变量（ENV）的生态系统与优先级矩阵
&lt;/h3>&lt;p>环境变量是导致“配置漂移（Configuration Drift）”的头号原因。必须严格区分 &lt;strong>插值（Interpolation）&lt;/strong> 和 &lt;strong>注入（Injection）&lt;/strong> 两个阶段。&lt;/p>
&lt;h4 id="31-优先级真值表priority-truth-table">3.1 优先级真值表（Priority Truth Table）
&lt;/h4>&lt;p>当变量冲突时，Docker Compose V2 遵循以下优先级（由高到低）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">优先级&lt;/th>
&lt;th style="text-align: left">来源&lt;/th>
&lt;th style="text-align: left">作用阶段&lt;/th>
&lt;th style="text-align: left">典型用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>1 (最高)&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>CLI 参数&lt;/strong> (&lt;code>run -e&lt;/code>)&lt;/td>
&lt;td style="text-align: left">容器运行时&lt;/td>
&lt;td style="text-align: left">临时调试、One-off 任务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>2&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>Shell 环境变量&lt;/strong> (&lt;code>export VAR=...&lt;/code>)&lt;/td>
&lt;td style="text-align: left">插值 &amp;amp; 运行时&lt;/td>
&lt;td style="text-align: left">CI/CD 流水线注入 Secrets&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>3&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>.env 文件&lt;/strong>&lt;/td>
&lt;td style="text-align: left">插值 &amp;amp; 运行时&lt;/td>
&lt;td style="text-align: left">开发者本地默认配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>4&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>YAML &lt;code>environment&lt;/code> 列表&lt;/strong>&lt;/td>
&lt;td style="text-align: left">运行时&lt;/td>
&lt;td style="text-align: left">服务特定的显式配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>5&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>YAML &lt;code>env_file&lt;/code> 指令&lt;/strong>&lt;/td>
&lt;td style="text-align: left">运行时&lt;/td>
&lt;td style="text-align: left">批量加载非敏感配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>6 (最低)&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>Dockerfile &lt;code>ENV&lt;/code> 指令&lt;/strong>&lt;/td>
&lt;td style="text-align: left">镜像构建时&lt;/td>
&lt;td style="text-align: left">镜像层面的默认值&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="32-关键语法技巧">3.2 关键语法技巧
&lt;/h4>&lt;ul>
&lt;li>&lt;strong>强制校验&lt;/strong>：&lt;code>${DB_PASSWORD?err}&lt;/code> —— 如果未设置变量，阻止 Compose 启动并报错。适用于生产环境防止裸奔。&lt;/li>
&lt;li>&lt;strong>默认值&lt;/strong>：&lt;code>${TAG:-latest}&lt;/code> —— 允许 CI 传入特定 Tag，本地开发默认为 latest。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="4-环境一开发环境dev-极致反馈循环">4. 环境一：开发环境（Dev）—— 极致反馈循环
&lt;/h3>&lt;p>目标：代码修改后毫秒级生效，支持断点调试。&lt;/p>
&lt;h4 id="41-架构策略">4.1 架构策略
&lt;/h4>&lt;p>利用 &lt;code>compose.override.yaml&lt;/code>（默认自动加载）实现“源代码热注入”。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># compose.yaml (基座)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 基础镜像名&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-app:base&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">internal&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># compose.override.yaml (开发专用)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">development&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 多阶段构建：开发阶段&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./src:/app/src &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 代码热重载核心&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">/app/node_modules&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 匿名卷：防止宿主机覆盖容器依赖&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">DEBUG=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8080:8080&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 应用端口&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;9229:9229&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 调试端口&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;npm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;run&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;dev:watch&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 覆盖启动命令&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="42-性能优化">4.2 性能优化
&lt;/h4>&lt;ul>
&lt;li>&lt;strong>VirtioFS&lt;/strong>: macOS/Windows Docker Desktop 用户务必开启 VirtioFS 以解决 Bind Mount IO 性能瓶颈。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="5-环境二测试环境test-自动化质量门禁">5. 环境二：测试环境（Test）—— 自动化质量门禁
&lt;/h3>&lt;p>目标：瞬态（Ephemeral）、确定性、自动化退出码。&lt;/p>
&lt;h4 id="51-架构策略">5.1 架构策略
&lt;/h4>&lt;p>CI 环境不需要 Bind Mount，而是需要构建出的镜像进行测试。利用 &lt;code>--exit-code-from&lt;/code> 实现自动化测试编排。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># compose.test.yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sut&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># System Under Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-app:${CI_COMMIT_SHA}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 确保测试的是当前构建产物&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;npm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_healthy&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 关键：等待依赖就绪&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">DB_HOST=db&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres:15-alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">tmpfs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/var/lib/postgresql/data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用内存盘，提升速度且自动清理&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">healthcheck&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;CMD-SHELL&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;pg_isready -U postgres&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">retries&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="52-ci-流水线命令流">5.2 CI 流水线命令流
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 构建&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker compose -f compose.yaml -f compose.test.yaml build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 运行并捕获退出码 (关键步骤)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --exit-code-from sut: 当 sut 容器退出时，关闭所有容器并返回 sut 的退出码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker compose -f compose.yaml -f compose.test.yaml up --exit-code-from sut
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 清理 (防止匿名卷残留)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker compose -f compose.yaml -f compose.test.yaml down -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="6-环境三生产环境prod-稳定性与安全">6. 环境三：生产环境（Prod）—— 稳定性与安全
&lt;/h3>&lt;p>目标：不可变基础设施（Immutable Artifacts）、资源隔离、零敏感信息泄露。&lt;/p>
&lt;h4 id="61-架构策略">6.1 架构策略
&lt;/h4>&lt;p>严禁包含 &lt;code>build&lt;/code> 指令，必须使用预构建并签名的镜像。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># compose.prod.yaml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">registry.com/my-app:v1.2.0&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 锁定具体版本&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;80:8080&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 仅暴露服务端口&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">NODE_ENV=production&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 生产级资源限制 (V2 支持非 Swarm 模式)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">limits&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpus&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;1.0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 硬限制&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">512M &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># OOM 熔断阈值&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">reservations&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpus&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;0.2&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 保证资源&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">memory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">128M&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 密钥管理：严禁使用 environment 传递密码&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">secrets&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">db_password&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">secrets&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db_password&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">file&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./secrets/prod_db_pwd.txt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="62-部署命令">6.2 部署命令
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --remove-orphans: 清理旧版本遗留的容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -d: 后台运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker compose -f compose.yaml -f compose.prod.yaml up -d --remove-orphans
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="7-高级主题数据卷管理的陷阱">7. 高级主题：数据卷管理的陷阱
&lt;/h3>&lt;h4 id="71-无法删除悖论">7.1 “无法删除”悖论
&lt;/h4>&lt;p>Docker Compose 的合并逻辑是&lt;strong>追加&lt;/strong>。你无法在 &lt;code>compose.prod.yaml&lt;/code> 中编写指令来“删除” &lt;code>compose.yaml&lt;/code> 中定义的卷挂载。
&lt;strong>解决方案&lt;/strong>：如果某个卷只在开发环境需要（如 &lt;code>./src:/app/src&lt;/code>），&lt;strong>千万不要&lt;/strong>写在 &lt;code>compose.yaml&lt;/code> 里，必须只写在 &lt;code>compose.override.yaml&lt;/code> 里。&lt;/p>
&lt;h4 id="72-生产环境数据持久化">7.2 生产环境数据持久化
&lt;/h4>&lt;p>生产环境应使用 &lt;strong>命名卷（Named Volumes）&lt;/strong> 而非绑定挂载，以便于利用 Docker 卷插件进行备份或迁移，并规避宿主机文件权限问题。&lt;/p>
&lt;hr>
&lt;h3 id="8-总结全生命周期命令速查表">8. 总结：全生命周期命令速查表
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">环节&lt;/th>
&lt;th style="text-align: left">文件组合&lt;/th>
&lt;th style="text-align: left">关键参数&lt;/th>
&lt;th style="text-align: left">核心特征&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>开发 (Dev)&lt;/strong>&lt;/td>
&lt;td style="text-align: left">(默认) &lt;code>base&lt;/code> + &lt;code>override&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>up -d&lt;/code>&lt;/td>
&lt;td style="text-align: left">源码挂载、调试端口开放、多阶段构建 Target: Dev&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>测试 (CI)&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;code>-f base -f test&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>up --exit-code-from sut&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>tmpfs&lt;/code> 数据、健康检查依赖、自动化退出&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>生产 (Prod)&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;code>-f base -f prod&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>up -d --remove-orphans&lt;/code>&lt;/td>
&lt;td style="text-align: left">不可变镜像 Tag、资源限制、Secrets、重启策略&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>通过严格遵守&lt;strong>基座极简原则&lt;/strong>和&lt;strong>分层配置策略&lt;/strong>，Docker Compose 能够优雅地支撑从单机开发到生产交付的全链路需求，消除环境不一致带来的工程风险。&lt;/p></description></item><item><title>build x86 image in ARM MAC platform and devolopmet to remote server</title><link>https://www.zata.cc/p/build-x86-image-in-arm-mac-platform-and-devolopmet-to-remote-server/</link><pubDate>Thu, 11 Sep 2025 16:50:53 +0800</pubDate><guid>https://www.zata.cc/p/build-x86-image-in-arm-mac-platform-and-devolopmet-to-remote-server/</guid><description>&lt;img src="https://www.zata.cc/p/build-x86-image-in-arm-mac-platform-and-devolopmet-to-remote-server/images/index/index.png" alt="Featured image of post build x86 image in ARM MAC platform and devolopmet to remote server" />&lt;h1 id="在-arm-mac-上构建-x86-镜像并部署至远程服务器的终极指南">在 ARM Mac 上构建 x86 镜像并部署至远程服务器的终极指南
&lt;/h1>&lt;p>这份指南涵盖了从在 Apple Silicon (M1/M2/M3) Mac 上交叉编译构建 x86 (amd64) Docker 镜像，到将其以不同方式保存和部署的完整流程。&lt;/p>
&lt;hr>
&lt;h2 id="1-在-arm-mac-上构建-x86-镜像">1. 在 ARM Mac 上构建 x86 镜像
&lt;/h2>&lt;p>由于本地 Mac 是 &lt;code>arm64&lt;/code> 架构，而目标服务器通常是 &lt;code>x86_64&lt;/code> (即 &lt;code>amd64&lt;/code>)，我们需要进行交叉编译构建。Docker Desktop for Mac 内置的 QEMU 模拟器和 &lt;code>buildx&lt;/code> 工具使这一切变得简单。&lt;/p>
&lt;h3 id="准备工作">准备工作
&lt;/h3>&lt;ul>
&lt;li>一台 Apple Silicon 芯片的 Mac。&lt;/li>
&lt;li>安装最新版的 Docker Desktop for Mac。&lt;/li>
&lt;/ul>
&lt;h3 id="构建步骤">构建步骤
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>创建 Dockerfile&lt;/strong>
在一个空的项目目录中，创建一个 &lt;code>Dockerfile&lt;/code> 文件。明确指定基础镜像的平台是一个好习惯。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Dockerfile&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 使用 --platform 标志确保拉取的是 amd64 版本的镜像&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> --platform=linux/amd64 ubuntu:22.04&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">LABEL&lt;/span> &lt;span class="nv">maintainer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Your Name&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">LABEL&lt;/span> &lt;span class="nv">target_architecture&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;x86_64/amd64&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># RUN 指令会在 QEMU 模拟的 x86 环境中执行&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y curl&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 验证架构&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;本镜像架构为:&amp;#34;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> uname -m&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;bash&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>执行构建命令&lt;/strong>
使用 &lt;code>docker build&lt;/code> 命令并附带 &lt;code>--platform&lt;/code> 标志来指定目标架构。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在包含 Dockerfile 的目录下执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将 my-x86-app:1.0 替换为你想要的镜像名和标签&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker build --platform linux/amd64 -t my-x86-app:1.0 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>如果构建的时候网络失败，可以先使用 docker pull &amp;ndash;platform linux/amd64 &amp;lt;镜像名&amp;gt;:&amp;lt;标签&amp;gt; 提前拉取镜像&lt;/li>
&lt;li>&lt;code>--platform linux/amd64&lt;/code>: &lt;strong>核心参数&lt;/strong>，告诉 Docker 你要构建的是 x86 镜像。&lt;/li>
&lt;li>构建过程会比原生编译慢，因为指令需要通过 QEMU 模拟执行。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>验证本地镜像架构&lt;/strong>
构建完成后，镜像会保存在你的本地。使用 &lt;code>docker inspect&lt;/code> 来确认其架构。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker inspect my-x86-app:1.0 &lt;span class="p">|&lt;/span> grep Architecture
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>预期输出应为:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Architecture&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;amd64&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-保存与分发构建好的镜像">2. 保存与分发构建好的镜像
&lt;/h2>&lt;p>现在你本地有了一个 x86 架构的镜像 &lt;code>my-x86-app:1.0&lt;/code>，接下来可以根据需求将其分发出去。&lt;/p>
&lt;h3 id="方案-a-推送到镜像仓库团队协作cicd-推荐">方案 A: 推送到镜像仓库（团队协作/CI/CD 推荐）
&lt;/h3>&lt;p>这是最规范、最通用的方法，适用于所有需要网络访问的场景。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>标记镜像 (Tag)&lt;/strong>
为镜像打上符合仓库规范的标签。以 Docker Hub 为例，格式为 &lt;code>&amp;lt;用户名&amp;gt;/&amp;lt;仓库名&amp;gt;:&amp;lt;标签&amp;gt;&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker tag my-x86-app:1.0 your-dockerhub-username/my-x86-app:1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>登录仓库&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>推送镜像 (Push)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker push your-dockerhub-username/my-x86-app:1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>推送后，任何有权限的人或服务器都可以通过 &lt;code>docker pull&lt;/code> 命令拉取此镜像。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="方案-b-直接传输到另一台服务器最高效的-p2p-方式">方案 B: 直接传输到另一台服务器（最高效的 P2P 方式）
&lt;/h3>&lt;p>如果你想将在本地构建好的镜像，快速地发送到一台已经配置好 SSH 访问的服务器上，这是最佳选择。&lt;strong>此方法不依赖镜像仓库，也不在本地产生中间文件&lt;/strong>。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>使用 &lt;code>docker save&lt;/code> 和 &lt;code>ssh&lt;/code> 管道流式传输&lt;/strong>
这条命令将本地镜像打包后，通过 SSH 管道直接在远程服务器上加载。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将 your-user@your-server 替换为你的 SSH 登录信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker save my-x86-app:1.0 &lt;span class="p">|&lt;/span> ssh your-user@your-server &lt;span class="s1">&amp;#39;docker load&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>工作原理&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>docker save my-x86-app:1.0&lt;/code>: 将镜像打包成 tar 流输出到标准输出。&lt;/li>
&lt;li>&lt;code>|&lt;/code>: 管道，将左侧命令的输出作为右侧命令的输入。&lt;/li>
&lt;li>&lt;code>ssh ... 'docker load'&lt;/code>: 连接到远程服务器，并在服务器上执行 &lt;code>docker load&lt;/code> 命令，该命令从标准输入读取 tar 流并加载镜像。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>验证&lt;/strong>
SSH 登录到你的服务器 &lt;code>ssh your-user@your-server&lt;/code>，然后运行 &lt;code>docker images&lt;/code>，即可看到 &lt;code>my-x86-app:1.0&lt;/code>。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="方案-c-导出为文件后手动传输离线场景">方案 C: 导出为文件后手动传输（离线场景）
&lt;/h3>&lt;p>适用于完全离线的环境，或者需要将镜像文件存档的场景。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>保存为 &lt;code>.tar&lt;/code> 文件&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker save -o my-x86-app-1.0.tar my-x86-app:1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>拷贝文件&lt;/strong>
使用 &lt;code>scp&lt;/code> 或其他工具将 &lt;code>my-x86-app-1.0.tar&lt;/code> 文件传输到服务器。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">scp my-x86-app-1.0.tar your-user@your-server:/path/to/destination/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>在服务器上加载&lt;/strong>
SSH 登录服务器后，从文件加载镜像。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ssh your-user@your-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在服务器上执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker load -i /path/to/destination/my-x86-app-1.0.tar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="附录-关于-docker-context-的说明">附录: 关于 &lt;code>docker context&lt;/code> 的说明
&lt;/h2>&lt;p>&lt;code>docker context&lt;/code> 是一个强大的工具，但它的用途与上述方案 B 和 C 不同。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>docker context&lt;/code> 的作用&lt;/strong>: 它是用来&lt;strong>切换 Docker CLI 的控制目标&lt;/strong>。当你 &lt;code>docker context use your-server&lt;/code> 时，你的本地终端就变成了远程服务器 Docker 的遥控器。&lt;/li>
&lt;li>&lt;strong>使用场景&lt;/strong>: 如果你希望&lt;strong>构建过程本身就发生在远程服务器上&lt;/strong>（利用服务器的 CPU 和原生 x86 架构），那么就应该先切换 context，再运行 &lt;code>docker build&lt;/code>。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 切换控制目标到服务器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context use your-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 本地的源码会自动上传到服务器，并在服务器上完成构建&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker build -t my-app-built-on-server:1.0 .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 操作完成后切回本地&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context use default
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>这与“在本地构建，然后把产物（镜像）发送过去”是两种不同的工作流。&lt;/p>
&lt;hr>
&lt;p>希望这份整合后的指南能清晰地解答你的所有疑问！&lt;/p></description></item><item><title>Docker使用实战-compose教程</title><link>https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E5%AE%9E%E6%88%98-compose%E6%95%99%E7%A8%8B/</link><pubDate>Tue, 18 Mar 2025 15:48:18 +0800</pubDate><guid>https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E5%AE%9E%E6%88%98-compose%E6%95%99%E7%A8%8B/</guid><description>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E5%AE%9E%E6%88%98-compose%E6%95%99%E7%A8%8B/images/index/index.png" alt="Featured image of post Docker使用实战-compose教程" />&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#--------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 通用/推荐的关闭方式 (任何系统)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#--------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 向 Redis 服务器发送关闭信号 (会安全保存数据)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 这是最推荐的关闭方式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli shutdown
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#--------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 直接运行 / Docker 容器内 (你当前的环境)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#--------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动 (前台运行，日志会占满终端)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动 (使用配置文件，推荐)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-server /path/to/redis.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动 (后台运行，就像你之前用的)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-server &lt;span class="p">&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 关闭 (在 Docker 或直接运行时，用 cli 关闭)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli shutdown
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 强行关闭 (如果上面的命令无效)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 找到进程 PID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ps aux &lt;span class="p">|&lt;/span> grep redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 杀死进程 (用 PID 替换 12345)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">kill&lt;/span> &lt;span class="m">12345&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#--------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Linux (使用 systemd, 如 Ubuntu, CentOS)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#--------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl start redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (服务名也可能是 redis)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># sudo systemctl start redis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 停止服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl stop redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 重启服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl restart redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看服务状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl status redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 设置开机自启&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> redis-server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下面是一个详细的 Docker Compose 教程，涵盖了从基础概念到实际操作的全面内容。教程将逐步讲解 Docker Compose 的用途、安装、配置文件编写以及常见使用场景，帮助你快速上手。&lt;/p>
&lt;hr>
&lt;h2 id="docker-compose-起步教程">Docker Compose 起步教程
&lt;/h2>&lt;h3 id="什么是-docker-compose">什么是 Docker Compose？
&lt;/h3>&lt;p>Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。它通过一个 YAML 文件（通常是 &lt;code>docker-compose.yml&lt;/code>）来配置多个服务、网络和卷，然后使用单一命令启动和管理这些容器。相比手动运行 &lt;code>docker run&lt;/code>，它更适合需要协调多个容器的复杂应用。&lt;/p>
&lt;h4 id="适用场景">适用场景
&lt;/h4>&lt;ul>
&lt;li>开发环境：快速搭建包含前端、后端、数据库等的本地开发环境。&lt;/li>
&lt;li>测试：运行集成测试或模拟生产环境。&lt;/li>
&lt;li>小型部署：单机多容器应用的部署。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="安装-docker-compose">安装 Docker Compose
&lt;/h3>&lt;p>Docker Compose 有两种形式：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>旧版（docker-compose）&lt;/strong>：一个独立的 Python 工具。&lt;/li>
&lt;li>&lt;strong>新版（docker compose）&lt;/strong>：集成在 Docker CLI 中的插件，从 Docker 20.10 开始默认包含。&lt;/li>
&lt;/ol>
&lt;h4 id="检查是否已安装">检查是否已安装
&lt;/h4>&lt;p>运行以下命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>如果输出类似 &lt;code>Docker Compose version v2.x.x&lt;/code>，说明已安装新版。&lt;/li>
&lt;li>如果提示未找到命令，尝试 &lt;code>docker-compose --version&lt;/code> 检查旧版。&lt;/li>
&lt;/ul>
&lt;h4 id="安装如果需要">安装（如果需要）
&lt;/h4>&lt;ul>
&lt;li>&lt;strong>Linux / macOS / Windows（新版）&lt;/strong>：
如果你的 Docker 是最新版（20.10+），无需额外安装，直接用 &lt;code>docker compose&lt;/code>。&lt;/li>
&lt;li>&lt;strong>旧版安装（可选）&lt;/strong>：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo curl -L &lt;span class="s2">&amp;#34;https://github.com/docker/compose/releases/latest/download/docker-compose-&lt;/span>&lt;span class="k">$(&lt;/span>uname -s&lt;span class="k">)&lt;/span>&lt;span class="s2">-&lt;/span>&lt;span class="k">$(&lt;/span>uname -m&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> -o /usr/local/bin/docker-compose
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo chmod +x /usr/local/bin/docker-compose
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h4 id="前提条件">前提条件
&lt;/h4>&lt;ul>
&lt;li>已安装 Docker Engine（运行 &lt;code>docker --version&lt;/code> 确认）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="基本概念">基本概念
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>服务（Services）&lt;/strong>：一个服务对应一个容器，可以基于镜像启动，定义端口、卷等。&lt;/li>
&lt;li>&lt;strong>网络（Networks）&lt;/strong>：Compose 默认创建网络，所有服务可以互相通信。&lt;/li>
&lt;li>&lt;strong>卷（Volumes）&lt;/strong>：用于持久化数据，防止容器删除后数据丢失。&lt;/li>
&lt;li>&lt;strong>&lt;code>docker-compose.yml&lt;/code>&lt;/strong>：核心配置文件，描述整个应用。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="创建第一个-docker-compose-文件">创建第一个 Docker Compose 文件
&lt;/h3>&lt;p>假设我们要搭建一个简单的 Web 应用，包括：&lt;/p>
&lt;ul>
&lt;li>一个 Nginx Web 服务器。&lt;/li>
&lt;li>一个 Redis 数据库。&lt;/li>
&lt;/ul>
&lt;h4 id="1-创建项目目录">1. 创建项目目录
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir my-compose-app
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-compose-app
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2-编写-docker-composeyml">2. 编写 &lt;code>docker-compose.yml&lt;/code>
&lt;/h4>&lt;p>创建一个名为 &lt;code>docker-compose.yml&lt;/code> 的文件，内容如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用最新稳定的版本&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">web&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx:latest &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用官方 Nginx 镜像&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8080:80&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 主机端口 8080 映射到容器端口 80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./html:/usr/share/nginx/html &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 挂载本地 html 文件夹&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">redis:latest &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用官方 Redis 镜像&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;6379:6379&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 映射 Redis 默认端口&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">redis-data:/data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用命名卷存储数据&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 定义一个命名卷&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="文件解析">文件解析
&lt;/h4>&lt;ul>
&lt;li>&lt;code>version: '3.8'&lt;/code>：指定 Compose 文件格式版本。&lt;/li>
&lt;li>&lt;code>services&lt;/code>：定义两个服务：
&lt;ul>
&lt;li>&lt;code>web&lt;/code>：运行 Nginx，映射端口 8080:80，挂载本地 &lt;code>./html&lt;/code> 文件夹。&lt;/li>
&lt;li>&lt;code>redis&lt;/code>：运行 Redis，映射端口 6379，数据存储在命名卷 &lt;code>redis-data&lt;/code>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>volumes&lt;/code>：定义一个命名卷 &lt;code>redis-data&lt;/code>，用于持久化 Redis 数据。&lt;/li>
&lt;/ul>
&lt;h4 id="3-创建-html-文件可选">3. 创建 HTML 文件（可选）
&lt;/h4>&lt;p>在项目目录下创建 &lt;code>html&lt;/code> 文件夹，并添加一个简单的 &lt;code>index.html&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;h1&amp;gt;Hello, Docker Compose!&amp;lt;/h1&amp;gt;&amp;#34;&lt;/span> &amp;gt; html/index.html
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-启动应用">4. 启动应用
&lt;/h4>&lt;p>运行以下命令启动所有服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose up -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>-d&lt;/code> 表示后台运行。&lt;/li>
&lt;/ul>
&lt;h4 id="5-验证">5. 验证
&lt;/h4>&lt;ul>
&lt;li>检查运行中的容器：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>输出应显示 &lt;code>web&lt;/code> 和 &lt;code>redis&lt;/code> 服务状态为 &lt;code>Up&lt;/code>。&lt;/li>
&lt;li>访问 Nginx：打开浏览器，输入 &lt;code>http://localhost:8080&lt;/code>，应看到 &amp;ldquo;Hello, Docker Compose!&amp;quot;。&lt;/li>
&lt;li>检查 Redis：用 Redis 客户端连接 &lt;code>localhost:6379&lt;/code>。&lt;/li>
&lt;/ul>
&lt;h4 id="6-停止和清理">6. 停止和清理
&lt;/h4>&lt;p>停止服务并删除容器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose down
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果要删除卷数据：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose down -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="常用配置字段">常用配置字段
&lt;/h3>&lt;p>以下是 &lt;code>docker-compose.yml&lt;/code> 中常用的字段：&lt;/p>
&lt;h4 id="服务配置">服务配置
&lt;/h4>&lt;ul>
&lt;li>&lt;code>image&lt;/code>：指定镜像（如 &lt;code>nginx:latest&lt;/code>）。&lt;/li>
&lt;li>&lt;code>build&lt;/code>：构建镜像（替代 &lt;code>image&lt;/code>），指定 Dockerfile 路径：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">. &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 构建上下文路径&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">dockerfile&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Dockerfile &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Dockerfile 文件名&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;code>ports&lt;/code>：端口映射，格式 &lt;code>&amp;quot;主机端口:容器端口&amp;quot;&lt;/code>。&lt;/li>
&lt;li>&lt;code>volumes&lt;/code>：卷挂载，支持本地路径或命名卷：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./data:/app/data &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 本地路径&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">my-volume:/data &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 命名卷&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;code>environment&lt;/code>：环境变量：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">DEBUG=true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">PORT=8080&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;code>depends_on&lt;/code>：定义服务启动依赖：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">redis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h4 id="全局配置">全局配置
&lt;/h4>&lt;ul>
&lt;li>&lt;code>networks&lt;/code>：自定义网络：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">my-network&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">driver&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bridge&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;code>volumes&lt;/code>：定义命名卷：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">my-volume&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="进阶示例wordpress--mysql">进阶示例：WordPress + MySQL
&lt;/h3>&lt;p>以下是一个更复杂的例子，搭建 WordPress 和 MySQL：&lt;/p>
&lt;h4 id="docker-composeyml">&lt;code>docker-compose.yml&lt;/code>
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">mysql:8.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">db-data:/var/lib/mysql&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">MYSQL_ROOT_PASSWORD&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">somewordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">MYSQL_DATABASE&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">MYSQL_USER&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">MYSQL_PASSWORD&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">wordpress&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress:latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8080:80&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">wp-data:/var/www/html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">WORDPRESS_DB_HOST&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">db&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">WORDPRESS_DB_USER&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">WORDPRESS_DB_PASSWORD&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">WORDPRESS_DB_NAME&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">wordpress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">db&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">unless-stopped&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">wp-data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="启动">启动
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose up -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>访问 &lt;code>http://localhost:8080&lt;/code>，即可看到 WordPress 安装页面。&lt;/p>
&lt;hr>
&lt;h3 id="常用命令">常用命令
&lt;/h3>&lt;ul>
&lt;li>启动服务：&lt;code>docker compose up -d&lt;/code>&lt;/li>
&lt;li>停止服务：&lt;code>docker compose stop&lt;/code>&lt;/li>
&lt;li>删除服务（保留卷）：&lt;code>docker compose down&lt;/code>&lt;/li>
&lt;li>查看日志：&lt;code>docker compose logs [服务名]&lt;/code>&lt;/li>
&lt;li>重启服务：&lt;code>docker compose restart&lt;/code>&lt;/li>
&lt;li>构建镜像：&lt;code>docker compose build&lt;/code>&lt;/li>
&lt;li>查看服务状态：&lt;code>docker compose ps&lt;/code>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="保存和迁移">保存和迁移
&lt;/h3>&lt;h4 id="保存镜像">保存镜像
&lt;/h4>&lt;ol>
&lt;li>提交容器为镜像：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker commit my-compose-app_web_1 mywebimage:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>导出镜像：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker save -o myimages.tar mywebimage:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h4 id="备份卷">备份卷
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --rm -v my-compose-app_db-data:/data -v &lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>:/backup busybox tar cvf /backup/db-backup.tar /data
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="恢复">恢复
&lt;/h4>&lt;ol>
&lt;li>加载镜像：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker load -i myimages.tar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>恢复卷：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --rm -v my-compose-app_db-data:/data -v &lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>:/backup busybox tar xvf /backup/db-backup.tar -C /data
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>启动：&lt;code>docker compose up -d&lt;/code>&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="注意事项">注意事项
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>版本选择&lt;/strong>：根据 Docker Engine 版本选择合适的 &lt;code>version&lt;/code>（推荐 &lt;code>3.8&lt;/code> 或更高）。&lt;/li>
&lt;li>&lt;strong>命名冲突&lt;/strong>：多个 Compose 项目运行时，卷和网络名称可能冲突，可用 &lt;code>docker compose -p 项目名&lt;/code> 指定项目名。&lt;/li>
&lt;li>&lt;strong>最佳实践&lt;/strong>：尽量通过 &lt;code>build&lt;/code> 和 Dockerfile 定义镜像，而不是手动修改容器。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="dokploy--traefik-生产部署">Dokploy + Traefik 生产部署
&lt;/h2>&lt;h3 id="背景">背景
&lt;/h3>&lt;p>在 Dokploy 上使用 &lt;code>docker-compose.dokploy.yml&lt;/code> 部署应用后，页面访问返回 404。这个 404 通常不是前端 Nginx 的 SPA fallback 返回的，因为前端容器内的 &lt;code>/&lt;/code> 一般会 fallback 到 &lt;code>index.html&lt;/code>。&lt;/p>
&lt;p>更常见的原因是 Traefik 没有匹配到任何路由：&lt;/p>
&lt;ul>
&lt;li>&lt;code>DOMAIN&lt;/code> 没有被 Docker Compose 插值，生成了空的 &lt;code>Host(``)&lt;/code>。&lt;/li>
&lt;li>访问的域名不是 compose labels 里声明的 Host rule。&lt;/li>
&lt;li>容器没有接入 Dokploy 的 Traefik 网络。&lt;/li>
&lt;li>后端不健康，依赖后端健康状态的前端服务没有启动。&lt;/li>
&lt;/ul>
&lt;h3 id="dokploy-环境变量">Dokploy 环境变量
&lt;/h3>&lt;p>在 Dokploy 的 Compose Environment 中必须配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-env" data-lang="env">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DOMAIN&lt;/span>&lt;span class="o">=&lt;/span>fsense.example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意：&lt;/p>
&lt;ul>
&lt;li>&lt;code>DOMAIN&lt;/code> 只写域名，不要写 &lt;code>http://&lt;/code>、&lt;code>https://&lt;/code> 或路径。&lt;/li>
&lt;li>本地 &lt;code>.env.dokploy&lt;/code> 不一定会被 Dokploy 自动加载。除非 Dokploy 的 compose 命令显式使用 &lt;code>--env-file .env.dokploy&lt;/code>，否则要把变量复制到 Dokploy UI 的 Environment 中。&lt;/li>
&lt;li>密钥、数据库地址、对象存储配置等也放在 Dokploy Environment，不要写入公开仓库。&lt;/li>
&lt;/ul>
&lt;h3 id="dns-记录">DNS 记录
&lt;/h3>&lt;p>如果 compose 使用三套前端域名，需要把这些域名都解析到 Dokploy 服务器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">fsense.example.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">admin.fsense.example.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">addin.fsense.example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对应关系：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>域名&lt;/th>
&lt;th>服务&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>https://${DOMAIN}&lt;/code>&lt;/td>
&lt;td>&lt;code>outlook-index&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>https://admin.${DOMAIN}&lt;/code>&lt;/td>
&lt;td>&lt;code>admin-frontend&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>https://addin.${DOMAIN}&lt;/code>&lt;/td>
&lt;td>&lt;code>outlook-addin&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="traefik-labels-模板">Traefik labels 模板
&lt;/h3>&lt;p>主站前端示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.docker.network=dokploy-network&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.middlewares.outlook-index-https-redirect.redirectscheme.scheme=https&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index-web.rule=Host(`${DOMAIN:?Set DOMAIN in Dokploy environment}`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index-web.entrypoints=web&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index-web.middlewares=outlook-index-https-redirect&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index.rule=Host(`${DOMAIN:?Set DOMAIN in Dokploy environment}`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index.entrypoints=websecure&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index.tls=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-index.tls.certresolver=letsencrypt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.services.outlook-index.loadbalancer.server.port=80&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>管理后台示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.docker.network=dokploy-network&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.middlewares.admin-frontend-https-redirect.redirectscheme.scheme=https&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend-web.rule=Host(`admin.${DOMAIN:?Set DOMAIN in Dokploy environment}`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend-web.entrypoints=web&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend-web.middlewares=admin-frontend-https-redirect&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend.rule=Host(`admin.${DOMAIN:?Set DOMAIN in Dokploy environment}`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend.entrypoints=websecure&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend.tls=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.admin-frontend.tls.certresolver=letsencrypt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.services.admin-frontend.loadbalancer.server.port=80&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Outlook add-in 示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">OUTLOOK_ADDIN_HOST_URL=https://addin.${DOMAIN:?Set DOMAIN in Dokploy environment}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.enable=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.docker.network=dokploy-network&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.middlewares.outlook-addin-https-redirect.redirectscheme.scheme=https&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin-web.rule=Host(`addin.${DOMAIN:?Set DOMAIN in Dokploy environment}`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin-web.entrypoints=web&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin-web.middlewares=outlook-addin-https-redirect&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin.rule=Host(`addin.${DOMAIN:?Set DOMAIN in Dokploy environment}`)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin.entrypoints=websecure&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin.tls=true&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.routers.outlook-addin.tls.certresolver=letsencrypt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;traefik.http.services.outlook-addin.loadbalancer.server.port=80&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关键点：&lt;/p>
&lt;ul>
&lt;li>&lt;code>${DOMAIN:?Set DOMAIN in Dokploy environment}&lt;/code> 可以让 compose 在变量缺失时直接失败，避免部署出空 Host rule。&lt;/li>
&lt;li>&lt;code>traefik.docker.network=dokploy-network&lt;/code> 明确告诉 Traefik 走 Dokploy 网络。&lt;/li>
&lt;li>&lt;code>web&lt;/code> 路由负责 HTTP 入口，并通过 middleware 跳到 HTTPS。&lt;/li>
&lt;li>&lt;code>websecure&lt;/code> 路由负责 HTTPS 入口和证书。&lt;/li>
&lt;/ul>
&lt;h3 id="网络配置">网络配置
&lt;/h3>&lt;p>服务需要接入 Dokploy 的外部网络：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">dokploy-network&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">external&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>每个需要被 Traefik 或反向代理访问的服务都要加入该网络：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">dokploy-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果服务不需要公网访问，例如内部 backend，可以只 &lt;code>expose&lt;/code> 端口，不直接挂 Traefik label。&lt;/p>
&lt;h3 id="只重建后端导致前端-api-代理失效">只重建后端导致前端 API 代理失效
&lt;/h3>&lt;p>Dokploy / Docker Compose 部署时可能只重建发生变更的服务。例如后端代码变化后，只重建 &lt;code>backend&lt;/code>，而 &lt;code>frontend&lt;/code> 容器没有重启。&lt;/p>
&lt;p>这种情况下可能出现一个隐蔽问题：&lt;/p>
&lt;ul>
&lt;li>&lt;code>frontend&lt;/code> 里的 Nginx 启动时解析过 &lt;code>backend:8000&lt;/code>。&lt;/li>
&lt;li>后端容器重建后，Docker 内部 IP 发生变化。&lt;/li>
&lt;li>前端容器没有重启，Nginx 仍然可能持有旧的 upstream 解析结果。&lt;/li>
&lt;li>页面本身还能打开，但 &lt;code>/api/*&lt;/code> 代理到后端失败，看起来像&amp;quot;前端路由&amp;quot;或&amp;quot;接口路由&amp;quot;坏了。&lt;/li>
&lt;/ul>
&lt;p>推荐做法是让 Nginx 使用 Docker 内置 DNS，并定期重解析后端服务名：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">resolver&lt;/span> &lt;span class="mi">127&lt;/span>&lt;span class="s">.0.0.11&lt;/span> &lt;span class="s">ipv6=off&lt;/span> &lt;span class="s">valid=5s&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">location&lt;/span> &lt;span class="s">/api/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">set&lt;/span> &lt;span class="nv">$backend_upstream&lt;/span> &lt;span class="n">backend&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">8000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">rewrite&lt;/span> &lt;span class="s">^/api/?(.*)&lt;/span>$ &lt;span class="s">/&lt;/span>&lt;span class="nv">$1&lt;/span> &lt;span class="s">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_pass&lt;/span> &lt;span class="s">http://&lt;/span>&lt;span class="nv">$backend_upstream&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_http_version&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="s">.1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_set_header&lt;/span> &lt;span class="s">Host&lt;/span> &lt;span class="nv">$host&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_set_header&lt;/span> &lt;span class="s">X-Forwarded-For&lt;/span> &lt;span class="nv">$proxy_add_x_forwarded_for&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_set_header&lt;/span> &lt;span class="s">X-Forwarded-Proto&lt;/span> &lt;span class="nv">$scheme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意：&lt;code>proxy_pass&lt;/code> 一旦使用变量，就不要再写成 &lt;code>proxy_pass http://$backend_upstream/;&lt;/code>。带变量的 &lt;code>proxy_pass&lt;/code> 不再沿用普通 &lt;code>proxy_pass http://backend:8000/;&lt;/code> 的 URI 替换语义，尾部 &lt;code>/&lt;/code> 很容易把请求错误地转发到根路径。&lt;/p>
&lt;p>这个问题和 Traefik Host rule 404 不是同一个层面：&lt;/p>
&lt;ul>
&lt;li>Traefik Host rule 错误：请求进不了前端容器，通常直接 404。&lt;/li>
&lt;li>Nginx backend DNS 过期：请求能进前端容器，但 &lt;code>/api/*&lt;/code> 代理后端失败。&lt;/li>
&lt;/ul>
&lt;h3 id="macos-docker-build-使用-clash-代理">macOS Docker build 使用 Clash 代理
&lt;/h3>&lt;p>在 macOS + Docker Desktop + Clash 环境里，&lt;code>docker compose build&lt;/code> 和宿主机终端不是同一个网络视角。宿主机里的 &lt;code>127.0.0.1&lt;/code> 是 Mac 自己，但 Docker build 容器里的 &lt;code>127.0.0.1&lt;/code> 是构建容器本身。&lt;/p>
&lt;p>一个典型现象是前端镜像构建时报平台原生依赖缺失，例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Error: Cannot find module &amp;#39;../lightningcss.linux-arm64-musl.node&amp;#39;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果日志里 &lt;code>RUN npm ci&lt;/code> 显示为 &lt;code>CACHED&lt;/code>，说明这次构建没有重新安装依赖。缓存层里可能缺了 Alpine / Linux / arm64 需要的 optional native package，例如 &lt;code>lightningcss-linux-arm64-musl&lt;/code>。这类问题看起来像 Node 包损坏，本质上经常是“旧 Docker build 缓存层 + 网络代理不稳定”共同导致的。&lt;/p>
&lt;p>先强制绕过缓存验证真实问题：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose build --no-cache kimi-ppt-frontend
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果错误变成 &lt;code>npm ERR! code ECONNRESET&lt;/code>，就说明重新拉包时 Docker build 到 npm registry 的链路不稳定。&lt;/p>
&lt;p>检查当前 shell 代理：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">printenv &lt;span class="p">|&lt;/span> grep -i proxy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>检查 &lt;code>~/.zshrc&lt;/code> 是否有重复覆盖：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">grep -nE &lt;span class="s1">&amp;#39;proxy|Proxy|Clash|789[0-9]&amp;#39;&lt;/span> ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>容易出现的错误配置是前面写了一个可用端口，后面又覆盖成另一个端口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">http_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://127.0.0.1:7897&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://127.0.0.1:7897&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">ALL_PROXY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;socks5://127.0.0.1:7897&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 后面又覆盖了 http_proxy / https_proxy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>http://127.0.0.1:7899 &lt;span class="nv">http_proxy&lt;/span>&lt;span class="o">=&lt;/span>http://127.0.0.1:7899
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>应该保留一套一致配置。普通宿主机命令可以继续使用 &lt;code>127.0.0.1&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">http_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://127.0.0.1:7897&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://127.0.0.1:7897&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">HTTP_PROXY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$http_proxy&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">HTTPS_PROXY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$https_proxy&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">ALL_PROXY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;socks5://127.0.0.1:7897&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">all_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$ALL_PROXY&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">NO_PROXY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;127.0.0.1,localhost,::1,host.docker.internal,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">no_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$NO_PROXY&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>但 Docker build 里要把代理地址改成宿主机网关：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose build --no-cache &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --build-arg &lt;span class="nv">HTTP_PROXY&lt;/span>&lt;span class="o">=&lt;/span>http://host.docker.internal:7897 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --build-arg &lt;span class="nv">HTTPS_PROXY&lt;/span>&lt;span class="o">=&lt;/span>http://host.docker.internal:7897 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> kimi-ppt-frontend
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果前端镜像已经重新构建成功，再构建整套服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker compose up -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>验证入口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -I http://localhost/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl http://localhost:8000/health
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果 &lt;code>http://localhost/&lt;/code> 返回 &lt;code>200 OK&lt;/code>，&lt;code>/health&lt;/code> 返回类似 &lt;code>{&amp;quot;status&amp;quot;:&amp;quot;ok&amp;quot;}&lt;/code>，说明前端和后端容器都已经正常启动。&lt;/p>
&lt;p>注意：不要把完整 &lt;code>~/.zshrc&lt;/code> 粘贴到公开位置。很多人会把 API key、token 和代理配置都放在同一个 shell 配置文件里。&lt;/p>
&lt;h3 id="404-排查命令">404 排查命令
&lt;/h3>&lt;p>在服务器或 Dokploy 构建环境中检查最终插值结果：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose -f docker-compose.dokploy.yml config &lt;span class="p">|&lt;/span> grep &lt;span class="s1">&amp;#39;Host&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>正常情况下应该看到实际域名，例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Host(`fsense.example.com`)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Host(`admin.fsense.example.com`)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Host(`addin.fsense.example.com`)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>检查容器是否运行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker ps &lt;span class="p">|&lt;/span> grep app-
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>检查后端健康和启动日志：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker logs app-backend --tail&lt;span class="o">=&lt;/span>&lt;span class="m">100&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果后端不健康，依赖 &lt;code>condition: service_healthy&lt;/code> 的前端服务可能不会启动，Traefik 也就没有可用服务。&lt;/p>
&lt;h3 id="修复检查清单">修复检查清单
&lt;/h3>&lt;ul>
&lt;li>Dokploy Environment 中已设置 &lt;code>DOMAIN&lt;/code>，且值不包含协议和路径。&lt;/li>
&lt;li>DNS 已解析 &lt;code>${DOMAIN}&lt;/code>、&lt;code>admin.${DOMAIN}&lt;/code>、&lt;code>addin.${DOMAIN}&lt;/code>。&lt;/li>
&lt;li>compose labels 中有 &lt;code>traefik.docker.network=dokploy-network&lt;/code>。&lt;/li>
&lt;li>HTTPS router 使用 &lt;code>entrypoints=websecure&lt;/code>、&lt;code>tls=true&lt;/code>、&lt;code>tls.certresolver=letsencrypt&lt;/code>。&lt;/li>
&lt;li>HTTP router 使用 &lt;code>entrypoints=web&lt;/code> 并绑定 HTTPS redirect middleware。&lt;/li>
&lt;li>&lt;code>docker compose config&lt;/code> 输出的 Host rule 是实际域名，不是空字符串。&lt;/li>
&lt;li>&lt;code>backend&lt;/code> 健康检查通过，前端容器正常启动。&lt;/li>
&lt;li>如果只重建过 &lt;code>backend&lt;/code> 后 &lt;code>/api/*&lt;/code> 失效，检查前端 Nginx 是否使用 Docker DNS 运行时重解析。&lt;/li>
&lt;/ul>
&lt;hr></description></item><item><title>docker使用教程</title><link>https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</link><pubDate>Tue, 18 Mar 2025 09:57:06 +0800</pubDate><guid>https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</guid><description>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-10.png" alt="Featured image of post docker使用教程" />&lt;ul>
&lt;li>
&lt;p>&lt;a class="link" href="#docker%e5%ae%b9%e5%99%a8%e5%b8%b8%e7%94%a8%e5%91%bd%e4%bb%a4" >docker容器常用命令&lt;/a>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="#%e9%95%9c%e5%83%8f-image-%e7%9b%b8%e5%85%b3%e5%91%bd%e4%bb%a4" >镜像 (Image) 相关命令&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%ae%b9%e5%99%a8-container-%e7%9b%b8%e5%85%b3%e5%91%bd%e4%bb%a4" >容器 (Container) 相关命令&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker-run-%e5%b8%b8%e7%94%a8%e9%80%89%e9%a1%b9" >&lt;code>docker run&lt;/code> 常用选项&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker-compose-%e7%9b%b8%e5%85%b3%e5%91%bd%e4%bb%a4" >Docker Compose 相关命令&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e7%b3%bb%e7%bb%9f%e4%b8%8e%e8%b5%84%e6%ba%90%e7%ae%a1%e7%90%86%e5%91%bd%e4%bb%a4" >系统与资源管理命令&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e7%ae%80%e5%8d%95%e5%bc%80%e5%a7%8b%e4%b8%80%e4%b8%aadocker%e9%95%9c%e5%83%8f%e7%9a%84%e4%bd%bf%e7%94%a8" >简单开始一个docker镜像的使用&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;a class="link" href="#docker%e4%bd%bf%e7%94%a8%e6%8a%80%e5%b7%a7" >Docker使用技巧&lt;/a>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="#docker%e7%9a%84%e5%8d%b7%e6%8c%82%e8%bd%bd%e5%8a%9f%e8%83%bd" >docker的卷挂载功能&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%b0%86%e9%95%9c%e5%83%8f%e6%8e%a8%e9%80%81%e5%88%b0%e8%bf%9c%e7%a8%8b%e4%bb%93%e5%ba%93" >将镜像推送到远程仓库&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker-context-%e6%95%99%e7%a8%8b" >Docker Context 教程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%9c%a8-windows-%e4%b8%8a%e5%ae%9e%e7%8e%b0-docker-in-docker-dind-%e7%bb%88%e6%9e%81%e6%95%99%e7%a8%8b" >在 Windows 上实现 Docker in Docker (DinD) 终极教程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e4%bd%bf%e7%94%a8%e5%b7%b2%e6%9c%89%e5%ae%b9%e5%99%a8%e5%88%9b%e5%bb%ba%e9%95%9c%e5%83%8f" >使用已有容器创建镜像&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%bc%80%e5%90%af%e9%87%8d%e5%90%afssh%e6%9c%8d%e5%8a%a1" >开启/重启ssh服务&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker-%e6%96%87%e4%bb%b6%e4%bc%a0%e8%be%93" >docker 文件传输&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e4%bf%ae%e6%94%b9%e6%9c%8d%e5%8a%a1%e5%99%a8%e9%85%8d%e7%bd%ae%e5%85%81%e8%ae%b8%e9%80%9a%e8%bf%87%e6%ad%a4%e6%9c%8d%e5%8a%a1%e5%99%a8%e8%bf%9b%e8%a1%8cssh%e8%bd%ac%e5%8f%91" >修改服务器配置允许通过此服务器进行ssh转发&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker%e5%ae%b9%e5%99%a8%e6%b7%bb%e5%8a%a0%e5%af%b9%e5%a4%96%e6%98%a0%e5%b0%84%e7%ab%af%e5%8f%a3" >docker容器添加对外映射端口&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker%e8%ae%be%e7%bd%ae%e5%8f%af%e7%94%a8cpu%e6%a0%b8%e5%bf%83%e6%95%b0%e9%87%8f" >docker设置可用CPU核心数量&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%ae%b9%e5%99%a8%e7%ab%af%e5%8f%a3%e8%bd%ac%e5%8f%91%e9%97%ae%e9%a2%98%e6%8e%92%e6%9f%a5%e5%92%8c%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88" >容器端口转发问题排查和解决方案&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%b1%80%e5%9f%9f%e7%bd%91%e8%ae%bf%e9%97%aedocker%e5%ae%b9%e5%99%a8%e5%ae%8c%e6%95%b4%e6%8c%87%e5%8d%97" >局域网访问Docker容器完整指南&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#docker%e8%ae%be%e7%bd%ae%e5%8f%af%e7%94%a8cpu%e6%a0%b8%e5%bf%83%e6%95%b0%e9%87%8f" >docker设置可用CPU核心数量&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;a class="link" href="#docker%e5%ae%9e%e6%88%98" >docker实战&lt;/a>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="#docker%e4%bd%bf%e7%94%a8%e5%ae%9e%e6%88%98-mysql%e5%92%8cubuntu%e5%ae%b9%e5%99%a8" >Docker使用实战-MySQL和Ubuntu容器&lt;/a>
&lt;ul>
&lt;li>&lt;a class="link" href="#1-%e5%ae%89%e8%a3%85docker-mysql%e5%ae%b9%e5%99%a8" >1. 安装docker mysql容器&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#2-%e5%ae%89%e8%a3%85ubuntu%e5%ae%b9%e5%99%a8%e5%b9%b6%e9%85%8d%e7%bd%ae" >2. 安装Ubuntu容器并配置&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a class="link" href="#docker%e4%bd%bf%e7%94%a8%e5%ae%9e%e6%88%98-%e5%ae%b9%e5%99%a8%e4%bf%9d%e5%ad%98%e5%92%8c%e8%bf%81%e7%a7%bb" >Docker使用实战-容器保存和迁移&lt;/a>
&lt;ul>
&lt;li>&lt;a class="link" href="#%e6%96%b9%e6%b3%95%e4%b8%80%e5%b0%86%e5%ae%b9%e5%99%a8%e4%bf%9d%e5%ad%98%e4%b8%ba%e9%95%9c%e5%83%8f" >方法一：将容器保存为镜像&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e6%96%b9%e6%b3%95%e4%ba%8c%e7%9b%b4%e6%8e%a5%e5%af%bc%e5%87%ba%e5%ae%b9%e5%99%a8" >方法二：直接导出容器&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a class="link" href="#docker-%e6%9c%8d%e5%8a%a1%e9%83%a8%e7%bd%b2%e6%95%99%e7%a8%8b" >Docker 服务部署教程&lt;/a>
&lt;ul>
&lt;li>&lt;a class="link" href="#%e7%8e%af%e5%a2%83%e5%87%86%e5%a4%87" >环境准备&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83%e9%83%a8%e7%bd%b2%e4%b8%80%e4%b8%aa-python-web-%e5%ba%94%e7%94%a8" >实战演练：部署一个 Python Web 应用&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e8%bf%9b%e9%98%b6%e4%bd%bf%e7%94%a8-docker-compose-%e7%bc%96%e6%8e%92%e6%9c%8d%e5%8a%a1" >进阶：使用 Docker Compose 编排服务&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a class="link" href="#docker%e6%9c%8d%e5%8a%a1%e9%83%a8%e7%bd%b2%e6%95%99%e7%a8%8b2-%e4%bd%bf%e7%94%a8uv%e6%9b%bf%e4%bb%a3pip" >Docker服务部署教程2-使用uv替代pip&lt;/a>
&lt;ul>
&lt;li>&lt;a class="link" href="#%e4%bd%bf%e7%94%a8-uv-%e7%9a%84%e6%a0%b8%e5%bf%83%e6%94%b9%e5%8a%a8" >使用 UV 的核心改动&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e6%9b%b4%e6%96%b0%e5%90%8e%e7%9a%84-dockerfile-%e4%bd%bf%e7%94%a8-uv" >更新后的 &lt;code>Dockerfile&lt;/code> (使用 uv)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a class="link" href="#docker%e5%ae%b9%e5%99%a8%e7%9a%84%e9%83%a8%e7%bd%b2%e6%95%99%e7%a8%8b3-%e5%b0%86%e5%ae%b9%e5%99%a8%e9%83%a8%e7%bd%b2%e5%88%b0%e6%9c%8d%e5%8a%a1%e5%99%a8%e4%b8%8a" >docker容器的部署教程3-将容器部署到服务器上&lt;/a>
&lt;ul>
&lt;li>&lt;a class="link" href="#%e6%a0%b8%e5%bf%83%e6%b5%81%e7%a8%8b" >核心流程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e7%ac%ac%e5%9b%9b%e6%ad%a5%e9%80%89%e6%8b%a9%e9%83%a8%e7%bd%b2%e6%96%b9%e6%a1%88-deploy" >第四步：选择部署方案 (Deploy)&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%a6%82%e4%bd%95%e9%80%89%e6%8b%a9" >如何选择？&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a class="link" href="#%e8%bf%90%e8%a1%8c%e5%ae%b9%e5%99%a8%e4%ba%86%e4%bd%86%e6%98%af%e5%b9%b6%e6%b2%a1%e6%9c%89%e7%94%9f%e6%95%88-%e9%80%9a%e8%bf%87%e6%9f%a5%e7%9c%8b%e6%97%a5%e5%bf%97%e8%a7%a3%e5%86%b3%e9%97%ae%e9%a2%98" >运行容器了但是并没有生效-通过查看日志解决问题&lt;/a>
&lt;ul>
&lt;li>&lt;a class="link" href="#%e7%ac%ac%e4%b8%80%e6%ad%a5%e6%a3%80%e6%9f%a5%e5%ae%b9%e5%99%a8%e6%98%af%e5%90%a6%e6%ad%a3%e5%9c%a8%e8%bf%90%e8%a1%8c" >第一步：检查容器是否正在运行&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e6%9f%a5%e7%9c%8b%e5%ae%b9%e5%99%a8%e6%97%a5%e5%bf%97" >第二步：查看容器日志&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e7%ac%ac%e4%b8%89%e6%ad%a5%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98%e5%92%8c%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88%e6%b1%87%e6%80%bb" >第三步：常见问题和解决方案汇总&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#from-werkzeugurls-import-url_quote-%e9%97%ae%e9%a2%98%e6%a0%b9%e6%ba%90" >from werkzeug.urls import url_quote 问题根源&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88" >解决方案&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a class="link" href="#wsl%e4%b8%ad%e9%95%9c%e5%83%8f%e7%bd%91%e7%bb%9c%e4%b8%8edocker%e7%bd%91%e7%bb%9c%e5%86%b2%e7%aa%81%e4%bd%bf%e7%94%a8netsh-winsock-reset%e4%bf%ae%e6%94%b9%e7%bd%91%e7%bb%9c%e8%ae%be%e7%bd%ae%e4%b9%8b%e5%90%8edocker%e8%bf%9e%e4%b8%8d%e4%b8%8a----%e8%a7%a3%e5%86%b3" >wsl中镜像网络与docker网络冲突，使用netsh winsock reset修改网络设置之后，docker连不上 &amp;ndash; 解决&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="#%e5%85%b7%e6%9c%89SSL%e8%af%81%e4%b9%a6%e7%9a%84%e5%90%8e%e7%ab%af%e5%9c%a8docker%e7%8e%af%e5%a2%83%e4%b8%ad%e5%87%ba%e7%8e%b0%e9%97%ae%e9%a2%98" >具有SSL证书的后端在docker环境中出现问题&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h1 id="常用命令">常用命令
&lt;/h1>&lt;p>&lt;strong>docker 单机常用命令&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 列出所有正在运行的容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps &lt;span class="o">[&lt;/span>-a&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 停止并删除容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stop &amp;lt;容器ID或容器名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm &amp;lt;容器ID或容器名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 删除指定卷&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker volume rm &amp;lt;卷ID或卷名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看所有容器的统计信息, --no-stream 不实时更新&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stats &lt;span class="o">[&lt;/span>容器ID或容器名&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>--no-stream&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 清理所有停止的容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm &lt;span class="k">$(&lt;/span>docker ps -aq&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 清理所有悬空的镜像&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rmi &lt;span class="k">$(&lt;/span>docker images -qf &lt;span class="s2">&amp;#34;dangling=true&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 清理所有悬空的网络&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker network prune &lt;span class="k">$(&lt;/span>docker network ls -qf &lt;span class="s2">&amp;#34;dangling=true&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 清理所有悬空的卷&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker volume prune &lt;span class="k">$(&lt;/span>docker volume ls -qf &lt;span class="s2">&amp;#34;dangling=true&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将容器的当前状态保存为新的镜像&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker commit &amp;lt;my-container&amp;gt; &amp;lt;my-new-image&amp;gt; &lt;span class="o">[&lt;/span>:tag&lt;span class="o">]&lt;/span> &lt;span class="c1"># 如果tag不填就默认是latest&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>docker swarm 常用命令&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看容器日志&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker logs -f &amp;lt;容器ID或容器名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化 Swarm 集群（管理节点）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm init --advertise-addr &amp;lt;IP地址&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 获取管理节点加入命令 &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm join-token manager
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 获取加入集群的命令（在工作节点执行）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm join-token worker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 加入 Swarm 集群&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm join --token &amp;lt;token&amp;gt; --advertise-addr &amp;lt;IP地址&amp;gt; --data-path-addr &amp;lt;IP地址&amp;gt; --listen-addr 0.0.0.0:2377 &amp;lt;管理节点IP&amp;gt;:2377
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 离开 Swarm 集群&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm leave
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 强制离开集群&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm leave --force
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 更新 Swarm 配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm update --param value
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看集群节点列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node ls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看节点详细信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node inspect &amp;lt;节点ID或名称&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 更新节点属性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node update --availability active/pause/drain &amp;lt;节点ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 提升节点为管理节点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node promote &amp;lt;节点ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 降级管理节点为工作节点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node demote &amp;lt;节点ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 删除节点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker node rm &amp;lt;节点ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service create --name &amp;lt;服务名&amp;gt; --replicas &amp;lt;副本数&amp;gt; -p &amp;lt;端口&amp;gt; &amp;lt;镜像名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看服务列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service ls
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看服务详细信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service inspect &amp;lt;服务名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看服务运行的任务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service ps &amp;lt;服务名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 扩展服务副本数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service scale &amp;lt;服务名&amp;gt;&lt;span class="o">=&lt;/span>&amp;lt;副本数&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 更新服务配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service update --image &amp;lt;新镜像&amp;gt; &amp;lt;服务名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 更新服务端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service update --publish-add &amp;lt;端口&amp;gt; &amp;lt;服务名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 删除服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service rm &amp;lt;服务名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看服务日志&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker service logs &amp;lt;服务名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建覆盖网络&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker network create --driver overlay &amp;lt;网络名&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看网络列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker network ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>镜像 (Image) 相关命令&lt;/p>
&lt;p>镜像是创建容器的基础，包含了应用程序及其依赖的环境。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">命令&lt;/th>
&lt;th style="text-align: left">描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker images&lt;/code>&lt;/td>
&lt;td style="text-align: left">列出本地所有镜像。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker pull [镜像名]:[标签]&lt;/code>&lt;/td>
&lt;td style="text-align: left">从 Docker Hub 或其他镜像仓库拉取镜像 (例如: &lt;code>docker pull ubuntu:22.04&lt;/code>)。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker push [用户名]/[镜像名]:[标签]&lt;/code>&lt;/td>
&lt;td style="text-align: left">将本地镜像推送到 Docker Hub 或其他镜像仓库。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker build -t [镜像名]:[标签] .&lt;/code>&lt;/td>
&lt;td style="text-align: left">根据当前目录下的 Dockerfile 构建镜像 (例如: &lt;code>docker build -t my-app:1.0 .&lt;/code>)。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker rmi [镜像ID或镜像名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">删除一个或多个镜像 (例如: &lt;code>docker rmi ubuntu:22.04&lt;/code>)。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker tag [源镜像] [新镜像名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">为本地镜像添加一个新的标签 (例如: &lt;code>docker tag my-app:1.0 my-app:latest&lt;/code>)。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker history [镜像名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">查看镜像的构建历史。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker save -o [文件名.tar] [镜像名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">将镜像保存为一个 tar 归档文件。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker load -i [文件名.tar]&lt;/code>&lt;/td>
&lt;td style="text-align: left">从一个 tar 归档文件加载镜像。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker rmi $(docker images -qf &amp;quot;dangling=true&amp;quot;)&lt;/code>&lt;/td>
&lt;td style="text-align: left">删除所有悬空的（dangling）镜像。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>容器 (Container) 相关命令&lt;/p>
&lt;p>容器是镜像的运行实例，是真正运行应用程序的地方。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">命令&lt;/th>
&lt;th style="text-align: left">描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker run [选项] [镜像名] [命令]&lt;/code>&lt;/td>
&lt;td style="text-align: left">创建并启动一个新的容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker ps&lt;/code>&lt;/td>
&lt;td style="text-align: left">列出所有正在运行的容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker ps -a&lt;/code>&lt;/td>
&lt;td style="text-align: left">列出所有容器（包括已停止的）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker start [容器ID或容器名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">启动一个或多个已停止的容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker stop [容器ID或容器名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">停止一个或多个正在运行的容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker restart [容器ID或容器名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">重启一个容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker rm [容器ID或容器名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">删除一个或多个容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker rm -f $(docker ps -aq)&lt;/code>&lt;/td>
&lt;td style="text-align: left">强制删除所有容器（无论运行中还是已停止）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker logs [容器ID或容器名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">查看容器的日志输出 (&lt;code>-f&lt;/code> 选项可以持续跟踪日志)。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker exec -it [容器ID] [命令]&lt;/code>&lt;/td>
&lt;td style="text-align: left">在正在运行的容器中执行一个交互式命令 (例如: &lt;code>docker exec -it my-nginx /bin/bash&lt;/code>)。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker cp [本地路径] [容器ID]:[容器内路径]&lt;/code>&lt;/td>
&lt;td style="text-align: left">在宿主机和容器之间复制文件/文件夹。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker stats&lt;/code>&lt;/td>
&lt;td style="text-align: left">实时显示容器的资源使用情况。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker top [容器ID]&lt;/code>&lt;/td>
&lt;td style="text-align: left">查看容器内运行的进程。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker inspect [容器ID或镜像ID]&lt;/code>&lt;/td>
&lt;td style="text-align: left">查看容器或镜像的详细信息（元数据）。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="docker-run-常用选项">&lt;code>docker run&lt;/code> 常用选项
&lt;/h3>&lt;ul>
&lt;li>&lt;code>-d&lt;/code>: 后台运行容器（detached mode）。&lt;/li>
&lt;li>&lt;code>-p [宿主机端口]:[容器端口]&lt;/code>: 端口映射。&lt;/li>
&lt;li>&lt;code>-v [宿主机路径]:[容器内路径]&lt;/code>: 数据卷挂载。&lt;/li>
&lt;li>&lt;code>--name [容器名]&lt;/code>: 为容器指定一个名称。&lt;/li>
&lt;li>&lt;code>-it&lt;/code>: 启动交互式会话 (&lt;code>-i&lt;/code> 交互, &lt;code>-t&lt;/code> 分配一个伪终端)。&lt;/li>
&lt;li>&lt;code>--rm&lt;/code>: 容器停止后自动删除。&lt;/li>
&lt;li>&lt;code>-e [环境变量名]=[值]&lt;/code>: 设置环境变量。&lt;/li>
&lt;li>&lt;code>--network [网络名]&lt;/code>: 将容器连接到指定网络。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>示例:&lt;/strong>
&lt;code>docker run -d -p 8080:80 --name my-web-server -v /webapp:/usr/share/nginx/html nginx&lt;/code>&lt;/p>
&lt;p>Docker Compose 相关命令&lt;/p>
&lt;p>用于定义和运行多容器 Docker 应用程序的工具。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">命令&lt;/th>
&lt;th style="text-align: left">描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose up&lt;/code>&lt;/td>
&lt;td style="text-align: left">根据 &lt;code>docker-compose.yml&lt;/code> 创建并启动所有服务。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose up -d&lt;/code>&lt;/td>
&lt;td style="text-align: left">在后台创建并启动所有服务。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose down&lt;/code>&lt;/td>
&lt;td style="text-align: left">停止并移除由 &lt;code>up&lt;/code> 创建的容器、网络、卷。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose ps&lt;/code>&lt;/td>
&lt;td style="text-align: left">列出 &lt;code>docker-compose.yml&lt;/code> 文件中定义的所有容器的状态。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose logs&lt;/code>&lt;/td>
&lt;td style="text-align: left">查看所有服务的日志。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose logs -f [服务名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">实时跟踪特定服务的日志。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose build&lt;/code>&lt;/td>
&lt;td style="text-align: left">构建或重新构建服务。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose pull&lt;/code>&lt;/td>
&lt;td style="text-align: left">拉取服务依赖的镜像。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose exec [服务名] [命令]&lt;/code>&lt;/td>
&lt;td style="text-align: left">在指定的服务容器中执行命令。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose stop&lt;/code>&lt;/td>
&lt;td style="text-align: left">停止服务，但不删除容器。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker-compose start&lt;/code>&lt;/td>
&lt;td style="text-align: left">启动已停止的服务。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>系统与资源管理命令&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">命令&lt;/th>
&lt;th style="text-align: left">描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker system prune&lt;/code>&lt;/td>
&lt;td style="text-align: left">清理系统中未使用的 Docker 资源（容器、镜像、网络、卷）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker system prune -a --volumes&lt;/code>&lt;/td>
&lt;td style="text-align: left">更彻底的清理，会删除所有未使用的镜像和数据卷。 &lt;strong>请谨慎使用！&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker system df&lt;/code>&lt;/td>
&lt;td style="text-align: left">查看 Docker 的磁盘使用情况。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker volume ls&lt;/code>&lt;/td>
&lt;td style="text-align: left">列出所有的数据卷。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker volume rm [卷名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">删除一个或多个数据卷。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker network ls&lt;/code>&lt;/td>
&lt;td style="text-align: left">列出所有的网络。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker network rm [网络名]&lt;/code>&lt;/td>
&lt;td style="text-align: left">删除一个或多个网络。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker login&lt;/code>&lt;/td>
&lt;td style="text-align: left">登录到 Docker Hub 或其他镜像仓库。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker logout&lt;/code>&lt;/td>
&lt;td style="text-align: left">登出 Docker Hub 或其他镜像仓库。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker info&lt;/code>&lt;/td>
&lt;td style="text-align: left">显示 Docker 系统范围的信息。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>docker version&lt;/code>&lt;/td>
&lt;td style="text-align: left">显示 Docker 的版本信息。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h1 id="docker-安装">docker 安装
&lt;/h1>&lt;h2 id="阿里云安装docker">阿里云安装docker
&lt;/h2>&lt;p>&lt;strong>阿里云服务器&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 先下载 GPGkey&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo chmod a+r /etc/apt/keyrings/docker.asc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add the Aliyun Repository&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;deb [arch=&lt;/span>&lt;span class="k">$(&lt;/span>dpkg --print-architecture&lt;span class="k">)&lt;/span>&lt;span class="s2"> signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.aliyun.com/docker-ce/linux/ubuntu &lt;/span>&lt;span class="k">$(&lt;/span>lsb_release -cs&lt;span class="k">)&lt;/span>&lt;span class="s2"> stable&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Update your Package List&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install Docker &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#配置镜像加速&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo mkdir -p /etc/docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo tee /etc/docker/daemon.json &lt;span class="s">&amp;lt;&amp;lt;-&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;#34;registry-mirrors&amp;#34;: [&amp;#34;https://docker.m.daocloud.io&amp;#34;,&amp;#34;https://&amp;lt;你的加速镜像地址，进入阿里云镜像加速服务获取&amp;gt;.mirror.aliyuncs.com&amp;#34;,&amp;#34;https://mirror.ccs.tencentyun.com&amp;#34;]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#设置docker服务自启&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl daemon-reload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> --now docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> containerd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># verify the installation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker compose version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 到这里docker到安装完成，下面是docker swarm的安装&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化docker swarm 集群 --advertise-addr &amp;lt;ip地址&amp;gt; #如果没有指定，默认是当前机器的局域网ip地址&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker swarm init --advertise-addr &amp;lt;公网ip地址&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="腾讯云安装docker">腾讯云安装docker
&lt;/h2>&lt;p>&lt;strong>腾讯云服务器&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo install -m &lt;span class="m">0755&lt;/span> -d /etc/apt/keyrings
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -fsSL http://mirrors.tencentyun.com/docker-ce/linux/ubuntu/gpg &lt;span class="p">|&lt;/span> sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo chmod a+r /etc/apt/keyrings/docker.gpg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;deb [arch=&lt;/span>&lt;span class="k">$(&lt;/span>dpkg --print-architecture&lt;span class="k">)&lt;/span>&lt;span class="s2"> signed-by=/etc/apt/keyrings/docker.gpg] http://mirrors.tencentyun.com/docker-ce/linux/ubuntu \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &lt;/span>&lt;span class="k">$(&lt;/span>. /etc/os-release &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$VERSION_CODENAME&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2"> stable&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo mkdir -p /etc/docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo tee /etc/docker/daemon.json &lt;span class="s">&amp;lt;&amp;lt;-&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;#34;registry-mirrors&amp;#34;: [&amp;#34;https://mirror.ccs.tencentyun.com&amp;#34;]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl daemon-reload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl restart docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> --now docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> containerd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="windows-安装docker-desktop">windows 安装docker desktop
&lt;/h2>&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9.png"
width="1920"
height="1032"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9_hu5379139890529886184.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-9_hu17260824400583683544.png 1024w"
loading="lazy"
alt="wsl 设置"
class="gallery-image"
data-flex-grow="186"
data-flex-basis="446px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10.png"
width="1469"
height="890"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10_hu8897781883495024280.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-10_hu18270858830634013591.png 1024w"
loading="lazy"
alt="vpn设置"
class="gallery-image"
data-flex-grow="165"
data-flex-basis="396px"
>&lt;/p>
&lt;p>如果需要查询vpm有没有在容器里面生效&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11.png"
width="738"
height="474"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11_hu10040779969051181402.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-11_hu1036659357537563735.png 1024w"
loading="lazy"
alt="curl -L ipinfo.io"
class="gallery-image"
data-flex-grow="155"
data-flex-basis="373px"
>&lt;/p>
&lt;h1 id="注意事项">注意事项
&lt;/h1>&lt;h1 id="使用指南">使用指南
&lt;/h1>&lt;h3 id="docker自动创建的数据卷自动挂载到-mnt">docker自动创建的数据卷自动挂载到 /mnt
&lt;/h3>&lt;p>在很多场景下，我们习惯让 Docker 自己管理数据卷（即 Named Volumes，比如 &lt;code>docker volume create my_vol&lt;/code>）。这些卷默认存放在 &lt;code>/var/lib/docker/volumes/&lt;/code>。&lt;/p>
&lt;p>如果你想让 Docker 把这些自己创建的卷也放到 &lt;code>/mnt&lt;/code> 里，这里有三种方案，请根据实际需求选择：&lt;/p>
&lt;p>&lt;strong>方案 A：修改全局默认存储路径（强烈推荐，一劳永逸）&lt;/strong>&lt;/p>
&lt;p>这是最规范的做法。它不仅会把数据卷存到 &lt;code>/mnt&lt;/code>，还会把拉取的镜像、容器日志等所有 Docker 数据都转移过去，彻底解放系统盘。&lt;/p>
&lt;p>操作步骤：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>停止 Docker 服务：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo systemctl stop docker
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#如果docker.socket服务没有停止，需要先停止它&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl stop docker.socket
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>修改或创建配置文件：&lt;/strong>
编辑 &lt;code>/etc/docker/daemon.json&lt;/code> 文件：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo nano /etc/docker/daemon.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>写入以下配置（假设你要把数据存在 &lt;code>/mnt/docker_data&lt;/code>）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;data-root&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/mnt/docker_data&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>（重要）迁移历史数据：&lt;/strong>
为了防止你以前创建的容器和镜像丢失，务必将原数据同步到新目录：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo rsync -aP /var/lib/docker/ /mnt/docker_data/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>重启 Docker：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo systemctl start docker
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>效果：&lt;/strong> 以后你执行任何关于 Docker 的操作，数据都会自动落在大硬盘 &lt;code>/mnt/docker_data&lt;/code> 里了！&lt;/p>
&lt;p>&lt;strong>方案 B：使用软链接（简单粗暴）&lt;/strong>&lt;/p>
&lt;p>如果你只想把数据卷（Volumes）挪到 &lt;code>/mnt&lt;/code>，而镜像等其他数据还要留在系统盘，可以使用 Linux 的软链接魔法。&lt;/p>
&lt;p>操作步骤：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>停止服务：&lt;code>sudo systemctl stop docker&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>在 &lt;code>/mnt&lt;/code> 创建目标文件夹：&lt;code>sudo mkdir -p /mnt/docker_volumes&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>迁移现有的卷数据：&lt;code>sudo mv /var/lib/docker/volumes/* /mnt/docker_volumes/&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>删除原本的空文件夹：&lt;code>sudo rm -rf /var/lib/docker/volumes&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>创建软链接：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo ln -s /mnt/docker_volumes /var/lib/docker/volumes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>重启服务：&lt;code>sudo systemctl start docker&lt;/code>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>效果：&lt;/strong> 欺骗了 Docker。Docker 以为自己还是写在 &lt;code>/var/lib&lt;/code> 下，但实际上数据已经悄悄存进了 &lt;code>/mnt&lt;/code>。&lt;/p>
&lt;p>&lt;strong>方案 C：在 Docker Compose 中按需指定物理路径（优雅灵活）&lt;/strong>&lt;/p>
&lt;p>如果你不想修改系统的任何配置，只想在部署某个特定项目时，让它自己的数据卷落在 &lt;code>/mnt&lt;/code>，可以在定义 volume 时使用 local 驱动。&lt;/p>
&lt;p>这在编写 &lt;code>docker-compose.yml&lt;/code> 时非常实用：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">mysql:8.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">mysql_data:/var/lib/mysql&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mysql_data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">driver&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">local&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">driver_opts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">none&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">o&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bind&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">device&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/mnt/my_project/mysql_data&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>效果：&lt;/strong> 既保留了 Named Volumes 的便捷性，又实现了数据的物理分离，非常适合项目迁移和备份。&lt;/p>
&lt;p>&lt;strong>总结建议&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>如果你的系统盘马上就要满了：别犹豫，直接用 &lt;strong>方案 A&lt;/strong>，修改 &lt;code>data-root&lt;/code> 把整个 Docker 搬家到大硬盘。&lt;/li>
&lt;li>如果你只是想把项目数据单独分类存放大盘：推荐 &lt;strong>方案 C&lt;/strong>，在 &lt;code>docker-compose.yml&lt;/code> 中使用 &lt;code>driver_opts&lt;/code>，干净且优雅。&lt;/li>
&lt;/ul>
&lt;p>希望这篇文章能帮你解决 Docker 存储空间的烦恼！如果遇到权限问题（Permission Denied），记得检查 &lt;code>/mnt&lt;/code> 对应目录的用户权限哦（chown）。&lt;/p>
&lt;p>(Tags: Docker, Linux, 运维，存储优化，DockerVolume)&lt;/p>
&lt;h3 id="简单开始一个docker镜像的使用">简单开始一个docker镜像的使用
&lt;/h3>&lt;h4 id="1-拉取镜像">1. 拉取镜像
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker pull ubuntu
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2查看镜像是否拉取成功">2.查看镜像是否拉取成功
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker images
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3-运行容器">3. 运行容器
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -itd --name &amp;lt;容器名称&amp;gt; -p &amp;lt;主机端口&amp;gt;:&amp;lt;容器端口&amp;gt; --cpus&lt;span class="o">=&lt;/span>&lt;span class="m">30&lt;/span> ubuntu
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -p设置端口 --cpus/-c 设置核心 &lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-通过-exec-命令进入-ubuntu-容器">4. 通过 exec 命令进入 ubuntu 容器
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it &amp;lt;容器名&amp;gt; /bin/bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="5-安装ssh">5. 安装ssh
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install openssh-client -y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install openssh-server -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="6-安装vim">6. 安装vim
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get install vim -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="7-安装conda">7. 安装conda
&lt;/h4>&lt;p>&lt;a class="link" href="https://zhuanlan.zhihu.com/p/307923089" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/307923089&lt;/a>
注意，可能要手动配置环境变量&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index.png"
width="683"
height="23"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index_hu18018924885186202092.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index_hu11055120303891815462.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="2969"
data-flex-basis="7126px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-1.png"
width="1061"
height="316"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-1_hu15996279467176129364.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-1_hu14527054585824512022.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="335"
data-flex-basis="805px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-2.png"
width="722"
height="21"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-2_hu15911958128711493842.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-2_hu10207799075979233041.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="3438"
data-flex-basis="8251px"
>&lt;/p>
&lt;h4 id="8-安装zipunzip">8. 安装zip、unzip
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get install zip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install unzip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="9-解决中文乱码问题">9. 解决中文乱码问题
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">export&lt;/span> &lt;span class="n">LC_ALL&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;C.UTF-8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">source&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bash&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bashrc&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="10-安装sudo">10. 安装sudo
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get install sudo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="docker使用实战-mysql和ubuntu容器">Docker使用实战-MySQL和Ubuntu容器
&lt;/h3>&lt;h4 id="1-安装docker-mysql容器">1. 安装docker mysql容器
&lt;/h4>&lt;h5 id="首先拉取mysql镜像-默认拉取最新版">首先拉取mysql镜像 ，默认拉取最新版
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker pull mysql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index.png"
width="950"
height="322"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index_hu15883106726238587353.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index_hu11001229380795782989.png 1024w"
loading="lazy"
alt="拉取mysql镜像"
class="gallery-image"
data-flex-grow="295"
data-flex-basis="708px"
>&lt;/p>
&lt;h5 id="然后创建容器">然后创建容器
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --name mysql_zata -p 3306:3306 -e &lt;span class="nv">MYSQL_ROOT_PASSWORD&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">123456&lt;/span> mysql &lt;span class="c1">#第一个端口是主机的端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">或
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run --name mysql_zata -p 53306:3306 -e &lt;span class="nv">MYSQL_ROOT_PASSWORD&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">123456&lt;/span> mysql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数&lt;/th>
&lt;th>解释&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>docker run:&lt;/td>
&lt;td>创建Docker容器的命令。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;ndash;name mysql_zata&lt;/td>
&lt;td>这个选项用于指定容器的名称，这里将容器命名为&amp;quot;mysql_zata&amp;quot;。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-p 3306:3306: [第一个3306是主机的端口可以改成其他的，第二个3306是容器的端口，也就是要固定的]可以改成如： &lt;br> -p 53306：3306&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>这个选项用于将容器内部的端口映射到宿主机上。具体来说，-p 3306:3306 将容器内部的MySQL数据库服务的端口3306映射到宿主机的端口3306。这样，您可以通过宿主机上的3306端口访问容器内的MySQL服务。&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-e MYSQL_ROOT_PASSWORD=123456:&lt;/td>
&lt;td>这个选项用于设置MySQL数据库的root用户的密码。在这里，密码被设置为&amp;quot;123456&amp;quot;。这是一个环境变量设置，MySQL容器会使用这个密码来授权root用户访问数据库。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mysql:&lt;/td>
&lt;td>这是要运行的Docker镜像的名称。在这里，它是MySQL镜像，意味着会启动一个MySQL数据库容器。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-1.png"
width="1803"
height="862"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-1_hu9353473650543336541.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-1_hu14472057940714386618.png 1024w"
loading="lazy"
alt="创建容器"
class="gallery-image"
data-flex-grow="209"
data-flex-basis="501px"
>&lt;/p>
&lt;p>现在是前台模式创建的（我也不是很清楚，反正关闭这个窗口服务就会停掉），关闭窗口也没事，可以重新再windows的docker界面重开&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-2.png"
width="1102"
height="183"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-2_hu7652912901109794189.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-2_hu9010311326302050966.png 1024w"
loading="lazy"
alt="docker图形界面运行mysql容器"
class="gallery-image"
data-flex-grow="602"
data-flex-basis="1445px"
>&lt;/p>
&lt;h4 id="2-安装ubuntu容器并配置">2. 安装Ubuntu容器并配置
&lt;/h4>&lt;h5 id="1-拉取镜像-1">1. 拉取镜像
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker pull ubuntu
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="2查看镜像是否拉取成功-1">2.查看镜像是否拉取成功
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker images
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="3-运行容器-1">3. 运行容器
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -itd --name &amp;lt;容器名称&amp;gt; -p &amp;lt;主机端口&amp;gt;:&amp;lt;容器端口&amp;gt; ubuntu
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="4-通过-exec-命令进入-ubuntu-容器-1">4. 通过 exec 命令进入 ubuntu 容器
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it &amp;lt;容器名&amp;gt; /bin/bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="5-安装ssh并启动">5. 安装ssh并启动
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get updata
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install openssh-client
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install openssh-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo service ssh start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="6-安装vim-1">6. 安装vim
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get install vim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="7-安装conda-1">7. 安装conda
&lt;/h5>&lt;p>&lt;a class="link" href="https://zhuanlan.zhihu.com/p/307923089" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/307923089&lt;/a>&lt;/p>
&lt;p>注意，可能要手动配置环境变量（如果能直接用也就不用配置）&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-3.png"
width="499"
height="292"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-3_hu784335540611786281.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/mysql-index-3_hu4687353665301487606.png 1024w"
loading="lazy"
alt="配置环境变量"
class="gallery-image"
data-flex-grow="170"
data-flex-basis="410px"
>&lt;/p>
&lt;h5 id="8-安装zipunzip-1">8. 安装zip、unzip
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get install zip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt-get install unzip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="9-解决中文乱码问题-1">9. 解决中文乱码问题
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">export&lt;/span> &lt;span class="n">LC_ALL&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;C.UTF-8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">source&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bash&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bashrc&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="10-安装sudo-1">10. 安装sudo
&lt;/h5>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">apt-get install sudo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;hr>
&lt;hr>
&lt;hr>
&lt;h3 id="docker使用技巧">Docker使用技巧
&lt;/h3>&lt;h4 id="docker的卷挂载功能">docker的卷挂载功能
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -v &amp;lt;host_path&amp;gt;:&amp;lt;container_path&amp;gt; &amp;lt;image_name&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 需要为每一个路径都添加-v&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -v &amp;lt;host_path1&amp;gt;:&amp;lt;container_path1&amp;gt; -v &amp;lt;host_path2&amp;gt;:&amp;lt;container_path2&amp;gt; ... &amp;lt;image_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>windows环境下面的特殊情况（路径反斜杠）
示例： 假设你想将 Windows 上的 C:\data 映射到容器内的 /app/data&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -v /c/data:/app/data -it &amp;lt;image_name&amp;gt; /bin/bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 如果路径存在空格，需要用&amp;#34;&amp;#34;包起来 如&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -v &lt;span class="s2">&amp;#34;/c/Program Files/my folder:/app&amp;#34;&lt;/span> -it &amp;lt;image_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="将镜像推送到远程仓库">将镜像推送到远程仓库
&lt;/h4>&lt;p>推送到公开&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker tag my-app:1.0 myusername/my-app:1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker push myusername/my-app:1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>到私有&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker login registry.example.com &lt;span class="c1"># 如果是docker hub 只需要 login in&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker tag my-app:1.0 registry.example.com/myusername/my-app:1.0 &lt;span class="c1"># 重命名tag&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker push registry.example.com/myusername/my-app:1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="docker-context-教程">Docker Context 教程
&lt;/h4>&lt;h3 id="什么是-docker-context">什么是 Docker Context？
&lt;/h3>&lt;p>在深入了解之前，我们首先需要理解 Docker Context 的概念。简单来说，&lt;strong>Docker Context&lt;/strong> 是 Docker CLI（命令行工具）用来连接和管理不同 Docker 守护进程（Docker daemon）或 Kubernetes 集群的配置集合。&lt;/p>
&lt;p>在 Docker CLI 的早期版本中，我们通常使用 &lt;code>DOCKER_HOST&lt;/code> 环境变量来指定要连接的远程 Docker daemon。这种方式虽然有效，但对于需要频繁切换不同环境（例如：本地开发、远程测试服务器、生产环境等）的开发者来说，管理起来非常不便，因为它要求每次切换时都手动更改环境变量。&lt;/p>
&lt;p>Docker Context 解决了这个问题。它允许你将连接信息（如主机地址、证书等）保存为一个命名的配置，并可以通过简单的命令 (&lt;code>docker context use&lt;/code>) 在这些配置之间快速切换，而无需手动管理环境变量。&lt;/p>
&lt;p>一个 Docker Context 可以包含：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Docker Endpoint&lt;/strong>：Docker 守护进程的地址（例如：&lt;code>tcp://&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;&lt;/code> 或 &lt;code>ssh://&amp;lt;user&amp;gt;@&amp;lt;host&amp;gt;&lt;/code>）。&lt;/li>
&lt;li>&lt;strong>Kubernetes Endpoint&lt;/strong>：Kubernetes API 服务器的地址。&lt;/li>
&lt;li>&lt;strong>安全认证信息&lt;/strong>：用于连接的证书和密钥。&lt;/li>
&lt;/ul>
&lt;p>默认情况下，当你安装 Docker 后，会自动创建一个名为 &lt;code>default&lt;/code> 的上下文，它指向你本地的 Docker 守护进程。&lt;/p>
&lt;h3 id="为什么使用-docker-context">为什么使用 Docker Context？
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>简化工作流&lt;/strong>：无需频繁设置和取消 &lt;code>DOCKER_HOST&lt;/code> 环境变量。&lt;/li>
&lt;li>&lt;strong>集中管理&lt;/strong>：将所有不同环境的连接配置集中管理。&lt;/li>
&lt;li>&lt;strong>支持多种环境&lt;/strong>：不仅支持远程 Docker daemon，还支持连接到 Kubernetes 集群。&lt;/li>
&lt;li>&lt;strong>安全&lt;/strong>：可以方便地管理和切换不同环境的安全认证信息。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="docker-context-常用命令">Docker Context 常用命令
&lt;/h3>&lt;p>在使用 Docker Context 之前，您需要确保您的 Docker 版本在 19.03 或更高。&lt;/p>
&lt;h4 id="1-查看现有上下文">1. 查看现有上下文
&lt;/h4>&lt;p>&lt;code>docker context ls&lt;/code> 命令用于列出所有已定义的上下文。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>您将看到类似以下的输出：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">NAME&lt;/span> &lt;span class="n">DESCRIPTION&lt;/span> &lt;span class="n">DOCKER&lt;/span> &lt;span class="n">ENDPOINT&lt;/span> &lt;span class="n">KUBERNETES&lt;/span> &lt;span class="n">ENDPOINT&lt;/span> &lt;span class="n">ORCHESTRATOR&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">default&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">Current&lt;/span> &lt;span class="n">DOCKER_HOST&lt;/span> &lt;span class="n">based&lt;/span> &lt;span class="n">configuration&lt;/span> &lt;span class="n">unix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">///&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">docker&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sock&lt;/span> &lt;span class="n">swarm&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>NAME&lt;/code>：上下文的名称。&lt;/li>
&lt;li>&lt;code>*&lt;/code>：星号表示当前正在使用的上下文。&lt;/li>
&lt;li>&lt;code>DESCRIPTION&lt;/code>：对上下文的描述。&lt;/li>
&lt;li>&lt;code>DOCKER ENDPOINT&lt;/code>：Docker 守护进程的连接地址。&lt;/li>
&lt;li>&lt;code>KUBERNETES ENDPOINT&lt;/code>：如果上下文用于 Kubernetes，则显示其连接地址。&lt;/li>
&lt;/ul>
&lt;h4 id="2-创建新上下文">2. 创建新上下文
&lt;/h4>&lt;p>&lt;code>docker context create&lt;/code> 命令用于创建一个新的上下文。&lt;/p>
&lt;p>&lt;strong>创建连接到远程 Docker daemon 的上下文&lt;/strong>&lt;/p>
&lt;p>有几种方式可以连接远程 Docker daemon，最常见的是通过 SSH 或 TCP。&lt;/p>
&lt;p>&lt;strong>a) 通过 SSH 连接&lt;/strong>&lt;/p>
&lt;p>这是最推荐和最安全的连接方式。您需要确保：&lt;/p>
&lt;ul>
&lt;li>本地机器已配置好无密码 SSH 登录远程主机。&lt;/li>
&lt;li>远程主机上已安装 Docker，且当前用户有权限执行 &lt;code>docker&lt;/code> 命令（通常通过将用户添加到 &lt;code>docker&lt;/code> 用户组来实现）。&lt;/li>
&lt;/ul>
&lt;!-- end list -->
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context create &amp;lt;context_name&amp;gt; --description &lt;span class="s2">&amp;#34;&amp;lt;description&amp;gt;&amp;#34;&lt;/span> --docker &lt;span class="s2">&amp;#34;host=ssh://&amp;lt;username&amp;gt;@&amp;lt;remote_host&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>示例：&lt;/strong>&lt;/p>
&lt;p>假设您的远程主机 IP 为 &lt;code>192.168.1.100&lt;/code>，用户名为 &lt;code>ubuntu&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context create remote-server --description &lt;span class="s2">&amp;#34;Remote Docker daemon on dev server&amp;#34;&lt;/span> --docker &lt;span class="s2">&amp;#34;host=ssh://ubuntu@192.168.1.100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>b) 通过 TCP 连接&lt;/strong>&lt;/p>
&lt;p>这种方式需要远程 Docker daemon 暴露在 TCP 端口上，并且通常需要TLS加密来保证安全。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>警告&lt;/strong>：不建议在没有TLS加密的情况下通过公共网络连接。&lt;/li>
&lt;/ul>
&lt;!-- end list -->
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 假设远程 Docker daemon 暴露在 2375 端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context create remote-tcp --description &lt;span class="s2">&amp;#34;Insecure remote connection&amp;#34;&lt;/span> --docker &lt;span class="s2">&amp;#34;host=tcp://192.168.1.100:2375&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 如果使用 TLS 加密&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context create remote-tls --description &lt;span class="s2">&amp;#34;Secure remote connection&amp;#34;&lt;/span> --docker &lt;span class="s2">&amp;#34;host=tcp://192.168.1.100:2376&amp;#34;&lt;/span> --tls-ca-path &amp;lt;path_to_ca&amp;gt; --tls-cert-path &amp;lt;path_to_cert&amp;gt; --tls-key-path &amp;lt;path_to_key&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>c) 创建连接到 Kubernetes 集群的上下文&lt;/strong>&lt;/p>
&lt;p>如果您已经使用 &lt;code>kubectl&lt;/code> 配置了集群连接，Docker Context 可以自动从您的 &lt;code>.kube/config&lt;/code> 文件中加载这些配置。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context create &amp;lt;context_name&amp;gt; --description &lt;span class="s2">&amp;#34;&amp;lt;description&amp;gt;&amp;#34;&lt;/span> --kubernetes &lt;span class="s2">&amp;#34;config-file=&amp;lt;path_to_kubeconfig&amp;gt;&amp;#34;&lt;/span> --kubernetes &lt;span class="s2">&amp;#34;context=&amp;lt;kube_context_name&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>示例：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context create k8s-cluster --description &lt;span class="s2">&amp;#34;My Kubernetes cluster&amp;#34;&lt;/span> --kubernetes &lt;span class="s2">&amp;#34;context=my-cluster-context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3-切换上下文">3. 切换上下文
&lt;/h4>&lt;p>&lt;code>docker context use&lt;/code> 命令用于切换当前活动的上下文。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context use &amp;lt;context_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>示例：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 切换到之前创建的远程服务器上下文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context use remote-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 验证当前上下文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此时，&lt;code>docker context ls&lt;/code> 的输出会显示 &lt;code>remote-server&lt;/code> 旁边有星号 &lt;code>*&lt;/code>。从现在开始，您所有的 &lt;code>docker&lt;/code> 命令（例如 &lt;code>docker ps&lt;/code>、&lt;code>docker images&lt;/code> 等）都将在远程主机上执行。&lt;/p>
&lt;h4 id="4-在单次命令中使用特定上下文">4. 在单次命令中使用特定上下文
&lt;/h4>&lt;p>如果您只想在某一次命令中使用某个上下文，而不想永久切换，可以使用 &lt;code>--context&lt;/code> 全局选项。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker --context &amp;lt;context_name&amp;gt; &amp;lt;docker_command&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>示例：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在不切换当前上下文的情况下，查看远程服务器上的容器列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker --context remote-server ps -a
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="5-检查上下文详情">5. 检查上下文详情
&lt;/h4>&lt;p>&lt;code>docker context inspect&lt;/code> 命令可以查看某个上下文的详细配置信息。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context inspect &amp;lt;context_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>示例：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context inspect remote-server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="6-更新上下文">6. 更新上下文
&lt;/h4>&lt;p>&lt;code>docker context update&lt;/code> 命令可以修改现有上下文的配置。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context update &amp;lt;context_name&amp;gt; --description &lt;span class="s2">&amp;#34;New description&amp;#34;&lt;/span> --docker &lt;span class="s2">&amp;#34;host=...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="7-移除上下文">7. 移除上下文
&lt;/h4>&lt;p>&lt;code>docker context rm&lt;/code> 命令可以删除一个上下文。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context rm &amp;lt;context_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="综合示例从本地切换到远程再切换回来">综合示例：从本地切换到远程，再切换回来
&lt;/h3>&lt;p>这是一个完整的流程，展示了如何利用 Docker Context 简化日常工作。&lt;/p>
&lt;p>&lt;strong>步骤 1：检查本地默认上下文&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 检查默认上下文，确保指向本地&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker context ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出应显示 &lt;code>default *&lt;/code>。&lt;/p>
&lt;p>&lt;strong>步骤 2：创建远程上下文&lt;/strong>&lt;/p>
&lt;p>假设您要连接到 IP 为 &lt;code>192.168.1.100&lt;/code> 的远程服务器。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context create my-remote-host --description &lt;span class="s2">&amp;#34;My development server&amp;#34;&lt;/span> --docker &lt;span class="s2">&amp;#34;host=ssh://user@192.168.1.100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>步骤 3：切换到远程上下文&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context use my-remote-host
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此时，您已成功切换到远程环境。&lt;/p>
&lt;p>&lt;strong>步骤 4：在远程主机上执行命令&lt;/strong>&lt;/p>
&lt;p>现在，您可以像操作本地 Docker 一样操作远程 Docker。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看远程主机上正在运行的容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在远程主机上拉取一个镜像&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker pull nginx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在远程主机上运行一个新容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d --name my-nginx -p 80:80 nginx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这些命令的执行都将在 &lt;code>192.168.1.100&lt;/code> 上完成。&lt;/p>
&lt;p>&lt;strong>步骤 5：切换回本地上下文&lt;/strong>&lt;/p>
&lt;p>当您完成远程操作后，可以轻松切换回本地环境。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context use default
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>步骤 6：验证切换&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出应再次显示 &lt;code>default *&lt;/code>。&lt;/p>
&lt;p>现在，您的 &lt;code>docker&lt;/code> 命令又将作用于本地机器。&lt;/p>
&lt;p>通过这个简单的教程，您可以看到 Docker Context 如何极大地简化了多 Docker 环境的管理，使其成为一个高效且安全的工具。&lt;/p>
&lt;h4 id="在-windows-上实现-docker-in-docker-dind-终极教程">在 Windows 上实现 Docker in Docker (DinD) 终极教程
&lt;/h4>&lt;p>在 Windows 环境下开发时，我们有时会遇到需要在 Docker 容器内部再次调用 Docker 命令的场景，例如在 Jenkins CI/CD 流水线中构建 Docker 镜像。这个技术通常被称为 &amp;ldquo;Docker in Docker&amp;rdquo; (DinD)。&lt;/p>
&lt;p>本文将详细介绍在 Windows (通过 Docker Desktop + WSL 2) 中实现 DinD 的两种主流方法，分析其优劣，并提供手把手的操作步骤。&lt;/p>
&lt;h5 id="核心概念两种实现方式">核心概念：两种实现方式
&lt;/h5>&lt;ol>
&lt;li>&lt;strong>挂载 Docker Socket (DooD - Docker-out-of-Docker)&lt;/strong>: &lt;strong>(官方推荐)&lt;/strong> 让容器内的 Docker CLI 直接与宿主机的 Docker 守护进程 (Daemon) 通信。这好比在办公室里装一部电话分机，直接使用公司总机的功能。&lt;/li>
&lt;li>&lt;strong>真正的 Docker-in-Docker (DinD)&lt;/strong>: 在容器内运行一个全新的、完全隔离的 Docker 守护进程。这好比在办公室里私建一个小基站，内外通信完全独立。&lt;/li>
&lt;/ol>
&lt;h5 id="结论先行哪种方法最适合你">结论先行：哪种方法最适合你？
&lt;/h5>&lt;p>对于绝大多数场景，&lt;strong>方法一 (挂载 Docker Socket)&lt;/strong> 是最佳选择。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">特性&lt;/th>
&lt;th style="text-align: left">方法一 (挂载 Socket)&lt;/th>
&lt;th style="text-align: left">方法二 (真·DinD)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>推荐度&lt;/strong>&lt;/td>
&lt;td style="text-align: left">⭐⭐⭐⭐⭐ &lt;strong>(强烈推荐)&lt;/strong>&lt;/td>
&lt;td style="text-align: left">⭐⭐ (仅特定场景)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>隔离性&lt;/strong>&lt;/td>
&lt;td style="text-align: left">弱&lt;/td>
&lt;td style="text-align: left">强&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>安全性&lt;/strong>&lt;/td>
&lt;td style="text-align: left">较高 (无需特权模式)&lt;/td>
&lt;td style="text-align: left">低 (必须使用 &lt;code>--privileged&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>性能/资源&lt;/strong>&lt;/td>
&lt;td style="text-align: left">开销极小&lt;/td>
&lt;td style="text-align: left">开销大 (双倍守护进程)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>配置复杂度&lt;/strong>&lt;/td>
&lt;td style="text-align: left">简单&lt;/td>
&lt;td style="text-align: left">复杂&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>镜像缓存&lt;/strong>&lt;/td>
&lt;td style="text-align: left">与宿主机共享&lt;/td>
&lt;td style="text-align: left">独立缓存，占用额外空间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>典型用例&lt;/strong>&lt;/td>
&lt;td style="text-align: left">CI/CD, 开发环境&lt;/td>
&lt;td style="text-align: left">隔离的 Docker 功能测试&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h5 id="方法一挂载-docker-socket-dood---官方推荐">方法一：挂载 Docker Socket (DooD) - 官方推荐
&lt;/h5>&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3.png"
width="644"
height="512"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3_hu9222955544559158541.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-3_hu17625505401200353027.png 1024w"
loading="lazy"
alt="示例效果"
class="gallery-image"
data-flex-grow="125"
data-flex-basis="301px"
>&lt;/p>
&lt;p>这是最简单、高效且安全的方式。&lt;/p>
&lt;h6 id="-原理">✨ 原理
&lt;/h6>&lt;p>通过 &lt;code>-v&lt;/code> 参数将宿主机的 Docker Socket 文件 (&lt;code>/var/run/docker.sock&lt;/code>) 挂载到容器内部。容器内的任何 &lt;code>docker&lt;/code> 命令都会通过这个 Socket 文件被发送到宿主机，由宿主机的 Docker Daemon 执行。&lt;/p>
&lt;h6 id="-优点">✅ 优点
&lt;/h6>&lt;ul>
&lt;li>&lt;strong>配置简单&lt;/strong>：一行命令参数即可搞定。&lt;/li>
&lt;li>&lt;strong>资源高效&lt;/strong>：无需启动额外的 Docker 服务，内存和 CPU 占用极低。&lt;/li>
&lt;li>&lt;strong>镜像共享&lt;/strong>：容器内拉取的镜像，宿主机可以直接使用，避免重复下载，节约时间和磁盘空间。&lt;/li>
&lt;/ul>
&lt;h6 id="-操作步骤">🚀 操作步骤
&lt;/h6>&lt;p>&lt;strong>1. 前提条件&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Windows 10/11 已安装 &lt;strong>Docker Desktop&lt;/strong>。&lt;/li>
&lt;li>Docker Desktop 使用 &lt;strong>WSL 2 后端&lt;/strong> (当前默认设置)。
&lt;ul>
&lt;li>&lt;strong>检查方法&lt;/strong>: 打开 Docker Desktop &amp;gt; &lt;code>Settings&lt;/code> &amp;gt; &lt;code>General&lt;/code>，确保 &lt;code>Use the WSL 2 based engine&lt;/code> 已勾选。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>2. 运行容器并挂载 Socket&lt;/strong>&lt;/p>
&lt;p>打开 PowerShell 或 CMD，执行以下命令来启动一个带有 Docker CLI 的容器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -it --rm &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -v /var/run/docker.sock:/var/run/docker.sock &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> docker:latest &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>命令解析&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>docker run -it --rm&lt;/code>: 以交互模式启动一个临时容器，退出后自动删除。&lt;/li>
&lt;li>&lt;code>-v /var/run/docker.sock:/var/run/docker.sock&lt;/code>: &lt;strong>核心命令&lt;/strong>。将宿主机的 Docker Socket 文件挂载到容器内的相同路径。
&lt;blockquote>
&lt;p>&lt;strong>提示&lt;/strong>: 即使在 Windows 系统，也请直接使用 &lt;code>/var/run/docker.sock&lt;/code> 这个 Linux 路径。Docker Desktop 会自动处理好与 WSL 2 之间的路径转换。&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>&lt;code>docker:latest&lt;/code>: 使用官方 &lt;code>docker&lt;/code> 镜像，它内置了 Docker 命令行工具 (CLI)。&lt;/li>
&lt;li>&lt;code>sh&lt;/code>: 启动容器内的 shell 环境。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>3. 在容器内验证&lt;/strong>&lt;/p>
&lt;p>命令执行后，你的终端提示符会变为 &lt;code>/#&lt;/code>，表示已进入容器内部。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>查看宿主机容器&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在容器内部执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出结果会列出你&lt;strong>宿主机&lt;/strong>上所有正在运行的容器，包括你刚刚启动的这一个。这证明容器已成功连接到宿主机的 Docker 服务。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>运行一个新容器&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在容器内部执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run --rm hello-world
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>你会看到 &lt;code>hello-world&lt;/code> 镜像被成功拉取并运行。此时，你在&lt;strong>宿主机&lt;/strong>上执行 &lt;code>docker images&lt;/code>，也能看到 &lt;code>hello-world&lt;/code> 这个镜像，证明了缓存是共享的。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h5 id="方法二真正的-docker-in-docker-dind---特定场景使用">方法二：真正的 Docker-in-Docker (DinD) - 特定场景使用
&lt;/h5>&lt;blockquote>
&lt;p>&lt;strong>⚠️ 安全警告&lt;/strong>
此方法必须开启 &lt;code>--privileged&lt;/code> (特权)模式，这会打破容器的隔离性，给予容器访问宿主机内核的权限，存在&lt;strong>严重的安全风险&lt;/strong>。请仅在完全信任镜像内容，并确实需要强隔离环境时使用。&lt;/p>
&lt;/blockquote>
&lt;h6 id="-原理-1">✨ 原理
&lt;/h6>&lt;p>使用官方提供的 &lt;code>docker:dind&lt;/code> 镜像，在容器内启动一个完整的、独立的 Docker 守护进程。&lt;/p>
&lt;h6 id="-缺点">❌ 缺点
&lt;/h6>&lt;ul>
&lt;li>&lt;strong>安全风险高&lt;/strong>：&lt;code>--privileged&lt;/code> 模式是危险的。&lt;/li>
&lt;li>&lt;strong>性能开销大&lt;/strong>：双重 Docker Daemon 运行，消耗更多系统资源。&lt;/li>
&lt;li>&lt;strong>双重存储&lt;/strong>：内外镜像是隔离的，同一镜像需要下载两次，占用双倍磁盘空间。&lt;/li>
&lt;/ul>
&lt;h6 id="-操作步骤-1">🚀 操作步骤
&lt;/h6>&lt;p>&lt;strong>1. 启动 DinD 守护进程容器&lt;/strong>&lt;/p>
&lt;p>这个容器专门用来在后台运行独立的 Docker 服务。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --privileged --name my-dind-daemon -d &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -e &lt;span class="nv">DOCKER_TLS_CERTDIR&lt;/span>&lt;span class="o">=&lt;/span>/certs &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> docker:24.0-dind
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>命令解析&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--privileged&lt;/code>: &lt;strong>(高危)&lt;/strong> 授予容器特权。&lt;/li>
&lt;li>&lt;code>--name my-dind-daemon&lt;/code>: 为该容器命名，方便后续连接。&lt;/li>
&lt;li>&lt;code>-d&lt;/code>: 后台运行。&lt;/li>
&lt;li>&lt;code>docker:24.0-dind&lt;/code>: 使用官方的 &lt;code>dind&lt;/code> 专用镜像。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>2. 启动客户端容器并连接到 DinD&lt;/strong>&lt;/p>
&lt;p>现在启动另一个容器作为客户端，并将其网络连接到刚才的 &lt;code>dind&lt;/code> 容器。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -it --rm &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --link my-dind-daemon:docker &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> docker:24.0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>命令解析&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--link my-dind-daemon:docker&lt;/code>: &lt;strong>核心命令&lt;/strong>。将 &lt;code>my-dind-daemon&lt;/code> 容器连接到当前容器，并设置网络别名为 &lt;code>docker&lt;/code>。这样，客户端内的 Docker CLI 就会自动找到名为 &lt;code>docker&lt;/code> 的主机作为其守护进程。&lt;/li>
&lt;li>&lt;code>docker:24.0&lt;/code>: 使用普通的 &lt;code>docker&lt;/code> 镜像作为客户端。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>3. 在客户端容器内验证&lt;/strong>&lt;/p>
&lt;p>进入客户端容器后，进行验证。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>查看容器&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在客户端容器内执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出结果应为空。因为它连接的是 &lt;code>my-dind-daemon&lt;/code> 提供的全新、隔离的环境。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>运行一个新容器&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在客户端容器内执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run --rm hello-world
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>hello-world&lt;/code> 镜像会被下载并运行。这个容器完全存在于 &lt;code>my-dind-daemon&lt;/code> 的环境中，你的宿主机对此毫不知情。在宿主机上执行 &lt;code>docker ps&lt;/code> 或 &lt;code>docker images&lt;/code> 都看不到这个 &lt;code>hello-world&lt;/code>。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="使用已有容器创建镜像">使用已有容器创建镜像
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker commit container-name new-image-name
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="开启重启ssh服务">开启/重启ssh服务
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">service ssh start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service ssh restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="docker-文件传输">docker 文件传输
&lt;/h4>&lt;p>宿主机到容器&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># docker cp 宿主机文件/路径 容器名：容器内路径&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker cp /home/Download/index.html wordpress-lee:/var/www/html
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>容器到宿主机&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># docker cp 容器名：文件/路径 宿主机路径 &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker cp wordpress-lee:/root/example.sh /root
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="修改服务器配置允许通过此服务器进行ssh转发">修改服务器配置允许通过此服务器进行ssh转发
&lt;/h4>&lt;p>进入配置文件，不要cd..然后在vim，直接vim &amp;hellip;&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">vim /etc/ssh/sshd_config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置文件内容&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-3.png"
width="1184"
height="1027"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-3_hu12745493966913438140.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-3_hu242959657583519928.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="115"
data-flex-basis="276px"
>&lt;/p>
&lt;p>修改其中的：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-4.png"
width="393"
height="132"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-4_hu11075327025778233217.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-4_hu3492958155437548261.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="297"
data-flex-basis="714px"
>
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-5.png"
width="417"
height="149"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-5_hu6223924596129936722.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-5_hu710651274989886181.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="279"
data-flex-basis="671px"
>&lt;/p>
&lt;p>重启ssh服务&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl"> service ssh restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="docker容器添加对外映射端口">docker容器添加对外映射端口
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参考：&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://www.cnblogs.com/zhumengke/articles/13525837.html" target="_blank" rel="noopener"
>https://www.cnblogs.com/zhumengke/articles/13525837.html&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>最简单省事方法：将现有的容器打包成镜像，然后在使用新的镜像运行容器时重新指定要映射的端口&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-7.png"
width="1042"
height="311"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-7_hu654794957350065612.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-7_hu8900178969343253002.png 1024w"
loading="lazy"
alt="打包镜像重新运行容器"
class="gallery-image"
data-flex-grow="335"
data-flex-basis="804px"
>&lt;/p>
&lt;h4 id="局域网访问docker容器完整指南">局域网访问Docker容器完整指南
&lt;/h4>&lt;p>这个问题非常常见，是团队协作或使用多台设备进行开发时必备的技能。&lt;/p>
&lt;p>另一台局域网上的电脑（我们称之为&amp;quot;开发机&amp;quot;）要进入运行 Docker 的电脑（我们称之为&amp;quot;主机&amp;quot;）的容器，核心思想是&lt;strong>通过网络暴露容器的服务或端口&lt;/strong>。容器本身是隔离的，你需要&amp;quot;开一个门&amp;quot;让局域网上的其他设备可以访问。&lt;/p>
&lt;p>这里有几种主流的方法，从简单到复杂，适用于不同的开发场景。&lt;/p>
&lt;h5 id="准备工作必须知道主机ip">&lt;strong>准备工作：必须知道主机IP&lt;/strong>
&lt;/h5>&lt;p>首先，你需要在运行 Docker 的主机上获取其局域网 IP 地址。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>在 Windows 上:&lt;/strong> 打开命令提示符（CMD）或 PowerShell，输入 &lt;code>ipconfig&lt;/code>，查找 &amp;ldquo;IPv4 地址&amp;rdquo;。通常是 &lt;code>192.168.x.x&lt;/code> 或 &lt;code>10.x.x.x&lt;/code> 的形式。&lt;/li>
&lt;li>&lt;strong>在 macOS 或 Linux 上:&lt;/strong> 打开终端，输入 &lt;code>ifconfig&lt;/code> 或 &lt;code>ip a&lt;/code>，查找 &lt;code>inet&lt;/code> 后面的地址。&lt;/li>
&lt;/ul>
&lt;p>下文中，我们假设主机的 IP 地址是 &lt;code>192.168.1.100&lt;/code>。请在你的实际操作中替换成你自己的主机 IP。&lt;/p>
&lt;h5 id="方法一端口映射-port-mapping---最常用">&lt;strong>方法一：端口映射 (Port Mapping) - 最常用&lt;/strong>
&lt;/h5>&lt;p>这是最直接、最常见的方法，适用于访问容器内运行的 Web 应用、API 服务、数据库等。&lt;/p>
&lt;p>&lt;strong>原理：&lt;/strong>
将主机的一个端口映射到容器内的一个端口。这样，访问主机的这个端口就等于访问了容器内的对应端口。&lt;/p>
&lt;p>&lt;strong>操作步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>启动容器时添加 &lt;code>-p&lt;/code> 参数：&lt;/strong>
在主机上，当你使用 &lt;code>docker run&lt;/code> 启动容器时，必须使用 &lt;code>-p&lt;/code> 或 &lt;code>--publish&lt;/code> 参数来暴露端口。&lt;/p>
&lt;p>格式为：&lt;code>-p &amp;lt;主机端口&amp;gt;:&amp;lt;容器端口&amp;gt;&lt;/code>&lt;/p>
&lt;p>&lt;strong>关键点：&lt;/strong> 为了让局域网上的其他电脑能访问，主机端口部分必须绑定到 &lt;code>0.0.0.0&lt;/code>，或者干脆省略 IP 地址（默认就是 &lt;code>0.0.0.0&lt;/code>）。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>示例1：运行一个 Nginx Web 服务器&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将主机的 8080 端口映射到容器的 80 端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 这样局域网内的任何机器都可以通过主机的 8080 端口访问&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d --name my-web -p 8080:80 nginx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>示例2：运行一个 Python Flask 应用&lt;/strong>
假设你的 Flask 应用在容器的 5000 端口运行。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将主机的 5000 端口映射到容器的 5000 端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d --name my-app -p 5000:5000 your-python-app-image
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>在开发机上访问：&lt;/strong>
现在，在局域网的另一台开发机上，打开浏览器或使用工具（如 cURL, Postman）访问 &lt;code>http://&amp;lt;主机IP&amp;gt;:&amp;lt;主机端口&amp;gt;&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>对于上面的 Nginx 示例，访问地址是：&lt;code>http://192.168.1.100:8080&lt;/code>&lt;/li>
&lt;li>对于 Flask 应用示例，访问地址是：&lt;code>http://192.168.1.100:5000&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>如果容器已经启动了怎么办？&lt;/strong>
你不能动态地给一个正在运行的容器添加端口映射。你需要停止并删除旧的容器，然后使用新的 &lt;code>-p&lt;/code> 参数重新启动它。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop &amp;lt;容器名或ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm &amp;lt;容器名或ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 然后使用上面的 docker run -p ... 命令重新创建&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="方法二通过-ssh-进入容器---获取完整的-shell-环境">&lt;strong>方法二：通过 SSH 进入容器 - 获取完整的 Shell 环境&lt;/strong>
&lt;/h5>&lt;p>如果你需要在容器内部执行命令、调试脚本，就像登录一台远程服务器一样，那么在容器里运行一个 SSH 服务是最佳选择。&lt;/p>
&lt;p>&lt;strong>原理：&lt;/strong>
在你的 Docker 镜像中安装并运行一个 SSH 服务器，然后像方法一那样，将容器的 SSH 端口（默认为 22）映射到主机的一个端口。&lt;/p>
&lt;p>&lt;strong>操作步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>准备一个带 SSH 服务的 Dockerfile：&lt;/strong>
你不能直接在官方的基础镜像（如 &lt;code>ubuntu&lt;/code>）里直接用 SSH，需要先安装。&lt;/p>
&lt;p>下面是一个基于 Ubuntu 的 &lt;code>Dockerfile&lt;/code> 示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 使用一个基础镜像&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> ubuntu:20.04&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 安装 SSH 服务端和一些常用工具&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y openssh-server sudo vim curl &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> rm -rf /var/lib/apt/lists/*&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 创建一个用于 SSH 登录的用户，并设置密码&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 注意：在生产环境中，不要用硬编码的密码！&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> useradd -m -s /bin/bash developer &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;developer:yourpassword&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> chpasswd &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> adduser developer sudo&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 允许 root 登录（仅用于开发，不推荐在生产中使用）&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> sed -i &lt;span class="s1">&amp;#39;s/#PermitRootLogin prohibit-password/PermitRootLogin yes/&amp;#39;&lt;/span> /etc/ssh/sshd_config&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 创建 SSH 服务运行所需的目录&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> mkdir /var/run/sshd&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 暴露容器的 22 端口&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">EXPOSE&lt;/span>&lt;span class="s"> 22&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 容器启动时运行 SSH 服务&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/usr/sbin/sshd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;-D&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>构建并运行镜像：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>在主机上，将上述 &lt;code>Dockerfile&lt;/code> 保存，然后在该目录下执行构建命令：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker build -t my-ssh-container .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>运行容器，并将主机的 2222 端口映射到容器的 22 端口（使用 2222 是为了避免与主机本身的 SSH 服务冲突）：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d --name dev-env -p 2222:22 my-ssh-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>在开发机上通过 SSH 连接：&lt;/strong>
在你的开发机上，打开终端，使用 &lt;code>ssh&lt;/code> 命令连接。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用你创建的 developer 用户登录&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh developer@192.168.1.100 -p &lt;span class="m">2222&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后输入你设置的密码 (&lt;code>yourpassword&lt;/code>)，就可以进入容器的命令行界面了。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h5 id="方法三使用-vs-code-remote-development---最现代化的开发体验">&lt;strong>方法三：使用 VS Code Remote Development - 最现代化的开发体验&lt;/strong>
&lt;/h5>&lt;p>这是目前最受推崇的开发方式，它结合了本地 IDE 的流畅体验和容器化环境的隔离与一致性。&lt;/p>
&lt;p>&lt;strong>原理：&lt;/strong>
你在本地的 VS Code 上编写代码，但所有的文件操作、终端命令、调试器都运行在远程主机上的容器内部。VS Code 会在容器里安装一个轻量的服务来实现这种无缝连接。&lt;/p>
&lt;p>&lt;strong>操作步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>在主机上：暴露 Docker Daemon&lt;/strong>
这是最关键的一步。你需要让 Docker 守护进程监听一个 TCP 端口，以便远程的 VS Code 可以连接。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>对于 Docker Desktop (Windows/macOS):&lt;/strong>
进入 Settings -&amp;gt; General，勾选 &amp;ldquo;Expose daemon on tcp://localhost:2375 without TLS&amp;rdquo;。&lt;/li>
&lt;li>&lt;strong>对于 Linux:&lt;/strong>
编辑 Docker 的配置文件 &lt;code>/etc/docker/daemon.json&lt;/code> (如果不存在则创建)，添加以下内容：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hosts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tcp://0.0.0.0:2375&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;unix:///var/run/docker.sock&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>然后重启 Docker 服务：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo systemctl restart docker
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>安全警告：&lt;/strong> 将 Docker Daemon 暴露在网络上存在安全风险，请确保你的局域网是受信任的，并且主机防火墙已正确配置。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>在主机上：确保你的开发容器正在运行。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 只需要一个普通的开发容器即可，无需特殊配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d --name my-project-container -it your-dev-image bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>-it&lt;/code> 和 &lt;code>bash&lt;/code> 让容器保持运行状态。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>在开发机上：配置 VS Code&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>安装 VS Code。&lt;/li>
&lt;li>在 VS Code 的扩展市场中，搜索并安装 &lt;strong>Remote Development&lt;/strong> 扩展包（由 Microsoft 发布）。&lt;/li>
&lt;li>打开 VS Code 的设置 (Settings JSON)，添加以下配置，告诉它 Docker 主机在哪里：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;docker.host&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tcp://192.168.1.100:2375&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>重启 VS Code。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>在开发机上：连接到容器&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>点击 VS Code 左下角的绿色 &lt;code>&amp;gt;&amp;lt;&lt;/code> 图标，或者按 &lt;code>F1&lt;/code> 输入 &lt;code>Remote-Containers: Attach to Running Container...&lt;/code>。&lt;/li>
&lt;li>VS Code 会列出主机 &lt;code>192.168.1.100&lt;/code> 上所有正在运行的容器。&lt;/li>
&lt;li>选择你的目标容器（例如 &lt;code>my-project-container&lt;/code>）。&lt;/li>
&lt;li>VS Code 会自动在容器内安装所需的服务，并重新加载窗口。完成后，你的 VS Code 就已经&amp;quot;进入&amp;quot;了容器。你可以直接打开容器内的文件夹、使用 VS Code 的集成终端（这个终端就是容器的 shell）、安装语言扩展、进行调试等，一切都像在本地一样。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h5 id="总结与选择">&lt;strong>总结与选择&lt;/strong>
&lt;/h5>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">方法&lt;/th>
&lt;th style="text-align: left">适用场景&lt;/th>
&lt;th style="text-align: left">优点&lt;/th>
&lt;th style="text-align: left">缺点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>端口映射&lt;/strong>&lt;/td>
&lt;td style="text-align: left">访问 Web 服务、API、数据库等需要端口的应用。&lt;/td>
&lt;td style="text-align: left">简单、直接、最常用。&lt;/td>
&lt;td style="text-align: left">只能访问暴露的服务，无法直接操作容器内部文件系统或执行任意命令。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>SSH 进入容器&lt;/strong>&lt;/td>
&lt;td style="text-align: left">需要完整的命令行权限，进行系统管理、脚本调试。&lt;/td>
&lt;td style="text-align: left">功能强大，像操作一台真实的 Linux 服务器。&lt;/td>
&lt;td style="text-align: left">配置相对复杂，需要在镜像中预置 SSH 服务和用户。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>VS Code Remote&lt;/strong>&lt;/td>
&lt;td style="text-align: left">现代化的编码、调试、测试一体化开发。&lt;/td>
&lt;td style="text-align: left">体验最佳，无缝集成，兼具本地 IDE 的流畅和容器环境的隔离。&lt;/td>
&lt;td style="text-align: left">需要配置 Docker Daemon，有一定安全风险，依赖 VS Code。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>强烈建议：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>对于&lt;strong>查看和测试 Web 应用&lt;/strong>，使用&lt;strong>方法一&lt;/strong>。&lt;/li>
&lt;li>对于&lt;strong>需要深入容器内部进行复杂操作&lt;/strong>的场景，使用&lt;strong>方法二&lt;/strong>。&lt;/li>
&lt;li>对于&lt;strong>日常编码和开发&lt;/strong>，强烈推荐学习并使用&lt;strong>方法三&lt;/strong>，这是目前最先进、最高效的流程。&lt;/li>
&lt;/ul>
&lt;p>最后，请务必检查&lt;strong>主机的防火墙&lt;/strong>，确保你映射的端口（如 &lt;code>8080&lt;/code>, &lt;code>2222&lt;/code>, &lt;code>2375&lt;/code>）是开放的，允许来自局域网的访问。&lt;/p>
&lt;h4 id="docker设置可用cpu核心数量">docker设置可用CPU核心数量
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参考：&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://www.cnblogs.com/sparkdev/p/8052522.html" target="_blank" rel="noopener"
>https://www.cnblogs.com/sparkdev/p/8052522.html&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-8.png"
width="668"
height="69"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-8_hu5883548660596835905.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-8_hu10735190958917900837.png 1024w"
loading="lazy"
alt="CPU数量"
class="gallery-image"
data-flex-grow="968"
data-flex-basis="2323px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-9.png"
width="697"
height="73"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-9_hu16433319545129989747.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-9_hu4415500239632671014.png 1024w"
loading="lazy"
alt="指定CPU"
class="gallery-image"
data-flex-grow="954"
data-flex-basis="2291px"
>&lt;/p>
&lt;h4 id="容器端口转发问题排查和解决方案">容器端口转发问题排查和解决方案
&lt;/h4>&lt;p>在容器中进行端口转发并遇到无法访问的问题，可能涉及多个原因。以下是详细的分析和可能的原因，以及解决方法：&lt;/p>
&lt;h5 id="1-容器端口转发的工作原理">1. &lt;strong>容器端口转发的工作原理&lt;/strong>
&lt;/h5>&lt;p>Docker 的端口转发通过 &lt;code>-p&lt;/code> 或 &lt;code>-P&lt;/code> 参数实现：&lt;/p>
&lt;ul>
&lt;li>&lt;code>-p &amp;lt;主机端口&amp;gt;:&amp;lt;容器端口&amp;gt;&lt;/code>：显式地将主机的某个端口映射到容器的某个端口。例如，&lt;code>-p 5980:5980&lt;/code> 会将主机的 5980 端口映射到容器的 5980 端口。&lt;/li>
&lt;li>&lt;code>-P&lt;/code>（大写）：自动将容器 Dockerfile 中通过 &lt;code>EXPOSE&lt;/code> 指令暴露的端口随机映射到主机的高位端口（通常是 32768 以上的端口）。&lt;/li>
&lt;/ul>
&lt;p>当你使用 &lt;code>-P&lt;/code> 参数时，Docker 会自动分配一个主机端口，而不是直接使用容器内的端口（比如 5980）。因此，访问 &lt;code>http://localhost:5980&lt;/code> 可能失败，因为主机的 5980 端口并未映射到容器的 5980 端口。&lt;/p>
&lt;p>&lt;strong>检查方法&lt;/strong>：
运行以下命令查看端口映射：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker port &amp;lt;容器名称或ID&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出会显示类似 &lt;code>0.0.0.0:49153-&amp;gt;5980/tcp&lt;/code> 的信息，表示容器内的 5980 端口被映射到主机的 49153 端口。你需要访问 &lt;code>http://localhost:49153&lt;/code> 而不是 &lt;code>http://localhost:5980&lt;/code>。&lt;/p>
&lt;p>&lt;strong>解决方法&lt;/strong>：
如果你希望明确映射到主机的 5980 端口，创建容器时使用：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -p 5980:5980 &amp;lt;镜像名称&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样容器内的 5980 端口会直接映射到主机的 5980 端口。&lt;/p>
&lt;hr>
&lt;h5 id="2-ssl-配置导致的问题">2. &lt;strong>SSL 配置导致的问题&lt;/strong>
&lt;/h5>&lt;p>你提到后端服务启用了 SSL（HTTPS）。这意味着服务可能在容器内监听的是 HTTPS 协议（&lt;code>https://&lt;/code>），而不是 HTTP 协议（&lt;code>http://&lt;/code>）。如果你尝试访问 &lt;code>http://localhost:5980&lt;/code>，可能会因为协议不匹配而失败。&lt;/p>
&lt;p>&lt;strong>可能原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>服务在容器内运行在 HTTPS 模式（默认 443 端口或你配置的 5980 端口），但你尝试用 HTTP 访问。&lt;/li>
&lt;li>SSL 证书配置错误，导致服务无法正常响应。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>检查方法&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>确认服务是否使用 HTTPS：
&lt;ul>
&lt;li>检查你的后端服务配置文件（例如 Spring Boot 的 &lt;code>application.properties&lt;/code> 或 Node.js 的 HTTPS 配置），确认监听的协议和端口。&lt;/li>
&lt;li>如果服务监听的是 HTTPS，访问时需要使用 &lt;code>https://localhost:5980&lt;/code> 而不是 &lt;code>http://localhost:5980&lt;/code>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>检查容器内的服务是否正常运行：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it &amp;lt;容器名称或ID&amp;gt; bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>进入容器后，运行 &lt;code>netstat -tuln&lt;/code> 或 &lt;code>ss -tuln&lt;/code> 检查 5980 端口是否在监听。如果没有，说明服务未正确启动。&lt;/li>
&lt;li>检查 SSL 证书：
&lt;ul>
&lt;li>确保 SSL 证书和密钥文件已正确挂载到容器中（通过 &lt;code>-v&lt;/code> 挂载卷或 Dockerfile 复制）。&lt;/li>
&lt;li>确保证书有效（未过期，主机名匹配等）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>解决方法&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>如果服务使用 HTTPS，访问时使用 &lt;code>https://localhost:5980&lt;/code>。&lt;/li>
&lt;li>如果证书有问题，尝试临时禁用 SSL（用于测试），或确保证书正确配置。&lt;/li>
&lt;li>如果你希望继续使用 HTTP，检查服务是否支持 HTTP 模式（例如，修改配置文件将服务切换到 HTTP）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h5 id="3-主机与容器的网络问题">3. &lt;strong>主机与容器的网络问题&lt;/strong>
&lt;/h5>&lt;p>Docker 容器默认运行在桥接网络（bridge network）中，主机和容器之间的网络通信需要正确配置。&lt;/p>
&lt;p>&lt;strong>可能原因&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>防火墙或安全组阻止了主机的 5980 端口（或随机映射的端口）。&lt;/li>
&lt;li>容器网络模式配置错误（例如使用了 &lt;code>host&lt;/code> 网络模式但未正确配置）。&lt;/li>
&lt;li>服务绑定了错误的网络接口（例如只绑定了 &lt;code>127.0.0.1&lt;/code>，而不是 &lt;code>0.0.0.0&lt;/code>）。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>检查方法&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>确认服务监听的 IP 地址：
&lt;ul>
&lt;li>进入容器，运行 &lt;code>netstat -tuln&lt;/code> 检查服务是否绑定到 &lt;code>0.0.0.0:5980&lt;/code>（接受所有接口的连接）或 &lt;code>127.0.0.1:5980&lt;/code>（仅接受本地连接）。&lt;/li>
&lt;li>如果绑定到 &lt;code>127.0.0.1&lt;/code>，容器外部无法访问。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>检查主机防火墙：
&lt;ul>
&lt;li>在 Linux 上运行 &lt;code>iptables -L&lt;/code> 或 &lt;code>ufw status&lt;/code> 检查是否阻止了 5980 端口。&lt;/li>
&lt;li>在 Windows/Mac 上，检查系统防火墙设置。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>检查 Docker 网络模式：
&lt;ul>
&lt;li>如果你使用了 &lt;code>--network host&lt;/code>，容器直接使用主机的网络，端口映射（&lt;code>-p&lt;/code> 或 &lt;code>-P&lt;/code>）无效，服务端口需要与主机一致。&lt;/li>
&lt;li>如果使用默认桥接网络，确保端口映射正确。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>解决方法&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>确保服务监听 &lt;code>0.0.0.0&lt;/code> 而不是 &lt;code>127.0.0.1&lt;/code>。在后端服务配置中修改监听地址（例如 Spring Boot 的 &lt;code>server.address=0.0.0.0&lt;/code>）。&lt;/li>
&lt;li>开放主机防火墙的相应端口：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo ufw allow &lt;span class="m">5980&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>如果不需要桥接网络，可以尝试使用 &lt;code>--network host&lt;/code> 运行容器：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --network host &amp;lt;镜像名称&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>但注意这会绕过 Docker 的端口映射机制，容器直接使用主机端口。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h5 id="4-本机运行-vs-容器运行的差异">4. &lt;strong>本机运行 vs. 容器运行的差异&lt;/strong>
&lt;/h5>&lt;p>你提到在本机运行代码可以访问，但在容器中无法访问。以下是可能的原因：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>环境差异&lt;/strong>：
&lt;ul>
&lt;li>本机运行时，服务可能默认监听 HTTP 或绑定到 &lt;code>127.0.0.1&lt;/code>，而容器中可能配置不同。&lt;/li>
&lt;li>容器中的环境变量、配置文件或依赖可能与本机不一致。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>端口冲突&lt;/strong>：
&lt;ul>
&lt;li>如果主机上已有其他服务占用了 5980 端口，Docker 端口映射可能失败。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>证书路径问题&lt;/strong>：
&lt;ul>
&lt;li>容器内的 SSL 证书路径可能与本机不同，导致服务启动失败或无法响应 HTTPS 请求。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>检查方法&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>比较本机和容器的服务配置：
&lt;ul>
&lt;li>检查本机和容器中的配置文件（例如 &lt;code>application.properties&lt;/code> 或 &lt;code>config.json&lt;/code>）。&lt;/li>
&lt;li>确保环境变量（通过 &lt;code>docker run -e&lt;/code> 设置）一致。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>检查主机端口占用：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">netstat -tuln &lt;span class="p">|&lt;/span> grep &lt;span class="m">5980&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>如果 5980 端口已被占用，尝试使用其他端口映射（例如 &lt;code>-p 5981:5980&lt;/code>）。&lt;/li>
&lt;li>检查容器日志：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker logs &amp;lt;容器名称或ID&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>查看服务是否启动成功，是否有 SSL 或端口相关的错误。&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>解决方法&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>确保容器内的配置文件与本机一致。&lt;/li>
&lt;li>如果端口冲突，释放主机上的 5980 端口或使用其他端口映射。&lt;/li>
&lt;li>检查日志并修复启动错误（例如缺少依赖、证书路径错误等）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h5 id="5-如何调试和解决">5. &lt;strong>如何调试和解决&lt;/strong>
&lt;/h5>&lt;p>按照以下步骤逐步排查：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>确认端口映射&lt;/strong>：
&lt;ul>
&lt;li>使用 &lt;code>docker port &amp;lt;容器名称或ID&amp;gt;&lt;/code> 查看实际映射的端口。&lt;/li>
&lt;li>尝试访问正确的映射端口（例如 &lt;code>http://localhost:&amp;lt;映射端口&amp;gt;&lt;/code> 或 &lt;code>https://localhost:&amp;lt;映射端口&amp;gt;&lt;/code>）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>检查服务状态&lt;/strong>：
&lt;ul>
&lt;li>进入容器，确认服务是否在 5980 端口监听，并检查绑定地址（&lt;code>0.0.0.0&lt;/code> 或 &lt;code>127.0.0.1&lt;/code>）。&lt;/li>
&lt;li>检查日志是否有错误。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>测试 HTTPS&lt;/strong>：
&lt;ul>
&lt;li>使用 &lt;code>curl -k https://localhost:5980&lt;/code>（&lt;code>-k&lt;/code> 忽略证书验证）测试是否能连接。&lt;/li>
&lt;li>如果失败，检查 SSL 配置或尝试 HTTP。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>尝试显式端口映射&lt;/strong>：
&lt;ul>
&lt;li>停止并删除当前容器，重新运行：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop &amp;lt;容器名称或ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm &amp;lt;容器名称或ID&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -p 5980:5980 &amp;lt;镜像名称&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>检查网络和防火墙&lt;/strong>：
&lt;ul>
&lt;li>确保主机防火墙允许 5980 端口。&lt;/li>
&lt;li>如果在云服务器上运行，检查安全组规则是否开放 5980 端口。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h5 id="6-总结">6. &lt;strong>总结&lt;/strong>
&lt;/h5>&lt;p>你无法访问 &lt;code>http://localhost:5980&lt;/code> 的主要原因可能是：&lt;/p>
&lt;ul>
&lt;li>使用 &lt;code>-P&lt;/code> 参数导致主机端口不是 5980（而是随机端口）。&lt;/li>
&lt;li>服务使用 HTTPS，但你尝试用 HTTP 访问。&lt;/li>
&lt;li>服务绑定了 &lt;code>127.0.0.1&lt;/code> 而不是 &lt;code>0.0.0.0&lt;/code>，或存在端口冲突。&lt;/li>
&lt;li>SSL 证书配置错误或容器环境与本机不一致。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>推荐步骤&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>使用 &lt;code>docker port&lt;/code> 检查实际映射端口，尝试访问正确的端口。&lt;/li>
&lt;li>确保访问协议正确（HTTP 或 HTTPS）。&lt;/li>
&lt;li>使用显式端口映射 &lt;code>-p 5980:5980&lt;/code> 重启容器。&lt;/li>
&lt;li>检查容器日志和服务配置，确保与本机运行一致。&lt;/li>
&lt;li>如果问题仍未解决，提供以下信息以便进一步分析：
&lt;ul>
&lt;li>你的 &lt;code>docker run&lt;/code> 完整命令。&lt;/li>
&lt;li>容器日志（&lt;code>docker logs &amp;lt;容器ID&amp;gt;&lt;/code>）。&lt;/li>
&lt;li>服务配置文件或代码片段（涉及端口和 SSL 配置的部分）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>如果需要更具体的帮助，请提供上述信息，我可以进一步协助你！&lt;/p>
&lt;h1 id="docker实战">docker实战
&lt;/h1>&lt;h3 id="docker使用实战-容器保存和迁移">Docker使用实战-容器保存和迁移
&lt;/h3>&lt;p>在 Docker 中，如果你想保存一个容器，可以将其保存为镜像（image），然后在需要时基于这个镜像重新创建容器。以下是具体步骤：&lt;/p>
&lt;h4 id="方法一将容器保存为镜像">方法一：将容器保存为镜像
&lt;/h4>&lt;ol>
&lt;li>
&lt;p>&lt;strong>查看正在运行的容器&lt;/strong>
使用以下命令列出当前运行的容器，找到你要保存的容器 ID 或名称：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker ps -a
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>提交容器为镜像&lt;/strong>
使用 &lt;code>docker commit&lt;/code> 命令将容器保存为一个新的镜像。假设你的容器 ID 是 &lt;code>abc123&lt;/code>，你想保存为镜像名称 &lt;code>myimage:latest&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker commit abc123 myimage:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>abc123&lt;/code> 是容器 ID 或容器名称。&lt;/li>
&lt;li>&lt;code>myimage:latest&lt;/code> 是你想保存的新镜像名称和标签。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>验证镜像&lt;/strong>
保存后，可以用以下命令查看新创建的镜像：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker images
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>（可选）保存镜像到文件&lt;/strong>
如果你想将镜像导出为一个 &lt;code>.tar&lt;/code> 文件以便备份或转移到其他机器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker save -o myimage.tar myimage:latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 或者&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 将镜像传递到远程服务器 将 your-user@your-server 替换为你的 SSH 登录信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker save my-x86-app:1.0 &lt;span class="p">|&lt;/span> ssh your-user@your-server &lt;span class="s1">&amp;#39;docker load&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样会生成一个 &lt;code>myimage.tar&lt;/code> 文件。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>（可选）加载镜像&lt;/strong>
在其他地方使用时，可以通过以下命令加载 &lt;code>.tar&lt;/code> 文件：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker load -i myimage.tar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h4 id="方法二直接导出容器">方法二：直接导出容器
&lt;/h4>&lt;p>如果你不需要将其转化为镜像，而是想直接保存容器的完整状态（包括文件系统和配置），可以用 &lt;code>docker export&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">export&lt;/span> abc123 &amp;gt; container.tar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>这会将容器导出为一个 &lt;code>.tar&lt;/code> 文件。&lt;/li>
&lt;li>之后可以用 &lt;code>docker import&lt;/code> 导入这个文件为镜像：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker import container.tar myimage:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h4 id="注意事项-1">注意事项
&lt;/h4>&lt;ul>
&lt;li>&lt;strong>&lt;code>docker commit&lt;/code> vs &lt;code>docker export&lt;/code>&lt;/strong>：
&lt;ul>
&lt;li>&lt;code>docker commit&lt;/code> 保存的是容器的运行时状态为一个新镜像，适合需要保留容器修改的情况。&lt;/li>
&lt;li>&lt;code>docker export&lt;/code> 导出的只是文件系统快照，不包括容器的元数据（如 CMD、ENTRYPOINT 等）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>推荐方式&lt;/strong>：通常建议使用 &lt;code>docker commit&lt;/code> 并结合 Dockerfile 来管理镜像，这样更符合 Docker 的最佳实践。&lt;/li>
&lt;/ul>
&lt;h4 id="使用保存的镜像">使用保存的镜像
&lt;/h4>&lt;p>保存为镜像后，随时可以用以下命令基于镜像启动新容器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d myimage:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果你有其他具体需求（比如保存到某个地方或自动化脚本），可以告诉我，我再帮你调整方案！&lt;/p>
&lt;h3 id="docker-服务部署教程">Docker 服务部署教程
&lt;/h3>&lt;p>在开始之前，我们先快速理解三个最重要的概念：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Docker&lt;/strong>：一个开源平台，用于开发、发布和运行应用程序。它允许你将应用程序及其所有依赖（库、环境变量、配置文件等）打包到一个标准化的单元中，这个单元被称为“容器”。&lt;/li>
&lt;li>&lt;strong>镜像 (Image)&lt;/strong>：一个只读的模板，包含了运行应用程序所需的所有内容——代码、运行时、库、环境变量和配置文件。你可以把镜像理解为安装程序或一个“蓝图”。&lt;/li>
&lt;li>&lt;strong>容器 (Container)&lt;/strong>：镜像的运行实例。当你运行一个镜像，你就启动了一个容器。容器是独立的、轻量级的，并且在你的主机操作系统上运行，但与主机和其他容器隔离。你可以同时运行同一个镜像的多个容器。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>简单比喻&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Dockerfile&lt;/code> 是菜谱。&lt;/li>
&lt;li>&lt;code>镜像 (Image)&lt;/code> 是做好的、打包在盒子里的预制菜。&lt;/li>
&lt;li>&lt;code>容器 (Container)&lt;/code> 是你把预制菜放进微波炉加热后，正在运行、可以享用的那份饭菜。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h4 id="环境准备">环境准备
&lt;/h4>&lt;p>在开始之前，你需要在你的机器上安装 Docker。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Windows / macOS&lt;/strong>: 下载并安装 &lt;strong>Docker Desktop&lt;/strong>。它提供了一个图形化界面和命令行工具。
&lt;ul>
&lt;li>官方下载地址: &lt;code>https://www.docker.com/products/docker-desktop/&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Linux&lt;/strong>: 根据你的发行版（如 Ubuntu, CentOS）按照官方文档进行安装。
&lt;ul>
&lt;li>官方安装指南: &lt;code>https://docs.docker.com/engine/install/&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>安装完成后，打开你的终端（在 Windows 上是 PowerShell 或 WSL2，macOS 上是 Terminal），运行以下命令来验证 Docker 是否安装成功：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果命令能够成功执行并显示版本信息和一个空的容器列表，说明 Docker 已经准备就绪。&lt;/p>
&lt;hr>
&lt;h4 id="实战演练部署一个-python-web-应用">实战演练：部署一个 Python Web 应用
&lt;/h4>&lt;p>我们将部署一个简单的 Python Flask Web 应用，它会在访问时返回 &amp;ldquo;Hello, Docker!&amp;quot;。&lt;/p>
&lt;h5 id="步骤一准备应用程序">步骤一：准备应用程序
&lt;/h5>&lt;p>首先，在你喜欢的位置创建一个新的项目文件夹，例如 &lt;code>my-docker-app&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir my-docker-app
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-docker-app
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在该文件夹中，创建以下两个文件：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>&lt;code>app.py&lt;/code>&lt;/strong> (我们的 Web 应用程序代码)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">flask&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Flask&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建 Flask 应用实例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Flask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="vm">__name__&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义根路由&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.route&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">hello&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Hello, Docker! This is a simple web service.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># host=&amp;#39;0.0.0.0&amp;#39; 让容器外部可以访问&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># port=5000 是应用监听的端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">host&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;0.0.0.0&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>&lt;code>requirements.txt&lt;/code>&lt;/strong> (列出 Python 依赖)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Flask==2.2.2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>现在你的文件夹结构应该是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">my-docker-app/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── app.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── requirements.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="步骤二编写-dockerfile">步骤二：编写 Dockerfile
&lt;/h5>&lt;p>&lt;code>Dockerfile&lt;/code> 是一个文本文件，它包含了构建 Docker 镜像所需的所有指令。在 &lt;code>my-docker-app&lt;/code> 文件夹中创建名为 &lt;code>Dockerfile&lt;/code> (没有文件扩展名) 的文件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 步骤 1: 选择一个基础镜像&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 我们选择一个官方的、轻量级的 Python 3.9 镜像&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> python:3.9-slim&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 步骤 2: 在镜像中设置工作目录&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 之后的所有命令都会在这个目录下执行&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 步骤 3: 复制依赖文件到工作目录&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 将 requirements.txt 复制到镜像的 /app 目录中&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> requirements.txt .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 步骤 4: 安装应用程序的依赖&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 在镜像中运行 pip 命令来安装 Flask&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> pip install --no-cache-dir -r requirements.txt&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 步骤 5: 复制应用程序代码到工作目录&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 将当前目录下的所有文件 (app.py) 复制到镜像的 /app 目录&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> . .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 步骤 6: 声明容器将监听的端口&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 这只是一个元数据声明，告诉用户这个容器内部的应用会使用 5000 端口&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">EXPOSE&lt;/span>&lt;span class="s"> 5000&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 步骤 7: 定义启动容器时要执行的命令&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 运行我们的 Python 应用&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;app.py&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="步骤三构建-docker-镜像">步骤三：构建 Docker 镜像
&lt;/h5>&lt;p>现在我们有了应用程序和 &lt;code>Dockerfile&lt;/code>，可以构建我们的镜像了。确保你的终端当前路径在 &lt;code>my-docker-app&lt;/code> 文件夹下。&lt;/p>
&lt;p>运行 &lt;code>docker build&lt;/code> 命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -t my-web-app:v1 给镜像起一个名字 (tag)，格式是 &amp;lt;name&amp;gt;:&amp;lt;version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># . 表示使用当前目录下的 Dockerfile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker build -t my-web-app:v1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-11.png"
width="464"
height="181"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-11_hu16836429575141968351.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-11_hu11469463485972152702.png 1024w"
loading="lazy"
alt="构建"
class="gallery-image"
data-flex-grow="256"
data-flex-basis="615px"
>&lt;/p>
&lt;p>你会看到 Docker 按照 &lt;code>Dockerfile&lt;/code> 中的步骤逐一执行。构建成功后，你可以用以下命令查看你刚刚创建的镜像：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker images
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>你应该能在列表中看到 &lt;code>my-web-app&lt;/code> 这个镜像。&lt;/p>
&lt;h5 id="步骤四运行-docker-容器">步骤四：运行 Docker 容器
&lt;/h5>&lt;p>镜像已经构建好了，现在让我们用它来启动一个容器。&lt;/p>
&lt;p>运行 &lt;code>docker run&lt;/code> 命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d -p 8888:5000 --name my-first-container my-web-app:v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>让我们分解一下这个命令：&lt;/p>
&lt;ul>
&lt;li>&lt;code>docker run&lt;/code>: 运行一个容器的命令。&lt;/li>
&lt;li>&lt;code>-d&lt;/code> (detached): 后台运行模式。容器将在后台启动并持续运行。&lt;/li>
&lt;li>&lt;code>-p 8888:5000&lt;/code> (port mapping): 端口映射。这是非常重要的一步。
&lt;ul>
&lt;li>它将&lt;strong>宿主机 (你的电脑)&lt;/strong> 的 &lt;code>8888&lt;/code> 端口映射到&lt;strong>容器&lt;/strong>的 &lt;code>5000&lt;/code> 端口。&lt;/li>
&lt;li>&lt;code>5000&lt;/code> 是我们在 &lt;code>app.py&lt;/code> 和 &lt;code>Dockerfile&lt;/code> 中指定的应用端口。&lt;/li>
&lt;li>&lt;code>8888&lt;/code> 是我们希望通过外部访问的端口。你可以换成其他未被占用的端口，如 &lt;code>80&lt;/code>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>--name my-first-container&lt;/code>: 给这个正在运行的容器起一个友好的名字，方便管理。&lt;/li>
&lt;li>&lt;code>my-web-app:v1&lt;/code>: 指定要运行的镜像。&lt;/li>
&lt;/ul>
&lt;h5 id="步骤五验证和管理">步骤五：验证和管理
&lt;/h5>&lt;ol>
&lt;li>
&lt;p>&lt;strong>验证服务&lt;/strong>
打开你的浏览器，访问 &lt;code>http://localhost:8888&lt;/code>。
你应该能看到页面上显示 &amp;ldquo;Hello, Docker! This is a simple web service.&amp;quot;。&lt;/p>
&lt;p>你也可以使用 &lt;code>curl&lt;/code> 命令在终端验证：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl http://localhost:8888
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>管理容器&lt;/strong>
以下是一些常用的容器管理命令：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>查看正在运行的容器&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>查看容器日志&lt;/strong> (非常适合排查问题)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker logs my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>停止容器&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>重新启动已停止的容器&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker start my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>删除容器&lt;/strong> (必须先停止容器才能删除)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker rm my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>删除镜像&lt;/strong> (必须先删除所有使用该镜像的容器才能删除)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker rmi my-web-app:v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h4 id="进阶使用-docker-compose-编排服务">进阶：使用 Docker Compose 编排服务
&lt;/h4>&lt;p>当你的应用变得复杂，比如需要一个 Web 服务和一个数据库服务时，手动管理多个容器会很麻烦。&lt;code>Docker Compose&lt;/code> 就是解决这个问题的工具。它允许你使用一个 &lt;code>YAML&lt;/code> 文件来定义和运行多容器的 Docker 应用程序。&lt;/p>
&lt;h5 id="步骤一安装-docker-compose">步骤一：安装 Docker Compose
&lt;/h5>&lt;p>如果你安装的是 Docker Desktop (Windows/macOS)，那么 Docker Compose 已经包含在内了。你可以通过 &lt;code>docker-compose --version&lt;/code> 或 &lt;code>docker compose version&lt;/code> 来验证。&lt;/p>
&lt;h5 id="步骤二创建-docker-composeyml-文件">步骤二：创建 &lt;code>docker-compose.yml&lt;/code> 文件
&lt;/h5>&lt;p>在你的 &lt;code>my-docker-app&lt;/code> 文件夹中，创建一个名为 &lt;code>docker-compose.yml&lt;/code> 的文件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># docker-compose.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 定义 Compose 文件的版本&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 定义服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 我们的 Web 应用服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">web&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 构建指令&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用当前目录下的 Dockerfile&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 镜像名称&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-web-app-compose:latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 容器名称&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-web-app-container&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 端口映射&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8888:5000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 卷挂载 (可选，用于代码热更新)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 将本地代码目录挂载到容器的 /app 目录&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 这样修改本地代码后，服务会自动重启（需要 Flask 开启 debug 模式）&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">.:/app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 环境变量&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">FLASK_ENV=development&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 开启 Flask 调试模式&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 假设我们还需要一个 Redis 服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 直接使用官方 Redis 镜像&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;redis:alpine&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-redis-cache&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="步骤三使用-docker-compose-启动服务">步骤三：使用 Docker Compose 启动服务
&lt;/h5>&lt;p>在 &lt;code>my-docker-app&lt;/code> 目录下，运行以下命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启动所有服务 (-d 表示后台运行)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose up -d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或者新版本命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># docker compose up -d&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compose 会自动帮你：&lt;/p>
&lt;ol>
&lt;li>构建 &lt;code>web&lt;/code> 服务的镜像（如果尚未构建）。&lt;/li>
&lt;li>拉取 &lt;code>redis&lt;/code> 镜像。&lt;/li>
&lt;li>创建并启动 &lt;code>web&lt;/code> 和 &lt;code>redis&lt;/code> 两个容器，并根据配置建立网络连接。&lt;/li>
&lt;/ol>
&lt;h5 id="步骤四管理-compose-服务">步骤四：管理 Compose 服务
&lt;/h5>&lt;ul>
&lt;li>&lt;strong>查看服务状态&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker-compose ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>查看日志&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker-compose logs -f web &lt;span class="c1"># 查看 web 服务的日志&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>停止并删除所有服务&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker-compose down
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h4 id="总结">总结
&lt;/h4>&lt;p>恭喜你！你已经学会了如何使用 Docker 部署一个单服务的 Web 应用，并了解了如何使用 Docker Compose 管理更复杂的应用场景。&lt;/p>
&lt;p>&lt;strong>核心流程回顾&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>写代码&lt;/strong> (&lt;code>app.py&lt;/code>, &lt;code>requirements.txt&lt;/code>)&lt;/li>
&lt;li>&lt;strong>写配置&lt;/strong> (&lt;code>Dockerfile&lt;/code>)&lt;/li>
&lt;li>&lt;strong>构建镜像&lt;/strong> (&lt;code>docker build&lt;/code>)&lt;/li>
&lt;li>&lt;strong>运行容器&lt;/strong> (&lt;code>docker run&lt;/code> 或 &lt;code>docker-compose up&lt;/code>)&lt;/li>
&lt;/ol>
&lt;p>Docker 的世界非常广阔，接下来你可以继续探索数据持久化（Volumes）、网络（Networking）、以及如何将你的镜像推送到 Docker Hub 与他人共享。&lt;/p>
&lt;h3 id="docker服务部署教程2-使用uv替代pip">Docker服务部署教程2-使用uv替代pip
&lt;/h3>&lt;p>好的，当然可以。使用 &lt;code>uv&lt;/code> 来替代 &lt;code>pip&lt;/code> 进行包管理是一个非常棒的选择，尤其是在 Docker 构建环境中，因为 &lt;code>uv&lt;/code> 的速度极快，可以显著缩短镜像构建的时间。&lt;/p>
&lt;p>改动主要集中在 &lt;code>Dockerfile&lt;/code> 文件中。你需要添加安装 &lt;code>uv&lt;/code> 的步骤，并用 &lt;code>uv pip install&lt;/code> 替换 &lt;code>pip install&lt;/code>。你的应用程序代码 (&lt;code>app.py&lt;/code>) 和依赖列表 (&lt;code>requirements.txt&lt;/code>) &lt;strong>完全不需要改变&lt;/strong>。&lt;/p>
&lt;p>下面是详细的说明和更新后的代码。&lt;/p>
&lt;h4 id="使用-uv-的核心改动">使用 UV 的核心改动
&lt;/h4>&lt;p>&lt;code>uv&lt;/code> 是一个独立的二进制文件，我们需要在 Docker 镜像中先安装它。最直接的方法是使用官方提供的安装脚本。&lt;/p>
&lt;p>更新后的 &lt;code>Dockerfile&lt;/code> 会包含以下几个新步骤：&lt;/p>
&lt;ol>
&lt;li>安装 &lt;code>curl&lt;/code> 工具，以便下载 &lt;code>uv&lt;/code> 安装脚本。&lt;/li>
&lt;li>运行 &lt;code>uv&lt;/code> 的安装脚本。&lt;/li>
&lt;li>将 &lt;code>uv&lt;/code> 的路径添加到环境变量 &lt;code>PATH&lt;/code> 中，以便后续命令可以找到它。&lt;/li>
&lt;li>使用 &lt;code>uv&lt;/code> 来安装依赖。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h4 id="更新后的-dockerfile-使用-uv">更新后的 &lt;code>Dockerfile&lt;/code> (使用 uv)
&lt;/h4>&lt;p>这是修改后的 &lt;code>Dockerfile&lt;/code>。你可以直接用它替换掉原来项目中的 &lt;code>Dockerfile&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Use a Python image with uv pre-installed&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> ghcr.io/astral-sh/uv:python3.12-bookworm-slim&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Set working directory&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Enable bytecode compilation&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">UV_COMPILE_BYTECODE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Copy from the cache instead of linking since it&amp;#39;s a mounted volume&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">UV_LINK_MODE&lt;/span>&lt;span class="o">=&lt;/span>copy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Copy requirements file first for better layer caching&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> requirements.txt .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Install dependencies using uv with requirements.txt&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> --mount&lt;span class="o">=&lt;/span>&lt;span class="nv">type&lt;/span>&lt;span class="o">=&lt;/span>cache,target&lt;span class="o">=&lt;/span>/root/.cache/uv &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> uv pip install --system -r requirements.txt&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copy application code&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> . .&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Expose port 5000 (Flask default)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">EXPOSE&lt;/span>&lt;span class="s"> 5000&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Reset the entrypoint, don&amp;#39;t invoke `uv`&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENTRYPOINT&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Run the Flask application&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;app.py&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>你的整体工作流程几乎不变，只是替换了 &lt;code>Dockerfile&lt;/code> 的内容。
之前使用gemini的教程但是报错了
&lt;img src="https://www.zata.cc/images/index/index-12.png"
loading="lazy"
alt="报错信息"
>
所以最好使用uv官方提供的镜像，具体使用教程可见&lt;/p>
&lt;p>&lt;span style="color:red;">&lt;a class="link" href="https://hellowac.github.io/uv-zh-cn/guides/integration/docker/" target="_blank" rel="noopener"
>在 Docker 中使用 uv&lt;/a>&lt;/span>&lt;/p>
&lt;h4 id="使用-uv-的完整流程">使用 UV 的完整流程
&lt;/h4>&lt;pre>&lt;code>```bash
docker build -t my-web-app:uv-latest .
```
（我们用了 `uv-latest` 这个新标签来区分版本）
在这一步，你会注意到 `RUN uv pip install` 这一层会比之前使用 `pip` 快非常多，尤其是在依赖项很多的情况下。
&lt;/code>&lt;/pre>
&lt;hr>
&lt;ol start="3">
&lt;li>
&lt;p>&lt;strong>运行容器（命令不变）&lt;/strong>
使用新的镜像标签来运行容器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d -p 8888:5000 --name my-uv-container my-web-app:uv-latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/images/index/index-13.png"
loading="lazy"
alt="结果"
>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>验证（方式不变）&lt;/strong>
访问 &lt;code>http://localhost:8888&lt;/code>，一切应该和之前一样正常工作。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="对于-docker-compose-呢">对于 Docker Compose 呢？
&lt;/h4>&lt;p>&lt;strong>你的 &lt;code>docker-compose.yml&lt;/code> 文件完全不需要任何改动。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># docker-compose.yml (无需改动)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">web&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8888:5000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># ... 其他配置&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这就是 Docker 的强大之处。&lt;code>docker-compose.yml&lt;/code> 只关心“做什么”（比如，从当前目录 &lt;code>.&lt;/code> 构建一个镜像），而不关心“怎么做”（具体是使用 &lt;code>pip&lt;/code> 还是 &lt;code>uv&lt;/code> 来安装依赖）。所有的实现细节都被封装在 &lt;code>Dockerfile&lt;/code> 里了。当你运行 &lt;code>docker-compose up&lt;/code> 时，Compose 会自动使用你更新后的 &lt;code>Dockerfile&lt;/code> 来构建镜像。&lt;/p>
&lt;h4 id="总结-1">总结
&lt;/h4>&lt;p>将包管理器从 &lt;code>pip&lt;/code> 切换到 &lt;code>uv&lt;/code>，你只需要：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>更新 &lt;code>Dockerfile&lt;/code>&lt;/strong> 以包含安装和使用 &lt;code>uv&lt;/code> 的指令。&lt;/li>
&lt;li>重新 &lt;strong>&lt;code>build&lt;/code>&lt;/strong> 你的镜像。&lt;/li>
&lt;/ol>
&lt;p>其他所有代码、文件和部署命令都保持原样，流程无缝衔接，同时还能享受到 &lt;code>uv&lt;/code> 带来的极致构建速度。&lt;/p>
&lt;h3 id="docker容器的部署教程3-将容器部署到服务器上">docker容器的部署教程3-将容器部署到服务器上
&lt;/h3>&lt;p>当你创建完 &lt;code>Dockerfile&lt;/code> 之后，部署一个应用通常遵循一个标准化的流程：&lt;strong>构建镜像 -&amp;gt; 推送镜像 -&amp;gt; 在目标环境运行容器&lt;/strong>。&lt;/p>
&lt;p>下面我将为你分解这个过程，并介绍从简单到复杂的不同部署方案。&lt;/p>
&lt;h4 id="核心流程">核心流程
&lt;/h4>&lt;h5 id="第一步构建-docker-镜像-build">第一步：构建 Docker 镜像 (Build)
&lt;/h5>&lt;p>这是将你的 &lt;code>Dockerfile&lt;/code> 和应用程序代码打包成一个标准化的、不可变的“Docker 镜像”的过程。&lt;/p>
&lt;p>在你的 &lt;code>Dockerfile&lt;/code> 所在的目录下，打开终端并运行以下命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># docker build -t &amp;lt;你的镜像名称&amp;gt;:&amp;lt;标签&amp;gt; .&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 示例:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker build -t my-awesome-app:v1.0 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>docker build&lt;/code>: 构建命令。&lt;/li>
&lt;li>&lt;code>-t my-awesome-app:v1.0&lt;/code>: &lt;code>-t&lt;/code> 参数用来给镜像打上“标签”（tag），格式通常是 &lt;code>image_name:version&lt;/code>。这非常重要，便于版本管理。&lt;/li>
&lt;li>&lt;code>.&lt;/code>: 这个点表示 &lt;code>Dockerfile&lt;/code> 的上下文路径（Context Path）是当前目录。Docker 会把这个目录下的所有文件发送给 Docker 守护进程来帮助构建。&lt;/li>
&lt;/ul>
&lt;p>构建成功后，你可以通过 &lt;code>docker images&lt;/code> 命令查看你本地的所有镜像。&lt;/p>
&lt;h5 id="第二步在本地运行和测试-run-locally">第二步：在本地运行和测试 (Run Locally)
&lt;/h5>&lt;p>在推送到服务器之前，首先在本地运行容器以确保它能正常工作。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># docker run --rm -p &amp;lt;本地端口&amp;gt;:&amp;lt;容器端口&amp;gt; &amp;lt;你的镜像名称&amp;gt;:&amp;lt;标签&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 示例: 假设你的应用在容器内监听 8080 端口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run --rm -p 3000:8080 my-awesome-app:v1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>docker run&lt;/code>: 运行命令。&lt;/li>
&lt;li>&lt;code>--rm&lt;/code>: 容器停止后自动删除，适合测试。&lt;/li>
&lt;li>&lt;code>-p 3000:8080&lt;/code>: &lt;code>-p&lt;/code> 参数用来做端口映射（Port Mapping），将你本机的 &lt;code>3000&lt;/code> 端口映射到容器的 &lt;code>8080&lt;/code> 端口。这样你就可以通过访问 &lt;code>http://localhost:3000&lt;/code> 来访问你的应用了。&lt;/li>
&lt;li>&lt;code>my-awesome-app:v1.0&lt;/code>: 指定要运行的镜像。&lt;/li>
&lt;/ul>
&lt;h5 id="第三步推送镜像到镜像仓库-push-to-registry">第三步：推送镜像到镜像仓库 (Push to Registry)
&lt;/h5>&lt;p>为了在其他机器（例如你的云服务器）上使用这个镜像，你需要将它推送到一个中央存储库，这被称为“镜像仓库”（Image Registry）。&lt;/p>
&lt;p>最常用的公共仓库是 &lt;a class="link" href="https://hub.docker.com/" target="_blank" rel="noopener"
>Docker Hub&lt;/a>。各大云服务商也提供私有仓库，如 Google Artifact Registry (GCR), Amazon Elastic Container Registry (ECR), Azure Container Registry (ACR) 等。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>为镜像打上符合仓库要求的标签&lt;/strong>：
推送前，你需要将镜像重命名为 &lt;code>&amp;lt;你的仓库用户名&amp;gt;/&amp;lt;镜像名称&amp;gt;:&amp;lt;标签&amp;gt;&lt;/code> 的格式。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 格式: docker tag &amp;lt;本地镜像&amp;gt; &amp;lt;仓库用户名&amp;gt;/&amp;lt;新镜像名&amp;gt;:&amp;lt;标签&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 示例 (以 Docker Hub 为例):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker tag my-awesome-app:v1.0 your-dockerhub-username/my-awesome-app:v1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>登录你的镜像仓库&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 登录 Docker Hub&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 按照提示输入你的 Docker Hub 用户名和密码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 然后这个页面不要关，下面推送的时候打开一个新的终端&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/images/index/index-15.png"
loading="lazy"
alt="保持这个界面"
>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>推送镜像&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 格式: docker push &amp;lt;仓库用户名&amp;gt;/&amp;lt;镜像名&amp;gt;:&amp;lt;标签&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 示例:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker push your-dockerhub-username/my-awesome-app:v1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/images/index/index-14.png"
loading="lazy"
alt="推送好的镜像"
>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="第四步选择部署方案-deploy">第四步：选择部署方案 (Deploy)
&lt;/h4>&lt;p>现在你的镜像已经准备就绪，可以部署了。以下是几种常见的部署方案，从易到难：&lt;/p>
&lt;hr>
&lt;h5 id="方案一在单台云服务器上手动部署-最简单">方案一：在单台云服务器上手动部署 (最简单)
&lt;/h5>&lt;p>这种方式适合个人项目、测试环境或小型应用。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>准备服务器&lt;/strong>：购买一台云服务器（如 GCP, AWS, Azure, 阿里云等），并确保已安装 Docker。&lt;/li>
&lt;li>&lt;strong>SSH 登录服务器&lt;/strong>：通过 SSH 客户端连接到你的服务器。&lt;/li>
&lt;li>&lt;strong>拉取镜像&lt;/strong>：在服务器上，从你之前推送的仓库中拉取镜像。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker pull your-dockerhub-username/my-awesome-app:v1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>运行容器&lt;/strong>：与在本地运行类似，但在服务器上通常使用 &lt;code>-d&lt;/code> 参数让容器在后台运行。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># -d (detached mode) 表示在后台运行容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d -p 80:8080 --name my-running-app your-dockerhub-username/my-awesome-app:v1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>这里我们将服务器的 &lt;code>80&lt;/code> 端口（HTTP 默认端口）映射到容器的 &lt;code>8080&lt;/code> 端口。现在，你可以通过服务器的 IP 地址直接访问你的应用。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h5 id="方案二使用-docker-compose-部署-推荐用于多容器应用">方案二：使用 Docker Compose 部署 (推荐用于多容器应用)
&lt;/h5>&lt;p>如果你的应用包含多个服务（例如一个 Web 应用 +一个数据库），使用 &lt;code>Docker Compose&lt;/code> 会让管理变得非常简单。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>创建 &lt;code>docker-compose.yml&lt;/code> 文件&lt;/strong>：在你的项目中创建一个 &lt;code>docker-compose.yml&lt;/code> 文件。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">your-dockerhub-username/my-awesome-app:v1.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;80:8080&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restart&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">always&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 容器挂掉后自动重启&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 如果有数据库，可以这样添加&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># db:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># image: postgres:13&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># environment:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># - POSTGRES_PASSWORD=mysecretpassword&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>部署&lt;/strong>：将 &lt;code>docker-compose.yml&lt;/code> 文件上传到你的服务器，然后在该文件所在的目录运行：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 以后台模式启动所有服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose up -d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 如果需要更新镜像并重新部署&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose pull &lt;span class="c1"># 拉取 yml 文件中定义的所有镜像的最新版&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose up -d --force-recreate &lt;span class="c1"># 强制重新创建容器&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h5 id="方案三部署到云平台的容器服务-paasserverless">方案三：部署到云平台的容器服务 (PaaS/Serverless)
&lt;/h5>&lt;p>这种方式可以让你不用管理底层服务器，只需提供容器镜像，平台会自动为你扩缩容和管理。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Google Cloud Run&lt;/strong>：非常简单的无服务器平台。你只需将镜像推送到 Google Artifact Registry，然后在 Cloud Run 上创建一个服务，指向这个镜像即可。平台按需计费，没有请求时不收费。&lt;/li>
&lt;li>&lt;strong>AWS App Runner / Azure Container Apps&lt;/strong>：与 Google Cloud Run 类似的服务。&lt;/li>
&lt;li>&lt;strong>Heroku&lt;/strong>：传统的 PaaS 平台，也支持通过 Docker 部署。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>优点&lt;/strong>：免运维、自动扩缩容、按需付费。
&lt;strong>缺点&lt;/strong>：平台锁定性较强，自定义程度较低。&lt;/p>
&lt;hr>
&lt;h5 id="方案四使用容器编排工具-kubernetes">方案四：使用容器编排工具 (Kubernetes)
&lt;/h5>&lt;p>对于大型、复杂、高可用的生产系统，Kubernetes (K8s) 是事实上的标准。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>它是什么&lt;/strong>：一个强大的系统，用于自动化部署、扩展和管理容器化应用程序。&lt;/li>
&lt;li>&lt;strong>何时使用&lt;/strong>：当你的应用需要高可用性、自动伸缩、滚动更新、服务发现和负载均衡等高级功能时。&lt;/li>
&lt;li>&lt;strong>如何使用&lt;/strong>：
&lt;ol>
&lt;li>学习 Kubernetes 的基本概念（Pod, Deployment, Service 等）。&lt;/li>
&lt;li>编写 Kubernetes 的 YAML 配置文件来定义你的应用部署。&lt;/li>
&lt;li>使用云服务商提供的托管 Kubernetes 服务，如：
&lt;ul>
&lt;li>&lt;strong>Google Kubernetes Engine (GKE)&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Amazon Elastic Kubernetes Service (EKS)&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Azure Kubernetes Service (AKS)&lt;/strong>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>优点&lt;/strong>：极其强大和灵活，是微服务架构的最佳拍档。
&lt;strong>缺点&lt;/strong>：学习曲线非常陡峭，管理复杂。&lt;/p>
&lt;h4 id="如何选择">如何选择？
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">&lt;strong>方案&lt;/strong>&lt;/th>
&lt;th style="text-align: left">&lt;strong>适用场景&lt;/strong>&lt;/th>
&lt;th style="text-align: left">&lt;strong>优点&lt;/strong>&lt;/th>
&lt;th style="text-align: left">&lt;strong>缺点&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>手动部署&lt;/strong>&lt;/td>
&lt;td style="text-align: left">个人项目、学习、快速原型&lt;/td>
&lt;td style="text-align: left">简单直接，易于理解&lt;/td>
&lt;td style="text-align: left">手动操作多，不易扩展和管理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>Docker Compose&lt;/strong>&lt;/td>
&lt;td style="text-align: left">多容器的单机应用、开发和测试环境&lt;/td>
&lt;td style="text-align: left">大大简化多容器管理&lt;/td>
&lt;td style="text-align: left">主要面向单机，集群能力弱&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>云平台容器服务&lt;/strong>&lt;/td>
&lt;td style="text-align: left">中小型应用、无服务器爱好者&lt;/td>
&lt;td style="text-align: left">极简运维，自动扩缩容&lt;/td>
&lt;td style="text-align: left">平台锁定，不够灵活&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>Kubernetes&lt;/strong>&lt;/td>
&lt;td style="text-align: left">生产级、大型复杂系统、微服务架构&lt;/td>
&lt;td style="text-align: left">功能强大，高可用，可移植性好&lt;/td>
&lt;td style="text-align: left">学习曲线陡峭，非常复杂&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>对于初学者，我强烈建议你从 &lt;code>方案一&lt;/code> 和 &lt;code>方案二&lt;/code> 开始。&lt;/strong> 当你对容器化部署有了更深入的理解后，再根据项目需求去探索 &lt;code>方案三&lt;/code> 和 &lt;code>方案四&lt;/code>。&lt;/p>
&lt;h3 id="运行容器了但是并没有生效-通过查看日志解决问题">运行容器了但是并没有生效-通过查看日志解决问题
&lt;/h3>&lt;p>我运行容器了，但是访问localhost:8888并没有生效&lt;/p>
&lt;p>好的，这是一个非常常见的问题，别担心，我们一步一步来排查。访问 &lt;code>localhost:8888&lt;/code> 不生效通常有以下几个原因。&lt;/p>
&lt;p>请打开你的终端，按顺序执行下面的命令来定位问题。&lt;/p>
&lt;h4 id="第一步检查容器是否正在运行">&lt;strong>第一步：检查容器是否正在运行&lt;/strong>
&lt;/h4>&lt;p>首先，我们需要确认你的容器是否成功启动并且正在运行。&lt;/p>
&lt;p>运行这个命令，它会列出所有正在运行的容器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker ps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>你应该会看到类似这样的输出：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">a1b2c3d4e5f6 my-web-app:v1 &amp;#34;python app.py&amp;#34; 2 minutes ago Up 2 minutes 0.0.0.0:8888-&amp;gt;5000/tcp my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>请检查几件事：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>你的容器在列表里吗？&lt;/strong>
&lt;ul>
&lt;li>&lt;strong>如果不在&lt;/strong>，说明容器启动失败或者已经退出了。请运行 &lt;code>docker ps -a&lt;/code> (这会显示所有容器，包括已停止的)，找到你的容器 (&lt;code>my-first-container&lt;/code>)，然后查看它的 &lt;code>STATUS&lt;/code>。如果显示 &lt;code>Exited&lt;/code>，请直接跳到下面的 &lt;strong>第二步&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>&lt;code>STATUS&lt;/code> 是否是 &lt;code>Up&lt;/code>？&lt;/strong>
&lt;ul>
&lt;li>如果状态是 &lt;code>Up&lt;/code>，说明容器正在运行，这是个好消息。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>&lt;code>PORTS&lt;/code> 是否正确？&lt;/strong>
&lt;ul>
&lt;li>请仔细检查 &lt;code>PORTS&lt;/code> 这一列。它&lt;strong>必须显示&lt;/strong> &lt;code>0.0.0.0:8888-&amp;gt;5000/tcp&lt;/code> 或类似的映射关系。这表示主机的 8888 端口已成功映射到容器的 5000 端口。如果这里是空的或者不正确，说明你运行 &lt;code>docker run&lt;/code> 命令时 &lt;code>-p&lt;/code> 参数可能有误。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h4 id="第二步查看容器日志">&lt;strong>第二步：查看容器日志&lt;/strong>
&lt;/h4>&lt;p>如果容器没有运行 (状态是 &lt;code>Exited&lt;/code>)，或者虽然在运行但服务依然无法访问，那么最重要的一步就是查看日志。日志会告诉我们应用程序在容器内部发生了什么。&lt;/p>
&lt;p>运行以下命令来获取容器的日志：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker logs my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/images/index/index-12.png"
loading="lazy"
alt="报错日志"
>&lt;/p>
&lt;p>我这里看到这个错误，于是去&lt;a class="link" href="#from-werkzeugurls-import-url_quote-%e9%97%ae%e9%a2%98%e6%a0%b9%e6%ba%90" >from-werkzeugurls-import-url_quote-问题根源&lt;/a>&lt;/p>
&lt;p>&lt;strong>分析日志输出：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>情况A：看到错误信息 (Error)&lt;/strong>
&lt;ul>
&lt;li>如果你在日志中看到任何像 &lt;code>SyntaxError&lt;/code>, &lt;code>ModuleNotFoundError&lt;/code>, &lt;code>ImportError&lt;/code> 或其他 Python 错误堆栈信息，说明是你的 &lt;code>app.py&lt;/code> 代码有问题。请检查代码是否和教程中的完全一致。最常见的错误是 &lt;code>pip install&lt;/code> 失败或者代码缩进错误。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>情况B：看到 Flask 正常的启动信息&lt;/strong>
&lt;ul>
&lt;li>如果你看到类似下面的信息，说明 Flask 应用本身是正常启动的：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> * Serving Flask app &amp;#39;app&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * Running on http://0.0.0.0:5000
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * Press CTRL+C to quit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>如果日志正常，但你依然无法访问，问题很可能出在 &lt;strong>端口映射&lt;/strong> 或 &lt;strong>网络/防火墙&lt;/strong> 上。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h4 id="第三步常见问题和解决方案汇总">&lt;strong>第三步：常见问题和解决方案汇总&lt;/strong>
&lt;/h4>&lt;p>根据以上检查，这里是问题排查的清单：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>问题：容器启动后立即退出 (&lt;code>Exited&lt;/code>)。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>原因&lt;/strong>：应用代码有错误。&lt;/li>
&lt;li>&lt;strong>解决&lt;/strong>：运行 &lt;code>docker logs my-first-container&lt;/code> 查看具体错误，修改 &lt;code>app.py&lt;/code> 或 &lt;code>requirements.txt&lt;/code>。修改后，你需要&lt;strong>重新构建镜像&lt;/strong>并&lt;strong>重新运行容器&lt;/strong>。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 停止并删除旧容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm my-first-container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 重新构建镜像&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker build -t my-web-app:v1 .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 重新运行容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d -p 8888:5000 --name my-first-container my-web-app:v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>问题：容器在运行 (&lt;code>Up&lt;/code>)，但 &lt;code>docker ps&lt;/code> 的 &lt;code>PORTS&lt;/code> 列是空的。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>原因&lt;/strong>：运行容器时忘记了 &lt;code>-p 8888:5000&lt;/code> 参数。&lt;/li>
&lt;li>&lt;strong>解决&lt;/strong>：停止并删除现有容器，然后使用正确的 &lt;code>docker run&lt;/code> 命令重新运行。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop my-first-container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm my-first-container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d -p 8888:5000 --name my-first-container my-web-app:v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>问题：容器在运行，端口映射也正确，日志也正常，但就是无法访问。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>原因1&lt;/strong>：你电脑的 &lt;code>8888&lt;/code> 端口可能被其他程序占用了（比如另一个开发服务）。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>解决1&lt;/strong>：尝试换一个主机端口，比如 &lt;code>8889&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker stop my-first-container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm my-first-container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -d -p 8889:5000 --name my-first-container my-web-app:v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后访问 &lt;code>http://localhost:8889&lt;/code>。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>原因2&lt;/strong>：防火墙或安全软件。公司电脑或一些安全软件可能会阻止端口访问。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>解决2&lt;/strong>：暂时关闭防火墙或安全软件进行测试。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>原因3 (特别是 Windows 用户)&lt;/strong>：如果你在使用一些旧版本的 Docker 或者网络配置特殊的虚拟机，&lt;code>localhost&lt;/code> 可能无法直接映射。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>解决3&lt;/strong>：尝试使用 &lt;code>127.0.0.1:8888&lt;/code> 访问。如果还不行，可以找到 Docker 虚拟机的 IP 地址来访问。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>请按照这个顺序排查，99% 的问题都能通过检查 &lt;strong>容器状态&lt;/strong> 和 &lt;strong>容器日志&lt;/strong> 来解决。如果你能把 &lt;code>docker ps&lt;/code> 和 &lt;code>docker logs my-first-container&lt;/code> 的输出结果贴出来，我可以给你更精确的诊断。&lt;/p>
&lt;hr>
&lt;h4 id="from-werkzeugurls-import-url_quote-问题根源">from werkzeug.urls import url_quote 问题根源
&lt;/h4>&lt;p>你遇到的错误是 &lt;code>ImportError: cannot import name 'url_quote' from 'werkzeug.urls'&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>简单解释&lt;/strong>：你的应用程序（Flask）依赖于另一个名为 &lt;code>Werkzeug&lt;/code> 的底层库。你安装的 Flask 版本 (2.2.2) 需要从 &lt;code>Werkzeug&lt;/code> 库中导入一个叫做 &lt;code>url_quote&lt;/code> 的函数。然而，&lt;code>pip&lt;/code> 在安装依赖时，为你安装了一个&lt;strong>非常新&lt;/strong>的 &lt;code>Werkzeug&lt;/code> 版本（3.0 或更高版本），而在这个新版本中，&lt;code>url_quote&lt;/code> 这个函数已经被移除了，从而导致了 &lt;code>ImportError&lt;/code>（导入错误）。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>为什么会发生？&lt;/strong>：在你的 &lt;code>requirements.txt&lt;/code> 文件中，你只指定了 &lt;code>Flask==2.2.2&lt;/code>。&lt;code>pip&lt;/code> 在安装它时，会去自动安装 Flask 所需的依赖（比如 Werkzeug），并且通常会选择最新的可用版本。这就导致了 Flask (旧) 和 Werkzeug (新) 之间的不兼容。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="解决方案">解决方案
&lt;/h4>&lt;p>解决方法很简单：我们需要在 &lt;code>requirements.txt&lt;/code> 中明确指定一个与 &lt;code>Flask==2.2.2&lt;/code> 兼容的 &lt;code>Werkzeug&lt;/code> 版本。Werkzeug 2.x 版本是兼容的。&lt;/p>
&lt;p>请按照以下步骤操作：&lt;/p>
&lt;h5 id="第一步修改-requirementstxt">&lt;strong>第一步：修改 &lt;code>requirements.txt&lt;/code>&lt;/strong>
&lt;/h5>&lt;p>打开你的项目文件夹中的 &lt;code>requirements.txt&lt;/code> 文件，将其内容修改为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Flask==2.2.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Werkzeug==2.2.2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>为什么要这样做？&lt;/strong>
通过“锁定”或“固定”&lt;code>Werkzeug&lt;/code> 的版本，我们确保了每次构建 Docker 镜像时，安装的都是经过测试、相互兼容的库版本组合。这正是 Docker 实现环境一致性和可复现性的核心思想之一。&lt;/p>
&lt;hr>
&lt;h5 id="第二步停止并删除旧的容器">&lt;strong>第二步：停止并删除旧的容器&lt;/strong>
&lt;/h5>&lt;p>由于之前的容器是基于错误的镜像创建的，我们需要先清理它。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 停止正在运行的容器（如果它还在尝试运行的话）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stop my-first-container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 删除已停止的容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm my-first-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/images/index/index-13.png"
loading="lazy"
alt="删除容器"
>&lt;/p>
&lt;blockquote>
&lt;p>如果命令提示 &amp;ldquo;No such container&amp;rdquo;，说明容器已经停止并被移除了，这没关系，直接进行下一步即可。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h5 id="第三步重新构建-docker-镜像">&lt;strong>第三步：重新构建 Docker 镜像&lt;/strong>
&lt;/h5>&lt;p>由于我们修改了 &lt;code>requirements.txt&lt;/code>，镜像的构建指令已经发生了变化，所以必须重新构建镜像。Docker 会很智能地利用缓存，只重新执行发生变化的步骤（即 &lt;code>RUN pip install&lt;/code> 之后的步骤）。&lt;/p>
&lt;p>在你的项目文件夹中，运行 &lt;code>build&lt;/code> 命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker build -t my-web-app:v1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre>&lt;code>![重构Docker镜像](images/index/index-14.png)
&lt;/code>&lt;/pre>
&lt;hr>
&lt;h5 id="第四步重新运行容器">&lt;strong>第四步：重新运行容器&lt;/strong>
&lt;/h5>&lt;p>现在，使用新构建的、包含了正确库版本的镜像来启动容器。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d -p 8888:5000 --name my-first-container my-web-app:v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;img src="https://www.zata.cc/images/index/index-15.png"
loading="lazy"
alt="启动容器"
>&lt;/p>
&lt;h5 id="第五步再次验证">&lt;strong>第五步：再次验证&lt;/strong>
&lt;/h5>&lt;p>等待几秒钟让容器启动，然后再次访问 &lt;code>http://localhost:8888&lt;/code>。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/images/index/index-16.png"
loading="lazy"
alt="问题解决"
>&lt;/p>
&lt;p>这次，你应该就能成功看到 &amp;ldquo;Hello, Docker! This is a simple web service.&amp;rdquo; 的消息了。&lt;/p>
&lt;p>这个错误完美地展示了为什么在 &lt;code>requirements.txt&lt;/code> 中锁定依赖版本是如此重要，尤其是在使用 Docker 创建可复现、无意外的环境时。&lt;/p>
&lt;h3 id="wsl中镜像网络与docker网络冲突使用netsh-winsock-reset修改网络设置之后docker连不上----解决">wsl中镜像网络与docker网络冲突，使用netsh winsock reset修改网络设置之后，docker连不上 &amp;ndash; 解决
&lt;/h3>&lt;p>报错信息
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-11.png"
width="464"
height="181"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-11_hu16836429575141968351.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/index-11_hu11469463485972152702.png 1024w"
loading="lazy"
alt="alt text"
class="gallery-image"
data-flex-grow="256"
data-flex-basis="615px"
>&lt;/p>
&lt;p>解决方法，将wsl setting的网络调到默认的nat模式，然后重置网络
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image.png"
width="464"
height="181"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image_hu16836429575141968351.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image_hu11469463485972152702.png 1024w"
loading="lazy"
alt="wsl setting"
class="gallery-image"
data-flex-grow="256"
data-flex-basis="615px"
>
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1.png"
width="660"
height="620"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1_hu8898885243080334072.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-1_hu1052763918837491881.png 1024w"
loading="lazy"
alt="重置网络"
class="gallery-image"
data-flex-grow="106"
data-flex-basis="255px"
>&lt;/p>
&lt;p>然后就ok了&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2.png"
width="1113"
height="702"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2_hu13279574493942317322.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-2_hu6858458842162574348.png 1024w"
loading="lazy"
alt="网络ok"
class="gallery-image"
data-flex-grow="158"
data-flex-basis="380px"
>&lt;/p>
&lt;h3 id="具有ssl证书的后端在docker环境中出现问题">具有SSL证书的后端在docker环境中出现问题
&lt;/h3>&lt;p>首先，后端服务是放在dokcer容器中 0.0.0.0上的，然后看vscode的映射，最后记得改成https（第一次加载超级慢，而且可能连接失败）
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4.png"
width="3348"
height="504"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4_hu1734424590945960189.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-4_hu10606547435160311554.png 1024w"
loading="lazy"
alt="后端服务"
class="gallery-image"
data-flex-grow="664"
data-flex-basis="1594px"
>
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6.png"
width="3600"
height="780"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6_hu7078629779953246620.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-6_hu893367478247058634.png 1024w"
loading="lazy"
alt="vscode映射"
class="gallery-image"
data-flex-grow="461"
data-flex-basis="1107px"
>
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5.png"
width="5104"
height="2742"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5_hu5225827151997963938.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-5_hu1221934025227137583.png 1024w"
loading="lazy"
alt="在本机https访问"
class="gallery-image"
data-flex-grow="186"
data-flex-basis="446px"
>&lt;/p>
&lt;p>如果使用http就无法访问
&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8.png"
width="5104"
height="2744"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8_hu8223364434973822633.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-8_hu1243765635687842984.png 1024w"
loading="lazy"
alt="如果使用http就无法访问"
class="gallery-image"
data-flex-grow="186"
data-flex-basis="446px"
>&lt;/p>
&lt;p>尽量还是不要在容器里面调试https吧&lt;/p>
&lt;p>然后我试了127.0.0.1&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7.png"
width="5104"
height="2742"
srcset="https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7_hu9965555966883908243.png 480w, https://www.zata.cc/p/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/images/index/image-7_hu191126954655620681.png 1024w"
loading="lazy"
alt="同样的访问地址不可以"
class="gallery-image"
data-flex-grow="186"
data-flex-basis="446px"
>&lt;/p></description></item></channel></rss>