[Docker] Docker 基础教程(概念/原理/基础操作)

1 概述

1.1 定义

Docker is a platform that allows you to "build ,ship ,and run any  app, anywhere. "

  • Docker是一个平台,它提供哪些服务呢?任何一台装有Docker的机器上你都可以构建发布运行你的应用程序

It has come a long way in an incredibly short time and is now considered a standard way of solving one of the costliest aspects of software: deployment.

  • Docker在很短的时间内就走得很远,如今它基本是解决软件最昂贵的环节:部署的标准解决方案

在Docker出现之前,有一堆与软件部署的工具软件,虚拟机、配置管理工具、打包管理工具、复杂的webs库依赖。所有这些工具都需要专业的工程师去管理和维护,多数软件有一套独特的配置方法。

小结:

  • Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。
    开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何支持 docker 的机器上运行。
    容器是完全使用沙箱机制,相互之间不会有任何接口调用。

  • DockerDocker公司开源的一个基于轻量级虚拟化技术的容器引擎项目,整个项目基于 Google 推出的 Go 语言开发,并遵从Apache 2.0协议。

由于隔离的进程独立于宿主机(真实机器)和其它的隔离的进程,因此也称其为容器。Docker最初实现是基于LXC
目前,Docker可以在容器内部快速自动化部署应用,并可以内核虚拟化技术Linuxnamespacescgroups等)、AUFS类的UnionFS等技术对进程进行封装隔离,以此提供容器的资源隔离安全保障等。
由于Docker通过操作系统层的虚拟化实现隔离,所以Docker容器在运行时,不需要类似虚拟机(VM)额外的操作系统开销,提高资源利用率,并且提升诸如IO等方面的性能。

由于众多新颖的特性以及项目本身的开放性,Docker在不到两年的时间里迅速获得诸多厂商的青睐,其中更是包括Google、Microsoft、VMware等业界行业领导者。
Google在2018年六月份推出了Kubernetes,提供Docker容器的调度服务,而2018年8月Microsoft宣布Azure上支持Kubernetes,随后传统虚拟化巨头VMware宣布与Docker强强合作。2018年9月中旬,Docker更是获得4000万美元的C轮融资,以推动分布式应用方面的发展。

从2018年的形势来看,Docker的前景一片大好。但后来的发展,又是另一回事了。

1.2 使用Docker来干什么?

比较一下Docker诞生前和诞生后软件开发的变化就能感受到Docker可以用来干嘛了。

Docker诞生标志着软件工程领域进入新纪元

1.3 为什么要使用Docker?

1.4 优缺点

1.4.1 优点

1)快
运行时的性能快,管理操作(启动,停止,开始,重启等等) 都是以秒或毫秒为单位的。
2)敏捷
像虚拟机一样敏捷,而且会更便宜,在 bare metal(裸机)上布署像点个按钮一样简单。
3)灵活
将应用和系统“容器化”,不添加额外的操作系统
4)轻量
在一台服务器上可以布署 100~1000 个 Containers 容器。
5)便宜
开源的,免费的,低成本的。

1.4.2 缺点

所有容器共用 linux kernel 资源,资源能否实现最大限度利用,所以在安全上也会存在漏洞。

1.5 版本

目前最近的2个版本

  • docker-ce
docker-ce-18.06.3.ce-3.el7
  • docker-ee

1.6 Docker 应用场景

  • Web 应用的自动化打包和发布。

  • 自动化测试和持续集成、发布。

  • 在服务型环境中部署和调整数据库或其他的后台应用。

  • 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。

1.X Docker 的安装

  • 参见

2 Docker的原理与概念

2.0 Docker的架构与模式

2.0.1 体系结构

  • 传统的虚拟机 vs Docker

  • VM是在宿主机器操作系统的基础上创建虚拟化的操作系统,需要重新加载一个Guest OS。
  • Docker是在宿主操作系统上运行Docker引擎,在引擎的基础上再安装应用。
  • Docker是共享【宿主机的内核】,有着比VM更少的抽象层。

  • 进程 vs 应用容器/虚拟容器

Docker,从狭义上来讲就是一个进程
Docker,从广义上来讲是一个虚拟容器,其实更专业的叫法是应用容器Application Container ),Docker 进程普通的进程没有任何区别,它就是一个普通的应用进程。不过是用来操作镜像文件的。所以 Docker 进程 + 构建的应用镜像文件就等于 Docker 容器。

  • 底层技术:基于 Linux 3.10 +

从 Docker 依赖的底层技术来看,Docker 原生态是不能直接在 Windows 平台上运行的,只支持 linux 系统,原因是 Docker 依赖 linux kernel 三项最基本的技术。
namespaces 充当隔离的第一级,是对 Docker 容器进行隔离,让容器拥有独立的 hostname,ip,pid,同时确保一个容器中运行一个进程而且不能看到或影响容器外的其它进程 ;
Cgroups 是容器对使用的宿主机资源进行核算并限制的关键功能。比如 CPU, 内存, 磁盘等
Union FS 主要是对镜像也就是 image 这一块作支持,采用 copy-on-write 技术,让大家可以共用某一层,对于某些差异层的话就可以在差异的内存存储,Libcontainer 是一个库,是对上面这三项技术做一个封装。
Docker engine 用来控制容器 container 的运行,以及镜像文件的拉取。

由架构图可见,用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者
详情参见: Docker源码分析(一):Docker架构 - CSDN

  • 安装Docker,需注意版本问题

安装之前,我们首先确保自己的 linux 系统内核版本高于 3.10,并且系统是 64 位,才能体验 Docker。

2.0.2 部署架构/运行架构

