# Docker 核心概念

# 容器

Docker 是世界领先的软件容器平台,所以想要搞懂 Docker 的概念我们必须先从容器开始说起。

# 容器是什么

一句话概括容器:容器就是将软件打包成标准化单元,以用于开发、交付和部署。

  • 容器镜像是轻量的、可执行的独立软件包,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。
  • 容器化软件适用于基于 Linux 和 Windows 的应用,在任何环境中都能够始终如一地运行。
  • 容器赋予了软件独立性,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同基础设施上运行不同软件时的冲突。

如果需要通俗地描述容器的话,我觉得容器就是一个存放东西的地方,就像书包可以装各种文具、衣柜可以放各种衣服、鞋架可以放各种鞋子一样。我们现在所说的容器存放的东西可能更偏向于应用比如网站、程序甚至是系统环境。

认识容器

认识容器

# 图解:物理机、虚拟机、容器

物理机:

物理机

虚拟机:

虚拟机

容器:

img

通过上面这三张抽象图,我们可以大概通过类比概括出:容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些

# Docker 思想

# Docker 是什么

通过四点向你说明 Docker 到底是个什么东西。

  • Docker 是世界领先的软件容器平台,基于 Go 语言 进行开发实现。
  • Docker 可以对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
  • Docker 能够自动执行重复性任务,例如搭建和配置开发环境,从而解放了开发人员。
  • 用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

# Docker 思想

  • 集装箱
  • 标准化
    • 运输方式
    • 存储方式
    • API 接口
  • 隔离

# Docker 容器的特点

轻量 : 在一台机器上运行的多个 Docker 容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。

标准 : Docker 容器基于开放式标准,能够在所有主流 Linux 版本、Microsoft Windows 以及包括 VM、裸机服务器和云在内的任何基础设施上运行。

安全 : Docker 赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker 默认提供最强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台机器。

# 为什么使用 Docker

借助 Docker,开发者可以将他们的应用以及依赖包打包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低

传统的开发流程中,我们的项目通常需要使用 MySQL、Redis、FastDFS 等等环境,这些环境都是需要我们手动去进行下载并配置的,安装配置流程极其复杂,而且不同系统下的操作也不一样。

Docker 的出现完美地解决了这一问题,我们可以在容器中安装 MySQL、Redis 等软件环境,使得应用和环境架构分开,它的优势在于:

  • 一致的运行环境:Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 “这段代码在我机器上没问题啊” 这类问题;
  • 快速启动:可以做到秒级、甚至毫秒级的启动时间,大大的节约了开发、测试、部署的时间;
  • 隔离性:避免公用的服务器,资源会容易受到其他用户的影响;
  • 弹性伸缩,快速扩展:善于处理集中爆发的服务器使用压力;
  • 迁移方便:可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况;
  • 持续交付和部署:使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署;

# 容器 vs 虚拟机

容器是一种虚拟化技术,常与虚拟机作比较。而 Docker 只是容器的一种实现,是一个容器化的解决方案和平台。不要把二者混为一谈。

每当说起容器,我们不得不将其与虚拟机做一个比较。就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存

简单来说:容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,而不是硬件,因此容器更容易移植,效率也更高

image-20240121175824099

虚拟化(Hypervisor)技术

image-20240121180015087

虚拟机(VM)示意图

image-20240121180238543

容器示意图

# 对比图

传统虚拟机技术(VM)是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便

img

# 二者总结

img

  • 容器是一个应用层抽象,用于将代码和依赖资源打包在一起。多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行。与虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完成启动
  • 虚拟机 (VM) 是一个物理硬件层抽象,用于将一台服务器变成多台服务器 管理程序允许多个 VM 在一台机器上运行。每个 VM 都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此 占用大量空间 。而且 VM 启动也十分缓慢

通过 Docker 官网,我们知道了这么多 Docker 的优势,但是大家也没有必要完全否定虚拟机技术,因为两者有不同的使用场景。虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而 Docker 通常用于隔离不同的应用 ,例如前端,后端以及数据库。

# 二者可共存

