Dockerfile介绍
Dockerfile是一堆指令,在docker build的时候,按照该指令进行操作,最终生成我们期望的镜像
Dockerfile指令
-
FROM 指定基础镜像,必须为第一个命令
格式: FROM <image> FROM <image>:<tag> 示例: FROM mysql:5.7 注意: tag是可选的,如果不使用tag时,会使用latest版本的基础镜像 -
MAINTAINER 镜像维护者的信息
格式: MAINTAINER <name> 示例: MAINTAINER yukikaze MAINTAINER daswa@foxmail.com MAINTAINER yukikaze <daswa@foxmail.com>查看镜像的MAINTAINER
# 通过docker inspect命令查看镜像Author的值。 docker inspect -f {{".Author"}} <镜像ID> -
LABEL 镜像元数据
格式: LABEL <key>=<value> <key>=<value> <key>=<value> ... 示例: LABEL "email"="Securitit@13.com" LABEL email-host="www.host.com" LABEL email-version="1.0" LABEL email-description="This is my \ persional email." LABEL multi.label-1="value-1" multi.label-2="value-2" multi.label-3="value-3" LABEL multi.label-4="value-4" \ multi.label-5="value-5" \ multi.label-6="value-6"- 基础镜像或父镜像中包含的元数据由当前镜像继承。如果元数据已经存在,但具有不同的值,则最近应用的值将覆盖以前设置的任何值。
- 可以使用LABEL maintainer=”xxx”代替MAINTAINER xxx,两者设置的值,在镜像的描述文件中所处位置是不一样的。
- MAINTAINER xxx位于顶层Author属性中。
- LABEL maintainer=”xxx”位于Config.Labels.maintainer属性中,查看镜像的 LABEL
docker inspect -f {{".Config.Labels"}} <镜像ID>
- MAINTAINER已经过时,在新版本已不推荐使用,推荐使用LABEL完成元数据设置。
-
COPY|ADD 添加本地文件到镜像中
格式: COPY <src>... <dest> 示例: ADD hom* /mydir/ # 添加所有以"hom"开头的文件 ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/ ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/ -
WORKDIR 工作目录
格式: WORKDIR /path/to/workdir 示例: WORKDIR /a (这时工作目录为/a) 注意: 通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行 -
RUN 构建镜像过程中执行命令
格式: RUN <command> 示例: RUN yum install nginx RUN pip install django RUN mkdir test && rm -rf /var/lib/unusedfiles 注意: RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache -
CMD 构建容器后调用,也就是在容器启动时才进行调用
格式: CMD ["executable","param1","param2"] (执行可执行文件,优先) CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数) CMD command param1 param2 (执行shell内部命令) 示例: CMD ["/usr/bin/wc","--help"] CMD ping www.baidu.com 注意: CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。 -
ENTRYPOINT 设置容器初始化命令,使其可执行化
格式: ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先) ENTRYPOINT command param1 param2 (shell内部命令) 示例: ENTRYPOINT ["/usr/bin/wc","--help"] 注意: ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令 -
ENV
格式: ENV <key> <value> ENV <key>=<value> 示例: ENV myName John ENV myCat=Tom -
EXPOSE
格式: EXPOSE <port> [<port>...] 示例: EXPOSE 80 443 EXPOSE 8080 EXPOSE 11211/tcp 11211/udp 注意: EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
Dockerfile示例
-
基础环境镜像
FROM java:8-alpine RUN apk add --update ca-certificates && rm -rf /var/cache/apk/* && \ find /usr/share/ca-certificates/mozilla/ -name "*.crt" -exec keytool -import -trustcacerts \ -keystore /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/cacerts -storepass changeit -noprompt \ -file {} -alias {} \; && \ keytool -list -keystore /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/cacerts --storepass changeit ENV MAVEN_VERSION 3.5.4 ENV MAVEN_HOME /usr/lib/mvn ENV PATH $MAVEN_HOME/bin:$PATH RUN wget http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz && \ tar -zxvf apache-maven-$MAVEN_VERSION-bin.tar.gz && \ rm apache-maven-$MAVEN_VERSION-bin.tar.gz && \ mv apache-maven-$MAVEN_VERSION /usr/lib/mvn RUN mkdir -p /usr/src/app WORKDIR /usr/src/app -
前端镜像
FROM nginx:1.19.0-alpine LABEL maintainer="mritd <mritd@linux.com>" ARG TZ='Asia/Shanghai' ENV TZ ${TZ} RUN apk upgrade --update \ && apk add bash tzdata curl wget ca-certificates \ && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ && echo ${TZ} > /etc/timezone \ && rm -rf /usr/share/nginx/html /var/cache/apk/* COPY dist /usr/share/nginx/html EXPOSE 80 443 CMD ["nginx", "-g", "daemon off;"] -
java镜像
FROM java:8u111 ENV JAVA_OPTS "\ -Xmx4096m \ -XX:MetaspaceSize=256m \ -XX:MaxMetaspaceSize=256m" ENV JAVA_HOME /usr/java/jdk ENV PATH ${PATH}:${JAVA_HOME}/bin COPY target/myapp.jar myapp.jar RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone EXPOSE 9000 CMD java ${JAVA_OPTS} -jar myapp.jar -
golang镜像
使用多阶段构建,请查看本文下面出现的《多阶段构建》章节。
1号进程
接下来通过1号进程理解容器的本质
$ docker exec -it nginx:1.19.0-alpine /bin/sh
#/ ps aux
容器启动的时候可以通过命令去覆盖默认的CMD
$ docker run -d --name xxx nginx:alpine <自定义命令>
# <自定义命令>会覆盖镜像中指定的CMD指令,作为容器的1号进程启动。
$ docker run -d --name test-3 nginx:alpine echo 123
$ docker run -d --name test-4 nginx:alpine ping www.baidu.com
本质上讲容器是利用namespace和cgroup等技术在宿主机中创建的独立的虚拟空间,这个空间内的网络、进程、挂载等资源都是隔离的。
$ docker exec -it nginx:1.19.0-alpine /bin/sh
#/ ip addr
#/ ls -l /
#/ apt install xxx
#/ #安装的软件对宿主机和其他容器没有任何影响,和虚拟机不同的是,容器间共享一个内核,所以容器内没法升级内核
多阶构建
java项目
这里先使用java镜像为例,介绍多阶构建的使用。
项目地址:(springboot-app)https://github.com/spring-projects/spring-boot
原始构建:
dockerfile内容如下:
FROM srinivasansekar/javamvn
WORKDIR /opt/springboot-app
COPY . .
RUN mvn clean package -DskipTests=true
CMD [ "sh", "-c", "java -jar /opt/springboot-app/target/sample.jar" ]
镜像构建命令:
$ docker build . -t sample:v1 -f Dockerfile
多阶构建:
FROM maven as builder
WORKDIR /opt/springboot-app
COPY . .
RUN mvn clean package -DskipTests=true
FROM openjdk:8-jdk-alpine
COPY --from=builder /opt/springboot-app/target/sample.jar sample.jar
CMD [ "sh", "-c", "java -jar /sample.jar" ]
镜像构建命令:
$ docker build . -t sample:v2 -f Dockerfile.multi
golang项目
项目地址:(href-counter)[https://gitee.com/agagin/href-counter.git]
原始构建:
dockerfile内容如下:
FROM golang:1.13
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY vendor vendor
COPY app.go .
ENV GOPROXY https://goproxy.cn
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
CMD ["./app"]
镜像构建命令:
docker build . -t href-counter:v1 -f Dockerfile
多阶构建:
dockerfile内容如下:
FROM golang:1.13 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY vendor vendor
COPY app.go .
ENV GOPROXY https://goproxy.cn
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:3.10
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
镜像构建命令:
docker build . -t href-counter:v2 -f Dockerfile.multi
虚悬镜像 dangling image
我们多阶构建完成后,通过 docker image ls (与 docker images相同)查看镜像时会发现一个或多个镜像ID和标签都为 none 关键字的镜像,这称为虚悬镜像(dangling image),也就是我们多阶构建的中间镜像。再构建同名且同tag的镜像的时候,原本的镜像也会变为虚悬镜像。
虚悬镜像,我们并不需要它们,但是它们还占用存储空间。该如何删除呢?可以查找出镜像id进行删除,也可以通过docker命令删除。
- 列出虚悬镜像
docker image ls -f dangling=true - 删除虚悬镜像
docker image prune
多阶构建原则
- 不必要的内容不要放在镜像中
- 减少不必要的层文件
- 减少网络传输操作
- 可以适当的包含一些调试命令