平台

Dokcer使用DokcerFile定制镜像

2022-07-20  本文已影响0人  羋学僧

Dokcer使用DokcerFile定制镜像

定制docker镜像的方式有两种:

Dockerfile简介

镜像是多层存储,每一层都是在前一层的基础上进行修改;
容器也是多层存储,以镜像为基础层,在其基础上加一层作为容器运行时的存储层。



刚才说了,创建镜像的两个方法:

Dockerfile主要组成部分

基础镜像信息 FROM centos:7.9 
制作镜像操作指令 RUN yum install -y nginx 
容器启动时执行指令 CMD ["/bin/bash"]

宿主机直接部署软件流程与Dockerfile部署软件流程对比

需求 : 安装一个mysql,并启动。

虚拟机部署形式:
  1. 开启vmware

  2. 运行某一个虚拟即,centos7

  3. centos7安装mysql yum install mysql-server

  4. 通过脚本或者命令,启动mysql即可

部署缓慢,且修改了宿主机的环境,删除较为麻烦,占用宿主机的一个3306端口

容器的部署形式:
  1.  开始vmware

  2. 运行虚拟机centos7(宿主机)

  3. 安装docker容器软件

  4. 获取mysql镜像即可,docker pull mysql:tag(你无法自由控制,该mysql的基础镜像时什么发行版本,你获取的镜像,是别人定制好的,你下载使用的默认时Debian发行版,你希望得到一个基于centos7.9的发行版本,运行mysql)

  5. 直接运行该镜像,通过端口映射,运行mysql

  6. 访问宿主机对的一个映射端口,访问到容器内的mysql

想自定义镜像,就得自己写脚本,也就是dockerfile了

Dokcerfile指令

FROM  指定基础镜像

MAINTAINER  指定维护者信息,可以没有

RUN  你想让它干啥(在命令前面加上RUN即可)

ADD  添加宿主机的文件到容器内,还多了一个自动解压的功能
# RUN tar -Zxf /opt/xx.tgz  # 报错!该tgz文件不存在! !

COPY  作用和ADD是一样的,都是拷贝宿主机的文件到容器内, COPY就是仅仅拷贝

WORKDIR  相当于cd命令,设置当前工作目录

VOLUME  设置目录映射,挂载主机目录

EXPOSE  指定对外的端口,在容器内暴露一个端口,端口 EXPORT 80

CMD  指定容器启动后的要干的事情

ENTRYPOINT  作用和CMD一样,都是在指定容器启动程序以及参数。
# 当指定了ENTRYPOINT之后,CMD指令的语义就有了变化,而是把CMD的内容当作参数传递给ENTRYPOINT指令。

ARG  设置环境变量
# ARG只是用于构建镜像需要设置的变量,容器运行时就消失了

ENV   和ARG一样,都是设置环境变量
# 区别在于ENV无论是在镜像构建时,还是容器运行,该变量都可以使用

USER  用于改变环境,用于切换用户

ONBUILD   用于设置镜像触发器

Dokcerfile实践

需求:通过dockerfile,构建nginx镜像,且运行容器后,生成的页面是"Docker mhg"。
1. 创建Dockerfile,注意文件名,必须是这个
mkdir /root/learn_docker

cd /root/learn_docker

vim Dockerfile
FROM nginx
RUN echo "<meta charset=utf-8>Docker mhg" > /usr/share/nginx/html/index.html
2. 构建Dockerfile
docker build .
3. 修改镜像名字
docker images
[root@qzcsbj-harbor learn_docker]# docker tag 531f41ec3c5a my_nginx
4. 运行该镜像
docker run -d -p 9080:80 my_nginx
5. 查看宿主机的9080端口
http://192.168.117.160:9080/

Dokcerfile相关指令用法

COPY

copy指令从宿主机复制文件或者目录到新的一层镜像内
如:
copy nana.py /opt

支持多个文件,以及通配符形式的复制,语法要满足Golang的filepath.Match
copy na* /tmp/cc?.txt /opt

COPY指令能够保留源文件的元数据,访问时间等等,这点很重要

