[06] Docker基础(上)
1. Docker 引入
1.1 云计算
设计一个超大容量、超高并发(同时访问)、超快速度、超强安全的云计算系统,满足亿级业务平稳运行的要求。这才是云计算的复杂之处。
- 第 1 层次:是最底层的硬件资源,主要包括 CPU(计算资源),硬盘(存储资源),还有网卡(网络资源)等;
- 第 2 层次:要高级一些,我不打算直接使用 CPU、硬盘、网卡,我希望你把操作系统(例如 Windows、Linux)装好,把数据库软件装好,我再来使用;
- 第 3 层次:更高级一些,你不但要装好操作系统这些基本的,还要把具体的应用软件装好,例如 FTP 服务端软件、在线视频服务端软件等,我可以直接使用服务。
这三种层次,就是大家经常听到的 IaaS、Paas、SaaS。
- IaaS:Infrastructure-as-a-Service,基础设施即服务;
- PaaS:Platform-as-a-Service,平台即服务;
- SaaS:Software-as-a-Service,软件即服务。
目前主流的云计算服务提供商,例如 AWS、阿里云、华为云、天翼云、腾讯云,说白了,都是为大家提供以上三个层次的云资源。你想要什么,它们就提供什么。你想要多少,它们就提供多少。
这么多样化多层次的云计算服务,阿里、华为们又是怎么提供的呢?
难道说,是人工安排?当你要八核 CPU、16GB 内存、500GB 硬盘的服务器,阿里临时安排工程师帮你组装?如果你要装 CentOS 7.8、MySQL 5.7.31,阿里也临时让工程师帮你安装配置?这显然是不可能的,耗不起人力,也等不起时间。于是,就有了各种软件和平台,负责对资源进行快速调用和集中管理。
1.2 什么是虚拟化?
如果要对物理资源进行管理,第一步,就是“虚拟化”。虚拟化是云计算的基础。简单来说,虚拟化就是在一台物理服务器上,运行多台“虚拟服务器”。这种虚拟服务器,也叫虚拟机(VM,Virtual Machine)。
从表面来看,这些虚拟机都是独立的服务器,但实际上,它们共享物理服务器的 CPU、内存、硬件、网卡等资源。通过模拟计算机的硬件,来实现在同一台计算机上同时运行不同的操作系统的技术。常用的 vmwore、openstack、kvm 都是使用的虚拟化技术物理机,通常称为“宿主机(Host)”,虚拟机则称为“客户机(Guest)”。
谁来完成物理资源虚拟化的工作呢?就是大名鼎鼎的 Hypervisor 。
Hypervisor 也叫做 VMM(Virtual Machine Monitor,虚拟机监视器)。它不是一款具体的软件,而是一类软件的统称。Hypervisor 是一种运行在基础物理服务器硬件之上的软件层,可以虚拟化硬件资源,例如:CPU、硬盘、内存、声卡等资源。然后我们可以通过在虚拟化出来的资源之上安装操作系统。也就是所谓的虚拟机。通过 Hyperviosr 我们可以创建不同的虚拟机,并且每个虚拟机都是分离、独立的系统。这样操作,我们就可以在一台硬件服务器和本地操作系统之上虚拟化出很多服务器,供我们用来部署应用程序。一台硬件服务器可以虚拟化多台服务器,让计算机资源得以充分利用。
Hypervisor 分为两大类:
- hypervisor 直接运行在物理机之上,虚拟机运行在 hypervisor 之上;
- 物理机上安装正常的操作系统(例如 Linux 或 Windows),然后在正常操作系统上安装 hypervisor,生成和管理虚拟机。像 VMware、KVM、Xen、Virtual Box,都属于 Hypervisor。
人们在使用虚拟化一段时间后,发现它存在一些问题,不同的用户,有时候只是希望运行各自的一些简单程序,跑一个小进程。为了不相互影响,就要建立虚拟机。如果建虚拟机,显然浪费就会有点大,而且操作也比较复杂,花费时间也会比较长。而且,有的时候,想要迁移自己的服务程序,就要迁移整个虚拟机。显然,迁移过程也会很复杂。安装的虚拟机越多,消耗的资源对应越多。环境兼容性问题,开发时的环境运行正常,部署到虚拟机环境进行测试则有可能发生错误。
有没有办法更灵活快速一些呢?有,这就引入了“容器(Container)”。
1.3 什么是容器?
基于硬件级虚拟化技术的缺点和不足,后续又发展出来另一种虚拟化技术,即操作系统级别的虚拟化技术。操作系统级虚拟化是运行在操作系统之上的虚拟化技术,它模拟的是运行在一个操作系统上的多个不同进程,并将其封闭在一个密闭的容器内,该技术也就被称之为“容器化技术”。
容器就是在隔离环境运行的一个进程,如果进程停止,容器就会销毁。隔离的环境拥有自己的系统文件、IP 地址、主机名等。容器也是虚拟化,但是属于“轻量级”的虚拟化。它的目的和虚拟机一样,都是为了创造“隔离环境”。但是,它又和虚拟机有很大的不同 —— 虚拟机是操作系统级别的资源隔离,而容器本质上是进程级的资源隔离。
容器和虚拟化的区别
容器是将代码和环境打包在一起的一个集合,而虚拟机是在物理层面上分离出来一个操作系统;多个容器可以运行在同一台硬件服务器上,并共享一个操作系统的内核资源。多个虚拟机也可以运行在同一台服务器上,但每个虚拟机都需要有一个完整的操作系统。
1.4 Docker
一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发 + 运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的迭代之后,不同版本环境的兼容,对运维人员都是考验。
我们写的代码会接触到好几个环境:开发环境、测试环境以及生产环境。
Docker 之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案。
环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题。
“水土”不服就把“水土”也迁过去。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。容器是完全使用沙箱机制,相互隔离;容器性能开销极低。
传统上认为,软件编码开发/测试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码等(Java 为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让维运团队得以部署应用程式,开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。Docker 镜像的设计,使得 Docker 得以打破过去「程序即应用」的观念。透过「镜像(images)」将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
a. 概述
Docker 是一个用 Go 语言实现的开源的应用容器引擎,基于 Linux 内核的 cGroup、Namespace 以及 OverlayFS 的联合文件系统(UnionFS)等技术,对进程进行封装隔离,属于 OS 层面的虚拟化技术。由于隔离的进程独立与宿主机和其他的隔离进程,因此也称其为“容器”。
从 17.03 版本之后 Docker 分为 CE(Community Edition,社区版)和 EE(Enterprise Edition,企业版)。
Docker 的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的 APP(可以是一个 WEB 应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。
Linux 容器技术的出现就解决了这样一个问题,而 Docker 就是在它的基础上发展过来的。将应用运行在 Docker 容器上面,而 Docker 容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
小结:解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的「容器虚拟化技术」。
b. 虚拟机&容器虚拟化
虚拟机(Virtual Machine)就是带环境安装的一种解决方案。
它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。
这类虚拟机完美的运行了另一套系统,能够使应用程序、操作系统和硬件三者之间的逻辑不变。
虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点:资源占用多;冗余步骤多;启动慢。
由于前面虚拟机存在上述这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
容器是进程级别的,相比虚拟机有很多优势:启动快;资源占用少;体积小。
比较上面两张图,我们发现虚拟机是携带操作系统,本身很小的应用程序却因为携带了操作系统而变得非常大,很笨重。Docker 是不携带操作系统的,所以 Docker 的应用就非常的轻巧。另外在调用宿主机的 CPU、磁盘等等这些资源的时候,拿内存举例,虚拟机是利用 Hypervisor 去虚拟化内存,整个调用过程是虚拟内存 → 虚拟物理内存 → 真正物理内存,但是 Docker 是利用 Docker Engine 去调用宿主的的资源,这时候过程是虚拟内存 → 真正物理内存。
比较了 Docker 和传统虚拟化方式的不同之处:
- 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程。
- 容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
- 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。
c. Why Docker?
容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而 Docker 只需要启动 10 个隔离的应用即可。
具体说来,Docker 在如下几个方面具有较大的优势 → DevOps(开发自运维)
- 【更快速的应用交付和部署】传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。Docker 化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间;
- 【更便捷的升级和扩缩容】随着微服务架构和 Docker 的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个 Docker 容器将变成一块“积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级;
- 【更简单的系统运维】应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的 BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复;
- 【更高效的计算资源利用】Docker 是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的 Hypervisor 支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的 CPU 和内存的利用率。
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
2. Docker 组成
左边是 Docker 的客户端,类似我们操作 MySQL 的工具 Navicat,只不过我们这里的是没有图形化界面的命令终端。Docker 客户端是用户与 Docker 服务交互的窗口!我们能看到图中就是各种操作的命令。
中间的是 Docker 后台运行的服务,一个称为 Docker daemon 的守护进程。可以理解为我们 MySQL 的服务,我们的操作命令都是在这部分进行处理!Docker deamon 监听着客户端的请求,并且管理着 Docker 的镜像、容器、网络、磁盘(图中只列出了镜像与容器)等对象。同样,Docker 的客户端与服务可以运行在同一机器上,也可以用某台机器上的客户端远程连接另一台机器上的 Docker 服务,这跟我们的 MySQL 一样的呢。
右边部分是注册仓库,在远古时代做开发的都知道,我们以前需要一个第三方包的时候需要去网上下载对应的 jar 包,很麻烦不说,还容易下的包是不稳定的版本。有了 Maven 之后,我们只要在 Maven 配置文件中引入对应的依赖,就可以直接从远程仓库中下载对应版本的 jar 包了。Docker 中的仓库与 Maven 的仓库是一个概念,可以远程下载常用的镜像,也可以 push 包到远程仓库(如图中的 Redis、Nginx 等镜像),同一个镜像又可以有多个版本,在 Docker 中称为 Tag。
2.1 组成说明
镜像
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。
容器与镜像的关系类似于面向对象编程中的对象和类:
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
镜像是一种轻量级的、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件。它包含运行某个软件所需要的所有的内容,包括代码、运行时、库、环境变量、配置文件等。我们开发的 web 应用需要 JDK 环境、需要 Tomcat 容器、需要 Linux 操作系统,那我们可以把我们所需要的一切都进行打包成一个整体(包括自己开发的 web 应用 + JDK + Tomcat + CentOS/Ubuntu + 各种配置文件)。打包后的镜像在某台机器上能运行,那它就能够在任何装有 Docker 的机器上运行。
任何镜像的创建会基于其他的父镜像,也就是说镜像是一层套一层,比如一个 Tomcat 镜像,需要运行在 CentOS/Ubuntu 上,那我们的 Tomcat 镜像就会基于 CentOS/Ubuntu 镜像创建(在后面的操作部分我们可以通过命令查看),这样的结构就类似于我们吃的洋葱。
容器
Docker 利用容器(Container)独立运行的一个或一组应用。「容器」是用「镜像」创建的“运行实例”。
容器可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
上一步我们构建的镜像只是一个静态的文件,这个文件需要运行就需要变为容器,我们可以把容器看做是一个简易版的 Linux 系统(包括 root 用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序!就是前面看到的鲸鱼背上的一个一个的集装箱,每个集装箱都是独立的!
容器的定义和镜像几乎一模一样,也是一堆叠层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
仓库
仓库(Repository)是集中存放“镜像文件”的场所。
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器(Docker Registry)上往往存放着多个仓库(Repository),每个仓库中又包含了多个镜像(Image),每个镜像有不同的标签(tag)。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本,我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像,如果不给出标签,将以 latest 作为默认标签。
以 Ubuntu 镜像为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如 14.04、16.04,我们可以通过 ubuntu:14.04 或 ubuntu:16.04 来具体指定所需哪个版本的镜像,如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。
仓库名经常以三段式路径形式出现,比如 heima.com/nginx-proxy:tag,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名,但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub(https://hub.docker.com),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等。
小结
Docker 本身是一个容器运行载体或称之为“管理引擎”。
我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是 image 镜像文件。
只有通过这个镜像文件才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
image 文件生成的容器实例,本身也是一个文件,称为“镜像文件”。
一个容器运行一种服务,当我们需要的时候,就可以通过 Docker 客户端创建一个对应的运行实例,也就是我们的容器。
至于仓储,就是放了一堆镜像的地方,我们可以把镜像发布到仓储中,需要的时候从仓储中拉下来就可以了。
Q1:Docker 是怎么工作的?
Docker 是一个 Client-Server 结构的系统,Docker 守护进程运行在主机上, 然后通过 Socket 连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们前面说到的集装箱。
Q2:为什么 Docker 比 VM 快?
VM(Virtual Machine)其实就是一个虚拟的硬件环境,在使用 VM 的时候我们都需要在 VM 中装一个完整的 OS 然后再在 OS 中部署我们的 Application,图片中的 Hypervisor 就是 VM 的核心,它用于虚拟硬件环境,然后可以在 Hypervisor 之上安装多个 OS。
Docker 有着比虚拟机更少的抽象层。由于 Docker 不需要 Hypervisor 实现硬件资源虚拟化,运行在 Docker 容器上的程序直接使用的都是实际物理机的硬件资源。因此在 CPU、内存利用率上 Docker 将会在效率上有明显优势。
Docker 利用的是宿主机的内核,而不需要 Guest OS。因此,当新建一个容器时,Docker 不需要和虚拟机一样重新加载一个操作系统内核,从而避免引寻、加载操作系统内核返个比较费时费资源的过程。当新建一个虚拟机时,虚拟机软件需要加载 Guest OS,整个新建过程是分钟级别的。而 Docker 由于直接利用宿主机的操作系统,则省略了整个过程,因此新建一个 Docker 容器只需要几秒钟。
Docker 没有模拟整个硬件环境,它只需要在宿主主机中装 Docker Engine(Docker 引擎),然后就可以在 Image(镜像)创建多个 Container(容器),各个容器运行环境都是独立的。简单来说,每个 Container 都是一个独立的 OS,每个 Container 与宿主使用同一个内核,对进程与资源进行了隔离;Docker 也不是新技术,Docker 只是在 LXC(Linux Container)的基础上进行封装发展起来的。
2.2 拆除 Docker Daemon
随着时间的推移,Docker daemon 的整体性带来了越来越多的问题,难于变更、运行越来越慢,这并非生态(或 Docker 公司)所期望的。
Docker 公司意识到了这些问题,开始努力着手拆解这个大而全的 Docker daemon 进程,并将其模块化,这项任务的目标是尽可能拆解出其中的功能特性,并用小而专的工具来实现它,这些小工具可以是可替换的,也可以被第三方拿去用于构建其他工具。
这一计划遵循了在 UNIX 中得以实践并验证过的一种软件哲学:小而专的工具可以组装为大型工具,这项拆解和重构 Docker 引擎的工作仍在进行中,不过,所有容器执行和容器运行时的代码已经完全从 daemon 中移除,并重构为小而专的工具。
runc
是低级别的容器运行实现(实际创建和运行容器的东西),它包括 libcontainer,一个用于创建容器的基于 Go 的本地实现。runc
是 Docker 贡献给 OCI 的容器运行时,也是目前使用比较多的容器运行时,Docker 目前的实现也是使用的 runc
。如前所述,runc
是 OCI 容器运行时规范的参考实现,Docker 公司参与了规范的制定以及 runc
的开发。
3. Docker 安装
3.1 前提说明
目前,CentOS 仅发行版本中的内核支持 Docker。
- Docker 运行在 CentOS 7 上,要求系统为 64 位、系统内核版本为 3.10 以上。
- Docker 运行在 CentOS 6.5 或更高的版本的 CentOS 上,要求系统为 64 位、系统内核版本为 2.6.32-431 或者更高版本。
查看自己内核的命令:uname -r
。
3.2 安装步骤
- yum 安装 gcc 相关:
yum -y install gcc
,yum -y install gcc-c++
- 卸载旧版本:
yum -y remove docker docker-common docker-selinux docker-engine
- 安装需要的软件包:
yum install -y yum-utils device-mapper-persistent-data lvm2
- 设置 stable 镜像仓库:
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 更新 yum 软件包索引:
yum makecache fast
- 安装 Docker 社区版:
yum -y install docker-ce
- 启动 Docker:
systemctl start docker
(设置开启自启:systemctl enable docker
) - 测试:
docker version
,docker run hello-world
3.3 卸载步骤
systemctl stop docker
yum -y remove docker-ce
rm -rf /var/lib/docker
3.4 镜像加速
鉴于国内网络问题,后续拉取 Docker 镜像十分缓慢,我们可以需要配置加速器来解决,我使用的是阿里云的镜像加速,每个阿里云账户有一个属于自己的镜像地址。
- 递归创建目录:
mkdir -p /etc/docker
- 创建上图蓝框标出的文件:
vim /etc/docker/daemon.json
,将红框标出的部分复制到该文件中。 - 重新加载 Docker 守护进程:
systemctl daemon-reload
- 重启 Docker 服务:
systemctl restart docker
3.5 HelloWorld
docker run hello-world
4. Docker 常用命令
4.1 命令分类
- 环境信息 —
docker [info|version]
- 容器生命周期管理 —
docker [create|exec|run|start|stop|restart|kill|rm|pause|unpause]
- 容器操作管理 —
docker [ps|inspect|top|attach|wait|export|port|rename|stat]
- 容器 rootfs 命令 —
docker [commit|cp|diff]
- 镜像仓库 —
docker [login|pull|push|search]
- 本地镜像管理 —
docker [build|images|rmi|tag|save|import|load]
- 容器资源管理 —
docker [volume|network]
- 系统日志信息 —
docker [events|history|logs]
4.2 镜像命令
a. docker pull
docker pull <镜像名字[:TAG]>
没写版本号,则默认拉的是最新版本,即:docker pull <镜像名字>:latest
。
Q1:如果多个不同的镜像中,同时包含了同一个层,这样重复下载,岂不是导致了存储空间的浪费么?
A1:实际上,Docker 并不会这么傻会去下载重复的层,Docker 在下载之前,会去检测本地是否会有同样 ID 的层,如果本地已经存在了,就直接使用本地的就好了。
Q2:不同仓库中,可能也会存在镜像重名的情况发生,这种情况咋办?
A2:从严格意义上讲,我们在使用 pull 命令时,还需要在镜像前面指定仓库地址(Registry),如果不指定,则 Docker 会使用您默认配置的仓库地址。例如上面,由于我配置的是国内 docker.io 的仓库地址,在 pull 的时候,Docker 会默认为我加上 docker.io/library 的前缀。例如:当我执行 docker pull tomcat:9.0.20-jre8
命令时,实际上相当于 docker pull docker.io/tomcat:9.0.20-jre8
,如果您未自定义配置仓库,则默认在下载的时候,会在镜像前面加上 DockerHub 的地址。Docker 通过前缀地址的不同,来保证不同仓库中,重名镜像的唯一性。
b. docker images
字段说明:
- REPOSITORY:表示镜像的仓库源
- TAG:镜像的标签
- IMAGE ID:镜像 ID
- CREATED:镜像创建时间
- SIZE:镜像大小
同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,我们使用 REPOSITORY:TAG
来定义不同的镜像。如果你不指定一个镜像的版本标签,例如你只使用 Ubuntu,Docker 将默认使用 Ubuntu:latest
镜像。
OPTIONS 说明:
-a
列出本地所有的镜像(含中间映像层)-q
只显示镜像 ID--digests
显示镜像的摘要信息--no-trunc
显示完整的镜像信息
c. docker save/load
(1)save
# 一个镜像 (-o: 输出到的文件)
docker save tomcat:9.0.20-jre8-alpine -o tomcat9.tar
docker save tomcat:9.0.20-jre8-slim > tomcat9.slim.tar
# 推荐开发岗的小伙伴使用idea开发工具中的列编辑模式制作 docker save 命令
mkdir -p /data
cd /data
docker save \
ubuntu:20.04 \
alpine:3.12.1 \
debian:10.6-slim \
centos:7.8.2003 \
-o linux.tar
docker save \
tomcat:9.0.20-jre8-alpine \
tomcat:9.0.20-jre8-slim \
tomcat:9.0.20-jre8 \
-o tomcat9.0.20.tar
(2)load
# --input,-i : 指定导入的文件
# --quiet,-q : 精简输出信息
docker load -i linux.tar
docker load < tomcat9.0.20.tar
d. docker inspect
通过 docker inspect 命令,我们可以获取镜像的详细信息,其中包括创建者、各层的数字摘要等。返回的是 JSON 格式的信息,如果您想获取其中指定的一项内容,可以通过 -f
来指定,如获取镜像大小。
docker inspect tomcat:9.0.20-jre8-alpine
docker inspect -f {{".Size"}} tomcat:9.0.20-jre8-alpine
e. docker search
docker search [OPTIONS] <镜像名字>
OPTIONS 说明:
--no-trunc # 显示完整的镜像描述
-s N # 列出收藏数不小于指定值 N 的镜像
--automated # 只列出 automated build 类型的镜像
搜索 tomcat 镜像:
f. docker tag
tag 命令用来标记本地镜像,将其归入某一仓库。先简单熟悉一下 tag 命令,后边的章节会详细进行讲解。
docker tag tomcat:9.0.20-jre8-alpine tree6x7/tomcat:9
g. docker rmi
删除镜像很简单,但也不是我们何时何地都能删除的,它存在一些限制条件。当通过该镜像创建的容器未被销毁时,镜像是无法被删除的。
- 删除单个:
docker rmi -f <镜像名字ID>
- 删除多个:
docker rmi -f <镜像名1:TAG> <镜像名2:TAG> ...
- 删除全部:
docker rmi -f $(docker images -qa)
// 组合命令
-f, -force:强制删除镜像,即便有容器引用该镜像;
-no-prune:不要删除未带标签的父镜像;
我们一般不推荐这样暴力的做法,正确的做法应该是:
- 先删除引用这个镜像的容器;
- 再删除这个镜像;
我们在使用 Docker 一段时间后,系统一般都会残存一些临时的、没有被使用的镜像文件,可以通过以下命令进行清理。执行完命令后,还是告诉我们释放了多少存储空间!
# -a, --all: 删除所有没有用的镜像,而不仅仅是临时文件
# -f, --force:强制删除镜像文件,无需弹出提示确认
docker image prune
4.3 容器命令
a. 新建&启动/退出
docker run [OPTIONS] <镜像名/镜像ID> [COMMAND] [ARG...]
OPTIONS 说明(有些是一个减号,有些是两个减号):
-d, --detach=false
:后台运行容器,并返回容器 ID;-i, --interactive=false
:以交互模式运行容器,通常与-t
同时使用;-P, --publish-all=false
:随机端口映射,容器内部端口随机映射到主机的端口。不推荐各位小伙伴使用该参数;-p, --publish=[]
:指定端口映射,格式为主机(宿主)端口:容器端口
。推荐各位小伙伴们使用;-t, --tty=false
:为容器重新分配一个伪输入终端,通常与-i
同时使用;--name="nginx-lb"
:为容器指定一个名称;-h, --hostname="tree6x7"
:指定容器的 hostname;-e, --env=[]
:设置环境变量,容器中可以使用该环境变量;--net="bridge"
:指定容器的网络连接类型,支持 bridge/host/none/container 四种类型;--link=[]
:添加链接到另一个容器。不推荐各位小伙伴使用该参数;-v, --volume
:绑定一个卷--privileged=false
:指定容器是否为特权容器,特权容器拥有所有的 capabilities;--rm=false
:指定容器停止后自动删除容器,不能以docker run -d
启动的容器;--restart=no
:指定容器停止后的重启策略;- no:容器退出时不重启;
- on-failure:容器故障退出(返回值非零)时重启;
- always:容器退出时总是重启,推荐各位小伙伴们使用;
(1)创建并以“交互方式”启动 CentOS 容器
(2)启动守护式容器,以 CentOS 为例 docker run -d centos
,然后通过 docker ps -a
进行查看,会发现容器已经退出?
Docker 容器后台运行,就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(比如运行 top,tail),就是会自动退出的。
这个是 Docker 的机制问题,以 Web 容器 —— Nginx 为例,正常情况下,我们配置启动服务只需要启动响应的 Service 即可。例如:systemctl start nginx
。但是这样做,Nginx 为后台进程模式运行,就导致 Docker 前台没有运行的应用,这样的容器后台启动后,会立即自杀,因为它觉得没事可做了。
可添加 -t
标志将阻止容器在后台运行时退出:docker run -t -d <image> <command>
。 然后,可以使用 docker exec -i -t 容器ID /bin/bash
进入 shell 提示符。
(3)退出容器
exit
容器停止退出CTRL + P + Q
容器不停止退出
使用 exit
退出:
b. 查询容器列表
docker ps [OPTIONS]
OPTIONS 说明:
-a
列出当前所有正在运行的容器 + 历史上运行过的-l
显示最近创建的容器-n
显示最近 n 个创建的容器-q
静默模式,只显示容器编号--no-trunc
不截断输出
状态有 7 种:
- created(已创建)
- restarting(重启中)
- running(运行中)
- removing(迁移中)
- paused(暂停)
- exited(停止)
- dead(死亡)
实用技巧:
# 停止所有运行容器
docker stop $(docker ps -qa)
# 删除所有的容器
docker rm $(docker ps -aq)
docker rm $(docker stop $(docker ps -q))
# 删除所有的镜像
docker rmi $(docker images -q)
c. 进入正在运行的容器并以命令行交互
使用“退出容器方式二:CTRL + P + Q” 退出后,再进入:
[方式一] docker exec -it <容器ID> <bashShell>
[方式二] docker attach <容器ID>
二者区别:attach
直接进入容器启动命令的终端,不会启动新的进程;exec
是在容器中打开新的终端,并且可以启动新的进程。
d. 启动/重启/停止容器
docker start <容器ID/容器名>
docker restart <容器ID/容器名>
docker stop <容器ID/容器名>
docker kill <容器ID/容器名>
docker run
后面指定的是一个镜像,而 docker start
指定的是一个容器。
docker start
是启动一个之前生成过的容器,而 docker run
相当于执行了两步操作:将镜像放入容器中(docker create),然后将容器启动,使之变成运行时容器(docker start)。
e. 删除已停止的容器
- 删除一个:
docker rm <容器ID/容器名>
// 删除正在运行的容器要加-f
强制删除 - 删除多个:
docker rm -f $(docker ps -a -q)
|docker ps -a -q | xargs docker rm
f. 容器其他操作
(1)查看容器日志
docker logs [-f -t --tail N] <容器ID>
-t 加入时间戳
-f 跟踪日志输出
--tail N 仅列出最新N条容器日志
(2)查看容器内运行的进程
docker top <容器ID>
(3)获取容器/镜像的元数据
docker inspect [OPTIONS] NAME|ID [NAME|ID...]
docker inspect <容器ID>
(4)从容器内拷贝文件到主机上
docker cp <容器ID:容器内路径> <目的主机路径>
4.4 小结
attach Attach to a running container
# 当前 shell 下 attach 连接指定运行镜像
build Build an image from a Dockerfile
# 通过 Dockerfile 定制镜像
commit Create a new image from a container changes
# 提交当前容器为新的镜像
cp Copy files/folders from the containers filesystem to the host path
#从容器中拷贝指定文件或者目录到宿主机中
create Create a new container
# 创建一个新的容器,同 run,但不启动容器
diff Inspect changes on a container filesystem
# 查看 docker 容器变化
events Get real time events from the server
# 从 docker 服务获取容器实时事件
exec Run a command in an existing container
# 在已存在的容器上运行命令
export Stream the contents of a container as a tar archive
# 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history Show the history of an image
# 展示一个镜像形成历史
images List images
# 列出系统当前镜像
import Create a new filesystem image from the contents of a tarball
# 从tar包中的内容创建一个新的文件系统映像[对应export]
info Display system-wide information
# 显示系统相关信息
inspect Return low-level information on a container
# 查看容器详细信息
kill Kill a running container
# kill 指定 docker 容器
load Load an image from a tar archive
# 从一个 tar 包中加载一个镜像[对应 save]
login Register or Login to the docker registry server
# 注册或者登陆一个 docker 源服务器
logout Log out from a Docker registry server
# 从当前 Docker registry 退出
logs Fetch the logs of a container
# 输出当前容器日志信息
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
# 查看映射端口对应的容器内部源端口
pause Pause all processes within a container
# 暂停容器
ps List containers
# 列出容器列表
pull Pull an image or a repository from the docker registry server
# 从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server
# 推送指定镜像或者库镜像至docker源服务器
restart Restart a running container
# 重启运行的容器
rm Remove one or more containers
# 移除一个或者多个容器
rmi Remove one or more images
# 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run Run a command in a new container
# 创建一个新的容器并运行一个命令
save Save an image to a tar archive
# 保存一个镜像为一个 tar 包[对应 load]
search Search for an image on the Docker Hub
# 在 docker hub 中搜索镜像
start Start a stopped containers
# 启动容器
stop Stop a running containers
# 停止容器
tag Tag an image into a repository
# 给源中镜像打标签
top Lookup the running processes of a container
# 查看容器中运行的进程信息
unpause Unpause a paused container
# 取消暂停容器
version Show the docker version information
# 查看 docker 版本号
wait Block until a container stops, then print its exit code
# 截取容器停止时的退出状态值