Docker系列学习文章 - docker镜像基本操作(五)

| 导语上一篇文章我们讲解了如何简单运行一个Nginx、Mysql、Redis容器服务。我们运行的很顺利,因为我们就用了一条命令就搞定了。确实,docker就是这么简单!那么这么方便快捷的原因其实很大程度是因为docker有丰富的镜像,镜像相当于一个模板,我们能快速“衍生、克隆”出我们想要的服务。

镜像是Docker里非常重要的一块内容,可以说镜像是docker的灵魂!那么本篇文章给大家讲解下镜像的相关基本操作。

一、获取镜像

想使用镜像,首先我们要有镜像,没有镜像我们也玩不起来。就像做饭,光有厨艺能做出佳肴,但是没有食材你也就是“巧妇难为无米之炊”了。生活中做饭获取食材的方式顶多分两种,一种是到集市买现成的,另一种就是自己种地种菜... docker里获取容器的方式也是如此,要么去网上下载现成的,要么自己动手做镜像。

1. 从网上下载现成的方式

我们先说说这种方式,这种方式适合初学者,这也是最方便最省事的一种方式。那么我们从哪去下载呢?一般的,我们去网上下载资料都是找那种权威、干净、好用的东西。找镜像也是如此,那么哪里的镜像权威、干净、好用呢?这里直接跟大家推荐docker官方的镜像仓库—— Docker Hub 。

地址:https://hub.docker.com/explore/

Docker 官方维护了一个公共镜像仓库 Docker Hub,其中已经包括了数量超过 15,000 的镜像。大部分人的容器服务需求都可以通过Docker Hub 中下载实现。

要使用这个仓库,最好还是注册下,因为上面除了能获取镜像,还能做自动构建和搭建私有仓库。

既然知道了镜像的获取地址,那么怎么下载这些镜像呢?其实,我们在前面的文章就跟大家讲过了,docker安装好后,默认会指向这个官方仓库地址。你只要运行docker run,如果本地没有你要运行的镜像,它就会自动在Docker Hub下载。

如果是想单纯的下载某些镜像,你可以使用下面命令进行下载:

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] 

比如下载ubuntu 16.04 和 httpd 2.2 的镜像,如果不写冒号+版本号,默认会下载最新latest版镜像

# docker pull ubuntu:16.04

# docker pull httpd:2.2

这里有同学就会问了,这地址会不会被墙掉或者速度很慢啊?对,很好的问题!确实,Docker Hub服务器在国外,慢,是肯定的了。所以,我们这里可以设置个国内加速器,让Docker Hub下载快些。

设置方法如下:

CentOS7 、Ubuntu 16.04+ 、Docker version 17.12.0-ce

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

{ "registry-mirrors": [ "https://registry.docker-cn.com" ] } 注意,一定要保证该文件符合 json 规范,否则 Docker 将不能启动。

然后重启下docker服务 systemctl daemon-reload && systemctl restart docker

最后你再试试docker pull命令,看看速度是否变快了。

2. 自己做镜像

如果不想去网上下载官方的镜像或者别人的镜像,那么你自己可以做镜像。关于制作镜像的具体方法我们后面会详细讲解,这里先不做介绍。

二、列出镜像

下载了镜像,我们肯定想知道镜像都放哪了,另外也想知道有什么方法可以列出下载好的镜像。

镜像下载后,默认是存在里 /var/lib/docker 路径下的。注意的是,这个目录很多结构,大家不要想着镜像就只放在一个目录下,docker对于镜像的存储是分层管理的,最主要的目录在/var/lib/docker/overlay2 下。

1. docker 镜像的目录结构

这里,我们详细说下 /var/lib/docker 的目录结构。在centos7,docker 版本17.12.0-ce 下 /var/lib/docker 的目录结构如下:

builder目录:builder就是用来build的目录,在其中的fscache.db主要是用来构建镜像的时候缓存数据,也就是缓存了相关镜像的数据。从而大大提高了创建镜像的速度,可以在底层的基础之上来构建更高层的镜像。

containerd目录:containerd主要职责是镜像管理(镜像、元信息等)、容器执行(调用最终运行时组件执行),这目录里面是存放着containerd相关信息。

containers目录:主要用来存储创建的容器的内容,当你新建一个容器之后,这边就会多出来一个容器,如下,我们创建了3个容器,这里就有3个目录:

每个目录都是每个容器的特有ID命名的,目录里面存着是容器的相关状态日志、hostname、配置信息等。

image目录:image目录主要是用来存储镜像的相关分配、分布、imagedb、layerdb、repositories等信息。信息大部分都是sha256加密形式的。当你使用docker images显示镜像的时候,其实读取的就是这个目录的信息。如果这个目录里的东西没了,那么你运行后面讲的docker images命令就会显示空。

