Docker基础(一)

Docker——从入门到实践(本笔记重点参考文章,强烈推荐看原文

Docker中文文档(强烈推荐作为补充文章观看)

Docker官网

docker中文

Play with Docker(A simple, interactive and fun playground to learn Docker)

首先在学习Docker之前,我们要先学习一下Linux基础,特别注意一下Linux的**ps命令top命令**。在之前的Linux笔记中,我们已经稍微提到过系统管理命令了(参考文章:Linux系统管理(进程管理、工作管理和系统定时任务)

Docker简介

  • 什么是Docker
  • 为什么要用Docker

什么是Docker

一句话:解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术

Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。

Docker基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于Linux 容器(Linux Containers,缩为LXC),从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 版本开始,则进一步演进为使用 runC 和 containerd。

Docker架构

  • runc 是一个 Linux 命令行工具,用于根据 OCI容器运行时规范 创建和运行容器
  • containerd 是一个守护程序,它管理容器生命周期提供了在一个节点上执行容器和管理镜像的最小功能集

Docker在容器的基础上,进行了进一步的封装从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护使得 Docker 技术比虚拟机技术更为轻便、快捷

在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。

传统虚拟化

Docker

为什么要用 Docker

一款产品从开发到上线,从操作系统,到运行环境,再到应用配置,作为开发+运维之间的协作我们需要关心很多东西,环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来开发人员利用Docker可以消除协作编码时在我的机器上可正常工作的问题

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势:

  • 更高效的利用系统资源:由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用

  • 更快速的启动时间:传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间

  • 一致的运行环境:开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现这段代码在我机器上没问题啊这类问题

  • 持续交付和部署:对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行(一次构建,随处运行

    使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
    ​ 而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像

  • 更轻松的迁移:由于Docker确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况

  • 更轻松的维护和扩展Docker使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本

Docker容器对比传统虚拟机总结:

特性 Docker容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于原生
系统支持量 单机支持上千个容器 一般几十个

Docker基本概念

Docker包括三个基本概念

  • 镜像Image
  • 容器Container
  • 仓库Repository

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

这里可以先看一下Docker的架构图:

Docker架构图

镜像(Image)

Image镜像

Docker镜像(lmage):一个只读的模板。镜像可以用来创建Docker容器,一个镜像可以创建很多容器(因为镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类和实例 一样

我们都知道,操作系统分为 内核用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。

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

镜像命名:我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像如果不给出标签,将以 latest 作为默认标签。

分层存储

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

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

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

关于镜像构建,我们后面会提到。

容器(Container)

Docker利用容器(Container) 独立运行的一个或一组应用容器是用镜像创建的运行实例。 它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台可以把容器看做是一个简 易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。 容器的定义和镜像几乎一模一样,也是一堆层的统一视角, 唯一区别在于容器的最上面那一层是可读可写的。

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

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。**容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。**这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。

前面讲过镜像使用的是分层存储,容器也是如此每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层容器存储层

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

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

数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。

仓库(Repository)

仓库(Repository) 是集中存放镜像文件的场所。 仓库(Repository)和仓库注册服务器(Registry) 是有区别的:仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多镜像, 每个镜像有不同的标签(tag) 。

仓库分为公开仓库(Public) 和私有仓库(Private) 两种形式。 最大的公开仓库是[Docker Hub](https://hub. docker.com/) 存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等

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

一个 Docker Registry 中可以包含多个 仓库Repository);每个仓库可以包含多个 标签Tag);每个标签对应一个镜像(Image)。

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

以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest

仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。

Docker Registry 公开服务

Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的 官方镜像。除此以外,还有 Red Hat 的 Quay.io;Google 的 Google Container RegistryKubernetes 的镜像使用的就是这个服务;代码托管平台 GitHub 推出的 ghcr.io

由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror,这些镜像服务被称为 加速器。常见的有 阿里云加速器DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多。在安装docker的内容中我们会再具体提到。

国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 网易云镜像服务DaoCloud 镜像市场阿里云镜像库 等。

私有 Docker Registry

除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。在 私有仓库 一节中,会有进一步的搭建私有 Registry 服务的讲解。

开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,HarborSonatype Nexus


Docker安装

Docker安装即配置镜像加速参考文章:安装 Docker

这里我们学习Docker就在Centos7上安装docker了,其他平台的安装和配置镜像加速在上面的推荐文章或者网上都有很多教程的。

CentOS上安装Docker

Docker 官方 CentOS 安装文档

警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker

安装Docker

系统要求:Docker 支持 64 位版本 CentOS 7/8,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。

查看内核版本命令:uname -r

卸载旧版本

旧版本的 Docker 称为 docker 或者 docker-engine,使用以下命令卸载旧版本:

1
2
3
4
5
6
7
8
9
10
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

安装新docker

在官网中提供了三种安装方式:

  • Most users set up Docker’s repositories and install from them, for ease of installation and upgrade tasks. This is the recommended approach.
  • Some users download the RPM package and install it manually and manage upgrades completely manually. This is useful in situations such as installing Docker on air-gapped systems with no access to the internet.
  • In testing and development environments, some users choose to use automated convenience scripts to install Docker.

官网上也推荐我们使用第一种安装方式: set up Docker’s repositories,便于安装和升级

在新主机上第一次安装Docker引擎之前,需要设置Docker存储库。之后,您可以从存储库中安装和更新Docker。(Before you install Docker Engine for the first time on a new host machine, you need to set up the Docker repository. Afterward, you can install and update Docker from the repository.)

执行以下命令安装依赖包:

1
sudo yum install -y yum-utils

鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。

linux yum介绍

执行下面的命令添加 yum 软件源:

1
2
3
4
5
6
7
8
9
10
sudo yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo

# 官方源
# sudo yum-config-manager \
# --add-repo \
# https://download.docker.com/linux/centos/docker-ce.repo

如果需要安装测试版本的 Docker 请执行以下命令:

1
sudo yum-config-manager --enable docker-ce-test

最后我们就可以安装docker了。更新 yum 软件源缓存,并安装 docker-ce。输入命令:

1
sudo yum install docker-ce docker-ce-cli containerd.io

启动Docker

输入以下命令启动Docker:

1
2
sudo systemctl enable docker
sudo systemctl start docker

建立docker用户组

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组

建立 docker 组:

1
sudo groupadd docker

将当前用户加入 docker 组:

1
sudo usermod -aG docker $USER

完成后就可以先退出当前终端并重新登录,测试一下docker是否成功安装。

由于这里我们以学习为主,所以就直接以root用户登陆Linux了(不用再额外配置docker用户组)

测试Docker是否安装正确

我们可以通过docker ps -a(查看所有容器,包括停止的)来测试docker是否安装成功:

也可以创建Docker的helloword容器:

1
docker run --rm hello-world

输出以下内容则说明Docker安装成功:

由于本地没有hello-world这个镜像,所以会下载一个hello-world的镜像,并在容器内运行。

**docker run**之后做了什么:

配置镜像加速

如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker 国内镜像加速

国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。国内很多云服务商都提供了国内加速器服务,例如:

由于镜像服务可能出现宕机,建议同时配置多个镜像。各个镜像站测试结果请到 docker-practice/docker-registry-cn-mirror-test **查看。**国内各大云服务商(腾讯云、阿里云、百度云)均提供了 Docker 镜像加速服务,建议根据运行 Docker 的云平台选择对应的镜像加速服务

我们这里以阿里云加速为例,输入以下命令即可:

1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://这里要看你自己的.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

下面再详细说一些:

目前主流 Linux 发行版均已使用 systemd 进行服务管理,这里介绍如何在使用 systemd 的 Linux 发行版中配置镜像加速器。

请首先执行以下命令,查看是否在 docker.service 文件中配置过镜像地址:

1
systemctl cat docker | grep '\-\-registry\-mirror'

如果该命令有输出,那么请执行 $ systemctl cat docker 查看 ExecStart= 出现的位置,修改对应的文件内容去掉 --registry-mirror 参数及其值,并按接下来的步骤进行配置。

如果以上命令没有任何输出:

那么就可以在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件):

1
2
3
4
5
6
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
]
}

注意,一定要保证该文件符合 json 规范,否则 Docker 将不能启动。

之后重新启动服务:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

Docker常用命令

菜鸟教程Docker 命令大全

官网Docker命令

  • docker version:查看当前docker版本
  • docker info:查看当前docker的
  • docker --help:帮助命令

还有一些常用命令会在下面具体的章节中提到,关于Docker命令大全可以参考上面的推荐文章

使用镜像

使用镜像

在上面的介绍中,我们知道镜像(Image)是 Docker 的三大组件之一。

Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。

这里我们将学习更多关于镜像的内容:

管理本地主机上的镜像

  • 列出镜像
  • 删除镜像

列出镜像

要想列出已经下载下来的镜像,可以使用 docker images 命令。

查询结果包含了 仓库名标签镜像 ID创建时间 以及 所占用的空间

其中仓库名、标签在上面已经介绍过了。镜像 ID 则是镜像的唯一标识一个镜像可以对应多个 标签。比如说,如果我们拉取了ubuntu的两个版本,那么我们就可以看到 ubuntu:18.04ubuntu:bionic 拥有相同的 ID,因为它们对应的是同一个镜像:

docker images 的常用OPTIONS:

  • -a:列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层)
  • -q :只显示镜像ID
  • --digests:显示镜像的摘要信息
  • --no-trunc:显示完整的镜像信息

