一、Docker 简述
1、介绍
Docker 是一个开源平台,用于开发、发布和运行应用程序。它利用操作系统级的虚拟化技术将应用程序及其依赖项打包在一个轻量级、可移植的容器中,从而实现环境的一致性、快速部署和可扩展性。
2、优势
-
环境一致性:Docker 彻底解决了 “It works on my machine” 的经典问题,容器确保应用程序在任何运行 Docker 的环境(开发、测试、生产)中都以完全相同的方式运行,因为依赖项全被打包在镜像里了。
-
隔离性:容器之间以及容器与宿主机之间是相互隔离的。一个容器的问题(如崩溃、资源耗尽)通常不会直接影响其他容器或宿主机,提高了安全性和稳定性。
-
轻量级与高效:与传统虚拟机相比,容器不需要运行完整的操作系统。它们共享宿主机操作系统的内核,只在进程级别进行隔离。这使得容器启动速度快(秒级),占用资源(磁盘、内存、CPU)少,一台宿主机可以运行成百上千个容器。
-
可移植性:Docker 镜像可以在任何支持 Docker 的 Linux、Windows 或 macOS 系统上运行,无论是在本地还是在公有云上,这极大地简化了部署和迁移。
-
微服务友好:Docker 是构建和部署微服务架构的天然载体。每个微服务可以打包在自己的容器中,独立开发、部署和扩展。
-
版本控制与回滚:镜像本身是分层的,并且可以通过标签进行版本控制。如果新版本的应用出现问题,可以使用旧版本的镜像轻松回滚并启动容器。
-
庞大的生态系统:Docker Hub 提供了海量的官方和社区维护的镜像,覆盖了几乎所有流行的软件、数据库、中间件等,极大地加速了开发。围绕 Docker 还有丰富的工具链(如 Docker Compose, Docker Swarm, Kubernetes 等)用于管理容器集群。
3、核心概念
(1)Docker 镜像
- 定义:一个只读的模板,包含了运行某个应用程序所需的一切:代码、运行时环境、系统工具、系统库和设置。
- 类比:就像面向对象编程中的“类”,或者一个安装光盘(.iso 文件)。
- 特点:分层结构,每一层代表镜像构建过程中的一个指令,如安装软件包、复制文件等,可复用、不可变,详见下文的 Dockerfile。实际上,我并不关心镜像的这一特点,因为我无需亲手构建镜像,我所做的只是用公司插件构建镜像包,然后部署到服务器。
(2)Docker 容器
- 定义:镜像的一个运行实例。当 Docker 基于镜像启动时,就创建了一个容器。
- 类比:就像面向对象编程中由“类”创建出来的“对象”,或者从安装光盘启动起来的操作系统。
- 特点:轻量级(共享宿主机操作系统内核)、可移植、隔离(有自己的文件系统、网络、进程空间)、临时性(容器停止后,除非通过挂载,否则其内部产生的变化默认不会保存到镜像中)。
(3)Dockerfile
- 定义:一个纯文本文件,包含一系列用于构建镜像的指令,如
FROM,RUN,COPY,ADD等。 - 作用:定义了构建镜像的步骤和最终容器的运行环境,确保了镜像构建过程的可重复性和自动化。
- 核心概念:Docker 镜像是由一系列只读层组成的,每一层都代表了 Dockerfile 中的一个指令。当运行容器时,Docker 会在镜像的最顶层添加一个可写层,所有对容器的修改(如写入文件)都发生在这个可写层中,不会影响原始镜像。容器删除后,这个可写层也会被删除,镜像保持不变。
(4)Docker 仓库
存储和分发 Docker 镜像的地方,主要分为:
- 公共仓库: 最著名的是 Docker Hub,提供大量官方和社区维护的镜像。
- 私有仓库: 用于企业内部存储和分发私有镜像。
(5)Docker 网络
Docker 的网络模式有以下五种:
1. bridge 网络(默认)
- 机制:Docker 创建一个虚拟网桥,容器通过 veth pair 连接到这个网桥。
- 特点:
- 容器有独立的 IP 地址。
- 容器之间可以通过 IP 通信。
- 外部访问需要通过端口映射(
-p)。
注:veth pair 是 Linux 的“虚拟网线”,Docker 用它把容器“插”到宿主机网络上,是容器通信的底层基石。
2. host 网络
- 机制:容器直接使用宿主机的网络接口。
- 特点:
- 容器没有独立的 IP,使用宿主机 IP。
- 网络性能高,但失去网络隔离。
3. none 网络
- 完全隔离,无法通信。
4. container 网络
- 容器共享另一个容器的网络。
5. 自定义网络
- Docker 允许用户创建自定义网络,支持 DNS 解析、容器名通信等高级功能,驱动类型包括 bridge、overlay、macvlan、ipvlan。
二、Docker 命令
注:Docker 允许使用“最短唯一前缀”代替完整的容器/镜像 ID,只要前缀在当前环境中不会匹配出第二个对象;冲突时 Docker 会提示再补充几位。
1、系统
# 查看 Docker 客户端和服务端的版本信息
docker version
# 显示 Docker 的系统信息,包括镜像、容器数量等
docker info
# 查看 Docker 命令帮助
docker --help
# 看镜像/容器/卷各占了多少磁盘
docker system df
2、镜像操作
镜像覆盖机制:镜像 ID 相同意味着这两个镜像是由完全相同的构建指令和文件内容生成的,导致它们的哈希值完全一致。如果加载的镜像在本地已存在(镜像 ID 相同),Docker 会用新加载的镜像覆盖本地已有的那个,并将原有镜像的名称和标签转移给新镜像,原镜像会失去其名称和标签,变成 <none> : <none>,即悬空镜像。
# 从本地加载镜像归档文件
docker load -i [文件路径]/[文件名]
docker load < [文件路径]/[文件名]
# 从远程仓库拉取镜像
docker pull [注册中心]/[仓库路径]/[镜像名]:[标签]
- 当有缺省时,默认从从 Docker Hub 拉取官方的最新镜像
- 注册中心:缺省值为 docker.io
- 仓库路径:缺省值为 library
- 标签:缺省值为 latest
# 例子
docker pull nginx
docker pull ubuntu:20.04
docker pull bitnami/mysql
docker pull registry.redhat.io/ubi8/ubi:8.6
docker pull myregistry.local:5000/mycompany/web-app:v2.1.5
docker pull 192.168.1.100:8080/auth-service:latest
# 列出所有镜像
docker images
# 强制删除镜像
docker rmi -f [镜像ID]
docker rmi -f [镜像名]:[标签]
# 使用 Dockerfile 构建镜像:略
docker build -t [镜像名]:[标签] -f [文件路径]/Dockerfile .
# 将镜像保存为归档文件
docker save -o [文件路径]/[文件名] [镜像名]:[标签]
docker save -o ./my_app.tar my-app:v1.0
# 这样写会导致悬空镜像
docker save -o [文件路径]/[文件名] [镜像ID]
docker save -o ./my_app.tar f4e0f8b7b6d1
# 重命名镜像
docker tag [镜像ID] [镜像名]:[标签]
# 推送镜像到远程仓库
docker push [注册中心]/[仓库路径]/[镜像名]:[标签]
3、容器生命周期
# 创建并启动容器:最简命令
docker run [启动参数] [镜像ID]
# 启动参数复杂多样,常见参数如下:
# 指定容器名,否则为随机字符串
--name nginx
# 端口映射
-p [宿主机端口]:[容器端口]
# 绑定挂载
-v [宿主机目录]:[容器目录]
# 命名卷挂载
-v [命名卷名称]:[容器目录]
# 环境变量
-e [变量名]=[变量值]
# 重启策略
--restart no
--restart always
--restart on-failure[:最大尝试次数]
--restart unless-stopped
# 资源分配
-m 512m
--cpus 1.5
# 后台启动
-d
# 完整例子
docker run -d \
--name web \
-p 8080:80 \
-v /var/log/nginx:/var/log/nginx \
-e NGINX_HOST=example.com \
--restart unless-stopped \
--cpus 1 \
-m 512m \
nginx:1.25
# 停止容器
docker stop [容器ID]
# 启动容器
docker start [容器ID]
# 重启容器
docker restart [容器ID]
# 强制删除容器
docker rm -f [容器ID]
# 创建但不启动容器
docker create [镜像ID]
# 查看运行中的容器
docker ps
# 查看所有容器并过滤
docker ps -a |grep n8n
4、容器调试
# 进入容器内部的新终端
docker exec -it [容器ID] bash
# 在容器内部执行某条命令,无需进入容器内部
docker exec [容器ID] [命令]
docker exec f4e nginx -t
# 查看容器详细配置
docker inspect [容器ID]
# 查看容器挂载的20行配置
docker inspect [容器ID] |grep -A20 'Mounts'
# 查看容器资源占用
docker stats
# 查看容器日志
docker logs [容器ID]
# 查看容器最新的100条日志中报错的地方,显示其上下5行,并在底部实时输出当前日志
docker logs -fn100 [容器ID] | grep -C5 ERROR
# 查看指定时间的日志
docker logs --since 5m [容器ID]
docker logs --since 2024-01-01T08:00:00 --until 2024-01-01T12:00:00 <容器ID>
# 宿主机和容器之间互传文件(容器内路径必须是绝对路径)
# 第一个参数是文件,第二个参数是存储路径
docker cp [宿主机路径]/[文件名] [容器ID]:[容器内路径]
docker cp [容器ID]:[容器内路径]/[文件名] [宿主机路径]
# 查看容器内的进程信息
docker top [容器ID]
5、容器挂载
定义:挂载是指把宿主机中的存储映射进容器文件系统,实现数据持久化的操作。挂载分为两类,绑定挂载和命名卷挂载。
(1)绑定挂载
容器内外实时双向同步,宿主机改动立即在容器内可见,反之亦然。实际经常将容器的配置文件进行挂载,以便随时修改,很常用。
# 例子:挂载 nginx 的配置文件以便修改
docker run -v /data/nginx.conf:/etc/nginx/nginx.conf [镜像ID]
(2)命名卷挂载
这种方式的本质是交给 Docker 进行管理,存储在 Docker 的存储区域,不利于手动修改,多用于数据库挂载。
# 创建命名卷
docker volume create [卷名]
# 查看所有命名卷
docker volume ls
# 查看卷详情
docker volume inspect [卷名]
# 删除卷
docker volume rm [卷名]
# 例子:数据库持久化
docker run -v mysql-data:/var/lib/mysql mysql:8.0
6、网络
核心概念见前文的 Docker 网络,实际没有关注过,暂略。
# 列出网络
docker network ls
# 建立自定义网络
docker network create --driver [驱动类型] [网络名]
# 查看端口映射
docker port [容器ID]
7、仓库
没用过,暂略。
# 登录
docker login/registory
# 登出
docker logout
三、容器编排技术
这里暂不深入介绍,写不完了,等下一篇《容器编排技术简述》另谈。