【Docker-k8s学习和实战】(八)dockerfile理论知识---dockerfile基础以及dockerfile指令

❤️ 专栏简介 :本专栏我们会从最基础的内容开始学习Docker的相关内容,循序渐进的掌握Docker知识并进行实战。

☀️ 专栏适用人群 :适用于具备基础 Linux 知识的 Docker 初学者,当然希望各位有经验的docker开发者不吝赐教。

🌙专栏特点:通俗易懂、图文并茂、非常详细;

🌴 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。

🍄 专栏地址:https://blog.csdn.net/anchenliang_1002/category_11878561.html

在这里插入图片描述

本节主要介绍了dockerfile理论知识;主要包括dockerfile基础镜像基础dockerfile的组成如何制作dockerfieldockerfile指令详解等。

一、dockerfile和镜像基础

1.1 什么是dockerfile?dockerfile是干什么用的?

dockerfile用于构建docker镜像,可以用来部署一个用于我们所需的容器环境;dockerfile相当于一个脚本,通过dockerfile我们自己的指令,来构建软件依赖、文件依赖、存储等等。

1.2 如何创建镜像

通过前面的学习我们知道了镜像是多层存储,每一层在前一层的基础上进行修改;相应的,在dockerfile中,每运行一个指令,就会生成一层docker镜像层。

容器是基于镜像运行出来的,所以容器也是多层存储,以镜像为基础层(可读不可修改),在其基础上加一层(容器层)作为容器运行时的存储层,在容器中的所有修改都是在容器层进行的。

创建镜像由两种方法:

方法1:手动修改容器内的内容,然后docker commit 提交容器为新的镜像。
方法2:在dockerfile中定义一系列的命令和参数,最终构成一个dockerfile脚本;通过运行该脚本中的命令,构建出基础镜像并依次添加各个层(dockerfile中的命令是逐行执行的,每个指令生成一层);最终生成一个新的镜像。

二、学习制作dockerfile

2.1 dockerfile主要组成部分

  • FROM :基础镜像信息
    FROM的作用是指定基础镜像,后面跟着镜像的名字,每个dockerfile的第一行都是FROM;
    比如FROM centos:7.6

  • RUN
    有了基础镜像后,我们要基于这个基础镜像做什么事情呢?比如我们要安装mysql;
    RUN指令是个万能指令,即在容器中要进行的操作;比如安装mysql如下:RUN yum install mysql

  • CMD
    那么现在我们有了基础镜像,又安装了具体的应用(mysql),最后一步就是在容器内将mysql运行起来;之前我们有讲过,容器内必须有进程在运行,容器才不会被杀死;如果容器内没有进程在运行,容器就被kill掉了。所以我们需要在容器中将进程跑起来。CMD就可以指定容器启动时执行的指令,比如CMD["/bin/bash"]就是指定在容器启动时执行bash指令;最终的效果是,我们通过镜像生成了该容器,并docker run该容器后,bash就会启动了。

2.2 docker常用指令

FROM:指定基础镜像
通俗的解释:FROM用来指定咱们要制作的镜像的妈妈是谁,即该镜像基于什么系统,可以是ubuntu、centos、debian等等。

MAINTAINER:指定维护者信息(可以没有)
通俗解释:即告诉别人,这个镜像的作者是谁,谁负责维护它

RUN:该镜像要执行什么命令,RUN后面一般加Linux命令
通俗解释:你想让镜像干啥,比如安装vim
RUN yum install vim

ADD:添加宿主机的文件到容器内,如果文件是压缩文件,则会自动解压

COPY:作用和ADD指令一样,都是拷贝宿主机的文件到容器内;但与ADD相比,COPY只是拷贝,不会解压。

WORKDIR:目录切换,可以理解为linux下的cd指令;一般用于设置当前工作目录

VOLUME:设置存储卷,用于数据映射;
通俗解释:就是将容器内的某个目录B挂载到宿主机的目录A下,这样我们宿主机访问目录A就可以访问到容器目录 B了。

EXPOSE:指定对外的端口

通俗即使:就是指定该容器要打开的门;即在容器内暴露一个端口;比如EXPORT 80,即开放容器的80端口。

CMD:告诉容器,启动后要执行什么操作

2.3 dockerfile简单的实战例子

我们的目的:
通过dockerfile构建出一个自己的nginx镜像;且运行该镜像生成容器后,生成我们自己设计的页面。

具体的过程如下:

第一步:创建Dockerfile,注意,名字必须是Dockerfile

mkdir learn_docker
cd learn_docker/
vim Dockerfile