img

Docker与虚拟机的共存关系

# Docker 基本概念

Docker 中有非常重要的三个基本概念,理解了这三个概念,就理解了 Docker 的整个生命周期。

  • 镜像(Image)Image 是一个只读的模板,可以用来创建多个 Container 。相当于是一个 root 文件系统,是用于创建 Container 的模板

  • 容器(Container)Container 是 Image 的一个运行实例,它提供了一个独立的可移植的环境,可以在这个环境中运行应用程序。Image 和 Container 的关系,就像是面向对象程序设计中的类和实例一样,Image 是静态的定义(类),Container 是 Image 运行时的实体(实例)。Container 是独立运行的一个或一组应用,是 Image 运行时的实体,可以被创建、启动、停止、删除、暂停等。

  • 仓库(Repository):用来存放 Image 的仓库,类似于代码控制中心。

    注册表(Registry)包含多个 Repository,每个 Repository 可以包含多个 Tag,每个 Tag 对应一个 Image

理解了这三个概念,就理解了 Docker 的整个生命周期

An Introductory Guide To Docker for Beginners

Docker 基本概念

image-20240121181052546

Docker 架构

# 镜像(Image):一个特殊的文件系统

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

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

Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。Image 实际是由多层文件系统联合组成。

**Image 是逐层构建的,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。** 比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随 Image 。因此在构建 Image 的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

分层存储的特征还使得 Image 的复用、定制变的更为容易。甚至可以用之前构建好的 Image 作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的 Image 。

# 容器(Container):Image 运行时的实体

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,Image 是静态的定义,Container 是 Image 运行时的实体。Container 可以被创建、启动、停止、删除、暂停等

Container 的实质是进程,但与直接在宿主执行的进程不同,Container 进程运行于属于自己的独立的命名空间。前面讲过 Image 使用的是分层存储,Container 也是分层存储

Container 存储层的生存周期和 Container 一样,Container 消亡时,Container 存储层也随之消亡。因此,任何保存于 Container 存储层的信息都会随 Container 删除而丢失。

按照 Docker 最佳实践的要求,Container 不应该向其存储层内写入任何数据 ,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主 (或网络存储) 发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器可以随意删除、重新 run ,数据却不会丢失。

# 仓库(Repository):集中存放 Image 文件的地方

# 概念

Image 构建完成后,可以很容易的在当前宿主上运行,但是,如果需要在其它服务器上使用这个 Image,我们就需要一个集中的存储、分发 Image 的服务,Docker Registry 就是这样的服务。

一个 Registry 中可以包含多个 Repository,每个 Repository 可以包含多个 Tag,每个 Tag 对应一个 Image。所以说:镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。

通常,一个 Repository 会包含同一个软件不同版本的 Image,而 tag 就常用于对应该软件的各个版本 。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。


这里补充一下 Docker Registry 公开服务私有 Docker Registry 的概念:

# Registry 公开服务

Docker Registry 公开服务是开放给用户使用、允许用户管理 Image 的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的 Image,并可能提供收费服务供用户管理私有 Image 。最常使用的 Registry 公开服务是官方的 Docker Hub ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:https://hub.docker.com/ 。官方是这样介绍 Docker Hub 的:

Docker Hub 是 Docker 官方提供的一项服务,用于与您的团队查找和共享容器镜像。

比如我们想要搜索自己想要的镜像:

利用Docker Hub 搜索镜像

利用Docker Hub 搜索镜像

在 Docker Hub 的搜索结果中,有几项关键的信息有助于我们选择合适的镜像:

  • OFFICIAL Image:代表镜像为 Docker 官方提供和维护,相对来说稳定性和安全性较高。
  • Stars:和点赞差不多的意思,类似 GitHub 的 Star。
  • Downloads:代表镜像被拉取的次数,基本上能够表示镜像被使用的频度。

当然,除了直接通过 Docker Hub 网站搜索镜像这种方式外,我们还可以通过 docker search 这个命令搜索 Docker Hub 中的镜像,搜索的结果是一致的。

