Docker 基础学习笔记

zangxin

Docker 基础学习笔记

Docker 用于构建、分发和运行容器化应用。它将应用程序及其依赖打包为镜像,再通过镜像创建相互隔离的容器。

基本概念

Docker 概览

概念 说明
镜像(Image) 只读的软件包,包含应用程序、运行环境和依赖
容器(Container) 镜像运行后产生的实例
仓库(Registry) 用于保存和分发镜像,例如 Docker Hub
Dockerfile 描述镜像构建过程的文本文件
Docker Compose 使用 YAML 文件统一管理多个容器

容器与虚拟机

容器与虚拟机对比

容器与虚拟机都可以隔离应用,但实现方式不同:

对比项 容器 虚拟机
操作系统 多个容器共享宿主机内核 每台虚拟机包含完整操作系统
启动速度 通常较快 通常较慢
资源占用 较低 较高
隔离方式 进程、网络和文件系统隔离 硬件虚拟化
可移植性 镜像可以跨环境分发 依赖虚拟机镜像和虚拟化平台

容器拥有独立的文件系统、进程空间、网络和资源限制,但仍与宿主机共享内核。

镜像管理

常用命令

命令 作用
docker search nginx 在镜像仓库中搜索 Nginx
docker pull nginx 拉取默认标签的 Nginx 镜像
docker pull nginx:1.26.0 拉取指定版本
docker images 查看本地镜像
docker rmi nginx:1.26.0 删除指定镜像
docker rmi <image-id> 根据镜像 ID 删除
docker run nginx 使用镜像创建并启动容器

没有显式指定标签时,Docker 通常使用 latest

1
2
docker pull nginx
docker pull nginx:latest

latest 只是一个普通标签,不代表镜像一定是最新稳定版本。生产环境更适合固定明确版本。

将容器保存为镜像

docker commit 可以把容器当前的文件系统修改保存为新镜像:

1
2
3
4
5
docker commit \
  -a "zangxin" \
  -m "Update index.html" \
  my-nginx \
  zangxin-nginx:v1.0

这种方式适合学习和临时保存。可重复构建的项目应优先使用 Dockerfile。

导出与导入镜像

将镜像保存为 tar 文件:

1
docker save -o zangxin-nginx.tar zangxin-nginx:v1.0

在另一台机器加载并运行:

1
2
docker load -i zangxin-nginx.tar
docker run -d --name my-nginx -p 22222:80 zangxin-nginx:v1.0

验证服务:

1
curl http://localhost:22222

推送到 Docker Hub

先登录 Docker Hub:

1
docker login

镜像名称需要包含 Docker Hub 用户名或组织名:

1
2
docker tag zangxin-nginx:v1.0 zangxin51/zangxin-nginx:v1.0
docker push zangxin51/zangxin-nginx:v1.0

如果需要提供默认标签,可以额外推送 latest

1
2
docker tag zangxin51/zangxin-nginx:v1.0 zangxin51/zangxin-nginx:latest
docker push zangxin51/zangxin-nginx:latest

容器管理

常用命令

命令 作用
docker ps 查看正在运行的容器
docker ps -a 查看全部容器
docker start <container> 启动已停止的容器
docker stop <container> 停止容器
docker restart <container> 重启容器
docker rm <container> 删除已停止的容器
docker rm -f <container> 强制删除容器
docker logs <container> 查看容器日志
docker inspect <container> 查看容器详细信息

删除所有容器属于危险操作,执行前应确认没有需要保留的数据:

1
docker rm -f $(docker ps -aq)

运行 Nginx

下面的命令会创建名为 my-nginx 的容器,在后台运行,并将宿主机 27182 端口映射到容器的 80 端口:

1
2
3
4
docker run -d \
  --name my-nginx \
  -p 27182:80 \
  nginx:latest

常用参数:

参数 作用
-d 在后台运行容器
--name 指定容器名称
-p host:container 将宿主机端口映射到容器端口
--rm 容器停止后自动删除
-e KEY=value 设置环境变量
-v source:target 配置目录或卷挂载
--network 指定容器网络

访问服务:

1
curl http://localhost:27182

进入容器

使用 docker exec 在运行中的容器里启动 Shell:

1
docker exec -it my-nginx bash

修改 Nginx 默认页面:

1
2
echo "<h1>Hello, Docker</h1>" \
  > /usr/share/nginx/html/index.html

退出容器后再次访问:

1
curl http://localhost:27182

直接修改容器内部文件不利于维护,容器重新创建后修改也会丢失。实际项目应使用挂载或自定义镜像。

数据持久化

Docker 常见的数据持久化方式包括绑定挂载和命名卷。

方式 示例 数据来源 适用场景
绑定挂载 -v /host/path:/container/path 宿主机指定目录 配置文件、源码和需要直接编辑的文件
命名卷 -v volume-name:/container/path Docker 管理的卷 数据库数据和容器生成的数据

