第4节 Dockerfile常见指令用法
ARG,变量
类似ENV,不过ENV是build阶段定义的变量,docker run阶段也同样可以使用
ARG只能在build阶段生效。
使用场景:
1、ENV就是build阶段定义的变量赋值,然后docker run阶段可以使用-e修改掉
2、ARG就是build阶段定义用的,docker run起来就没有这些变量了。
3、ARG可以写到FROM前面
ARG常用来定义版本号👇
ARG VERSION=3.20.0
FROM alpine-base:$VERSION
LABEL maintainer="oneyearice <oneyearice@gmail.com>"
ARG NGINX_VERSION=1.26.0
ADD nginx-$NGINX_VERSION.tar.gz /usr/local/src/
RUN cd /usr/local/src/nginx-$NGINX_VERSION && ./configure --prefix=/apps/nginx && make && make install && ln -s /apps/nginx/sbin/nginx /usr/bin && addgroup -g 2024 -S nginx && adduser -s /sbin/nologin -S -D -u 2024 -G nginx nginx && mkdir /apps/nginx/conf/conf.d
COPY nginx.conf /apps/nginx/conf/nginx.conf
ADD index.html /apps/nginx/html/index.html
RUN chown -R nginx.nginx /apps/nginx/
#EXPOSE 80 443
#CMD nginx && tail -f /apps/nginx/logs/*
CMD ["nginx","-g","daemon off;"]
VOLUME,持久化
volume的目录可以自动创建的,无需事先创建
相当于同时:①实现数据持久化②也顺便创建了目录
build成功就说明VOLUME创建了目录了,因为entrypoint.sh里有一句echo $HOST > ${DOC_ROOT}index.html没报错,说明目录OK的。
目录exec进去可见
而且整个VOLUME创建的目录是持久化的,所以是独立于容器的目录的
这些LowerDir、MergerDir、UppereDir、WorkDir目录,容器删了,这些目录就没了
但是volume是独立的文件持久化的
测试
而且整个映射出来的宿主上的文件内容改了,容器里也同步的
容器里的所有文件都是在宿主机上存放的其实~
mysql的数据就很需要volume持久化一下
Dockerfile里的VOLUME遗憾
但是宿主机上的目录名称没有任何识别度,就是自动生成的这些volume在宿主上的目录是无法维护的,你都不知道谁是谁👇
容器如果删了,还可以docker inspect 查看谁是谁的谁,容器删了可就没法查了,只能编列cat里面的内容去一个个找了。
还有一个命令可以看volume的
总之Dockerfile里的VOLUME就是没法指定宿主的映射目录的,只能这样,也算是一个缺点。怎么解决,docker run -v可以做宿主和容器里的目录映射,docker compose的yml也可以实现。
也可以使用VOLUME挂载多个容器里的目录出来
EXPOSE仅仅是一个申明
申明的是-P要映射的端口是啥而已。至于是不是容器里监听的,也不一定,没有直接关系。
1、暴露端口的时候,可以用docker run -p 8080:80来 将容器里的80暴露到宿主的8080。
2、也可以用Dockerfile里的EXPOSE来说明容器里暴露的是80或443,但是宿主用哪个端口来对接就是在docker run -P来随机指定了,如果不像随机,那么还是用-p再次指定,此时Dockerfile里的EXPOSE就没意义了。
3、EXPOSE和VOLUME一个德行,都是仅仅申明容器里要暴露的端口和目录。至于暴露成宿主的哪个端口和路径,Dockerfile里没有解决,都是通过docker run -p和-v来解决,然而既然用了-p和-v,那么容器里的端口和目录都是直接写了,无需Dockerfile重复写了。 # 但是EXPOSE和VOLUME的区别是EXPOSE不管容器里是否监听某个端口,只是申明供-P暴露出来;而VOLME是容器里没有这个目录就给你自动创建出来(而且是mkdir -p 的方式来创建多层目录的),然后暴露成宿主的目录。
4、然后EXPOSE就是配合docker -P,大P来使用的,告诉大P暴露容器里的什么端口为宿主的随机端口。
5、凡是EXPOSE写的端口不是容器里监听的端口的,都是坏人~,哪家好人干这种事啊,对不对,不写也是不对的,总不能让别人去猜你nginx或其他app监听的啥吧,不猜就去找脚本,或者你要是默认编译的基本就是80了。
以下截图就看EXPOSE就行了,其他前面的内容
443只是暴露出来,并不代表容器里有监听443,而实际情况是容器里只监听了80,443没有开,暴露是80和443都暴露的,所以👇
WORKDIR,切换工作目录
类似cd,切换文件夹的,确实切掉了,
但是如果在Dockerfile里 RUN cd xxx/xx/xx 也仅仅是在这一条RUN里切换了路径,下一行又回到容器里的/根了。实验测试下👇
1、原材料
2、build
2、run看效果👇
所以RUN cd和WORKDIR是不一样的,前置式只在RUN cd的哪一行临时切过去,后面又回到了默认的/路径,而WORKDIR就是后面都切过去了。
至于RUN一行里用&& 还是; 都行,一行都是临时切过去了。
ONBUILD,
父镜像build指定的CMD在当时不执行,在子镜像build的时候才执行。
举例:rm -rf /,就是说父镜像ONBUILD制定了rm -rf /,一旦子镜像继承了就把子镜像的根删掉。 # 你要继承我,我就把你删了。。。 这里只是一个没人这个干的例子,呵呵。 也许你不想让别人用的镜像作为父镜像,就可以这么做,ONBUILD RUN rm -rf /* 呵呵。
那么问题来了:
WORKDIR /bin
ONBUILD touch helo.log
这个touch在子镜像build的时候创建的,那么是在子镜像的/下还是下/bin下呢?是子镜像的/bin下的,
因为子镜像FROM xxx的时候已经继承了WORKDIR的默认工作目录已经是/bin了,然后ONBUILD又是子镜像才执行显然是在FROM以后的。
1、首先父镜像里是没有touch helo.log文件的👇
顺便看一个视频中的典型错误案例👇
图中错误就是,entrypoint.sh不会被执行,因为找不到路径,
理由很简单:你看下图是不是一样
解释给你听哦:①你要让光秃秃的xxx.sh,这种不带绝对/相对路径的脚本直接执行,那就要让xxx.sh放到$PATH变量里的路径里去;②结果你上面COPY是放到/根③你WORDIR /切到/根,人家也不会去/当前目录根里找这个xxx.sh的。哈哈,不要学到了K8S还在这里闹小学一年级的笑话~还说什么WORKDIR没切过去,这是WORKDIR的事嘛~,基础不牢地动山摇,人心不稳王座倾塌。
下面就是Dockerfile制作一个父镜像,然后再Dockerfile.son做一个子镜像,其中父镜像里的是ONBUILD touch,哦错了,是ONBUILD RUN touch才对👇
由于父镜像里已经WORKDIR /bin了,所以父镜像docker run起来进去就是/bin下的;
同样子镜像是继承的父镜像,所以子镜像的工作目录就是/bin的,所以再执行ONBUILD RUN touch helo.log就生成了文件了👇
判断下ONBUILD的 echo的动作是在WORKDIR前还是后
判读是在后,因为WORKDIR是父镜像build的时候就会执行,一旦执行,子镜像继承了就是工作目录默认就是/bin了,然后ONBUILD echo是CMD还是ENTRYPOINT啊,我的意思是在build阶段还是在docker run阶段生效啊? 就算ONBUILD是build生效,那也是子镜像执行,而子镜像FROM xxx继承父镜像这个FROM就是父镜像的WORKDIR已经完成了,所以ONBUILD是肯定是在WORKDIR的目录里的了。
然后ONBUILD RUN或ONBUILD ENV、ONBUILD ARG、ONBUILD ENTRYPOINT
再看
buid一下,注意build的时候,镜像不要用之前的镜像名称,会有问题,待会单独研究。
验证结论的时候到了,
验证如下👇
看案例,就是父镜像没再次build导致子镜像还是用的之前的父镜像的Dockerfile👇,总之看CACHE比较明朗就:
然后为什么会有之前的影响,继续研究
还是还原故障
还原个屁,是自己搞错了,父镜像没有build覆盖原来的tag导致的
这里图中打叉的地方-t写错了,本来是覆盖nginx_env_web:1.5这个父镜像的,结果写成了_son了,所以原来的父镜像没变,也就是说ONBUILD echo '123321' > helo.log本来就在,所以子镜像继承了,人家CACHE既然看到了就说明利用了之前的分层了,就说明父镜像没有变了。
USER,指定后续指令的执行者
HEALTHCHECK,健康检查
注意👆上图IP:=哪里写错了,PORT也写错了,复习变量高级用法去,nnd。
starting就是正在检查中
过一会就发现检查失败了
失败的原因是HEALTHCHECK里的CMD里的变量高级用法写错了,
修改后OK👇
上图👆就是默认的时间是30s timeout,30s检查ok了就判定为healthy。如果30s检查不OK,就retries 3次也就是90s才会判定unhealthy。
然后研究下curl 和 wget的静默用法👇
FROM nginx
# 添加健康检查脚本
COPY healthcheck.sh /usr/bin/healthcheck.sh
RUN chmod +x /usr/bin/healthcheck.sh
# 定义健康检查命令
HEALTHCHECK --interval=30s --timeout=3s \
CMD /usr/bin/healthcheck.sh # 这里为啥不能直接写curl和wget,肯定可以的啊,脚本结果还导致少了一个人家健康检查无法执行的返回值2
if curl -I http://localhost:80/health >/dev/null 2>&1; then
exit 0
else
exit 1
fi
#!/bin/sh
if wget -q --spider http://localhost:80/health; then
exit 0
else
exit 1
fi
所以这样写就行了
#HEALTHCHECK --start-period=3s CMD wget -q --spider http://${IP:-0.0.0.0}:${PORT:-80}/
HEALTHCHECK --start-period=3s CMD curl -I http://${IP:-0.0.0.0}:${PORT:-80}/ &> /dev/null
#这样做的好处,就是docker logs里就没有多余的curl wget信息了,不过这些系也是timeout到了才会出现,比如默认30s就是要到最后一秒才会出现curl或wget日志,不太能理解,因为--start-perod=3s默认就是3秒就执行了啊,我以为日志就容器起来3s就curl就会有日志了,结果docker logs查看发现要等timeout30秒到了才会有日志出来。
HEALTHCHECK 后面的CMD只是HEALTHCHECK里的一个固定参数,不要理解成Dockerfile 的CMD哦
如果健康检查失败还需要进一步去处理故障的,GPT给了一些思路,不过有待后面学到docker-compose的时候验证:
工作案例-抓某个软件的包
1、背景
公司打不开discord的客户端软件
软件还不是浏览器,无法通过F12直接看到那些URL打不开
2、需求
需要定位某个软件访问了那些域名,然后进一步写hosts,然后放行
3、classwire只能看到已经正常运行了的软件,对于打不开得,就是比如discord还在update,以及update好了还在starting的状态下是看不到 discord这个app的流量统计的。至少我的免费版看不到
4、落地方案,如下
参考两个链接
一个cmd里findstr如何写or 👇
https://blog.csdn.net/zhigang0529/article/details/86240577
一个是如何抓包抓软件👇
https://blog.csdn.net/weixin_51309915/article/details/122382555
方法1:有白名单限制的情况下进行
1、打开wireshark软件开始抓包
2、打开软件discord,让其update转圈圈,后面update好了starting一样的处理思路
这个已经没法复现了,我已经解决了,所以仅文字描述了
3、打开任务管理器
找到pid,
4、打开cmd
手动不断回车,配合discord update界面出现的时间,下图findstr 后面空格是或的关系,表示同时抓这些PID。
已经ESTABLISHED基本是OK的,不用管
找到SYN SENT
5、wireshark过滤该ip
提取第一个该ip出现的时间
然后取消过滤,再全部信息里,找到时间点的这条抓包,往上看到DNS解析对应的域名👇
这就知道了该软件目前需要访问的域名了,重复多次,找到需要访问的所有域名
然后①域名解析本地dns固定成一个IP②路由指向vpn③vpn节点放行该ip。这就是海外白名单管理的思路。
以上就是app如何抓包的思路,要抓到域名嘛
除此之外,还有一个方法
方法2:取消白名单限制去做统计
★放开本地的海外白名单,针对自己的IP,然后打开GlassWire软件,此时由于海外全通,所以discord可以打开,包括update,start都行, 这样等软件正常运行了,GlassWire就可以正常显示该软件的域名访问了。
由于已经升级和能够打开了,所以还需要手动升级来让classwire看到流量
操作①需要退出软件②打开③手动升级
发现不全少了一个dl.discordapp.net,这个域名。可见上面的手动方法还是要掌握的。至少可以先用方法2,然后再用方法1查漏补缺。
说明
这种情况就是PID3118一开始不断和130.221.15.150去TCP CONN,但是dns解析写道104.18.52.172后,软件方面也延迟了一小会然后IP解析更正过来了以后,就可以了。
上图同样是通过wireshark过滤IP,找到时间,然后在全部信息里找到时间,看上面的A记录里的域名。
一开始是SYN还是和130.211.15.150建立,后面我解析固定后,discord这个APP的连接请求也就自动更正了
然后通过上面的实验发现
1、wireshark抓了很多很多很的包后,截图软件存在差距
2、微信截图依然顺序好用,但是PixPin截图异迟钝,慢的很咯,关闭wireshark后PinxPin截图反映速度恢复正常。