概念 说明
Docker 镜像(Images) Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统。
类似于 VM 虚拟机里面的快照,但是可比快照轻量化多了。快照不懂?那可以把 images 直接理解成一个文件夹。我们可以通过 ID 或者易识别的名字 + tag 来确认唯一的目标镜像。ImagesID 是一个 64 位的字符,但是一般我们都是使用前面 12 位就足够区别了。
镜像是分层的,有基础镜像,仅仅包含操作系统,比如 centos 镜像;有中间件镜像,比如 redis 等数据库镜像;最后是应用镜像,就是指具体的应用服务了,应用镜像可以非常丰富,随时可以发布,这三者之间依次叠加。
当我们在使用 Docker 构建镜像的时候,每一个命令都会在前一个命令的基础上形成一个新镜像层。如下图,基础镜像就是 centos 镜像,中间件镜像就是两个红色圈,应用镜像就是紫色圈。其中 redis+centos 这样叠加组合的中间件镜像就可以供 A 服务或者 B 服务使用,这样叠加组合更加灵活。仍和一种镜像都可以从 Docker hub 公共仓库中拉取。
Docker 容器(Container) 容器是独立运行的一个或一组应用,是镜像运行时的实体。
容器就是一个个独立的封闭的集装箱,但是也需要对外提供服务的,所以 Docker 允许公开容器的特定端口,在启动 Docker 的时候,我们就可以将容器的特定端口映射到宿主机上面的任意一个端口
Docker 客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。
Docker 主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
Docker 仓库(Registry) Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库
docker 仓库和存放集装箱的仓库是一样的,不过 docker 使用来存放镜像的。仓库存在公有私有之分,公有仓库 docker hub 提供了非常多的镜像文件,这些镜像直接拉取下来就可以运行了,你也可以上传自己的镜像到 docker hub 上面。同时也可以自己搭建私有仓库用于团队项目管理。
Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
Docker Machine Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。

  • Docker 使用了 client / server 的架构模式,如下图所示
  • Docker daemon 是后台总管,对外提供了REST API,当收到 Docker Client 发送的请求时,执行诸如镜像、容器、网络、存储等一系列操作

2.0.3 PUID与PGID

引言

  • 前情提要:无Linux、Docker基础的朋友,因过于晦涩,建议跳过本章

  • 在 docker 容器创建命令中偶然看到这2个参数。故此,有必要深究一二。

Demo

docker create --name=beets -e PUID=1000 -e PGID=1000 linuxserver/beets
  • 参考文献

先来了解下 linux 的 uid 和 gid

  • uidgidLinux 内核负责管理,并通过内核级别系统调用来决定是否应该为某个请求授予特权

比如当进程试图写入文件时,内核会检查创建进程的 uid 和 gid,以确定它是否有足够的权限修改文件。注意,内核使用的是 uid 和 gid,而不是用户名和组名。
简单起见,本文中剩下的部分只拿 uid 进行举例,系统对待 gid 的方式和 uid 基本相同。

很多同学简单地把 docker 容器理解为轻量的虚拟机,虽然这简化了理解容器技术的难度,但是也容易带来很多的误解。
事实上,与虚拟机技术存在不同之处:同一主机上运行的所有容器共享【同一个内核(主机的内核)】。
容器化带来的巨大价值之一在于————所有这些独立的容器(其实是进程)可以【共享一个内核】。
这意味着即使由成百上千的容器运行在 docker 宿主机上,但内核控制的 uidgid 则仍然只有一套。
所以,同一个 uid宿主机容器中代表的是同一个用户(即便在不同的地方显示了不同的用户名)。

注意,由于普通的用来显示用户名的 Linux 工具并不属于内核(比如 id 等命令)
所以,我们可能会看到同一个 uid 在不同的容器中显示为不同的用户名。
但是对于相同的 uid 不能有不同的特权,即使在不同的容器中也是如此。

你需要注意的是到目前为止,docker 默认并没有启用 user namesapce,这也是本文讨论的情况。笔者会在接下来的文章中介绍如何配置 docker 启用 user namespace。

Q1 为什么 Docker 容器需要使用 PUIDPGID ?

  • 因为Docker需要系统中管理网络文件系统进程的权限。所以,通常是以 root 的身份在运行。

这意味着,容器内的用户默认也是以 root 用户运行。
这种高权限的访问,并不是日常使用中所推荐的,除非你对linux运维有着深入的理解。

  • 另一个问题就是对容器映射出来的文件的管理。如果进程是以 root 身份运行,那么它所创建的文件的所有者都是 root 用户,这有可能会导致你没有权限去修改这些文件(如果你是以非root身份登陆服务器。)
    而是用 PUIDPGID 允许我们将容器内的用户权限映射给宿主机上的用户。

我们所有的容器都可以使用这种方法来映射用户权限。

Q2 使用方式

  • 当使用我们的镜像创建容器时,确保你是用了 -e PUID-e PGID
docker create --name=beets -e PUID=1000 -e PGID=1000 linuxserver/beets
  • 如果使用 docker-compose ,把他们添加在 environment:下:
environment:
  - PUID=1000
  - PGID=1000

Q3 如何获知宿主机的 UIDGID?

  • 到这里,你很可能希望使用自己的 id,可以通过下面的命令来查询你当前用户的id。其中 uidgid 分别对应 PUIDPGID:
~# id $user
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

深究:Docker 容器中默认使用 root 用户

  • 如果不做相关的设置,容器中的进程默认以 root 用户权限启动,下面的 demo 使用 ubuntu 镜像运行 sleep 程序:
$ docker run -d  --name sleepme ubuntu sleep infinity

注意:上面的命令中并没有使用 sudo。笔者在宿主机中的登录用户是 nickuid1000

  • 在宿主机中查看 sleep 进程的信息:
$ ps aux | grep sleep

sleep 进程的有效用户名称是 root,也就是说 sleep 进程具有 root 权限。
然后,进入容器内部看看,看到的情况和刚才一样,sleep 进程也具有 root 权限:

那么,容器内的 root 用户和宿主机上的 root 用户是同一个吗?
答案是:是的,它们对应的是同一个 uid。
原因我们在前面已经解释过了:整个系统共享同一个内核,而内核只管理一套 uid 和 gid

其实,我们可以通过数据卷来简单的验证上面的结论。在宿主机上创建一个只有 root 用户可以读写的文件:

然后挂载到容器中:

$ docker run --rm -it -w=/testv -v $(pwd)/testv:/testv ubuntu
  • 在容器中可以读写该文件:

我们可以通过 Dockerfile 中的 USER 命令或者是 docker run 命令的 --user 参数指定容器中进程的用户身份。下面我们分别来探究这两种情况。

深究:在 Dockerfile 中指定用户身份

我们可以在 Dockerfile 中添加一个用户 appuser,并使用 USER 命令指定以该用户的身份运行程序,Dockerfile 的内容如下:

FROM ubuntu
RUN useradd -r -u 1000 -g appuser
USER appuser
ENTRYPOINT ["sleep", "infinity"]
  • 编译成名称为 test 的镜像:
$ docker build -t test .

  • 用 test 镜像启动一个容器:
$ docker run -d --name sleepme test
  • 在宿主机中查看 sleep 进程的信息:

这次显示的有效用户是 nick,这是因为在宿主机中,uid 为 1000 的用户的名称为 nick。再进入到容器中看看:

$ docker exec -it sleepme bash

容器中的当前用户就是我们设置的 appuser,如果查看容器中的 /etc/passwd 文件,你会发现 appuser 的 uid 就是 1000,这和宿主机中用户 nick 的 uid 是一样的。

