2、Docker基础:Docker核心概念与第一个Dockerfile

好,咱们进入实战环节。说实话,很多新手一上来就对着Kubernetes猛学,结果发现连镜像怎么打包都搞不清楚。我个人习惯是,先把Docker玩明白,再上K8s会轻松很多。这一章,咱们就聊聊Docker最核心的三个东西:镜像、容器、仓库,再手把手写一个Dockerfile。

2.1 Docker核心概念:镜像、容器、仓库

这三个概念,说白了就是Docker世界的「三件套」。你想想看,没有镜像,容器跑什么?没有容器,镜像怎么用?没有仓库,镜像往哪存?

2.1.1 镜像(Image)

镜像是什么?镜像就是一个只读的模板。它包含了运行某个应用所需的一切:代码、运行时、系统工具、库、配置……

我打个比方:镜像就像你电脑上装的一个ISO系统安装盘。你拿着这个ISO,可以装出无数个一模一样的系统。镜像也一样,一个镜像可以启动无数个容器。

关键点:镜像是分层构建的。每一层都是只读的,修改时只会新增一层。这种设计让镜像复用变得非常高效。

我在项目中遇到过一个问题:团队里有人把镜像做得特别大,动不动几个G。后来发现,他每次构建都从零开始,没有利用缓存层。嗯,这里要注意,合理利用镜像分层,能大幅减少构建时间和存储空间

2.1.2 容器(Container)

容器是镜像的运行实例。你可以把容器理解成一个轻量级的、隔离的、可运行的进程

镜像和容器的关系,就像类和对象的关系。镜像是类,定义好了结构和行为;容器是对象,真正跑起来干活。

容器有几个特点:

  • 隔离性:每个容器有自己的文件系统、网络、进程空间
  • 可移植性:容器在任何安装了Docker的机器上都能跑
  • 快速启动:秒级甚至毫秒级启动,比虚拟机快得多

小技巧:容器停止后,数据默认不会丢失。但如果你删除了容器,里面的数据就没了。所以重要数据一定要用卷(Volume)挂载出来。

2.1.3 仓库(Registry)

仓库就是存放镜像的地方。最常用的是Docker Hub,还有私有的Harbor、AWS ECR等。

仓库的作用很简单:让你能拉取(pull)和推送(push)镜像。就像GitHub存代码一样,仓库存镜像。

概念 类比 特点
镜像 ISO安装盘 只读模板,分层构建
容器 运行中的虚拟机 隔离、可移植、快速启动
仓库 GitHub 存储和分发镜像

2.2 常用Docker命令

命令这东西,不用全记住,记住最常用的几个就行。我平时用得最多的就是pull、run、exec、build、push。咱们一个一个过。

2.2.1 docker pull:拉取镜像

从仓库拉取镜像到本地。比如拉一个Ubuntu镜像:

docker pull ubuntu:22.04

不指定标签的话,默认拉取latest。我个人习惯永远指定具体版本号,不然哪天latest变了,你的环境可能就炸了。

2.2.2 docker run:运行容器

这是最常用的命令。从镜像启动一个容器:

docker run -it --name my-ubuntu ubuntu:22.04 /bin/bash

参数说明:

  • -it:交互式终端,让你能进容器操作
  • --name:给容器起个名字
  • /bin/bash:容器启动后执行的命令

如果你想在后台运行,用-d参数:

docker run -d --name my-nginx -p 8080:80 nginx:latest

这里-p 8080:80是把宿主机的8080端口映射到容器的80端口。你访问宿主机IP:8080,就能看到Nginx页面了。

注意:我曾经遇到过一个问题:容器启动后马上退出。后来发现是前台进程没有保持住。容器必须有一个前台进程在运行,否则会立即退出。比如Nginx默认是后台守护进程,你需要用nginx -g 'daemon off;'来保持前台运行。

2.2.3 docker exec:进入容器

容器已经在后台跑了,你想进去看看日志或者调试一下,用exec:

docker exec -it my-nginx /bin/bash

这就进入了my-nginx容器内部。你可以像操作普通Linux一样操作它。

2.2.4 docker build:构建镜像

这是最核心的命令之一。通过Dockerfile构建镜像:

docker build -t my-app:v1.0 .

-t指定镜像名称和标签,最后的.表示Dockerfile所在的目录(当前目录)。

2.2.5 docker push:推送镜像

构建好的镜像,推送到仓库:

docker push my-app:v1.0

推送前需要先登录:docker login。如果是私有仓库,要指定仓库地址。

2.3 编写第一个Dockerfile

好,重头戏来了。咱们写一个最简单的Dockerfile,部署一个Python Flask应用。

先看项目结构:

my-flask-app/
├── app.py
├── requirements.txt
└── Dockerfile

app.py内容:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello, Docker!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

requirements.txt内容:

flask==2.3.0

现在,写Dockerfile:

# 使用官方Python镜像作为基础镜像
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY requirements.txt .

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY app.py .

# 暴露端口
EXPOSE 5000

# 启动命令
CMD ["python", "app.py"]

逐行解释一下:

  • FROM:指定基础镜像。我选python:3.9-slim,因为它小,只有100多M
  • WORKDIR:设置容器内的工作目录,后续命令都在这个目录下执行
  • COPY:从宿主机复制文件到镜像中
  • RUN:在构建过程中执行的命令,这里安装Python依赖
  • EXPOSE:声明容器运行时监听的端口,这只是文档作用,实际映射还要靠-p
  • CMD:容器启动时执行的命令

最佳实践:COPY requirements.txt .RUN pip install放在COPY app.py .前面。为什么?因为依赖文件不常变,而代码经常变。这样可以利用Docker的缓存层,每次只重新构建代码层,节省大量时间。

构建并运行:

docker build -t my-flask-app:v1.0 .
docker run -d -p 5000:5000 my-flask-app:v1.0

打开浏览器访问http://localhost:5000,就能看到「Hello, Docker!」了。

2.4 本章知识体系

为了让你更直观地理解本章内容,我画了一张图:

Docker核心概念与工作流程 仓库 (Registry) 存储和分发镜像 docker pull / push pull 镜像 (Image) 只读模板 docker build run 容器 (Container) 运行实例 docker exec Dockerfile → build push Dockerfile 定义镜像构建步骤 docker build

这张图清晰地展示了Docker的工作流程:从Dockerfile构建镜像,从仓库拉取/推送镜像,从镜像运行容器。三者环环相扣,缺一不可。

2.5 避坑指南

最后,分享几个我踩过的坑:

  • 镜像太大:尽量用-slim-alpine版本的基础镜像,能小很多
  • 容器退出:确保你的应用是前台运行的,不然容器秒退
  • 缓存失效:把不常变的层放在Dockerfile前面,充分利用缓存
  • 端口冲突:运行容器前,检查宿主机端口是否被占用

我的习惯:每次构建镜像前,先跑一遍docker system prune清理无用的镜像和容器,保持环境干净。不然磁盘很快就满了。

好了,这一章的内容就到这里。Docker基础打牢了,后面上Kubernetes才会顺手。记住:镜像、容器、仓库这三个概念,以及pull、run、exec、build、push这五个命令,足够你应付大部分场景了。


专注资料整理