WD
Classnote Docs课程课件
18

Docker 容器技术详解

学习目标:

  • 理解容器、镜像、仓库三大核心概念及其关系
  • 能区分容器技术与虚拟机技术的差异
  • 掌握 Docker 安装、镜像管理、容器运行、端口映射、卷挂载等基础能力
  • 能使用 Dockerfile 和 Docker Compose 构建并编排基础应用环境
  • 具备排查常见 Docker 部署问题的基础能力

本章重点:

  • 容器与虚拟机的本质区别
  • 镜像、容器、仓库三者关系
  • run / exec / logs / volume / network 等核心命令
  • Dockerfile 多阶段构建与 Compose 多服务编排
01 / Section

1. 背景介绍

1.1 容器技术的起源

假如我们正在开发一个名叫"谁是小黑子"的应用,程序员自己从头到尾搭建了一套环境开始写代码,写完代码后程序员要把代码交给测试同学测试,这时测试同学开始从头到尾搭建这套环境,测试同学测完后终于可以上线了,这时运维同学又要重新从头到尾搭建这套环境,费了九牛二虎之力搭建好环境开始上线,糟糕,上线系统就崩溃了。

image-20230321173307331
image-20230321173307331

不难看出,不管是开发,还是测试,还是运维,想要运行我们的应用程序,都需要从头开始搭建环境,如果环境没有搭建好,那么我们的应用程序就运行不起来。这种做法,不仅仅会大大增加各个岗位的工作量,还可能引发一些不必要的冲突......

image-20230321173518316
image-20230321173518316

从以上整个过程可以看到,在从开发到上线的过程中,反复搭建了三套环境,这个过程十分浪费时间与效率,而聪明的程序员们是永远不会满足现状的。所以,容器技术应运而生。

1.2 虚拟机技术

针对以上的情况,聪明的同学可以想到,其实可以使用VMware虚拟机,先搭建好一套虚拟机环境,然后克隆出来分别交给开发、测试和运维,这样就可以解决反复搭建环境的痛点了。

image-20230321175700983
image-20230321175700983

通过上图可以知道,这确实是一个不错的办法,不过也不是一个完美的解决办法。

因为我们的本意是在开发环境、测试环境和生产环境,部署并运行我们JAVA应用程序所依赖的其他应用,如上图中的MySQL、Nginx和Redis,但是现在我们在这三个环境中都运行了一个虚拟机,在这个虚拟机中运行了我们所需要的应用。

而虚拟机本身又运行了一个操作系统,操作系统是很笨重的,需要消耗更多的资源。

image-20230321231902929
image-20230321231902929

在上面的案例中,以开发环境为例:

image-20230321232442604
image-20230321232442604

由上图不难看出,因为虚拟机的存在,消耗了更多的内存资源,其实不仅仅是内存资源,还有CPU和磁盘等资源。

💡 提示:有同学可能会有疑问:那我在右边的图中对虚拟机分配3G内存不就行了吗?其实不行,因为在这个虚拟机操作系统中,不光光要运行MySQL、Nginx、Redis等应用程序(也可以简单理解为进程),还需要运行一些额外的系统进程,比如说会话管理器、登录服务、任务管理器。

以下以Windows操作系统为例,举例说明一些额外的系统进程,Linux等操作系统也是类似的。

image-20230321234009210
image-20230321234009210

那么有没有一种技术,既能获得虚拟机的好处(只需要部署一次,部署快速简单),又能克服虚拟机的缺点(操作系统笨重,需要消耗更多的资源)呢?

答案是肯定的,这就是 容器技术

1.3 容器

容器,英文是container,其实container还有集装箱的意思。

集装箱的特点:

特点 说明
互相隔离 集装箱之间相互隔离,互不影响
长期反复使用 可以重复使用,标准化设计
快速装载与卸载 装卸效率高
规格标准 在任何码头上都可以摆放
image-20230322000113660
image-20230322000113660

其实容器和集装箱的概念特别类似,集装箱是为了货物之间的隔离,容器也是为了应用程序之间的隔离。其实虚拟机本质上也是一个隔离技术,隔离出宿主机的各个操作资源。

image-20230322003051606
image-20230322003051606

⚠️ 重要区别与虚拟机技术不同的是,虚拟机是在操作系统层面实现的隔离,而容器技术只隔离应用程序的运行时环境,但容器之间可以共享同一个操作系统。

通过上图不难看出,容器更加轻量而且占用的资源更少,与宿主机的交互更快更直接,所以容器内的应用启动更加迅速,容器相比较与虚拟机是一种更加高效的技术。

我们要学习的Docker就是容器引擎的一种实现,除了Docker之外,容器引擎还有很多,比如:

  • Podman:开源容器引擎,无守护进程
  • Pouch:阿里巴巴开源的容器引擎
  • containerd:Docker核心组件,也可独立使用
02 / Section

2. Docker 介绍

Docker是一个开源的应用容器引擎,基于Go语言(使用协程)并且遵从Apache2.0协议开源。

