title: DockerFile 编译语法详解(5)
date: 2018-12-16 16:53:20
tags:
- Docker
categories: Docker
copyright: true
---
Docker是基于Go语言实现的开源容器项目,Docker让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何接口,Docker诞生于2013年年初,最初发起者是dotCloud公司.Docker自开源后受到广泛的关注和讨论,目前已有多个相关项目(包括Docker三剑客、Kubernetes等),逐渐形成了围绕Docker容器的生态体系,由于Docker在业界造成的影响力实在太大,dotCloud公司后来也直接改名为Docker Inc,并专注于Docker相关技术和产品的开发.
Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile来快速创建自定义的镜像,本小结首先介绍Dockerfile典型的基本结构和它支持的众多指令,并具体讲解通过这些指令来编写定制镜像的Dockerfile,以及如何生成镜像.最后介绍使用Dockerfile的一些最佳实践经验.
DockerFile基本结构
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行,一般而言,Dockerfile分为四部分.基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令.例如下面的一个小例子.
# This Dockerfile uses the ubuntu image
FROM ubuntu:lastest
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
其中,一开始必须指明所基于的镜像名称,接下来一般是说明维护者信息.后面则是镜像操作指令,例如RUN指令,RUN指令将对镜像执行跟随的命令.每运行一条RUN指令,镜像就添加新的一层,并提交.最后是CMD指令,用来指定运行容器时的操作命令.
实例1: 在debian:latest
基础镜像基础上安装Nginx环境,从而创建一个新的nginx镜像.
FROM debian:latest
MAINTAINER NGINX Docker Maintainers "docker-maint@nginx.com"
ENV NGINX_VERSION 1.10.1-1~jessie
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC64107
9A6ABABF5BD827BD9BF62 \
&& echo "deb http://nginx.org/packages/debian/ jessie nginx" >> /etc/
apt/sources.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
ca-certificates \
nginx=${NGINX_VERSION} \
nginx-module-xslt \
nginx-module-geoip \
nginx-module-image-filter \
nginx-module-perl \
nginx-module-njs \
gettext-base \
&& rm -rf /var/lib/apt/lists/*
# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
实例2: 基于buildpack-deps:latest
基础镜像,安装Golang相关环境,制作一个GO语言的运行环境镜像.
FROM buildpack-deps:lastest
# gcc for cgo
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc \
libc6-dev \
make \
&& rm -rf /var/lib/apt/lists/*
ENV GOLANG_VERSION 1.6.3
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
ENV GOLANG_DOWNLOAD_SHA256 cdde5e08530c0579255d6153b08fdb3b8e47caabbe717bc7bcd
7561275a87aeb
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
&& echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
&& tar -C /usr/local -xzf golang.tar.gz \
&& rm golang.tar.gz
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR $GOPATH
COPY go-wrapper /usr/local/bin/
DockerFile命令详解
指令的一般格式为INSTRUCTION arguments
指令包括FROM、MAINTAINER、RUN等,参见下表.
指 令 | 指 令 说 明 |
---|---|
FROM | 创建镜像的基础镜像 |
MAINTAINER | 维护者信息(说明) |
RUN | 运行命令,安装软件用 |
CMD | 启动容器时默认执行的命令 |
LABEL | 指生成镜像的元数据标签信息 |
EXPOSE | 声明镜像内服务所监听的端口 |
ENV | 声明环境变量 |
ADD | 复制指令,将拷贝到容器中的 |
COPY(推荐) | 复制指令,将拷贝到容器中的 |
ENTRYPOINT | 指定镜像的默认入口 |
VOLUME | 创建数据卷挂载点 |
USER | 指定运行容器时的用户名或UID |
WORKDIR | 配置工作目录 |
ARG | 指定镜像内使用的参数 |
ONBUILD | 配置当所创建镜像作为其他镜像基础时,所执行的命令 |
STOPSIGNAL | 容器退出的信号值 |
HEALTHCHECK | 如何进行健康检查 |
SHELL | 指定使用SHELL时的默认shell类型 |
接下来,我将详细介绍几个常用命令的参数的详细说明信息.
FROM:(指定基础镜像的名称)
构建指令,必须指定且需要在Dockerfile其他指令的前面.后续的指令都依赖于该指令指定的image,FROM指令指定的基础image可以是官方远程仓库中的,也可以位于本地仓库.
example:
FROM centos:latest
FROM ubuntu:14.04
MAINTAINER:(指定镜像创建者信息)
构建指令,用于将image的制作者相关的信息写入到image中,当我们对该image执行docker inspect命令时,输出中有相应的字段记录该信息.
example:
MAINTAINER LyShark "www.mkdirs.com"
RUN:(运行命令,安装软件用)
RUN指令是用来执行命令行命令的,由于命令行的强大能力,RUN指令在定制镜像时是最常用的指令之一.
设置指令,用于container启动时指定的操作.该操作可以是执行自定义脚本,也可以是执行系统命令.该指令只能在文件中存在一次,如果有多个,则只执行最后一条.
FROM centos:latest
RUN yum 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
Dockerfile 中每一个指令都会建立一层,RUN也不例外.每一个RUN的行为,就和刚才我们手工建立镜像的过程一样,新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像.
而上面的这种写法,创建了 6 层镜像.这是完全没有意义的,不仅仅增加了构建部署的时间,也很容易出错,这是很多初学 Docker 的人常犯的一个错误,Union FS 是有最大层数限制的,比如 AUFS曾经是最大不得超过 42 层,现在是不得超过127 层.
上面的 Dockerfile 正确的写法应该是这样:
FROM centos:latest
RUN buildDeps='gcc libc6-dev make' \
&& 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
CMD:(设置容器启动时执行的操作)
设置指令,用于容器启动时指定的操作,该操作可以是执行自定义脚本,也可以是执行系统命令,该指令只能在文件中存在一次,如果有多个,则只执行最后一条.
example:
CMD echo "Hello, World!"
ENTRYPOINT:(设置容器启动时执行的操作)
设置指令,指定容器启动时执行的命令,可以多次设置,但是只有最后一个有效.
example:
ENTRYPOINT ls -l
该指令的使用分为两种情况,一种是独自使用,另一种和CMD指令配合使用.当独自使用时,如果你还使用了CMD命令且CMD是一个完整的可执行的命令,那么CMD指令和ENTRYPOINT会互相覆盖只有最后一个CMD或者ENTRYPOINT有效.如下命令:CMD指令将不会被执行,只有ENTRYPOINT指令被执行
example:
CMD echo "Hello, World!"
ENTRYPOINT ls -l
另一种用法和CMD指令配合使用来指定ENTRYPOINT的默认参数,这时CMD指令不是一个完整的可执行命令,仅仅是参数部分:ENTRYPOINT指令只能使用JSON方式指定执行命令,而不能指定参数.
example:
FROM centos:latest
CMD ["-l"]
ENTRYPOINT ["/usr/bin/ls"]
USER:(设置container容器的用户)
设置指令,设置启动容器的用户,默认是root用户.或者说以那个身份的用户运行容器,如下所示运行memcached,并以daemon用户运行.
example:
USER daemon = ENTRYPOINT ["memcached", "-u", "daemon"]
EXPOSE:(指定容器需要映射到宿主机器的端口)
设置指令,该指令会将容器中的端口映射成宿主机器中的某个端口.当你需要访问容器的时候,可以不是用容器的IP地址而是使用宿主机器的IP地址和映射后的端口.要完成整个操作需要两个步骤,首先在Dockerfile使用EXPOSE设置需要映射的容器端口,然后在运行容器的时候指定-p选项加上EXPOSE设置的端口,这样EXPOSE设置的端口号会被随机映射成宿主机器中的一个端口号.也可以指定需要映射到宿主机器的那个端口,这时要确保宿主机器上的端口号没有被使用.EXPOSE指令可以一次设置多个端口号,相应的运行容器的时候,可以配套的多次使用-p选项.
example:
映射一个端口
EXPOSE 22
相应的运行容器使用的命令
docker run -p port1 image
映射多个端口
EXPOSE port1 port2 port3
相应的运行容器使用的命令
docker run -p port1 -p port2 -p port3 image
还可以指定需要映射到宿主机器上的某个端口号
docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image
ENV:(用于设置环境变量)
构建指令,在image中设置一个环境变量.设置了后,后续的RUN命令都可以使用,container启动后,可以通过docker inspect查看这个环境变量,也可以通过在docker run --env key=value
时设置或修改环境变量.假如你安装了JAVA程序,需要设置JAVA_HOME,那么可以在Dockerfile中这样写:
example:
ENV JAVA_HOME /path/to/java/dirent
ADD:(从src复制文件到容器的dest路径)
example:
ADD <src> <dest>
<src> 是相对被构建的源目录的相对路径,可以是文件或目录的路径,也可以是一个远程的文件url
<dest> 是容器中的绝对路径
COPY:(从src复制文件到容器的dest路径)
example:
COPY <src> <dest>
VOLUME:(指定挂载点)
设置指令,使容器中的一个目录具有持久化存储数据的功能,该目录可以被容器本身使用,也可以共享给其他容器使用.我们知道容器使用的是AUFS这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失.当容器中的应用有持久化数据的需求时可以在Dockerfile中使用该指令.
examp:
FROM base
VOLUME ["/tmp/data"]
WORKDIR:(切换目录)
设置指令,可以多次切换(相当于cd命令),对RUN,CMD,ENTRYPOINT生效.
example:
WORKDIR /p1 WORKDIR p2 RUN vim a.txt
ONBUILD:(在子镜像中执行)
ONBUILD指定的命令在构建镜像时并不执行,而是在它的子镜像中执行.
example:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
好了关于编译命令还有很多,这里就不一一列举了,更多指令操作语法请自行百度,下面我们来看使用DockerFile编译构建一些好玩的东西吧,相信看完下面的小例子,你就能丰衣足食了.
构建Apache镜像
Apache是一个高稳定性的、商业级别的开源Web服务器.目前Apache已经是世界使用排名第一的Web服务器软件,由于其良好的跨平台和安全性,Apache被广泛应用在多种平台和操作系统上.作为Apache软件基金会支持的项目,它的开发者社区完善而高效.自1995年发布至今,一直以高标准进行维护与开发.Apache名称源自美国的西南部一个印第安人部落:阿帕奇族,它支持类UNIX和Windows系统.
1.首先我们要解决Docker容器内不得网络问题.修改DockerDNS,默认没有文件自行创建即可.
[root@localhost ~]# vim /etc/default/docker
docker_OPTS="--dns 8.8.8.8 --dns 114.114.114.114"
[root@localhost ~]# systemctl restart docker
2.接着在当前目录创建一个Dockerfile文件,和一个index.html文件,文件内容如下.
[root@localhost ~]# vim DockerFile
FROM centos:latest
MAINTAINER email@email.com
RUN yum install -y -q apr apr-util httpd
COPY ./index.html /var/www/html/
EXPOSE 80
ENTRYPOINT apachectl start && tail -f /var/log/httpd/access_log
3.使用docker build
命令创建centos:httpd
镜像,注意命令最后的"."表示当前目录.
[root@localhost ~]# docker build -t centos:httpd .
Sending build context to Docker daemon 18.94kB
Step 1/6 : FROM centos:latest
....省略....
Successfully built 65d0de3819df
Successfully tagged centos:httpd
4.下面开始使用run
指令测试镜像,可以使用-P
参数映射需要开放的端口(22和80端口)
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos httpd 65d0de3819df 36 seconds ago 305MB
centos latest 1e1148e4cc2c 11 days ago 202MB
[root@localhost ~]# docker run -itd -p 80:80 centos:httpd
构建Nginx镜像
Nginx是一款功能强大的开源反向代理服务器,支持HTTP、HTTPS、SMTP、POP3、IMAP等协议.它也可以作为负载均衡器、HTTP缓存或Web服务器.Nginx一开始就专注于高并发和高性能的应用场景,它使用类BSD开源协议,支持Linux、BSD、Mac、Solaris、AIX等类Unix系统,同时也有Windows上的移植版本.
1.首先我们要解决Docker容器内不得网络问题.修改DockerDNS,默认没有文件自行创建即可.
[root@localhost ~]# vim /etc/default/docker
docker_OPTS="--dns 8.8.8.8 --dns 114.114.114.114"
[root@localhost ~]# systemctl restart docker
2.接着在当前目录创建一个Dockerfile文件,和一个index.html文件,文件内容如下.
[root@localhost ~]# vim DockerFile
FROM centos:latest
MAINTAINER email@email.com
RUN yum install -y epel-release
RUN yum install -y gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel
RUN rpm -i http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y nginx
EXPOSE 80
ENTRYPOINT nginx && tail -f /var/log/nginx/access.log #tail必须加,否则容器瞬间终止
3.开始通过dockerfile编译生成nginx:centos
镜像文件.
[root@localhost ~]# docker build -t nginx:centos .
Sending build context to Docker daemon 18.43kB
Step 1/8 : FROM centos:latest
....省略....
Successfully built 956a361043bc
Successfully tagged nginx:centos
4.查看生成的镜像文件,并运行这个镜像测试一下吧.
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx centos 956a361043bc About a minute ago 591MB
centos latest 1e1148e4cc2c 11 days ago 202MB
[root@localhost ~]# docker run --name nginx -p 80:80 -d nginx:centos
[root@localhost ~]# curl 127.0.0.1
构建Tomcat镜像
Tomcat是由Apache软件基金会下属的Jakarta项目开发的一个Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和Java Server Page(JSP)的支持.同时,它提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等.由于Tomcat本身也内含了一个HTTP服务器,也可以当作一个单独的Web服务器来使用.下面介绍如何定制Tomcat镜像.
1.首先准备好原材料,Tomcat,jdk环境.
[root@localhost ~]# ls -lh
total 100M
-rw-r--r-- 1 root root 92M Dec 16 23:21 jdk.tar.gz
-rw-r--r-- 1 root root 7.6M Dec 16 23:21 tomcat.tar.gz
2.编写这个构建模板文件,如下内容.
[root@localhost ~]# vim Dockerfile
FROM centos:latest
MAINTAINER email@email.com
ADD ./tomcat.tar.gz /root
ADD ./jdk.tar.gz /root
ENV JAVA_HOME /root/jdk1.7.0_25
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 8080
ENTRYPOINT /root/apache-tomcat-7.0.42/bin/startup.sh && tail -F /root/apache-tomcat-7.0.42/logs/catalina.out
3.使用docker build
命令创建centos:tomcat
镜像,注意命令最后的"."表示当前目录.
[root@localhost ~]# docker build -t centos:tomcat .
Sending build context to Docker daemon 104.3MB
Step 1/8 : FROM centos:lastest
....省略....
Successfully built feac1f1c6ed4
Successfully tagged centos:tomcat
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos tomcat 65d0de3819df 36 seconds ago 405MB
centos latest 1e1148e4cc2c 11 days ago 202MB
4.下面开始使用run
指令测试镜像,可以使用-P
参数映射需要开放的端口(22和80端口)
[root@localhost ~]# docker run --name tomcat -p 80:8080 -d centos:tomcat
[root@localhost ~]# curl 127.0.0.1:80
[root@localhost ~]# docker save 镜像ID > /home/xxx.tar