ADD

特性和COPY基本一致,不过多了些功能
1. 源文件是一个URL,此时dockcer引擎会下载该链接,放入目标路径,且权限自动设为600。若这不是期望结果,还得增加一层RUN指令进行调整
# ADD nana.tgz /home
# RUN xxx修改命令
2. 源文件是一个URL,且是一个压缩包,不会自动解压,也得单独用RUN指令解压
3. 源文件是一个压缩文件,且是gzip,bzip,xz,tar情况,ADD指令会自动解压压缩该文件到没有文件

CMD

用法,注意是双引号
# CMD在容器内运行某个命令,启动程序
# 该镜像在运行容器实例的时候,执行的具体参数是什么
CMD["参数1","参数2"]
在指定了entrypoint指令后,用CMD指定具体的参数

dokcer不是虚拟机,容器就是一个进程,既然是进程,那么程序在启动的时候需要指定些运行参数,这就是CMD指令作用

例如centos镜像默认的CMD是/bin/bash,直接docker run -it centos会直接进入bash解释器。
也可以启动容器时候,指定参数: docker run -it centos cat /etc/os-release

CMD ["/bin/bash"]

# 该容器运行时,执行的命令
# 等同于命令行的直接操作:docker run -it centos cat /etc/os-release
CMD ["cat","/etc/os-release"]

容器内运行程序

这里要注意的是,docker不是虚拟机的概念,虚拟机的程序运行,基本上都是在后台运行,利用systemctl运行,但是容器内没有后台进程的概念,必须在前台运行。
容器就是为了主进程而存在的,主进程如果退出了,容器也就失去意义,自动退出。

例如一个经典的问题:
# 这样的写法是错误的,容器会立即退出
CMD systemctl start nginx

因为systemctl start nginx是以守护进程(默认在后台运行)的形式启动nginx,且CMD命令会转化为

CMD ["sh","-c","systemctl start nginx" ]
这样的命令主进程是sh解释器,执行完毕后立即结束了,因此容器也就退出了。

# 相当于nginx -g daemon off
因此正确的做法应该是 CMD ["nginx","-g","daemon off;"]

把宿主机安装,启动nginx的理念放入到dockerfile中

1. RUN yum install nginx
2. RUN 配置文件修改 sed
# RUN systemctl start nginx   容器内的程序必须在前台运行,容器时启动不了的
3. 正确的写法应该时CMD ["nginx","-g","daemon off;"]

ENTRYPOINT

dokcer面试题:
ENTRYPOINT和CMD的区别以及用法! ! !

CMD:指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT:指定这个容器启动的时候要运行的命令,可以追加命令

ENTRYPOINT作用和CMD一样,都是在指定容器启动程序以及参数。

当指定了ENTRYPOINT之后,CMD指令的语义就有了变化,而是把CMD的内容当作参数传递给ENTRYPOINT指令。

ENTRYPOINT和CMD的实际用法

实际用法:
1. 准备一个Dokcerfile
[root@qzcsbj-harbor ~]# cd /learn_docker/
[root@qzcsbj-harbor learn_docker]# > Dockerfile 
[root@qzcsbj-harbor learn_docker]# vim Dockerfile 
FROM centos:7.8.2003
RUN rpm --rebuilddb && yum install epel-release -y
RUN rpm --rebuilddb && yum install curl -y
CMD ["curl","-s","ip.sb"]

# 用法如下
dokcer run my_centos curl -s ip.sb      # curl -s ip.sb获取本机的公网ip地址

2. 构建镜像
[root@qzcsbj-harbor learn_docker]# docker build .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM centos:7.8.2003
 ---> afb6fca791e0
Step 2/4 : RUN rpm --rebuilddb && yum install epel-release -y
 ---> Using cache
 ---> 81b4e83fb0a5
Step 3/4 : RUN rpm --rebuilddb && yum install curl -y
 ---> Using cache
 ---> bd0074c78b6c
Step 4/4 : CMD ["curl","-s","ip.sb"]
 ---> Running in 295418f71093
