1. 容器化基础:Docker核心概念与实战入门
各位同学,咱们开始第一节课。说实话,容器化这个概念在游戏后端圈子里,这几年已经快成标配了。我最早接触Docker是在2016年,那时候团队还在用虚拟机部署,每次扩容都要等十几分钟。后来切到容器,秒级启动的感觉,真香。
这一章,咱们把Docker最核心的三个东西讲透:镜像、容器、仓库。再加上Docker的安装配置和Dockerfile怎么写。别急,一步步来。
1.1 Docker是什么?为什么游戏服务器需要它?
Docker说白了就是一个轻量级的虚拟化方案。它不像虚拟机那样要模拟整个操作系统,而是直接跑在宿主机内核上,共享内核但隔离进程。
你想想看,游戏服务器最头疼的是什么?环境不一致。开发环境跑得好好的,一上测试就崩,到了生产环境直接炸。Docker把应用和它的依赖打包成一个镜像,走到哪跑到哪,环境问题一次性解决。
核心优势:
- 秒级启动,比虚拟机快一个数量级
- 资源利用率高,一台物理机可以跑几十个容器
- 环境一致性,开发、测试、生产完全一样
- 易于扩展,配合K8s可以实现自动扩缩容
1.2 三大核心概念:镜像、容器、仓库
1.2.1 镜像(Image)
镜像是什么?你可以把它理解成一个只读的模板。它包含了运行应用所需的一切:代码、运行时、系统工具、库文件、环境变量、配置文件。
我习惯把镜像比作一个"光盘"。你刻录好之后,内容就不能改了。每次修改代码,都要重新"刻录"一个新的镜像版本。
镜像采用分层结构,每一层都是只读的。这样做的好处是:多个镜像可以共享底层的基础层,节省磁盘空间。比如你的游戏服务器镜像和Web服务器镜像,底层可能都是同一个Ubuntu基础镜像。
小技巧:查看本地镜像列表用 docker images,查看镜像的构建历史用 docker history 镜像名。我曾经靠这个命令排查过一个镜像体积过大的问题,发现是某层不小心装了一堆调试工具。
1.2.2 容器(Container)
容器是镜像的运行实例。镜像启动后,在最上层加一个可写层,就变成了容器。你可以对容器进行启动、停止、删除等操作。
嗯,这里要注意:容器是有状态的。你在容器里写的数据,容器删除后就没了。所以游戏服务器的日志、数据库数据,一定要挂载到宿主机或者使用数据卷。
我记得有一次线上事故,同事把游戏日志直接写在容器里,结果容器被调度到另一台机器上,日志全丢了。从那以后,我们团队定了个规矩:所有持久化数据必须走挂载。
避坑指南:我曾经见过有人用 docker commit 来保存容器状态,然后当成新镜像用。千万别这么干!这会导致镜像层数爆炸,而且不可复现。正确的做法是用Dockerfile构建镜像。
1.2.3 仓库(Registry)
仓库就是存放镜像的地方。有公共的Docker Hub,也有私有的Harbor、Nexus等。游戏公司一般都会搭建私有仓库,毕竟代码和配置不能随便公开。
我建议团队至少搭建一个私有仓库。原因很简单:
- 拉取速度快,内网传输比公网快得多
- 安全性高,镜像不会泄露
- 可以控制版本,避免意外使用错误版本
镜像的命名规则是:仓库地址/命名空间/镜像名:标签。比如 registry.game.com/game-server/match-service:v1.2.3。
1.3 Docker安装与配置
安装Docker其实很简单,但不同操作系统略有差异。我主要讲Linux环境,因为游戏服务器99%跑在Linux上。
1.3.1 在Ubuntu上安装
# 更新包索引
sudo apt-get update
# 安装依赖
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 设置稳定版仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装Docker引擎
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# 启动Docker
sudo systemctl start docker
sudo systemctl enable docker
# 验证安装
sudo docker run hello-world
1.3.2 配置镜像加速
国内拉取Docker Hub镜像经常超时,我建议配置镜像加速器。编辑 /etc/docker/daemon.json:
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2"
}
个人经验:日志配置很重要。默认情况下Docker日志会无限增长,我曾经见过一个容器日志占满磁盘导致服务宕机。所以一定要限制日志大小和文件数量。
1.3.3 非root用户运行Docker
默认情况下Docker需要root权限。为了方便开发,可以把当前用户加入docker组:
sudo usermod -aG docker $USER
newgrp docker
注意:这样做有安全风险,生产环境不建议这么搞。但在开发环境,能省去每次敲sudo的麻烦。
1.4 Dockerfile编写规范
Dockerfile是构建镜像的"配方"。写得好不好,直接影响镜像大小、构建速度和安全性。我见过太多人把Dockerfile写成"屎山",一个镜像几个G,构建一次半小时。
1.4.1 基础结构
一个标准的Dockerfile长这样:
# 指定基础镜像
FROM ubuntu:20.04
# 设置维护者信息
LABEL maintainer="yourname@game.com"
# 设置环境变量
ENV APP_HOME /app
ENV TZ Asia/Shanghai
# 设置工作目录
WORKDIR $APP_HOME
# 安装依赖
RUN apt-get update && apt-get install -y \
libssl-dev \
libcurl4-openssl-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制代码
COPY ./bin/game-server .
# 暴露端口
EXPOSE 8080
# 设置启动命令
CMD ["./game-server"]
1.4.2 编写规范与最佳实践
我总结了几条铁律,照着做基本不会踩坑:
| 规范 | 说明 | 为什么 |
|---|---|---|
| 选择合适的基础镜像 | 优先用alpine或slim版本 | 基础镜像越小,攻击面越小,构建越快 |
| 合并RUN命令 | 用 && 连接多个命令 | 减少镜像层数,每层都会增加体积 |
| 清理缓存 | 安装完依赖后删除apt缓存 | 避免缓存留在镜像中,减小体积 |
| 使用.dockerignore | 排除不需要的文件 | 避免把本地调试文件、git目录等打包进镜像 |
| 不要用latest标签 | 指定具体版本号 | latest会变化,导致构建结果不可复现 |
多阶段构建示例:这是我最常用的技巧,特别适合Go、C++等编译型语言。
# 第一阶段:编译
FROM golang:1.20 AS builder
WORKDIR /build
COPY . .
RUN go build -o game-server .
# 第二阶段:运行
FROM alpine:3.18
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /build/game-server /app/
EXPOSE 8080
CMD ["/app/game-server"]
你看,最终镜像只有十几兆,比直接基于golang镜像小了10倍。为什么?因为运行时不需要Go的编译工具链,只需要编译好的二进制文件。
1.4.3 常见错误与避坑
我刚开始写Dockerfile时也犯过不少错,分享几个典型的:
- 把配置文件写死在镜像里:不同环境(开发、测试、生产)的配置不同,应该通过环境变量或挂载卷注入
- 用root用户运行容器:安全风险极大,应该创建专用用户
- 不设置WORKDIR:默认工作目录是根目录,容易造成文件混乱
- 暴露不必要的端口:只暴露应用需要的端口,减少攻击面
曾经踩过的坑:有一次我把数据库密码直接写在Dockerfile里,然后推到了公共仓库。虽然很快就删了,但镜像已经被拉取了几百次。从那以后,所有敏感信息都用环境变量或者Secret管理。
1.5 本章小结
这一章我们讲了Docker的三大核心概念:镜像(只读模板)、容器(运行实例)、仓库(镜像存储)。然后手把手装了Docker,配置了镜像加速和日志限制。最后讲了Dockerfile的编写规范和最佳实践。
说实话,这些内容看起来简单,但真正用好需要大量实践。我建议你动手写几个Dockerfile,把公司的游戏服务器容器化试试。下一章我们会讲Docker Compose,到时候就能编排多个容器了。
记住一句话:容器化不是目的,稳定高效的交付才是。咱们下节课见。