➜  ~ docker search mysql
NAME                              DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
mysql                             MySQL is a widely used, open-source relation…   8763                [OK]
mariadb                           MariaDB is a community-developed fork of MyS…   3073                [OK]
mysql/mysql-server                Optimized MySQL Server Docker images. Create…   650                                     [OK]

在国内访问 Docker Hub 可能会比较慢,国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像库网易云镜像服务DaoCloud 镜像市场阿里云镜像库等。

# 私有 Registry

除了使用公开服务外,用户还可以 **在本地搭建私有 Docker Registry** 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

# Dockerfile

容器化的过程包括三个步骤:

  1. 编写 Dockerfile
  2. 使用 Dockerfile 构建 Image
  3. 使用 Image 创建和运行 Container

其中,Dockerfile 是一个包含指令的文本文件,用于告诉 Docker 如何构建应用程序的 Image,包括应用程序的命令、依赖、配置环境和运行环境等内容。一般在项目根目录下创建一个名为 Dockerfile 的文件,第一个字母 D 大写,其他都小写。

有了 Image 之后,就可以使用该 Image 来创建 Container,并在 Container 中运行应用程序

image-20240121194842382

# Docker Compose

docker compose 可以定义和运行多个 docker 容器和应用程序,解决服务之间关联关系的问题。通过一个单独的 docker-compose.yml 配置文件,将一组互相关联的容器组合在一起,形成一个项目,然后使用一条命令 docker compose up 就可以启动、停止或重建这些服务,方便管理。这对于提高开发效率和减少沟通成本非常有用,建议初学者学习使用。

image-20240121194754264

# Docker 常见命令

# 基本命令

docker version # 查看 docker 版本
docker images # 查看所有已下载镜像,等价于:docker image ls 命令
docker container ls # 查看所有容器
docker ps #查看正在运行的容器
docker image prune # 清理临时的、没有被使用的镜像文件。-a, --all: 删除所有没有用的镜像,而不仅仅是临时文件;

# 拉取镜像

docker search mysql # 查看 mysql 相关镜像
docker pull mysql:5.7 # 拉取 mysql 镜像
docker image ls # 查看所有已下载镜像

# 删除镜像

比如我们要删除我们下载的 mysql 镜像。

通过 docker rmi [image] (等价于 docker image rm [image] )删除镜像之前首先要确保这个镜像没有被容器引用(可以通过 tag 名称或者 image ID 删除)。

举个例子,通过我们前面讲的 docker ps 命令即可查看。

➜  ~ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
c4cd691d9f80        mysql:5.7           "docker-entrypoint.s…"   7 weeks ago         Up 12 days          0.0.0.0:3306->3306/tcp, 33060/tcp   mysql

可以看到 mysql 正在被 id 为 c4cd691d9f80 的容器引用,我们需要首先通过 docker stop c4cd691d9f80 或者 docker stop mysql 暂停这个容器。

然后查看 mysql 镜像的 id

➜  ~ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
mysql                   5.7                 f6509bac4980        3 months ago        373MB

通过 IMAGE ID 或者 REPOSITORY 名字即可删除

docker rmi f6509bac4980 #  或者 docker rmi mysql

# Docker 两句口号

  • “Build, Ship and Run(搭建、发送、运行)”
  • “Build once,Run anywhere(搭建一次,到处能用)”

如果你搜索 Docker 官网,会发现如下的字样:“Docker - Build, Ship, and Run Any App, Anywhere”。那么 Build, Ship, and Run 到底是在干什么呢?

img

  • Build(构建镜像):镜像就像是集装箱,包括文件以及运行环境等等资源。
  • Ship(运输镜像):主机和仓库间运输,这里的仓库就像是超级码头一样。
  • Run(运行镜像):运行的镜像就是一个容器,容器就是运行程序的地方。

Docker 运行过程也就是去仓库把镜像拉到本地,然后用一条命令把镜像运行起来变成容器。所以,我们也常常将 Docker 称为码头工人或码头装卸工,这和 Docker 的中文翻译搬运工人如出一辙。

# Docker 底层原理

