zoukankan      html  css  js  c++  java
  • docker file

    更准确的理解,或英文水平好的,建议直接看官方文档https://docs.docker.com/engine/reference/builder/,因为每个人的英语水平不同,理解能力不同。这节应该是比较难的一节,也是比较重要 一节。

    Dockerfile是个文本文件,这个文件就以“Dockerfile”命名(至少默认就叫这个名子,而且就在软件的要目录下,可以在docker built时通过-f参数来指定这个Dockerfile的位置和名子),Docker能通过读取这个文件里的指令集来自动创建一个镜像。然后使用docker build命令,我们能创建出一个能执行一系列指令的镜像。

    格式:

    # Comment使用“#”注释
    INSTRUCTION arguments
    指令      参数

      指令是不分大小写的。(这个是我看了官方文档才知道,因为网上各大神都是指令大写的。)入乡随俗,国际惯例,指令大写,那就指令大写吧。

    所有指令顶格编写

    错误示范

    无效的,因为使用了换行符

    # direc 
    tive=value

    无效的,因为使用了两次

    # directive=value1
    # directive=value2
    
    FROM ImageName

    被当作注释,因为在一个构建指令之后。

    FROM ImageName
    # directive=value

    被当作注释,因为前面还有注释。

    # About my dockerfile
    # directive=value
    FROM ImageName

    一个是未被识别的指令,会被当成注释,而下面那个却因为上面那个成了注释,尽管这是一个能被识别的指令,但也是被当作注释。

    # unknowndirective=value
    # knowndirective=value

    以下的效果是一样的,空格不影响

    #directive=value
    # directive =value
    #	directive= value
    # directive = value
    #	  dIrEcTiVe=value
    

     escape转义,如果没有特殊指定,如下操作。那么系统默认“”为作转义字符。

    # escape=   or # escape=`

    ENV 

    Environment replacement 环境置换

    环境参数使用ENV来声明

    ENV 设计环境变量。它们使用键值对,增加运行程序的灵活性。
    ENV <key> <value>
    ENV <key>=<value> ...

     环境变理,可以使用$variable_name 或 ${variable_name}来表示。这些变量与shell有点类似。

    如:

    ${variable:-word} 表示如果变量有东西就直接显示,如果为空,就使用word来代替
    ${variable:+word} 表示如果变量为word就显示word,否则,就为空。

    要注意以下情况

    ENV abc=hello
    ENV abc=bye def=$abc
    ENV ghi=$abc

    def的结果是hello而不是bye,ghi的结果是bye。因为是不同部分的指令。

    其它例子

    ENV myName="John Doe" myDog=Rex The Dog 
        myCat=fluffy
    ENV myName John Doe
    ENV myDog Rex The Dog
    ENV myCat fluffy

    .dockerignore文件。一个被命名为.dockerignore的隐藏文件,如果他存在,Docker就会去找这个文件里的内容,出现在这个文件里的路径都会被忽略。

    以下是一些.dockerignore的例子。

    # comment
    */temp*
    */*/temp*
    temp?

    可使用通配符。

    解析指令

    在这个Dockerfile里的指令有:ADD,COPY,ENV,EXPOSE,FROM,LABEL,STOPSIGNAL,USER,VOLUME,WORKDIR

    FROM

    所有的Dockfile都必须以FROM命令开始。这个命令是指基于哪个镜像开始创建。下面是常见命令总表

    FROM <image> [AS <name>]
    or
    FROM <image>[:<tag>] [AS <name>]
    or
    FROM <image>[@<digest>] [AS <name>]

    ARG构建参数

    ARG <arg name> [=<defaults value>]

    ARG必须在FROM之前来声明参数,在后面的构建中是不会用到这个变量的。然后只有定义过的ARG才能在docker built 中使用--build-arg<参数名>=<值>
    来覆盖。

     在一个Dockerfile里面,可出现多次FROM指令。

    除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

    RUN/CMD/ENTRYPOINT

    RUN

    RUN <command>在shell或者exec的环境下要执行的命令。
    RUN <command> (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows)
    RUN ["executable", "param1", "param2"] (exec form)

    使用一次RUN就等于创建一层。所以链接使用RUN是没有必要的,要使用换行加&&的形式来表示

    例如:

    FROM debian:jessie
    
    RUN apt-get update
    RUN apt-get install -y gcc libc6-dev make
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
    RUN mkdir -p /usr/src/redis
    RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
    RUN make -C /usr/src/redis
    RUN make -C /usr/src/redis install
    
    可替换成
    FROM debian:jessie
    
    RUN buildDeps='gcc libc6-dev make' 
        && apt-get update 
        && apt-get install -y $buildDeps 
        && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" 
        && mkdir -p /usr/src/redis 
        && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 
        && make -C /usr/src/redis 
        && make -C /usr/src/redis install 
        && rm -rf /var/lib/apt/lists/* 
        && rm redis.tar.gz 
        && rm -r /usr/src/redis 
        && apt-get purge -y --auto-remove $buildDeps

    这个我是参考https://github.com/yeasy/docker_practice/blob/master/image/build.md

    首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 RUN 对一一对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。

    并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 的命令换行方式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。

    此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。

    很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。

    CMD 容器启动命令的三种形式,但CMD只能使用一次,多个CMD会抵消之前的指令。
    CMD ["executable","param1","param2"](推荐这种json格式,要使用双引号,不使用单引号。)
    CMD ["param1","param2"]作为一个参数向ENTRYPOINT传递
    CMD command param1 param2

    ENTRYPOIONT 配置容器一个可执行的命令与CMD比较相似,也是只能使用一次,多个命令会抵消之前的。它有两种形式
    ENTRYPOIONT ["executable","param1","param2"]
    ENTRYPOIONT command param1 param2

    CMD

    Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的

    如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。推荐这种json格式,要使用双引号,不使用单引号。还有需要了解清命令启动容器,容器里的东西就生效,要是命令结束了,容器就生命周期就结束。所以要启动一个类似于service nginx start的命令时,实际是在运行init.d下的一个nginx的脚本。而执行这个脚本是sh 。当这个脚本被执行完后,程序就结束,容器就会被销毁。所以应该直接运行nginx命令。

    ENTRYPOINT

    这节参考来源于https://github.com/yeasy/docker_practice/blob/master/image/dockerfile/entrypoint.md

    ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
    ENTRYPOINT command param1 param2 (shell form)

    ENTRYPOINT 命令格式和RUN一样,而其目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。当我们定义了ENTRYPOINT后,CMD的含意就发生了改变,变成把CMD的内容作为参数传给ENTRYPOINT

    <ENTRYPOINT> "<CMD>"

    例子1:让镜像变成像命令一样使用

    假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:

    FROM ubuntu:16.04
    RUN apt-get update 
        && apt-get install -y curl 
        && rm -rf /var/lib/apt/lists/*
    CMD [ "curl", "-s", "http://ip.cn" ]

    假如我们使用 docker build -t myip . 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:

    $ docker run myip
    如果再试
    $ docker run myip -i 报错

    改成

    FROM ubuntu:16.04
    RUN apt-get update 
        && apt-get install -y curl 
        && rm -rf /var/lib/apt/lists/*
    ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

    则正常。其实每个一外来的参数就是一个CMD。之前使用CMD电把参数替换了CMD的位置,而ENTRYPOINT则是把CMD当成参数来替换他的参数部分。

    场景二:应用运行前的准备工作

    启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。

    比如 mysql 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的 mysql 服务器运行之前解决。

    此外,可能希望避免使用 root 用户去启动服务,从而提高安全性,而在启动服务前还需要以 root 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 root 身份执行,方便调试等。

    这些准备工作是和容器 CMD 无关的,无论 CMD 为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会将接到的参数(也就是 <CMD>)作为命令,在脚本最后执行。比如官方镜像 redis 中就是这么做的:

    FROM alpine:3.4
    ...
    RUN addgroup -S redis && adduser -S -G redis redis
    ...
    ENTRYPOINT ["docker-entrypoint.sh"]
    
    EXPOSE 6379
    CMD [ "redis-server" ]

    EXPOSE 6379 CMD [ "redis-server" ] 可以看到其中为了 redis 服务创建了 redis 用户,并在最后指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本。

    #!/bin/sh
    ...
    # allow the container to be started with `--user`
    if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
        chown -R redis .
        exec su-exec redis "$0" "$@"
    fi
    
    exec "$@"

    该脚本的内容就是根据 CMD 的内容来判断,如果是 redis-server 的话,则切换到 redis 用户身份启动服务器,否则依旧使用 root 身份执行。比如:

    $ docker run -it redis id uid=0(root) gid=0(root) groups=0(root)

     MAINTAINER <author name>镜像作者,从官方文档来看,这个参数是过时的,不建议使用的。官方提出使用标签来代替这个指令

    MAINTAINER <name>
    使用标签来代替这个指令
    LABEL maintainer="SvenDowideit@home.org.au"


    EXPOSE

    暴露端口,指定容器在运行时监听的端口。不知道为什么在容器里喜欢说暴露。

    EXPOSE <port> [<port>...]


    ADD/COPY

    ADD <src>... <dest>
    ADD ["<src>",... "<dest>"]
    (推荐这种,即使有空格也好使)

    ADD hom* /mydir/        # 复制所有以hom开头的文件
    ADD hom?.txt /mydir/    # ? 能代替任意一个字符e.g., "home.txt"
    ADD test relativeDir/          # 把"test" 复制到`WORKDIR`/relativeDir/
    ADD test /absoluteDir/         # 把"test" 复制到绝对路径/absoluteDir/

    有特殊字符即需要转义。
    ADD arr[[]0].txt /mydir/ # copy a file named "arr[0].txt" to /mydir/

    COPY <src>... <dest>
    COPY ["<src>",... "<dest>"]

    COPY hom* /mydir/
    COPY hom?.txt /mydir/

    目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径。工作目录可使用WORKDIR来设定。
    使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
    与ADD不同的是,ADD支持 URL。这个时候Docker会试图下载这个链接的文件放到目标路径。被下载的文件权限为600。如果需要调整权限需要使用RUN命令来调整。
    如果<源路径>为一个tar压缩文件,压缩格式为gzip/bzip2/xz的话,ADD指令会自动解压文件到<目标路径>去
    因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD

    VOLUME定义匿名卷

    这是比较重要一节,这是关系到数据持久化的问题。

    VOLUME ["/data"]
    例:
    FROM ubuntu
    RUN mkdir /myvol
    RUN echo "hello world" > /myvol/greeting
    VOLUME /myvol

    这样的操作是自动挂载匿名卷,这样任何写在/data中的信息都不会记录在容器存储层。也可以在运行的时候代替这个data

    在这里,我们可以定义,web目录、定义配置目录、定义数据库目录、定义日志目录等。

    USER

    USER <user>[:<group>] or
    USER <UID>[:<GID>]

    改变环境状态,影响到以后的层,使RUN/CMD/ENTRPOINT使用指定身份运行。

    WORKDIR

    WORKDIR 指定RUN/CMD/ENTRYPOINT命令的工作目录。
    WORKDIR /PATH/TO/WORKDIR

    用来设定RUN, CMD, ENTRYPOINT, COPY 和 ADD这些操作的默认路径。其参数如果是相对路径,则是相对于workdir的路径

    WORKDIR /a
    WORKDIR b
    WORKDIR c
    RUN pwd
    =》/a/b/c

    ONBUILD

    ONBUILD [INSTRUCTION]

    这个命令后面接其RUN/COPY等指令,但个在当前镜像构建时并不会执行,而是在下次构建时才会执行的。

    HEALTHCHECK

    HEALTHCHECK 设置检查容器的健康情况
    HEALTHCHECK [option] <command>
    HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
    


    LABEL标签

    LABEL <key>=<value> <key>=<value> <key>=<value> ...

    标签指令能为镜像增加元数据,一个标签就是一个键值对。
    可以使用引号或反斜杠。

    举个粟子:

    LABEL "com.example.vendor"="ACME Incorporated"
    LABEL com.example.label-with-value="foo"
    LABEL version="1.0"
    LABEL description="This text illustrates 
    that label-values can span multiple lines."

    一个镜像,可以有一个或多个标签,多个标签也可以写成一个命令行。例如:

    LABEL multi.label1="value1" multi.label2="value2" other="value3"
    等效于
    LABEL multi.label1="value1" 
          multi.label2="value2" 
          other="value3"


     

  • 相关阅读:
    silverlight 自定义 鼠标 双击事件
    silverlight 常用特效
    silverlight 碰撞检测
    silverlight的自定义依赖属性
    在程序代码中集成跨域服务文件
    Silverlight动画基础
    silverlight 虚线框
    建立纯代码的silverlight项目
    silverlight字符串加密之二
    silverlight3 加载其他xap
  • 原文地址:https://www.cnblogs.com/gorgage/p/7286201.html
Copyright © 2011-2022 走看看