让我们再创建一个只有用户 nick 可以读写的文件:

  • 同样,以数据卷的方式把它挂载到容器中:
$ docker run -d --name sleepme -w=/testv -v $(pwd)/testv:/testv test
  • 在容器中 testfile 的所有者居然变成了 appuser,当然 appuser 也就有权限读写该文件。

这里到底发生了什么?而这些又这说明了什么?

  • 首先,宿主机系统中存在一个 uid 为 1000 的用户 nick。其次容器中的程序是以 appuser 的身份运行的,这是由我们通过 USER appuser 命令在 Dockerfile 程序中指定的。
  • 事实上,系统内核管理的 uid 1000 只有一个,在宿主机中它被认为是用户 nick,而在容器中,它则被认为是用户 appuser。
  • 所以,有一点我们需要清楚:在容器内部,用户 appuser 能够获取容器外部用户 nick 的权力和特权。在宿主机上授予用户 nick 或 uid 1000 的特权也将授予容器内的 appuser。

深究:从命令行参数中指定用户身份

  • 我们还可以通过 docker run 命令的 --user 参数指定容器中进程的用户身份。

比如执行下面的命令:

$ docker run -d --user 1000 --name sleepme ubuntu sleep infinity

  • 因为我们在命令行上指令了参数 --user 1000,所以这里 sleep 进程的有效用户显示为 nick。进入到容器内部看一下:
$ docker exec -it sleepme bash

这是个什么情况?用户名称居然显示为 "I have no name!"!去查看 /etc/passwd 文件,里面果然没有 uid 为 1000 的用户。即便没有用户名称,也丝毫不影响该用户身份的权限,它依然可以读写只有 nick 用户才能读写的文件,并且用户信息也由 uid 代替了用户名:

需要注意的是,在创建容器时通过 docker run --user 指定的用户身份会覆盖掉 Dockerfile 中指定的值。
我们重新通过 test 镜像来运行两个容器:

$ docker run -d test
  • 查看 sleep 进程信息:

$ docker run --user 0 -d test
  • 再次查看 sleep 进程信息:

指定了 --urser 0 参数的进程显示有效用户为 root,说明命令行参数 --user 0 覆盖掉了 DockerfileUSER 命令的设置。

小结

从上述示例我们可以了解到,容器中运行的进程同样具有访问主机资源的权限(docker 默认并没有对用户进行隔离),当然一般情况下容器技术会把容器中进程的可见资源封锁在容器中。
但是通过我们演示的对数据卷中文件的操作可以看出,一旦容器中的进程有机会访问到宿主机的资源,它的权限和宿主机上用户的权限是一样的。
所以,比较安全的做法是为容器中的进程指定一个具有合适权限的用户,而不要使用默认的 root 用户。
当然还有更好的方案,就是应用 Linux 的 user namespace 技术隔离用户,如何配置 docker 开启 user namespace 的支持,参见:隔离 docker 容器中的用户 - 博客园/sparkdev

2.1 镜像(Image) ≈ 容器的静态模板

Docker的image可以理解是一个只读的静态模板,类似于我们在装系统的时候用到的.iso文件

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

现在重新理解一下我们使用一个linux系统的过程:

我们开机时,linux内核会先启动;然后,挂载root文件系统作为用户空间;
而Docker镜像就相当于是一个root文件系统,每开一个就有了一个独立的用户空间。

2.2 容器(Container) ≈ 镜像的动态实例

container则相对而言是一个动态的instance
或说可以理解为我们装好了系统之后的某一台计算机,可以开机关机重启等等,也可以被格式化删除
如果我们想在另一台计算机上复现我这台计算机的系统,则可以ghost一个新的镜像,然后去安装(虽然这种操作通常不行,因为底层硬件的驱动可能不匹配)。

2.2.1 镜像(Image)与容器(Container)的关系

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的类,容器是镜像运行时的实体。
容器可以被创建、启动、停止、删除、暂停等 。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。
前面讲过镜像使用的是分层存储,容器也是如此。

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

所谓“绑定宿主目录”,就是在run一个container的时候使用-v命令把宿主机的存储目录映射进container。

我们可以这样理解:
  image是分层存储的,我们每做一次修改就会在原本的Image存储层上多搭一层,记录这个更改;而新搭的这层是静态、且可以持久化存储的。
  Container也是相同额存储架构,但是每次修改新搭的一层是动态的,而且并不是持久化存储的;
  当容器被remove,或者重启计算机(内存断电)之后,container没了,相应的这些容器存储层也没了。
  因此,要尽量保持容器存储层无状态化,所有的contain里产生的数据(比如container里的程序的运行结果)都要通过挂载宿主目录直接写进宿主硬盘里。

2.3 仓库(Repository) ≈ 模板仓库

2.3.1 Docker Hub 和 Docker Registry

  • Docker Hub 是一个公共的镜像注册中心,开发者可以在其中找到大量官方和社区维护的 Docker 镜像。同时,您也可以在 Docker Hub 上创建自己的账户,并上传自己构建的镜像供他人使用。

  • Docker Registry 则是 Docker 镜像的私有仓库,它允许您在自己的服务器上存储和管理镜像。企业和组织通常使用 Docker Registry 来保护和控制访问其内部构建的镜像。

最常使用的Registry公开服务是官方的Docker Hub,这也是默认的Registry,并拥有大量的高质量的官方镜像

2.3.2 Docker Registry

我们可以把构建好后的镜像(Image)上传到服务器,从而可以在任何地方使用到这个镜像。

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

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

2.4 Docker Container 生命周期

Docker 容器的生命周期包括以下几个关键阶段:

  • 创建(Create):基于镜像创建一个新的容器实例。
  • 启动(Start):启动已创建的容器,使其处于运行状态。
  • 运行(Run):在容器中运行应用程序,处理请求等。
  • 停止(Stop):停止正在运行的容器,但并不会删除它。
  • 删除(Delete):从主机系统中删除已停止的容器实例。
  • 监控(Monitor):监控容器的运行状态和资源使用情况。

3 docker的基本用法

3.0 docker服务管理

3.0.1 查看安装信息(CENTOS7)

whereis docker

which docker

3.0.2 版本信息

docker version

3.0.3 服务启停(CENTOS7)

systemctl start docker

systemctl status docker

3.0.4 开启自启(CENTOS7)

systemctl enable docker

3.1 镜像操作

创建镜像

  • 创建镜像

Docker build 是一个 Docker 命令,用于构建 Docker 镜像

docker build [OPTIONS] PATH | URL | -
  • -f,--file 指定Dockerfile文件的路径。不指定时,默认会读取上下文路径下的 dockerfile
  • -t,--tag 指定构建的镜像名和 tag。例如,构建的镜像指定多个 tag:docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
  • --add-host 可以使用一个或多个 --add-host 标志将其他主机添加到容器的 /etc/hosts 文件中。如:docker build --add-host=docker:10.180.0.1 .
  • --no-cache 构建镜像时不使用缓存
  • --network 在构建过程中为 RUN 指令设置网络模式

更多参数可以看官方文档: https://docs.docker.com/engine/reference/commandline/build/

[case 1] 使用当前目录的 Dockerfile 创建镜像,标签为 runoob/ubuntu:v1
format: docker build -t <image_name>[:<tag, defaultValue=latest>] .

docker build -t runoob/ubuntu:v1 . 

[case 2] 使用URL github.com/creack/docker-firefox 的 Dockerfile 创建镜像

docker build github.com/creack/docker-firefox

[case 3] 可以通过 -f Dockerfile 文件的位置:

$ docker build -f /path/to/a/Dockerfile .

[case 4] 在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,有语法错误时会返回:

$ docker build -t test/myapp .
Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD

从 dockerhub 查找镜像

  • 方法1:基于 dockerhub 官网 或 github dockerhub 官方镜像库 【推荐】

这样能搜索支持的具体版本有哪些、使用文档、注意事项等 (以 mysql 镜像为例)

https://hub-stage.docker.com/_/mysql
https://github.com/docker-library/mysql/blob/3288a66368f16deb6f2768ce373ab36f92553cfa/5.7/Dockerfile

https://github.com/docker-library/docs/tree/master/mysql

  • 方法2: search 命令行方式: docker search [options] {某个镜像的名称}
  • 查找对应DockerHub仓库中的镜像
  • --filter=stars=50 : 列出收藏数不小于指定值的镜像
docker search tomcat
...
docker search centos

解释说明:

  • NAME: 镜像仓库源的名称
  • DESCRIPTION: 镜像的描述
  • OFFICIAL: 是否docker 官方发布
  • stars: 类似Github 里面的star,表示点赞、喜欢的意思。AUTOMATED: 自动构建。

补充

[root@guoweixin ~]# docker search --help
Usage: docker search [OPTIONS] TERM
Search the Docker Hub for images
Options:
-f, --filter filter Filter output based on conditions provided
根据提供的条件过滤器输出
--format string Pretty-print search using a Go template
⽤Go模板打印出漂亮的搜索结果
--limit int Max number of search results (default 25)
搜索结果的最⼤数量(默认值为25)
--no-trunc Don't truncate output
不要截断输出

下载镜像

docker pull [选项] [Docker Registey 地址[:端口号]/] 仓库名[:标签]
从 Docker 镜像仓库获取镜像

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

又例如:我们需要一个tomcat的镜像来作为我们的web服务。通过 docker pull获取镜像

docker pull tomcat:版本号 //不写 :版本号 代表laster版本

查看本地镜像

要想列出已下载下来的镜像,可以使用 docker image ls 命令
列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间。

其中,仓库名、标签在之前的基础概念已经介绍过了。
镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。
因此,如果拥有相同的 ID,因为它们对应的是同一个镜像。

docker images
  • -a--all,列出所有镜像
  • -q--quiet,只显示镜像的id
  • --digests: 显示镜像的摘要信息

REPOSITORY 镜像的仓库源
TAG        镜像的标签
IMAGE ID   镜像的 ID
CREATED    镜像创建时间
SIZE       镜像大小

同一个仓库源可以有多个 TAG,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG 定义不同的镜像;
如果你不定义镜像的标签版本,docker将默认使用 lastest 镜像!

将镜像做成离线压缩包/将Docker镜像导出到本地

  • 将Docker镜像导出到本地

format: docker save -o <保存路径/文件名.tar> <镜像名:标签>

docker save -o centos.tar.gz centos
ls ./

加载镜像到本地Docker引擎

  • 将Docker镜像加载到本地Docker引擎中

format: docker load -i <path to image tar file>

  • 其中-i参数指定要加载的镜像tar文件的路径
docker load -i centos.tar.gz

更新容器

  • docker update 命令可以用于更新一个或多个 Docker容器配置。该命令后面的 CONTAINER 可以是容器Id,或是容器名

您可以使用此命令来防止容器消耗 Docker 主机的过多资源。使用单个命令,您可以对单个容器或多个容器进行限制。要指定多个容器,请提供以空格分隔的容器名称或 ID 列表。

  • 语法格式
# docker update [OPTIONS] CONTAINER [CONTAINER...]

注:Windows 容器不支持 docker update 命令

  • 命令参数
    | 参数 | 描述 |
    | -------------- | -------------- |
    | --cpu-rt-runtime | 将CPU实时运行时间限制在微秒级 |
    | --cpu-rt-period | 限制CPU实时周期(以微秒为单位) |
    | --cpu-quota | 限制CPU CFS(完全公平调度程序)配额 |
    | --cpu-period | 限制CPU CFS(完全公平调度程序)周期 |
    | --blkio-weight | 块 IO(相对权重),介于 10 到 1000 之间,或 0 禁用(默认 0) |
    | --cpu-shares,-c | 更新 cpu-shares。 |
    | --cpus | cpu数量 |
    | --cpuset-cpus | 允许执行的 CPU (0-3, 0,1) |
    | --cpuset-mems | 允许执行的 MEM (0-3, 0,1) |
    | --kernel-memory | 更新内核内存限制。 |
    | --memory,-m | 更新内存限制。 |
    | --memory-reservation | 内存软限制 |
    | --memory-swap | 交换限制等于内存加交换:-1 启用无限制交换 |
    | --pids-limit | API 1.40+ 调整容器 pid 限制(设置 -1 表示无限制) |
    | --restart | 更新重启策略。(容器退出时应用的重新启动策略) |

  • 案例:更新 cpu-shares

使用 docker create -it 命令,创建一个 dokcer 容器。

# docker create -it --name cnter centos
efb5ecbf143c4e1a7e62c0d7d55a4d271923c5e37148837d3b11ae23ad886d40

使用 docker update 命令,更新容器的 cpu-shares。

# docker update --cpu-shares 512 cnter

使用 docker killdocker rm 命令,删除所有容器。

docker kill `docker ps -qa` ; docker rm `docker ps -aq`
  • 案例:更新内存限制

使用 docker create -it 命令,创建一个 dokcer 容器。

docker create -it --name cnter centos
efb5ecbf143c4e1a7e62c0d7d55a4d271923c5e37148837d3b11ae23ad886d40

使用 docker update 命令,更新容器的内存限制。

docker update -m 512M cnter 

使用 docker kill 和 docker rm 命令,删除所有容器。

docker kill `docker ps -qa` ; docker rm `docker ps -aq`
  • 案例

要将容器的 cpu 份额限制为 512,请首先确定容器名称或 ID。您可以使用 docker ps 来查找这些值。您还可以使用从命令返回的 ID docker run 。然后,执行以下操作:

docker update --cpu-shares 512 abebf7571666
  • 案例

要更新多个容器的多个资源配置:

$ docker update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse

删除镜像

  • 根据镜像的 ID 或名称删除指定镜像

format: docker rmi <镜像ID 或 名称>

docker rmi -f centos:latest

3.2 容器操作

创建 + 运行容器(run)

  • 命令格式
docker [container] run [OPTIONS] IMAGE [COMMAND] [ARG...]

Usage: Run a command in a new container
意译:通过run命令创建一个新的容器(container)
注:container参数可省略之

  • 常用选项说明
  • -d, --detach=false, 指定容器运行于前台还是后台,默认为false
  • -i, --interactive=false, 打开STDIN,用于控制台交互
  • -t, --tty=false, 分配tty设备,该可以支持终端登录,默认为false

-itd : 以交互模式(i)运行容器,支持命令交互(i)、支持终端登录(t)、容器以后台方式(d)运行

  • -u, --user="", 指定容器的用户
  • -a, --attach=[], 登录容器(必须是以docker run -d启动的容器)
  • -w, --workdir="", 指定容器的工作目录
  • -c, --cpu-shares=0, 设置容器CPU权重,在CPU共享场景使用
  • -e, --env=[], 指定环境变量,容器中可以使用该环境变量
  • -m, --memory="", 指定容器的内存上限
  • -P, --publish-all=false, 指定容器暴露的端口
  • -p, --publish=[], 指定容器暴露的端口
  • -h, --hostname="", 指定容器的主机名
  • -v, --volume=[], 给容器挂载存储卷,挂载到容器的某个目录
  • --volumes-from=[], 给容器挂载其他容器上的卷,挂载到容器的某个目录
  • --cap-add=[], 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
  • --cap-drop=[], 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
  • --cidfile="", 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
  • --cpuset="", 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
  • --device=[], 添加主机设备给容器,相当于设备直通
  • --dns=[], 指定容器的dns服务器
  • --dns-search=[], 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
  • --entrypoint="", 覆盖image的入口点
  • --env-file=[], 指定环境变量文件,文件格式为每行一个环境变量
  • --expose=[], 指定容器暴露的端口,即修改镜像的暴露端口
  • --link=[], 指定容器间的关联,使用其他容器的IP、env等信息
  • --lxc-conf=[], 指定容器的配置文件,只有在指定--exec-driver=lxc时使用
  • --name="", 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
  • --net="bridge", 容器网络设置:
  • bridge 使用docker daemon指定的网桥
  • host //容器使用主机的网络
  • container:NAME_or_ID >//使用其他容器的网路,共享IP和PORT等网络资源
  • none 容器使用自己的网络(类似--net=bridge),但是不进行配置
  • --privileged=false, 指定容器是否为特权容器,特权容器拥有所有的capabilities
  • --restart="no", 指定容器停止后的重启策略:
  • no:容器退出时不重启
  • on-failure:容器故障退出(返回值非零)时重启
  • always:容器退出时总是重启
  • --rm=false, 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
  • --sig-proxy=true, 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理

  • 示例
  • 运行一个在后台执行的容器,同时,还能用控制台管理:
docker run -i -t -d ubuntu:latest
  • -i--interactive,以交互模式运行容器,并保持终端激活。

注:交互就是用户输入命令,机器执行命令,并提供返回结果。那么在哪里输入命令呢?得有一个终端吧,所以这个命令必须与 -a-t 命令配合使用。
-a 选项用于指定将容器的标准输出连接到终端。使用 -t 选项创建的容器会分配一个伪终端,并将容器的标准输入、输出和错误连接到该终端。

  • -t--tty:为容器分配一个伪终端。

补充(网友观点,尚未验证): docker exec -it ... 中的 -it 参数和 docker run -it 中相同。
docker exec 中的 -it 不会受 docker run 的参数影响。他们是两个工具,只要 docker run 把容器启动了,docker exec 执行什么命令是它的事。

  • 创建、并运行容器,同时在镜像内执行shell命令
# ↓ 仅用于调试 (用于容器运行后,通过 docker logs baili-web 来查验镜像内 /app 的目录结构)
# docker run -it --name xxxx-web -d -p 8003:3003 xxxx-web:latest /bin/sh -c "ls -la /app"
  • 运行一个带命令在后台不断执行的容器,不直接展示容器内部信息:
docker run -d ubuntu:latest ping www.docker.com
  • 运行一个在后台不断执行的容器,同时带有命令,程序被终止后还能重启继续跑,还能用控制台管理:
docker run -d --restart=always ubuntu:latest ping www.docker.com
  • 为容器指定一个名字
docker run -d --name=ubuntu_server ubuntu:latest
  • 容器暴露80端口,并指定宿主机80端口与其通信(: 之前是宿主机端口,之后是容器需暴露的端口)
docker run -d --name=ubuntu_server -p 80:80 ubuntu:latest
  • 指定容器内目录与宿主机目录共享(: 之前是宿主机文件夹,之后是容器需共享的文件夹)
docker run -d --name=ubuntu_server -v /etc/www:/var/www ubuntu:latest

操纵容器/容器内执行命令(exec)

  • 官方文档

https://docs.docker.com/engine/reference/commandline/exec/

  • docker exec命令用于在运行中的容器内执行命令。

使用docker exec命令可以无需进入容器,在容器内执行命令,方便管理复杂多容器环境。

  • 语法
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

说明:

  • OPTIONS:可选项,用于指定一些选项,具体可以查看docker exec --help命令获取;
  • CONTAINER:必选项,指定容器名称或容器ID;
  • COMMAND:必选项,指定要在容器内执行的命令;
  • ARG:可选项,指定命令要使用的参数。

常用选项:

  • -i,--interactive:以交互模式运行容器。
  • -t,--tty:为容器分配一个虚拟终端/tty。
  • -d,--detach:在后台模式下运行容器。
  • -u,--user[=""]:以指定的用户名或UID运行容器中的命令。
  • -w,--workdir=" ":指定命令的工作目录。
  • 举例:
  • 【综合案例】在宿主机中执行 mysql 容器 的 命令
  • 方法1
