Docker学习笔记
1. Docker简介
1.1 什么是Docker
Docker是一种开源的容器化平台,用于快速构建、部署和运行应用程序。它通过容器技术将应用程序及其依赖环境打包成一个轻量级、可移植的标准化单元,确保应用在不同计算环境中一致运行。
核心优势:
- Docker容器与传统虚拟机不同,共享主机操作系统内核
- 无需模拟完整操作系统,启动更快、占用资源更少
- 通过Dockerfile定义容器配置,实现自动化构建和版本控制
应用场景:
- 微服务架构
- 持续集成/交付(CI/CD)
- 开发环境标准化
- 利用Docker Hub丰富的预构建镜像简化开发
生态系统:
- Docker Compose(多容器编排)
- Docker Swarm(集群管理)
- Kubernetes(容器编排)
2. 安装Docker
我的博客下有详细的文档,就不在这里过多赘述,各位彦祖亦菲们请移步下方对应文档即可:
参考详细教程
:Linux下安装docker教程- 常见报错解决方案:yum install报错处理
3. Docker基础入门
3.1 docker常见命令
官方文档:Docker Docs
以一张图为例,简述docker常见使用命令
3.2 Docker 常见命令对照表
命令 | 作用 | 示例 |
---|---|---|
docker pull |
拉取镜像到本地 | docker pull nginx:alpine 拉取Nginx的Alpine版本镜像 |
docker push |
推送镜像到仓库 | docker push myrepo/myapp:v1.0 推送自定义镜像到私有仓库 |
docker images |
查看所有本地镜像 | docker images -a 显示所有镜像(包括中间层) |
docker rmi |
删除本地镜像 | docker rmi ubuntu:18.04 删除Ubuntu 18.04镜像 |
docker run |
创建并运行容器 | docker run -d -p 8080:80 --name web nginx 后台运行Nginx容器 |
docker stop |
停止运行中的容器 | docker stop web 停止名为web的容器 |
docker start |
启动已停止的容器 | docker start web 重新启动web容器 |
docker exec |
在运行中的容器执行命令 | docker exec -it web bash 进入web容器的交互式bash终端 |
docker logs |
查看容器日志 | docker logs -f --tail 100 web 实时查看web容器最后100行日志 |
docker ps |
查看运行中的容器 | docker ps -a 显示所有容器(包括已停止的) |
docker rm |
删除容器 | docker rm -f web 强制删除web容器(包括运行中的) |
docker load |
从文件加载镜像 | docker load < myapp.tar 从tar文件加载镜像 |
docker save |
将镜像保存到文件 | docker save -o nginx.tar nginx:latest 保存Nginx镜像到tar文件 |
docker build |
构建镜像 | docker build -t myapp:v1 . 使用当前目录Dockerfile构建镜像 |
小技巧:
带-it参数:进入交互模式(如docker exec -it)
带-d参数:后台运行容器(如docker run -d)
带-p参数:端口映射(主机端口:容器端口)
带-v参数:目录挂载(主机路径:容器路径)
带–name参数:指定容器名称
组合命令示例:docker exec -it mysql mysql -uroot -p 直接进入MySQL命令行
💡 使用提示:
当不知道用法,或不知道还有什么参数可以跟的时候,可以使用–help进行查看用法,例如docker ps --help可以提示你docker ps -a可以查看所有容器,包括未运行的
3.2 数据卷挂载
3.2.1 什么是Docker数据卷
Docker数据卷(Volume)是用于持久化存储容器数据的核心机制,本质上是特殊目录,直接映射到主机文件系统。
主要特点:
- 持久化:生命周期独立于容器
- 共享性:支持多容器同时挂载
- 高性能:直接访问主机文件系统
- 可移植:支持跨主机迁移备份
3.2.2 数据卷常见命令
docker volume create [卷名] # 创建数据卷
docker volume ls # 列出所有数据卷
docker volume inspect [卷名] # 查看卷详情
docker volume rm [卷名] # 删除数据卷
3.2.3 挂载数据卷
在docker run
时使用-v
参数挂载:
# 创建命名数据卷
docker volume create html
# 启动容器时挂载数据卷
docker run -d --name nginx -v html:/usr/share/nginx/html nginx
详细拆解:
docker run -d --name nginx -v html:/usr/share/nginx/html nginx
命令 | 说明 | 作用 |
---|---|---|
docker run | 创建一个docker容器 | 创建一个docker容器 |
-d | 使容器在后台运行 | 使容器在后台运行 |
–name nginx | 给容器命名 | 将容器命名为nginx |
-v | 指定挂载 | 用于指定挂载卷 |
html:/usr/share/nginx/html | html指宿主机的路径;/usr/share/nginx/html 是容器中静态文件的绝对路径 |
挂载容器数据卷到宿主机 |
nginx | 容器 | 指定docker中的容器 |
注意:
docker容器中的文件路径应该如何寻找可以参考详细的[docker仓库官方文档](https://hub.docker.com/) 但该网站需要科学上网哦
注意事项:
- 容器创建后无法再指定挂载,需删除重建
- 挂载时数据卷不存在会自动创建
3.3 本地目录挂载
3.3.1 什么是本地目录挂载
同样也是通过-v
参数将宿主机物理路径映射到容器内部:
docker run -v /宿主机/绝对路径:/容器内目录 nginx
与数据卷的区别:
特性 | 数据卷 | 本地目录挂载 |
---|---|---|
存储位置 | Docker管理区域 | 宿主机指定路径 |
创建方式 | docker volume create |
直接使用现有目录 |
路径格式 | 名称(如mysql 、nginx ) |
绝对路径(如/data ) |
3.3.2 挂载本地目录
# 正确示例(绝对路径)
docker run -v /home/user/app:/app nginx
# 正确示例(相对路径)
docker run -v ./mysql:/var/lib/mysql mysql
# 错误识别(会被当作数据卷进行挂载)
docker run -v mysql:/var/lib/mysql mysql
关键区别:
- 以
/
或./
开头 → 识别为本地目录 - 直接以名称开头 → 识别为数据卷
4.练手时刻之——快速部署MySQL和Nginx
4.1 配置镜像加速器
问题:直接docker run
可能因国外源导致超时
原因:因为docker run
默认是去官方的镜像仓库拉取镜像,但官方的仓库是在国外的,因此要么会链接超时,要么会拉取的特别慢,所以在我们真正进行拉取的时候,还需要做一步:配置镜像加速器
解决方案(阿里云为例):
- 登录阿里云控制台 → 搜索"容器镜像服务"
- 获取镜像仓库加速器地址(如
https://xxxx.mirror.aliyuncs.com
) - 在宿主机执行:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
附图:
验证配置是否生效:
docker info | grep Mirrors -A 2
小提示:
若未生效以下还有个邪修流派的方法:
将以下命令全部复制,并修改vi /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.1panelproxy.com",
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://dockerhub.icu",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.iscas.ac.cn",
"https://docker.rainbond.cc"
]
}
4.2 部署命令示例
# 部署MySQL(带密码和端口映射)
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3306:3306 \
-v mysql_data:/var/lib/mysql \
mysql:8.0
# 部署Nginx(带目录挂载)
docker run -d --name nginx \
-p 80:80 \
-v ./html:/usr/share/nginx/html \
nginx:alpine
💡 提示:首次运行会自动拉取镜像,配置加速器后可大幅提升下载速度
5. 自定义镜像(制作镜像)
5.1 为什么要自定义镜像?
在Docker生态中,官方镜像提供了各种语言和框架的基础环境,但在实际生产环境中,我们几乎总是需要创建自定义镜像,主要原因包括:
环境标准化需求
不同应用需要特定的运行时配置(如时区、字符集)、依赖库版本和安全策略,自定义镜像确保开发、测试、生产环境完全一致。应用封装要求
将应用程序与其运行环境打包成不可变单元,实现"一次构建,处处运行"的承诺,避免环境差异导致的问题。安全加固必要
官方基础镜像通常包含非必要的工具和权限,自定义镜像可以:
移除不必要的软件包
使用非root用户运行应用
设置严格的权限控制
集成安全扫描工具性能优化空间
通过精简镜像层、选择更小的基础镜像(如Alpine Linux)、优化构建过程,可显著减小镜像体积,加速部署和启动过程。企业合规要求
满足内部审计规范,集成公司标准的监控代理、日志收集组件和安全证书。
典型案例:当你的Java应用需要特定时区(如东八区)、自定义JVM参数和私有依赖库时,官方OpenJDK镜像就无法直接满足需求,必须通过自定义镜像实现。
5.2 Dockerfile基础语法
Dockerfile是构建镜像的蓝图,由一系列指令组成,核心语法包括:
指令 | 作用描述 | 示例 |
---|---|---|
FROM | 指定基础镜像(必须第一条指令) | FROM openjdk:17-jdk-slim |
WORKDIR | 设置工作目录(后续指令的相对路径) | WORKDIR /app |
COPY | 复制文件/目录到镜像中 | COPY target/app.jar ./ |
ADD | 高级复制(支持URL和解压) | ADD https://example.com/file.tar.gz / |
RUN | 执行命令并创建新的镜像层 | RUN apt-get update && apt-get install -y curl |
ENV | 设置环境变量 | ENV TZ=Asia/Shanghai |
ARG | 构建时变量(构建结束后不可用) | ARG APP_VERSION=1.0.0 |
EXPOSE | 声明容器运行时监听的端口 | EXPOSE 8080 |
USER | 设置运行用户(提升安全性) | USER 1000 |
ENTRYPOINT | 容器启动命令(不可被覆盖) | ENTRYPOINT ["java", "-jar"] |
CMD | 默认命令参数(可被docker run 覆盖) |
CMD ["app.jar"] |
VOLUME | 创建挂载点 | VOLUME ["/data"] |
最佳实践原则:
- 指令按稳定性排序(从最稳定到最易变)
- 合并RUN指令减少镜像层数
- 使用
.dockerignore
排除无关文件 - 多阶段构建分离编译环境和运行环境
- 明确指定镜像版本(避免使用latest标签)
5.3 自定义镜像实战
下面以构建一个Java应用镜像为例,演示完整的Dockerfile制作流程:
需求场景:
- 基于OpenJDK 17
- 设置东八区时区
- 通过外部挂载配置文件
- 使用非root用户运行
- 支持健康检查
Dockerfile实现:
# 第一阶段:运行环境
FROM openjdk:17-jdk-slim
# 第二阶段:设置时区(东八区)
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
# 第三阶段:设置工作目录
WORKDIR /data
# 创建配置目录和日志目录(按需)
RUN mkdir -p /data/config /data/logs
# 从构建阶段复制JAR文件
COPY your-application.jar /data
# 启动命令(从config目录加载配置)
ENTRYPOINT ["java", "-Dspring.config.location=file:/data/config/", "-jar", "your-application.jar"]
构建与运行:
# 构建镜像
docker build -t my-java-demo:1.0.0 .
# 运行容器(挂载配置文件目录)
docker run -d \
--name java-app \
-p 8080:8080 \
-v /host/config:/data/config \
my-java-app:1.0.0
关键优化点:
- 多阶段构建:分离构建环境和运行环境,最终镜像不包含Maven等构建工具
- 权限控制:创建专用用户appuser运行应用,降低安全风险
- 配置分离:通过VOLUME声明配置目录,运行时动态挂载
- 健康监控:添加HEALTHCHECK确保应用可用性
- 时区设置:确保应用使用正确的时区(东八区)
6. Docker容器网络解析
6.1 理解容器网络基础
6.1.1网络架构图解
6.1.2核心概念解析
网桥机制:
- 在安装Docker后默认会在宿主机创建虚拟网桥
docker0
作为网络枢纽 - 所有容器通过
veth pair
虚拟设备连接到该网桥 - 默认IP分配范围:
172.17.0.0/16
- 在安装Docker后默认会在宿主机创建虚拟网桥
容器独立IP:
# 查看容器IP地址 docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名
- 每个容器获得独立IP(如
172.17.0.2
) - 容器间通过IP直接通信
- 宿主机可通过
docker0
网桥访问容器
- 每个容器获得独立IP(如
IP变动问题
:- 容器重启后IP会重新分配
- 服务依赖固定IP时会导致连接失败
6.2 自定义网络
6.2.1Docker网络管理常用命令
命令 | 作用 | 示例 |
---|---|---|
docker network ls |
列出所有网络 | docker network ls |
docker network create |
创建新网络 | docker network create -d bridge backend-net |
docker network inspect |
查看网络详情 | docker network inspect backend-net |
docker network connect |
连接容器到网络 | docker network connect backend-net mysql |
docker network disconnect |
从网络断开容器 | docker network disconnect backend-net redis |
docker network prune |
清理未使用网络 | docker network prune -f |
docker network rm |
删除网络 | docker network rm test-net |
6.2.2如何创建自定义网络
# 创建自定义网络
docker network create networkname
# 创建带网关、子网的自定义网络
docker network create \
--driver bridge \
--subnet 192.168.100.0/24 \
--gateway 192.168.100.1 \
networkname
6.2.3容器加入自定义网络
# 在创建、启动容器时加入网络
docker run -d --name containername --network networkname nginx
# 将已有容器加入网络
docker network connect networkname containername
6.3 固定容器IP
方法1:自定义网络指定IP
docker run -d --name containername \
--network networkname \
--ip 192.168.100.1 \ # 固定容器IP地址
nginx
注意事项:
- 必须使用自定义网络才能固定容器IP(非默认bridge)
- 指定的IP需在子网范围内(如
192.168.100.0/24
) - 每个IP在子网内必须唯一
方法2:通过容器名配置(推荐)
# 在相同网络中的容器
docker run -d --name containername --network networkname nginx
# 在容器内可通过名称访问其他容器
ping nginx # 自动解析为192.168.100.100
核心优势:
- 无需记忆IP地址
- 容器重启后名称不变
- 自动DNS解析(Docker内置DNS服务器)
6.4 网络规划——经典案例
6.4.1多服务服务器网络架构示意图
6.4.2实施步骤示例
# 1. 创建隔离网络
docker network create frontend-net
docker network create backend-net
docker network create data-net
# 2. 启动数据库服务(仅加入数据网络)
docker run -d --name mysql-db --network data-net mysql
docker run -d --name redis-cache --network data-net redis
# 3. 启动后端服务(连接后端和数据网络)
docker run -d --name user-service --network backend-net user-service
docker network connect data-net user-service
# 4. 启动前端服务(连接前端和后端网络)
docker run -d --name api-gateway -p 80:80 --network frontend-net api-gateway
docker network connect backend-net api-gateway
6.4.3安全加固策略
网络分层隔离:
- 前端网络:暴露80/443端口
- 后端网络:不直接暴露端口
- 数据网络:完全隔离,仅允许后端访问
访问控制:
# 创建仅允许特定容器访问的网络 docker network create --internal secure-net # 启动敏感服务 docker run -d --name audit-log --network secure-net audit-service
端口管理:
- 使用
-p 8080:80
显式映射端口 - 避免使用
-P
随机映射敏感服务
- 使用
7.Docker项目部署实战
7.1 Java应用容器化部署
核心部署流程
7.1.1 Dockerfile(支持外部配置)
FROM openjdk:17-jdk-slim
# 设置默认环境变量
ENV CONFIG_DIR=/external-config
# 创建配置目录(用于挂载外部配置)
RUN mkdir -p ${CONFIG_DIR}
# 复制应用JAR(不含配置)
COPY target/myapp.jar /app/app.jar
# 设置启动命令(优先使用外部配置)
ENTRYPOINT ["sh", "-c", "java -jar /app/app.jar \
--spring.config.location=${CONFIG_DIR}/application.yml"]
7.1.2 部署与配置管理
# 1. 构建镜像
docker build -t my-java-app:2.0 .
# 2. 首次运行(挂载配置文件)
docker run -d --name java-service \
-p 8080:8080 \
-v yourfolder/config:/external-config \ # 挂载配置目录
my-java-app:2.0
# 3. 修改配置后重启生效
vim config/application.yml # 修改配置文件
docker restart java-service
7.1.3 配置文件热更新技巧
# 查看当前配置
docker exec java-service cat /external-config/application.yml
# 动态更新配置(无需重启)
curl -X POST http://localhost:8080/actuator/refresh
# 监控配置变化
docker logs -f java-service | grep "Config change"
7.2 前端应用容器化部署
7.2.1 部署架构流程
7.2.2 Dockerfile(支持外部配置)
# 构建阶段(不变)
FROM node:16 as builder
...
# 生产阶段(支持外部配置挂载)
FROM nginx:alpine
# 复制默认静态文件
COPY --from=builder /app/build /usr/share/nginx/html
# 创建配置目录
RUN mkdir -p /etc/nginx/external-conf
# 复制默认配置(可作为模板)
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 启动脚本(优先加载外部配置)
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
7.2.3 entrypoint.sh 启动脚本
#!/bin/sh
# 检查是否存在外部配置
if [ -f "/etc/nginx/external-conf/nginx.conf" ]; then
cp /etc/nginx/external-conf/nginx.conf /etc/nginx/conf.d/
fi
# 启动Nginx
exec nginx -g 'daemon off;'
7.2.4 部署与配置管理
# 1. 构建镜像
docker build -t my-frontend:2.0 -f Dockerfile.prod .
# 2. 运行容器(挂载配置目录)
docker run -d --name frontend \
-p 80:80 \
-v yourfolder/nginx-conf:/etc/nginx/external-conf \ # 挂载配置目录
my-frontend:2.0
# 3. 修改路由配置
vim nginx-conf/nginx.conf # 添加新的代理规则
# 4. 热重载配置(无需重启容器)
docker exec frontend nginx -s reload
7.2.5 动态配置示例
# nginx.conf (可动态修改)
server {
location /api/v1 {
proxy_pass http://backend-service:8080;
}
location /api/v2 {
proxy_pass http://new-backend:8081;
}
}
7.3 Docker Compose
7.3.1 Docker命令 vs Compose对比
功能 | Docker命令 | docker-compose.yml |
---|---|---|
启动服务 | docker run -d --name frontend -p 80:80 frontend-image |
services: frontend: image: frontend-image ports: - "80:80" |
网络配置 | docker network create app-net docker run --network app-net ... |
networks: app-net: driver: bridge |
数据卷挂载 | docker run -v ./data:/app/data ... |
volumes: - ./data:/app/data |
环境变量 | docker run -e "DB_HOST=db" ... |
environment: - DB_HOST=db |
依赖管理 | 需手动控制启动顺序 | depends_on: - db |
多容器启动 | 多个docker run 命令 |
单条docker-compose up 命令 |
7.3.2 docker-compose.yml结构
version: '3.8'
services:
frontend:
image: my-frontend:2.0
ports: ["80:80"]
volumes:
- ./frontend/nginx-conf:/etc/nginx/external-conf # 挂载前端配置
networks: ["app-net"]
depends_on: ["backend"]
backend:
image: my-java-app:2.0
ports: ["8080:8080"]
volumes:
- ./backend/config:/external-config # 挂载Java配置
environment:
- SPRING_PROFILES_ACTIVE=prod
networks: ["app-net"]
depends_on: ["mysql", "redis"]
mysql:
image: mysql:8.0
volumes: ["mysql-data:/var/lib/mysql"]
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} # 使用环境变量
networks: ["app-net"]
redis:
image: redis:6.2-alpine
networks: ["app-net"]
networks:
app-net:
driver: bridge
volumes:
mysql-data:
7.3.3 完整部署工作流
# 1. 构建镜像(可选)
docker-compose build
# 2. 启动所有服务
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 3. 动态调整配置
# 修改配置文件后执行:
docker-compose restart backend # Java应用
docker-compose exec frontend nginx -s reload # 前端路由
# 4. 监控服务状态
docker-compose ps
docker-compose logs -f
# 5. 安全关闭
docker-compose down --volumes --remove-orphans