博文大纲:

  • 一、docker镜像的创建方法 1、基于已有镜像创建 2、基于本地模板创建 3、基于dockerfile创建 4、搭建私有仓库及其使用方法

一、docker镜像的创建方法

docker镜像是除了docker的核心技术之外,也是应用发布的标准格式。一个完整的docker镜像可以支撑一个docker容器的运行,在docker的整个使用过程中,进入一个已经定型的容器之后,就可以在容器中进行操作,最常见的操作就是在容器中安装应用服务,如果要把已经安装的服务进行迁移,就需要把环境及搭建的服务生成新的镜像。

创建镜像的方法有三种,分别是基于已有镜像创建、基于本地模板创建及基于dockerfile创建。接下来会将这几种方法依次写下来。

1、基于已有镜像创建

基于已有镜像创建主要是使用docker commit命令,本质上就是把一个容器里面运行的程序及该程序的运行环境打包起来生成新的镜像。

命令格式如下:

docker  commit  [选项]  容器ID/名称  仓库名称:[标签]

常用选项如下:

  • -m:说明信息;
  • -a:作者信息;
  • -p:生成过程中停止容器的运行

基于已有镜像创建新的镜像举例:

(1)启动一个镜像,在容器里做修改,然后将修改后的容器提交为新的镜像,需要记住该容器的ID号,如下:

[root@localhost ~]# docker ps -a                #查看当前运行的容器
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                       NAMES
c037e7a5734b        docker.io/sameersbn/bind   "/sbin/entrypoint...."   19 seconds ago      Up 7 seconds        53/tcp, 10000/tcp, 53/udp   hopeful_clarke
[root@localhost ~]# docker exec -it c037e7a5734b /bin/bash            #进入该容器
root@c037e7a5734b:/# echo 11111111111 > /etc/a.txt               #随便写入一个文件
root@c037e7a5734b:/# exit                      #退出该容器
exit
[root@localhost ~]# docker commit -m "newnamed" -a "ljz" c037e7a5734b docker:mynamed
#使用docker commit命令创建一个新的镜像
sha256:e178f320e4821642bed66d0e61e8a85eedd841cb8a3a84db3d38e7d92d844eae
[root@localhost ~]# docker images | grep mynamed          #查看新创建的镜像
docker                        mynamed             e178f320e482        11 seconds ago      323 MB
[root@localhost ~]# docker create -it docker:mynamed /bin/bash       #基于新创建的镜像创建一个容器
dc37cf2d6ef754200aea067d7a15c83713f2488dac0913013373809633266f07
[root@localhost ~]# docker ps -a               #获取刚刚创建的容器ID号,下面标红的就是
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                       NAMES
'dc37cf2d6ef7'        docker:mynamed             /sbin/entrypoint....   11 seconds ago      Created                                         pensive_williams
c037e7a5734b        docker.io/sameersbn/bind   /sbin/entrypoint....   6 minutes ago       Up 5 minutes        53/tcp, 10000/tcp, 53/udp   hopeful_clarke
[root@localhost ~]# docker start dc37cf2d6ef7            #启动刚创建的容器
dc37cf2d6ef7
[root@localhost ~]# docker exec -it dc37cf2d6ef7 /bin/bash          #查看在之前容器里写入的文件是否存在
root@dc37cf2d6ef7:/# cat /etc/a.txt 
11111111111
#可以看到新创建的容器中,有在之前容器中创建的文件,说明镜像更改成功

2、基于本地模板创建

通过导入操作系统模板文件可以生成镜像,模板可以从OPENVZ开源项目下载,或者 https://wiki.openvz.org/Download/template/precreated ,优先使用OPENVZ开源项目那个链接;

基于本地模板创建举例:

1、下载centos 7的迷你版系统模板,使用docker导入命令导入为本地镜像:

[root@localhost ~]# wget https://download.openvz.org/template/precreated/centos-7-x86_64-minimal.tar.gz             #下载centos7的模板压缩包
                           .................. #省略部分内容
[root@localhost ~]# ls | grep centos*                  #确认已经下载centos7的模板压缩包
centos-7-x86_64-minimal.tar.gz
[root@localhost ~]# cat centos-7-x86_64-minimal.tar.gz | docker import - docker:new  
#使用docker导入命令导入为本地镜像
sha256:c065d5c0571df48eba3b95b1302494b596cf9f67c24eacc82ff75a9e9c2b7622
[root@localhost ~]# docker images | grep new         #查看导入的镜像
docker                        new                 c065d5c0571d        56 minutes ago      435 MB
#至此,可使用这个镜像创建容器并部署需要的功能,进行使用了。

3、基于dockerfile创建