镜像体积

具体可以看上面的推荐文章,总结一下就是:

  • docker images 查询出的镜像所占空间比Docker Hub上显示的要大,这是因为Docker Hub中显示的体积是压缩后的体积,在镜像下载和上传过程中镜像是保持着压缩状态的
  • docker images 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。

虚悬镜像

有时我们使用docker images查看镜像列表,可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 <none>

虚悬镜像造成原因如下:

这个镜像原本是有镜像名和标签的,比如原来为 mongo:3.2,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2 时,mongo:3.2 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 <none>除了 docker pull 可能导致这种情况,docker build 也同样可以导致这种现象由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none> 的镜像。

这类无标签镜像也被称为 虚悬镜像(dangling image) ,可以用下面的命令专门显示这类镜像:

1
docker image ls -f dangling=true

一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除:

1
docker image prune

docker rmi:删除本地一个或多个镜像)

中间层镜像

为了加速镜像构建、重复利用资源,Docker 会利用 中间层镜像。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 docker images 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 -a 参数。

1
$ docker imags -a

这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。**这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。**实际上,这些镜像也没必要删除,因为之前说过,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。

删除镜像

如果要删除本地的镜像,可以使用 docker image rmi 命令,其格式为:

1
docker rmi [OPTIONS] IMAGE [IMAGE...]

OPTIONS说明:

  • -f:强制删除
  • --no-prune:不移除该镜像的过程镜像,默认移除

docker prune 命令:prune 命令用来删除不再使用的 docker 对象(具体见菜鸟教程Docker rmi命令)

以删除wordpress镜像为例,输入命令:

1
docker rmi -f hello-world

补充:

  • 删除多个镜像:docker rm -f 镜像名1[:TAG] 镜像名2[:TAG]
  • 删除全部镜像:docker rmi -f $(docker images -qa)

从仓库获取镜像

之前提到过,Docker Hub 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。

从Docker镜像仓库获取镜像的命令是 docker pull,其命令格式为:

1
docker pull [OPTIONS] NAME[:TAG|@DIGEST]

我们以拉取tomcat镜像为例,输入命令:

1
docker pull tomcat

因为没有指定tag,所以默认为latest

结果如下:

如果配置了阿里云镜像加速的话速度会快很多

具体的OPTIONS说明可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式:

  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub(docker.io)
  • 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。

上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io)获取镜像。而镜像名称是 tomcat:latest,因此将会获取官方镜像 library/tomcat 仓库中标签为 latest 的镜像。docker pull 命令的输出结果最后一行给出了镜像的完整名称,即: docker.io/library/tomcat:latest

下载完毕后本地镜像就有了tomcat了:


操作容器

操作容器

容器(Container)是 Docker 三大重要概念之一。

简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。