绑定挂载

将宿主机目录映射到 Nginx 的静态文件目录:

1
2
3
4
5
docker run -d \
  --name nginx-app \
  -p 15000:80 \
  -v ~/docker-data/nginx/html:/usr/share/nginx/html \
  nginx:latest

挂载后,宿主机与容器访问的是同一份文件。宿主机目录不存在时,使用 -v 通常会自动创建目录。

需要注意:绑定挂载会覆盖容器目标路径中原有的内容。将空目录挂载到 /etc/nginx,可能遮蔽镜像自带配置并导致 Nginx 无法启动。

如果容器需要使用宿主机时区,可以挂载时区文件:

1
-v /etc/localtime:/etc/localtime:ro

命名卷

命名卷由 Docker 统一管理,可以在首次挂载时接收镜像目标目录中的初始内容:

1
2
3
4
5
docker run -d \
  --name nginx-app \
  -p 15000:80 \
  -v nginx-conf:/etc/nginx \
  nginx:latest

常用命令:

命令 作用
docker volume ls 查看所有卷
docker volume create app-data 创建命名卷
docker volume inspect app-data 查看卷的详细信息
docker volume rm app-data 删除未使用的卷
docker volume prune 删除所有未使用的卷,需谨慎

Linux 中的卷数据通常位于 /var/lib/docker/volumes/。Docker Desktop 运行在内部虚拟机中,不建议依赖宿主机直接访问该目录。

删除容器默认不会删除命名卷,因此重新创建容器后仍可以复用数据。执行 docker compose down -v 或显式删除卷时,数据才会被移除。

Docker 网络

默认 bridge 网络

Docker 默认会创建 bridge 网络,常见网桥名称为 docker0。容器会获得内部 IP,并通过网桥访问其他容器或外部网络。

查看容器网络信息:

1
docker inspect nginx-app

Docker 容器网络信息

容器可以使用内部 IP 和容器端口互相访问:

1
curl http://172.17.0.3:80

但容器 IP 可能在重新创建后变化,不应将它写死在应用配置中。

自定义网络

自定义 bridge 网络支持通过容器名进行 DNS 解析,适合容器之间稳定访问:

1
2
docker network create mynet
docker network ls

创建容器时加入网络:

1
2
3
4
docker run -d \
  --name app1 \
  --network mynet \
  nginx:latest

同一网络中的其他容器可以直接使用容器名访问:

1
curl http://app1

只有需要从宿主机或外部网络访问的服务才需要配置 -p。容器之间通信可以直接使用容器端口。

Redis 主从示例

Docker 环境中的 Redis 主从配置

创建 Redis 主节点:

1
2
3
4
5
6
7
8
docker run -d \
  --name redis-master \
  --network mynet \
  -p 6379:6379 \
  -v ~/docker-data/redis/master:/bitnami/redis/data \
  -e REDIS_REPLICATION_MODE=master \
  -e REDIS_PASSWORD=root \
  bitnami/redis:latest

创建 Redis 从节点:

1
2
3
4
5
6
7
8
9
10
11
docker run -d \
  --name redis-replica \
  --network mynet \
  -p 6380:6379 \
  -v ~/docker-data/redis/replica:/bitnami/redis/data \
  -e REDIS_REPLICATION_MODE=slave \
  -e REDIS_PASSWORD=root \
  -e REDIS_MASTER_HOST=redis-master \
  -e REDIS_MASTER_PORT_NUMBER=6379 \
  -e REDIS_MASTER_PASSWORD=root \
  bitnami/redis:latest

从节点通过自定义网络中的容器名 redis-master 连接主节点。可以在主节点写入数据,再到从节点验证复制结果。

环境变量与中间件

-e 用于向容器传递环境变量:

1
2
3
docker run --rm \
  -e APP_ENV=production \
  alpine env

环境变量经常用于传递数据库名称、运行模式和服务地址。密码等敏感信息不应硬编码到公开脚本或仓库中,生产环境应使用 Secret 或安全的配置管理方式。

MySQL 示例

1
2
3
4
5
6
7
docker run -d \
  --name mysql-app \
  -p 3307:3306 \
  -v ~/docker-data/mysql/conf:/etc/mysql/conf.d \
  -v ~/docker-data/mysql/data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=example-password \
  mysql:8.0

运行中间件前应检查:

问题 建议
是否需要从宿主机访问 需要时才配置端口映射
是否需要修改配置 使用绑定挂载或配置文件
是否需要保存数据 使用命名卷或绑定挂载
是否需要运行参数 使用环境变量或启动命令
是否包含敏感信息 不要提交真实密码和密钥

Docker Compose

Docker Compose 使用 compose.yaml 描述多个服务、网络和卷,再通过一组命令统一管理应用。

