docker容器的状态转换所涉及的操作和机制,图中彩色标识的是可以看出docker容器处于一下状态中的某一种,如:created(创建),running(运行),paused(暂停),stopd(停止)。

docker-16

docker主机,也就是docker server端,通过http,https接受客户端的命令。docker daemon接受到命令后会在本地启动创建容器,一个docker主机上运行多个容器。容器启动基于镜像启动。如果没有镜像就会链接到docker registries。如下图:

docker-17

获取镜像后存储在本地,这个存储是特殊的,在1.18后的版本使用的是overlay2的存储驱动。镜像本身是只读的,镜像在镜像registries上的名称和镜像应用程序名称是一样的。仓库内可以有多个镜像,或者多个不同镜像的不同版本。使用标签来识别这些镜像。

镜像的构建#

镜像内包含有启动容器需要的文件系统和其他内容,作用在于创建并启动docker容器

docker采用了分层机制,最下层是bootfs,其中可以构建用户空间并且运行进程容器的是rootfs,如下图:

docker-18

底层驱动有lxc,aufs/btrfs,这些用来引导启动一个用户空间,这里的kernel内核实现的功能仅仅用来启动和引导用户空间,容器启动完成后会被卸载以节约内存资源

位于bootfs之上,表现为docker容器的根文件系统

[marksugar@www.linuxea.com ~]$ docker exec -it nginx_createrepo bash
[marksugar@www.linuxea.com /]# ls
bin   dev  home  lib64  mnt  proc  run   srv         sys  usr
data  etc  lib   media  opt  root  sbin  startup.sh  tmp  var

传统系统中,系统启动时候,根文件被挂载后会进行自检,为了避免自检时候出现错误而导致数据被误伤,在自检之前一般挂载的是只读,自检完成没有问题后才会重新挂载为读写模式。

而在docker中整个rootfs都是由内核挂载为"只读",而后通过“联合挂载”技术额外挂载一个“可写”层。

image layers#

一般在一个应用镜像中,最底层的称为系统镜像,位于下层的也可以叫做父镜像。最上是可读写层,其下都是只读。如下图

docker-19

假如要做一个nginx镜像,就需要在一个基础镜像上,这个镜像也必然是纯净的,最小化的系统镜像,或centos,debian,ubuntu等。

在这之上添加软件,构建过程等,并且这些是有层级关系的。系统镜像供给,配置编译等等,而这些都会被认为是一个层,在启动的时候这些都会被认为是一个层级。而bootfs在引导完成后就会被从内存中移除掉。而最上层writable才是可进行编辑可写,并且随着容器的删除而删除,也就是说可写层也没了。如上图。

其他参考:https://docs.docker.com/storage/storagedriver/#images-and-layers

aufs#

镜像的分层构建和联合挂载依赖于中游文件系统支撑实现,早期的aufs( advanced multi-layered unification filesystem,翻译过来是多层统一的文件系统)就是被docker用于实现联合挂载的linux文件系统。aufs是对早期UnionFS的完全重构。

docker的分层镜像除了aufs,还支持btrfs,devicemapper和vfs等。在ubuntu系统下,docker默认使用ubuntu的aufs;而centos7,用的是devicemapper。而overlayfs在3.18版本开始已经被合并到linux内核。我们现在用的是overlayfs2

registry#

镜像在本地build完成可以上传到registry上,而后在从registry拉取使用。如果本地有,就先用本地的,如果本地没有拉取registry上的。registry默认是dockerhub,如果需要指定自定义就需要重新修改配置,如果没有就默认dockerhub。docker本身是https,如果自建registry要使用http,就需要重新定义信任关系。

分类: ​ sponsor registry:第三方的registry,供客户和社区使用 ​ mirror registry: 第三方registry,如:aliyun加速镜像。 ​ vendor registry: 服务商的registry,如: 红帽registry ​ private registry: 自建registry。

registry分为两部分,仓库和索引共同组成,每个仓库有特定的docker镜像和不同的版本组成,同时分为顶层仓库和用户仓库两类,用户仓库名格式就是"用户名/仓库名"。而没个仓库可以包含多个tag,每个标签对应一个镜像。