dockerfile是由一组指令组成的文件,其中每条指令对应Linux中的一条命令,docker程序将读取dockerfile中的指令生成指定镜像。

dockerfile结构大致分为四个部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。dockerfile每行支持一条指令,每条指令可携带多个参数,支持使用“#”号开头的注释。

dockerfile中的配置项介绍:

[root@localhost ~]# docker tag docker:new centos7:system            
#将上面下载的centos 7迷你镜像更改下名字及标签,以便区分
[root@localhost ~]# docker images | grep system             
#确认基础镜像已经准备好(就是一个centos 7的迷你系统进行)
centos7                       system              c065d5c0571d        About an hour ago   435 MB
[root@localhost ~]# vim Dockerfile   #编辑一个Dockerfile文件,注意:文件名最好就是Dockerfile

FROM centos           #第一行必须指明基于的基础镜像(该镜像必须存在)
MAINTAINER The centos project <ljz@centos.org>    #维护该镜像的用户信息
#以下是镜像的操作指令
RUN yum -y update      
RUN yum -y install openssh-server
RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
EXPOSE 22       #开启22端口
CMD ["/usr/sbin/sshd","-D"]     #启动容器时执行指令

在编写dockerfile时,有严格的格式需要遵循:第一行必须使用FROM指令指明所基于的镜像名称;之后使用MAINTAINER指令说明维护该镜像的用户信息;然后是镜像操作相关指令,如RUN指令,每运行一条指令,都会给基础镜像添加新的一层;最后使用CMD指令来指定启动容器时要运行的命令操作。

dockerfile有十几条命令可用于构建镜像,其中常见的指令如下:

dockerfile使用举例——使用dockerfile创建apache镜像并在容器中运行

(1)创建镜像并加载到容器中运行:

[root@localhost ~]# mkdir apache               #建立工作目录
[root@localhost ~]# cd apache/                #切换至新建的目录
[root@localhost apache]# vim Dockerfile             #编写dockerfile文件

FROM centos                #基于的基础镜像centos
MAINTAINER the centos project <ljz@163.com>     #维护该镜像的用户信息
RUN yum -y update            #使用yum进行更新
RUN yum -y install httpd        #镜像操作指令安装apache软件包
EXPOSE 80                         #开启80端口
ADD index.html /var/www/html/index.html            #将本地的首页文件index.html复制到镜像中
ADD run.sh /run.sh      #将本地的运行脚本run.sh复制到镜像中
RUN chmod 775 /run.sh       #赋予脚本执行权限
RUN systemctl disable httpd             #设置apache服务不自行启动
CMD ["/run.sh"]             #启动容器时执行脚本
#输入上述信息后,保存退出
[root@localhost apache]# docker images | grep centos     
#确保本地有centos的基础镜像,因为在dockerfile文件中指定了该镜像
centos7                       system              c065d5c0571d        About an hour ago   435 MB
[root@localhost apache]# vim run.sh               #编写执行脚本内容