network目录:network目录主要用来存储网络相关的信息,当在创建容器的时候,如果使用到了网络,那么就会更新这个数据文件的信息。

overlay2目录:overlay2是一种存储驱动,从Docker 1.12开始推出,较之最初的overlay的实现有重大的提升。此目录主要是用来存储镜像的,所有的镜像都会存储在这个位置,包括base image或者是在其上的image。所以,上面提到的下载的镜像存哪了,这个目录是镜像存储非常重要的目录,如果你的镜像很多,这个文件夹也是会很大的。

plugins目录:Docker 1.13 开始提供插件化管理,这样能快速轻松地利用插件的可扩展性。Docker的任务是让所有插件都像容器一样被管理和运行,把Docker Hub作为集中资源,让插件更可用,从而推动过程标准化。那么这个目录就是存取相关插件信息的目录。

runtimes目录:这是docker运行时信息存放目录

swarm目录:swarm是docker出的集群管理工具,这里存放的是swarm的相关文件信息,如果你没装swarm,那么里面是空的。

tmp目录:这是docker的临时目录

trust目录:这是docker的信任目录

volumes目录:volume 方式是 docker 中数据持久化的最佳方式,默认在主机上会有一个特定的区域(/var/lib/docker/volumes/),该区域用来存放 volume。volume 在生成的时候如果不指定名称,便会随机生成。所以,这个目录下的名字都是随机的ID。

2. 如何修改docker镜像的存储路径

在centos7下 /var/lib/docker 目录是很重要的目录,所以这个目录需要保证充足的空间,不然下载镜像的时候如果空间不够就会失败。如果你想更改存储镜像的路径,可以采用下面这个软连接的方法:

首先,先停止docker 服务

# systemctl stop docker

然后,把/var/lib/docker全部移动到你空间足够大的目录里,比如我们放到 /data/docker 下;然后我们再用ln -s创建个软连接指向/var/lib/docker;最后我们再启动docker服务。

# mv /var/lib/docker /data/docker

# ln -s /data/docker /var/lib/docker # systemctl start docker

这个方法比较简单,如果其他的方法我们还得修改docker的配置文件,对于原来的配置文件,我们能不修改尽量别修改。

3. 列出docker镜像的命令

说完目录结构,我们再说下镜像怎么列出。其实很简单,我们只要运行docker images命令就可以把下载好的镜像列出来了:

通过这个命令我们能看到镜像的名称、TAG版本信息、image ID、创建时间、镜像大小。

三、导入导出镜像

下载好的镜像,假如我们想要导出保存到其它地方或者导入到另外的容器环境里怎么办?那么我们这里要用到save 、load和export 命令。

比如我们要保存之前nginx镜像到本地/home路径下,那么执行命令:

# docker save 2d04e7c52fc3 > /home/nginx-save-v3.tar 

2d04e7c52fc3 就是这个nginx:v3镜像的ID,然后nginx-save-v3.tar就是你自己名的tar包名,注意,这里只能是tar包的格式,不要写成其他的格式。

通过save的方式能保存镜像的所有信息。

如果想把保存的镜像导入到其他容器环境里,只要把导出的tar包文件拷贝到目标容器环境里,比如放到目录容器服务器里的/root路径下,然后执行docker load命令就可以导入:

docker load < /root/nginx-save-v3.tar      

加载成功后,运行docker images发现镜像名称、标签均为none,所以我们得使用docker tag 1e70071f4af4 nginx:v3 命令把镜像名称和标签名称给打上,这样方便我们识别镜像。

说完save 和 load命令,我们来讲讲export命令。

注意,export命令用于持久化容器,而不是镜像。所以,这个命令是作用于容器的。它的作用就是把当前运行的容器打包成tar,但是这个打包成的tar只是保存着容器当前的状态信息,之前的历史信息或者其他镜像分层信息是没有的。

我们运行 docker export <CONTAINER ID> > /home/export.tar 这样的命令即可操作,比如:

# docker export 5eff30763079 > /home/redis-save-latest.tar

注意那个ID是容器的ID,不是image 的ID。

export后的这个tar,我们还可以通过import命令导入到镜像列表里:

# docker import /home/redis-save-latest.tar redis/redis:v5

四、删除本地镜像

我们能获取镜像,当然我们也有可能删除镜像。因为有的镜像我们不需要了,或者本地硬盘不够了... 删除镜像的方法很简单,首先你的保证当前你运行的容器没用到你想要删除的镜像,不然是删除不了的。

比如我们删除这个ubuntu镜像,那么它就提示说这个镜像有容器正在使用...

因此,你首先得把这个正在运行的容器服务stop掉,然后rm掉这个容器服务,才能删除这个image。

# docker stop b37f8d63c82d            停止容器服务

# docker rm b37f8d63c82d               删除容器

# docker image rmi ubuntu                删除镜像

这里要注意的是,假如你没停止容器服务,直接运行docker image rm -f ubuntu的话,那么你运行docker images命令,ubuntu那列会显示none,这样,这个镜像其实没有真正删除。

真正删除应该是有这样的提示:

五、使用commit构建镜像

commit英文里是提交的意思。在docker里,我们用这个命令才提交容器的当前状态,然后形成一个镜像文件。其实它是制作镜像的一种方法,但是这种方法有它的缺陷,我们不推荐使用。真正做镜像的专业方法得用dockerfile的方式,我们下一篇文章会讲到。

我们从docker hub里下载的通用官方镜像如果不满足我们个性化需求的时候,可能我们得做一些修改,在下载好的镜像基础上安装或者配置我们需要的设置。当做好这些修改后,我们就需要保存下,然后继续下次直接使用。我们前面说过,镜像是一层一层叠加的,像集装箱一样,后面一层都是以前面一层为基础。

比如我们之前运行的那个nginx容器,我们从官网下载运行后,界面是nginx默认的欢迎界面。如果我们想改成我们想要的界面,那么我们可以进到这个容器里,运行下面命令把index.html文件的内容改下:

# docker exec -it webserver bash    进入nginx容器

# echo '<h1>Welcome, Docker</h1>' > /usr/share/nginx/html/index.html

然后我们刷新下浏览器,首页就会改变成 Welcome, Docker 。

我们还可以运行docker diff 命令查看都做了哪些操作:

我们发现,简简单单的修改一个界面,就多了这么多变更...

最后,我们做了修改,想把当前的容器做成image(好比虚拟机做快照打包),那么我们可以用docker commit命令,语法格式为:

docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

比如下面这个commit命令:

# docker commit --author "bowenqiu" --message "修改了默认网页" webserver nginx:v3

--author "bowenqiu"  : 注明下镜像作者

--message "修改了默认网页" : 注明下这个镜像做了什么修改

webserver:指定对哪个运行的容器做commit

nginx:v3 : 生成后的镜像名和版本号

运行完这个命令后,然后执行docker images,可以看到新生成的镜像:

然后这个新生成的镜像运行容器了,运行成功后,你看下界面是不是显示Welcome, Docker而不是nginx的默认界面。

使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是还是提醒大家,如果要真正构建镜像还得用dockerfile方式,因为commit方式有很多缺陷

首先,如果仔细观察刚才运行的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿(容器镜像拒绝肥胖!)。

此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。

而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。

所以, 还是用下一篇讲到的dockerfile方式吧.......

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏散尽浮华

Docker容器学习梳理--Volume数据卷使用

之前部署了Docker容器学习梳理--基础环境安装,接下来看看Docker Volume的使用。 Docker volume使用 Docker中的数据可以存储在...

6698
来自专栏搜云库

Docker Compose 1.16.1 安装

Docker Compose 简介 一个使用Docker容器的应用,通常由多个容器组成。使用Docker Compose,不再需要使用shell脚本来启动容器。...

26510
来自专栏緣來來來

Mac下Docker容器的安装和简单使用

https://store.docker.com/editions/community/docker-ce-desktop-mac

1.1K2
来自专栏流柯技术学院

CentOS 6.8下安装docker并使用

Docker是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。利用Linux的LXC、AUFS、Go语言、cgroup实...

3732
来自专栏LuckQI

linux命令下~docker的使用学习

1093
来自专栏散尽浮华

Docker容器学习梳理--容器间网络通信设置(Pipework和Open vSwitch)

自从Docker容器出现以来,容器的网络通信就一直是被关注的焦点,也是生产环境的迫切需求。容器的网络通信又可以分为两大方面:单主机容器上的相互通信,和跨主机的容...

1K10
来自专栏python3

Docker修改hosts

Docker修改hosts?这还不简单,打开vim直接敲就完事儿了!然而事与愿违,事情没有我们想的那么简单。在很多场景中,比如我们需要搭建一个集群,这时候容器要...

6842
来自专栏Jerry的SAP技术分享

Docker入门系列之二:使用dockerfile制作包含指定web应用的镜像

在前一篇文章:Docker入门系列之一:在一个Docker容器里运行指定的web应用 里,

990
来自专栏木子昭的博客

从零搭建docker私有仓库

771
来自专栏Java帮帮-微信公众号-技术文章全总结

三个技巧,将Docker镜像体积减小90%【面试+工作】

在构建Docker容器时,应该尽量想办法获得体积更小的镜像,因为传输和部署体积较小的镜像速度更快。

971

扫码关注云+社区