Removing intermediate container 295418f71093
 ---> c920b743282a
Successfully built c920b743282a

3. 查看结果(出现Successfully代表镜像构建完成)
Step 4/4 : CMD ["curl","-s","ip.sb"]
 ---> Running in 295418f71093
Removing intermediate container 295418f71093
 ---> c920b743282a
Successfully built c920b743282a

4. 检查镜像
[root@qzcsbj-harbor learn_docker]# docker tag c920b743282a centos_curl 
[root@qzcsbj-harbor learn_docker]# docker images | grep curl
centos_curl   latest     c920b743282a   3 minutes ago    471MB

5. 运行镜像,生成容器记录,没有前台运行,因此立即挂了
[root@qzcsbj-harbor learn_docker]# docker run centos_curl
139.227.102.189

6. 上述运行正确,但是我想再传入一个参数,该怎么办
# 发现是无法直接传入参数的,该形式是覆盖镜像中的cmd
# 就好比把docker镜像,当作一个环境,去执行后面的命令
[root@qzcsbj-harbor learn_docker]# docker run centos_curl pwd
/
[root@qzcsbj-harbor learn_docker]# 
[root@qzcsbj-harbor learn_docker]# docker run centos_curl -I
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-I": executable file not found in $PATH: unknown.

7. 想要正确的给容器传入一个参数该怎么办
希望容器内能够正确完整的运作该命令的执行结果
[root@qzcsbj-harbor learn_docker]# curl -s ip.sb -I         # 获取http报头信息
HTTP/1.1 200 OK
Date: Wed, 28 Jul 2021 15:16:18 GMT
...

8. 解决办法
方式一:给容器传入新的完整的命令,让后面的命令覆盖镜像中的cmd
# 这是投机取巧的办法,不合适
[root@qzcsbj-harbor learn_docker]# docker run centos_curl curl -s ip.sb -I
HTTP/1.1 200 OK
Date: Wed, 28 Jul 2021 15:18:05 GMT
Content-Type: text/plain

9. 正确的解决办法
[root@qzcsbj-harbor learn_docker]# vim Dockerfile 
FROM centos:7.8.2003
RUN rpm --rebuilddb && yum install epel-release -y
RUN rpm --rebuilddb && yum install curl -y
ENTRYPOINT ["curl","-s","ip.sb"]

10. 重新构建镜像
# 重新构建镜像速度特别快,并且我们发现镜像的前三个Step的IMAGE ID是一致的,说明前三个的IMAGE ID是直接从缓存中拿的。
# 只有Step 4/4的IMAGE ID发生了变化(Dockerfile文件的第四步是更改过的,是重新构建的镜像层),因此更加验证了我们之前所提到的镜像是分层构建的。
[root@qzcsbj-harbor learn_docker]# docker build .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM centos:7.8.2003
 ---> afb6fca791e0
Step 2/4 : RUN rpm --rebuilddb && yum install epel-release -y
 ---> Using cache
 ---> 81b4e83fb0a5
Step 3/4 : RUN rpm --rebuilddb && yum install curl -y
 ---> Using cache
 ---> bd0074c78b6c
Step 4/4 : ENTRYPOINT ["curl","-s","ip.sb"]
 ---> Running in df106e04d533
Removing intermediate container df106e04d533
 ---> e9479067148c
Successfully built e9479067148c

11. 重新运行该镜像,看结果,以及传入新的参数
[root@qzcsbj-harbor learn_docker]# docker tag e9479067148c centos_curl_new

# 此时发现,传入的CMD指令,当作了ENTRYPOINT的参数
# 其实容器内,执行的完命令是: curl -s ip.sb -I
[root@qzcsbj-harbor learn_docker]# docker run centos_curl_new -I
HTTP/1.1 200 OK
Date: Wed, 28 Jul 2021 15:24:58 GMT
...

ARG和ENV指令

设置环境变量

dockerfile脚本,shell脚本

ENV NAME="nana"
ENV AGE=18
ENV MYSQL_VERSION=5.6