#!/bin/bash
rm -rf /run/httpd/*              #清理httpd的缓存
exec /usr/sbin/apachectl -D FOREGROUND           #启动apache服务
[root@localhost apache]# echo "dockerfile  test" >index.html          #编写一个首页文件
[root@localhost apache]# ls               #确保当前目录下的文件有以下三个
Dockerfile  index.html  run.sh
#当以上准备完成后,就可以使用docker  build命令来创建镜像,如下:
[root@localhost apache]# docker build -t httpd:centos .     
#注意上面命令的末尾有个“.”,表示当前路径,若不加则会报错。
#其中“-t”选项用来指定镜像的标签信息
Sending build context to Docker daemon 4.096 kB
Step 1/10 : FROM centos
Trying to pull repository docker.io/library/centos ... 
latest: Pulling from docker.io/library/centos
  .................. #省略部分内容,此处需要静等几分钟,此时系统在执行dockerfile文件中的指令。
	#若在显示信息中有报红的信息,只要配置文件无误,一般属于正常(是有关yum的信息)。
Removing intermediate container 81a3d6c9d3db
Step 10/10 : CMD /run.sh
 ---> Running in 5cdc467fd874
 ---> 5d56b826432d
Removing intermediate container 5cdc467fd874
Successfully built 5d56b826432d
#当出现以上提示,则表示新的镜像已经创建成功了。
#在整个创建过程中,可以看到每运行一次dockerfile中的指令,都会以给初始镜像加上新的一层。
[root@localhost apache]# docker run -d -p 81:80 httpd:centos  #将新生成的镜像加载到容器中运行。
#其中“-p”选项实现从本地端口81到容器中80端口的映射。
192cd783028dcb3013ebb40b65ba8450e695e424e700a13cb8a44bb84af3e71a
[root@localhost apache]# docker ps -a         #查询容器是否运行
CONTAINER ID        IMAGE                      COMMAND                  CREATED              STATUS              PORTS                       NAMES
192cd783028d        httpd:centos               "/run.sh"                About a minute ago   Up About a minute   0.0.0.0:81->80/tcp          gallant_khorana
                  #省略部分内容

至此,可以看到新生成的镜像已经在容器中加载运行了,本机(宿主机,不是docker容器)的IP地址为192.168.1.1,此时client访问192.168.1.1的81端口,就相当于访问了刚运行的容器的80端口,如下:

4、搭建私有仓库及其使用方法

(2)将镜像上传到仓库中:

随着创建的镜像增多,就需要有一个保存镜像的地方,这就是仓库,目前有两种仓库:公共仓库和私有仓库,公司的生产环境中大多数都是保存到私有仓库的,最简单的还是在公共仓库上下载镜像,若是上传镜像至公共仓库,还需要注册并登陆,关于公共仓库的上传,可以参考博文Docker的概念及安装配置中的上传镜像部分。

那么怎么构建私有仓库呢?可以使用registry来搭建本地私有仓库。如下:

[root@localhost ~]# docker search registry               #查询关键字“registry”
INDEX       NAME                                          DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/registry                            The Docker Registry 2.0 implementation for...   2679      [OK]       
                 ..................#省略部分内容
[root@localhost ~]# docker pull docker.io/registry     #下载排名靠前的镜像
                 ..................#省略部分内容
Status: Downloaded newer image for docker.io/registry:latest    #下载成功
[root@localhost ~]# vim /etc/sysconfig/docker           
#修改docker配置文件指定私有仓库URL,否则在自定义的私有仓库中上传镜像时会报错

# /etc/sysconfig/docker

# Modify these options if you want to change the way the docker daemon runs
OPTIONS='--selinux-enabled --insecure-registry=192.168.1.1:5000'
#更改上面一行内容,其中的IP地址是作为私有仓库服务器的IP地址,这里就是本机的IP地址。
                 ..................#省略部分内容
#修改完毕后保存退出
[root@localhost ~]# systemctl restart docker      #重启docker服务

使用下载好的registry镜像启动一个容器,默认情况下仓库存放于容器内的/tmp/registry目录下,使用-v选项可以将本地目录挂载到容器内的/tmp/registry目录下使用,这样就不怕容器被删除后镜像也会随之丢失。在本地启动一个私有仓库服务,监听端口号为5000。

**注意:**我本地有一个/data/registry目录(挂载的是一个高可用的GFS文件系统,也可以使用NFS,自行选择即可,但是建议对于重要的数据存放目录,一定要保证容量的动态扩展以及磁盘损坏造成数据丢失的问题),将要挂载到私有仓库容器中的/tmp/registry目录中用于存放上传到私有仓库的镜像文件。

[root@localhost ~]# df -hT /data/registry/             #查看我这个目录所使用的文件系统
文件系统         类型            容量  已用  可用 已用% 挂载点
node4:dis-stripe fuse.glusterfs   80G  130M   80G    1% /data/registry
[root@localhost ~]# docker run -d -p 5000:5000 -v /data/registry/:/tmp/registry docker.io/registry
#启动私有仓库,并做端口映射到主机的5000端口,将本地的/data/registry目录挂载到容器中的/tmp/registry目录
#docker.io/registry是刚才下载的私有仓库镜像。
a6bf726c612b826e203d6a5bc9eaba26c36195913d3ea546c2111ce290a5524d
[root@localhost ~]# docker tag docker.io/registry 192.168.1.1:5000/registry     
#使用docker  tag命令将要上传的镜像docker.io/registry改一下标记,其中的IP及端口为固定的,否则无法连接到私有仓库
#因为在上面运行容器时,做了端口映射,将私有仓库的端口号映射到了宿主机的5000端
口,
#所以直接访问宿主机的5000端口,就相当于访问了私有仓库。
[root@localhost ~]# docker images | grep 5000            #找到要上传的镜像
192.168.1.1:5000/registry     latest              f32a97de94e1        6 months ago        25.8 MB
[root@localhost ~]# docker push 192.168.1.1:5000/registry            #上传至刚刚运行的私有仓库
The push refers to a repository [192.168.1.1:5000/registry]
73d61bf022fd: Pushed 
5bbc5831d696: Pushed 
d5974ddb5a45: Pushed 
f641ef7a37ad: Pushed 
d9ff549177a9: Pushed 
latest: digest: sha256:b1165286043f2745f45ea637873d61939bff6d9a59f76539d6228abf79f87774 size: 1363
#下面再上传一个镜像,进行测试。
[root@localhost ~]# docker images | grep mynamed            #就上传它了
docker                        mynamed             e178f320e482        4 hours ago         323 MB
[root@localhost ~]# docker tag docker:mynamed 192.168.1.1:5000/named:test    
#老规矩,必须改仓库名,注意:若标签不是默认的latest,那么还需要在仓库名后面接上标签名
[root@localhost ~]# docker images | grep 192.168.1.1:5000/named     #确定更改成功
192.168.1.1:5000/named        test                e178f320e482        4 hours ago         323 MB
[root@localhost ~]# docker push 192.168.1.1:5000/named:test       #上传至私有仓库
The push refers to a repository [192.168.1.1:5000/named]
c756b9ec7fb0: Pushed 
7d8d01394159: Pushed 
72b7cd87d69b: Pushed 
3be48ef75683: Pushed 
9b28c58ad64b: Pushed 
75e70aa52609: Pushed 
dda151859818: Pushed 
fbd2732ad777: Pushed 
ba9de9d8475e: Pushed 
test: digest: sha256:44894a684eac72a518ae5fa66bcbe4e4a9429428ef7ac6f4761022f8ac45ac5f size: 2403

至此,测试就完毕了,但是,如何证明私有仓库使用的是本地的/data/registry这个目录呢?以及如何查看上传的镜像呢?(上传至私有仓库的镜像是无法使用普通的ls命令查看的)。

[root@localhost ~]# df -hT /data/registry/           #先查看本地/data/registry/ 挂载的文件系统
文件系统         类型            容量  已用  可用 已用% 挂载点
node4:dis-stripe fuse.glusterfs   80G  130M   80G    1% /data/registry
[root@localhost ~]# docker exec -it a6bf726c612b /bin/sh 
#进入私有仓库的容器中,该容器没有/bin/bash,所以使用的是/bin/sh。
/ # df -hT /tmp/registry/   #查看发现,该目录挂载的和宿主机挂载的文件系统是同一个,说明没问题。
Filesystem           Type            Size      Used Available Use% Mounted on
node4:dis-stripe     fuse.glusterfs
                                    80.0G    129.4M     79.8G   0% /tmp/registry
                     ——————————我是分割线——————————	
#那么如何查看上传至私有仓库的镜像呢?请看下面:
[root@localhost ~]# curl -XGET http://192.168.1.1:5000/v2/_catalog   
#查看已经上传的镜像,可以看到刚刚上传的那两个镜像
{"repositories":["named","registry"]}
#只知道镜像名还不够,若要下载,还需要镜像对应的标签,那么怎么查看某个镜像的标签呢?
[root@localhost ~]# curl -XGET http://192.168.1.1:5000/v2/named/tags/list
#就这样查看咯,上面URL路径中的named就是镜像名,查看的就是镜像named对应的标签
{"name":"named","tags":["test"]}
[root@localhost ~]# docker pull 192.168.1.1:5000/named:test      #将私有仓库中的镜像下载下来
#前面必须指定私有仓库的访问地址,就是上传时的名字是什么,下载时就是什么,哪怕查询的镜像名中没有IP地址。
Trying to pull repository 192.168.1.1:5000/named ... 
sha256:44894a684eac72a518ae5fa66bcbe4e4a9429428ef7ac6f4761022f8ac45ac5f: Pulling from 192.168.1.1:5000/named
Digest: sha256:44894a684eac72a518ae5fa66bcbe4e4a9429428ef7ac6f4761022f8ac45ac5f
Status: Downloaded newer image for 192.168.1.1:5000/named:test

附加: 若需要在其他服务器上下载私有仓库的镜像,需要在那个其他服务器上执行以下命令,以便指定私有仓库服务器地址:

[root@node1 ~]# echo '{ "insecure-registries":["xxx.xxx.xxx.xxx:5000"] }' > /etc/docker/daemon.json
#其中xxx.xxx.xxx.xxx:5000代表访问私有仓库的IP地址及端口,根据自己的服务器情况来定
[root@node1 ~]#systemctl restart docker          #重启docker服务

上面的方法是我百度到的,亲测有效,也可是尝试下这种方法(我没试过):

[root@node1 ~]# vim /etc/sysconfig/docker     #修改docker配置文件指定私有仓库URL

# /etc/sysconfig/docker

# Modify these options if you want to change the way the docker daemon runs
OPTIONS='--selinux-enabled --insecure-registry=192.168.1.1:5000'    #修改此行
[root@node1 ~]#systemctl restart docker          #重启docker服务

———————— 本文至此结束,感谢阅读 ————————