Docker
Docker 完全入门指南:从核心概念到实战演练
在现代软件开发中,你一定听过那句经典的“魔咒”:“在我电脑上明明是好的啊!” Docker 的诞生,就是为了彻底终结这个魔咒。它不仅仅是一个工具,更是一种变革软件开发、测试和部署流程的哲学。
🚀 Docker 解决了什么?
想象一下,你把你的应用程序(代码、依赖库、配置文件、运行时环境)全部打包进一个标准化的、轻量级的“集装箱”里。这个集装箱可以在任何安装了 Docker 的机器上(你的电脑、同事的电脑、测试服务器、云服务器)被完美地、一致地运行,无需关心底层操作系统的差异。
这就是 Docker 的核心价值:构建一次,随处运行 (Build Once, Run Anywhere)。
核心概念:理解 Docker 的世界观
在敲下第一行命令前,理解 Docker 的几个核心概念至关重要。
概念 | 通俗比喻 | 技术解释 |
---|---|---|
📜 镜像 (Image) |
房屋的建筑蓝图 | 一个只读的模板,包含了运行应用程序所需的一切:代码、运行时、库、环境变量和配置文件。镜像是分层的,每一层都是对前一层的增量修改。 |
🏠 容器 (Container) |
根据蓝图建造的真实房屋 | 镜像的运行实例。容器是活动的、可读写的。你可以创建、启动、停止、移动或删除容器。每个容器都与其他容器和宿主机相互隔离。 |
📄 Dockerfile |
一张详细的建筑施工说明书 | 一个文本文档,包含了一系列指令,用于指导 Docker 如何从头开始自动构建一个镜像。每一条指令都会在镜像中创建一个新的层。 |
📦 仓库 (Repository) |
存放各种蓝图的档案馆 | 集中存放镜像文件的地方。最著名的公共仓库是 Docker Hub。你也可以搭建自己的私有仓库。 |
💾 卷 (Volume) |
房屋旁边独立的地基/仓库 | 一种由 Docker 管理的、用于持久化容器数据的机制。它独立于容器的生命周期,即使容器被删除,卷中的数据依然存在。 |
核心关系:我们使用 Dockerfile
来定义如何构建一个 镜像
,然后从这个 镜像
运行一个或多个 容器
。
安装 Docker (Ubuntu 24.04)
安装过程非常直接。
# 更新包列表
sudo apt update
# 安装需要的软件包以使apt能够通过HTTPS使用仓库
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加Docker官方的GPG密钥
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/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://mirrors.tuna.tsinghua.edu.cn/docker-ce/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 docker-ce docker-ce-cli containerd.io
# (可选)安装nvidia-container-toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
# 设置Docker开机自启动并重启Docker服务
sudo systemctl enable docker
sudo systemctl restart docker
# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 查看Docker是否已启动
sudo systemctl status docker
# (推荐) 将当前用户添加到 docker 组,避免每次都输入 sudo
# 执行后需要重新登录或新开终端才能生效
sudo usermod -aG docker ${USER}
newgrp docker # 立即应用组成员资格
Docker 实战之旅:从入门到精通
让我们通过一系列循序渐进的实战来掌握 Docker。
第一站:运行你的第一个容器
最简单的开始,就是运行一个官方的 hello-world
镜像。
docker run hello-world
当你执行这条命令时,Docker 在幕后做了什么?
- 检查本地:Docker 检查本地是否有
hello-world
这个镜像。 - 拉取镜像:发现本地没有,于是从 Docker Hub 仓库拉取 (pull) 该镜像。
- 创建容器:使用拉取到的镜像创建一个新的容器。
- 运行容器:执行容器内的默认命令,打印出欢迎信息。
- 停止容器:命令执行完毕,容器自动停止。
来点更有趣的:让我们运行一个 Nginx Web 服务器。
docker run -d -p 8080:80 --name my-nginx nginx:latest
命令解析:
docker run
: 运行一个容器。-d
(--detach
): 后台运行。容器将在后台启动并持续运行。-p 8080:80
(--publish
): 端口映射。将主机的8080
端口映射到容器的80
端口。这样,我们访问主机的 8080 端口,流量就会被转发到容器的 80 端口,也就是 Nginx 监听的端口。--name my-nginx
: 给我们的容器起一个友好的名字,方便管理。nginx:latest
: 指定要使用的镜像名称和标签(版本)。
现在,打开你的浏览器,访问 http://localhost:8080
,你将看到 Nginx 的欢迎页面!
第二站:管理你的容器
容器已经运行起来了,我们如何管理它?
# 查看正在运行的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 停止容器
docker stop my-nginx
# 再次启动已停止的容器
docker start my-nginx
# 查看容器的日志
docker logs my-nginx
# 进入正在运行的容器内部(非常有用!)
docker exec -it my-nginx bash
# 删除一个已停止的容器
docker rm my-nginx
# 强制删除一个正在运行的容器
docker rm -f my-nginx
# 清理所有已停止的容器(慎用)
docker container prune -f
第三站:构建你自己的镜像 (Dockerfile)
这是从 Docker 用户到 Docker 玩家的进阶之路。我们将为一个小型的 Python Flask 应用构建一个镜像。
1. 准备应用代码 创建一个项目文件夹,包含以下三个文件:
app.py
:from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello from inside a Docker Container!" if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
requirements.txt
:Flask==2.2.2
Dockerfile
:# Step 1: 选择一个基础镜像 # 这是一个包含 Python 3.10 的轻量级官方镜像 FROM python:3.10-slim # Step 2: 设置工作目录 # 容器内的所有后续操作都将在此目录下进行 WORKDIR /app # Step 3: 复制依赖文件并安装依赖 # 先复制 requirements.txt 并安装,可以利用 Docker 的缓存机制 # 只有当 requirements.txt 变化时,这一层才会重新构建 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Step 4: 复制应用程序代码 # 将当前目录下的所有文件复制到容器的 /app 目录下 COPY . . # Step 5: 暴露端口 # 声明容器将监听 5000 端口,但不会自动映射 EXPOSE 5000 # Step 6: 定义容器启动时执行的命令 # 启动 Flask 应用 CMD ["python", "app.py"]
2. 构建镜像
在项目根目录下,执行 docker build
命令:
docker build -t my-python-app:1.0 .
-t
: (--tag
) 为你的镜像打上标签,格式为name:version
。.
: 表示 Dockerfile 所在的上下文路径(当前目录)。
3. 运行你的自定义镜像
docker run -d -p 5000:5000 --name web-app my-python-app:1.0
访问 http://localhost:5000
,你将看到 "Hello from inside a Docker Container!"。你成功地将你的应用容器化了!
第四站:持久化你的数据 (Volumes)
容器是无状态和易逝的。如果你在容器里存了数据,一旦容器被删除,数据就没了。这对于数据库等需要持久化数据的应用是致命的。卷 (Volumes) 就是解决方案。
让我们运行一个 PostgreSQL 数据库,并使用卷来保存它的数据。
docker run -d \
-p 5432:5432 \
--name my-postgres \
-e POSTGRES_PASSWORD=mysecretpassword \
-v pgdata:/var/lib/postgresql/data \
postgres:14
-v pgdata:/var/lib/postgresql/data
: 这就是卷的核心。pgdata
: Docker 会自动创建一个名为pgdata
的卷。/var/lib/postgresql/data
: 这是 PostgreSQL 在容器内存储数据的默认路径。- 这条命令的含义是:将
pgdata
这个卷挂载到容器的/var/lib/postgresql/data
目录上。所有写入这个目录的数据,实际上都保存在了pgdata
卷里。
验证持久化:
- 停止并删除容器:
docker stop my-postgres
然后docker rm my-postgres
。 - 查看卷是否还在:
docker volume ls
(你会看到pgdata
)。 - 重新启动一个新的容器,但使用同一个卷:
你之前在数据库中创建的任何数据,都会在新容器中完好无损。docker run -d -p 5432:5432 --name new-postgres -e POSTGRES_PASSWORD=mysecretpassword -v pgdata:/var/lib/postgresql/data postgres:14
Docker 命令参数深度解析
掌握常用命令的参数是提升效率的关键。这里我们对最重要的参数进行详细解析。
docker run:创建并运行一个新容器
参数 | 详细说明 |
---|---|
-d , --detach |
后台运行模式。容器将在后台启动并返回容器ID,而不会占用当前终端。 |
-p , --publish |
端口映射。格式:<host_port>:<container_port> 。例如,-p 8080:80 将主机的 8080 端口映射到容器的 80 端口。 |
-v , --volume |
挂载卷或绑定挂载。 • 命名卷: -v my-volume:/path/in/container (推荐)• 绑定挂载: -v /path/on/host:/path/in/container (直接映射主机目录) |
--name |
为容器指定一个名称。例如 --name my-web-server 。如果没有指定,Docker 会随机生成一个。 |
-e , --env |
设置环境变量。例如 -e MYSQL_ROOT_PASSWORD=secret 。 |
--rm |
容器退出时自动删除。非常适合用于临时的、一次性的任务,避免留下垃圾容器。 |
-it |
交互模式。通常组合使用,-i (--interactive ) 保持 STDIN 打开,-t (--tty ) 分配一个伪终端。常用于进入容器内部,如 docker exec -it my-container bash 。 |
--restart |
重启策略。设置容器在退出时的重启行为。 • no : (默认) 不重启• on-failure : 非0状态退出时重启• always : 总是重启• unless-stopped : 总是重启,除非被手动停止 |
--network |
连接到指定网络。例如 --network my-custom-network 。用于容器间通信。 |
docker build:从 Dockerfile 构建镜像
参数 | 详细说明 |
---|---|
-t , --tag |
为镜像打上标签。格式:name:tag ,例如 -t my-app:1.0 。一个好习惯是同时打上版本号。 |
-f , --file |
指定 Dockerfile 的路径。默认为当前目录下的 Dockerfile 。例如 -f ./path/to/MyDockerfile 。 |
--build-arg |
设置构建时变量。可以在 Dockerfile 中通过 ARG 指令使用。例如 --build-arg HTTP_PROXY=http://... 。 |
--no-cache |
禁用构建缓存。构建时完全不使用之前构建的缓存层,确保从头开始全新构建。 |
. (上下文路径) |
命令末尾的 . 指定了构建上下文 (Build Context) 的路径。Docker 会将此路径下的所有文件发送给 Docker 守护进程,以便在构建过程中使用(如 COPY . . )。 |
docker ps:列出容器
参数 | 详细说明 |
---|---|
-a , --all |
显示所有容器,包括已停止的容器。默认只显示正在运行的。 |
-q , --quiet |
只显示容器ID。非常适合用于脚本中,例如 docker stop $(docker ps -aq) 。 |
-f , --filter |
根据条件过滤。例如 -f "status=exited" 或 -f "name=my-nginx" 。 |
更进一步:Docker Compose
当你的应用由多个服务(如 Web 应用 + 数据库 + 缓存)组成时,手动管理多个 docker run
命令会变得非常繁琐。Docker Compose 允许你使用一个 YAML
文件来定义和运行多容器应用。
docker-compose.yml
示例:
version: '3.8'
services:
web:
build: . # 使用当前目录的 Dockerfile 构建
ports:
- "5000:5000"
depends_on:
- db
db:
image: postgres:14
environment:
- POSTGRES_PASSWORD=mysecretpassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
只需一个命令,即可启动整个应用栈:
docker-compose up -d
关闭并清理:
docker-compose down
结语
你已经完成了从 Docker 零基础到能够独立构建、运行和管理容器化应用的旅程。Docker 的世界远不止于此,但这为你探索微服务、CI/CD、Kubernetes 等更广阔的领域打下了坚实的地基。
记住,Docker 的核心是封装隔离和环境一致性。掌握了它,你就掌握了现代软件交付流程中最强大的一环
No comments to display
No comments to display