镜像制作大法
一、前言
上一节《程序员背锅救星-docker》中,我们讲述了docker
最基本的概念以及镜像、容器以及仓库的概念和使用,还包含了实践内容,我们通过拉取centos
镜像,创建容器并且进入容器对linux
系统进行操作;但是那只是利用了仓库中的镜像,然后创建容器进行操作的,那我们如果需要自己的镜像改怎么做呢?这次就和大家一起看看镜像是怎么制作的~
二、制作镜像
一般制作镜像有两种方式,一种是基于commit
命令,另外一种是基于dockerfile
创建镜像;
过程如下图所示:
![](https://imgkr2.cn-bj.ufileos.com/89c8aeea-83da-4161-80b9-53a5d09a304d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=xuAR5MNWiCBd4UQwK6RCITEcBUA%253D&Expires=1605365629)
对于这两种创建镜像的方式,我们会实地操作一下,并且会区分一下两种镜像创建的优点缺点;
一般在工作中,会采用第二种方式,至于为什么不采用第一种,后面细说~
<1>基于commit
命令
基于
commit
命令
命令:
docker commit :从容器创建一个新的镜像。
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
OPTIONS说明:
-
-a :提交的镜像作者; -
-c :使用 Dockerfile
指令来创建镜像; -
-m :提交时的说明文字; -
-p :在 commit
时,将容器暂停。
实践:
按照之前《程序员背锅救星-docker》中的实例,我们把这个容器制作成一个新的镜像;
1、拉取镜像
docker pull centos:centos7
![](https://imgkr2.cn-bj.ufileos.com/64aacb14-e36e-4259-9083-95c30d4a1957.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=G7O5Njd7H9tGMSfCMSrYUh1NZWA%253D&Expires=1605365710)
2、运行镜像
docker run -itd --name centos-test centis:centos7
![](https://imgkr2.cn-bj.ufileos.com/5e48da51-4b84-474c-b637-10a5d63c18f6.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=2pmSy4%252BT3sfyoSYUSpo%252FBijS5sI%253D&Expires=1605365695)
3、进入容器并安装gcc、vim
![](https://imgkr2.cn-bj.ufileos.com/b5fd4ada-c4f6-45e7-aa13-4b8eec31d8a9.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=3h9LIQW%252FDdWPrg%252BIbg%252FCPcDxZp8%253D&Expires=1605365739)
4、在vim中编写C代码并编译运行
#include <stdio.h>
int main()
{
printf("this is test\n");
return 0;
}
![](https://imgkr2.cn-bj.ufileos.com/8f26be41-bea0-4d01-a64d-bf35e0b488aa.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=qgmIF05wfo0lsiA9TRXixPSVKkA%253D&Expires=1605365766)
5、退出容器
exit
![](https://imgkr2.cn-bj.ufileos.com/53d4fe7b-8c48-40d4-8933-557b97b2db80.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=kZ3gS%252BOS%252FOU7%252BCQI5z%252F9jsMmkJ0%253D&Expires=1605365782)
6、获取容器
docker pa -a
![](https://imgkr2.cn-bj.ufileos.com/122d5c76-691e-4878-8b0f-9b60f883b161.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=7C%252FuJw2Y7SjtH4EaFwk7Q%252BIkLoU%253D&Expires=1605365796)
7、从容器中制作镜像
docker image 10ba02adb18f
![](https://imgkr2.cn-bj.ufileos.com/2da61cfc-cdeb-48e3-a725-bfd0803357af.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=FHhKlMlgS788FHo3WEkFDjde7wo%253D&Expires=1605365806)
8、再次制作容器
docker run -it --name test mycentos:V1 /bin/bash
进去发现,我们之前所有的环境都存在,证明我们的镜像制作的很成功
![](https://imgkr2.cn-bj.ufileos.com/7b4404a1-96d8-44f7-979b-853c2e615efb.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=e7baDmAATr7Uw%252FMtNRtzp%252FgGPbA%253D&Expires=1605365819)
以上就是使用commit制作docker进行全部过程啦~
<2>基于dockerfile
基于
dockerfile
1、dockerfile
概念
Dockerfile
是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile
从FROM命令开始,紧接着跟随者各种方法,命令和参数。其产出为一个新的可以用于创建容器的镜像。
Dockerfile
由一行行命令语句组成,支持#注释。一般分为四个部分:基础镜像,维护者信息,镜像操作指令和容器启动时执行指令
2、Dockerfile
命令
下面简单的介绍几个命令:
-
FROM
指明构建的新镜像是来自于基础镜像是什么,比如:
FROM gcc:latest
-
RUN
用于执行后面跟着的命令行命令,有两种格式:
-
shell 格式:RUN <命令行命令>(终端操作命令一样),例如:
RUN mkdir tmp
-
exec 格式:RUN ["可执行文件", "参数1", "参数2"],例如:
RUN ["./test"]
-
-
COPY
拷贝文件或目录到镜像中,COPY...,例如:
COPY test.cpp /home/CPP
-
ADD
拷贝文件或目录到镜像中,ADD...例如:
ADD test.cpp /home/CPP
如果是URL或压缩包,会自动下载或自动解压。
-
WORKDIR
为RUN、CMD、ENTRYPOINT以及COPY和AND设置工作目录,例如:
WORKDIR /home/CPP
-
CMD
启动容器时执行的Shell命令,例如:
CMD [“./test”]
-
CMD 在docker run 时运行。 -
RUN 是在 docker build。
注:如果
Dockerfile
中如果存在多个CMD
指令,仅最后一个生效。 -
-
ENV
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV <key> <value>
基本的命令就是这些了,对于我们构建一个镜像也差不多了,后续需要其他命令,可以自行学习~
实践
构建一个运行C++程序的镜像
1、编写C++代码
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
while(1)
{
sleep(1);
cout<<"this is test"<<endl;
}
return 0;
}
2、编写dockerfile
FROM codenvy/cpp_gcc
RUN mkdir /home/test
ADD test.cpp /home/test
WORKDIR /home/test
RUN g++ test.cpp
CMD ["./a.out"]
3、创建镜像
执行命令:
docker build -f ./test_dockerfile -t test_cpp_dockerfile .
如果提示下面的错误:
---> Running in de73d0b0da3e
mkdir: cannot create directory ‘/home/test’: Permission denied
The command '/bin/sh -c mkdir /home/test' returned a non-zero code: 1
需要在dockerflie中加上
USER root
![](https://imgkr2.cn-bj.ufileos.com/d72ebf7b-1b5a-4c2e-afad-eb1b08b1b9ef.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=TBRHR6pbgMekIMZ0pY8PDNBCns4%253D&Expires=1605365846)
5、查看镜像是否创建成功
docker images
![](https://imgkr2.cn-bj.ufileos.com/e5cc5b91-1118-4e56-9368-a155f45bd6c8.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=ffhvur3jxDgZoSLrnVOUIsiPKRM%253D&Expires=1605365858)
6、运行容器
docker run test_cpp_dockerfile
![](https://imgkr2.cn-bj.ufileos.com/3bf4dd18-a009-497b-8bed-7a5d76a117c4.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=dOWSJCYL6m%252FljgQuUGxhLp5vvvI%253D&Expires=1605365872)
我们会发现我们的程序运行正常,没有异常。
三、总结
这就是我们一般使用这两种方式构建镜像,但是我们一般使用**Dockerfile
**进行创建,原因如下:(采用网络资料)
使用docker commit命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。
首先,如果在安装软件,编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像及其臃肿。
此外,使用docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制定镜像的人知道执行过什么命令,怎么生成的镜像,别人根本无从得知,而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作的。虽然docker diff或许可以得到一点线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。
而且,任何修改的结果仅仅是在当前层进行标记,添加,修改,而不会改动上一层。如果使用docker commit制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到,这会使镜像更加臃肿。
docker commit有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用docker commmit定制镜像,定制行为应该使用Dockerfile来完成。
祝大家早日熟练使用docker
相关命令~