Dockerfile

admin
2023-04-16 / 0 评论 / 186 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年04月16日,已超过587天没有更新,若内容或图片失效,请留言反馈。

基础镜像的选择 (FROM)

  • 官方镜像优于非官方的镜像,如果没有官方镜像,则尽量选择Dockerfile开源的
  • 固定版本tag而不是每次都使用latest
  • 尽量选择体积小的镜像

空镜像:scratch

最小镜像:alpine

通过 RUN 执行指令

RUN 主要用于在Image里执行指令,比如安装软件,下载文件等。

FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install -y wget
RUN wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz
RUN tar zxf ipinfo_2.0.1_linux_amd64.tar.gz
RUN mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo
RUN rm -rf ipinfo_2.0.1_linux_amd64.tar.gz

每执行一次RUN执行,就会新产生一个分层。

一般使用,优化后的执行命令

FROM ubuntu:20.04
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz && \
    tar zxf ipinfo_2.0.1_linux_amd64.tar.gz && \
    mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_2.0.1_linux_amd64.tar.gz

&&:指令拼接

\:命令换行。

只执行了一次RUN,则只会产生一个分层。

文件复制和目录操作

(ADD,COPY,WORKDIR)

往镜像里复制文件有两种方式,COPYADD , 我们来看一下两者的不同。

复制普通文件

COPYADD 都可以把local的一个文件复制到镜像里,如果目标目录不存在,则会自动创建。

FROM python:3.9.5-alpine3.13
COPY hello.py /app/hello.py

比如把本地的 hello.py 复制到 /app 目录下。 /app这个folder不存在,则会自动创建。

复制压缩文件

ADD 比 COPY高级一点的地方就是,如果复制的是一个gzip等压缩文件时,ADD会帮助我们自动去解压缩文件。

FROM python:3.9.5-alpine3.13
ADD hello.tar.gz /app/

所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。

注意:对应文件的权限复制到基础镜像里面后权限一样。

目录切换

WORKDIR:目录切换,类似cd的操作。比cd好的是,没有目录时,会帮自动创建。

FROM python:3.9.5-alpine3.13
WORKDIR /app
COPY hello.py hello.py

WORKDIR切换目录后,copy默认copy到切换后的app目录下。

构建参数和环境变量 (ARG vs ENV)

ARGENV 是经常容易被混淆的两个Dockerfile的语法,都可以用来设置一个“变量”。 但实际上两者有很多的不同。

例子:安装某个特定版本软件时,需要对版本进行升级,就可以定义参数变量,方便后续升级,直接改版本号即可。

FROM ubuntu:20.04
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz && \
    tar zxf ipinfo_2.0.1_linux_amd64.tar.gz && \
    mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_2.0.1_linux_amd64.tar.gz

上面是没有使用变量时,每次升级就需要改版本号2.0.1,这样会修改很多地方,这时就可以定义参数变量,之后升级改一个地方就可以了。

ENV变量

FROM ubuntu:20.04
ENV VERSION=2.0.1
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-${VERSION}/ipinfo_${VERSION}_linux_amd64.tar.gz && \
    tar zxf ipinfo_${VERSION}_linux_amd64.tar.gz && \
    mv ipinfo_${VERSION}_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_${VERSION}_linux_amd64.tar.gz

ARG变量

FROM ubuntu:20.04
ARG VERSION=2.0.1
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-${VERSION}/ipinfo_${VERSION}_linux_amd64.tar.gz && \
    tar zxf ipinfo_${VERSION}_linux_amd64.tar.gz && \
    mv ipinfo_${VERSION}_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_${VERSION}_linux_amd64.tar.gz

使用时,都是${变量名}这样使用。如果要改版本,这样直接改VERSION=新版本号就可以了。

ENV与ARG区别

ARG:只作用域镜像构建过程中,镜像环境中是没有该参数的,后续容器中不能使用改参数变量。build镜像时可以使用参数--build-arg动态改变参数值

例如:

docker image build -f .\Dockerfile-arg -t ipinfo-arg-2.0.0 --build-arg VERSION=2.0.0 .

ENV:会将参数设置到镜像中,通过env可以看到环境变量中有对应的参数,后续容器中也可以使用这些参数变量。不能动态改值。

使用场景

ARG主要用于关注build镜像的过程。

ENV主要用于后续容器中使用。

容器启动时默认执行的命令 CMD

CMD可以用来设置容器启动时默认会执行的命令。

  • 容器启动时默认执行的命令
  • 如果docker container run启动容器时指定了其它命令,则CMD命令会被忽略
  • 如果定义了多个CMD,只有最后一个会被执行。

1、清理后台已经停止的全部容器:

docker system prune -f

也可以一个一个清理

docekr container rm 容器名称

2、删除没有用到的全部镜像

docker image prune -a

当没有定义CMD命令时,会默认进入到shell是因为在ubuntu的基础镜像里有定义CMD。

如果想一次行运行容器,运行完后自动删除容器:只需要在运行容器时加参数--rm即可

容器启动命令 ENTRYPOINT

ENTRYPOINT 也可以设置容器启动时要执行的命令,但是和CMD是有区别的。

  • CMD 设置的命令,可以在docker container run 时传入其它命令,覆盖掉 CMD 的命令,但是 ENTRYPOINT 所设置的命令是一定会被执行的。
  • ENTRYPOINTCMD 可以联合使用,ENTRYPOINT 设置执行的命令,CMD传递参数。

1、CMD命令的Dockerfile,build成一个叫 demo-cmd 的镜象。

FROM ubuntu:20.04
CMD ["echo", "hello docker"]

2、ENTRYPOINT命令的Dockerfile,build成一个叫 demo-entrypoint 的镜象。

FROM ubuntu:20.04
ENTRYPOINT ["echo", "hello docker"]

比较:

CMD的镜像,如果执行创建容器,不指定运行时的命令,则会默认执行CMD所定义的命令,打印出hello docker

$ docker container run -it --rm demo-cmd
hello docker

如果我们docker container run的时候指定命令,则该命令会覆盖掉CMD的命令:

$ docker container run -it --rm demo-cmd echo "hello world"
hello world

ENTRYPOINT的容器里ENTRYPOINT所定义的命令则无法覆盖,一定会执行:

$ docker container run -it --rm demo-entrypoint
hello docker
$ docker container run -it --rm demo-entrypoint echo "hello world"
hello docker echo hello world

会将echo "hello world"当作参数传入。

3、CMD作为参数参数传入ENTRYPOINT,Dockerfile如下:

FROM ubuntu:21.04
ENTRYPOINT ["echo"]
CMD []

将3编译成叫demoboth的镜像。

docker run --rm -it demoboth

不加cmd执行:执行echo打印了空行

[root@10 ~]# docker run --rm -it demoboth:1.0

加cmd传入作为echo的参数打出:

[root@10 ~]# docker run --rm -it demoboth:1.0 echo "hi docker"
echo hi docker

Shell 格式和 Exec 格式

Shell格式

CMD echo "hello docker"
ENTRYPOINT echo "hello docker"

Exec格式

以可执行命令的方式

ENTRYPOINT ["echo", "hello docker"]
CMD ["echo", "hello docker"]

注意shell脚本的问题

FROM ubuntu:20.04
ENV NAME=docker
CMD echo "hello $NAME"

假如我们要把上面的CMD改成Exec格式,下面这样改是不行的。

FROM ubuntu:20.04
ENV NAME=docker
CMD ["echo", "hello $NAME"]

它会打印出 hello $NAME , 而不是 hello docker ,那么需要怎么写呢? 我们需要以shell脚本的方式去执行:

FROM ubuntu:20.04
ENV NAME=docker
CMD ["sh", "-c", "echo hello $NAME"]

"sh":执行sh命令

“-c”: 执行的命令

4

评论 (0)

取消