第三章 Docker基础:容器技术原理、Docker安装、Dockerfile编写、镜像构建与仓库管理

聊到云原生,Docker 是绕不开的第一道门槛。我记得刚接触容器时,心里就一个疑问:这玩意儿跟虚拟机到底有啥区别?后来亲手折腾了几次,才真正明白——容器不是模拟一整台电脑,而是把应用和它需要的环境打包在一起,共享宿主机的操作系统内核。

说白了,容器就是一个“轻量级的隔离沙箱”。它跑起来快,启动以毫秒计,占用的磁盘空间也小。我在项目中遇到过好几次,开发环境明明好好的,一上测试服务器就崩。后来全换成 Docker,这种“在我机器上能跑”的尴尬事就再没发生过。

3.1 容器技术原理

容器的核心秘密,藏在 Linux 内核的两个机制里:NamespaceCgroups

  • Namespace:负责“隔离”。它让每个容器都觉得自己是独立的,有自己的进程树、网络栈、文件系统。实际上它们都跑在同一个宿主机上。
  • Cgroups:负责“限制”。它能控制容器能用多少 CPU、多少内存。防止某个容器把整台机器吃垮。

我个人的理解是:Namespace 是“障眼法”,让进程以为自己是老大;Cgroups 是“紧箍咒”,不让它太放肆。

关键区别:容器 vs 虚拟机

  • 虚拟机:每个 VM 包含完整操作系统,占用 GB 级空间,启动分钟级。
  • 容器:共享宿主机内核,占用 MB 级空间,启动毫秒级。

3.2 Docker 安装

安装 Docker 其实很简单。我习惯在 Linux 上搞,CentOS 和 Ubuntu 都行。这里以 Ubuntu 为例:

# 更新包索引
sudo apt-get update

# 安装依赖
sudo apt-get install \
    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 Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

# 启动 Docker
sudo systemctl start docker
sudo systemctl enable docker

# 验证安装
sudo docker run hello-world

安装完后,记得把当前用户加到 docker 组,不然每次都要 sudo:

sudo usermod -aG docker $USER
newgrp docker

小提示:Windows 和 Mac 用户直接下载 Docker Desktop 就行。但我个人建议,生产环境还是用 Linux 服务器,稳当。

3.3 Dockerfile 编写

Dockerfile 就是构建镜像的“配方”。你写清楚要什么基础镜像、装什么软件、复制什么文件、跑什么命令,Docker 就按步骤给你打包好。

来看一个简单的 Node.js 应用 Dockerfile:

# 指定基础镜像
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm install --production

# 复制源代码
COPY . .

# 暴露端口
EXPOSE 3000

# 启动命令
CMD ["node", "server.js"]

这里有几个坑,我踩过:

  • 分层缓存:把变化少的指令放前面(比如 COPY package.json),变化多的放后面(比如 COPY .)。这样能利用缓存,加快构建速度。
  • 镜像大小:尽量用 alpine 版本的基础镜像,能小好几倍。我见过有人用 ubuntu 跑个 hello world,镜像 1GB,其实 alpine 才几十 MB。
  • 多阶段构建:编译和运行分开。编译阶段用大镜像,运行阶段只复制编译产物到小镜像。

多阶段构建示例:

# 第一阶段:编译
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# 第二阶段:运行
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]

3.4 镜像构建与仓库管理

写好 Dockerfile,构建镜像就一行命令:

docker build -t myapp:v1.0 .

-t 是给镜像打标签,格式是 名称:版本。我习惯用语义化版本号,比如 v1.0.0、v2.1.3。

构建完的镜像,可以推到仓库里。仓库分两种:

  • 公共仓库:Docker Hub、GitHub Container Registry、阿里云容器镜像服务。
  • 私有仓库:自己搭的 Harbor、Nexus,或者云厂商的私有镜像仓库。

推镜像到 Docker Hub 的步骤:

# 登录
docker login

# 给镜像打上仓库地址的标签
docker tag myapp:v1.0 username/myapp:v1.0

# 推送
docker push username/myapp:v1.0

注意:千万不要把敏感信息(密码、密钥)写进 Dockerfile 或镜像里。我曾经见过有人把数据库密码直接写在 Dockerfile 里,然后推到公共仓库……后果你懂的。

拉取镜像也很简单:

docker pull username/myapp:v1.0

查看本地镜像列表:

docker images

删除镜像:

docker rmi myapp:v1.0

3.5 常用 Docker 命令速查

命令 作用
docker ps 查看运行中的容器
docker ps -a 查看所有容器(包括已停止的)
docker stop <容器ID> 停止容器
docker rm <容器ID> 删除容器
docker logs <容器ID> 查看容器日志
docker exec -it <容器ID> /bin/sh 进入容器内部

避坑指南:我曾经在线上环境直接 docker rm -f 删了一个正在跑业务的容器,结果数据全丢了。后来学乖了,删容器前先确认有没有挂载卷,数据有没有备份。

嗯,Docker 基础就这些。说白了,它就是把你的应用和依赖打包成一个“集装箱”,走到哪都能跑。下一章我们会聊 Docker Compose,那是管理多容器应用的利器。