Docker 容器化入门:从「我电脑能跑」到「哪台机器都能跑」

Docker 容器化入门:从「我电脑能跑」到「哪台机器都能跑」

本文是一篇 Docker 学习日志。用一个「Nginx 反向代理 Node 服务」的最小 demo,把 Docker 最核心的概念------**镜像(image)、容器(container)、端口映射(-p)、卷挂载(-v)**一次性讲透。文末还附上「用 Docker 跑 MySQL」和运维必考的「正向代理 / 反向代理」知识点。

目录

  • [一、为什么需要 Docker](#一、为什么需要 Docker "#%E4%B8%80%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81docker")
  • [二、Docker 是什么:一句话类比](#二、Docker 是什么:一句话类比 "#%E4%BA%8Cdocker%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%80%E5%8F%A5%E8%AF%9D%E7%B1%BB%E6%AF%94")
  • [三、两个核心概念:image 与 container](#三、两个核心概念:image 与 container "#%E4%B8%89%E4%B8%A4%E4%B8%AA%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5image-%E4%B8%8E-container")
  • 四、环境准备
  • [五、实战:Nginx 反向代理 Node 服务](#五、实战:Nginx 反向代理 Node 服务 "#%E4%BA%94%E5%AE%9E%E6%88%98nginx-%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86-node-%E6%9C%8D%E5%8A%A1")
  • [六、端口映射 -p 详解](#六、端口映射 -p 详解 "#%E5%85%AD%E7%AB%AF%E5%8F%A3%E6%98%A0%E5%B0%84--p-%E8%AF%A6%E8%A7%A3")
  • [七、卷挂载 -v 详解](#七、卷挂载 -v 详解 "#%E4%B8%83%E5%8D%B7%E6%8C%82%E8%BD%BD--v-%E8%AF%A6%E8%A7%A3")
  • 八、常用命令速查
  • [九、附:用 Docker 跑一个 MySQL](#九、附:用 Docker 跑一个 MySQL "#%E4%B9%9D%E9%99%84%E7%94%A8-docker-%E8%B7%91%E4%B8%80%E4%B8%AA-mysql")
  • [十、运维考点:正向代理 vs 反向代理](#十、运维考点:正向代理 vs 反向代理 "#%E5%8D%81%E8%BF%90%E7%BB%B4%E8%80%83%E7%82%B9%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86-vs-%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86")
  • 十一、总结

一、为什么需要 Docker

每个开发者大概都遇到过这样的对话:

甲:「我电脑上跑得好好的呀。」 乙:「那为什么我 clone 下来跑不起来?」

这就是**「我电脑能跑,你电脑怎么跑」问题的本质。一个 Web 项目,除了你的代码本身,还依赖一大坨有版本要求的运行环境**:

  • Node(还要对版本,比如 Node 16 + npm 8
  • Redis、MySQL 这类中间件
  • Next.js / React 这类框架
  • 各种系统级依赖

举一个真实的例子 🌰:

你到公司接手了一个 N 年前的 Vue2 老项目,要求 Node 16 + npm 8,但你自己电脑装的是 Node 22。装回老版本又怕影响别的项目,来回切换环境,折腾一下午项目还没跑起来。

Docker 干的事情,就是把这「代码 + 运行环境」整体打包成一个集装箱,在任何一台装了 Docker 的机器上,一条命令就能跑起来,和宿主机环境完全隔离、互不干扰。


二、Docker 是什么:一句话类比

先给一个非常直观的类比:

ini 复制代码
Agent  = LLM + Harness(tool + mcp + rag + skill + ...)
Docker = 应用 + 运行环境
  • Agent 是把「大模型」和「工具/知识/技能」这些能力打包在一起,成为一个能干活的东西;
  • Docker 是把「应用程序」和「它需要的运行环境」打包在一起,成为一个能运行的东西。

再往生活里想:Docker 就像海运的万吨巨轮,用标准化的**集装箱(container)**装货,不管里面装的是衣服还是家具,码头(宿主机)都按统一标准吊装。开发者就是那个「货主」,你只负责把货(应用 + 环境)装箱,剩下的运输(部署)交给 Docker。


三、两个核心概念:image 与 container

这是新手最容易混淆的一对概念,用「光盘」和「DVD 播放」来理解就通了:

概念 类比 说明
image(镜像) 光盘 只读的「应用程序 + 环境」模板,安装包一样的存在
container(容器) 光盘放进 DVD 播放 image 运行起来后的实例,可读可写,运行态

几个关键关系:

  1. image 是静态的模板 ,container 是 image 的运行实例
  2. 一个 image 可以同时启动成多个 container,彼此隔离;
  3. 就像「从 git 仓库 pull 代码到本地」,你也可以 docker pull 一个镜像,再 docker run 它。

记住一句话:image 是「光盘」,container 是「正在播放的光盘」


四、环境准备

Docker 官网 下载 Docker Desktop 安装即可(Windows / macOS 一键安装,Linux 走包管理器)。

装好后验证:

bash 复制代码
docker --version
# Docker version 24.0.7, build ...
docker run hello-world
# 打印出一段欢迎信息,说明 Docker 正常工作

五、实战:Nginx 反向代理 Node 服务

下面这个 demo 要演示一件很「运维」的事:用户访问 http://localhost(80 端口),由 Nginx 代理转发给一个跑在 1234 端口上的 Node 服务

5.1 先写一个极简 Node 服务

demo/index.js

javascript 复制代码
// node 早期是 commonjs 规范,这里用了 ESM
import { createServer } from 'http';

const server = createServer((req, res) => {
  res.end('hello world');
});

server.listen(1234, '0.0.0.0', () => {
  console.log('server is running at http://0.0.0.0:1234');
});

注意 0.0.0.0:它表示「监听所有网卡」,这样容器外部(宿主机)才能访问到这个端口。

demo/package.json(重点是 "type": "module",让 Node 识别 import 语法):

json 复制代码
{
  "name": "demo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "type": "module"
}

5.2 写 Nginx 反向代理配置

demo/nginx.conf

nginx 复制代码
# nginx.conf
events {}

http {
    server {
        listen 80;                       # nginx 监听 80 端口
        location / {
            # 把请求反向代理到宿主机的 1234 端口(也就是我们的 Node 服务)
            proxy_pass http://host.docker.internal:1234;
            proxy_set_header Host $host;
        }
    }
}

host.docker.internal 是 Docker 提供的特殊主机名,指向宿主机,用来让容器访问宿主机的服务。

5.3 一条命令启动 Nginx 容器

bash 复制代码
docker run \
  --name my-nginx-demo \
  -p 80:80 \
  -v D:/workspace/zj-ai/backend/docker/demo/nginx.conf:/etc/nginx/nginx.conf \
  -d nginx

先在本机把 Node 服务跑起来(node demo/index.js),再执行上面命令。然后在浏览器打开 http://localhost,就能看到 hello world ------ 你的请求经历了一段完整的旅程:

csharp 复制代码
浏览器输入 http://localhost
  → 宿主机 80 端口(-p 映射到容器 80)
  → 容器内 nginx 监听 80
  → nginx.conf 把请求 proxy_pass 到 host.docker.internal:1234
  → 宿主机 1234 端口的 Node 服务
  → 返回 hello world

下面把这条命令里的每个参数拆开讲。


六、端口映射 -p 详解

bash 复制代码
-p 80:80
#    │  └── 容器内部端口(container 的 80,nginx 的监听端口)
#    └───── 宿主机端口(本机的 80)

规则:-p 宿主机端口:容器端口

  • 用户浏览器访问 http://localhost:80(浏览器默认就是 80,可以省略端口号);
  • Docker 把宿主机 80 端口的请求,转发给容器内部的 80 端口;
  • 容器内的 nginx 监听的正是 80 端口。

为什么要做这一层映射?因为容器有自己独立的网络命名空间 ,它内部的端口默认对外是不可见的。-p 就是把「宿主机端口」和「容器端口」搭一座桥。

常见用法:宿主机 8080 映射容器 80,写 -p 8080:80,访问 http://localhost:8080


七、卷挂载 -v 详解

bash 复制代码
-v D:/workspace/zj-ai/backend/docker/demo/nginx.conf:/etc/nginx/nginx.conf
#  │ 宿主机文件路径                                              │ 容器内路径
#  └────────────────────── 冒号分隔,左边宿主机,右边容器 ──────┘

规则:-v 宿主机路径:容器路径

它的作用是:把宿主机上的一个文件或目录,映射到容器内部的指定路径

  • 这里我们把本机的 nginx.conf 挂载到容器内的 /etc/nginx/nginx.conf
  • 好处:改配置不用重新打包镜像,直接改本机文件、重启容器即可生效;
  • 数据卷(volume)也是持久化数据的关键手段------容器删了,挂载的数据还在宿主机上。

想象成「共享文件夹」:你在宿主机里改文件,容器里立刻就能看到,两边其实是同一份数据。


八、常用命令速查

bash 复制代码
# 拉取镜像
docker pull nginx
docker pull mysql:8.0

# 运行镜像为容器(-d 后台运行)
docker run -d --name xxx -p 80:80 nginx

# 查看正在运行的容器
docker ps

# 停止 / 删除容器(常用组合拳:先停后删)
docker stop $(docker ps -q)      # 停止所有运行中的容器
docker rm   $(docker ps -aq)     # 删除所有容器

# 删除镜像
docker rmi nginx

# 进入容器内部的 Linux 终端
docker exec -it xxx /bin/bash

参数速记

参数 作用
--name 给容器起名字
-d 后台运行(detached)
-p 端口映射
-v 卷挂载
-e 设置环境变量
-it 交互式终端

九、附:用 Docker 跑一个 MySQL

不用在本地装 MySQL,一条命令搞定:

bash 复制代码
# 拉取 MySQL 8.0 镜像(版本号要写清楚,不同版本行为不一样)
docker pull mysql:8.0

# 运行:宿主机 3307 映射容器 3306,并设置 root 密码
docker run -d \
  --name mysql-demo \
  -p 3307:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:8.0
  • -p 3307:3306:宿主机 3307 → 容器 3306(因为本地可能已经装了 MySQL 占了 3306,所以映射到 3307);
  • -e MYSQL_ROOT_PASSWORD=123456:通过环境变量给容器传 root 密码。

进入容器操作数据库:

bash 复制代码
docker exec -it mysql-demo /bin/bash   # 进入容器 Linux 终端
mysql -uroot -p                         # 输入密码 123456 登录 MySQL

这就是 Docker 最爽的地方:装一个 MySQL 只要两条命令,不想用了 docker rm 一键删干净,不污染宿主机环境


十、运维考点:正向代理 vs 反向代理

这是运维、后端面试的高频考点,用「用户上网」这条链路一次讲清。

10.1 正向代理(Forward Proxy)

代理客户端。用户知道自己要访问目标网站,但通过一个「中间人」去访问。

复制代码
用户 → 正向代理服务器 → 目标网站

典型场景:翻墙 / 公司内网代理。客户端明确把请求发给代理,代理替它去请求真正的服务器。

10.2 反向代理(Reverse Proxy)

代理服务端。用户以为自己在直接访问网站,其实背后是一台代理服务器在帮忙转发、负载均衡。

scss 复制代码
用户 → (以为是目标) → 反向代理(nginx) → 真正的后端服务(1234 端口)

典型场景:我们上面的 demo 就是反向代理------用户只看到 localhost:80,根本不知道后端其实跑在 1234 端口

一句话区分:

  • 正向代理:客户端知道代理,服务器不知道真实客户端;
  • 反向代理:客户端不知道真实服务器,服务器知道「我背后是谁」。

反向代理的价值:隐藏后端真实地址、负载均衡、统一入口(SSL、限流、缓存),所以 Nginx 在大厂架构里几乎是标配。


十一、总结

这篇文章我们用一个小 demo,把 Docker 最核心的东西串了起来:

  1. 为什么用:解决「我电脑能跑,你电脑跑不了」的环境不一致问题;
  2. 核心概念:image 是「光盘」(只读模板),container 是「播放中的光盘」(运行实例);
  3. 两条关键命令参数-p 端口映射(打通宿主机和容器),-v 卷挂载(共享文件 / 持久化数据);
  4. 一个实战Nginx 反向代理 Node,理解了「正向代理」和「反向代理」的本质区别。

Docker 的下一步是 Dockerfile (把应用也打包进镜像)和 docker-compose(一次编排多个容器),有了本文的基础,上手会非常顺。

一句话记住 Docker 的定位Docker = 应用 + 运行环境,就像 Agent = LLM + Harness,都是「打包起来、随处可用」的思想。


相关推荐
颜进强1 小时前
11 - 从需求拆解到 OpenSpec:为什么不要直接敲 /opsx:explore
前端·后端·ai编程
禁止摆烂_才浅1 小时前
HTML 高频面试题
前端·面试·html
foggyprojects1 小时前
AI 说销售额下降了,哪些客户拖累了结果?
后端
用户852495071841 小时前
NestJS 架构实战:给后端代码请来一位“项目经理
后端
唐青枫1 小时前
一个点号省掉一堆类型:Zig .{} 语法、类型推导与实战
后端
烬羽1 小时前
nginx 里写 localhost 反而 502?一条请求带你彻底搞懂 Docker 端口映射与反向代理
nginx·docker·程序员
月才1 小时前
告别System.out.println,打造专业日志系统
后端
jimidou1 小时前
从 PM2、Supervisor 到 Docker:老项目部署现代化实战(以 HOJ 评测机为例)
docker
无糖可可果1 小时前
从零理解 Docker:为什么我的代码在你的电脑上跑不起来?
javascript·后端