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 两个例子,覆盖了端口映射、配置挂载、环境变量、进入容器这几类最常见的使用方式。理解了这些,再去看更复杂的编排工具,就有了一条能对上的基线。

相关推荐
_itgo40 分钟前
意图识别落地方案
运维·docker·容器
java_logo7 小时前
Docker 部署 ClickHouse Server:轻松搭建列式 OLAP 分析数据库平台
数据库·clickhouse·docker·私有化部署·olap·列式数据库·轩辕镜像
冰块的旅行7 小时前
完整的 Docker + Docker Compose 部署教程亲测有效
docker·容器
乌恩大侠7 小时前
【Sionna】docker build ubuntu cuda
ubuntu·docker·容器·spark·o-ru·ai-ran
FungLeo8 小时前
成为全栈·Node 后端篇·容器化:给 Node 应用写一个像样的 Dockerfile
docker·node.js·dockerfile·成为全栈·后端服务容器·docker 容器
梅梅绵绵冰9 小时前
Docker容器化平台
docker·容器·eureka
我是唐青枫9 小时前
Docker diff 详解:看清容器里到底改了什么
docker·容器
jiao糖瓜子11 小时前
Docker 完全指南:从镜像容器到生产部署
docker·容器·eureka·镜像·仓库
JavaPub-rodert12 小时前
Docker 容器启动了,不代表服务真的能用:Healthcheck 实战
运维·docker·容器
先吃饱再说13 小时前
别再手动敲三遍 docker run 了:学会使用 Docker Compose 多容器编排
docker·容器