接下来我们将学习如何来管理一个容器,包括创建、启动和停止等

  • 容器启动、停止和重启
  • 列出当前容器
  • 删除容器
  • 守护态运行
  • 进入容器
  • 查看容器日志和内部相关信息
  • 容器导出和导入

容器启动、停止和重启

启动容器有两种方式:

  1. 基于镜像新建一个容器并启动
  2. 将在终止状态(exited)的容器重新启动

因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。

新建并启动

现在我们还没有一个容器:

使用命令docker run新建并启动一个容器:

**docker run :**创建一个新的容器并运行一个命令。语法:

1
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

常用OPTIONS说明:

  • --name 容器新名字:容器指定一个名称
  • -d:后台运行容器,并返回容器ID, 也即启动守护式容器
  • -i:以交互模式运行容器,通常与-t同时使用
  • -t:为容器重新分配一个伪输入终端,通常与-i同时使用
  • --volume , -v:绑定一个卷
  • -e username="ritchie":设置环境变量
  • -P:随机端口映射
  • -p:指定端口映射,有以下四种格式
    • ip:hostPort:containerPort
    • ip::containerPort
    • hostPort:containerPort(前面的是自己指定本机端口,后面是容器端口是不能随意指定的)
    • containerPort

详细可见:Docker run 命令

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从 registry 下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

启动已终止容器

  • docker start:启动一个或多个已经被停止的容器
  • docker stop:停止一个运行中的容器
  • docker restart:重启容器
  • docker kill:杀掉一个运行中的容器(强制停止容器)

语法:

1
2
3
4
5
6
7
docker start [OPTIONS] CONTAINER [CONTAINER...]

docker stop [OPTIONS] CONTAINER [CONTAINER...]

docker restart [OPTIONS] CONTAINER [CONTAINER...]

docker kill [OPTIONS] CONTAINER [CONTAINER...]

例子:我们要重启关闭的odoo镜像,输入命令:

1
docker start odoo

停止和重启容器是一样的,命令也已经给出了

列出当前容器

docker ps:列出容器

语法:

1
docker ps [OPTIONS]

常用OPTIONS:

  • -a:列出当前所有正在运行的容器+历史上运行过的
  • -|:显示最近创建的容器
  • -n a:显示最近a个创建的容器(a为具体数字)
  • -q:静默模式,只显示容器编号
  • --no-trunc:不截断输出

Docker ps 命令

比如我们要查看全部容器,输入命令:

1
docker ps -a

删除容器

docker rm :删除一个或多个容器

语法:

1
docker rm [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS说明:

  • -f:通过 SIGKILL 信号强制删除一个运行中的容器。
  • -l:移除容器间的网络连接,而非容器本身。
  • -v:删除与容器关联的卷。

docker删除所有的镜像

守护态运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,在运行docker run时可以通过添加 -d 参数来实现。(上面的新建并启动一节中也已经提到过相关参数了:-d:后台运行容器,并返回容器ID, 也即启动守护式容器)

举个例子:

如果不使用 -d 参数运行容器:docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

容器会把输出的结果 (STDOUT) 打印到宿主机上面

如果使用了 -d 参数运行容器:docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)。

注意 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。

使用 -d 参数启动后会返回一个唯一的 id,要获取容器的输出信息,可以通过 docker container logs 命令。

注意一下Docker的机制:

Docker容器后台运行,就必须有一个前台进程容器运行的命令如果不是那些一直挂起的命令 (比如运行top,tail) ,就是会自动退出的,这个是docker的机制问题。比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可,即 service nginx start 。但是,这样做nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后就会立即自杀因为他觉得他没事可做了。所以,最佳的解决方案是将你要运行的程序以前台进程的形式运行。

docker容器中的前台程序和后台程序,为什么一定要前台运行

视频讲解

进入容器

在使用 -d 参数时,容器启动后会进入后台。某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令,原因会在下面说明。

attach命令

Docker attach 命令

docker attach:连接到正在运行中的容器

语法:

1
docker attach [OPTIONS] CONTAINER

要attach上去的容器必须正在运行,可以同时连接上同一个container来共享屏幕(与screen命令的attach类似)。

官方文档中说attach后可以通过CTRL-C来detach,但实际上经过其他人的测试,如果container当前在运行bash,CTRL-C自然是当前行的输入,没有退出;如果container当前正在前台运行进程,如输出nginx的access.log日志,CTRL-C不仅会导致退出容器,而且还stop了。这不是我们想要的,detach的意思按理应该是脱离容器终端,但容器依然运行。好在attach是可以带上–sig-proxy=false来确保CTRL-D或CTRL-C不会关闭容器。

注意如果从这个stdin中 exit,会导致容器的停止

exec命令(推荐)

Docker exec 命令

docker exec :在运行的容器中执行命令

语法:

1
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

OPTIONS说明:

  • -d:分离模式: 在后台运行
  • -i:即使没有附加也保持STDIN 打开
  • -t:分配一个伪终端

这里主要说明 -i -t 参数:

  • 只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回
  • -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符

注意如果从这个stdin中 exit,不会导致容器的停止。这也是为什么推荐大家使用 docker exec 的原因

例子:docker exec -it mysql8 bash

一般常用的进入容器命令:

  • docker exec -it [容器id或name] /bin/bash
  • docker exec -it [容器id或name] /bin/sh(镜像是使用alpine制作的)

查看容器日志和内部相关信息

  • 查看容器日志
  • 查看容器内运行的进程
  • 查看容器内部细节

查看容器日志

Docker logs 命令

docker logs:获取容器的日志

语法:

1
docker logs [OPTIONS] CONTAINER

OPTIONS说明:

  • -f:跟踪日志输出
  • --since:显示某个开始时间的所有日志
  • -t:显示时间戳
  • --tail:仅列出最新N条容器日志

例子:docker logs -t hello

查看容器内运行的进程

Docker top 命令

docker top:查看容器中运行的进程信息,支持 ps 命令参数

语法:

1
docker top [OPTIONS] CONTAINER [ps OPTIONS]

容器运行时不一定有/bin/bash终端来交互执行top命令,而且容器还不一定有top命令,可以使用docker top来实现查看container中正在运行的进程。

例子:docker top mysql8

查看容器内部细节

Docker inspect 命令

docker inspect:获取容器/镜像的元数据

语法:

1
docker inspect [OPTIONS] NAME|ID [NAME|ID...]

OPTIONS说明:

  • -f:指定返回值的模板文件
  • -s:显示总的文件大小
  • --type:为指定类型返回JSON

例子:docker inspect hello

容器导出和导入

  • 导出容器
  • 导入容器快照
  • 从容器内拷贝文件到主机上

导出和导入

导出容器

Docker export 命令

如果要导出本地某个容器,可以使用 docker export 命令将导出容器快照到本地文件

docker export:将文件系统作为一个tar归档文件导出到STDOUT

语法:

1
docker export [OPTIONS] CONTAINER

OPTIONS说明:

  • -o:将输入内容写到文件(通过docker export --help查看:-o, --output string Write to a file, instead of STDOUT)

导入容器快照

Docker import 命令

可以使用 docker import 从容器快照文件中再导入为镜像,也可以通过指定 URL 或者某个目录来导入

docker import : 从归档文件中创建镜像

语法:

1
docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]

OPTIONS说明:

  • -c:应用docker 指令创建镜像
  • -m:提交时的说明文字

注意:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息,即仅保存容器当时的快照状态(docker import方式),而镜像存储文件将保存完整记录,体积也要大(docker load方式)。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

从容器内拷贝文件到主机上

Docker cp 命令

docker cp:用于容器与主机之间的数据拷贝

语法:

1
2
3
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-

docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

OPTIONS说明:

  • -L:保持源目标中的链接

例子:docker cp odoo: /tmp/yum.log /root(表示将odoo容器内的/tmp/yum.log文件拷贝到宿主机的/root目录下)

视频讲解,25:54)


访问仓库

  • Docker Hub
  • 私有仓库
    • 私有仓库高级配置
  • Nexus3

仓库(Repository)是集中存放镜像的地方。

一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 docker.io/ubuntu 来说,docker.io 是注册服务器地址,ubuntu 是仓库名。

更详细的内容就去看原文吧:访问仓库