一、制作Docker image 有两种方式

1、使用 Docker container,直接构建容器,再导出成 image 使用

2、使用 Dockerfile,将所有动作写在文件中,再 build 成 image。Dockerfile 的方式非常灵活,本文使用Dockerfile

二、dockerfile指令介绍

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

#指定基础镜像 
FROM   
Dockerfile中必备指令,并且必须是第一条指令

FROM scratch   不以任何镜像为基础,接下来的指令将作为镜像第一层开始存在

#指定维护者信息
MAINTAINER
格式: 
MAINTAINER <name>

#执行命令行命令
RUN  
定义每一层该如何构建(不是在写 Shell 脚本)
每一个 RUN = 启动一个容器、执行命令、然后提交存储层文件变更
两行 RUN 命令的执行环境不同

格式:
1) shell 格式: RUN <命令>                                   #类似命令行输入
2) exec  格式: RUN ["可执行文件", "参数1", "参数2"]           #类似函数调用

行尾 \ 换行
行首 # 注释
&& 命令串联

#复制文件
COPY 
格式: 
1) COPY <源路径>... <目标路径> 
2) COPY ["<源路径1>",... "<目标路径>"]

<源路径> 可以是多个,甚至可以是通配符   #上下文路径的相对路径,且不能是url
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)

#更高级的复制文件
ADD
1)ADD <src>... <dest>
2)ADD ["<src>",... "<dest>"]

<源路径> 可以是一个 URL, 如果是gzip , bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去 所有的文件复制均使用COPY 指令,仅在需要自动解压缩的场合使用 ADD
尽量不要把<scr>写成一个文件夹,如果<src>是一个文件夹了,复制整个目录的内容,包括文件系统元数据

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

对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

格式: 
1) shell 格式:  CMD <命令>
2) exec  格式:  CMD ["可执行文件", "参数1", "参数2"...]
一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 " ,而不要使用单引号

CMD echo $HOME 在实际执行中,会将其变更为: CMD [ "sh", "-c", "echo $HOME" ]

#入口点,功能是启动时的默认命令
ENTRYPOINT 

1)ENTRYPOINT ["executable", "param1", "param2"]
2)ENTRYPOINT command param1 param2

和 CMD 一样,都是在指定容器启动程序及参数。实际执行时,将变为: <ENTRYPOINT> "<CMD>" 

#启动时,可再对可执行文件进行传参 ENTRYPOINT ["docker-entrypoint.sh"]

#应用运行前的准备工作,指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本,并且可在镜像启动时候传入参数来服务脚本

ENTRYPOINT和CMD

1. 相同点:

只能写一条,如果写了多条,那么只有最后一条生效

容器启动时才运行,运行时机相同

2. 不同点:

ENTRYPOINT不会被运行的command覆盖,一定会运行,而CMD则会被覆盖

如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数, 如下:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效, 如下:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ls -al

那么将执行ls -al ,top -b不会执行

#设置环境变量 ENV 格式:
1) ENV <key> <value> 
2) ENV <key1>=<value1> <key2>=<value2>...
两者的区别就是第一种是一次设置一个,第二种是一次设置多个

#构建参数
ARG 
和 ENV 所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
格式: 
ARG <参数名>[=<默认值>]

可以在构建命令docker build 中用 --build-arg <参数名>=<值> 来覆盖

#定义匿名卷
VOLUME 

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

格式: 
1) VOLUME <路径>
2) VOLUME ["<路径1>", "<路径2>"...] 

VOLUME /data 
这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层

-v mydata:/data
mydata 这个命名卷挂载到了 /data 这个位置,替代了Dockerfile 中定义的匿名卷的挂载配置

#声明端口
EXPOSE 
声明运行时容器提供服务端口

#指定工作目录,相当于 cd
WORKDIR 
改变以后各层的工作目录
格式:
WORKDIR <工作目录路径> 
设置工作目录,对RUN,CMD,ENTRYPOINT,COPY,ADD生效。如果不存在则会创建,也可以设置多次。 如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为 /a/b/c