然后将以下内容复制进去:

FROM nginx
RUN echo '<meta charset=utf8>我们自己的的nginx容器测试用例.'  > /usr/share/nginx/html/index.html

如下所示:

在这里插入图片描述

保存退出,此时Dockerfile就写好了。

第二步:构建Dockerfile

命令为:docker build . ,其中.代表当前目录

过程如下:
在这里插入图片描述
此时我们docker images看一下

在这里插入图片描述
可以看到确实已经产生了我们刚刚的镜像。

第三步:修改镜像名称

但是该镜像没有名字和tag标签,现在我们给他改一下:

指令是:

docker tag 1fee05add27c my_nginx

在这里插入图片描述
可以看到名字已经改为了my_nginx;

第四步:运行镜像

docker run -d -p 80:80 my_nginx

运行该镜像,如果该指令运行成功,会返回容器的id。

在这里插入图片描述

第五步:查看宿主机80端口

在浏览器访问我们宿主机的80端口:
在这里插入图片描述
可以看到,成功访问到了刚刚我们通过Dockerfile创建的容器。

2.4 Dockerfile指令语法详解

COPY

copy指令从宿主机复制文件或目录到新的一层容器镜像内。

copy test.py /home

将宿主机上的test.py文件复制到容器内的/home下。

也支持多个文件、以及通配符形式复制,语法要满足Golang的filepath.Match

copy test* /tmp/cc?.txt. /home

将test开头的文件以及tmp目录下cc相关.txt文件全都拷贝到容器内的/home目录下。

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

ADD

特性和COPY基本一致,不过多了些功能:
1、源文件是一个URL,此时docker引擎会下载该链接,放到目标路径,且权限自动设为600,若这不是期望结果,还得增加一层RUN指令进行调整。
2、源文件是一个URL,且是一个压缩包,不会自动解压,也得单独用RUN指令解压到指定文件夹。
3、源文件是一个压缩文件,且是gzip,bzip2,bz,tar情况,ADD指令会自动解压。

Dockerfile官方更推荐使用COPY,ADD包含了更复杂的功能,且ADD会使构建缓存失效,导致镜像构建缓慢。

CMD

CMD用于指定该镜像在运行容器实例的时候,执行的具体参数是什么。即该容器运行时,执行的命令,等同于命令行的直接操作。
用法:注意是双引号

CMD ["参数2","参数2"]

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

例如centos镜像默认的CMD是/bin/bash,即centos默认的CMD是:CMD["/bin/bash"],所以直接docker run -it centos会直接进入bash解释器。

也可以启动容器的时候指定参数:docker run -it centos cat /etc/os-releasea

CMD运行shell命令,也会转化成shell形式
例如:CMD echo $PATH 会被转化为CMD ["sh","-c","echo $PATH"]

容器内运行程序

这里要注意的是,docker不是虚拟机的概念,虚拟机里的程序运行,基本上都是在后台运行,利用systemctl 运行,但是容器内没有后台进程的概念,必须在前台运行。

容器就是为了主进程而存在的,主进程如果退出了,容器也就失去意义,自动退出。

例如有一个经典问题:

在这里插入图片描述
第九行nginx -g daemon off的意思是让nginx在前台运行,即占用一个住终端。

ENTRYPOINT

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

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

示例过程如下:

第一步:准备一个Dockerfile,内容如下:

FROM centos:7.8.2003
RUN rpm --rebuilddb && yum install epel-release -y
RUN rpm --rebuilddb && yum install curl -y
CMD ["curl","-s","http://ipinfo.io/ip"]

CMD ["curl","-s","http://ipinfo.io/ip"]指令相当于在运行出容器的时候运行curl -s http://ipinfo.io/ip指令,即docker run my_centos curl -s http://ipinfo.io/ip

在这里插入图片描述

第二步:构建镜像

docker build .

第三步:查看结果
执行完成后如下:
在这里插入图片描述
成功后会显示successfully,且返回了一个镜像id。

第四步:检查镜像
docker images查看镜像,然后docker tag对该镜像改名字:

在这里插入图片描述

第五步:运行镜像生成容器

docker run centos_curl

运行结果如下,即打印了一行我们个人的ip,运行完之后进程就结束了,容器也就随之结束了。
在这里插入图片描述

curl -s http://ipinfo.io/ip指令就是获取我们个人宿主机的ip信息,我们在宿主机上执行一下,结果如下:

在这里插入图片描述
可以看到也是获取了我们自己的ip,此时我们给这个指令价格-I参数:

在这里插入图片描述
可以看到获取了响应结果;

第六步:上述运行正确,但是我们想再传入一个参数,该怎么办?
比如我们想再运行容器的时候传入一个-I的参数,用于显示出如上图所示的相应结果,该如何呢?试一下直接传入:

[root@localhost learn_docker]# docker run centos_curl -I
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "-I": executable file not found in $PATH: unknown.

发现是无法直接传入参数的,该形式是覆盖镜像中的CMD;就好比把该docker镜像,当做一个环境,去执行后面的命令。

第七步:正确给容器传入一个-I参数
希望容器内能正确完成curl -s http://ipinfo.io/ip -I这个命令的执行。

第八步:解决办法1
给容器传入新的完整的命令:

docker run centos_curl curl -s http://ipinfo.io/ip -I

如下所示:
在这里插入图片描述

可以看到跟我们直接在宿主机执行的指令结果是一样的。

但是此方法是个投机取巧的办法,不合适,不方便,每次都需要输入完整的指令,没什么意义。

第九步:正确的方法应该是使用ENTRYPOINT
修改Dockerfile如下:

将原来的CMD改为ENTRYPOINT,
在这里插入图片描述

第十步:重新构建镜像

docker build .

在这里插入图片描述

改名字:

docker tag 9779eb3aaec6 centos_curl_new

第十一步:重新运行该镜像,查看结果,并传入新的参数
我们先直接运行该镜像,

在这里插入图片描述

可以发现跟之前的运行结果是一样的;

然后我们再传入-I参数试试:

在这里插入图片描述
可以发现,这次没有报错了。之前在第六步中(Dockerfile使用CMD)是执行失败的;此次我们使用ENTRYPOINT生成的镜像则可以直接传入参数了。

所以当使用了ENTRYPOINT后,传入的CMD指令当做了ENTRYPOINT的参数;即传入的-I参数,成功的传给了ENTRYPOINT后的指令;ENTRYPOINT原来的指令是curl -s http://ipinfo.io/ip,当我们运行容器时加入-I后,执行的完整指令就是curl -s http://ipinfo.io/ip -I 了。

ARG和ENV指令

这两个指令的作用都是设置环境变量。

比如咱们有多个容器,容器1是nginx,容器2是php-fpm,容器3是mysql;

ENV指令使用
例如:
设置环境变量:NAME、AGE、MYSQL_VERSION

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

使用环境变量:
后续所有的操作,就可以通过$NAME,就可以直接获取NAME的变量值"ACL"和AGE值"18"了;通过环境变量,维护dockerfile脚本时更加友好,方便。

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

VOLUME

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

语法如下:

VOLUME /data 

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

在Dockerfile中的应用:

#Dockerfile1
FROM centos:7.8.2003
MAINTAINER ACL//指定容器名字
VOLUME ["data1","data2"]

当容器运行时,data1和data2两个目录自动和宿主机的目录做好了映射关系;来,我们来实际操作一下:

将Dockerfile的内容改为上面的内容,如下所示:
在这里插入图片描述

然后docker build .构建容器;docker images查看容器;然后docker run运行该容器
在这里插入图片描述
执行完之后很自然的退出了,因为该容器什么事儿也没做;但是我们可以看到该容器的执行记录:

docker ps -a

在这里插入图片描述
我们可以通过docker inspect 7edad0e0555f指令查看容器信息;从里面我们可以找到容器挂载相关信息,如下图:

在这里插入图片描述
我们可以看到,容器内的data1目录,挂载到了宿主机的/var/lib/docker/volumes/e2e6a8a9301663a59423763a999da4400d0997d5179471441577747cfd771dbf/_data目录;容器内的data2目录,挂载到了宿主机的/var/lib/docker/volumes/52358a212e872b14854d957e5889baf8aefb47a93deb2bc741bfaa723ed48a59/_data目录。

题外话:容器内实现挂载的方法
1、通过在Dockerfile中设置VOLUME可以实现容器数据挂载;
2、也可以通过docker run -v参数,直接设置需要映射挂载的目录

EXPOSE

该指令用于指定容器运行时对外提供的端口服务;

  • 帮助使用该镜像的人,快速理解该容器的所使用的端口业务。
#涉及到端口的命令:
docker port 容器
docker run -p 宿主机端口:容器端口
docker run -P #随机生成宿主机端口:容器端口

WORKDIR

用于在Dockerfile中,目录的切换;更改工作目录。相当于linux中的cd
示例:

WORKDIR /opt

切换到/opt工作目录。

USER

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

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乘凉~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值