Docker将程序以及程序所有的依赖都打包在一起,运行在一个Docker容器中,这样你的程序可以在任何环境都会有一致的表现。这里程序运行的依赖也就是容器就好比集装箱,容器所处的操作系统环境就好比货船或港口程序的表现只和集装箱有关系(容器),和集装箱放在哪个货船或者哪个港口(操作系统)没有关系

Docker的口号:"Build once, run anywhere" —— 一次构建,处处运行。

03 / Section

3. 核心概念

Docker的核心概念主要包括三个:镜像(Image)容器(Container)仓库(Repository)

3.1 镜像(Image)

Docker镜像就类似于操作系统的镜像(比如之前安装虚拟机使用到的Ubuntu18),是一个只读的模板。一个镜像中包含了一个或多个特定的应用程序以及运行他们所依赖的环境。

镜像的特点:

  • 只读性:镜像内容不可修改
  • 分层存储:镜像由多层组成,共享底层
  • 可复用:一个镜像可以创建多个容器

3.2 容器(Container)

把镜像下载到本地,容器就是从镜像创建的应用运行实例。它可以启动、停止、删除,而这些容器都是彼此相互隔离、互不可见的。

容器 vs 镜像:

特性 镜像(Image) 容器(Container)
状态 只读 可读写
功能 模板/蓝图 运行实例
数量 一份 可多份
类比 类(Class) 对象(Object)

3.3 仓库(Repository)

仓库是集中存放镜像文件的场所。

常用仓库:

企业也可以搭建自己的私有仓库,常用的私有仓库方案有:

  • Harbor:企业级容器镜像仓库
  • Registry:Docker官方提供的简单仓库
image-20230322101913959
image-20230322101913959
04 / Section

4. Docker 安装

4.1 Windows 安装

Windows 10/11 使用 Docker Desktop 完成安装。Docker Desktop 集成了 Docker Engine、CLI、镜像管理和容器运行环境。

下载安装程序后,按提示完成安装。

Docker Desktop 安装程序
Docker Desktop 安装程序

首次打开 Docker Desktop 后,可先跳过注册和登录。

如果打开后提示需要更新 WSL,可以使用管理员身份打开 PowerShell 或 CMD,执行下面的命令:

shell
wsl --update
WSL 更新提示
WSL 更新提示

在 Docker Desktop 的 Settings -> Docker Engine 中配置镜像加速地址:

json
{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "registry-mirrors": [
    "https://liwtpuon.mirror.aliyuncs.com",
    "https://b66kcrvu.mirror.aliyuncs.com",
    "https://docker.m.daocloud.io/",
    "https://dockerpull.com",
    "https://docker-0.unsee.tech",
    "https://docker-cf.registry.cyou",
    "https://docker.1panel.live",
    "https://liwtpuon.mirror.aliyuncs.com"
  ]
}
Docker Engine 镜像加速配置
Docker Engine 镜像加速配置

4.2 Linux 安装

4.2.1 准备工作

更新软件包索引:

bash
sudo apt-get update

新安装的 Ubuntu 虚拟机如果执行更新失败,先修复相关依赖:

bash
sudo apt-get install --reinstall libappstream4

如果系统中已经存在旧版本 Docker,先检查并卸载:

bash
# 查找 docker 相关软件
dpkg -l | grep docker
查找已安装的 Docker 相关软件
查找已安装的 Docker 相关软件
bash
# --purge: 删除软件及其配置文件
sudo apt-get remove --purge docker docker.io

4.2.2 安装 Docker CE

安装基础工具:

bash
sudo apt install curl
sudo apt install ca-certificates curl gnupg lsb-release

添加阿里云 Docker 软件源并安装 Docker:

bash
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

# 这几个命令如果失败可以先跳过
wget http://security.ubuntu.com/ubuntu/pool/main/libs/libseccomp/libseccomp2_2.5.3-2ubuntu2_amd64.deb
sudo dpkg -i libseccomp2_2.5.3-2ubuntu2_amd64.deb
sudo apt --fix-broken install

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

安装完成后切换到 root 用户,检查 Docker 版本:

bash
sudo -i
docker version
Docker 版本检查
Docker 版本检查

4.2.3 权限配置与开机自启

如果执行 docker version 时出现 permission denied,可以把当前用户加入 docker 用户组:

bash
# 添加 docker 用户组
sudo groupadd docker

# 将当前登录用户添加到 docker 用户组
sudo gpasswd -a $USER docker

# 更新用户组
newgrp docker

# 再次测试
docker version
Docker 用户组权限配置
Docker 用户组权限配置

如果仍然提示 /var/run/docker.sock 权限不足,执行:

docker.sock 权限提示
docker.sock 权限提示
bash
sudo chmod 777 /var/run/docker.sock

设置 Docker 开机自启:

bash
sudo systemctl enable docker

4.2.4 配置镜像加速

修改 Docker 配置文件:

bash
sudo vim /etc/docker/daemon.json

在配置文件中写入镜像加速地址:

