容器技术之Docker镜像
在docker容器里运行的服务必须前台运行,如果后台运行会导致容器一启动就退出了,原因是容器内部本身就只有一个进程在跑,如果你后台运行,就没有进程在前台,所以docker会认为该容器已经宕机;其实我们可以理解为容器内部前台跑的程序是支撑整个容器为运行态的重要骨架;
前文我们聊了下docker的基础使用方法,大概介绍了下docker的架构,管理镜像、运行容器、管理容器的一些相关命令说明;回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12933412.html;今天这边博客主要来聊docker的镜像的制作和分发,以及相关镜像的操作和说明;
前面我们提到过docker最核心资源之一就是镜像,镜像就好比是我们的应用程序,要想运行它,前提是要拥有它,并把它装载操作系统上;而docker里的镜像就是把应用程序所依赖的库、文件、环境等打包在一起,组成一个静态的镜像文件;这样一来有了这个镜像,我们就可以把我们自己的应用程序在任何有docker环境的主机上运行成容器;这也是docker受欢迎的原因之一吧,解脱了程序员为其应用程序而苦恼;
docker镜像包含了应用程序启动所需的文件系统以及内容,其目的就是为创建并启动为docker容器;那么docker的镜像到底是怎么构建的呢?我们来看看下面的图大概就会明白
提示:docker镜像采用分层构建的机制,最低层为bootfs,该层的主要作用是用于系统引导的文件系统,包括bootloader和内核,容器启动完成后会被卸载以节省内存资源;在bootfs之上的是rootfs,该层主要表现为docker容器的根文件系统;传统模式中,系统启动之时,内核挂载rootfs时会首先将其挂载为只读模式,完成完整性自检后在将其重新挂载为读写模式;而docker中,rootfs有内核挂载为只读模式,而后通过联合挂载技术额外挂载一个可写成;如下图所示
提示:位于下层的镜像我们称为父镜像,最底层的镜像称其为基础镜像;最上层为可写层,其下的所有层都是只读层;从上面的图我们也可以看到,只有镜像运行成容器以后才会有可写层的;也就是说可写层属于容器而不属于镜像;其实docker容器是共享下面镜像层的,只有启动为容器后,各自的可写层相互隔离;
了解了docker的镜像构建方式后,我们在看看多个镜像怎么做到联合挂载的?
联合挂载的技术要源于AUFS这个文件系统,该文件系统的全称是advanced multi-layered unification filesystem翻译过来就是高级多层统一文件系统;该文件主要作用是为Linux文件系统实现“联合挂载”;aufs的前身是unionfs,2006年由junjiro okajima开发;docker最初使用aufs作为容器文件系统层,它目前仍作为存储后端之一,aufs的竞争产品是overlayfs,后者自从3.18版本开始被合并到Linux内核;docker的分层镜像,除了aufs,docker还支持btrfs,devicemapper和vfs等,在ubuntu系统下,docker默认使用的aufs;而在早期centos7上用的是devicemapper;所以早期跑docker都跑在ubuntu上,原因之一是ubuntu的内核版本较新,能够很好的兼容aufs文件系统;现在我们在centos7上运行docker默认使用的是overlayfs2文件系统;如下所示
提示:如果用运行docker建议backing filesystem 使用xfs文件系统,不要使用ext系列文件系统;
了解了docker的文件系统后,在来说一下docker的镜像仓库
docker启动容器时,docker 服务端会试图从本地获取相关镜像;本地镜像不存在时,将会从docker默认的镜像仓库中下载指定镜像并保持本地,然后再启动为容器;大致过程如下图所示
提示:从上图可以了解到docker客户端和服务端的一个工作流程,客户端通过http协议向服务端发送指令,服务端首先从本地查找是否有对应版本的镜像,如果有就启动为容器,如果没有就会去默认的registry中下载对应版本的镜像到本地,然后在启动为容器;对于docker服务端和registry来讲他们都需要各自的存储设备驱动来存储镜像;registry是用于保存docker镜像,包括镜像的层次机构和元数据;用户可以自建registry,也可使用官方的docker hub;registry分类主要有Sponsor registry 第三方的registry,供客户和docker社区使用;mirror registry 第三方的registry,只让客户使用;vendor registry 由发布docker镜像的供应商提供的registry;private registry 通过设有防火墙和额外的安全层的私有实体提供的registry;
registry主要包含了仓库和索引;所谓仓库就是由特定的docker镜像的所有迭代版本组成的镜像仓库;一个registry中可以存在多个repository,而对于repository来说可以分为顶层仓库和用户仓库,顶层仓库就是没有斜线分割的仓库,用户仓库是由用户名加斜线分割再加仓库名组成;每个仓库可以包含多个tag标签,每个标签对应一个镜像;所谓索引(index)主要用于维护用户帐号、镜像的校验以及公共命名空间的信息,相对于为registry提供一个完整用户认证等功能的检索接口;所以我们把registry理解为存放docker镜像仓库的仓库更为准确;docker registry中的镜像通常由开发人员制作,而后推送至公共或私有的仓库上保持,供其他人员使用;如下图所示
了解了registry和repository的关系后,我们在来说说docker hub;docker hub 是docker的官方仓库,它主要有如下几种特性;
1、image repositories,提供镜像仓库的功能
2、autometed builds,提供自动编译功能,什么意思呢?它支持我们向上传一个构建docker镜像的源码文件,它可以根据这个源码文件自动生成docker 镜像;这个源码文件叫dockerfile 主要用于编写镜像的构建过程的指令文件;
3、Webhooks,提供监控目标资源的变化实现根据目标监控对象上的资源变化而变化的一种机制;比如我们可以使用github和dockerhub联合起来,开发人员通过git向github提交代码(dockerfile),然后dockerhub就会一直盯着github上的资源,如果有变化,它就根据监控的资源变化而重新编译成一个镜像,类似这种功能;
4、organizations,支持创建组的方式对镜像仓库的访问管理
5、github and bitbucket integration,支持将github和bitbucket添加到你当前的docker 镜像工作流中;
了解了以上内容,我们在dockerhub上去注册一帐号,就可以创建仓库去存放我们自己制作好的镜像了;注册帐号这里我就不多说了,我们来说一下怎样制作镜像和怎么把镜像推送到我们自己的仓库中去;
基于现有镜像制作镜像
1、拖镜像到本地
[root@node1 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]# docker image pull busybox:latest latest: Pulling from library/busybox d9cbbca60e5f: Pull complete Digest: sha256:836945da1f3afe2cfff376d379852bbb82e0237cb2925d53a13f53d6e8a8c48c Status: Downloaded newer image for busybox:latest docker.io/library/busybox:latest [root@node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest 78096d0a5478 9 days ago 1.22MB centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]#
2、运行busybox为容器
提示:busybox容器默认提供的是一个模拟Linux很多命令的一个程序,本身没有跑任何进程;所以我们启动容器必须得用交互式终端才行,否则容器启动不起来;-it表示启动为交互式终端;
3、创建httpd的网页文件
提示:到此我们在容器内部的数据就准备好了。现在需要将我们修改后的数据保存下来制作成镜像分发给其他人使用;
4、基于现有容器制造镜像
[root@node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest 78096d0a5478 9 days ago 1.22MB centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]# docker container commit --help Usage: docker container commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] Create a new image from a container\'s changes Options: -a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>") -c, --change list Apply Dockerfile instruction to the created image -m, --message string Commit message -p, --pause Pause container during commit (default true) [root@node1 ~]# docker container commit -a "qiuhom <qiuhom@admin123.com>" -p -m "this is test image" -c \'CMD ["/bin/sh","-c","/bin/httpd -f -h /var/www/web/html"]\' b1 linux1874/myimg:v0.1 sha256:e408b1c6e04f0e5f5129989f35e6f613dec17d963770c4be8d6daa54343c5399 [root@node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE linux1874/myimg v0.1 e408b1c6e04f 5 seconds ago 1.22MB busybox latest 78096d0a5478 9 days ago 1.22MB centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]#
提示:-a表示指定作者信息,通常情况下作者信息是带邮箱地址的;-p 表示制作镜像时暂停容器,这样做主要是为了数据统一,预防制作过程中的数据增加;-m表示注解信息;-c表示指定容器内部运行的命令;从上面的结果看,我们在本地就可以看到我们制作的镜像;这里需要特别注意一点的时,在docker容器里运行的服务必须前台运行,如果后台运行会导致容器一启动就退出了,原因是容器内部本身就只有一个进程在跑,如果你后台运行,就没有进程在前台,所以docker会认为该容器已经宕机;其实我们可以理解为容器内部前台跑的程序是支撑整个容器为运行态的重要骨架;
5、登录自己dockerhub帐号
[root@node1 ~]# docker login Login with your Docker ID to push and pull images from Docker Hub. If you don\'t have a Docker ID, head over to https://hub.docker.com to create one. Username: linux1874 Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded [root@node1 ~]#
6、推送镜像到dockerhub中我们自己的仓库中去
[root@node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE linux1874/myimg v0.1 e408b1c6e04f 7 minutes ago 1.22MB busybox latest 78096d0a5478 9 days ago 1.22MB centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]# docker image push --help Usage: docker image push [OPTIONS] NAME[:TAG] Push an image or a repository to a registry Options: --disable-content-trust Skip image signing (default true) [root@node1 ~]# docker image push linux1874/myimg:v0.1 The push refers to repository [docker.io/linux1874/myimg] 4d567d38fed1: Pushed 1079c30efc82: Mounted from library/busybox v0.1: digest: sha256:6c2f6b7a0df5ca0a46cd46d858e9fd564169471e6715c0155027ac77672508f6 size: 734 [root@node1 ~]#
提示:制作的镜像打标签时,需要打成同仓库名一致的名称;到此我们就把我们制作好的镜像推送到我们的仓库中去了
7、到dockerhub仓库中查看是否存在我们刚才制作好的镜像?
提示:可以看到我们有一个叫v0.1的镜像;
8、另外开启一主机,下载该镜像,看看是否能够启动为容器,并提供httpd服务呢?
[root@docker_node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE wordpress latest c3fa1c8546fb 3 weeks ago 540MB mysql 5.7 f965319e89de 3 weeks ago 448MB httpd 2.4.37-alpine dfd436f9a5d8 17 months ago 91.8MB [root@docker_node1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@docker_node1 ~]# docker image pull linux1874/myimg:v0.1 Error response from daemon: pull access denied for linux1874/myimg, repository does not exist or may require \'docker login\': denied: requested access to the resource is denied [root@docker_node1 ~]# docker login Login with your Docker ID to push and pull images from Docker Hub. If you don\'t have a Docker ID, head over to https://hub.docker.com to create one. Username: linux1874 Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded [root@docker_node1 ~]# docker image pull linux1874/myimg:v0.1 v0.1: Pulling from linux1874/myimg d9cbbca60e5f: Pull complete ab68b1a31f97: Pull complete Digest: sha256:6c2f6b7a0df5ca0a46cd46d858e9fd564169471e6715c0155027ac77672508f6 Status: Downloaded newer image for linux1874/myimg:v0.1 docker.io/linux1874/myimg:v0.1 [root@docker_node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE linux1874/myimg v0.1 e408b1c6e04f 20 minutes ago 1.22MB wordpress latest c3fa1c8546fb 3 weeks ago 540MB mysql 5.7 f965319e89de 3 weeks ago 448MB httpd 2.4.37-alpine dfd436f9a5d8 17 months ago 91.8MB [root@docker_node1 ~]# docker run --name myweb1 -d linux1874/myimg:v0.1 5bd8e32089c0431399c9f81bbbdcf946817d3f8ab32ffc1caf072e73ed9ef5d9 [root@docker_node1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5bd8e32089c0 linux1874/myimg:v0.1 "/bin/sh -c \'/bin/ht…" 8 seconds ago Up 7 seconds myweb1 [root@docker_node1 ~]#
提示:因为我建立的是私有仓库,所以只有登录仓库后才可以下载;如果是公有仓库就不需要登录;从上面的信息看,我们下载的镜像已经下载到本地,并启动为容器了;
9、在宿主机上访问容器内部httpd服务,看看是否能够响应我们提供的主页?
[root@docker_node1 ~]# docker container inspect -f "{{.NetworkSettings.Networks.bridge.IPAddress}}" myweb1 172.17.0.2 [root@docker_node1 ~]# curl http://172.17.0.2 this test file [root@docker_node1 ~]#
提示:可以看到是可以访问到内部容器的httpd服务,响应给我们的主页也是我们自己制作镜像时提供的主页文件;
到此基于现有容器制作镜像、分发镜像到仓库的过程就测试完了;