第三章 Docker基础:容器技术原理、Docker安装、Dockerfile编写、镜像构建与仓库管理
聊到云原生,Docker 是绕不开的第一道门槛。我记得刚接触容器时,心里就一个疑问:这玩意儿跟虚拟机到底有啥区别?后来亲手折腾了几次,才真正明白——容器不是模拟一整台电脑,而是把应用和它需要的环境打包在一起,共享宿主机的操作系统内核。
说白了,容器就是一个“轻量级的隔离沙箱”。它跑起来快,启动以毫秒计,占用的磁盘空间也小。我在项目中遇到过好几次,开发环境明明好好的,一上测试服务器就崩。后来全换成 Docker,这种“在我机器上能跑”的尴尬事就再没发生过。
3.1 容器技术原理
容器的核心秘密,藏在 Linux 内核的两个机制里:Namespace 和 Cgroups。
- 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,那是管理多容器应用的利器。