# 虚拟化技术

首先,Docker 容器虚拟化技术为基础的软件,那么什么是虚拟化技术呢?

简单点来说,虚拟化技术可以这样定义:

虚拟化技术是一种资源管理技术,是将计算机的各种实体资源CPU内存磁盘空间网络适配器等),予以抽象、转换后呈现出来并可供分割、组合为一个或多个电脑配置环境。由此,打破实体结构间的不可切割的障碍,使用户可以比原本的配置更好的方式来应用这些电脑硬件资源。这些资源的新虚拟部分是不受现有资源的架设方式,地域或物理配置所限制。一般所指的虚拟化资源包括计算能力和数据存储。

# Docker 基于 LXC 虚拟容器技术

Docker 技术是基于 LXC(Linux container- Linux 容器)虚拟容器技术的。

LXC,其名称来自 Linux 软件容器(Linux Containers)的缩写,一种操作系统层虚拟化(Operating system–level virtualization)技术,为 Linux 内核容器功能的一个用户空间接口。它将应用软件系统打包成一个软件容器(Container),内含应用软件本身的代码,以及所需要的操作系统核心和库。通过统一的名字空间和共用 API 来分配不同软件容器的可用硬件资源,创造出应用程序的独立沙箱运行环境,使得 Linux 用户可以容易的创建和管理系统或应用容器。

LXC 技术主要是借助 Linux 内核中提供的 CGroup 功能和 namespace 来实现的,通过 LXC 可以为软件提供一个独立的操作系统运行环境

cgroup 和 namespace 介绍:

  • namespace 是 Linux 内核用来隔离内核资源的方式。 通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。

  • CGroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组 (process groups) 所使用的物力资源 (如 cpu memory i/o 等等) 的机制

cgroup 和 namespace 两者对比:

两者都是将进程进行分组,但是两者的作用还是有本质区别。namespace 是为了隔离进程组之间的资源,而 cgroup 是为了对一组进程进行统一的资源监控和限制

# 总结

本文主要把 Docker 中的一些常见概念做了详细的阐述,但是并不涉及 Docker 的安装、镜像的使用、容器的操作等内容。这部分东西,希望读者自己可以通过阅读书籍与官方文档的形式掌握。如果觉得官方文档阅读起来很费力的话,这里推荐以下内容:

  • 《Docker 技术入门与实战第二版》[前言 - Docker — 从入门到实践 (gitbook.io)]
  • 10 分钟看懂 Docker 和 K8S
  • 从零开始入门 K8s:详解 K8s 容器基本概念

# Docker 实战

# 安装

# Windows

# Mac

直接使用 Homebrew 安装即可

brew install --cask docker

# Linux

下面来看看 Linux 中如何安装 Docker,这里以 CentOS7 为例。

在测试或开发环境中,Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,执行这个脚本后就会自动地将一切准备工作做好,并且把 Docker 的稳定版本安装在系统中。

curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun

安装完成后直接启动服务:

systemctl start docker

推荐设置开机自启,执行指令:

systemctl enable docker

# 几个概念

# 镜像(Image)

Image 是一个只读的模板,可以用来创建多个 Container 

# 容器(Container)

Container 是根据 Image 创建的运行实例,Docker 利用 Container 独立运行一个或一组应用。它可以被启动、开始、停止、删除,每个 Container 都是相互隔离的、保证安全的平台。 可以把 Container 看作是一个简易的 Linux 环境和运行在其中的应用程序。Container 的定义和 Image 几乎一模一样,也是一堆层的统一视角,唯一区别在于 Container 的最上面那一层是可读可写的

# 仓库(Repository)

仓库(Repository)是集中存放 Image 文件的场所。仓库(Repository)和仓库注册服务器(Registry)是有区别的,Registry 上往往存放着多个 Repository,每个 Repository 中又包含了多个 Image,每个 Image 有不同的 Tag。Repository 分为公开 Repository 和私有 Repository 两种形式,最大的公开 Repository 是 DockerHub,存放了数量庞大的镜像供用户下载,国内的公开仓库有阿里云、网易云