docker exec -it mysql bash
> mysql -uroot -p${MYSQL_ROOT_PASSWORD} --port=3306 -h127.0.0.1 -e 'show databases;'
> exit
  • 方法2:
docker exec -it mysql sh -c "mysql -uroot -p${MYSQL_ROOT_PASSWORD} --port=3306 -h127.0.0.1 -e 'show databases;'"

注: MYSQL_ROOT_PASSWORD 也是 mysql container 内的内置环境变量

  • 执行一个交互式的bash终端:
docker exec -it container_name bash

docker exec -it container_name /bin/bash
  • 执行一个命令:
docker exec container_name ls -a

docker exec container_name /path/to/my_script.sh
  • 在后台模式下执行命令:
docker exec -d container_name ls -a
  • 在指定用户下执行命令:
docker exec -u user_name container_name ls -a
  • 指定命令工作目录:
docker exec -w /usr/src/myapp container_name ls -a
  • 容器间通信
    我们可以使用 docker exec -it 命令在不同的容器之间进行通信。例如,假设我们有两个容器 container1container2 ,我们可以通过以下命令在 container1 中执行一个命令,并将结果发送到 container2 :
docker exec -it container1 sh -c "echo 'Hello' > /tmp/message.txt"

docker exec -it container2 cat /tmp/message.txt

总结:docker exec命令是一个很好的容器管理工具,使用起来非常简单方便。掌握这个命令可以更好地管理容器的生命周期和维护运行环境。

查看容器

  • 查看容器
# 查看运行中的容器
docker [container] ps 

# 查看全部容器
docker [container] ps -a

注:container参数可省略之

输出详情介绍:

  • CONTAINER ID: 容器 ID。
  • IMAGE: 使用的镜像。
  • COMMAND: 启动容器时运行的命令。
  • CREATED: 容器的创建时间。
  • STATUS: 容器状态。

状态有7种:

  • created(已创建)
  • restarting(重启中)
  • running 或 Up(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)
  • PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。
  • NAMES: 自动分配的容器名称。

查看容器的COMMAND详情

在显示容器列表的命令后面加上--no-trunc(大意:不省略)

docker ps -a --no-trunc

查看容器的运行日志

  • 查看日志
    在宿主主机内使用 docker logs <containerId> 命令,查看容器内的标准输出:
root@xxx:~$ docker logs 2b1b7a428627
root@xxx:~$ docker logs nginx
  • 实时查看/跟踪日志
docker logs -f nginx
  • 实时查看/跟踪最近的10条日志
docker logs -f --tail {lineNum} {containerName/containerId}
  • 查看最近XX分钟的日志
docker logs --since {xxm} {containerName/containerId}

如最近1分钟:docker logs --since 1m nginx
  • 查看指定日期至今的所有日志
如:查看2023-01-16至今的所有日志
docker logs -f --since "2023-01-16" nginx

启动/停止容器

  • 启动停止容器

format: docker start/stop {containerName | containerId}

停止容器(stop container),并不意味着容器物理上不存在了,其实它依旧存在。如果此时,新建同名容器时,将报类似如下错误:

docker: Error response from daemon: Conflict. The container name "/mysql" is already in use by container "c08e6zzzzd274e5fxxxxcb883d55f3yyyy5b54efff". You have to remove (or rename) that container to be able to reuse that name.


  • demo
docker stop nginx

删除容器

  • 删除容器

如果容器为运行状态,则:需停止容器成功后才能被成功删除

# 删除指定的容器
docker rm [-f] <containerid|containerName>

# 删除未启动成功的容器
docker rm $(docker ps -a|grep Created|awk '{print $1}')
或
docker rm $(docker ps -qf status=created)

# 删除退出状态的容器
docker rm $(docker ps -a|grep Exited|awk '{print $1}')
或
docker rm $(docker ps -qf status=exited)

# 删除所有未运行的容器
# 注:正在运行的删除不了,所有未运行的都被删除了
docker rm $(docker ps -a -q) 
或
# 注:Docker 1.13版本以后,可以使用 docker containers prune 命令,删除孤立的容器 (prune ,修剪之意)
docker container prune 

查看容器(元)信息(inspect)

docker inspect <containerName|containerId>

docker inspect [OPTIONS] NAME|ID [NAME|ID...]
  -f	指定返回值的模板文件
  -s	如果类型为容器,则显示文件总大小
  --type	返回指定类型的JSON

  • 案例
# 获取容器 IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CONTAINER_ID

# 获取IP地址
docker inspect tomcat7 | grep IPAddress

# 获取日志路径
docker inspect --format='{{.LogPath}}' $CONTAINER_ID

卷标管理(volume)

# 新增卷标
docker volume create my-vo

# 查看所有卷标
docker volume ls

# 查看批量的卷标 
docker volume ls | grep mysql

# 查看具体的volume对应的真实地址
docker volume inspect my-vo

# 删除卷标
docker volume rm my-vol

容器与主机间的数据拷贝(cp)

  • 语法
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-

docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

OPTIONS说明:

  • -L : 保持源目标中的链接
  • 案例1:将主机/www/a 目录拷贝到容器96f7f14e99ab的/www目录下
docker cp /www/a 96f7f14e99ab:/www/
  • 案例2:将主机/www/a目录拷贝到容器96f7f14e99ab中,目录重命名为www
docker cp /www/a 96f7f14e99ab:/www
  • 案例3:将容器96f7f14e99ab的/www目录拷贝到主机的/tmp目录中
docker cp  96f7f14e99ab:/www /tmp/
//docker cp mysql:/var/log/mysqld.log /opt/mysql/log/mysqld.log
//docker cp mysql:/var/lib/mysql /opt/mysql/data
//docker cp mysql:/etc/mysql/conf.d /opt/mysql/conf
//docker cp mysql:/etc/my.cnf /opt/mysql

3.3 Docker 仓库管理

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

3.3.1 配置镜像仓库

  • step1 配置镜像仓库
# vi /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://mirrors.docker.io/",
    "https://registry.cn-hangzhou.aliyuncs.com/",
    ""http://hub-mirror.c.163.com""
  ]
}
  • step2 重启docker进程
sudo systemctl restart docker

3.3.1 查看镜像仓库

可以使用 docker info 命令查看Docker的配置信息,其中包括镜像仓库地址。例如:

docker info

关键词Registry Mirrors

