Docker 入门:镜像、容器与反向代理

问题从哪来

先交代一个常见场景。你写好的代码在本地能跑,换一台机器就报错。同一个项目,同事的电脑上是 Node 16 加 npm 8,你的电脑装的是 Node 22,依赖装不上,服务起不来。更麻烦的是,一个完整的应用往往不只有代码:它还要 Node、Redis、MySQL、Nginx,每个组件有各自的版本要求。

这些环境差异不是代码本身的问题,但会真实地挡住部署。Docker 要解决的,就是「我的电脑能跑,你的电脑怎么跑」这一类问题。它把应用和运行环境打包成一个整体,让这个整体在任何装了 Docker 的设备上都能以相同的方式启动。

Docker 的基本思路

Docker 的核心可以浓缩成一句话:

Docker = 应用 + 运行环境

这里的「运行环境」包含操作系统层面的依赖、语言运行时、第三方服务,以及它们之间的版本关系。打包之后得到的单位可以在不同机器之间搬运,启动结果一致。

理解 Docker 需要先分清两个概念:镜像(image)和容器(container)。

可以借用光盘和 DVD 的关系来理解:镜像像一张光盘,是静态、只读的模板,里面装好了应用和它需要的环境;容器像放进播放器的 DVD,是镜像运行起来的实例。一个镜像可以启动多个容器,容器之间相互隔离。

镜像通常不是从零制作,而是从仓库拉取,动作和 git pull 类似。官方和社区维护了大量现成镜像,比如 noderedisnginxmysql。需要用哪个,先拉取对应镜像,再启动成容器。

一个具体例子:Nginx 反向代理 Node 服务

概念说完,用一个例子串起「拉镜像、启动容器、端口映射、挂载配置」这一整套动作。

demo 目录下有一个 Node 服务,代码很短:

javascript 复制代码
const http = require('http')
const server = http.createServer((req, res) => {
  res.end('hello world')
})
server.listen(1314, '0.0.0.0', () => {
  console.log('server is running at http://0.0.0.0:1314')
})

它监听 1314 端口,收到请求就返回 hello world

再有一份 Nginx 配置:

ini 复制代码
events {}

http {
    server {
        listen 80;
        location / {
            proxy_pass http://host.docker.internal:1314;
            proxy_set_header Host $host;
        }
    }
}

Nginx 监听 80 端口,把到达的请求转发给 host.docker.internal:1314,也就是宿主机上那个 Node 服务。

这里涉及运维里一个常见概念:反向代理。用户访问 http://localhost:80,浏览器只知道自己在和 80 端口通信,并不知道后端实际跑在 1314 端口。Nginx 站在前端,接收 80 端口的请求,再按配置转发到后端。对用户来说,后端服务的真实端口被藏起来了;对后端来说,多了一层统一入口,可以承接高并发并做分发。

启动这个 Nginx 用一条 docker run

css 复制代码
docker run --name my-nginx-demo -p 80:80 \
  -v C:\Users\11293\Desktop\workspace\xt_ai\backend\docker\demo\nginx.conf:/etc/nginx/nginx.conf \
  -d nginx

这条命令里的参数各管一件事,逐个拆开:

  • --name my-nginx-demo:给容器起一个名字,方便后续用名字而不是 ID 去操作它。
  • -p 80:80:端口映射,左边是本机的 80 端口,右边是容器内的 80 端口。用户访问本机 80,被转给容器里的 80。
  • -v 本机路径:/etc/nginx/nginx.conf:挂载卷,把本机上的 nginx.conf 文件映射到容器内的配置路径。这样改本机文件,容器读到的配置就跟着变,不用进容器里去改。
  • -d nginx:以后台方式运行 nginx 这个镜像。-d 是 detached,容器在后台常驻,不占当前终端。

把整条链路串起来看:

rust 复制代码
用户浏览器 -> localhost:80 -> docker -p 端口映射 -> 容器内 80
  -> nginx 读 /etc/nginx/nginx.conf(由 -v 挂载)-> 反向代理 -> 1314 端口 Node 服务

用容器跑 MySQL

同样的思路可以用到数据库上。本机装 MySQL,版本和配置都可能和服务器不一致。改用容器,一条命令就能起一个指定版本的 MySQL:

css 复制代码
docker run -d --name mysql-demo -p 3307:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

几个参数的含义和前面对得上:

  • -p 3307:3306:把本机 3307 映射到容器的 3306。本机可能已经装了 MySQL 占用了 3306,所以换一个对外端口。
  • -e MYSQL_ROOT_PASSWORD=123456:设置环境变量,指定 root 密码。镜像启动脚本会读取这个变量完成初始化。
  • mysql:8.0:指定镜像和版本。不同项目对 MySQL 版本要求不同,这里可以按需选择。

容器起来之后,可以进入它的 Linux 终端:

bash 复制代码
docker exec -it mysql-demo /bin/bash
mysql -uroot -p123456

docker exec 在运行中的容器里执行命令,-it 打开一个交互式终端,/bin/bash 是进入 shell。进去之后再用 mysql 客户端连库。整个容器就是一个独立的小 Linux 环境,隔离了宿主机上已有的 MySQL。

常用命令

日常操作主要围绕镜像和容器两类:

makefile 复制代码
docker pull nginx        # 拉取镜像
docker run ...           # 运行镜像为容器
docker stop $(docker ps -q)   # 停止所有运行中的容器
docker rm $(docker ps -aq)    # 删除所有容器
docker rmi nginx         # 删除镜像

docker ps 列出运行中的容器,加 -a 列出包括已停止的;-q 只输出容器 ID。配合 $() 可以批量清理。镜像和容器是两层,删容器用 rm,删镜像用 rmi,先停再删的顺序不能乱。

收束

Docker 的价值落在两处:一是把「应用 + 运行环境」打包,消除不同机器之间的环境差异;二是把这种打包交给统一的命令(pullrunstop),让部署动作可重复、可描述。上面的 Nginx 和 MySQL 两个例子,覆盖了端口映射、配置挂载、环境变量、进入容器这几类最常见的使用方式。理解了这些,再去看更复杂的编排工具,就有了一条能对上的基线。

相关推荐
java_logo2 小时前
Docker 部署 openGauss:轻松搭建企业级开源关系型数据库平台
数据库·docker·开源·opengauss·轩辕镜像·opengauss部署教程·opengauss部署文档
HjhIron2 小时前
前端开发必会的Docker实战:从“我电脑能跑”到“轻松部署”
docker
嘟嘟07172 小时前
从浏览器到 hello world:Docker + nginx 反向代理 + Node 最小服务器一次讲清
docker·容器·node.js
阿黎梨梨3 小时前
Docker 容器化实战:从零搭建 Web 服务与反向代理
前端·后端·docker
小月土星3 小时前
当 LLM 遇上集装箱:我的 Docker 学习笔记与 AI 后端的思考(含反向代理Nginx)
后端·docker·容器
何时梦醒3 小时前
Docker 容器化入门:从「我电脑能跑」到「哪台机器都能跑」
后端·docker·面试
烬羽3 小时前
nginx 里写 localhost 反而 502?一条请求带你彻底搞懂 Docker 端口映射与反向代理
nginx·docker·程序员
jimidou3 小时前
从 PM2、Supervisor 到 Docker:老项目部署现代化实战(以 HOJ 评测机为例)
docker
小林ixn3 小时前
Docker + Nginx 反向代理:从“我电脑能跑”到“哪里都能跑”
nginx·docker·容器