# 总结

通俗点说,一个 Image 就代表一个软件;而基于某个 Image 运行就是生成一个程序实例,这个程序实例就是 Container ;而 Repository 是用来存储 Docker 中所有 Image 的。

其中 Repository 又分为远程 Repository 和本地 Repository。

和 Maven 类似,倘若每次都从远程下载依赖,则会大大降低效率。为此,Maven 的策略是第一次访问依赖时,将其下载到本地仓库,第二次、第三次使用时直接用本地仓库的依赖即可,Docker 的远程 Repository 和本地 Repository 的作用也是类似的。

# 初体验

下面我们来对 Docker 进行一个初步的使用,这里以下载一个 MySQL 的镜像为例 (在CentOS7下进行)

和 GitHub 一样,Docker 也提供了一个 DockerHub 用于查询各种镜像的地址和安装教程,为此,我们先访问 DockerHub:https://hub.docker.com/

DockerHub

DockerHub

在左上角的搜索框中输入 MySQL 并回车:

DockerHub 搜索 MySQL

DockerHub 搜索 MySQL

可以看到相关 MySQL 的镜像非常多,若右上角有 OFFICIAL IMAGE 标识,则说明是官方镜像,所以我们点击第一个 MySQL 镜像:

MySQL 官方镜像

MySQL 官方镜像

右边提供了下载 MySQL 镜像的指令为 docker pull MySQL ,但该指令始终会下载 MySQL 镜像的最新版本。

若是想下载指定版本的镜像,则点击下面的 View Available Tags

查看其他版本的 MySQL

查看其他版本的 MySQL

这里就可以看到各种版本的镜像,右边有下载的指令,所以若是想下载 5.7.32 版本的 MySQL 镜像,则执行:

docker pull MySQL:5.7.32

然而下载镜像的过程是非常慢的,所以我们需要配置一下镜像源加速下载,访问 阿里云 官网,点击控制台

阿里云镜像加速

阿里云镜像加速

然后点击左上角的菜单,在弹窗的窗口中,将鼠标悬停在产品与服务上,并在右侧搜索容器镜像服务,最后点击容器镜像服务:

阿里云镜像加速

阿里云镜像加速

点击左侧的镜像加速器,并依次执行右侧的配置指令即可。

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://679xpnpz.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

# Image 指令

若想 **查看 Docker 中当前拥有哪些镜像**,则可以使用 docker images 命令。