后续所有的操作,通过$NAMME就可以直接获取变量值使用了,维护dockerfile更加方便

ARG和ENV一样,都是设置环境变量
ENV无论是在镜像构建时,还是容器运行,该变量都可以使用
ARG只是用于构建镜像需要设置的变量,容器运行时就消失了

VOLUME

容器在运行时,应该保证在存储层不写入任何数据,运行在容器内产生的数据,我们推荐是挂载,写入到宿主机上,进行维护。

# mount /mnt 

VOLUME /data  
# 将容器内的/data文件夹,在容器运行时,该目录自动挂载为匿名卷,任何向该目录中写入数据的操作,都不会被容器记录,保证的容器存储无状态理念。

# Dockerfile
[root@qzcsbj-harbor ~]# cd /learn_docker/
[root@qzcsbj-harbor learn_docker]# > Dockerfile 
[root@qzcsbj-harbor learn_docker]# vim Dockerfile 
FROM centos
MAINTAINER nana
VOLUME ["/data1","/data2"]

# 该容器运行的时候,这两个目录自动和宿主机的目录做好映射关系
docker build .

# 运行该镜像
docker run 86b4dceba89a
# 查看生成的容器信息
[root@qzcsbj-harbor nana]# docker ps -a | head -2
CONTAINER ID   IMAGE             COMMAND                  CREATED         STATUS                     PORTS     NAMES
84014622b3a4   86b4dceba89a      "/bin/bash"              2 minutes ago   Exited (0) 2 minutes ago             sharp_noether

# dokcer inspect命令查看
[root@qzcsbj-harbor learn_docker]# docker inspect 86b4dceba89a
            "Volumes": {
                "/data1": {},
                "/data2": {}
            },

1. 容器数据挂载的方式,通过dockerfile,指定VOLUME目录
2. 通过docker run -v参数,直接设置需要映射挂载的目录

EXPOSE

指定容器运行时对外提供的端口服务。

帮助使用该镜像的人,快速理解该容器的一个端口业务
docker port 容器
dokcer run -p 宿主机端口:容器端口
docker run -P # 作用是随机 宿主机端口:容器内端口

WORKDIR

用于在dockerfile中,目录的切换,更改工作目录

WORKDIR /opt

USER

用于改变环境,用于切换用户

USER root
USER nana

ONBUILD

用于设置镜像触发器,从当前镜像创建子镜像,会触发ONBUILD的执行

注意:ONBUILD指令不会继承

# 创建一个Dockerfile,写入触发器
[root@qzcsbj-harbor ~]# mdkir /docker
[root@qzcsbj-harbor ~]# vim /docker/Dockerfile 
FROM centos:7
RUN yum -y install curl
ENTRYPOINT ["curl","-s","https://www.cip.cc/"]
ONBUILD RUN echo "==images onbuild=="

# 构建镜像(触发器并没有执行)
[root@qzcsbj-harbor ~]# cd /docker/
[root@qzcsbj-harbor docker]# docker build . -t "nana_ip"
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM centos:7
7: Pulling from library/centos
2d473b07cdd5: Pull complete 
Digest: sha256:0f4ec88e21daf75124b8a9e5ca03c37a5e937e0e108a255d890492430789b60e
Status: Downloaded newer image for centos:7
 ---> 8652b9f0cb4c
...

我们基于构建完成的镜像(nana_ip),创建一个新的Dockerfile
[root@qzcsbj-harbor docker]# cd
[root@qzcsbj-harbor ~]# vim Dockerfile
FROM nana_ip
RUN yum -y install curl
ENTRYPOINT ["curl","-s","https://www.cip.cc/"]

# 构建子镜像,触发器执行成功
[root@qzcsbj-harbor ~]# docker build . -t "nana_ip02"
Sending build context to Docker daemon  17.92kB
Step 1/3 : FROM nana_ip
# Executing 1 build trigger
 ---> Running in 70833b9adb69
==images onbuild==
Removing intermediate container 70833b9adb69
 ---> fce1318ec153
...
上一篇 下一篇

猜你喜欢

热点阅读