docker 基础学习4---dockerFile

需要注意的几点:

1,每个关键字(指令)都必须大写

2,指令从上到下执行

3,#表示注释

4,每一条指令都会创建一个新的镜像层

编译镜像文件

语法:docker build [OPTIONS] PATH| URL| - 
常见选项: 
          -t 设置镜像的名称和TAG,格式为name:tag 
          -f Dockerfile的名称,默认为PATH/Dockerfile 
例子:docker build -f ~/php.Dockerfile . 

        :docker build -t nginx:test .
注意:PATH是编译镜像使用的工作目录,Docker Daemon在编译开始时,会扫描PATH中的所有文件,可以在编译目录中加入.dockerignore过滤不需要的文件

指令:FROM 
功能描述:设置基础镜像 
语法:FROM < image>[:< tag> | @< digest>] 
提示:镜像都是从一个基础镜像(操作系统或其他镜像)生成,可以在一个Dockerfile中添加多条FROM指令,一次生成多个镜像 
注意:如果忽略tag选项,会使用latest镜像
例子:FROM centos
指令:MAINTAINER 
功能描述:设置镜像作者 
语法:MAINTAINER < name>
例子:MAINTAINER 张三<1234567@qq.com>

 

指令:RUN 
功能描述: 
语法:RUN < command> 
     RUN [“executable”,”param1”,”param2”] 
提示:RUN指令会生成容器,在容器中执行脚本,容器使用当前镜像,脚本指令完成后,Docker Daemon会将该容器提交为一个中间镜像,供后面的指令使用 
补充:RUN指令第一种方式为shell方式,使用/bin/sh -c < command>运行脚本,可以在其中使用\将脚本分为多行 
          RUN指令第二种方式为exec方式,镜像中没有/bin/sh或者要使用其他shell时使用该方式,其不会调用shell命令 
例子:RUN source $HOME/.bashrc;\ 
          echo $HOME

          RUN [“/bin/bash”,”-c”,”echo hello”]

          RUN [“sh”,”-c”,”echo”,”$HOME”] 使用第二种方式调用shell读取环境变量
补充:
shell 格式:
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。

exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

例如:
FROM centos
RUN yum install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:
FROM centos
RUN yum install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz
指令:CMD 
功能描述:设置容器的启动命令 
语法:CMD <shell 命令> 
     CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
     CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

注意:Dockerfile中只能有一条CMD命令,如果写了多条则最后一条生效
    CMD 在docker run 时运行。
    RUN 是在 docker build。
指令:LABEL 
功能描述:设置镜像的标签 
延伸:镜像标签可以通过docker inspect查看 
格式:LABEL < key>=< value> < key>=< value> … 
提示:不同标签之间通过空格隔开 
注意:每条指令都会生成一个镜像层,Docker中镜像最多只能有127层,如果超出Docker Daemon就会报错。
说明:LABEL会继承基础镜像种的LABEL,如遇到key相同,则值覆盖

使用LABEL设置镜像元数据
使用LABEL指令,可以为镜像设置元数据,例如镜像创建者或者镜像说明。旧版的Dockerfile语法使用MAINTAINER指令指定镜像创建者,但是它已经被弃用了。有时,一些外部程序需要用到镜像的元数据,例如nvidia-docker需要用到com.nvidia.volumes.needed。

例子:LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"

指令:EXPOSE 
功能描述:设置镜像暴露端口,记录容器启动时监听哪些端口 
语法:EXPOSE < port> < port> … 
延伸:镜像暴露端口可以通过docker inspect查看 
提示:容器启动时,Docker Daemon会扫描镜像中暴露的端口,如果加入-P参数,Docker Daemon会把镜像中所有暴露端口导出,并为每个暴露端口分配一个随机的主机端口(暴露端口是容器监听端口,主机端口为外部访问容器的端口) 
注意:EXPOSE只设置暴露端口并不导出端口,只有启动容器时使用-P/-p才导出端口,这个时候才能通过外部访问容器提供的服务
指令:ENV 
功能描述:设置镜像中的环境变量 
格式有两种:
    ENV <key> <value>
    ENV <key1>=<value1> <key2>=<value2>...
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。
例子1:
ENV VERSION=1.0 DEBUG=on \
    NAME="Happy Feet"
例子2:
ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
  && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
  && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
  && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
  && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
  && ln -s /usr/local/bin/node /usr/local/bin/nodejs

这样做的好处是什么?
在这里先定义了环境变量 NODE_VERSION,其后的 RUN 这层里,多次使用 $NODE_VERSION 来进行操作定制。可以看到,将来升级镜像构建版本的时候,只需要更新 7.2.0 即可,Dockerfile 构建维护变得更轻松了。

下列指令可以支持环境变量展开: ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD。