[root@izrcf5u3j3q8xaz ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
MySQL         5.7.32    f07dfa83b528   11 days ago     448MB
tomcat        latest    feba8d001e3f   2 weeks ago     649MB
nginx         latest    ae2feff98a0c   2 weeks ago     133MB
hello-world   latest    bf756fb1ae65   12 months ago   13.3kB

其中 REPOSITORY 为镜像名TAG 为版本标志, IMAGE ID 为镜像 id (唯一的), CREATED 为创建时间,注意这个时间并不是我们将镜像下载到 Docker 中的时间,而是镜像创建者创建的时间SIZE 为镜像大小。


该指令能够 **根据镜像名查询指定镜像**:

docker image MySQL

若如此做,则会查询出 Docker 中的所有 MySQL 镜像:

[root@izrcf5u3j3q8xaz ~]# docker images MySQL
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
MySQL        5.6       0ebb5600241d   11 days ago     302MB
MySQL        5.7.32    f07dfa83b528   11 days ago     448MB
MySQL        5.5       d404d78aa797   20 months ago   205MB

该指令还能够携带 -q 参数: docker images -q-q 表示仅显示镜像的 id

[root@izrcf5u3j3q8xaz ~]# docker images -q
0ebb5600241d
f07dfa83b528
feba8d001e3f
d404d78aa797

若是要 **下载指定版本的镜像**,则使用:

docker pull MySQL:5.7

docker pull 是固定的,后面写上需要下载的镜像名及版本标志;若是不写版本标志,而是直接执行 docker pull MySQL ,则会下载镜像的最新版本。

一般在下载镜像前我们需要 **搜索一下镜像有哪些版本** 才能对指定版本进行下载,使用指令:

docker search MySQL

img

不过该指令只能查看 MySQL 相关的镜像信息,而不能知道有哪些版本,若想知道版本,则只能这样查询:

docker search MySQL:5.5

若是查询的版本不存在,则结果为空:

img


**删除镜像** 使用指令:

docker image rm MySQL:5.5

若是不指定版本,则默认删除的也是最新版本。

还可以通过指定镜像 id 进行删除

docker image rm bf756fb1ae65

然而此时报错了:

[root@izrcf5u3j3q8xaz ~]# docker image rm bf756fb1ae65
Error response from daemon: conflict: unable to delete bf756fb1ae65 (must be forced) - image is being used by stopped container d5b6c177c151

这是因为要删除的 hello-world 镜像正在运行中,所以无法删除镜像,此时需要强制执行删除

docker image rm -f bf756fb1ae65

该指令会将镜像和通过该镜像执行的容器全部删除,谨慎使用


Docker 还提供了 **删除镜像的简化版本**: docker rmi 镜像名:版本标志

此时我们即可借助 rmi-q 进行一些联合操作,比如现在想删除所有的 MySQL 镜像,那么你需要查询出 MySQL 镜像的 id,并根据这些 id 一个一个地执行 docker rmi 进行删除,但是现在,我们可以这样:

docker rmi -f $(docker images MySQL -q)

首先通过 docker images MySQL -q 查询出 MySQL 的所有镜像 id, -q 表示仅查询 id,并将这些 id 作为参数传递给 docker rmi -f 指令,这样所有的 MySQL 镜像就都被删除了。

# Container 指令

掌握了镜像的相关指令之后,我们需要了解一下容器的指令,容器是基于镜像的。

若需要 **通过 Image 运行一个 Container**,则使用:

docker run tomcat:8.0-jre8

当然了,运行的前提是你拥有这个镜像,所以先下载镜像

docker pull tomcat:8.0-jre8

下载完成后就可以运行了,运行后 **查看一下当前运行的 Container**: docker ps

img

其中 CONTAINER_ID 为容器的 id, IMAGE 为镜像名, COMMAND 为容器内执行的命令CREATED 为容器的创建时间, STATUS 为容器的状态, PORTS 为容器内服务监听的端口NAMES 为容器的名称

该指令有几个参数,比如 -a

docker ps -a

该参数会将运行和非运行的容器全部列举出来

-q 参数将查询结果只显示容器 iddocker ps -q

[root@izrcf5u3j3q8xaz ~]# docker ps -q
f3aac8ee94a3
074bf575249b
1d557472a708
4421848ba294

若是组合使用,则查询运行和非运行的所有容器 id: docker ps -qa

[root@izrcf5u3j3q8xaz ~]# docker ps -aq
f3aac8ee94a3
7f7b0e80c841
074bf575249b
a1e830bddc4c
1d557472a708
4421848ba294
b0440c0a219a
c2f5d78c5d1a
5831d1bab2a6
d5b6c177c151

通过该方式运行的 tomcat 是不能直接被外部访问的,因为容器具有隔离性,若是想直接通过 8080 端口访问容器内部的 tomcat,则需要 **对宿主机端口与 Container 内的端口进行映射**:

docker run -p 8080:8080 tomcat:8.0-jre8

解释一下这两个端口的作用 ( 8080:8080 ),第一个为宿主机端口,第二个为容器内的端口,外部访问 8080 端口就会通过映射访问容器内的 8080 端口。

此时外部就可以访问 Tomcat 了:

img

若是这样进行映射:

docker run -p 8088:8080 tomcat:8.0-jre8

则外部需访问 8088 端口才能访问 tomcat,需要注意的是,每次运行的容器都是相互独立的,所以同时运行多个 tomcat 容器并不会产生端口的冲突


通过 -d ,容器还能够以后台的方式运行,这样就不会占用终端:

docker run -d -p 8080:8080 tomcat:8.0-jre8

启动容器时默认会给容器一个名称,但容器名称其实是可以通过 --name 设置的,使用指令:

docker run -d -p 8080:8080 --name tomcat01 tomcat:8.0-jre8

此时的容器名称即为 tomcat01,容器名称必须是唯一的


接下来是 **容器的运行、重启、暂停、杀死** 指令,因为非常简单,就不过多介绍了。

docker start c2f5d78c5d1a

通过该指令能够将已经停止运行的容器运行起来,可以通过容器的 id 启动,也可以通过容器的名称启动。

docker restart c2f5d78c5d1a

该指令能够重启指定的容器。

docker stop c2f5d78c5d1a

该指令能够停止指定的容器。

docker kill c2f5d78c5d1a

该指令能够直接杀死指定的容器。

以上指令都能够通过容器的 id 和容器名称两种方式配合使用


当容器被停止之后,容器虽然不再运行了,但仍然是存在的,若是想 **删除容器**,则使用指令:

docker rm d5b6c177c151

需要注意的是容器的 id 无需全部写出来,只需唯一标识即可

若是想 **删除正在运行的容器**,则需要添加 -f 参数强制删除:

docker rm -f d5b6c177c151

若是想 **删除所有容器**,则可以使用组合指令:

docker rm -f $(docker ps -qa)

先通过 docker ps -qa 查询出所有容器的 id,然后通过 docker rm -f 进行删除。


当容器以后台的方式运行时,我们无法知晓容器的运行状态,若此时需要 **查看容器的运行日志**,则使用指令:

docker logs 289cc00dc5ed

这样的方式显示的日志并不是实时的,若是想实时显示,需要使用 -f 参数:

docker logs -f 289cc00dc5ed

通过 -t 参数还能够显示日志的时间戳,通常与 -f 参数联合使用:

docker logs -ft 289cc00dc5ed

查看容器内运行了哪些进程,可以使用指令:

docker top 289cc00dc5ed

若是 **想与容器进行交互**,则使用指令:

docker exec -it 289cc00dc5ed bash

此时终端将会进入容器内部,执行的指令都将在容器中生效,在容器内只能执行一些比较简单的指令,如:ls、cd 等,若是想退出容器终端,重新回到 CentOS 中,则执行 exit 即可。

现在我们已经能够进入容器终端执行相关操作了,那么该如何向 tomcat 容器中部署一个项目呢?

docker cp ./test.html 289cc00dc5ed:/usr/local/tomcat/webapps

通过 docker cp 指令能够将文件从 CentOS 复制到容器中, ./test.html 为 CentOS 中的资源路径, 289cc00dc5ed 为容器 id, /usr/local/tomcat/webapps 为容器的资源路径,此时 test.html 文件将会被复制到该路径下。

[root@izrcf5u3j3q8xaz ~]# docker exec -it 289cc00dc5ed bash
root@289cc00dc5ed:/usr/local/tomcat# cd webapps
root@289cc00dc5ed:/usr/local/tomcat/webapps# ls
test.html
root@289cc00dc5ed:/usr/local/tomcat/webapps#

若是想将容器内的文件复制到 CentOS 中,则反过来写即可:

docker cp 289cc00dc5ed:/usr/local/tomcat/webapps/test.html ./

所以现在若是想要部署项目,则先将项目上传到 CentOS,然后将项目从 CentOS 复制到容器内,此时启动容器即可。


虽然使用 Docker 启动软件环境非常简单,但同时也面临着一个问题,我们无法 **查看容器内部具体的细节,比如监听的端口、绑定的 ip 地址等等**,好在这些 Docker 都帮我们想到了,只需使用指令:

docker inspect 923c969b0d91

img


最后再介绍几个容器和镜像相关的指令:

docker commit -m "描述信息" -a "镜像作者" tomcat01 my_tomcat:1.0

tomcat01 为 Container 名称,my_tomcat 为打包后的 Image 名称,1.0 是对应的 tag。

该指令能够 **将 Container 打包成一个 Image**,此时查询镜像:

[root@centos-7 _data]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
my_tomcat           1.0                 79ab047fade5        2 seconds ago       463MB
tomcat              8                   a041be4a5ba5        2 weeks ago         533MB
MySQL               latest              db2b37ec6181        2 months ag

若是想 **将 Image 备份出来** 出来,则可以使用指令:

docker save my_tomcat:1.0 -o my-tomcat-1.0.tar
[root@centos-7 ~]# docker save my_tomcat:1.0 -o my-tomcat-1.0.tar
[root@centos-7 ~]# ls
anaconda-ks.cfg  initial-setup-ks.cfg  公共  视频  文档  音乐
get-docker.sh    my-tomcat-1.0.tar     模板  图片  下载  桌面

将 .tar 格式的 Image 加载到 Docker 中,执行指令:

docker load -i my-tomcat-1.0.tar
root@centos-7 ~]# docker load -i my-tomcat-1.0.tar
b28ef0b6fef8: Loading layer [==================================================>]  105.5MB/105.5MB
0b703c74a09c: Loading layer [==================================================>]  23.99MB/23.99MB
......
Loaded image: my_tomcat:1.0
[root@centos-7 ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
my_tomcat           1.0                 79ab047fade5        7 minutes ag

# 数据卷(volume)

学习了容器的相关指令之后,我们来了解一下 Docker 中的数据卷,它能够实现宿主机与容器之间的文件共享,它的好处在于我们对宿主机的文件进行修改将直接影响容器,而无需再将宿主机的文件再复制到容器中

现在若是想将宿主机中 /opt/apps 目录与容器中 webapps 目录 **做一个数据卷**,则应该通过 -v 参数这样编写指令:

docker run -d -p 8080:8080 --name tomcat01 -v /opt/apps:/usr/local/tomcat/webapps tomcat:8.0-jre8

然而此时访问 tomcat 会发现无法访问:

img

这就说明我们的数据卷设置成功了,Docker 会将容器内的 webapps 目录与 /opt/apps 目录进行同步,而此时 /opt/apps 目录是空的,导致 webapps 目录也会变成空目录,所以就访问不到了。

此时我们只需向 /opt/apps 目录下添加文件,就会使得 webapps 目录也会拥有相同的文件,达到文件共享,测试一下:

[root@centos-7 opt]# cd apps/
[root@centos-7 apps]# vim test.html
[root@centos-7 apps]# ls
test.html
[root@centos-7 apps]# cat test.html
<h1>This is a test html!</h1>

/opt/apps 目录下创建了一个 test.html 文件,那么容器内的 webapps 目录是否会有该文件呢?进入容器的终端:

[root@centos-7 apps]# docker exec -it tomcat01 bash
root@115155c08687:/usr/local/tomcat# cd webapps/
root@115155c08687:/usr/local/tomcat/webapps# ls
test.html

容器内确实已经有了该文件,那接下来我们编写一个简单的 Web 应用:

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("Hello World!");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

这是一个非常简单的 Servlet,我们将其打包上传到 /opt/apps 中,那么容器内肯定就会同步到该文件,此时进行访问:

img


这种方式设置的数据卷称为自定义数据卷,因为数据卷的目录是由我们自己设置的,Docker 还为我们提供了 **另外一种设置数据卷的方式**:

docker run -d -p 8080:8080 --name tomcat01 -v aa:/usr/local/tomcat/webapps tomcat:8.0-jre8

此时的 aa 并不是数据卷的目录,而是数据卷的别名,Docker 会为我们自动创建一个名为 aa 的数据卷,并且会将容器内 webapps 目录下的所有内容复制到数据卷中,该数据卷的位置在 /var/lib/docker/volumes 目录下:

[root@centos-7 volumes]# pwd
/var/lib/docker/volumes
[root@centos-7 volumes]# cd aa/
[root@centos-7 aa]# ls
_data
[root@centos-7 aa]# cd _data/
[root@centos-7 _data]# ls
docs  examples  host-manager  manager  ROOT

此时我们只需修改该目录的内容就能能够影响到容器。

更新于 阅读次数