index维护用户账号,镜像验证以及公共名称空间的信息,相当于registry提供一个完成用户认证等功能的检索接口。

registry中的镜像由开发人员制作,而后推送到公共或者是有的registry仓库中保存,以便于其他人员部署使用。如下:

docker-20

通常,开发人员(从内网仓库或者dockerhub)拉取基础镜像,在原有的镜像之上添加一些应用,而后push到私有仓库中,而后使用的人进行拉取使用。其中,可进行环境变量配置,并且能够注入一些变量替换配置文件的。

而dockerhub可以将Docker Hub关联到GitHub帐户进行使用。如下图

docker-21

github仓库可以与docker hub仓库建立关联关系,而docker hub可以持续监控github仓库,一旦添加新的内容,push到github后,github内容改变就进行重新build。

制作镜像#

通常,编辑一个dockerfile进行build,或者将运行中的容器containers,使用commit将容器最上一层可写层单独创建一个镜像。或者如上github antomated builds,这种方式还是需要基于dockerfile。大致形式如下:

docker-22

我们run起一个容器,而后在容器内添加一些文件。随后将这个容器commit成一个镜像,在将此镜像run起来。如上图containers部分。

我使用busybox,使用httpd测试,首先创建一个index.html文件,添加一段helo www.linuxea.com

[marksugar@www.linuxea.com ~]$ docker run --name 5idocker -it busybox 
/ # mkdir /data/5idocker -p
/ # cat /data/5idocker/index.html 
helo www.linuxea.com
/ # httpd -d -h /data/5idocker/

现在这个容器的是运行的

[marksugar@www.linuxea.com ~]$ docker ps -a
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS               NAMES
8b8016950ac7        busybox                      "sh"                     10 minutes ago      Up 10 minutes                           5idocker

而后将这个容器commit -m版本是v1,id是8b8016950ac7,保存名称为linuxea/httpd:v1,这两段的意思是linuxea为名称,httpd是仓库名称也是镜像名称,v1是tag,这里的tag可以进行多次打标。

但是我这里不使用,而后直接用命令启动的时候输入命令即可

[marksugar@www.linuxea.com ~]$ docker commit -m "v1"  8b8016950ac7 linuxea/httpd:v1
sha256:2f502ef7d89a3f28b10d1c30c49e066634b4cfdab96b24bbb0e9e97b1f7a4291

而后如下

[marksugar@www.linuxea.com ~]$ docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
linuxea/httpd                      v1                  2f502ef7d89a        4 seconds ago       1.15MB

现在有了刚刚commit的镜像,现在run起这个镜像,并且传入参数httpd -f -h /data/5idocker,这样容器就运行在前台了。

[marksugar@www.linuxea.com ~]$ docker run -d -p 80:80 linuxea/httpd:v1 httpd -f -h /data/5idocker
9257381bd8abcbf25601aca4e94a05f115d75cc9981839ce5fc7b68a7ec4b49f

运行状态如下:

[marksugar@www.linuxea.com ~]$ docker ps -a
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS                       PORTS                NAMES
9257381bd8ab        linuxea/httpd:v1             "httpd -f -h /data/5…"   5 seconds ago       Up 4 seconds                 0.0.0.0:80->80/tcp   nostalgic_bhaskara

我们获取到ip,然后curl下

[marksugar@www.linuxea.com ~]$ docker inspect 9257381bd8ab|tail -20
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "aeb93fd1bb6abcc31fb3f34c055d8a24eb8e9b30ede398623848f514f8299d04",
                    "EndpointID": "677570f365c32a805400a69d779712d55aa6769dc83d4b8dd8c29248b771273c",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:04",
                    "DriverOpts": null
                }
            }
        }
    }
]
[marksugar@www.linuxea.com ~]$ curl 172.17.0.4
helo www.linuxea.com

这样以来镜像就已经打包好了。

dockerhub使用#

我们现在push到dockerhub上,我此前在dockerhub创建的一个5idocker。那么就需要将将名称打标签成5idocker。这里的用户名称(也就是linuxea这段)必须要dockerhub上的用户一样。因此我有5idocker用户,就需要打标成5idocker。

[marksugar@www.linuxea.com ~]$ docker tag linuxea/httpd:v1 5idocker/httpd:v1
[marksugar@www.linuxea.com ~]$ docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
5idocker/httpd                     v1                  2f502ef7d89a        34 minutes ago      1.15MB

登陆

[marksugar@www.linuxea.com ~]$ docker login -u 5idocker
[marksugar@www.linuxea.com ~]$ docker login -u 5idocker
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

docker push,这里可以不需要事先创建好httpd,也可以事先创建好这个项目

[marksugar@www.linuxea.com ~]$ docker push 5idocker/httpd:v1 
The push refers to repository [docker.io/5idocker/httpd]
de9d93fb552a: Pushed 
8a788232037e: Mounted from library/busybox 
v1: digest: sha256:07b0a8045f5df6ba1cd170f61ea66be1e44cbd6dcc2121d304c37cbd6c6e8a26 size: 734

而后dockerhub上就有了这个镜像

docker-23

并且可以被其他人所使用,因为是公开的。使用docker pull 5idocker/httpd即可

docker-24

dev.aliyum使用#

假如我要推送到dev.aliyun.com的话就需要在json文件中在添加一行

像这样:

[marksugar@www.linuxea.com ~]$ cat /etc/docker/daemon.json
{
  "registry-mirrors": ["https://9ykgc5q2.mirror.aliyuncs.com","https://registry.docker-cn.com"]
}

那么阿里云的创建参考和登陆密码设置就不演示,我们直接push

先退出之前的登陆

[marksugar@www.linuxea.com ~]$ docker logout
Removing login credentials for https://index.docker.io/v1/

登陆阿里云

[marksugar@www.linuxea.com ~]$  docker login --username=usertzc@163.com registry.cn-hangzhou.aliyuncs.com
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

而后修改下标签

[marksugar@www.linuxea.com ~]$ docker tag linuxea/httpd:v1 registry.cn-hangzhou.aliyuncs.com/linuxea/httpd:v1

push到阿里云

[marksugar@www.linuxea.com ~]$  docker push registry.cn-hangzhou.aliyuncs.com/linuxea/httpd:v1
The push refers to repository [registry.cn-hangzhou.aliyuncs.com/linuxea/httpd]
de9d93fb552a: Pushed 
8a788232037e: Pushed 
v1: digest: sha256:07b0a8045f5df6ba1cd170f61ea66be1e44cbd6dcc2121d304c37cbd6c6e8a26 size: 734

而后就可以在阿里云看到镜像被上传,大致如下图:

docker-25

本地save镜像#

当不想上传到hub上,或者没有网且没有仓库的情况下, 我们可以进行打包,甚至于打好几个镜像包,如下:

[marksugar@www.linuxea.com ~]$ docker save -o linuxea.gz linuxea/httpd:v1 nginx:1.14-alpine 
[marksugar@www.linuxea.com ~]$ ll linuxea.gz 
-rw------- 1 root root 20333568 11月 26 21:41 linuxea.gz

我们scp到其他机器解压使用即可

[marksugar@www.linuxea.com ~]$ scp linuxea.gz marksugar@10.10.240.142:~/
The authenticity of host '10.10.240.142 (10.10.240.142)' can't be established.
ECDSA key fingerprint is SHA256:CuxYCbuJuzJBjj2Ps7anOR7BKYx4w1Md80APwSJ5Ei8.
ECDSA key fingerprint is MD5:0b:26:0a:e4:1f:04:b5:71:64:a3:0f:e6:e7:11:ee:b0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.10.240.142' (ECDSA) to the list of known hosts.
marksugar@10.10.240.142's password: 
linuxea.gz    

而后load即可

[marksugar@DS-VM-Node142 ~]# docker load -i linuxea.gz 
de9d93fb552a: Loading layer [==================================================>]   5.12kB/5.12kB
Loaded image: linuxea/httpd:v1
df64d3292fd6: Loading layer [==================================================>]  4.672MB/4.672MB
9d687ea74fd6: Loading layer [==================================================>]  14.24MB/14.24MB
07616dc06659: Loading layer [==================================================>]  3.584kB/3.584kB
4468d9e70dc1: Loading layer [==================================================>]  4.608kB/4.608kB
Loaded image: nginx:1.14-alpine