可以从这个指令列表里感觉到,环境变量可以使用的地方很多,很强大。通过环境变量,我们可以让一份 Dockerfile 制作更多的镜像,只需使用不同的环境变量即可。
指令:ADD
格式:
ADD [--chown=<user>:<group>] <源路径1>...  <目标路径>
ADD [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。
为目录时,复制目录中所有内容,包括文件系统的元数据,但不包括目录本身 。
为压缩文件,并且压缩方式为gzip,bzip2或xz时,指令会将其解压为目录 。
为文件,则复制文件和元数据 。

<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

ADD指令和COPY 的格式和性质基本一致。但是在 COPY`基础上增加了一些功能。官方推荐在不需要自动解压的场景下使用COPY指令
COPY指令从构建上下文目录中复制文件或目录到容器的文件系统中。

和 RUN 指令一样,COPY也有两种格式,一种类似于命令行,一种类似于函数调用,格式如下:

COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

复制单个文件示例:

COPY package.json /usr/src/app/
源路径>可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/
复制src目录下内容到 /tmp 目录下

COPY src/ /tmp
复制多个目录下内容到 /tmp 目录下

COPY src1/ src2/ /tmp
上面的命令只会将文件夹内容复制到镜像目录下,复制整个src目录到/tmp目录下,如果源目录名不存在将自动逐级创建:

COPY src/ /tmp/src
指定文件权限
在使用该指令的时候还可以加上 --chown=: 选项来改变文件的所属用户及所属组。

COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
指令:ENTRYPOINT

详细解释:https://www.kancloud.cn/willseecloud/docker-handbook/1273827

ENTRYPOINT 格式和RUN格式一样,分为 exec 格式和 shell 格式。

例子:我们构建一个可以获取查询当前公网 IP的镜像
FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "https://ip.cn" ]

然后我们获取ip即可通过
$ docker run myip
当前 IP:61.148.226.66 来自:北京市 联通

这个时候我们想获取HTTP消息头信息需要传递-i
但是我们发现这么写报错了
$ docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
为什么呢?如果CMD指令直接传-i参数,那么是直接覆盖了CMD [ "curl", "-s", "https://ip.cn" ]这段,所以会报错

我们就必须重新完整的输入这个命令:
$ docker run myip curl -s https://ip.cn -i

这显然不是很好的解决方案,而使用 ENTRYPOINT 就可以解决这个问题。现在我们重新用 ENTRYPOINT 来实现这个镜像:

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

所以ENTRYPOINT的意义是可以获取docker run xxxx 后面的参数,而不是后面的参数直接覆盖指令
指令:VOLUME 
功能描述:设置容器的挂载点 

可实现挂载功能,可以将本地文件夹或者其他容器中的文件夹挂载到这个容器中:

格式为:

VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
如下几种写法都是正确的:

VOLUME ["/var/log/"]
VOLUME /var/log
指令:USER
格式:USER <用户名>[:<用户组>]
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。
注意:

上一个镜像的用户对下一个镜像有效

但对下一个镜像的COPY命令无效,需要提前创建相关目录

设置启动容器的用户,可以是用户名或UID,所以,只有下面的两种写法是正确的

USER daemo

USER UID

注意:如果设置了容器以daemon用户去运行,那么RUN, CMD 和 ENTRYPOINT 都会以这个用户去运行
指令:WORKDIR
格式:WORKDIR <工作目录路径>

之前提到一些初学者常犯的错误是把 Dockerfile 等同于 Shell 脚本来书写,这种错误的理解还可能会导致出现下面这样的错误:

RUN cd /app
RUN echo "hello" > world.txt
如果将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /app/world.txt 文件,或者其内容不是 hello。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。这就是对 Dockerfile 构建分层存储的概念不了解所导致的错误。

之前说过每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN cd /app 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。

因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。

语法:

WORKDIR /path/to/workdir

设置工作目录,对RUN,CMD,ENTRYPOINT,COPY,ADD生效。如果不存在则会创建,也可以设置多次。
指令:ARG
格式:ARG <name>[=<default value>]
ARG是唯一可以在FROM之前执行的参数
ARG参数与ENV效果一样,但ARG参数只在镜像构建阶段存在,容器运行时看不到该环境变量
指令:ONBUILD
格式:ONBUILD 其他指令
用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这是执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
实战apache
FROM centos
MAINTAINER xxxxxxx
ENV TZ "Asia/Shanghai"
#安装依赖
RUN yum -y update && \
yum install -y gcc gcc-c++ tcl && \
yum clean all
#安装http
RUN yum install -y httpd && \
mkdir /home/www && \
sed -i 's#DocumentRoot \"/var/www/html\"#DocumentRoot "/home/www"#g' /etc/httpd/conf/httpd.conf && \
sed -i 's/AllowOverride none/AllowOverride ALL/g' /etc/httpd/conf/httpd.conf && \
sed -i 's/DirectoryIndex index.html/DirectoryIndex index.html index.htm index.php/g' /etc/httpd/conf/httpd.conf && \
sed -i '285a AddType application/x-httpd-php .php' /etc/httpd/conf/httpd.conf && \
sed -i 's#<Directory "/var/www/html">#<Directory "/home/www">#g' /etc/httpd/conf/httpd.conf && \
sed -i 's/AllowOverride None/AllowOverride ALL/g' /etc/httpd/conf/httpd.conf

ADD start.sh /usr/local/sbin/start.sh
RUN chmod 755 /usr/local/sbin/start.sh
RUN cd /home/www && \
touch index.html && \
echo "apache 镜像构建成功" >> /home/www/index.html

EXPOSE 80 443
CMD ["/usr/local/sbin/start.sh"]

 

docker 容器使用 systemctl 命令是报错:https://www.cnblogs.com/infoo/p/11900607.html

解决方法:

      docker run -itd   --privileged --name myCentos centos /usr/sbin/init

      创建完成后: 请使用以下命令进入容器

      docker exec -it myCentos /bin/bash

      加粗的内容要特别注意,不能遗忘

      原因就是: 默认情况下,在第一步执行的是 /bin/bash,而因为docker中的bug,无法使用systemctl 

                      所以我们使用了 /usr/sbin/init 同时 --privileged 这样就能够使用systemctl了,但覆盖了默认的 /bin/bash

       因此我们如果想进入容器 就不能再使用 docker attach myCentos 

        而只能使用  docker exec -it myCentos /bin/bash  因为 exec 可以让我们执行被覆盖掉的默认命令 /bin/bash 

        同时 -it 也是必须的。

       对于ubuntu 也类似,可能init 目录不太相同

当然具体什么bug 我是不知道的,期望以后会会修复

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值