WORKDIR也可以解析环境变量, 如:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd的执行结果是/path/$DIRNAME

#指定当前用户 
USER USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层 
USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。 

#健康检查 HEALTHCHECK 
格式: 
1) HEALTHCHECK [选项] CMD <命令> #设置检查容器健康状况的命令 

2) HEALTHCHECK NONE #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令 

HEALTHCHECK 支持下列选项: 

--interval=<间隔> :两次健康检查的间隔,默认为 30 秒; 

--timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒; 

--retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3次。

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

HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1 #为他人做嫁衣裳 

ONBUILD 当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行 
格式: ONBUILD <其它指令> 做一个基础镜像,基础镜像更新,各个项目不用同步 
Dockerfile 的变化,重新构建后就继承了基础镜像的更新

构建镜像

#构建镜像 
docker build [选项] <指定上下文路径/URL/-> 
镜像并非在本地构建,而是在服务端,也就是镜像是在 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢? 
当构建的时候,用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。
这样Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。 
为什么COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。 
例如:COPY ./package.json /app/  是复制 上下文(context) 目录下的package.json   #COPY 这类指令中的源文件的路径都是上下文路径的相对路径 
-f ../Dockerfile.php 参数指定某个文件作为Dockerfile 
其它 docker build 的用法 
直接用 Git repo 进行构建:Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建 
用给定的 tar 压缩包构建:Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建

三、Dockerfile的编写

# 版本信息
FROM centos:7
MAINTAINER wuweixiang <wuweixiang.alex@gmail.com>
# 设置工作目录
WORKDIR /var/
# 添加jdk、tomcat
ADD jdk-8u191-linux-x64.tar.gz .
#ADD http://mirrors.hust.edu.cn/apache/tomcat/tomcat-8/v8.5.35/bin/apache-tomcat-8.5.35.tar.gz .
ADD apache-tomcat-8.5.35.tar.gz .

# 设置环境变量
ENV JAVA_HOME /var/jdk1.8.0_191
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
ENV TIME_ZONE Asia/Shanghai

# 更改时区
RUN set -x \
&& echo "${TIME_ZONE}" > /etc/timezone \
&& ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime

# 开启内部服务端口
EXPOSE 8080

# 启动tomcat服务器
CMD ["/var/apache-tomcat-8.5.35/bin/catalina.sh","run"] && tail -f /var/apache-tomcat-8.5.35/logs/catalina.out

build  nginx

