Docker
Docker介绍
http://www.runoob.com/docker/docker-tutorial.html
什么是docker
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
为什么用docker
简化程序:
Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是 Docker的最大优势,过去需要用数天乃至数周的 任务,在Docker容器的处理下,只需要数秒就能完成。
避免选择恐惧症:如果你有选择恐惧症,还是资深患者。Docker 帮你 打包你的纠结!比如 Docker 镜像;Docker 镜像中包含了运行环境和配置,所以 Docker 可以简化部署多种应用实例工作。比如 Web 应用、后台应用、数据库应用、大数据应用比如 Hadoop 集群、消息队列等等都可以打包成一个镜像部署。
节省开支一方面,云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker 改变了高性能必然高价格的思维定势。Docker 与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理的问题,也改变了虚拟化的方式。
持续交付和部署
对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像
更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况
对比传统虚拟机总结
Docker总结
Docker是世界领先的软件容器平台。
Docker使用Google公司推出的Go语言进行开发实现,基于Linux内核的cgroup,namespace,以及AUFS类的UnionFS等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。Docke最初实现是基于LXC。
Docker能够自动执行重复性任务,例如搭建和配置开发环境,从而解放了开发人员以便他们专注在真正重要的事情上:构建杰出的软件。
用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现“这段代码在我机器上没问题啊”这类问题;——一致的运行环境
可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。——更快速的启动时间
避免公用的服务器,资源会容易受到其他用户的影响。——隔离性
善于处理集中爆发的服务器使用压力;——弹性伸缩,快速扩展
可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。——迁移方便
使用Docker可以通过定制应用镜像来实现持续集成、持续交付、部署。——持续交付和部署
Docker架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
Docker 容器通过 Docker 镜像来创建。
容器与镜像的关系类似于面向对象编程中的对象与类
Docker 基本概念
Docker 包括三个基本概念
镜像(Image)
容器(Container)
仓库(Repository)
理解了这三个概念,就理解了 Docker 的整个生命周期。
Docker 引擎
Docker 架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
Docker 容器通过 Docker 镜像来创建。
容器与镜像的关系类似于面向对象编程中的对象与类。
Docker 镜像
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
分层存储
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
java |
Centos(linux) |
docker |
Docker 容器
容器与镜像的关系类似于面向对象编程中的对象与类。
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失
Docker 仓库
Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。
Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。
公有 Docker Registry
Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。
最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。
由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为加速器。常见的有 阿里云加速器、DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多。
国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像仓库、网易云镜像服务、DaoCloud 镜像市场、阿里云镜像库 等。
私有 Docker Registry
除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。
开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。在官方的商业化版本 Docker Trusted Registry 中,提供了这些高级功能。
除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,VMWare Harbor 和 Sonatype Nexus。
Docker安装
CentOS安装要求:
http://www.runoob.com/docker/centos-docker-install.html
Docker支持以下的CentOS版本:
CentOS 7 (64-bit)
CentOS 6.5 (64-bit) 或更高的版本
安装
目前,CentOS 仅发行版本中的内核支持 Docker。
Docker 运行在 CentOS 7 上,要求系统为64位、系统内核版本为 3.10 以上。
Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版本为 2.6.32-431 或者更高版本
1、校验版本
命令:uname -r
3.10以上版本
校验Linux内核版本
从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。
Docker CE 即社区免费版,Docker EE 即企业版,强调安全,但需付费使用。
本文介绍 Docker CE 的安装使用。
2、移除旧的版本
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
3、安装一些必要的系统工具:
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
4、添加软件源信息(不建议用aliyun源 docker-compose可能会因为源安装不上):
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
5、更新 yum 缓存:
sudo yum makecache fast
6、安装 Docker-ce:
sudo yum -y install docker-ce
7、启动 Docker 后台服务:
sudo systemctl start docker
8、重启 Docker服务:
sudo systemctl restart docker
安装成功后可通过:
docker version
删除
执行以下命令来删除 Docker CE:
$ sudo yum remove docker-ce
$ sudo rm -rf /var/lib/docker
Docker 镜像加速器
鉴于国内网络问题,后续拉取 Docker 镜像十分缓慢,我们可以需要配置加速器来解决
Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
Docker 官方提供的中国 registry mirror
阿里云加速器
DaoCloud 加速器
我们以 Docker 官方加速器为例进行介绍
https://docs.docker.com/registry/recipes/mirror/#use-case-the-china-registry-mirror
CentOS 7
通过命令查看:在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
注意,一定要保证该文件符合 json 规范,否则 Docker 将不能启动
重启Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
检查加速器是否生效
配置加速器之后,如果拉取镜像仍然十分缓慢,请手动检查加速器配置是否生效,在命令行执行 docker info,如果从结果中看到了如下内容,说明配置成功
测试
尝试从docker库中下载镜像:
docker pull tomcat
通过观察,下载速度明显提升。
查看docker下载的镜像内容:
docker images
下载指定的镜像版本:
docker images tomcat:9-jre11
启动tomcat:
docker run –p 8080:8080 tomcat
Docker镜像
镜像是Docker的三大组件之一。
Docker运行容器前需要本地存在对应的镜像,如果本地不存,Docker会从镜像仓库下载。
本节将介绍更多关于镜像的内容,包括:
从仓库获取镜像;
管理本地主机上的镜像;
介绍镜像实现的基本原理;
Docker 获取镜像
之前提到过,Docker Hub 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。
从 Docker 镜像仓库获取镜像的命令是 docker pull。其命令格式为:
具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
示例:
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 centos:7.1.1503,因此将会获取官方镜像 library/centos仓库中标签为 7.1.1503 的镜像。
从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。
在使用上面命令的时候,你可能会发现,你所看到的层 ID 以及 sha256 的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的 bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像。
运行
有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 centos:7.1.1503 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令
docker run 就是运行容器的命令,我们这里简要的说明一下上面用到的参数。
-it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。(可通过新开一窗口docker ps –a 查看)
centos:7.1.1503:这是指用 centos:7.1.1503 镜像为基础来启动容器。
bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。
进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 centos:7系统。
最后我们通过 exit 退出了这个容器。(Ctrl+D)
例:如果不使用 --rm 参数,我们通过 docker ps –a 命令可看到:(容器退出后未删除)。可通过 docker rm CONTAINER-ID 删除
查找镜像
我们可以从 Docker Hub 网站来搜索镜像,Docker Hub 网址为: https://hub.docker.com/
我们也可以使用 docker search 命令来搜索镜像。比如我们需要一个httpd的镜像来作为我们的web服务。我们可以通过 docker search 命令搜索 httpd 来寻找适合我们的镜像。
docker search httpd
Docker 列出镜像
要想列出已经下载下来的镜像,可以使用 docker image ls 命令。
列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间。
其中仓库名、标签在之前的基础概念章节已经介绍过了。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。因此,如果拥有相同的 ID,因为它们对应的是同一个镜像。
镜像体积
如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 docker image ls 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小。
另外一个需要注意的问题是,docker image ls 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
虚悬镜像
镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 <none>
这个镜像原本是有镜像名和标签的,原来为 mongo:3.2,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2 时,mongo:3.2 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 <none>。除了 docker pull 可能导致这种情况,docker build 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none> 的镜像。这类无标签镜像也被称为 虚悬镜像(dangling image) 。
一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除:
Docker 删除本地镜像
如果要删除本地的镜像,可以使用 docker image rmi 命令,
用 ID、镜像名、摘要删除镜像
其中,<镜像> 可以是 镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要。
练习:
一、从docker官网上下载一个tomcat下来,启动并访问。再将其镜像移除。
Docker pull tomcat
Docker run –it –rm –p 8080:8080 tomcat
Docker rm shaid
二、对指定的容器进行修改。将tomcat镜像 的容器 主页面进行修改内容
1、首先启动tomcat容器
docker run -it -p 8080:8080 tomcat
2、交互的方式进入容器(修改指定页面)
docker exec -it 7bfbdc2d10e4 bash
3、将主页面index.jsp后追加
docker run //交互的方式启动容器
docker exec //交互的方式进入容器
4、再访问主页面查看:
三、将容器关闭后,再访问index.jsp。发现又变成新的原始内容了
docker stop 9be696a0c283 //停止正运行容器(或Ctrl+c)
Docker定制镜像
当我们从docker镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。
1.从已经创建的容器中更新镜像,并且提交这个镜像
2.使用 Dockerfile 指令来创建一个新的镜像
Dockerfile 定制镜像
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
还以之前定制 tomcat镜像为例,这次我们使用 Dockerfile 来定制
案例:创建一个镜像(基于tomcat) 里面要有一个index.html,并写入Hello qfjy Docker
在一个空白目录中,建立一个文本文件,并命名为 Dockerfile:
其内容为:
这个 Dockerfile 很简单,一共就两行。涉及到了两条指令,FROM 和 RUN。
FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 FROM 就是指定基础镜像,因此一个 Dockerfile中 FROM 是必备的指令,并且必须是第一条指令。
在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node、openjdk、python、ruby、golang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntu、debian、centos、fedora、alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm、coreos/etcd。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
RUN 执行命令
RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:
shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。
exec 格式:RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。
既然 RUN 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:
之前说过,Dockerfile 中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。
而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。
Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。上面的 Dockerfile 正确的写法应该是这样:
首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 RUN 对一一对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。
并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。
此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。
很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。
build构建镜像
回到之前定制的 tomcat 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。
在 Dockerfile 文件所在目录执行:
从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 Step 2 中,如同我们之前所说的那样,RUN 指令启动了一个容器 9f8…,执行了所要求的命令,并最后提交了这一层 9f8..,随后删除了所用到的这个容器 d93..。
这里我们使用了 docker build 命令进行镜像构建。其格式为:
-t :指定要创建的目标镜像名
. :Dockerfile 文件所在目录,可以指定Dockerfile 的绝对路径
在这里我们指定了最终镜像的名称 -t tomcat-1,构建成功后,我们可以像之前运行 tomcat 那样来运行这个镜像,其结果会和 tomcat 一样。(如果有版本号名称:tomcat:01)
案例:基于上一个镜像(基于tomcat) 将ROOT内多余的文件都删除。只保留index.html
1、修改Dockerfile
构建镜像
docker build –t 镜像名 .
查看镜像列表 docker images
删除虚拟镜像
docker image prune
WORKDIR 指定工作目录
格式为 WORKDIR <工作目录路径>。
使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
之前提到一些初学者常犯的错误是把 Dockerfile 等同于 Shell 脚本来书写,这种错误的理解还可能会导致出现下面这样的错误:
如果将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /use/local 文件,或者其内容不是想要的。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。这就是对 Dockerfile 构建分层存储的概念不了解所导致的错误。
之前说过每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN cd /usr/local/tomcat的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。
因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。
镜像构建上下文(Context)
如果注意,会看到 docker build 命令最后有一个 .。. 表示当前目录,而 Dockerfile 就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定上下文路径。那么什么是上下文呢?
首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
案例:基于上一个镜像(基于tomcat) 外部复制一个文件(图片),并复制下并能访问
构建境像:
docker build –t tomcat_1 .
运行:
docker run -it --rm –d –p 8080:8080 tomcat_1
docker exec –it 容器id bash
COPY 复制文件
格式:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。
COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:
COPY qfjy.png /usr/local/tomcat/webapps/ROOT/
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
案例:部署项目到Docker容器(练习)
1、docker下创建项目工程名称
mkdir -p /usr/local/docker/qfjy_exam
cd /usr/local/docker/qfjy_exam
2、将桌面qfjy_exam.zip复制到访目录下
cp qfjy_exam-1.0-SNAPSHOT.zip /usr/local/docker/qfjy_exam/
3、创建镜像文件Dockerfile
4、构建镜像
docker build -t qfjy_exam .
5、进入镜像内查看:
docker run -it qfjy_exam bash
2步、将默认端口号改为8081
EXPOSE 暴露端口
格式为 EXPOSE <端口1> [<端口2>...]。
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
此外,在早期 Docker 版本中还有一个特殊的用处。以前所有容器都运行于默认桥接网络中,因此所有容器互相之间都可以直接访问,这样存在一定的安全性问题。于是有了一个 Docker 引擎参数 --icc=false,当指定该参数后,容器间将默认无法互访,除非互相间使用了 --links 参数的容器才可以互通,并且只有镜像中 EXPOSE 所声明的端口才可以被访问。这个 --icc=false 的用法,在引入了 docker network 后已经基本不用了,通过自定义网络可以很轻松的实现容器间的互联与隔离。
要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射
docker run -P 时,会自动随机映射 EXPOSE 的端口。
执行 -P
docker run -P tomcat_1
浏览器访问:
docker run -p 时,<宿主端口>:<容器端口> (将容器的端口指定宿主机端口)
Docker 容器
容器是 Docker 又一核心概念。
简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。
查看容器状态
docker ps //正在运行的容器显示
docker container ls
docker ps –a
docker container ls –a
如下将具体介绍如何来管理一个容器,包括创建、启动和停止等。
Docker 启动容器
启动容器有三种方式,一种是基于镜像新建一个容器并启动,一种是将在终止状态(stopped)的容器重新启动。
新建并启动
所需要的命令主要为 docker run
下面的命令则启动一个 bash 终端,允许用户进行交互
docker run -it 镜像 bash
其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。
在交互模式下,用户可以通过所创建的终端来输入命令,例如
当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
检查本地是否存在指定的镜像,不存在就从公有仓库下载
利用镜像创建并启动一个容器
分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
从地址池配置一个 ip 地址给容器
执行用户指定的应用程序
执行完毕后容器被终止
启动已终止容器
可以利用 docker start 命令,直接将一个已经终止的容器启动运行
docker stop 9be696a0c283 //停止正运行容器(或Ctrl+c)
docker restart 9be696a0c283//启动容器 (根据 ID或NAMES)
如果根据容器名称,可通过参数 - - name 来自定义名称
docker run --rm -p 8081:8080 --name myTomcat 镜像名称
Docker 守护态运行
更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现
如果不使用 -d 参数运行容器:将会在当前宿主机运行。
如果使用了 -d 参数运行容器。此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs查看)。
docker run --rm --name myTomcat -d -p 8081:8080 镜像名
查看控制台结果:
docker logs myTomcat //(ID或Names)
Docker 终止容器
docker stop 9be696a0c283 //停止正运行容器(或Ctrl+c)
docker container stop myTomcat//停止正运行容器(ID或Names)
Docker 进入容器
某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令
exec 命令
-i -t 参数
docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。
只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。
docker exec –it 容器ID(Names) bash
区别:如果从这个 stdin 中 exit,不会导致容器的停止。
Docker 删除容器
可以使用 docker container rm 或 docker rm 来删除一个处于终止状态的容器。
清理所有处于终止状态的容器
用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器
docker container prune 或 docker prune
Docker 导出容器
http://www.runoob.com/
docker export :将文件系统作为一个tar归档文件导出到STDOUT
-o 参数:将输入内容写到文件。
实例
将id为a404c6c174a2的容器按日期保存为tar文件
docker export -o tomcat-`date +%Y%m%d`.tar 9be696a0c283
注意: `` 号是Esc下的按键。 date + 中要求有空格。
Docker 导入容器
docker import : 从归档文件中创建镜像。
语法
docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
-c :应用docker 指令创建镜像;
-m :提交时的说明文字;
实例
从镜像归档文件tomcat-20190116.tar 创建镜像,命名为tomcat_1:1.0
docker import tomcat-20190116.tar tomcat_1:1.0
可通过此处查看镜像交互内容:
docker run -it tomcat_1:1.0 bash
Docker 安装MySQL
参考:http://www.runoob.com/docker/docker-install-mysql.html
1、查看本地镜像
docker images
2、搜索镜像
docker search mysql
3、下载镜像
docker pull mysql:5.6
创建并启动MySQL容器
docker run -d --name mysql3306 -p 3306:3306 -e MYSQL_ROOT_PASSWORD='guoweixin' mysql:5.6
docker run -d
--name mysql3306
-p 3306:3306
-e MYSQL_ROOT_PASSWORD='guoweixin'
mysql:5.6
5、访问测试
进入到容器内部
docker exec -it mysql3306 bash
连接mysql数据库:
mysql -u root -p
输入数据库密码
授权其他机器登陆
1、授权主机访问:MySQL>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'guoweixin' WITH GRANT OPTION;
2、刷新权限:MySQL>FLUSH PRIVILEGES;
3、退出:MySQL>EXIT;
注意:(如果阿里云服务器部署和访问,防火墙默认是关闭的,需要手动开启)
https://www.cnblogs.com/kccdzz/p/8110143.html
阿里云控制组中加入该权限:
https://blog.csdn.net/yenange/article/details/89499840
Docker 查看日志
Docker查看日志
docker logs 容器名称/ID
docker logs -f -t --since="2018-12-1" --tail=10 qfjy_exam
--since : 此参数指定了输出日志开始日期,即只输出指定日期之后的日志。
-f : 查看实时日志
-t : 查看日志产生的日期
-tail=10 : 查看最后的10条日志
qfjy_exam : 容器名称
Docker 总结
Docker服务的操作:
systemctl start docker 启动镜像
systemctl stop docker 停止镜像
systemctl restart docker 重启镜像
Docker就是来装软件的,比如:Tomcat、Redis、MySQL等
安装容器步骤:
1、查询本地镜像
docker images
2、搜索仓库找中的镜像
docker search 镜像名称
比如:
搜索Tomcat
docker search tomcat
3、下载镜像
docker pull 名称:版本号
4、创建容器
docker create --name 容器名称 -p 宿主端口号:容器端口号 -v 宿主路径:容器路径 镜像名称:版本号
5、启动容器
docker start 容器名称
6、访问测试
Docker 安装Redis
安装单机版Redis
1、搜索redis
docker search redis
2、下载镜像
docker pull redis:4.0.1
3、创建并运行容器
docker run --rm -d --name redis6379 -p 6379:6379 redis:4.0.1 --requirepass "guoweixin"
docker run --rm
-d
--name redis6379
-p 6379:6379 redis:4.0.1
--requirepass "guoweixin" //设置redis密码
阿里云访问设置Redis:
https://www.jianshu.com/p/d9d70c3559c4
4、测试Redis
docker exec -it redis6379 bash //进入redis命令
redis-cli //开启客户端功能
安装集群版Redis-Cluster
https://note.youdao.com/ynoteshare1/index.html?id=d298d8971662d9ce930b7d939632872f&type=notebook#/
Docker 数据卷
问题:通过镜像创建一个容器。容器一旦被销毁,则容器内的数据将一并被删除。但有些情况下,通过服务器上传的图片出会丢失。容器中的数据不是持久化状态的。
那有没有一种独立于容器、提供持久化并能服务于多个容器的东西呢?
数据卷 : 是一个可供一个或多个容器使用的特殊目录
特性:
数据卷可以在容器之间共享和重用
对数据卷的修改会立马生效
对数据卷的更新,不会影响镜像
数据卷默认会一直存在,即使容器被删除
为什么需要数据卷?
这得从 docker 容器的文件系统说起。出于效率等一系列原因,docker 容器的文件系统在宿主机上存在的方式很复杂,这会带来下面几个问题:
不能在宿主机上很方便地访问容器中的文件。
无法在多个容器之间共享数据。
当容器删除时,容器中产生的数据将丢失。
为了解决这些问题,docker 引入了数据卷(volume) 机制。数据卷是存在于一个或多个容器中的特定文件或文件夹,这个文件或文件夹以独立于 docker 文件系统的形式存在于宿主机中。数据卷的最大特定是:其生存周期独立于容器的生存周期。
使用数据卷的最佳场景
在多个容器之间共享数据,多个容器可以同时以只读或者读写的方式挂载同一个数据卷,从而共享数据卷中的数据。
当宿主机不能保证一定存在某个目录或一些固定路径的文件时,使用数据卷可以规避这种限制带来的问题。
当你想把容器中的数据存储在宿主机之外的地方时,比如远程主机上或云存储上。
当你需要把容器数据在不同的宿主机之间备份、恢复或迁移时,数据卷是很好的选择。
参考文献(后续阅读):
https://blog.csdn.net/jiangyu1013/article/details/80881097
https://www.jb51.net/article/135904.htm
创建一个数据卷
docker volume create my-vol
查看所有的 数据卷
docker volume ls
案例:现在将公共的资源images放入数据卷,进行各容器启动访问
Docker部署springboot项目
https://www.jianshu.com/p/2909593e30ed
docker run -d --name blog-web -p 8443:8443 blog-web
docker run -d --name blog-web -p 8080:8443 blog-web