Docker容器从入门到精通

Docker 从入门到企业实战

本笔记整合自多份 Docker 学习资料,涵盖从基础概念、核心原理到高级编排与生产运维的完整知识体系,适合系统学习与日常查阅。


目录

  • [一、Docker 概述](#一、Docker 概述)
    • [1.1 虚拟化与容器化](#1.1 虚拟化与容器化)
    • [1.2 Docker 概述](#1.2 Docker 概述)
    • [1.3 Docker 的应用场景](#1.3 Docker 的应用场景)
    • [1.4 Docker 与虚拟机比较](#1.4 Docker 与虚拟机比较)
  • [二、Docker 核心组件](#二、Docker 核心组件)
    • [2.1 Docker 引擎](#2.1 Docker 引擎)
    • [2.2 Docker 镜像](#2.2 Docker 镜像)
    • [2.3 Docker 容器](#2.3 Docker 容器)
    • [2.4 Docker 仓库](#2.4 Docker 仓库)
  • [三、Docker 环境安装](#三、Docker 环境安装)
    • [3.1 系统环境准备](#3.1 系统环境准备)
    • [3.2 RHEL/Redhat 安装](#3.2 RHEL/Redhat 安装)
    • [3.3 Ubuntu 安装](#3.3 Ubuntu 安装)
    • [3.4 脚本安装](#3.4 脚本安装)
    • [3.5 配置镜像加速](#3.5 配置镜像加速)
  • [四、Docker 核心原理](#四、Docker 核心原理)
    • [4.1 Docker 内部结构](#4.1 Docker 内部结构)
    • [4.2 容器生命周期](#4.2 容器生命周期)
    • [4.3 命名空间(Namespace)](#4.3 命名空间(Namespace))
    • [4.4 控制组(Cgroups)](#4.4 控制组(Cgroups))
    • [4.5 联合文件系统(UnionFS)](#4.5 联合文件系统(UnionFS))
  • [五、Docker 基本操作](#五、Docker 基本操作)
    • [5.1 镜像管理](#5.1 镜像管理)
    • [5.2 容器管理](#5.2 容器管理)
    • [5.3 仓库管理](#5.3 仓库管理)
  • 六、容器网络管理
    • [6.1 网络模式概览](#6.1 网络模式概览)
    • [6.2 Bridge 网络模式](#6.2 Bridge 网络模式)
    • [6.3 Host 网络模式](#6.3 Host 网络模式)
    • [6.4 Container 网络模式](#6.4 Container 网络模式)
    • [6.5 None 网络模式](#6.5 None 网络模式)
    • [6.6 端口映射与外部访问](#6.6 端口映射与外部访问)
    • [6.7 自定义网络与容器互联](#6.7 自定义网络与容器互联)
    • [6.8 DNS 配置](#6.8 DNS 配置)
  • 七、容器数据管理
    • [7.1 数据管理命令](#7.1 数据管理命令)
    • [7.2 数据卷(Volume)操作](#7.2 数据卷(Volume)操作)
    • [7.3 数据卷容器(Data Volume Containers)](#7.3 数据卷容器(Data Volume Containers))
    • [7.4 tmpfs 挂载](#7.4 tmpfs 挂载)
    • [7.5 数据卷备份与恢复](#7.5 数据卷备份与恢复)
  • [八、Dockerfile 镜像构建](#八、Dockerfile 镜像构建)
    • [8.1 什么是 Dockerfile](#8.1 什么是 Dockerfile)
    • [8.2 常用指令详解](#8.2 常用指令详解)
    • [8.3 中级概念](#8.3 中级概念)
    • [8.4 高级技巧](#8.4 高级技巧)
    • [8.5 调试与故障排除](#8.5 调试与故障排除)
    • [8.6 编写最佳实践](#8.6 编写最佳实践)
    • [8.7 常见错误避免](#8.7 常见错误避免)
    • [8.8 镜像构建与管理](#8.8 镜像构建与管理)
  • [九、Dockerfile 案例实战](#九、Dockerfile 案例实战)
    • [9.1 基于 CentOS 构建 Nginx 镜像](#9.1 基于 CentOS 构建 Nginx 镜像)
    • [9.2 基于 Debian 构建 Nginx 镜像](#9.2 基于 Debian 构建 Nginx 镜像)
    • [9.3 基于 Alpine 构建 Nginx 镜像](#9.3 基于 Alpine 构建 Nginx 镜像)
    • [9.4 基于 Alpine 构建错误页面镜像](#9.4 基于 Alpine 构建错误页面镜像)
    • [9.5 构建 HTTPS 服务镜像](#9.5 构建 HTTPS 服务镜像)
    • [9.6 构建 Python 计算镜像(马青公式)](#9.6 构建 Python 计算镜像(马青公式))
  • [十、Swarm 集群管理](#十、Swarm 集群管理)
    • [10.1 Swarm 核心概念](#10.1 Swarm 核心概念)
    • [10.2 Swarm 安装](#10.2 Swarm 安装)
    • [10.3 Swarm 集群搭建](#10.3 Swarm 集群搭建)
    • [10.4 服务管理](#10.4 服务管理)
    • [10.5 滚动更新与回滚](#10.5 滚动更新与回滚)
    • [10.6 节点管理(污染与激活)](#10.6 节点管理(污染与激活))
    • [10.7 路由网格](#10.7 路由网格)
  • [十一、Docker Compose 编排](#十一、Docker Compose 编排)
    • [11.1 Compose 概念](#11.1 Compose 概念)
    • [11.2 Compose 安装](#11.2 Compose 安装)
    • [11.3 docker-compose.yml 详解](#11.3 docker-compose.yml 详解)
    • [11.4 常用命令](#11.4 常用命令)
    • [11.5 使用案例:Flask + Redis](#11.5 使用案例:Flask + Redis)
    • [11.6 与 Swarm 集成](#11.6 与 Swarm 集成)
  • [十二、Harbor 私有仓库](#十二、Harbor 私有仓库)
    • [12.1 Harbor 概述](#12.1 Harbor 概述)
    • [12.2 环境准备](#12.2 环境准备)
    • [12.3 安装 Docker 与 Compose](#12.3 安装 Docker 与 Compose)
    • [12.4 Harbor 安装部署](#12.4 Harbor 安装部署)
    • [12.5 证书配置](#12.5 证书配置)
    • [12.6 启动与配置服务](#12.6 启动与配置服务)
    • [12.7 测试本地仓库](#12.7 测试本地仓库)
  • 十三、案例实战:中间件容器化
    • [13.1 MySQL 安装](#13.1 MySQL 安装)
    • [13.2 Redis 安装](#13.2 Redis 安装)
    • [13.3 Elasticsearch 安装](#13.3 Elasticsearch 安装)
    • [13.4 Kibana 安装](#13.4 Kibana 安装)
    • [13.5 MySQL 主从复制](#13.5 MySQL 主从复制)
    • [13.6 MySQL 双主双从](#13.6 MySQL 双主双从)
    • [13.7 Nginx 高可用(Keepalived)](#13.7 Nginx 高可用(Keepalived))
  • 附录:常用命令速查表

一、Docker 概述

1.1 虚拟化与容器化

1.1.1 虚拟化

在最早的时候,想要在线上部署一个应用,需要先购买服务器,然后安装操作系统及各种依赖环境,最后进行应用的部署。

存在的问题:

  1. 部署应用上线过程时间非常长
  2. 购买服务器的花费不菲
  3. 物理服务器的资源容易浪费
  4. 迁移和扩展比较困难

为了解决上面的问题,出现了虚拟化技术。虚拟化技术就是在操作系统上多加了一个虚拟化层(Hypervisor),可以将物理机的 CPU、内存、硬盘、网络等资源进行虚拟化,再通过虚拟化出来的空间上安装操作系统,构建虚拟的应用程序执行环境。这就是通常说的虚拟机,比如 VMware、VirtualBox、KVM 等。

虚拟化技术的优点:

  • 降低运维成本
  • 更快速地部署工作负载
  • 提高应用性能
  • 提高服务器可用性

虚拟机的缺点:

  • 占用资源较多
  • 性能较差
  • 扩展能力较差
  • 环境迁移能力较差
1.1.2 容器化

容器化起源集装箱。在运输之前一次性将货物封装到集装箱里面,然后将集装箱直接放到卡车、火车、货轮上,而且集装箱是统一标准的,自然容易机械化,节省了大量的资源、物流成本大大降低。

借鉴了集装箱的解决方案,提出了类似集装箱的方式封装应用以及它的运行环境,将应用和环境打包成一个轻量化、可移植、自包含的容器

容器的四个特点:

  • 容器共享宿主机内核
  • 容器使用内核的功能对进程进行分组和资源限制
  • 容器通过命名空间保证隔离
  • 容器像是轻量级的 VM(占用空间更少,速度更快),但不是虚拟机

1.2 Docker 概述

1.2.1 什么是 Docker

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器或 Windows 机器上,也可以实现虚拟化。

Docker 是一个主流容器管理工具,它是第一个基于 Linux 容器(LXC)的,但是现在被 runC 所取代了。Docker 容器支持分层的文件系统,因此它能够和宿主机共享系统内核。这个特性意味着即便一个 Docker 镜像基于一个 1GB 的操作系统,在同一个主机上运行 10 个容器实例并不需要消耗 10GB 的空间,相比之下,每一台虚拟机都需要一个完整的 1GB 操作系统。

Docker 开源项目背景:

Docker 是基于 Go 语言实现的开源容器项目,诞生于 2013 年年初,最初发起者是 dotCloud 公司(Docker Inc)的创始人 Solomon Hykes(所罗门·海克斯)。后来加入了 Linux 基金会,并遵循 Apache 2.0 协议,全部开源代码均在 https://github.com/docker/docker 上进行维护。

Docker 的构想是要实现 "Build, Ship and Run Any App, Anywhere",即通过对应用的封装(Packaging)、分发(Distribution)、部署(Deployment)、运行(Runtime)等生命周期进行管理,达到应用组件"一次封装,到处运行"的目的。

早期的 Docker 是基于 Linux 容器技术(LXC)的。最早的容器技术可以追溯到 1982 年 Unix 系列操作系统上的 chroot 工具。在 LXC 的基础上,Docker 进一步优化了容器的使用体验,提供了各种容器管理工具(如分发、版本、移植等)。Docker 通过引入分层文件系统构建(aufs)和高效的镜像机制,降低了迁移难度,极大地提升了用户体验。自 0.9 版本开始,Docker 开发了 libcontainer 项目,作为更广泛的容器驱动实现,从而替换掉了 LXC 的实现。

1.3 Docker 的应用场景

  1. 快速、一致地交付应用程序

    Docker 可以为开发人员提供标准化的本地工作环境,简化了开发生命周期。容器非常适合持续集成和持续交付(CI/CD)工作流程。

  2. 响应式部署和扩展

    Docker 容器可以在开发人员的本地笔记本电脑、数据中心中的物理或虚拟机上、云提供商上或混合环境中运行。Docker 的可移植性和轻量级的特性可以轻松地动态管理工作负载,并根据业务需求实时扩展或拆除应用程序和服务。

  3. 在同一个硬件上运行更多工作负载

    Docker 轻巧快速,为基于虚拟机管理程序的虚拟机提供了可行、经济高效的替代方案。

1.4 Docker 与虚拟机比较

特性 容器 虚拟机
启动速度 秒级 分钟级
性能 接近原生 较弱
内存代价 很小 较多
硬盘使用 一般为 MB 一般为 GB

Docker 的优势:

  • 更高效的利用系统资源:Docker 对系统资源的利用率更高,一个相同配置的主机往往可以运行更多数量的应用。
  • 更快速的启动时间:可以做到秒级、甚至毫秒级的启动时间。
  • 一致的运行环境:不会再出现「这段代码在我机器上没问题啊」这类问题。
  • 持续交付和部署:使用 Dockerfile 使镜像构建透明化。
  • 更轻松的迁移:可以在很多平台上运行,运行结果一致。
  • 更轻松的维护和扩展:分层存储以及镜像的技术,使得应用重复部分的复用更为容易。

二、Docker 核心组件

2.1 Docker 引擎

Docker 引擎是一个包含以下主要组件的客户端服务器应用程序:

  • 一种服务器,它是一种称为守护进程并且长时间运行的程序(dockerd)
  • REST API,用于指定程序可以用来与守护进程通信的接口
  • 一个有命令行界面(CLI)工具的客户端(docker 命令)

Docker 客户端是用户与 Docker 交互的主要方式,与 Docker 守护进程进行通信。该守护进程完成了构建、运行和分发 Docker 容器的繁重工作。Docker 客户端和守护程序在 UNIX 套接字或网络接口上使用 REST API 进行通信。Docker 守护进程侦听 Docker API 请求并管理 Docker 对象,例如镜像、容器、网络和卷等。

2.2 Docker 镜像

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

分层存储架构:

因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。镜像并非是一个像 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说由多层文件系统联合组成。

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。分层存储的特征使得镜像的复用、定制变的更为容易。

【多份笔记存在不同记录】部分笔记中 Docker 镜像描述为"相当于是一个 root 文件系统",另有笔记强调"镜像是一个虚拟的概念,由多层文件系统联合组成"。两者均正确,前者从功能角度描述,后者从技术实现角度描述。

2.3 Docker 容器

Docker 容器类似于一个轻量级的沙箱,Docker 利用容器来运行和隔离应用。容器是镜像的一个运行实例。容器可以被创建、启动、开始、停止、删除、暂停等,而这些容器都是彼此相互隔离的、互不可见的。可以把容器看做是一个简易版的 Linux 系统环境(包括 root 用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子。

容器存储层:

每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。

按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据 ,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。

2.4 Docker 仓库

Docker 仓库类似于代码仓库,它是 Docker 集中存放镜像文件的场所。一个 Docker Registry 中可以包含多个仓库(Repository),每个仓库可以包含多个标签(Tag),每个标签对应一个镜像。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

以 Ubuntu 镜像为例,Ubuntu 是仓库的名字,其内包含有不同的版本标签,如 20.04、22.04、24.04。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。

根据所存储的镜像公开分享与否,Docker 仓库可以分为**公开仓库(Public)私有仓库(Private)**两种形式。目前,最大的公开仓库是官方提供的 Docker Hub。


三、Docker 环境安装

3.1 系统环境准备

在安装 Docker 之前,需要对操作系统进行基础环境配置。

bash 复制代码
# 1. 关闭防火墙
systemctl disable --now firewalld.service

# 2. 关闭 SELinux
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=permissive/" /etc/selinux/config

# 3. 关闭交换分区(Kubernetes 环境需要)
swapoff -a
sed -i "s/.*swap.*/#&/" /etc/fstab

# 4. 安装基本软件
dnf install -y net-tools nfs-utils vim wget curl tree bash-completion device-mapper-persistent-data psmisc ipvsadm

# 5. 修改最大连接数
cat >> /etc/security/limits.conf <<EOF
* soft nofile 655350
* hard nofile 655350
* soft nproc 655350
* hard nproc 655350
* soft memlock unlimited
* hard memlock unlimited
EOF

# 6. 开启路由转发
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

# 7. 配置宿主机网卡转发(Docker 要求)
cat > /etc/sysctl.d/docker.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p /etc/sysctl.d/docker.conf

3.2 RHEL/Redhat 安装

3.2.1 配置本地仓库(离线环境)
bash 复制代码
cat > /etc/yum.repos.d/base.repo <<EOF
[BaseOS]
name=BaseOS
baseurl=/mnt/BaseOS
gpgcheck=0
[AppStream]
name=AppStream
baseurl=/mnt/AppStream
gpgcheck=0
EOF

# 挂载镜像
mount /dev/sr0 /mnt
echo '/dev/sr0 /mnt iso9660 defaults 0 0' >> /etc/fstab
3.2.2 官方安装方法
bash 复制代码
# 1. 删除旧版本
sudo dnf remove docker docker-client docker-client-latest docker-common \
    docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc

# 2. 安装仓库
dnf -y install dnf-plugins-core
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo

# 3. 安装 Docker 引擎
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 4. 启动 Docker
systemctl enable --now docker
3.2.3 阿里云镜像安装
bash 复制代码
# 1. 安装必要工具
yum install -y yum-utils

# 2. 添加软件源信息
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/rhel/docker-ce.repo

# 3. 安装 Docker
yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 4. 启动 Docker
systemctl enable --now docker
3.2.4 清华镜像安装
bash 复制代码
yum install -y yum-utils
yum-config-manager --add-repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/rhel/docker-ce.repo
yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker
3.2.5 离线安装(RPM 包)

下载地址:https://download.docker.com/linux/rhel/9/x86_64/stable/Packages/

bash 复制代码
# 1. 上传 RPM 包
# containerd.io-1.7.25-3.1.el9.x86_64.rpm
# docker-ce-28.0.1-1.el9.x86_64.rpm
# docker-ce-cli-28.0.1-1.el9.x86_64.rpm

# 2. 安装
rpm -ivh containerd.io-*.rpm
rpm -ivh docker-ce-cli-*.rpm
rpm -ivh docker-ce-*.rpm
3.2.6 官方卸载
bash 复制代码
# 1. 卸载引擎
sudo dnf remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

# 2. 删除镜像、容器和数据卷
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

3.3 Ubuntu 安装

3.3.1 官方安装
bash 复制代码
# 1. 卸载所有冲突的包
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
    sudo apt-get remove $pkg
done

# 2. 设置 Docker 的 apt 存储库
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

# 3. 安装最新版本
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
3.3.2 阿里云安装
bash 复制代码
# 1. 安装必要系统工具
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

# 2. 信任 Docker 的 GPG 公钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 3. 写入软件源信息
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 4. 安装 Docker
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
3.3.3 离线安装(Deb 包)

官网下载地址:https://download.docker.com/linux/ubuntu/dists/noble/pool/stable/amd64/

bash 复制代码
# 1. 上传并安装
sudo dpkg -i containerd.io_*.deb
sudo dpkg -i docker-ce-cli_*.deb
sudo dpkg -i docker-ce_*.deb
sudo dpkg -i docker-buildx-plugin_*.deb
sudo dpkg -i docker-compose-plugin_*.deb

# 2. 验证
docker -v
3.3.4 官方卸载
bash 复制代码
# 1. 卸载引擎
sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

# 2. 删除镜像、容器和数据卷
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

# 3. 删除认证
sudo rm /etc/apt/sources.list.d/docker.list
sudo rm /etc/apt/keyrings/docker.asc

3.4 脚本安装

bash 复制代码
# 下载安装脚本
curl -fsSL https://get.docker.com -o get-docker.sh

# 清理旧版本(Dry Run)
sh get-docker.sh --dry-run

# 安装新版本
sh get-docker.sh

# 验证
docker version

3.5 配置镜像加速

注意:2025 年开始,各大厂商都不支持 docker 镜像拉取,需要配置镜像加速器。

bash 复制代码
# 1. 配置 Docker 镜像加速
cat > /etc/docker/daemon.json <<EOF
{
    "default-ipc-mode": "shareable",
    "data-root": "/data/docker",
    "exec-opts": ["native.cgroupdriver=systemd"],
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "50"
    },
    "registry-mirrors": [
        "https://docker.1ms.run",
        "https://func.ink",
        "https://proxy.1panel.live",
        "https://docker-0.unsee.tech",
        "https://docker.zhai.cm",
        "https://a.ussh.net",
        "https://docker.melikeme.cn",
        "https://docker.hlmirror.com",
        "https://docker.xiaogenban1993.com",
        "https://docker.1panel.top",
        "https://docker.kejilion.pro",
        "https://dockerpull.cn",
        "https://docker.xuanyuan.me",
        "https://docker.anye.in",
        "https://hub.fast360.xyz"
    ]
}
EOF

# 2. 重启 Docker 服务
sudo systemctl daemon-reload
sudo systemctl restart docker

# 3. 查看是否生效
docker info | grep "Registry Mirrors" -A 5

四、Docker 核心原理

4.1 Docker 内部结构

Docker Engine 是具有以下主要组件的客户端-服务器应用程序:

  • 服务器是一种长期运行的程序,称为守护进程(dockerd)
  • REST API,指定程序可以用来与守护程序进行通信并指示其操作的接口
  • 命令行界面(CLI)客户端(docker 命令)

【多份笔记存在不同记录】部分笔记中 Docker 引擎组件的流程图中包含客户端、REST API、守护进程三层架构;另有笔记补充说明守护进程还可以与其他守护进程通信以管理 Docker 服务。

4.2 容器生命周期

4.2.1 状态与命令对应表
状态 说明 命令
created 初创建 docker create
running 运行状态 docker rundocker startdocker restartdocker unpause
stopped 停止状态 docker stopdocker kill
paused 暂停状态 docker pause
4.2.2 状态转换说明

需要根据实际情况选择的操作:

  • killed by out-of-memory(因内存不足被终止):宿主机内存被耗尽,也被称为 OOM,这时需要杀死最吃内存的容器,然后进行选择操作
  • container process exited(异常终止) :出现容器被终止后,将进入 Should restart? 选择操作:
    • yes:需要重启,容器执行 start 命令,转为运行状态
    • no:不需要重启,容器转为停止状态

4.3 命名空间(Namespace)

命名空间(namespace)是 Linux 内核的一个强大特性,为容器虚拟化的实现带来极大便利。命名空间用于实现内存、CPU、网络 IO、硬盘 IO、存储空间、文件系统、网络、PID、UID、IPC 等的相互隔离。

4.3.1 进程命名空间(PID namespace)

PID namespace 对进程 PID 重新编号,即两个不同的 namespace 下的进程可以用相同的 PID。内核为所有的 PID namespace 维护了一个树状结构,最顶层的是系统初始时创建的,被称作 root namespace(进程号 1)。父节点可以看到子节点中的进程,反过来,子节点却看不到父节点 PID namespace 的任何内容。

bash 复制代码
# 查看进程示例
[root@localhost ~]# ps -ef | grep docker
root 2335 1 0 21:01 ? 00:00:00 /usr/bin/dockerd
root 2896 2881 0 21:05 ? 00:00:00 nginx: master process nginx -g daemon off;
101 2933 2896 0 21:05 ? 00:00:00 nginx: worker process
4.3.2 网络命名空间(network namespace)

通过网络命名空间,可以实现网络隔离。网络命名空间为进程提供了一个完全独立的网络协议栈的视图,包括网络设备接口、IPv4 和 IPv6 协议栈、IP 路由表、防火墙规则、sockets 等。Docker 采用虚拟网络设备(Virtual Network Device)的方式,将不同命名空间的网络设备连接到一起。默认情况下,容器中的虚拟网卡将同本地主机上的 docker0 网桥连接在一起。

4.3.3 IPC 命名空间(IPC namespace)

进程间通信(Inter-Process Communication,IPC)涉及的 IPC 资源包括常见的信号量、消息队列和共享内存。PID Namespace 和 IPC Namespace 可以组合起来一起使用,在同一个 IPC namespace 下的进程彼此可见,不同 IPC namespace 下的进程则互相不可见。

4.3.4 挂载命名空间(Mount namespace)

类似于 chroot,将一个进程放到一个特定的目录执行。挂载命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间中的进程所看到的文件目录彼此被隔离。可以通过 /proc/[pid]/mounts 查看到所有挂载在当前 namespace 中的文件系统。

4.3.5 UTS 命名空间(UTS namespace)

UTS(UNIX Time-sharing System)命名空间允许每个容器拥有独立的主机名和域名。Docker 中,每个镜像基本上都以自身提供的服务名来命名镜像的 hostname,其原理就是利用了 UTS namespace。默认情况下,Docker 容器的主机名就是返回的容器 ID。

4.3.6 用户命名空间(User namespace)

每个容器可以有不同的用户和组 ID,也就是说可以在容器内使用特定的内部用户执行程序,而非本地系统上存在的用户。每个容器内部都可以有 root 帐号,但跟宿主机不在一个命名空间。

命名空间汇总表:

Namespace 系统参数调用 隔离内容
UTS CLONE_NEWUTS 主机名和域名
IPC CLONE_NEWIPC 信号量、消息队列和共享内存
PID CLONE_NEWPID 进程编号
Network CLONE_NEWNET 网络设备、网络栈、端口等
Mount CLONE_NEWNS 挂载点(文件系统)
User CLONE_NEWUSER 用户和用户组

4.4 控制组(Cgroups)

控制组(Cgroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。默认情况下,容器没有资源限制,可以使用 docker stats 容器ID 查看容器状态。Docker 提供了一些方法来控制容器可以使用多少内存或 CPU。

4.4.1 CPU 限制
选项 描述
--cpus 指定一个容器可以使用多少可用的 CPU 资源。例如 --cpus="1.5"
--cpu-period 指定 CPU CFS 调度程序周期,与 --cpu-quota 一起使用
--cpu-quota 在容器上设置 CPU CFS 配额
--cpuset-cpus 限制容器可以使用的特定 CPU 或内核,如 0-31,3
--cpu-shares 设置 CPU 权重(默认 1024),这是一个软限制
bash 复制代码
docker run --cpus 1.5 --rm -d --network host nginx:1.28.1
4.4.2 内存限制
选项 描述
-m, --memory 容器可以使用的最大内存量,最小允许值为 4m
--memory-swap 允许此容器交换到磁盘的内存量
--memory-swappiness 调整内存交换倾向(0-100)
--memory-reservation 软限制,低于 --memory
--oom-kill-disable 禁用 OOM 杀手
bash 复制代码
docker run --rm -d --network host -m 100m nginx:1.28.1
4.4.3 磁盘读写限制

BlockIO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽。

bash 复制代码
docker run -it --device-write-bps /dev/sda:30MB ubuntu

4.5 联合文件系统(UnionFS)

联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。UnionFS 是实现 Docker 镜像的技术基础。

4.5.1 Docker 存储

Docker 目前通过插件化方式支持多种文件系统后端。当 Docker 利用镜像启动一个容器时,将在镜像文件系统的最顶端再挂载一个新的可读写的层给容器。容器中的内容更新将会发生在可读写层。

bash 复制代码
# 查看镜像分层
docker history nginx:1.28.1
4.5.2 多种文件系统比较
文件系统 说明
AUFS 最早支持的文件系统,对 Debian/Ubuntu 支持好,成熟度很高
OverlayFS 类似于 AUFS,性能更好,已合并到内核,未来会取代 AUFS
Device Mapper Redhat 公司和 Docker 团队一起开发,内核支持,性能略慢
btrfs 参考 zfs 等特性设计,试图未来取代 Device Mapper
vfs 基于普通文件系统的中间层抽象,性能差,比较占用空间
zfs 最初设计为 Solaris 10 上的写时文件系统,对 Linux 支持不够成熟

通过 docker info 的 Storage Driver 可以查看当前使用的文件系统。


五、Docker 基本操作

5.1 镜像管理

5.1.1 搜索镜像
bash 复制代码
docker search 镜像名称[:TAG]

注意:只能搜索 Docker Hub 仓库镜像,私有仓库无法搜索到。

bash 复制代码
[root@localhost ~]# docker search mysql
NAME                     DESCRIPTION                           STARS
mysql                    MySQL is a widely used...             9760
mariadb                  MariaDB is a community-developed...   3564
mysql/mysql-server       Optimized MySQL Server Docker...      717
5.1.2 拉取镜像
bash 复制代码
docker pull 镜像名称[:TAG]

如果不显式指定 TAG,则默认会选择 latest 标签。镜像的仓库名称中还应添加仓库地址(registry)作为前缀,默认使用的是 Docker Hub 服务,该前缀可以忽略。

bash 复制代码
[root@localhost ~]# docker pull mysql:8.4.4
8.4.4: Pulling from library/mysql
8559a31e96f4: Pull complete
d51ce1c2e575: Pull complete
...
5.1.3 查看本地镜像
bash 复制代码
docker images
# 或
docker image ls
bash 复制代码
[root@localhost ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
mysql         8.4.4     9cfcce23593a   6 weeks ago   448MB
5.1.4 添加镜像标签
bash 复制代码
docker tag 原镜像名[:TAG] 目标镜像名[:TAG]

如果 TAG 缺省,默认 latest。

5.1.5 查看镜像信息
bash 复制代码
docker inspect 镜像名称[:TAG]
docker history 镜像名称[:TAG]

返回的是一个 JSON 格式的消息,如果只要其中一项内容时,可以使用 -f 参数来指定:

bash 复制代码
docker inspect mysql:8.4.4 -f {{".Architecture"}}
5.1.6 导出镜像
bash 复制代码
docker save -o 本地路径/文件名称 镜像名称[:TAG] [...]
bash 复制代码
# 导出单个镜像
docker image save busybox > docker-busybox.tar.gz

# 导出多个镜像
docker image save busybox:latest nginx:1.28.1 -o /opt/busybox_nginx.tar.gz
5.1.7 导入镜像
bash 复制代码
docker load -i 本地路径/文件名称
bash 复制代码
[root@localhost ~]# docker image load -i docker-busybox.tar.gz
23bc2b70b201: Loading layer 1.37 MB/1.37 MB
Loaded image: busybox:latest
5.1.8 查看悬空镜像

悬虚镜像(Dangling Images)是指那些没有标签没有被任何容器使用的镜像。

bash 复制代码
docker images -f "dangling=true"
5.1.9 删除悬空镜像
bash 复制代码
# 删除所有悬空镜像
docker image prune -f

# 删除所有未被标签引用的镜像(包括悬空镜像和未使用的中间层镜像)
docker image prune -a -f

# 或者使用 docker rmi
docker rmi $(docker images -f "dangling=true" -q)
5.1.10 删除镜像
bash 复制代码
docker rmi 镜像名称[:TAG] [...]

当同一个镜像拥有多个标签的时候,docker rmi 命令只是删除该镜像多个标签中的指定标签而已,并不影响镜像文件。如果镜像只剩下一个标签,使用 docker rmi 会彻底删除镜像。如果有容器正在运行该镜像,则不能删除,可使用 -f 强行删除(不推荐)。

bash 复制代码
# 删除单个标签
docker rmi mysql:8.4.4

# 彻底删除镜像(只剩一个标签时)
docker rmi mysql8:latest

5.2 容器管理

以下以 Nginx 镜像为例进行演示:

bash 复制代码
[root@localhost ~]# docker pull nginx:1.28.1
1.28.1: Pulling from library/nginx
ae9b786964d0: Pull complete
...
Status: Downloaded newer image for nginx:1.28.1
5.2.1 查看容器
bash 复制代码
# 查看正在运行的容器
docker ps

# 查看所有容器(包括停止的)
docker ps -a
5.2.2 创建容器
bash 复制代码
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
bash 复制代码
[root@localhost ~]# docker create -it nginx:1.28.1
6242066cddcadcd51777900a290f885be1752ff65e567e5b8af4b7a6ab0da3ea

# 创建停止后自动删除的容器
[root@localhost ~]# docker create --rm nginx:1.28.1
c6109c3eccbbce0e190c403a8df2f44ccacc8158b2cc6cd5da4833ce15b0a284
5.2.3 启动容器
bash 复制代码
docker start 容器名/ID
5.2.4 创建并启动容器
bash 复制代码
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

常用选项:

选项 说明
-t 打开一个终端
-i 交互式运行
-d 后台运行
-p 端口映射,格式:<hostPort>:<containerPort>
--name 指定容器名称
--rm 容器停止后自动删除
--network 指定容器使用的网络
5.2.5 停止容器
bash 复制代码
# 优雅停止(发送 SIGTERM,默认等待 10 秒)
docker stop 容器名/ID [-t 时间]

# 强制停止(发送 SIGKILL)
docker kill 容器名/ID

# 重启容器
docker restart 容器名/ID

# 停止所有容器
docker stop $(docker ps -qa)
5.2.6 进入容器
bash 复制代码
docker exec -it 容器名/ID /bin/bash
bash 复制代码
[root@localhost ~]# docker exec -it strange_wescoff /bin/bash
root@6242066cddca:/# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>
root@6242066cddca:/# exit
exit
5.2.7 暂停与恢复
bash 复制代码
# 暂停容器
docker pause 容器名/ID

# 恢复暂停的容器
docker unpause 容器名/ID
5.2.8 查看容器信息
bash 复制代码
# 查看容器详细信息(JSON 格式)
docker inspect 容器名/ID

# 查看容器资源使用情况
docker stats 容器名/ID

# 查看容器进程
docker top 容器名/ID

# 查看容器日志
docker logs 容器名/ID
5.2.9 复制容器文件
bash 复制代码
# 将文件从容器复制出来
docker cp 容器名:容器内路径 宿主机路径

# 将文件复制到容器内部
docker cp 宿主机路径 容器名:容器内路径
bash 复制代码
# 复制文件到容器
docker cp /root/index2.html nginx:/usr/share/nginx/html/

# 从容器复制文件到宿主机
docker cp nginx:/usr/share/nginx/html/50x.html .
5.2.10 删除容器
bash 复制代码
# 删除指定容器(只能删除停止状态的容器)
docker rm 容器名/ID

# 强制删除运行中的容器
docker rm -f 容器名/ID

# 删除所有容器
docker rm -f $(docker ps -qa)
5.2.11 通过容器创建镜像
bash 复制代码
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
bash 复制代码
# 修改容器内容
docker exec -it nginx /bin/bash
echo "welcome to nginx!" > /usr/share/nginx/html/index.html

# 创建新镜像
docker commit nginx nginx1:1.28.1

# 使用新镜像运行容器
docker run --name nginx1 -d -p 80:80 nginx1:1.28.1
5.2.12 重命名容器
bash 复制代码
docker rename 旧容器名 新容器名
5.2.13 查看容器暴露端口
bash 复制代码
docker port 容器名
bash 复制代码
[root@localhost ~]# docker port nginx
80/tcp -> 0.0.0.0:80
80/tcp -> [::]:80
5.2.14 导出与导入容器
bash 复制代码
# 导出容器为归档文件
docker export -o 文件.tar 容器名

# 导入归档文件为镜像
docker import 文件.tar 镜像名[:TAG]
bash 复制代码
# 导出容器
docker export -o nginx-1.28.1.tar.gz nginx

# 导入为镜像
docker import /root/nginx-1.28.1.tar.gz nginx2:1.28.1
5.2.15 事件监听
bash 复制代码
docker events [OPTIONS]
bash 复制代码
# 监听容器停止事件
docker events -f 'event=stop'
5.2.16 检查文件变更
bash 复制代码
docker diff 容器名
bash 复制代码
[root@localhost ~]# docker diff nginx
C /run
D /run/nginx.pid
C /etc
C /etc/nginx
...
5.2.17 连接容器
bash 复制代码
docker attach [OPTIONS] CONTAINER
bash 复制代码
# 连接到正在运行的容器
docker attach nginx

# 在连接到容器后执行命令
docker attach --sig-proxy=false nginx

5.3 仓库管理

5.3.1 登录仓库
bash 复制代码
docker login -u 用户名 -p 密码 [SERVER]
bash 复制代码
[root@localhost ~]# docker login -u haha -p 123465
Login Succeeded
5.3.2 登出仓库
bash 复制代码
docker logout [SERVER]
5.3.3 认证文件

Mac/Win 机器上的是隐藏密码的,但是在 Linux 下是显示密码的,只不过进行了 base64 编码,只要拷贝此文件到其他机器指定目录下(/root/.docker/config.json)即可免登录。


六、容器网络管理

6.1 网络模式概览

Docker 的网络定义:

模式 说明
bridge 默认值,在 Docker 网桥 docker0 上为容器创建新的网络
host 容器直接使用宿主机的网络(不隔离网络命名空间)
container 与指定容器共享网络命名空间
none 禁用所有网络,只有 lo 接口
overlay 覆盖网络,用于 Swarm 集群中跨主机通信
macvlan 为容器分配 MAC 地址,使其在网络上显示为物理设备

查看已安装的网络驱动:

bash 复制代码
[root@localhost ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
f23b4899add1   bridge    bridge    local
65520497f693   host      host      local
a0c5f18e0f04   none      null      local

6.2 Bridge 网络模式

6.2.1 模式介绍

当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,默认地址 172.17.0.0/16。从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡 veth pair 设备,一端放在容器中(eth0),另一端放在主机中(vethxxx),并加入到 docker0 网桥中。

bash 复制代码
# 查看网桥
[root@localhost ~]# brctl show
bridge name    bridge id          STP enabled    interfaces
docker0        8000.920bf364e96c  no             veth4fc6117
                                                  veth645d9ec

前置工具安装:

bash 复制代码
dnf install -y https://mirrors.aliyun.com/epel/epel-release-latest-9.noarch.rpm
dnf install -y bridge-utils
6.2.2 模式演示
bash 复制代码
docker run --name bx1 -it --network bridge --rm busybox:latest
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> ...
2: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # ping 192.168.72.169
64 bytes from 192.168.72.169: seq=0 ttl=64 time=0.194 ms

6.3 Host 网络模式

6.3.1 模式介绍

如果启动容器的时候使用 host 模式,容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器不会虚拟出自己的网卡,而是使用宿主机的 IP 和端口。

6.3.2 模式演示
bash 复制代码
docker run --name bx2 -it --rm --network host busybox:latest
/ # ip a
# 输出与宿主机一致,包含 ens160、docker0 等

6.4 Container 网络模式

6.4.1 模式介绍

新创建的容器和已经存在的一个容器共享一个 Network Namespace。两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

6.4.2 模式演示
bash 复制代码
# 终端1:创建容器 bx1
docker run --name bx1 -it --rm busybox:latest
/ # echo "hello world bx1" >> /tmp/index.html
/ # httpd -h /tmp/
/ # netstat -lntup
tcp        0      0 :::80                   :::*                    LISTEN      9/httpd

# 终端2:创建容器 bx2,共享 bx1 的网络
docker run --name bx2 -it --rm --network container:bx1 busybox:latest
/ # ip a
# IP 与 bx1 相同
/ # wget -O - -q 127.0.0.1
hello world bx1
/ # ls /tmp/
# 文件系统不共享,目录为空

6.5 None 网络模式

6.5.1 模式介绍

使用 none 模式,Docker 容器拥有自己的 Network Namespace,但是并不进行任何网络配置。没有网卡、IP、路由等信息,只有 lo 网络接口。

6.5.2 模式演示
bash 复制代码
docker run --name bx3 --rm -it --network none busybox:latest
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> ...
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 路由表为空
/ # ping 192.168.72.169
ping: sendto: Network is unreachable

6.6 端口映射与外部访问

6.6.1 随机映射(-P)
bash 复制代码
docker run --name nginx -P -d nginx:1.28.1
docker ps
PORTS: 0.0.0.0:32768->80/tcp, [::]:32768->80/tcp
6.6.2 映射所有端口地址

格式:hostPort:containerPort

bash 复制代码
docker run --name nginx -d --network bridge -p 80:80 nginx:1.28.1
6.6.3 映射到指定地址的指定端口

格式:IP:hostPort:containerPort

bash 复制代码
docker run --name nginx -d -p 127.0.0.1:8080:80 nginx:1.28.1
# 只能通过 127.0.0.1:8080 访问
6.6.4 映射指定地址的任意端口

格式:IP::containerPort

bash 复制代码
docker run --rm -d -p 127.0.0.1::80 nginx:1.28.1
6.6.5 查看端口映射
bash 复制代码
docker port nginx

【多份笔记存在不同记录】部分笔记示例中使用 nostalgi_c_morse 作为容器名,另一些使用 nginx。两者均为示例容器名,实际使用时替换为具体容器名即可。

6.7 自定义网络与容器互联

6.7.1 新建网络
bash 复制代码
# 创建 bridge 类型网络
docker network create -d bridge my-net

# 查看网络列表
docker network ls

# 查看网络详细信息
docker network inspect my-net
6.7.2 连接容器
bash 复制代码
# 在 my-net 网络中创建容器
docker run --name bx1 -it --rm --network my-net busybox:latest sh
docker run --name bx2 -it --rm --network my-net busybox:latest sh

# 通过容器名互相访问(DNS 自动解析)
# 在 bx1 中:
/ # ping bx2
PING bx2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.257 ms

# 在 bx2 中:
/ # ping bx1
PING bx1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.087 ms
6.7.3 不同网络隔离测试
bash 复制代码
# 在默认 bridge 网络中创建容器
docker run -it --name bx3 --network bridge --rm busybox:latest sh
/ # ping bx1
ping: bad address 'bx1'
# 无法通信,因为不在同一个网络
6.7.4 删除网络
bash 复制代码
docker network rm my-net

⚠️ 注意:不要去删除原生的三个网络类型(bridge、host、none)。

6.8 DNS 配置

6.8.1 全局 DNS 配置

/etc/docker/daemon.json 文件中配置 DNS 信息:

json 复制代码
{
    "dns": [
        "223.5.5.5",
        "8.8.8.8"
    ]
}
bash 复制代码
systemctl daemon-reload
systemctl restart docker

验证:

bash 复制代码
docker run -it --rm busybox:latest cat /etc/resolv.conf
# Generated by Docker Engine.
nameserver 223.5.5.5
nameserver 8.8.8.8
6.8.2 自定义主机名
bash 复制代码
docker run --name bx -it --rm --hostname busybox busybox:latest
/ # cat /etc/hostname
busybox

# 或使用 -h 简写
docker run --name bx -it --rm -h busybox busybox:latest
6.8.3 自定义 DNS
bash 复制代码
docker run --name bx -it --rm --hostname busybox --dns 172.17.0.9 busybox:latest
/ # cat /etc/resolv.conf
nameserver 172.17.0.9
6.8.4 自定义搜索域
bash 复制代码
docker run --name bx -it --rm --hostname busybox --dns 172.17.0.9 --dns-search www.example.com busybox:latest
/ # cat /etc/resolv.conf
nameserver 172.17.0.9
search www.example.com

七、容器数据管理

默认情况下,在容器内创建的所有文件都存储在可写容器层上。这样做的问题有:

  • 当容器不再存在时,数据不会持久保存
  • 容器的可写层与运行容器的主机紧密耦合
  • 写入容器的可写层需要存储驱动程序来管理文件系统,性能比直接写入主机要低

Docker 为容器提供了两种选项来持久存储数据:数据卷(Data Volumes)数据卷容器(Data Volume Containers) ,也称为 bind mounts(绑定挂载)。如果在 Linux 上运行 Docker,还可以使用 tmpfs 挂载

Volume、Bind 和 tmpfs 的异同:

  • 相同之处:无论选择使用哪种类型,数据在容器内看起来都是相同的,被视为容器文件系统中的目录或单个文件。
  • 不同之处
    • 卷(Volume) :存储在由 Docker 管理的主机文件系统中(/var/lib/docker/volumes/),非 Docker 进程不应该修改这部分文件系统。
    • 绑定挂载(Bind Mount):把主机的本地目录挂载到容器中某个挂载点,可以存储在主机系统的任何位置。
    • tmpfs 挂载:仅存储在主机系统的内存中,而不会写入主机系统的文件系统。

7.1 数据管理命令

bash 复制代码
docker volume --help
# Commands:
#   create      Create a volume
#   inspect     Display detailed information on one or more volumes
#   ls          List volumes
#   prune       Remove unused local volumes
#   rm          Remove one or more volumes

7.2 数据卷(Volume)操作

7.2.1 创建数据卷
bash 复制代码
docker volume create myvol
7.2.2 查看数据卷
bash 复制代码
docker volume ls
docker volume inspect myvol
# 输出包含 Mountpoint: /var/lib/docker/volumes/myvol/_data
7.2.3 使用数据卷
bash 复制代码
# 使用创建好的数据卷
docker run --name nginx -d -p 80:80 -v myvol:/usr/share/nginx/html nginx:1.28.1

# 挂载宿主机目录
docker run --name nginx -d -p 80:80 -v /opt/nginx/html:/usr/share/nginx/html nginx:1.28.1

# 只读挂载
docker run --name nginx -d -p 80:80 -v /opt/nginx/html:/usr/share/nginx/html:ro nginx:1.28.1
7.2.4 验证数据共享
bash 复制代码
# 在宿主机修改数据
echo 1111 > /var/lib/docker/volumes/myvol/_data/index.html

# 进入容器查看
docker exec -it nginx bash
cat /usr/share/nginx/html/index.html  # 1111

# 在容器内修改
echo 2222 > /usr/share/nginx/html/index.html

# 在宿主机查看
cat /var/lib/docker/volumes/myvol/_data/index.html  # 2222
7.2.5 删除数据卷
bash 复制代码
# 必须先停止并删除关联的容器
docker stop nginx && docker rm nginx

# 删除数据卷
docker volume rm myvol

# 清理未使用的数据卷
docker volume prune

7.3 数据卷容器(Data Volume Containers)

7.3.1 创建数据卷容器
bash 复制代码
# 创建宿主机目录
mkdir /data/nginx

# 创建数据卷容器
docker create -v /data/nginx:/data --name voldata nginx:1.28.1

# 验证挂载
docker inspect voldata
# "Mounts": [{"Type": "bind", "Source": "/data/nginx", "Destination": "/data"}]
7.3.2 挂载数据卷容器
bash 复制代码
docker run --name n1 --volumes-from voldata --rm -d nginx:1.28.1
docker run --name n2 --volumes-from voldata --rm -d nginx:1.28.1
7.3.3 验证数据共享
bash 复制代码
# 在 n1 中创建文件
docker exec -it n1 bash
cd /data
echo haha > n1.txt

# 在 n2 中查看
docker exec -it n2 bash
cat /data/n1.txt  # haha

# 在宿主机修改
echo 22222 >> /data/nginx/n1.txt

# n1 和 n2 中都能看到更新
7.3.4 只读绑定安装
bash 复制代码
# 使用 ro 选项
docker run -d --name devtest -v "$(pwd)"/target:/app:ro nginx:1.28.1

# 验证
docker inspect devtest
# "Mounts": [{"Type":"bind","Source":"/root/target","Destination":"/app","Mode":"ro","RW":false}]

7.4 tmpfs 挂载

bash 复制代码
# 使用 --tmpfs 标志
docker run -d --name tmptest --tmpfs /app nginx:1.28.1

# 使用 --mount 标志(支持更多选项)
docker run -d --name tmptest \
    --mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
    nginx:1.28.1

7.5 数据卷备份与恢复

7.5.1 备份
bash 复制代码
# 创建备份目录
mkdir /root/backup

# 备份数据卷容器
docker run --rm --volumes-from voldata \
    -v /root/backup:/backup \
    nginx:1.28.1 \
    tar -zcf /backup/data.tar.gz /data

# 查看备份文件
ls /backup/data.tar.gz
tar -tf /backup/data.tar.gz

注意:tar 命令是在容器内部执行的,路径应该是容器内部的路径。

7.5.2 恢复
bash 复制代码
# 删除测试数据
docker exec -it n1 bash
rm -rf /data/*

# 恢复数据
docker run --rm --volumes-from voldata \
    -v /root/backup:/backup \
    nginx:1.28.1 \
    tar -zxf /backup/data.tar.gz -C /

八、Dockerfile 镜像构建

8.1 什么是 Dockerfile

Dockerfile 是一个纯文本文件,包含一系列用于构建 Docker 镜像的指令。Dockerfile 中的每一行都代表镜像构建过程中的一个步骤。

Dockerfile 的关键组件:

  1. 基础镜像:构建起点
  2. 应用程序代码与依赖项:添加代码并安装依赖
  3. 命令与配置项:执行命令、设置环境变量、暴露端口

为什么 Dockerfile 很重要?

  • 标准化应用程序的构建和部署方式
  • 确保在不同环境(开发、测试、生产)中的一致性
  • 使应用程序具有可移植性且更易于管理
  • 实现 CI/CD 自动化
  • 基础设施版本控制

8.2 常用指令详解

8.2.1 FROM

指定基础镜像,必须是 Dockerfile 中非注释行的第一个指令。

dockerfile 复制代码
FROM python:3.14-slim
8.2.2 COPY

从宿主机复制文件或目录到镜像中。

dockerfile 复制代码
COPY <src>... <dest>
# 或
COPY ["<src>",... "<dest>"]
dockerfile 复制代码
COPY requirements.txt /app/
COPY . /app/

如果目标路径不存在,会自动创建。

8.2.3 RUN

在构建过程中执行命令,常用于安装软件包。

dockerfile 复制代码
RUN <command>
# 或
RUN ["<executable>", "<param1>", "<param2>"]
dockerfile 复制代码
RUN apt-get update \
    && apt-get install -y --no-install-recommends gcc \
    && rm -rf /var/lib/apt/lists/*
8.2.4 CMD

指定容器启动时运行的默认命令。Dockerfile 中可以有多个 CMD 指令,但仅最后一个会生效。

dockerfile 复制代码
CMD <command>
CMD ["<executable>","<param1>","<param2>"]
CMD ["<param1>","<param2>"]
dockerfile 复制代码
CMD ["flask", "run", "--host=0.0.0.0"]
8.2.5 WORKDIR

为后续指令设置工作目录。

dockerfile 复制代码
WORKDIR /app
8.2.6 ADD

类似于 COPY,但支持 TAR 文件和 URL 路径。

dockerfile 复制代码
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]

与 COPY 的区别:

  1. 都可以将主机上的资源复制到镜像中
  2. COPY 只能从本地读取资源;ADD 还支持从远程 URL 获取资源
  3. 满足同等功能的情况下,推荐使用 COPY 指令
  4. ADD 更擅长读取本地 tar 文件并解压缩
  5. 当要读取 URL 远程资源时,建议使用 RUN 指令中的 wget 或 curl
dockerfile 复制代码
ADD http://nginx.org/download/nginx-1.28.1.tar.gz .
ADD nginx-1.27.4.tar.gz .
8.2.7 EXPOSE

声明容器监听的端口,用于文档化和端口映射。

dockerfile 复制代码
EXPOSE <port>[/<protocol>] [<port>[/<protocol>]]...
dockerfile 复制代码
EXPOSE 5000
EXPOSE 80 443
EXPOSE 11211/udp 11211/tcp
8.2.8 ENV

定义环境变量,可被后续指令调用。

dockerfile 复制代码
ENV <key> <value>
ENV <key>=<value> [<key>=<value>...]
dockerfile 复制代码
ENV NGINX_VERSION=1.28.1
ENV FLASK_APP=app.py FLASK_ENV=production
8.2.9 VOLUME

声明数据卷挂载点。

dockerfile 复制代码
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]
dockerfile 复制代码
VOLUME /app/nginx-1.27.4

如果挂载点目录路径下此前有文件存在,docker run 命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中。

8.2.10 ENTRYPOINT

与 CMD 类似,但不会被 docker run 命令行参数覆盖。命令行参数会作为参数传递给 ENTRYPOINT。

dockerfile 复制代码
ENTRYPOINT <command>
ENTRYPOINT ["<executable>", "<param1>", "<param2>"]
dockerfile 复制代码
ENTRYPOINT ["flask", "run", "--host=0.0.0.0"]
8.2.11 ARG

构建参数,可在构建时动态配置。

dockerfile 复制代码
ARG version="1.0"
bash 复制代码
docker build --build-arg version=2.0 -t myapp:2.0 .
8.2.12 LABEL

添加元数据。

dockerfile 复制代码
LABEL maintainer="1062080730@qq.com"
LABEL author="jock"
8.2.13 HEALTHCHECK

设置健康检查。

dockerfile 复制代码
HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost:5000/ || exit 1
8.2.14 USER

切换用户。

dockerfile 复制代码
USER appuser

8.3 中级概念

8.3.1 多阶段构建

通过分离构建和运行时环境来创建精简的生产镜像。

dockerfile 复制代码
# 第一阶段:构建
FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app

# 第二阶段:运行时(更小镜像)
FROM alpine:3.23
COPY --from=builder /app/app /usr/local/bin/
EXPOSE 443
CMD ["app"]

优势:

  • 更小的生产环境镜像
  • 将构建工具排除在运行时环境之外,提升安全性
8.3.2 环境变量使用
dockerfile 复制代码
ENV APP_ENV=production
CMD ["node", "server.js", "--env", "${APP_ENV}"]

运行时覆盖:

bash 复制代码
docker run -e APP_ENV=development myapp

8.4 高级技巧

8.4.1 优化镜像大小

1. 使用更小体积的基础镜像

dockerfile 复制代码
FROM python:3.14-alpine
# 或
FROM python:3.14-slim

2. 最小化层数

dockerfile 复制代码
# 不推荐(多层)
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean

# 推荐(一层)
RUN apt-get update && apt-get install -y curl && apt-get clean
8.4.2 安全最佳实践

1. 避免使用 root 用户

dockerfile 复制代码
RUN adduser --disabled-password appuser
USER appuser

2. 使用可信的基础镜像

dockerfile 复制代码
FROM nginx:stable

3. 扫描镜像漏洞

bash 复制代码
trivy image myimage

8.5 调试与故障排除

bash 复制代码
# 增量构建特定阶段
docker build --target builder -t debug-image

# 检查镜像层
docker history <image_id>

# 查看容器日志
docker logs <container_id>

# 使用 .dockerignore 过滤不必要文件
# .dockerignore 内容:
Dockerfile
node_modules
*.log

8.6 编写最佳实践

  1. 固定镜像版本 :避免使用 latest 标签
  2. 优化分层:合并指令以减少层数
  3. 使用 .dockerignore 文件:防止不必要文件被包含
  4. 保持镜像轻量 :使用 alpineslim 版本
  5. 添加元数据:使用 LABEL 添加版本、作者等信息
  6. 使用非 root 用户:增强安全性
  7. 清理临时文件:减少镜像大小

8.7 常见错误避免

错误 解决方案
使用大型基础镜像 使用 alpine 或 slim 版本
未能利用多阶段构建 分离构建和运行环境
硬编码敏感信息 使用环境变量或密钥管理工具
安装后未清理 在同一条 RUN 中清理缓存
未添加注释 添加有意义的注释

8.8 镜像构建与管理

bash 复制代码
# 构建镜像
docker build -t myapp:1.0 .

# 指定 Dockerfile
docker build -f Dockerfile.prod -t myapp:1.0 .

# 使用构建参数
docker build --build-arg VERSION=2.0 -t myapp:2.0 .

# 不缓存构建
docker build --no-cache -t myapp:1.0 .

# 多阶段构建指定目标阶段
docker build --target builder -t myapp:builder .

# 查看镜像分层
docker history myapp:1.0

九、Dockerfile 案例实战

9.1 基于 CentOS 构建 Nginx 镜像

1. 创建项目目录

bash 复制代码
mkdir demo1 && cd demo1

2. 编写 Dockerfile

dockerfile 复制代码
FROM centos:7 AS builder

RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak

RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \
    curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo

RUN yum install -y wget make gcc gcc-c++ pcre-devel zlib-devel openssl-devel && yum clean all

RUN wget https://nginx.org/download/nginx-1.28.1.tar.gz && \
    tar -zxf nginx-1.28.1.tar.gz && \
    rm nginx-1.28.1.tar.gz

WORKDIR /nginx-1.28.1

ARG version="1.0"

RUN ./configure --prefix=/usr/local/nginx --with-http_ssl_module && \
    make && \
    make install

RUN mkdir -p /usr/local/nginx/html && \
    echo "hello jock | welcome to nginx! | version ${version}" > /usr/local/nginx/html/index.html

FROM centos:7

LABEL maintainer="1062080730@qq.com"
LABEL author="jock"

COPY --from=builder /usr/local/nginx /usr/local/nginx

RUN echo '#!/bin/bash' > /start.sh && \
    echo 'hostname > /usr/local/nginx/html/hostname.html' >> /start.sh && \
    echo '/usr/local/nginx/sbin/nginx -g "daemon off;"' >> /start.sh && \
    chmod +x /start.sh

EXPOSE 80 443

ENTRYPOINT ["/start.sh"]

3. 构建镜像

bash 复制代码
docker build -t myapp:1.0 .

4. 运行容器并测试

bash 复制代码
docker run --rm -d --name myapp -p 80:80 myapp:1.0
curl localhost
# hello jock | welcome to nginx! | version 1.0
curl localhost/hostname.html
# c245dd948565

9.2 基于 Debian 构建 Nginx 镜像

1. 创建项目目录并上传依赖库

bash 复制代码
mkdir demo2 && cd demo2
ls
# openssl-3.0.16.tar.gz  pcre2-10.45.tar.gz  zlib-1.3.1.tar.gz

2. 编写 Dockerfile

dockerfile 复制代码
FROM debian:bullseye-slim AS builder

ENV NGINX_VERSION=1.28.1 \
    OPENSSL_VERSION=3.0.16 \
    PCRE_VERSION=10.45 \
    ZLIB_VERSION=1.3.1

RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    sed -i 's|security.debian.org|mirrors.aliyun.com/debian-security|g' /etc/apt/sources.list

RUN apt-get update && \
    apt-get install -y \
    build-essential \
    wget \
    libpcre3 \
    libpcre3-dev \
    zlib1g-dev \
    libssl-dev \
    libgd-dev \
    && rm -rf /var/lib/apt/lists/*

COPY ./openssl-3.0.16.tar.gz /usr/local/src
COPY ./pcre2-10.45.tar.gz /usr/local/src
COPY ./zlib-1.3.1.tar.gz /usr/local/src

WORKDIR /usr/local/src

RUN wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
    tar -xzf nginx-${NGINX_VERSION}.tar.gz && \
    tar -xzf openssl-${OPENSSL_VERSION}.tar.gz && \
    tar -xzf pcre2-${PCRE_VERSION}.tar.gz && \
    tar -xzf zlib-${ZLIB_VERSION}.tar.gz && \
    rm -f *.tar.gz

WORKDIR /usr/local/src/nginx-${NGINX_VERSION}

RUN ./configure --prefix=/usr/local/nginx \
    --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --user=nginx \
    --group=nginx \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_gzip_static_module \
    --with-http_stub_status_module \
    --with-threads \
    --with-stream \
    --with-openssl=../openssl-${OPENSSL_VERSION} \
    --with-pcre=../pcre2-${PCRE_VERSION} \
    --with-zlib=../zlib-${ZLIB_VERSION} && \
    make -j $(nproc) && \
    make install

FROM debian:bullseye-slim

LABEL maintainer="1062080730@qq.com"
LABEL author="jock"

RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \
    sed -i 's|security.debian.org|mirrors.aliyun.com/debian-security|g' /etc/apt/sources.list

RUN apt-get update && \
    apt-get install -y \
    libpcre3 \
    zlib1g \
    openssl \
    && rm -rf /var/lib/apt/lists/*

RUN groupadd -r nginx && \
    useradd -r -g nginx -s /sbin/nologin -d /nonexistent -c "nginx user" nginx

COPY --from=builder /usr/local/nginx /usr/local/nginx
COPY --from=builder /usr/sbin/nginx /usr/sbin/nginx
COPY --from=builder /etc/nginx /etc/nginx
COPY --from=builder /var/log/nginx /var/log/nginx

RUN mkdir -p /usr/local/nginx/html && \
    echo "hello jock | welcome to nginx! | version 1.0" > /usr/local/nginx/html/index.html

RUN echo '#!/bin/bash' > /start.sh && \
    echo 'hostname > /usr/local/nginx/html/hostname.html' >> /start.sh && \
    echo '/usr/sbin/nginx -g "daemon off;"' >> /start.sh && \
    chmod +x /start.sh

WORKDIR /usr/local/nginx

EXPOSE 80 443

ENTRYPOINT ["/start.sh"]

3. 构建并运行

bash 复制代码
docker build -t myapp:2.0 .
docker run --rm --name myapp -d -p 80:80 myapp:2.0
curl localhost
# hello jock | welcome to nginx! | version 1.0

9.3 基于 Alpine 构建 Nginx 镜像

dockerfile 复制代码
FROM alpine:3.23 AS builder

RUN apk add --no-cache \
    build-base \
    pcre-dev \
    zlib-dev \
    openssl-dev \
    linux-headers \
    wget \
    tar

ARG version=1.0
ENV NGINX_VERSION=1.28.1

RUN wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
    tar -xzf nginx-${NGINX_VERSION}.tar.gz && \
    rm nginx-${NGINX_VERSION}.tar.gz

WORKDIR /nginx-${NGINX_VERSION}

RUN ./configure --prefix=/usr/local/nginx \
    --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --user=nginx \
    --group=nginx \
    --with-threads \
    --with-file-aio && \
    make && \
    make install

FROM alpine:3.23

COPY --from=builder /usr/local/nginx /usr/local/nginx
COPY --from=builder /usr/sbin/nginx /usr/sbin/nginx
COPY --from=builder /etc/nginx /etc/nginx
COPY --from=builder /var/log/nginx /var/log/nginx

RUN addgroup -S nginx && \
    adduser -S -G nginx -s /sbin/nologin -D -H nginx

RUN mkdir -p /usr/local/nginx/html && \
    echo "hello jock | welcome to nginx! | version ${version}" > /usr/local/nginx/html/index.html

RUN echo '#!/bin/sh' > /start.sh && \
    echo 'hostname > /usr/local/nginx/html/hostname.html' >> /start.sh && \
    echo '/usr/sbin/nginx -g "daemon off;"' >> /start.sh && \
    chmod +x /start.sh

RUN apk add --no-cache pcre zlib openssl && \
    mkdir -p /var/cache/nginx && \
    chown -R nginx:nginx /var/cache/nginx && \
    chown -R nginx:nginx /var/log/nginx && \
    ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log

WORKDIR /usr/local/nginx

EXPOSE 80 443

ENTRYPOINT ["/start.sh"]
bash 复制代码
docker build -t myapp:1.0 .
docker run --rm --name myapp -d -p 80:80 myapp:1.0
docker images
# myapp:1.0  20.7MB  6.33MB

9.4 基于 Alpine 构建错误页面镜像

1. 创建目录并编写 Dockerfile

dockerfile 复制代码
FROM alpine:3.23 AS builder

RUN apk add --no-cache \
    build-base \
    pcre-dev \
    zlib-dev \
    openssl-dev \
    linux-headers \
    wget \
    tar

ENV NGINX_VERSION=1.28.1

RUN wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
    tar -xzf nginx-${NGINX_VERSION}.tar.gz && \
    rm nginx-${NGINX_VERSION}.tar.gz

WORKDIR /nginx-${NGINX_VERSION}

RUN ./configure \
    --prefix=/usr/local/nginx \
    --with-http_ssl_module \
    --with-http_realip_module \
    --with-http_stub_status_module \
    --with-threads \
    --with-file-aio && \
    make && \
    make install

FROM alpine:3.23

COPY --from=builder /usr/local/nginx /usr/local/nginx
COPY error.html /usr/local/nginx/html/error.html

RUN apk add --no-cache pcre zlib openssl && \
    ln -sf /dev/stdout /usr/local/nginx/logs/access.log && \
    ln -sf /dev/stderr /usr/local/nginx/logs/error.log

RUN echo "index.html" > /usr/local/nginx/html/index.html

RUN echo 'server { error_page 404 /error.html; location = /error.html { root /usr/local/nginx/html; } }' > /usr/local/nginx/conf/default.conf

EXPOSE 80 443

ENTRYPOINT ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]

2. 编写 error.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义错误页面</title>
<style>
body {
    font-family: Arial, sans-serif;
    text-align: center;
    padding: 50px;
}
h1 { color: #333; }
p { color: #666; }
</style>
</head>
<body>
<h1>哎呀!页面未找到</h1>
<p>你所请求的页面可能已经被移除或者地址错误。</p>
</body>
</html>

3. 构建并运行

bash 复制代码
docker build -t error:1.0 .
docker run --rm --name error -d -p 8080:80 error:1.0
curl localhost:8080/error.html

9.5 构建 HTTPS 服务镜像

1. 创建目录并编写 main.go

go 复制代码
package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, HTTPS!\n"))
    })
    log.Println("Server running on https://localhost:443")
    err := http.ListenAndServeTLS(":443", "/etc/ssl/certs/server.crt",
        "/etc/ssl/private/server.key", nil)
    if err != nil {
        log.Fatal("ListenAndServeTLS: ", err)
    }
}

2. 编写 go.mod

go 复制代码
module github.com/yourusername/go-https-server

go 1.21

require (
    golang.org/x/crypto v0.14.0 // indirect
    golang.org/x/net v0.17.0 // indirect
    golang.org/x/sys v0.13.0 // indirect
)

3. 编写 Dockerfile

dockerfile 复制代码
FROM golang:1.21-alpine AS builder

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
ENV GOPROXY=https://goproxy.cn,direct

RUN apk add --no-cache git make openssl

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o app

RUN openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
    -keyout server.key -out server.crt -subj "/CN=localhost" \
    -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

FROM alpine:3.23

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

COPY --from=builder /app/app /usr/local/bin/app
COPY --from=builder /app/server.crt /etc/ssl/certs/
COPY --from=builder /app/server.key /etc/ssl/private/

RUN chown appuser:appgroup /usr/local/bin/app && \
    chmod 755 /usr/local/bin/app && \
    chmod 644 /etc/ssl/certs/server.crt && \
    chmod 644 /etc/ssl/private/server.key

WORKDIR /home/appuser
USER appuser

EXPOSE 443

HEALTHCHECK --interval=30s --timeout=3s \
    CMD wget --no-check-certificate -qO- https://localhost/healthz || exit 1

ENTRYPOINT ["app", "--cert", "/etc/ssl/certs/server.crt", "--key", "/etc/ssl/private/server.key"]

4. 构建并运行

bash 复制代码
docker build -t https-server:1.0 .
docker run --rm --name hs -d -p 443:443 https-server:1.0
curl -k https://localhost
# Hello, HTTPS!

9.6 构建 Python 计算镜像(马青公式)

1. 编写 main.py

python 复制代码
# -*- coding: utf-8 -*-
from __future__ import division
import time

time1 = time.time()
number = 1000
number1 = number + 10
b = 10 ** number1
x1 = b * 4 // 5
x2 = b // -239
he = x1 + x2
number *= 2

for i in range(3, number, 2):
    x1 //= -25
    x2 //= -57121
    x = (x1 + x2) // i
    he += x

pai = he * 4
pai //= 10 ** 10
paistring = str(pai)
result = paistring[0] + '.' + paistring[1:len(paistring)]
print(result)
time2 = time.time()
print('Total time:' + str(time2 - time1) + 's')

2. 编写 Dockerfile

dockerfile 复制代码
FROM python:2.7-slim
ADD main.py /root
CMD ["/usr/bin/python", "/root/main.py"]

3. 构建并运行

bash 复制代码
docker build -t maqing:1.0 .
docker run --name maqing -d maqing:1.0
docker logs maqing
# 3.141592653589793238462...
# Total time:0.000882s

十、Swarm 集群管理

10.1 Swarm 核心概念

Docker Swarm 是 Docker 官方提供的一套容器编排系统。

核心概念:

概念 说明
集群(Cluster) 由多个 Docker 主机组成,以集群模式运行
节点(Node) 集群中的一台机器,可以是 Manager 或 Worker
Manager 节点 管理成员资格和委派,使用 Raft 协议协同工作
Worker 节点 运行实际应用服务的容器
服务(Service) 对运行在集群上的应用服务的期望状态描述
任务(Task) 部署的最小单元,与容器一一对应
栈(Stack) 描述一系列相关服务的集合(YAML 文件)

10.2 Swarm 安装

bash 复制代码
# 拉取镜像
docker pull swarm

# 查看版本
docker run --rm swarm:latest -v
# swarm version 1.2.9 (527a849)

10.3 Swarm 集群搭建

10.3.1 主机规划
主机名 IP 地址 角色
manager1 192.168.72.47 管理节点
worker1 192.168.72.48 工作节点
worker2 192.168.72.49 工作节点
10.3.2 环境准备

所有节点配置 hosts:

bash 复制代码
cat >> /etc/hosts <<EOF
192.168.72.47  manager1
192.168.72.48  worker1
192.168.72.49  worker2
EOF
10.3.3 初始化集群
bash 复制代码
# 在管理节点执行
docker swarm init --advertise-addr 192.168.72.47

# 输出示例:
# Swarm initialized: current node (ny9sbiscw1dax5y92nzzwvhk5) is now a manager.
# To add a worker to this swarm, run the following command:
# docker swarm join --token SWMTKN-xxx 192.168.72.47:2377

# 查看节点
docker node ls
10.3.4 加入工作节点
bash 复制代码
# 在工作节点执行(使用 init 输出的 token)
docker swarm join --token SWMTKN-xxx 192.168.72.47:2377

# 如果忘记 token,在管理节点查看
docker swarm join-token worker

# 如果 token 过期,重新生成
docker swarm join-token --rotate worker
10.3.5 查看集群状态
bash 复制代码
# 查看节点列表
docker node ls

# 查看节点详细信息
docker node inspect self

# 查看集群状态
docker info | grep Swarm

10.4 服务管理

10.4.1 创建服务
bash 复制代码
# 创建服务
docker service create --replicas 3 --name nginx -p 80:80 nginx:1.28.1

# 查看服务列表
docker service ls

# 查看服务任务
docker service ps nginx

# 查看服务详情
docker service inspect --pretty nginx
10.4.2 扩展服务
bash 复制代码
# 扩展到 5 个副本
docker service scale nginx=5
10.4.3 删除服务
bash 复制代码
docker service rm nginx

10.5 滚动更新与回滚

10.5.1 发布服务
bash 复制代码
# 创建 Redis 服务
docker service create --replicas 3 --name redis --update-delay 10s redis:8.2
10.5.2 滚动更新
bash 复制代码
# 更新到 8.4 版本
docker service update --image redis:8.4 redis

# 查看更新状态
docker service ps redis
10.5.3 回滚
bash 复制代码
# 回滚到上一个版本
docker service update --rollback redis

10.6 节点管理(污染与激活)

10.6.1 污染节点(Drain)
bash 复制代码
# 将节点设置为 Drain 状态(维护模式)
docker node update --availability drain worker1

# 查看节点状态
docker node ls
# worker1 的 AVAILABILITY 变为 Drain

# 查看任务迁移
docker service ps redis
# 任务已从 worker1 迁移到其他节点
10.6.2 激活节点
bash 复制代码
# 恢复到 Active 状态
docker node update --availability active worker1

注意drain 状态只会影响 Swarm 服务工作负载,不会影响独立容器。

10.6.3 节点管理命令
命令 说明
docker node demote 降级管理节点为工作节点
docker node inspect 查看节点详细信息
docker node ls 列出所有节点
docker node promote 升级工作节点为管理节点
docker node ps 查看节点上的任务
docker node rm 删除节点
docker node update 更新节点

10.7 路由网格

10.7.1 发布服务端口
bash 复制代码
docker service create --name my-web \
    --publish published=8080,target=80 \
    --replicas 2 \
    nginx:1.28.1
10.7.2 TCP 和 UDP 端口
bash 复制代码
# 仅 TCP
docker service create --name dns-cache -p 53:53 dns-cache

# TCP 和 UDP
docker service create --name dns-cache \
    --publish published=53,target=53 \
    --publish published=53,target=53,protocol=udp \
    dns-cache

# 仅 UDP
docker service create --name dns-cache \
    --publish published=53,target=53,protocol=udp \
    dns-cache
10.7.3 绕过路由网格(Host 模式)
bash 复制代码
docker service create --name dns-cache \
    --publish published=53,target=53,protocol=udp,mode=host \
    --mode global \
    dns-cache

十一、Docker Compose 编排

11.1 Compose 概念

Docker Compose 定位是定义和运行多个 Docker 容器的应用。它将所管理的容器分为三层:

层级 说明
项目(Project) 由一组关联的应用容器组成的一个完整业务单元
服务(Service) 一个应用的容器,可包括若干运行相同镜像的容器实例
容器(Container) Docker 容器

11.2 Compose 安装

bash 复制代码
# 下载 Docker Compose
curl -L https://github.com/docker/compose/releases/download/v5.0.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/bin/docker-compose

# 或者使用 wget
wget https://github.com/docker/compose/releases/download/v5.0.2/docker-compose-linux-x86_64
mv docker-compose-linux-x86_64 /usr/bin/docker-compose

# 添加执行权限
chmod +x /usr/bin/docker-compose

# 验证
docker-compose --version

11.3 docker-compose.yml 详解

11.3.1 基本示例
yaml 复制代码
services:
  nginx:
    image: nginx:1.28.1
    restart: always
    container_name: nginx
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /opt/nginx/log:/var/log/nginx
      - /opt/nginx/www:/usr/share/nginx/html
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:8.4.4
    container_name: mysql
    ports:
      - "3306:3306"
    command:
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
    environment:
      MYSQL_ROOT_PASSWORD: "123456"
    volumes:
      - /opt/mysql/db:/var/lib/mysql

  redis:
    image: redis:8.4
    container_name: redis
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "6379:6379"
    volumes:
      - /opt/redis/data:/data
    depends_on:
      - mysql
11.3.2 常见指令
指令 说明
services 定义服务
image 指定服务的镜像名称或 ID
ports 端口映射 HOST:CONTAINER
build 指定 Dockerfile 路径,自动构建镜像
command 覆盖容器启动后默认执行的命令
depends_on 确定容器的启动顺序
environment 设置环境变量
volumes 挂载数据卷
network_mode 网络模式
networks 定义网络

11.4 常用命令

命令 说明
docker-compose up -d 启动所有服务(后台运行)
docker-compose down 停止并删除所有容器
docker-compose down -v 同时删除数据卷
docker-compose ps 查看运行状态
docker-compose logs 查看日志
docker-compose stop 停止服务
docker-compose start 启动服务
docker-compose restart 重启服务
docker-compose exec 服务名 bash 进入容器
docker-compose build 重新构建镜像
docker-compose pull 拉取最新镜像
docker-compose scale 服务名=3 扩展服务副本
docker-compose ls 查看运行的项目
docker-compose events 查看容器事件
docker-compose pause 暂停服务
docker-compose unpause 恢复暂停的服务
docker-compose port 服务名 端口 显示暴露的端口
docker-compose run 服务名 命令 运行一次性命令

11.5 使用案例:Flask + Redis

11.5.1 编写应用程序

app.py

python 复制代码
import time
import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

requirements.txt:

复制代码
Flask==3.1.2
redis==7.1.0

Dockerfile:

dockerfile 复制代码
FROM python:3.14-alpine

WORKDIR /code
COPY . .

RUN pip install --no-cache-dir -r requirements.txt

EXPOSE 5000
CMD ["python", "app.py"]

docker-compose.yml:

yaml 复制代码
services:
  web:
    build: .
    ports:
      - "5000:5000"
    depends_on:
      - redis
    volumes:
      - .:/code    # 动态修改代码无需重建
  redis:
    image: redis:alpine
11.5.2 运行与测试
bash 复制代码
docker-compose up -d
docker-compose ps

curl localhost:5000
# Hello World! I have been seen 1 times.
curl localhost:5000
# Hello World! I have been seen 2 times.

# 修改代码后无需重建
vim app.py
# 访问立即生效

11.6 与 Swarm 集成

yaml 复制代码
services:
  nginx:
    image: nginx:1.28.1
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /docker/nginx/log:/var/log/nginx
      - /docker/nginx/www:/etc/nginx/html
    deploy:
      mode: replicated
      replicas: 2

  mysql:
    image: mysql:8.4.4
    ports:
      - "3306:3306"
    command:
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
    environment:
      MYSQL_ROOT_PASSWORD: "123456"
    volumes:
      - "/docker/mysql/db:/var/lib/mysql"
    deploy:
      mode: replicated
      replicas: 2
bash 复制代码
# 部署 Stack
docker stack deploy -c docker-compose.yml web

# 查看服务
docker stack services web

# 删除 Stack
docker stack rm web

十二、Harbor 私有仓库

12.1 Harbor 概述

Harbor(港湾)是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器。由 VMware 在 Docker Registry 的基础之上进行了二次封装。

主要组件:

  • proxy:nginx 前端代理,分发前端页面 UI 访问和镜像上传下载流量
  • ui:Web 管理页面,包含前端页面和后端 API,使用 MySQL 数据库
  • registry:镜像仓库,存储镜像文件
  • adminserver:系统配置管理中心
  • jobservice:镜像复制工作
  • log:日志汇总组件

12.2 环境准备

bash 复制代码
# 修改主机名
hostnamectl hostname harbor

# 修改 IP(仅主机模式网络)
nmcli c m ens160 ipv4.method manual ipv4.address 192.168.10.20/24 \
    ipv4.gateway 192.168.10.200 ipv4.dns "223.5.5.5 8.8.8.8" \
    connection.autoconnect yes
nmcli c up ens160

# 开启路由转发
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p

# 配置主机映射
cat > /etc/hosts <<EOF
192.168.10.20 hb.reg.com harbor
EOF

12.3 安装 Docker 与 Compose

bash 复制代码
# 添加 Docker 源
dnf install yum-utils -y
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/rhel/docker-ce.repo

# 安装 Docker
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

# 配置 Docker
cat > /etc/docker/daemon.json <<EOF
{
    "default-ipc-mode": "shareable",
    "data-root": "/data/docker",
    "exec-opts": ["native.cgroupdriver=systemd"],
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "50"
    },
    "insecure-registries": ["https://hb.reg.com"],
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://docker.imgdb.de",
        "https://docker-0.unsee.tech",
        "https://docker.hlmirror.com",
        "https://func.ink"
    ]
}
EOF

# 启动 Docker
systemctl enable --now docker

# 安装 Docker Compose
wget https://github.com/docker/compose/releases/download/v2.35.1/docker-compose-linux-x86_64
mv docker-compose-linux-x86_64 /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose

12.4 Harbor 安装部署

bash 复制代码
# 创建数据目录
mkdir -p /data

# 下载 Harbor
cd /data
wget https://github.com/goharbor/harbor/releases/download/v2.13.2/harbor-offline-installer-v2.13.2.tgz

# 解压
tar -xzf harbor-offline-installer-v2.13.2.tgz
cd harbor

# 复制配置文件
cp harbor.yml.tmp1 harbor.yml

# 修改配置文件
vim harbor.yml
# hostname: hb.reg.com
# https:
#   port: 443
#   certificate: /data/cert/hb.reg.com.crt
#   private_key: /data/cert/hb.reg.com.key

12.5 证书配置

生成 CA 证书:

bash 复制代码
mkdir ssl && cd ssl

# 生成 CA 私钥
openssl genrsa -out ca.key 4096

# 生成 CA 证书
openssl req -x509 -new -nodes -sha512 -days 3650 \
    -subj "/C=CN/ST=Chongqing/L=Banan/O=Example/OU=Personal/CN=MyPersonal Root CA" \
    -key ca.key -out ca.crt

生成服务器证书:

bash 复制代码
# 生成私钥
openssl genrsa -out hb.reg.com.key 4096

# 生成证书签名请求
openssl req -sha512 -new \
    -subj "/C=CN/ST=Chongqing/L=Banan/O=Example/OU=Personal/CN=hb.reg.com" \
    -key hb.reg.com.key -out hb.reg.com.csr

# 创建 v3 扩展文件
cat > v3.ext <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth
subjectAltName=alt_names
[alt_names]
DNS.1=hb.reg.com
DNS.2=hb.reg
DNS.3=hb
EOF

# 生成证书
openssl x509 -req -sha512 -days 3650 -extfile v3.ext \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -in hb.reg.com.csr -out hb.reg.com.crt

# 复制证书到 Harbor 目录
mkdir /data/cert
cp hb.reg.com.crt /data/cert/
cp hb.reg.com.key /data/cert/

# 转换证书供 Docker 使用
openssl x509 -inform PEM -in hb.reg.com.crt -out hb.reg.com.cert

# 复制到 Docker 证书目录
mkdir -p /etc/docker/certs.d/hb.reg.com:443
cp hb.reg.com.cert /etc/docker/certs.d/hb.reg.com:443/
cp hb.reg.com.key /etc/docker/certs.d/hb.reg.com:443/
cp ca.crt /etc/docker/certs.d/hb.reg.com:443/

# 重启 Docker
systemctl restart docker

12.6 启动与配置服务

bash 复制代码
cd /data/harbor

# 加载镜像并安装
docker load -i harbor.v2.13.2.tar.gz
./install.sh

# 查看容器
docker-compose ps

# 配置系统服务
cat > /usr/lib/systemd/system/harbor.service <<EOF
[Unit]
Description=Harbor
After=docker.service network-online.target
Requires=docker.service

[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/bin/docker-compose --file /data/harbor/docker-compose.yml up
ExecStop=/usr/bin/docker-compose --file /data/harbor/docker-compose.yml down

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now harbor

12.7 测试本地仓库

bash 复制代码
# 拉取镜像
docker pull nginx:1.28.0
docker pull mysql:8.4.4

# 打标签
docker tag nginx:1.28.0 hb.reg.com/library/nginx:1.28.0
docker tag mysql:8.4.4 hb.reg.com/library/mysql:8.4.4

# 登录 Harbor
docker login hb.reg.com
# 用户名: admin
# 密码: Harbor12345

# 推送镜像
docker push hb.reg.com/library/nginx:1.28.0
docker push hb.reg.com/library/mysql:8.4.4

# 从 Harbor 拉取镜像
docker rmi hb.reg.com/library/nginx:1.28.0
docker pull hb.reg.com/library/nginx:1.28.0

# API 查询
curl -k -u 'admin:Harbor12345' 'https://hb.reg.com/api/v2.0/projects/library/repositories'

十三、案例实战:中间件容器化

13.1 MySQL 安装

13.1.1 拉取镜像
bash 复制代码
docker pull mysql:8.4.8
13.1.2 创建挂载目录
bash 复制代码
mkdir -p /opt/mysql/{conf,data,logs}
13.1.3 创建配置文件
bash 复制代码
# 创建临时容器获取配置文件
docker run --name mysql_temp -d --rm -e MYSQL_ROOT_PASSWORD=12345 mysql:8.4.8
docker cp mysql_temp:/etc/my.cnf /opt/mysql/conf/
docker cp mysql_temp:/etc/mysql/conf.d /opt/mysql/conf/
docker stop mysql_temp
13.1.4 安装 MySQL
bash 复制代码
docker run --name mysql -d \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /opt/mysql/conf/my.cnf:/etc/my.cnf \
    -v /opt/mysql/conf/conf.d:/etc/mysql/conf.d \
    -v /opt/mysql/data:/var/lib/mysql \
    -v /opt/mysql/logs:/var/log/mysql \
    -p 3306:3306 \
    --privileged=true \
    mysql:8.4.8 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_unicode_ci

13.2 Redis 安装

bash 复制代码
# 拉取镜像
docker pull redis:8.4

# 创建目录和配置
mkdir -p /opt/redis/{data,conf}
cat > /opt/redis/conf/redis.conf <<EOF
appendonly yes
requirepass redis123
EOF

# 启动容器
docker run --name redis -d \
    -p 6379:6379 \
    -v /opt/redis/data:/data \
    -v /opt/redis/conf/redis.conf:/etc/redis/redis.conf \
    redis:8.4 redis-server /etc/redis/redis.conf

13.3 Elasticsearch 安装

bash 复制代码
# 拉取镜像
docker pull elasticsearch:8.19.5

# 修改内核参数
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
sysctl -p

# 创建网络
docker network create es-net

# 启动容器
docker run --name es -d \
    --restart=always \
    --network es-net \
    -p 9200:9200 \
    -p 9300:9300 \
    --privileged=true \
    -e discovery.type=single-node \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    elasticsearch:8.19.5

# 修改配置(关闭安全认证)
docker cp -a es:/usr/share/elasticsearch/config/elasticsearch.yml .
vim elasticsearch.yml
# xpack.security.enabled: false
# xpack.security.enrollment.enabled: false
# xpack.security.http.ssl.enabled: false
# xpack.security.transport.ssl.enabled: false
docker cp -a ./elasticsearch.yml es:/usr/share/elasticsearch/config/
docker restart es

# 访问验证
curl http://localhost:9200

13.4 Kibana 安装

bash 复制代码
# 拉取镜像(版本必须与 ES 一致)
docker pull kibana:8.19.5

# 启动容器
docker run --name kibana \
    --network es-net \
    -d -p 5601:5601 \
    -e ELASTICSEARCH_HOSTS=http://es:9200 \
    kibana:8.19.5

# 浏览器访问 http://IP:5601

13.5 MySQL 主从复制

13.5.1 主从复制原理
  1. 主库更新事件写入二进制日志(binlog)
  2. 主库开启 binlog dump 线程,将日志事件发送给从库
  3. 从库 IO 线程接收 binlog,写入中继日志(relay log)
  4. 从库 SQL 线程读取并执行 relay log 中的 SQL 语句
13.5.2 配置主服务器
bash 复制代码
# 创建配置
mkdir -p /data/master/conf
cat > /data/master/conf/my.cnf <<EOF
[mysqld]
server-id=1
read-only=0
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysqld.sock
[client]
socket=/var/run/mysqld/mysqld.sock
EOF

# 启动主服务器
docker run -d --name mysql-master \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /data/master/conf/my.cnf:/etc/my.cnf \
    -p 3306:3306 \
    mysql:9.3.0 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_unicode_ci

# 创建复制用户
docker exec -it mysql-master mysql -uroot -p123456
mysql> CREATE USER 'backups'@'%' IDENTIFIED BY '123456';
mysql> GRANT REPLICATION SLAVE ON *.* TO 'backups'@'%';
mysql> FLUSH PRIVILEGES;
mysql> SHOW BINARY LOG STATUS;
# 记录 File 和 Position
13.5.3 配置从服务器
bash 复制代码
# 创建配置
mkdir -p /data/slave/conf
cat > /data/slave/conf/my.cnf <<EOF
[mysqld]
server-id=2
read-only=1
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysqld.sock
[client]
socket=/var/run/mysqld/mysqld.sock
EOF

# 启动从服务器
docker run -d --name mysql-slave \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /data/slave/conf/my.cnf:/etc/my.cnf \
    -p 3307:3306 \
    mysql:9.3.0 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_unicode_ci

# 配置同步
docker exec -it mysql-slave mysql -uroot -p123456
mysql> CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='主服务器IP',
    SOURCE_USER='backups',
    SOURCE_PASSWORD='123456',
    SOURCE_LOG_FILE='binlog.000003',
    SOURCE_LOG_POS=864,
    GET_SOURCE_PUBLIC_KEY=1;
mysql> START REPLICA;
mysql> SHOW REPLICA STATUS\G;
# 确认 Slave_IO_Running: Yes, Slave_SQL_Running: Yes
13.5.4 验证
bash 复制代码
# 在主库创建数据
mysql> CREATE DATABASE mydb;
mysql> USE mydb;
mysql> CREATE TABLE t1(id INT, name VARCHAR(20));
mysql> INSERT INTO t1 VALUES(1, 'zhangsan');

# 在从库查询
mysql> SHOW DATABASES;
mysql> SELECT * FROM mydb.t1;

13.6 MySQL 双主双从

13.6.1 创建网络
bash 复制代码
docker network create mysql-replica-net
13.6.2 配置文件
bash 复制代码
# master1.cnf
[mysqld]
server-id=1
log_bin=mysql-bin
auto_increment_increment=2
auto_increment_offset=1

# master2.cnf
[mysqld]
server-id=2
log_bin=mysql-bin
auto_increment_increment=2
auto_increment_offset=2

# slave1.cnf
[mysqld]
server-id=3
log_bin=mysql-bin
relay_log=mysql-relay-bin
read_only=1

# slave2.cnf
[mysqld]
server-id=4
log_bin=mysql-bin
relay_log=mysql-relay-bin
read_only=1
13.6.3 创建容器
bash 复制代码
# Master1
docker run -d --name master1 \
    --network mysql-replica-net \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /data/master1.cnf:/etc/my.cnf \
    -p 3306:3306 \
    mysql:9.3.0 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_unicode_ci

# Master2
docker run -d --name master2 \
    --network mysql-replica-net \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /data/master2.cnf:/etc/my.cnf \
    -p 3307:3306 \
    mysql:9.3.0 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_unicode_ci
13.6.4 配置复制
bash 复制代码
# 在 master1 和 master2 分别创建复制用户
CREATE USER 'backups'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'backups'@'%';
FLUSH PRIVILEGES;
SHOW BINARY LOG STATUS;

# Master1 复制 Master2
CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='master2',
    SOURCE_USER='backups',
    SOURCE_PASSWORD='123456',
    SOURCE_LOG_FILE='binlog.000003',
    SOURCE_LOG_POS=864,
    GET_SOURCE_PUBLIC_KEY=1;
START REPLICA;

# Master2 复制 Master1
CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='master1',
    SOURCE_USER='backups',
    SOURCE_PASSWORD='123456',
    SOURCE_LOG_FILE='binlog.000003',
    SOURCE_LOG_POS=864,
    GET_SOURCE_PUBLIC_KEY=1;
START REPLICA;

13.7 Nginx 高可用(Keepalived)

13.7.1 主机规划
主机名 IP 地址 角色
kp-master 192.168.72.135 Nginx + Keepalived MASTER
kp-slave 192.168.72.136 Nginx + Keepalived BACKUP
VIP 192.168.72.100 虚拟 IP
13.7.2 部署 Nginx
bash 复制代码
# 创建配置目录
mkdir -p /opt/nginxdir

# 创建 Nginx 配置
cat > /opt/nginxdir/default.conf <<EOF
server {
    listen 80;
    server_name localhost;
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}
EOF

# 创建首页
echo "Nginx on $(hostname)" > /opt/nginxdir/index.html

# 运行 Nginx 容器
docker run -d --name my-nginx -p 80:80 \
    -v /opt/nginxdir/default.conf:/etc/nginx/conf.d/default.conf \
    -v /opt/nginxdir/index.html:/usr/share/nginx/html/index.html \
    nginx:1.28.1
13.7.3 部署 Keepalived(主节点)
bash 复制代码
# 创建配置文件
mkdir -p /opt/keepalived/conf
cat > /opt/keepalived/conf/keepalived.conf <<EOF
global_defs {
    router_id nginx1
}

vrrp_script check_nginx {
    script "/check_nginx.sh"
    interval 2
    weight -20
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.72.100
    }

    track_script {
        check_nginx
    }
}
EOF

# 创建检查脚本
cat > /opt/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
count=$(ps -C nginx --no-header | wc -l)
if [ $count -eq 0 ]; then
    docker start my-nginx 2>/dev/null || docker run -d --name my-nginx -p 80:80 \
        -v /opt/nginxdir/default.conf:/etc/nginx/conf.d/default.conf \
        -v /opt/nginxdir/index.html:/usr/share/nginx/html/index.html \
        nginx:1.28.1
    sleep 2
    if [ $(ps -C nginx --no-header | wc -l) -eq 0 ]; then
        systemctl stop keepalived
    fi
fi
EOF
chmod +x /opt/keepalived/check_nginx.sh

# 运行 Keepalived 容器
docker run --cap-add=NET_ADMIN --cap-add=NET_BROADCAST --cap-add=NET_RAW \
    --net=host -d --name kp-master \
    -v /opt/keepalived/conf/keepalived.conf:/container/service/keepalived/assets/keepalived.conf \
    -v /opt/keepalived/check_nginx.sh:/check_nginx.sh \
    -v /etc/localtime:/etc/localtime \
    osixia/keepalived:2.0.20
13.7.4 部署 Keepalived(备节点)
bash 复制代码
# 配置类似,修改 state 为 BACKUP,priority 为 90
docker run --cap-add=NET_ADMIN --cap-add=NET_BROADCAST --cap-add=NET_RAW \
    --net=host -d --name kp-backup \
    -v /opt/keepalived/conf/keepalived.conf:/container/service/keepalived/assets/keepalived.conf \
    -v /opt/keepalived/check_nginx.sh:/check_nginx.sh \
    -v /etc/localtime:/etc/localtime \
    osixia/keepalived:2.0.20

附录:常用命令速查表

Docker 基础命令

分类 命令 说明
系统 docker info 查看 Docker 系统信息
系统 docker version 查看 Docker 版本
镜像 docker images 列出本地镜像
镜像 docker pull 拉取镜像
镜像 docker push 推送镜像
镜像 docker build 构建镜像
镜像 docker rmi 删除镜像
镜像 docker save 导出镜像
镜像 docker load 导入镜像
镜像 docker tag 添加镜像标签
镜像 docker inspect 查看镜像详情
镜像 docker history 查看镜像历史
容器 docker ps [-a] 列出容器
容器 docker run 创建并启动容器
容器 docker create 创建容器
容器 docker start/stop/restart 启动/停止/重启容器
容器 docker exec 在容器中执行命令
容器 docker logs 查看容器日志
容器 docker rm 删除容器
容器 docker cp 复制文件
容器 docker commit 通过容器创建镜像
容器 docker rename 重命名容器
容器 docker port 查看端口映射
容器 docker export/import 导出/导入容器

网络命令

命令 说明
docker network ls 列出所有网络
docker network create 创建网络
docker network inspect 查看网络详情
docker network rm 删除网络
docker network connect 连接容器到网络
docker network disconnect 断开容器网络

数据卷命令

命令 说明
docker volume ls 列出数据卷
docker volume create 创建数据卷
docker volume inspect 查看数据卷详情
docker volume rm 删除数据卷
docker volume prune 清理未使用的数据卷

Swarm 命令

命令 说明
docker swarm init 初始化集群
docker swarm join 加入集群
docker node ls 列出节点
docker service create 创建服务
docker service ls 列出服务
docker service scale 扩展服务
docker service update 更新服务
docker stack deploy 部署 Stack

Compose 命令

命令 说明
docker-compose up -d 启动服务(后台)
docker-compose down 停止并删除
docker-compose ps 查看状态
docker-compose logs 查看日志
docker-compose exec 进入容器
docker-compose build 构建镜像
docker-compose stop/start 停止/启动服务
docker-compose pause/unpause 暂停/恢复服务
docker-compose ls 查看运行项目
docker-compose port 显示暴露端口
docker-compose events 查看容器事件

持续更新中... 本笔记将随着 Docker 技术的发展不断完善。如有错误或建议,欢迎指正。

相关推荐
取谖慕12.2 小时前
Harbor仓库从讲解到部署实战
docker·harbor
逍遥德4 小时前
运维技术栈Linux+docker+Kubernetes+Jenkins/GitLab CI 知识点详细列表
linux·运维·docker
名字还没想好☜4 小时前
Kubernetes CrashLoopBackOff 排查全流程:从 describe 到日志到 exit code
运维·docker·云原生·容器·kubernetes
冰封之寂4 小时前
Docker 部署与基础命令详解:从安装到容器管理全流程指南
linux·运维·docker·容器
Baron X4 小时前
Gitea Docker 部署教程
docker·gitea
sulikey6 小时前
服务器安装Docker教程。如何在云服务器安装Docker容器?
运维·服务器·ubuntu·docker·安装教程·云服务器
窝子面7 小时前
认识Docker与启动容器与镜像的关系
运维·docker·容器
张忠琳20 小时前
【NVIDIA】 NVIDIA Container Toolkit v1.19.1 — OCI 模块超深度分析之三
云原生·容器·架构·kubernetes·nvidia
Elastic 中国社区官方博客1 天前
将你的 Grafana Kubernetes 仪表板迁移到 Elastic Observability:相同的 PromQL,30 倍更快的查询
大数据·人工智能·elasticsearch·搜索引擎·容器·kubernetes·grafana