json
{
  "registry-mirrors": [
    "https://liwtpuon.mirror.aliyuncs.com",
    "https://b66kcrvu.mirror.aliyuncs.com",
    "https://docker.m.daocloud.io/",
    "https://dockerpull.com",
    "https://docker-0.unsee.tech",
    "https://docker-cf.registry.cyou",
    "https://docker.1panel.live",
    "https://liwtpuon.mirror.aliyuncs.com"
  ]
}

重新加载配置并重启 Docker:

bash
sudo systemctl daemon-reload
sudo systemctl restart docker

验证镜像加速配置:

bash
docker info
Docker 镜像加速验证
Docker 镜像加速验证

镜像加速地址可以参考:

4.2.5 安装完成后的快速验证

安装完成后,运行一个简单容器验证 Docker 是否正常工作:

bash
docker run --name redis-vector -d -p 6379:6379 redislabs/redisearch
05 / Section

5. 镜像常用命令

5.1 查看本机镜像

bash
docker images
# REPOSITORY   TAG      IMAGE ID       CREATED       SIZE
# nginx        1.21.6   0e901e68141f   6 weeks ago   142MB

这个命令会展示镜像仓库名、标签、镜像 ID、创建时间和镜像大小。

5.2 拉取镜像

bash
docker pull nginx:1.21.6

Docker 会先从镜像仓库中下载镜像到本地,再用于创建容器。

5.3 删除镜像

bash
# 根据镜像 ID 删除
docker rmi 镜像ID

# 根据镜像名和版本删除
docker rmi nginx:1.21.6
06 / Section

6. 容器常用命令

6.1 创建并启动容器

通过 docker run 可以基于镜像创建并启动容器:

bash
docker run 镜像名称
docker run 镜像名称:版本

如果镜像本地不存在,Docker 会先自动拉取镜像,再启动容器。

给容器追加命令时,容器会执行完该命令后退出:

bash
docker run nginx echo hello

查看 docker run 常见参数:

bash
docker run --help

常见参数:

  • -i:以交互模式运行容器
  • -t:为容器分配伪终端
  • -d:后台运行容器
  • --name:指定容器名称
  • -e:设置环境变量
  • -p:端口映射
  • -v:目录或文件挂载

以交互方式进入容器:

bash
docker run -it nginx /bin/bash

后台方式运行容器:

bash
docker run -d nginx

6.2 查看容器

bash
docker ps      # 查看运行中的容器
docker ps -a   # 查看所有容器

删除停止状态的容器:

bash
docker rm 容器名
docker rm 容器ID

6.3 进入运行中的容器

如果容器已经在运行,可以使用 docker exec 进入容器内部:

bash
docker exec -it 容器名 /bin/bash

6.4 查看容器日志

bash
docker logs 容器ID
docker logs -f 容器名

6.5 容器启停与删除

bash
# 停止容器
docker stop 容器ID|容器名

# 启动容器
docker start 容器ID|容器名

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

# 删除容器
docker rm 容器ID|容器名
07 / Section

7. 端口映射

容器具有隔离性,容器内部端口默认不能直接从宿主机访问。要对外提供服务,需要建立宿主机端口与容器端口之间的映射关系。

使用 -p 参数可以实现端口映射:

bash
# 将宿主机 8080 端口映射到容器 80 端口
docker run -d -p 8080:80 nginx
Nginx 单端口映射
Nginx 单端口映射

也可以配置多个端口映射:

bash
docker run -d -p 1080:80 -p 1090:90 nginx
Nginx 多端口映射
Nginx 多端口映射
08 / Section

8. 文件映射与容器拷贝

容器有独立的文件系统。如果容器被删除,而数据只保存在容器内部,那么这些数据也会丢失。为了让数据持久化,可以把宿主机目录挂载到容器内部。

使用 -v 可以完成目录映射:

bash
docker run -d -p 8081:80 -v /tmp/test:/usr/share/nginx/html nginx
Nginx 文件映射
Nginx 文件映射

上面的命令会把宿主机 /tmp/test 映射到容器内的 /usr/share/nginx/html

例如:

bash
# 在容器内部执行
echo "<html>hello world</html>" > /usr/share/nginx/html/index.html

这时宿主机中的 /tmp/test/index.html 也会同步变化。

把宿主机文件复制到容器内部:

bash
docker cp /etc/nginx/nginx.conf 容器名或ID:/etc/nginx/nginx.conf
09 / Section

9. MySQL 容器环境搭建

9.1 拉取并运行 MySQL 8

bash
# 1. 拉取镜像
docker pull mysql:8.0.35

# 2. 如果之前已经在 Linux 上安装过 mysql,可以先删除旧数据目录
sudo rm -rf /docker/stone65/mysql

# 3. 运行容器
docker run --name mysql \
  --restart=always \
  -v /docker/stone65/mysql:/var/lib/mysql \
  -p 33060:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e TZ=Asia/Shanghai \
  -d mysql:8.0.35

9.2 查看 MySQL 容器状态

MySQL 容器运行状态
MySQL 容器运行状态

9.3 进入容器执行 MySQL

进入 MySQL 容器
进入 MySQL 容器

9.4 使用客户端连接

可以通过 Navicat 等客户端连接容器中的 MySQL:

Navicat 连接 MySQL 容器
Navicat 连接 MySQL 容器