使用Dockerfile构建自己的镜像
Dockerfile是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。使用docker build命令可以根据Dockerfile里面的指令编排来打包定制我们自己的docker镜像,首先我们来看一个通用的例子,制作自己的nginx镜像。
1.构建nginx镜像
# Base image
FROM centos:7
# MAINTAINER
MAINTAINER cbmiao <miaocbin@126.com>
# 将nginx以及pcre源代码加入镜像
ADD nginx-1.20.1.tar.gz /usr/local/src/
ADD pcre-8.45.tar.gz /usr/local/src/
# 安装编译器
RUN yum install -y gcc gcc-c++ make openssl-devel lsof
RUN useradd -s /sbin/nologin -M nginx
# 指定工作目录
WORKDIR /usr/local/src/nginx-1.20.1/
# 编译nginx
RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.45 && make && make install
RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
# 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH
# 暴露80端口
EXPOSE 80
# 容器默认启动命令
ENTRYPOINT ["nginx"]
以上是通过Dockerfile来定制我们自己的nginx的Dockerfile,包含了如下指令:
2. Dockerfile基本格式
2.1 FROM指令
FROM指令用于指定基础镜像,必须为Dockerfile的第一个指令
# 格式:
# FROM <image>
# FROM <image>:<tag>
# 示例:
FROM mysql:5.7
# 注意:
# tag是可选的,如果不使用tag,则默认会使用latest版本的基础镜像
参数解释:
-
FROM mysql:5.7:第一行必须指定 基础镜像信息
2.2 MAINTAINER 指令
用于说明镜像维护者的信息,名字,邮箱,联系方式,实例如下
# 格式:
# MAINTAINER <name>
# 示例:
MAINTAINER Michael Miu
MAINTAINER miaocbin@126.com
MAINTAINER Michael Miu <miaocbin@126.com>
2.3 COPY|ADD指令
这两个命令的用法类似,都可以用于添加本地文件到镜像中(同样需求下,官方推荐使用 COPY),功能也类似,如:
- 源路径可以有多个,相对于执行build的路径,如果源路径是一个目录,则该目录下面的所有内容都会被加入到容器,但目录本身不会被加入到容器;
- 目标路径:必须是绝对路径或者相对于WORKDIR的相对路径;若目标路径不存在,则会自动创建完整路径;目标路径如果是文件夹,则必须已/结尾;
- 路径中可以使用通配符
二者区别如下:
- ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
- ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
- COPY:指令能够将构建命令所在的主机本地的文件或目录,复制到镜像文件系统。
- ADD:指令不仅能够将构建命令所在的主机本地的文件或目录,而且能够将远程URL所对应的文件或目录,作为资源复制到镜像文件系统。
所以,可以认为ADD是增强版的COPY,支持将远程URL的资源加入到镜像的文件系统。
# 格式:
# ADD|COPY <src>... <dest>
# 示例:
ADD|COPY tes* /mydir/ # 添加所有以"tes"开头的文件
ADD|COPY test mydir/ # 添加 "test" 到 WORKDIR/mydir/
ADD|COPY test /opt/ # 添加 "test" 到 /opt/
# [--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
说明:
- 对于从远程URL获取资源的情况,由于ADD指令不支持认证,如果从远程获取资源需要认证,则只能使用RUN wget或RUN curl替代。
- 如果源路径的资源发生变化,则该ADD指令将使Docker Cache失效,Dockerfile中后续的所有指令都不能使用缓存。因此尽量将ADD指令放在Dockerfile的后面。
- <源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。
- <目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
2.4 WORKDIR
指定工作目录,通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。(WORKDIR 指定的工作目录,必须是提前创建好的)
# 格式:
# WORKDIR /path/to/workdir
# 示例:
WORKDIR /abc (这时工作目录为/abc)
2.5 RUN
构建镜像过程中执行命令(注意:RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache)
# 格式:
# RUN <command>
# 示例:
RUN yum -y install nginx
RUN pip install djaong
RUN mkdir test && rm -rf /var/lib/testfile
2.6 CMD
类似于RUN指令,用于运行程序,但二者运行的时间点不同:
- CMD:用于指定在容器启动时所要执行的命令,在docker run 时运行。
- RUN:RUN用于指定镜像构建时所要执行的命令,是在 docker build时运行。
作用:为启动的容器指定默认要运行的程序,也就是在容器启动时才进行调用。程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
# 格式:
# CMD ["executable","param1","param2"] (执行可执行文件,优先)
# CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
# CMD command param1 param2 (执行shell内部命令)
# 示例:
CMD ["/usr/bin/wc","--help"]
CMD ping www.baidu.com
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
2.7 ENTRYPOINT
类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。
# 格式:
# ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
# ENTRYPOINT command param1 param2 (shell内部命令)
# 示例:
ENTRYPOINT ["/usr/bin/wc","--help"]
注意:ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。
2.8 ENV
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
# 格式:
# ENV <key> <value>
# ENV <key>=<value>
# 示例:
ENV myName John
ENV myCat=tomcat
2.9 EXPOSE指令
声明端口,作用如下:
- 1)帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;
- 2)在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口;但是,EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口。
- 3)docker ps的时候看到的PORTS字段信息就是这里声明的信息;
- 4)需要说明的是,即便这里不写EXPOSE指令,也不影响镜像构建和运行,只是会导致其他的使用者不清楚镜像监听的端口,使用的时候映射端口没有那么直观。
# 格式:
# EXPOSE <port> [<port>...]
# 示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
2.10 LABEL指令
用来给镜像添加一些元数据(metadata),以键值对的形式
# 语法格式:
# LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 例如:添加镜像作者
LABEL org.opencontainers.image.authors="runoob"
2.11 VOLUME指令
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
# 格式:
# VOLUME ["<路径1>", "<路径2>"...]
# VOLUME <路径>
# 举例:
VOLUME [/var/lib/mysql]
3. Dockerfile镜像构建命令
利用Dockerfile构建自己的镜像命令
docker build . -t ImageName:ImageTag -f Dockerfile
- . 是上下文路径
-
上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
3.1 执行构建过程
docker build . -t my-nginx:v1.20.1 -f Dockerfile
Sending build context to Docker daemon 3.163MB
Step 1/13 : FROM centos:7
---> 8652b9f0cb4c
Step 2/13 : LABEL maintainer="cbmiao <miaocbin@126.com>"
---> Using cache
---> 16c2487146bc
Step 3/13 : ENV DEBIAN_FRONTEND noninteractive
---> Using cache
---> 5f3132177398
Step 4/13 : ADD nginx-1.20.1.tar.gz /usr/local/src/
---> Using cache
---> 116344a28ea2
Step 5/13 : ADD pcre-8.45.tar.gz /usr/local/src
---> Using cache
---> 23451b87c29b
Step 6/13 : RUN yum install -y wget gcc gcc-c++ make openssl-devel
---> Using cache
---> dcaa1cbaf4bd
Step 7/13 : RUN useradd -s /sbin/nologin -M nginx
---> Using cache
---> e0ace7f20ffc
Step 8/13 : WORKDIR /usr/local/src/nginx-1.20.1/
---> Using cache
---> d437722febe2
Step 9/13 : RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.45 && make && make install
---> Using cache
---> 307b6228d110
Step 10/13 : RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
---> Using cache
---> dc8e0e800987
Step 11/13 : ENV PATH /usr/local/nginx/sbin:$PATH
---> Using cache
---> e5a1119dced2
Step 12/13 : EXPOSE 80
---> Using cache
---> 63c669be20e4
Step 13/13 : ENTRYPOINT ["nginx"]
---> Using cache
---> 391f75b825bd
Successfully built 391f75b825bd
Successfully tagged my-nginx:v1.20.1
3.2 查看构建结果
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my-nginx v1.20.1 391f75b825bd 17 minutes ago 490MB
3.3 运行镜像
# 利用我们刚构建的镜像来启动容器
docker run -d --name nginx-test my-nginx:v1.20.1
348a73f420181c36abf7a99c958ed76862503e930793331ab819629ea2bdc4a2
# 进入容器查看nginx状态
docker exec -ti nginx-test bash
[root@348a73f42018 nginx-1.20.1]#
# 确认80端口
[root@348a73f42018 nginx-1.20.1]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1 root 6u IPv4 645923 0t0 TCP *:http (LISTEN)
# 确认nginx进程已经启动成功
[root@348a73f42018 nginx-1.20.1]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 46096 3280 ? Ss 05:43 0:00 nginx: master process nginx
nginx 7 0.0 0.0 46532 1880 ? S 05:43 0:00 nginx: worker process
root 8 0.2 0.0 11828 1908 pts/0 Ss 05:43 0:00 bash
root 26 0.0 0.0 51732 1712 pts/0 R+ 05:43 0:00 ps aux
# 访问nginx也正常
[root@348a73f42018 nginx-1.20.1]# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
若想让我们的nginx向外提供服务,还需要在启动容器时,将端口映射到主机,然后我们通过主机IP加上映射端口,就可以访问了
docker run -d --name nginx-test1 -p 80:80 my-nginx:v1.20.1
以上就添加了一个[-p 80:80]参数,将容器内的nginx的80端口,映射到宿主机。然后我们就可以通过宿主机的IP加上80端口访问刚才启动的nginx容器里的nginx服务,有关docker启动容器有不清楚的,可以看我的另外一篇博客:Docker简介及基本使用
4. Dockerfile构建镜像的原则
-
不必要的内容不要放在镜像中:以免增大镜像,同时也带来安全隐患;
-
减少不必要的层文件
-
减少网络传输操作:尽量使用本地文件,加快镜像构建速度,特别是后面我们将利用Jenkins做自动化构建的时候;
-
可以适当的包含一些调试命令
5. JAR包制作成docker镜像实例
FROM java:8u211
ENV JAVA_OPTS "\
-Xmx4096m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m"
ENV JAVA_HOME /usr/local/java
ENV PATH ${PATH}:${JAVA_HOME}/bin
COPY target/myweb.jar myweb.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
EXPOSE 8080
CMD java ${JAVA_OPTS} -jar myweb.jar
人们永远没有足够的时间把它做好,但永远有足够的时间重新来过。 可是,因为并不是总有机会重做一遍,你必须做得更好,换句话说, 人们永远没有足够的时间去考虑到底是不是想要它,但永远有足够的时间去为之后悔。 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ 浅掘千口井,不如深挖一口井!当知识支撑不了野心时,那就静下心来学习吧!运维技术交流QQ群:618354452
个人微信公众号,定期发布技术文章和运维感悟。欢迎大家关注交流。