FROM centos:7  #docker  pull centos:7 默认是7.8版本
MAINTAINER www.ctnrs.com
RUN yum install -y gcc gcc-c++ make \
    openssl-devel pcre-devel gd-devel \
    iproute net-tools telnet wget curl && \
    yum clean all && \
    rm -rf /var/cache/yum/*
RUN wget http://nginx.org/download/nginx-1.15.5.tar.gz && \
    tar zxf nginx-1.15.5.tar.gz && \
    cd nginx-1.15.5 && \
    ./configure --prefix=/usr/local/nginx \
    --with-http_ssl_module \
    --with-http_stub_status_module && \
    make -j 4 && make install && \
    rm -rf /usr/local/nginx/html/* && \
    echo "ok" >> /usr/local/nginx/html/status.html && \
    cd / && rm -rf nginx-1.12.2* && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH $PATH:/usr/local/nginx/sbin
#COPY nginx.conf /usr/local/nginx/conf/nginx.conf
WORKDIR /usr/local/nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] 

构建

# docker build -t nginx:v1  -f  Dockerfile-nginx  . (-f 指定dockerfile文件)
# docker build -t nginx:v1 .  (此文件为标准的Dockerfile名不用-f)

#docker  run  --name docker_nginx_v1  -itd  -p 80:80 nginx:v1  

build一个centos

[root@localhost Dockerfile]# pwd
/root/Dockerfile
[root@localhost Dockerfile]# cat Dockerfile 
FROM daocloud.io/centos:7
MAINTAINER "you" <your@email.here>
ENV container docker
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i ==systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

执行构建

#docker build --rm -t centos:v1 .
Successfully built b9fa69e719a8
Successfully tagged centos:v1

构建php

FROM centos:7
MAINTAINER www.ctnrs.com
RUN yum install epel-release -y && \
    yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
    libcurl-devel libjpeg-devel libpng-devel openssl-devel \
    libmcrypt-devel libxslt-devel libtidy-devel autoconf \
    iproute net-tools telnet wget curl && \
    yum clean all && \
    rm -rf /var/cache/yum/*

RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php \
    --with-config-file-path=/usr/local/php/etc \
    --enable-fpm --enable-opcache \
    --with-mysql --with-mysqli --with-pdo-mysql \
    --with-openssl --with-zlib --with-curl --with-gd \
    --with-jpeg-dir --with-png-dir --with-freetype-dir \
    --enable-mbstring --with-mcrypt --enable-hash && \
    make -j 4 && make install && \
    cp php.ini-production /usr/local/php/etc/php.ini && \
    cp sapi/fpm/php-fpm.conf /usr/local/php/etc/php-fpm.conf && \
    sed -i "90a \daemonize = no" /usr/local/php/etc/php-fpm.conf && \
    mkdir /usr/local/php/log && \
    cd / && rm -rf php* && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH $PATH:/usr/local/php/sbin:/usr/local/php/bin
COPY php.ini /usr/local/php/etc/
COPY php-fpm.conf /usr/local/php/etc/
WORKDIR /usr/local/php
EXPOSE 9000
CMD ["php-fpm"]

验证
docker exec -it  4bb bash
[root@4bb85a16488a php]# ls
bin  etc  include  lib  log  php  sbin  var
[root@4bb85a16488a php]# php -v
PHP 5.6.36 (cli) (built: Aug 23 2020 12:01:07) 
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

四、优化

1、一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。 例如:

FROM centos:7
MAINTAINER www.ctnrs.com
RUN yum install epel-release -y 
RUN yum install -y gcc gcc-c++ make -y
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz
RUN tar zxf php-5.6.36.tar.gz
RUN cd php-5.6.36
RUN ./configure --prefix=/usr/local/php 
RUN make -j 4 
RUN make install
EXPOSE 9000
CMD ["php-fpm"]

改为:

FROM centos:7
MAINTAINER www.ctnrs.com
RUN yum install epel-release -y && \
    yum install -y gcc gcc-c++ make

RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php && \
    make -j 4 && make install
EXPOSE 9000
CMD ["php-fpm"]

2、一次RUN形成新的一层,如果没有在同一层删除,无论文件是否最后删除,都会带到下一层,所以要在每一层清理对应的残留数据,减小镜像大小。

FROM centos:7
MAINTAINER www.ctnrs.com
RUN yum install epel-release -y && \
    yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
    libcurl-devel libjpeg-devel libpng-devel openssl-devel \
    libmcrypt-devel libxslt-devel libtidy-devel autoconf \
    iproute net-tools telnet wget curl && \
    yum clean all && \
    rm -rf /var/cache/yum/*

RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php \
    make -j 4 && make install && \
    cd / && rm -rf php*

注意:

1、 docker  build   -t  后面镜像的名字中不能包含大写字母!
2、 docker run –p 8080:80 –d nginx 符号错误 “-” 其实为"-"
       docker run -p 8080:80 -d nginx

3、配置文件里面可能会发生转行,注意检查

4、当前路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

 

 

参考

https://docs.docker.com/engine/reference/builder/   dockerfile官网介绍

https://github.com/zhangguanzhang/docker-need-to-know   不错的学习地址

http://blog.ctnrs.com/post/dockerfile-skills/

https://www.runoob.com/docker/docker-dockerfile.html  菜鸟教程,讲解详细

https://docs.docker.com/engine/reference/builder/#add  docker官方文档

相关文章: