实战 Dockerfile
前言
博主语录:一文精讲一个知识点,多了你记不住,一句废话都没有
经典语录:别在生活里找你想要的,要去感受生活里发生的东西
Dockerfile 基础知识已经在上一篇做了详细介绍,如果还不是很清楚的同学可以点击传送门再复习一遍。
传送门:【云原生 | Docker篇】深入Dockerfile_Lansonli的博客-CSDN博客
以下是实战经典十例,反复练习,可玩转 Dockerfile
运行实例命令
# 修改dockerfile文件
vim Docderfile
# 构建容器看执行过程
docker build --no-cache -t demo:test -f Dockerile .
#传入构建参数
docker build --no-cache --build-arg param="11 22 33" msg="aa bb cc" -t demo:test -f Dockerfile2 .
#进入容器控制台
docker exec -it mydemo1 /bin/sh
第一例、这是我第一个 Dockerfile
# 这是我第一个 Dockerfile
FROM alpine
# 给镜像加个标签
LABEL maintainer="lanson @ dd" \
abc=def \
aaa=bbb cccc=ddd
# 运行的指令,安装了软件,修改了文件,默认是用 id=0 也就是 root,这个基础系统的 root 用户
# 代表镜像构建过程中运行的命令。
RUN echo 11111
# 镜像启动如果要运行很长命令才行,容器启动执行的命令
## 1、准备一个 sh 文件,让镜像启动运行 sh 文件(大多镜像操作)
## 2、直接在 CMD 的位置写即可
CMD sleep 10;echo success
第二例、ARG 指令和 ENV 指令简单使用
# 不可以引用多个
FROM alpine
LABEL maintainer="llanson @ dd" \
abc=def \
aaa=bbb cccc=ddd
#指定构建参数【构建时】
ARG aaa=aaaa
#指定环境变量【为 RUN 以及 CMD 指定环境变量的】
ENV parm=11111
# shell* 形式; bash -c "echo 11111"
RUN echo $parm
# exec 形式。$parm 默认拿不到 ENV
RUN ["echo","$aaa"]
# 错误语法 RUN ["echo",'$parm']
# 错误语法 RUN ["echo",$parm]
# 错误语法。NOT FOUND(取不出环境变量【ENV】,ARG 也是取不出)
#RUN ["echo",'${aaa}']
#RUN ["echo",${parm}]
#都是可以启动容器的命令有什么不同
#CMD sleep 1;echo $parm;echo $aaa;
# 都是可以启动容器的命令有什么不同
ENTRYPOINT sleep 1;echo $parm;
第三例、ARG 指令可任意位置定义
#可以在任意位置定义,并在以后取值使用,
#使用--build-arg version=3.13 改变;以我们传入的为准
ARG version=3.13.4
# 3.13
FROM alpine:$version
LABEL maintainer="lanson" a=b \
c=dd
#构建期+运行期都可以生效;但是只能在运行期进行修改
#怎么修改:构建期修改和运行期修改
#构建期不能改 ENV 的值
#运行期:docker run -e app=atguigu 就可以修改
ENV app=itdachang
##测试构建期间生效
RUN echo $app
RUN echo $param
# 定义以后的剩下环节(不包括运行时)能生效:取值 $param;
#可以在构建时进行变化,docker build
# ARG 不像 ENV 不能并排写
ARG param=123456
ARG msg="hello docker"
#构建时期我们会运行的指令(根据 Dockerfile 创建一个镜像的整个过程时期)
RUN echo 11111
RUN echo $param
RUN echo $msg
#运行时期我们会运行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
#(docker run/docker start)
# CMD 和 ENTRYPOINT` 都是指定的运行时的指令
CMD ["/bin/sh","-c","echo 1111;echo $param;echo app_${app}"]
第四例、ENV 的坑--构建期间就已经确定好值
# env 的坑
FROM alpine
# ARG msg=hello
# # ENV 肯定能引用 ARG
# ENV name=${msg}
# RUN echo ${name}
# RUN echo ${msg}
# ENV 只能运行期改掉
ENV msg1=hello
ENV msg2=$msg1
# 以上构建期间就已经确定好值了;ENV 持久化问题。
RUN echo ${msg1}
RUN echo ${msg2}
# msg1=msg2 没问题;如果我运行期修改了 msg1=66666 的值,请求 msg1;msg2 输出什么
# 结果输出: 6666 hello; 传值不是传引用???原因:
# docker build 的时候,env 环境的信息会固化,直接在镜像配置里面就已经写死,msg1=hello,msg2=hello。
# -e 真的只能修改当前 env 本身
# 为什么运行期间能用 ENV 定义的所有值,一定是 ENV 存在某个地方
CMD ["/bin/sh","-c","echo ${msg1};echo ${msg2};"]
第五例、ADD 与 COPY 指令简单使用
# ADD 与 COPY 指令
FROM alpine
#把上下文 Context 指定的内容添加到镜像中,如果是压缩包,自动解压,
# 把当前内容复制到这个 alpine 小系统里面
# 如果是远程文件,自动下载;
# 如果是压缩包,自动解压;
ADD https://download.redis.io/releases/redis-6.2.1.tar.gz /dest/
#本地 linux 系统的内容文件添加进去 【宿主机 镜像内】
# docker build -t demo:test -f Dockerfile 【.:上下文的文件路径】 : .代表上下文环境;代表 Dockerfile 所在的当前目录
#自动解压
# 压缩包位置:/root/dockerfiles
ADD *.tar.gz /app/
# RUN ls -l
# 相当于给当前容器开一个用户,以后的命令可以用这个用户运行
# 不自动解压和下载
# COPY nginx
# 以容器的用户:
# RUN "useradd "
COPY --chown=redis:redis *.tar.gz /redis/
# RUN 指令上下并没有上下文关系;
# RUN cd /dest
# 当前还是列举的根目录
# RUN ls -l
RUN cd /dest && ls -l
RUN cd /app && ls -l
RUN cd /redis && ls -l
#把上下文 Context 指定的内容复制到镜像中
# COPY
第六例、COPY 的文件可以改变用户
# COPY 的文件可以改变用户
FROM alpine
# 开用户
#RUN adduser -u lanson -g lanson
# 以后的所有命令会用 lanson:lanson 来执行。有可能没有执行权限
# 容器中的 ROOT 虽然不是 linux 宿主机的真实 root,但是可以改掉这个镜像的所有
USER 1000:1000
# 把复制来的文件给用户所有权
COPY --chown=lanson:lanson *.txt /a.txt
RUN ls -l /
#不是 root 不能写
RUN echo 2222 >> a.txt
第七例、 WORKDIR 的应用
# WORKDIR 的应用
FROM alpine
RUN pwd && ls -l
# 为以下所有的命令运行指定了基础目录
WORKDIR /app
# 可以为进入容器指定一个默认目录
WORKDIR abc
##比如我们的 nginx 镜像可以做成这样
#WORKDIR /usr/share/nginx/html
# /app/abc 多个 WORKDIR 可以嵌套
RUN pwd && ls -l
#复制到当前目录下
COPY *.txt ./
RUN pwd && ls -l
CMD ping baidu.com
# Nginx 镜像 WORKDIR 应用
FROM nginx
WORKDIR /usr/share/nginx/html
#剩下都是原来 nginx 默认的
第八例、VOLUME 需要注意的坑与 EXPOSE 使用
FROM alpine
RUN mkdir /hello && mkdir /app
RUN echo 1111 > /hello/a.txt
RUN echo 222 > /app/b.txt
#挂载 容器的指定文件夹,如果不存在就创建。
#指定了 VOLUME ,即使启动容器没有指定 -v 参数,我们也会自动进行匿名卷挂载
# 容器内的 /hello ,/app 文件夹,请你在使用镜像启动容器的时候,自动给宿主机上挂载
# VOLUME 挂载出去的东西,容器改变也不会最终 commit 的时候生效
# -v 使用 VOLUME 和-v 挂载出去的目录(外面变,容器里面变)。但是
# 所有改变也生效了
# 1)、但是 docker commit 提交当前容器的所有变化为镜像的时候,就会丢弃
# 2)、VOLUME [ "/hello","/app" ] 容器以后自动挂载,在 Dockerfile 中对 VOLUME 的所有修改都不生效
# 3)、挂载只有一点就是方便在外面修改,或者把外面的东西直接拿过来
# 所以这个写在最后
# JAVA 日志都要挂外面 /app/log
# VOLUME ["/log"]
VOLUME [ "/hello","/app" ]
# VOLUME 指定的挂载目录
# 这两句话没有生效
RUN echo 6666 >> /hello/a.txt
RUN echo 8888 >> /app/b.txt
RUN cd /hello && echo 88888 >>a.txt
#暴露 ,这个只是一个声明;给程序员看。docker 也能看到
# docker -d -P(随机分配端口,)
EXPOSE 8080
EXPOSE 999
CMD ping baidu.com
第九例、CMD、ENTRYPOINT 容器启动指令
FROM alpine
# ENTRYPOINT: 入口(真正的门)
# ENTRYPOINT [ "ping" ]
# 命令(进门的时候带口令)
# 最终的用法: CMD 是给 ENTRYPOINT 提供参数的
#CMD 可以被修改
# CMD ping baidu.com
# ENTRYPOINT + CMD = 容器的完整启动命令
# 这是启动命令
# ENTRYPOINT ping + CMD baidu.com = 错误
#多个 CMD 只有最后一次生效
# CMD ping baidu.com
# ["echo","${param}"] 不是 bash -c 的方式,取不出环境变量性 【】
# echo $param = ["/bin/sh","-c","多长的命令都写在这里 echo ${param}"]
# ENTRYPOINT 或者 CMD 作为唯一入口,只能写一个,最后一个生效
# ENTRYPOINT ping atguigu.com
# RUN,CMD,ENTRYPOINT
# []: ["/bin/sh","-c"] = shell
# shell:
FROM alpine
ENV url=baidu.com
#CMD ["ping","baidu.com"]
# CMD ["useradd","-u","1000","-g","2000"]
# CMD ["ping","${url}"] 取不出变量
# CMD ping ${url}
# 官方都是建议使用 []方式
# CMD ["/bin/sh","-c","ping ${url}"]
# ENTRYPOINT ping baidu.com + CMD 怎么写都没用,容器启动都是以 ENTRYPOINT 的完整命令为准
# java -jar xxxx.jar --spring.profile=dev --server.port=8888
# 这两个合在一起不能是错误的命令
#官方推荐的写法,,变化的写 CMD,而 CMD 是提供参数给 ENTRYPOINT
# docker run imageName cmd1 一旦传递了 cmd1,CMD 指定的所有参数都会被覆盖,
# 自定义参数的情况下一定要传完
CMD [ "5","baidu.com" ]
#exec 的写法 不变的写 ENTRYPOINT;未来他是容器启动的唯一入口,
ENTRYPOINT [ "ping","-c" ]
第十例、 多阶段构建
FROM alpine
RUN 安装 maven
RUN mvn clean package
COPY xx.jar /app.jar
ENTRYPOINT [ "java","-jar","app.jar" ]
#SpringBoot 应用 java -jar xxx.jar
# jre 环境;可以自己打包
# 一个镜像分为多个大的阶段进行构建,最终的构建结果是最后一个阶段的结果
# 多阶段构建
# FROM alpine AS build
# xxxxxx
# FROM jre
# COPY --from=build xxx xxx
# ENTRYPOINT [ "executable" ]
FROM maven:3.6.1-jdk-8-alpine AS buildapp
WORKDIR /app
COPY pom.xml .
COPY src .
RUN mvn clean package -Dmaven.test.skip=true
# /app 下面有 target
RUN pwd && ls -l
RUN cp /app/target/*.jar /app.jar
RUN ls -l
### 以上第一阶段结束,我们得到了一个 app.jar
## 只要一个 JRE
FROM openjdk:8-jre-alpine
#FROM openjdk:8u282-slim
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
LABEL maintainer="lanson"
# 把上一个阶段的东西复制过来
COPY --from=buildapp /app.jar /app.jar
# docker run -e JAVA_OPTS="-Xmx512m -Xms33 -" -e PARAMS="--spring.profiles=dev --server.port=8080" -jar /app/app.jar
# 启动 java 的命令
ENV JAVA_OPTS=""
ENV PARAMS=""
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]
十大案例比较经典,里面的备注信息一定看,注意点都在写在注释里,如果对 Docker 还不是很了解可以回顾看我之前的文章:
大数据需要拥抱云原生吗?云原生为什么这么火?_Lansonli的博客-CSDN博客_云原生大数据
【云原生 | Docker篇】《带你走进Docker的世界》轻松学会原理|架构|安装|加速(一)_Lansonli的博客-CSDN博客
【云原生 | Docker篇】轻松学会 Docker命令(二)_Lansonli的博客-CSDN博客
【云原生 | Docker篇】网络和存储原理_Lansonli的博客-CSDN博客