参考文档:Compose 文件规范

常用命令

命令 作用
docker compose up -d 创建并后台启动服务
docker compose down 停止并删除容器和 Compose 网络
docker compose start 启动已存在的服务容器
docker compose stop 停止服务,但不删除容器
docker compose restart 重启服务
docker compose ps 查看服务状态
docker compose logs -f 持续查看日志
docker compose up -d --scale app=3 将指定服务扩容为三个实例

docker compose down 默认不会删除命名卷。使用 docker compose down -v 会同时删除卷,应谨慎执行。

当 Compose 文件或镜像配置发生变化时,再次执行:

1
docker compose up -d

Compose 会重新创建发生变化的服务,未变化的服务通常保持不动。

WordPress 与 MySQL 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
name: myblog

services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: example-root-password
      MYSQL_DATABASE: wordpress
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql-conf:/etc/mysql/conf.d
    restart: unless-stopped
    networks:
      - myblog

  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: root
      WORDPRESS_DB_PASSWORD: example-root-password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress-data:/var/www/html
    restart: unless-stopped
    networks:
      - myblog
    depends_on:
      - mysql

volumes:
  mysql-data:
  wordpress-data:

networks:
  myblog:

启动指定 Compose 文件:

1
docker compose -f compose.yaml up -d

停止并删除服务容器和网络:

1
docker compose -f compose.yaml down

depends_on 只控制基本启动顺序,不等同于等待数据库完全可用。需要严格控制就绪状态时,应增加 healthcheck 或应用侧重试机制。

Dockerfile

Dockerfile 将镜像构建过程写成可重复执行的步骤。

参考文档:Dockerfile reference

Dockerfile 常用指令

常用指令

指令 作用
FROM 指定基础镜像
LABEL 添加镜像元数据
WORKDIR 设置后续命令的工作目录
COPY 将构建上下文中的文件复制到镜像
RUN 构建镜像时执行命令
ENV 设置环境变量
EXPOSE 声明容器监听端口
CMD 设置默认启动命令,可被运行参数覆盖
ENTRYPOINT 设置容器入口程序

Java 应用示例:

1
2
3
4
5
6
7
8
9
10
FROM eclipse-temurin:17-jre

LABEL author="zangxin"

WORKDIR /app
COPY app.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

构建镜像:

1
docker build -f Dockerfile -t my-java-app:v1.0 .

最后的 . 表示使用当前目录作为构建上下文。

镜像分层机制

Docker 镜像分层机制

Docker 镜像由多个只读层组成。容器启动后,会在镜像层之上增加一个可写层。

分层机制带来的特点:

  • 不同镜像可以复用相同基础层,减少磁盘占用。
  • Dockerfile 中未变化的层可以使用构建缓存。
  • 删除容器后,容器可写层中的数据会丢失。
  • 经常变化的构建步骤应尽量放在 Dockerfile 后部,提高缓存命中率。

查看容器可写层大小:

1
docker ps -s

查看镜像构建历史:

1
docker history my-java-app:v1.0

日志与排查

查看容器日志:

1
2
docker logs my-nginx
docker logs -f --tail 100 my-nginx

常用排查命令:

命令 作用
docker stats 实时查看 CPU、内存和网络使用情况
docker top <container> 查看容器中的进程
docker inspect <container> 查看容器配置和状态
docker exec -it <container> sh 进入容器排查
docker port <container> 查看端口映射
docker system df 查看 Docker 磁盘占用

其他中间件安装示例可参考:Docker 常用中间件安装

远程 Docker 与 IDE 集成

Docker CLI 可以通过 Context 管理本地和远程 Docker Engine。优先使用 SSH 或启用 TLS 的 TCP 连接,不要把未加密的 Docker API 暴露到公网。

使用 SSH Context

创建远程 Context:

1
2
docker context create remote-host \
  --docker "host=ssh://docker-user@example-host"

查看并切换 Context:

1
2
3
docker context ls
docker context use remote-host
docker ps

切回本地:

1
docker context use default

TCP 2375 的风险

2375 通常表示未启用 TLS 的 Docker API。能够访问该端口的客户端几乎等同于获得宿主机管理员权限,因此只适合隔离的实验环境。

如果只是本机测试,可以将 Docker Engine 绑定到回环地址,而不是 0.0.0.0

1
2
3
4
5
6
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd \
  -H fd:// \
  -H tcp://127.0.0.1:2375 \
  --containerd=/run/containerd/containerd.sock

重新加载 systemd 配置:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker.service

本机测试:

1
docker -H tcp://127.0.0.1:2375 ps

需要远程 TCP 连接时,应参考 Docker 官方文档配置双向 TLS,并使用防火墙限制来源。IDEA 中添加 Docker 连接时,可以选择 SSH 或安全的 TCP Socket,然后填写对应 Engine API 地址。

参考文档: