dockerfile arg_「系统架构」如何使用Dockerfile制作Docker容器?(2)

b826cc3b963b0634c536b1404b21c3db.png

接着文章「系统架构」如何使用Dockerfile制作Docker容器?(1)我们继续介绍ENV、ARG、VOLUME、EXPOSE、WORKDIR、USER、HEALTHCHECK、ONBUILD几个命令。

7、ENV

这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。

它的格式有两种:

  • ENV
  • ENV ==...
ENV NODE_VERSION 7.2.0RUN 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

ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD等命令都支持环境变量展开。

8、ARG

和ENV命令一样,ARG命令也是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。

ARG的格式为:ARG [=]

Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg = 来覆盖。

在 1.13 之前的版本,要求 --build-arg 中的参数名,必须在 Dockerfile 中用 ARG 定义过了,换句话说,就是 --build-arg 指定的参数,必须在 Dockerfile 中使用了。如果对应参数没有被使用,则会报错退出构建。从 1.13 开始,这种严格的限制被放开,不再报错退出,而是显示警告信息,并继续构建。这对于使用 CI 系统,用同样的构建流程构建不同的 Dockerfile 的时候比较有帮助,避免构建命令必须根据每个 Dockerfile 的内容修改。

9、VOLUME

此命令用来定义匿名卷。之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。

为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

它的格式为:

  • VOLUME ["", ""...]
  • VOLUME
VOLUME /data

这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如:

docker run -d -v mydata:/data xxxx

在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

10、EXPOSE

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

要将 EXPOSE 和在运行时使用 -p : 区分开来。-p是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。

11、WORKDIR

WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

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

RUN cd /appRUN echo "hello" > world.txt

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

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

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

它的格式为 WORKDIR 。

12、USER

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN、CMD 以及 ENTRYPOINT 这类命令的身份。

当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。

RUN groupadd -r redis && useradd -r -g redis redisUSER redisRUN [ "redis-server" ]

如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu。

# 建立 redis 用户,并使用 gosu 换另一个用户执行命令RUN groupadd -r redis && useradd -r -g redis redis# 下载 gosuRUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64"     && chmod +x /usr/local/bin/gosu     && gosu nobody true# 设置 CMD,并以另外的用户执行CMD [ "exec", "gosu", "redis", "redis-server" ]

它的使用格式为:

格式:USER [:]

13、HEALTHCHECK

HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。

在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。而自 1.12 之后,Docker 提供了 HEALTHCHECK 指令,通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。

当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy。

HEALTHCHECK 支持下列选项:

  • --interval=:两次健康检查的间隔,默认为 30 秒;
  • --timeout=:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
  • --retries=:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。

和 CMD, ENTRYPOINT 一样,HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

它的使用格式为:

  • HEALTHCHECK [选项] CMD :设置检查容器健康状况的命令
  • HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否在正常工作,我们可以用 curl 来帮助判断,其 Dockerfile 的 HEALTHCHECK 可以这么写:

FROM nginxRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*HEALTHCHECK --interval=5s --timeout=3s   CMD curl -fs http://localhost/ || exit 1

这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。

14、ONBUILD

ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。

好了,命令介绍完了,下面以使用Dockerfile构建lnmp容器的过程,我们再来具体介绍下Dockerfile吧。

如下是Dockerfile的构建目录,这里我们先提前下载好了源码包:

dockerfile/├── nginx│   ├── Dockerfile│   ├── nginx-1.12.1.tar.gz│   └── nginx.conf└── php    ├── Dockerfile    ├── php-5.6.31.tar.gz    └── php.ini

nginx、php里面分别存放Dockerfile文件、源码包。nginx目录下还放了nginx.conf配置文件,php目录下也放置了php.ini配置文件。

有些人可能会问为什么要把nginx.conf、php.ini配置文件放到这里,有两个原因,其一,把这两个默认的配置文件放在这里可以提前修改好所需要的参数,当容器启动后,就不需要在进入容器去修改了。当然,我这里只是练习环境,并未对这两个文件做任何更改。其二,在实际环境中,这两个文件是经常需要修改的,单独拿出来后在启动容器时你可以把这两个文件mount到容器中,便于管理。

一、Nginx构建

