当前位置:   article > 正文

【零基础入门Docker】Dockerfile中的USER指令以及dockerfile命令详解_dockerfile user

dockerfile user

面向读者:所有人

所属专栏:Docker零基础入门专栏

目录

第 1 步:创建 Dockerfile

第 2 步:构建 Docker 镜像

第 3 步:运行 Docker 容器

第 4 步:验证输出

dockerfile命令详解

最佳实践

默认情况下,Docker 容器以 Root 用户身份运行。如果您在 Docker 容器内大规模部署应用程序,这会带来巨大的安全威胁。您可以使用USER指令更改或切换到 Docker 容器内的不同用户。为此,您首先需要在容器内创建一个用户和一个组。

在本文中,我们将使用USER指令将容器内的用户从 Root 切换到我们将创建的用户。为此,请按照以下步骤操作:

第 1 步:创建 Dockerfile

您可以在Dockerfile中指定创建新用户和组以及切换用户的指令。在此示例中,我们将简单地创建一个 Ubuntu 映像,并通过除 Root 用户之外的其他用户来使用 bash。

  1. FROM ubuntu:latest
  2. RUN apt-get -y update
  3. RUN groupadd -r user && useradd -r -g user user
  4. USER user

在上面的dockerfile中,我们已经拉取了基础镜像 Ubuntu 并更新了它。我们创建了一个名为user的新组,并在该组内创建了一个具有相同名称的新用户。使用USER选项,我们已经切换了用户。

第 2 步:构建 Docker 镜像

创建Dockerfile后,我们现在可以使用 Build 命令创建 Docker 映像。

sudo docker build -t user-demo 

第 3 步:运行 Docker 容器

使用 Docker Run 命令来运行容器。

sudo docker run -it user-demo bash

第 4 步:验证输出

现在,您可以使用id命令检查默认用户和组是否已更改为我们在Dockerfile中创建的用户和组。

id