[root@xxx ~]# docker info
Client: Docker Engine - Community
 Version:    25.0.3
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.12.1
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.24.5
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 17
  Running: 6
  Paused: 0
  Stopped: 11
 Images: 16
 Server Version: 25.0.3
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: ae07eda36dd25f8a1b98dfbf587313b99c0190bb
 runc version: v1.1.12-0-g51d5e94
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
 Kernel Version: 3.10.0-1160.90.1.el7.x86_64
 Operating System: CentOS Linux 7 (Core)
 OSType: linux
 Architecture: x86_64
 CPUs: 2
 Total Memory: 1.794GiB
 Name: iZ2vc3en6658r8vwdlz5s3Z
 ID: 95c8a460-14cc-49cf-ac8e-37ccb6b19679
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://mirrors.docker.io/
  https://registry.cn-hangzhou.aliyuncs.com/
 Live Restore Enabled: false

3.3.3 登录/登出镜像仓库

  • 前情提要

docker pull xx时,报"Error response from daemon: pull access denied for sqlite, repository does not exist or may require 'docker login': denied: requested access to the resource is denied ..."
如果你在拉取 Docker Hub 上的 XXX 镜像时遇到了这类错误,可能是因为 Docker Hub/镜像仓库 的政策变化导致了匿名用户无法再访问部分镜像。
此时,就需要在Docker Hub或镜像仓库中注册账号,再通过docker login登录后,进行操作了。

  • 登录镜像仓库
  • 登录背景;若目标镜像 或 镜像仓库需要认证,需使用docker login命令并输入你的Docker Hub的凭证信息。
  • 前提条件:确保你的网络能够访问到Docker Hub,你可以尝试ping或者在浏览器中访问Docker Hub来检查网络。
  • 例子。以阿里云为例,需要提前在阿里云【容器镜像服务】的控制台创建【个人版(免费)/企业版(付费)】实例、设置固定密码。(参见:登录阿里云镜像仓失败(unauthorized: authentication required)的解决方法 - CSDN
# 登录到 docker hub
docker login

docker login --username=xxxxaliyunyyyyy@@163.com registry.cn-hangzhou.aliyuncs.com
docker login --username=<你的Access Key ID> --password=<你的Access Key Secret> registry.cn-hangzhou.aliyuncs.com

  • 查看当前登录的镜像仓库情况
cat $HOME/.docker/config.json
{
	"auths": {
		"registry.cn-chengdu.aliyuncs.com": {
			"auth": "xxxxxxxxxx"
		}
	}
}
  • 本命令将列出已登录的所有镜像仓库
  • auh的内容格式:base64(username:password)
  • 登出镜像仓库
docker logout [SERVER]

其中,SERVER参数指定要退出登录的远程镜像仓库的地址。如果未指定SERVER参数,则默认退出当前登录的所有远程镜像仓库。

例如:

# 退出当前登录的Docker Hub
docker logout

# 退出登录指定的镜像仓库
docker logout my.private.registry.com

# 以退出阿里云的镜像仓库为例:
[root@xxx ~]# docker logout registry.cn-chengdu.aliyuncs.com
Removing login credentials for registry.cn-chengdu.aliyuncs.com
[root@xxx ~]# cat $HOME/.docker/config.json
{
	"auths": {}
}

3.3.3 从指定的镜像仓库中拉取镜像

  • 拉取镜像
# docker pull registry.cn-hangzhou.aliyuncs.com/acs-sample/nginx-web-server:latest
# docker pull registry.cn-hangzhou.aliyuncs.com/mysql:8.0
# docker pull registry.cn-beijing.aliyuncs.com/mysql:8.0
# docker pull registry.cn-chengduu.aliyuncs.com/mysql:8.0

3.3.X Docker Hub(官方公共仓库)

目前 Docker 官方维护了一个公共仓库 ·Docker Hub·。
大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

  • Step1 注册

https://hub.docker.com 免费注册一个 Docker 账号。

  • Step2 登录和退出

登录需要输入用户名和密码,登录成功后,我们就可以从 docker hub 上拉取自己账号下的全部镜像。

$ docker login

  • Step3 退出

退出 docker hub 可以使用以下命令:

$ docker logout
  • Step4 拉取镜像

你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。

以 ubuntu 为关键词进行搜索:

$ docker search ubuntu

  • Step4 使用 docker pull 将官方 ubuntu 镜像下载到本地:
$ docker pull ubuntu 

  • Step5 推送镜像

用户登录后,可以通过 docker push 命令将自己的镜像推送到 Docker Hub。
以下命令中的 username 请替换为你的 Docker 账号用户名。

$ docker tag ubuntu:18.04 username/ubuntu:18.04
$ docker image ls

REPOSITORY      TAG        IMAGE ID            CREATED           ...  
ubuntu          18.04      275d79972a86        6 days ago        ...  
username/ubuntu 18.04      275d79972a86        6 days ago        ...  
$ docker push username/ubuntu:18.04
$ docker search username/ubuntu

NAME             DESCRIPTION       STARS         OFFICIAL    AUTOMATED
username/ubuntu

3.4 Docker Dockerfile

3.4.1 什么是 Dockerfile?

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

DockerFileDocker的一个配置文件,本质上来说它只是一个文本文件,它是用来构建【Docker镜像】的。
DockerFile配置文件中包含了一系列的指令配置信息,用于描述如何构建镜像以及如何运行容器。
通过编写 Dockerfile,我们可以将构建 Docker 镜像的【过程】自动化,实现应用程序的快速部署迭代

3.4.2 Dockerfile的基本结构

  • Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。一般而言,Dockerfile,分为四部分:
  • 基础镜像信息;
  • 维护者信息;
  • 镜像操作指令;
  • 和容器启动时执行指令

例如:

# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/
sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx

其中,一开始必须指明【所基于的镜像名称】,接下来一般是说明维护者信息,后面则是镜像操作指令,例如 RUN 指令,RUN 指令将对镜像执行跟随的命令。每运行一条 RUN 指令,镜像就添加新的一层,并提交。最后是 CMD 指令,用来指定运行容器时的操作命令。

3.4.3 Dockerfile 如何编写与使用?(详解)

3.4.x Dockerfile/DockerComposeYml中Service的expose和ports的区别

  • 背景描述

形如: 对宿主机开放 3307 端口,容器内部使用 3306 端口的 mysql container:

# docker-compose.yml
...
services:
  mysql:
    container_name: openmetadata_mysql
    image: docker.getcollate.io/openmetadata/db:1.3.0
    command: "--sort_buffer_size=10M"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password
    expose:
      - 3306
    ports:
      - "3307:3306"
    volumes:
     - ./docker-volume/db-data:/var/lib/mysql
    networks:
      - app_net
    healthcheck:
      test: mysql --user=root --password=$$MYSQL_ROOT_PASSWORD --silent --execute "use openmetadata_db"
      interval: 15s
      timeout: 10s
      retries: 10

...
  • 解释/辨析:
  • exposeports在Docker中都用于容器的网络配置,但它们的用途和方式略有不同。
  • expose : 这是Dockerfile的指令,用于在创建镜像时声明容器打算使用的端口。它是对构建服务的人的一种文档性的声明,意味着运行的容器会监听某个端口。然而,只有当容器启动时使用了-P(大写)或-p(小写)参数,这些端口才会在主机上映射。此外,EXPOSE不会让容器的端口对外界可见。
  • ports: 这是docker-compose.yml文件中的一个字段,用于定义容器的端口映射。这实际上在主机上打开了端口,使得外部世界可以访问到容器的网络服务。它的格式通常为 :。例如,如果你想在主机的8080端口上运行一个在80端口上运行的web服务器,你可以使用ports配置如下:- "8080:80"。

总的来说,expose主要用于在构建阶段声明容器需要使用的端口,而ports主要用于在运行阶段定义主机和容器之间的端口映射关系

形如: ports 3307:3306 , 是指 宿主机的端口(3307)映射到容器的端口(3306)

  • 参考文献

4 综合应用场景

4.1 docker 环境下,设置容器自动启动

  • 方法1:使用 docker run 命令运行时

增加 --restart=always 参数即可

  • 方法2:使用 docker-compose 命令运行时

在 yml 文件中,需要自启动的 service 下

增加 restart: always 项目即可

  • 方法3:已运行的容器修改其自启动策略

执行命令:

  • docker update --restart=always {容器名或容器ID}
  • docker container update --restart=【容器策略】 容器名称
容器策略:
       # no 容器退出时不重启容器
       # on-failure 只有在非零状态退出时才重新启动容器
              --restart=on-failure:【重启次数】
       # always 无论退出状态如何都

K FAQ/问题集

Q1 配置docker镜像加速器?

  • Step1 登陆阿里云镜像仓库

https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
如果没有开通,可开通阿里云的镜像服务

编辑/etc/docker/daemon.json

  • Step2 重启docker
systemctl daemon-reload
systemctl restart docker

Q2 docker hub官网进不去?

问题分析

docker hub进不去是因为“hub.docker.com”是在国外的,所以访问速度很慢,导致无法访问该网址。

https://hub.docker.com/

解决方法

  • 1、找到“daemon.json”文件
  • 2、使用vim命令将其打开
  • 3、添加“{ "registry-mirrors" :["https://docker.mirrors.ustc.edu.cn"]}”内容
  • 4、重启docker即可

参考文献

Q3 怎么理解docker中的registry(注册表) 和repository(仓库)?

  • Registry:(英译:注册表、登记处)

A service responsible for hosting and distributing images. The default registry is the Docker Hub.
Docker registry 能够被第三方组织host,作为共有或者私有的registry,下面就是一些registries:

  • Repository:(英译:仓库)

A collection of related images (usually providing different versions of the same application or service).

相关映像的集合(通常提供相同应用程序或服务的不同版本)。
Docker repository is a collection of different docker images with same name, that have different tags.
Docker存储库是不同Docker映像的集合,具有相同的名称,具有不同的标签。
也就是说repository是关于同一种镜像(如python)的不同版本”tag“的“集合地”,例如 https://hub.docker.com/r/library/python/tags/。这里有许多不同tag的官方python镜像,官方python repository中所有的版本都位于Docker hub这个registry内!

  • Tag:(英译:标签)

An alphanumeric identifier (字母数字的标志符)attached to images within a repository (e.g., 14.04 or stable ).
对于 [image name] = [repository] : [tag] ,其中的“repository”是对镜像而言,某一个镜像可以有好多的tag。
所以有一种“仓库”的概念在里面,看某些书的时候会发现,有这样的的语句“每个repository可以有多个tag”。
有过创建自己的私有registry经历的就会知道,我们会重新“命名”我们的本地的image,例如:myregistryhost:5000/namespace/repo-name:tag;
此外,我们在docker hub 上面上传自己的docker 镜像的时候也会填上自己的 namespace(用户名),即你在docker hub上面注册的账号。

Y 推荐资源

Y.1 核心资源/核心链接

  • Docker 官网主页

https://www.docker.com
https://docs.docker.com/

  • Docker 官方博客:

https://blog.docker.com/

  • Docker 官方文档:

https://docs.docker.com/

  • Docker github

https://hub.docker.com
https://hub-stage.docker.com
https://github.com/moby/moby 【Docker 源代码仓库】
https://docs.docker.com/release-notes/ 【Docker 发布版本的历史】

  • Dockerhub 镜像站官网

https://hub-stage.docker.com/
https://registry.hub.docker.com

  • Docker 官方 registry 镜像加速

https://dashboard.daocloud.io/mirror

  • Docker Store:

https://store.docker.com

  • Docker Cloud:

https://cloud.docker.com

  • Docker 常见问题:

https://docs.docker.com/engine/faq/

  • Docker 远端应用 API:

https://docs.docker.com/develop/sdk/

Y.2 Docker Hub 镜像加速器列表

  • Docker 官方镜像

https://hub.docker.com/

镜像加速器 镜像加速器地址URL 专属加速器 其他加速
Docker 中国官方镜像站 https://registry.docker-cn.com ... Docker Hub
Dao Cloud 镜像站 http://f136db2.m.daocloud.io/
https://www.daocloud.io/mirror#accelerator-doc
可登录,系统分配 Docker Hub
Azure 中国镜像站 https://dockerhub.azk8s.cn Docker Hub / GCR / Quay
科大镜像站 https://docker.mirrors.ustc.edu.cn ... Docker Hub / GCR / Quay
阿里云(ACR) registry.cn-hangzhou.aliyuncs.com
registry.cn-beijing.aliyuncs.com
registry.cn-chengdu.aliyuncs.com
...
需登录,系统分配(https://<阿里云容器镜像服务为你随机分配的ID>.mirror.aliyuncs.com Docker Hub
七牛云 https://reg-mirror.qiniu.com ... Docker Hub / GCR / Quay
网易云 https://hub-mirror.c.163.com ... Docker Hub
腾讯云(TCR) https://mirror.ccs.tencentyun.com
https://ccr.ccs.tencentyun.com
...(参见:https://mirrors.cloud.tencent.com/
Docker Hub
中国科学技术大学镜像 https://docker.mirrors.ustc.edu.cn ... --

Y.3 其他

https://cr.console.aliyun.com/cn-hangzhou/instances/artifact

X 参考文献

待阅读

posted @ 2023-05-29 01:26  千千寰宇  阅读(437)  评论(2编辑  收藏  举报