FROM centos:7 // 以 centos:7 镜像为基础镜像MAINTAINER yuanma // 指定镜像制作者ENV TIME_ZOME Asia/Shanghai // 设置时区环境变量RUN yum -y install gcc gcc-c++ make openssl-devel pcre-devel // 使用 RUN 命令下载编译相关工具,为后面的编译做准备ADD nginx-1.12.1.tar.gz /tmp // ADD 可以自动解压文件,并将解压后的文件 COPY 到指定目录RUN cd /tmp/nginx-1.12.1 &&         ./configure --prefix=/usr/local/nginx &&         make &&         make install // 进入到 nginx 临时安装目录,然后执行编译安装RUN rm -rf /tmp/nginx* && yum clean all &&         echo "${TIME_ZOME}" > /etc/timezone &&         ln -sf /usr/share/zoneinfo/${TIME_ZOME} /etc/localtime // 删除编译安装期间产生的临时文件,同时记录时区COPY nginx.conf /usr/local/nginx/conf/ # 转移构建目录中的配置文件到 nginx 配置文件所在目录,以达到修改 nginx 配置文件的目的WORKDIR /usr/local/nginx/ # 指定工作目录,以后每一层的当前目录都是此目录EXPOSE 80 // 说明将对外暴露80端口,但这不代表会自动映射80端口到宿主机CMD ["./sbin/nginx","-g","daemon off;"] // 指定容器启动后,在容器中执行的命令,这里表示容器启动后,就启动nginx。

构建镜像

docker build -t  nginx:demo(镜像名称)

2、PHP构建

FROM centos:7 // 以 centos:7 镜像为基础镜像MAINTAINER yuanma // 指定镜像制作者ENV TIME_ZOME Asia/Shanghai // 设置时区环境变量RUN yum install -y gcc gcc-c++ make gd-devel libxml2-devel libcurl-devel libjpeg-devel libpng-devel openssl-devel # 使用 RUN 命令下载编译相关工具,为后面的编译做准备ADD php-5.6.31.tar.gz /tmp/ // ADD 可以自动解压文件,并将解压后的文件 COPY 到指定目录RUN cd /tmp/php-5.6.31 &&         ./configure --prefix=/usr/local/php         --with-config-file-path=/usr/local/php/etc         --with-mysql --with-mysqli         --with-openssl --with-zlib --with-curl --with-gd         --with-jpeg-dir --with-png-dir --with-iconv         --enable-fpm --enable-zip --enable-mbstring &&         make -j 4 &&         make install // 进入到 php 临时安装目录,然后执行编译安装RUN cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf &&         sed -i 's/127.0.0.1/0.0.0.0/g' /usr/local/php/etc/php-fpm.conf &&         sed -i "21a daemonize=no" /usr/local/php/etc/php-fpm.conf &&         echo "${TIME_ZOME}" > /etc/timezone &&         ln -sf /usr/share/zoneinfo/${TIME_ZOME} /etc/localtime // 修改 php-fpm 配置文件COPY php.ini /usr/local/php/etc/ //  转移构建目录中的配置文件到 PHP 配置文件所在目录,以达到修改 PHP 配置文件的目的RUN rm -rf /tmp/php* && yum clean all // 同时删除安装 PHP 时产生的一些临时文件WORKDIR /usr/local/php/ // 指定工作目录,以后每一层的当前目录都是此目录EXPOSE 9000 // 说明将对外暴露9000端口,但这不代表会自动映射9000端口到宿主机CMD ["./sbin/php-fpm","-c","/usr/local/php/etc/php-fpm.conf"] // 指定容器启动后,在容器中执行的命令,这里表示容器启动后,就启动php-fpm。

构建镜像

docker build -t  php:demo(镜像名称)

3、MySQL构建

FROM centos:7 # 以 centos:7 镜像为基础镜像MAINTAINER yuanma # 指定镜像制作者ENV TIME_ZOME Asia/Shanghai # 设置时区环境变量RUN yum install mysql mysql-serverRUN /etc/init.d/mysqld start &&mysql -e "grant all privileges on *.* to 'root'@'%' identified by 'abc123';" &&mysql -e "grant all privileges on *.* to 'root'@'localhost' identified by 'abc123';"      //root在本地,非本地登录时都使用abc123密码EXPOSE 3306CMD ["mysqld_safe"]

构建镜像

docker build -t  mysql:demo(镜像名称)

4、运行容器

首先创建自定义网络lnmp,然后运行ningx、php这些容器的时候加入到lnmp网络中来

# docker network  create  lnmp

创建php容器

docker run -itd --name lnmp_php --network lnmp --mount type=bind,src=/app/wwwroot,dst=/usr/local/nginx/html php:demo

参数说明:
-itd:   在容器中打开一个伪终端进行交互操作,并在后台运行;
--name: 为容器分配一个名字lnmp_php;
--network:为容器指定一个网络环境为lnmp网络;
--mout: 把宿主机的/app/wwwroot目录挂载到容器的/usr/local/nginx/html目录,挂载也相当于数据持久化;
php:demo:指定刚才构建的php镜像来启动容器;

创建nginx容器

# docker run -itd --name lnmp_nginx --network lnmp -p 80:80 --mount type=bind,src=/app/wwwroot,dst=/usr/local/nginx/html nginx:demo

创建mysql容器

# docker volume  create  mysql-volumedocker run -itd  --name lnmp_mysql --network lnmp -p 3306:3306 --mount src=mysql-volume,dst=/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql --character-set-server=utf8

至此,使用 dockerfile 构建 Docker 容器就完成了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值