总之,在本文中,我们讨论了如何使用Dockerfile中的USER指令将 Docker 容器的默认用户从 Root 切换到我们可以使用useraddgroupadd命令创建的另一个用户。 

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

  1. 基础镜像信息
  2. FROM   第一条指令必须为FROM指令,并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个FROM指令(每个镜像一次)
  3. MAINTAINET 指定维护者的信息。
  4. Label    给构建的镜像打标签。
  5. 镜像操作指令
  6. RUN 每条指令将在当前镜像基础上执行,并提交为新的镜像。(可以用“\”换行)
  7. CMD 指定启动容器时执行的命令,每个Dockerfile只能有一条CMD指令,如果指定了多条指令,则最后一条执行。(会被启动时指定的命令覆盖)
  8. ADD 复制指定的到容器的中,可以是Dockerfile所在的目录的一个相对路径;可以是URL,也可以是tar.gz(自动解压)
  9. COPY 复制本地主机的( 为 Dockerfile 所在目录的相对路径)到容器中的(当使用本地目录为源目录时,推荐使用 COPY
  10. WORKDIR 为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录。(可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径, 则会基于之前命令指定的路径)
  11. EXPOSE 告诉Docker服务端暴露端口,在容器启动时需要通过 -p 做端口映射
  12. VOLUME 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等
  13. USER 指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
  14. ONBUILD 配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令
  15. 容器启动时执行指令
  16. ENV 指定环境变量,会被RUN指令使用,并在容器运行时保存
  17. ENTRYPOINT 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。(每个 Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效)
  1. 实例
  2. FROM openjdk:8-jdk-alpine
  3. MAINTAINER Kurisu "makise_kurisuu@outlook.jp"
  4. LABEL maintainer="makise_kurisuu@outlook.jp"
  5. VOLUME /tmp
  6. ARG JAR_FILE=target/alice-server.jar
  7. COPY ${JAR_FILE} app.jar
  8. ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

dockerfile命令详解

FROM  构建的镜像设置基础镜像FROM <image> [AS <name>] FROM <image>[:<tag>] [AS <name>] FROM <image>[@<digest>] [AS <name>]FROM指令初始化新的构建阶段,并为后续指令设置基础镜像,FROM openjdk:8-jdk-alpine。因此,Dockerfile文件必须以FROM指令开头。 而指定的镜像可以使任何有效的镜像(推荐使用公共仓库的镜像,因为拉取更为容易)。在Dockerfile文件中ARG是唯一一个可以用于FROM之前的指令ARG CODE_VERSION=latest FROM base:${CODE_VERSION} CMD /code/run-app FROM extras:${CODE_VERSION} CMD /code/run-extras
  MAINTAINET  指定维护者的信息  格式:MAINTAINET <name>
LABLE  给构建的镜像打标签格式:LABEL <key>=<value> <key>=<value> <key>=<value> ...
RUN 在镜像的构建过程中执行特定的命令,并生成一个中间镜像。比如安装一些软件、配置一些基础环境,可使用\来换行。
  RUN <command> (shell格式)  RUN ["executable", "param1", "param2"] (exec格式)
要注意的是,executable是命令,后面的param是参数采用exec格式指令将会被解析成json格式所以不能使用单引号,并且使用反斜杠也是必须要转移的,这在windows上尤为重要。
CMD 指定容器运行时的默认参数
CMD ["executable","param1","param2"](exec格式,首选)CMD ["param1","param2"](给ENTRYPOINT提供默认参数)CMD command param1 param2(shell格式)
注意:RUN是在构建的时候执行,并生成一个新的镜像,CMD在构建时不进行任何操作,在容器运行的时候执行。如果CMD用于为ENTRYPOINT指令提供缺省参数,那么CMD和ENTRYPOINT指令都应该使用JSON数组格式。
ADD 构建镜像时,复制上下文中的文件到镜像内。
ADD <src>... <dest>ADD ["<src>",... "<dest>"](路径包含空格的必须使用这种格式)

<src>可以是文件、目录,也可以是文件URL。可以使用模糊匹配(wildcards,类似shell的匹配),可以指定多个<src>,必须是在上下文目录和子目录中, 无法添加../a.txt这样的文件。如果<src>是个目录,则复制的是目录下的所有内容,但不包括该目录。如果<src>是个可被docker识别的压缩包, docker会以tar -x的方式解压后将内容复制到<desct>。<dest>可以是绝对路径,也可以是相对WORKDIR目录的相对路径。 所有文件的UID和GID都是0
 

COPY 将主机的文件复制到镜像内,如果目的位置不存在,Docker会自动创建所有需要的目录结构,但是它只是单纯的复制,并不会去做文件提取和解压工作COPY <src>... <dest>COPY ["<src>",... "<dest>"](路径包含空格的必须使用这种格式)注意:需要复制的目录一定要放在Dockerfile文件的同级目录下。因为构建环境将会上传到Docker守护进程,而复制是在Docker守护进程中进行的。任何位于构建环境之外的东西都是不可用的。 COPY指令的目的的位置则必须是容器内部的一个绝对路径。
 
WORKDIR 为接下来的Dockerfile指令指定当前工作目录,可多次使用,如果使用的是相对路径,则相对的是上一个工作目录,类似shell中的cd命令。WORKDIR /path/to/workdir受影响的指令有:RUN、CMD、ENTRYPOINT、COPY和ADD。 可以在Dockerfile中多次使用WORKDIR指令。如果提供了相对路径,则它将相对于上一个WORKDIR指令的路径。例如:WORKDIR /aWORKDIR bWORKDIR cRUN pwd此Dockerfile中pwd指令将输出 /a/b/c.。WORKDIR指令可以解析之前使用ENV设置的环境变量,只能使用Dockerfile中显式设置的环境变量。例如:ENV DIRPATH /pathWORKDIR $DIRPATH/$DIRNAMERUN pwd此Dockerfile中pwd指令将输出 /path/$DIRNAME。
 
EXPOSE 为构建的镜像设置监听端口。EXPOSE <port> [<port>/<protocol>...]EXPOSE指令让docker容器在运行的时候监听指定的端口,可以指定端口是upd还是tcp协议,如果没有指定则默认tcp协议。EXPOSE指令并不会发布端口,如果发布端口,则需要在docker run时使用-p来发布和映射一个或多个容器端口、或者使用-P来发布所有公开的端口并将它们映射到高阶端口。
 
VOLUME 指定镜像内的目录为数据卷。VOLUME ["/data"]在容器运行的时候,docker会把镜像中的数据卷的内容复制到容器的数据卷中去。 如果在接下来的Dockerfile指令中,修改了数据卷中的内容,则修改无效。
 
USER 为接下来的Dockerfile指令指定用户。受影响的指令有:RUN、CMD、ENTRYPOINT。USER <user>[:<group>] orUSER <UID>[:<GID>]注意:当用户没有主要组时,镜像(或下一条指令)将与该root组一起运行
 
ONBUILD 向镜像中添加一个触发器,当以该镜像为基础镜像再次构建新的镜像时,会触发执行其中的指令。ONBUILD [INSTRUCTION]比如我们生成的镜像是用来部署Python代码的,但是因为有多个项目可能会复用该镜像。你可以这样使用:[...]# 在下一次以此镜像为base image的构建中,执行ADD . /app/src,将项目代目添加到新镜像中去ONBUILD ADD . /app/src# 并且build Python代码ONBUILD RUN /usr/local/bin/python-build --dir /app/src[...]不允许ONBUILD使用链接指令ONBUILD ONBUILD。ONBUILD只会继承给子节点的镜像,不会再继承给孙子节点。ONBUILD不会触发FROM、MAINTAINER指令。
ENV 在构建的镜像中设置环境变量,在后续的Dockerfile指令中可以直接使用,也可以固化在镜像里,在容器运行时仍然有效。 ENV指令有两种格式:ENV :把第一个空格之后的所有值都当做的值,无法在一行内设定多个环境变量。ENV = ...:可以设置多个环境变量,如果中存在空格,需要转义或用引号"括起来。ENV myName John DoeENV myDog Rex The DogENV myCat fluffy或者
ENV myName="John Doe" myDog=Rex\ The\ Dog \    myCat=fluffy推荐使用第二种,同样的这样能减少图层提高效率。
注意:可以在容器运行时指定环境变量,替换镜像中的已有变量,docker run --env <key>=<value>。使用ENV可能会对后续的Dockerfile指令造成影响,如果只需要对一条指令设置环境变量,可以使用这种方式:RUN <key>=<value> <command>
ENTRYPOINT 指定镜像的执行程序。ENTRYPOINT指令有两种格式:ENTRYPOINT ["executable", "param1", "param2"] (执行格式,首选)ENTRYPOINT command param1 param2 (shell格式)CMD和ENTRYPOINT至少得使用一个。ENTRYPOINT应该被当做docker的可执行程序,CMD应该被当做ENTRYPOINT的默认参数。docker run <image> <arg1> <arg2> ...会把之后的参数传递给ENTRYPOINT,覆盖CMD指定的参数。可以用docker run --entrypoint来重置默认的ENTRYPOINT。
 
STOPSIGNAL 触发系统信号。STOPSIGNAL signalSTOPSIGNAL指令设置将发送到容器以退出的系统调用信号。此信号可以是与内核的SysCall表中的位置匹配的有效无符号数字(例如9),也可以是格式为SIGNAME的信号名称(例如SIGKILL)。
HEALTHCHECK 增加自定义的心跳检测功能,多次使用只有最后一次有效。格式:HEALTHCHECK [OPTIONS] CMD command (通过在容器内运行命令来检查容器运行状况)HEALTHCHECK NONE (禁用从基础映像继承的任何运行状况检查)可选的OPTION:
--interval=DURATION(检测间隔,默认值:30s)--timeout=DURATION(命令超时时间,默认值:30s)--start-period=DURATION(初始化后开始检查时间,默认值:0s)--retries=N(连续N次失败后标记为不健康,默认值:3次)Start Period为需要时间引导的容器提供初始化时间。在此期间的探测失败将不计入最大重试次数。但是,如果运行状况检查在开始期间成功,则容器将被视为已启动,并且所有连续失败将计入最大重试次数。
<command>可以是shell脚本,也可以是exec格式的json数组。docker以<command>的退出状态码来区分容器是否健康,这一点同shell一致:
0:命令返回成功,容器健康1:命令返回失败,容器不健康2:保留状态码,不要使用举个栗子:每5分钟检测本地网页是否可访问,超时设为3秒:
HEALTHCHECK --interval=5m --timeout=3s \  CMD curl -f http://localhost/ || exit 1复制代码你可以使用docker inspect命令查看健康状态。当容器的运行状况发生更改时,将生成带有新状态的health_status事件
SHELL更改后续的Dockerfile指令中所使用的shell。默认的shell是["bin/sh", "-c"]。可多次使用,每次都只改变后续指令。
SHELL ["executable", "parameters"]
 
特别注意在上面的描述中可以看到有两组指令在功能上都差不多,但也是有区别的:
RUN、CMD、ENTRYPOINT执行脚本的指令三个指令都是执行脚本RUN是在创建镜像是执行,即使用docker build命令时执行,在一个Dockerfile里面可以有多个RUNCMD和ENTRYPOINT是在运行容器时执行,即使用docker run命令时执行,这两个指令在Dockerfile中都只有最行一条被执行CMD在使用docker run时可以加参数将Dockerfile中的CMD覆盖ENTRYPOINT在Dockerfile中出现后就一定会在docker run时被执行,不必担心会被其他参数所覆盖。ADD、COPY拷贝文件(夹)到容器ADD拷贝文件(夹)时可以指定本地文件、远程URL地址,如果拷贝的是tar文件时将会被自动解压成文件夹COPY拷贝文件(夹)时不可以指定远程URL地址,拷贝tar文件也不会被自动解压成文件夹,在拷贝本地文件时建议使用COPY

最佳实践

容器应该是短暂的

通过 Dockerfile 构建的镜像所启动的容器应该尽可能短暂 (ephemeral)。短暂意味着可以很快地启动并且终止

使用 .dockerignore 排除构建无关文件

.dockerignore 语法与 .gitignore 语法一致。使用它排除构建无关的文件及目录,如 node_modules

使用多阶段构建

多阶段构建可以有效减小镜像体积,特别是对于需编译语言而言,一个应用的构建过程往往如下

  1. 安装编译工具
  2. 安装第三方库依赖
  3. 编译构建应用

而在前两步会有大量的镜像体积冗余,使用多阶段构建可以避免这一问题

避免安装不必要的包

减小体积,减少构建时间。如前端应用使用 npm install --production 只装生产环境所依赖的包。

一个容器只做一件事

如一个web应用将会包含三个部分,web 服务,数据库与缓存。把他们解耦到多个容器中,方便横向扩展。如果你需要网络通信,则可以将他们至于一个网络下。

减少镜像层数

只有 RUN, COPY, ADD 会创建层数, 其它指令不会增加镜像的体积
尽可能使用多阶段构建
使用以下方法安装依赖

RUN yum install -y node python go

将多行参数排序

便于可读性以及不小心地重复装包

 
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion

充分利用构建缓存

在镜像的构建过程中 docker 会遍历 Dockerfile 文件中的所有指令,顺序执行。对于每一条指令,docker 都会在缓存中查找是否已存在可重用的镜像,否则会创建一个新的镜像。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/58156
推荐阅读
相关标签
  

闽ICP备14008679号