1

1、Dockerfile 简介

Docker容器化技术作为DevOps中的一个重要组成部分,具体表现在开发、测试、生产环境的统一这一大特点;

实际上应用上线(应用构建和部署)用Docker实现时,就是基于某个运行环境或者操作系统的 Image 做一些配置调整,生成新的 Image,然后基于此 Image 运行一个 Container;

你可以 pull 一个基础镜像,然后基于此镜像运行一个容器,进入此容器安装一些软件或者进行一些相应的环境配置,然后通过 docker commit 命令来基于这个容器创建一个新的镜像;但是这样操作有一些弊端,比如,其他人员不知道你这个镜像具体怎么创建的,安全性、镜像创建过程、镜像内容不够透明,所以,我们一般基于 Dockerfile 来创建新镜像文件。

Dockerfile 中存放一条条指令,用于指定其基础镜像( image 是只读的,负责应用的存储、分发,而 Container 则是通过 image 创建的可读写的层,创建新的镜像的时候,需要运行一个 Container,过后会删掉这个临时运行的 Container)、创建者信息、需要执行的命令、启动时的指令等信息;当你的 Dockerfile 创建好了以后,就可以通过 docker build 命令来构建一个新的 image。

2、Dockerfile 指令

Dockerfile常用指令

类型 命令
基础镜像信息 FROM
维护者信息 MAINTAINER
镜像操作指令 RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等
容器启动时执行指令 CMD、ENTRYPOINT

2.1、FROM

格式为 FROM <image>FROM <image>:<tag>。第一条指令必须为 FROM 指令

2.2、MAINTAINER

创建镜像的用户信息,如:MAINTAINER docker_user docker_user@email.com

LABEL 指令是此指令的更为灵活的版本,你应该使用它,因为它可以设置所需的任何元数据,并且可以使用 docker inspect 查看相关内容。要设置与MAINTAINER字段相对应的标签,可以使用:LABEL maintainer="docker_user docker_user@email.com",一个 Dockerfile 可以有多个 LABLE 标签

2.3、RUN

  1. shell 格式:RUN <命令行命令> ,等同于,在终端操作的 shell 命令。
  2. exec 格式:RUN ["可执行文件", "参数1", "参数2"],例如 RUN ["./test.php", "dev", "offline"] ,等价于 RUN ./test.php dev offline
提示:当命令较长时可以使用 \ 来换行;Dockerfile 的指令每执行一次都会在 docker 上新建一层,过多无意义的层,会造成镜像膨胀过大,所以在写 RUN 指令时,尽量将多条命令用 && 来链接。(此提示仅针对较旧的Docker版本,新版本请使用多阶段构建,下面有解释多阶段构建的意义)

2.4、CMD

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令,如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。

支持三种格式

  1. CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
  2. CMD command param1 param2/bin/sh 中执行,提供给需要交互的应用;
  3. CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

2.5、EXPOSE

EXPOSE指令通知Docker运行时容器在指定的网络端口上进行侦听,你可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。

格式:EXPOSE <port> [<port>/<protocol>...]

例子:EXPOSE 80/tcpEXPOSE 80/udpEXPOSE 80

EXPOSE指令实际上不会开放端口,它充当构建映像和运行容器之间的一种文档类型,实际开放哪些端口,要在运行容器时,在 docker run 后面跟上 -p 参数开放并映射一个或多个端口,或使用 -P 开放并映射到随机端口。

2.6、ENV

ENV指令将环境变量<key>设置为值<value>,此值将在构建阶段中所有后续指令的环境中使用,并且在许多情况下也可以内联替换。

ENV指令有两种形式:

  1. ENV <key> <value>
  2. ENV <key1>=<value1> <key2>=<value2>...

例子:

  • ENV myName John Doe
  • ENV myName="John Doe" myDog=Rex\ The\ Dog \ myCat=fluffy

2.7、COPY

复制指令,从源目录中复制文件或者目录到容器里指定路径。

格式:

  1. COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
  2. COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
  • <源路径>:源文件或者源目录,支持通配符,通配符规则要满足 Go 的 filepath.Match 规则。
  • <目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建;支持相对路径和绝对路径。

例子:

COPY hom* /mydir/

COPY hom?.txt /mydir/

2.8、ADD

ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY),功能也类似,不同之处如下:

ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

2.9、ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 --entrypoint 选项,此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式:ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,示例:

假设已通过 Dockerfile 构建了 nginx:test 镜像:

FROM nginx
ENTRYPOINT ["nginx",  "-c"]  # 定参
CMD ["/etc/nginx/nginx.conf"]  # 变参

1、不传参运行:$ docker run nginx:test

容器内会默认运行以下命令,启动主进程。

nginx -c /etc/nginx/nginx.conf

2、传参运行:$ docker run nginx:test -c /etc/nginx/new.conf

容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

nginx -c /etc/nginx/new.conf

2.10、VOLUME

定义数据卷,在启动容器时忘记挂载数据卷,会自动挂载到此处定义的数据卷。

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。

格式:

VOLUME ["<路径1>", "<路径2>"...]

VOLUME <路径>

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

2.11、USER

用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

格式:USER <用户名>[:<用户组>]

例子:USER jack

2.12、WORKDIR

指定工作目录,格式为 WORKDIR /path/to/workdir

为后续的 RUNCMDENTRYPOINT 指令配置工作目录。

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

# 最终路径为 /a/b/c
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

3、Dockerfile 编写建议

3.1、多阶段构建

本节内容完全抄自Dockerfile多阶段构建原理和使用场景

Docker 17.05版本以后,新增了Dockerfile多阶段构建。所谓多阶段构建,实际上是允许一个Dockerfile 中出现多个FROM指令。这样做有什么意义呢?

老版本Docker中为什么不支持多个 FROM 指令?

在17.05版本之前的Docker,只允许Dockerfile中出现一个FROM指令,这得从镜像的本质说起。

Docker 有个 “层” 的概念,最主要的文件是 层。

Dockerfile 中,大多数指令会生成一个层,比如下方的两个例子:

# 示例一,foo 镜像的Dockerfile
# 基础镜像中已经存在若干个层了
FROM ubuntu:16.04

# RUN指令会增加一层,在这一层中,安装了 git 软件
RUN apt-get update \  && apt-get install -y --no-install-recommends git \  && apt-get clean \  && rm -rf /var/lib/apt/lists/*
# 示例二,bar 镜像的Dockerfile
FROM foo

# RUN指令会增加一层,在这一层中,安装了 nginx
RUN apt-get update \  && apt-get install -y --no-install-recommends nginx \  && apt-get clean \  && rm -rf /var/lib/apt/lists/*

假设基础镜像ubuntu:16.04已经存在5层,使用第一个Dockerfile打包成镜像 foo,则foo有6层,又使用第二个Dockerfile打包成镜像bar,则bar中有7层。

如果ubuntu:16.04等其他镜像不算,如果系统中只存在 foo 和 bar 两个镜像,那么系统中一共保存了多少层呢?

是7层,并非13层,这是因为,foo和bar共享了6层。层的共享机制可以节约大量的磁盘空间和传输带宽,比如你本地已经有了foo镜像,又从镜像仓库中拉取bar镜像时,只拉取本地所没有的最后一层就可以了,不需要把整个bar镜像连根拉一遍。但是层共享是怎样实现的呢?

原来,Docker镜像的每一层只记录文件变更,在容器启动时,Docker会将镜像的各个层进行计算,最后生成一个文件系统,这个被称为 联合挂载。对此感兴趣的话可以进入了解一下 AUFS。

Docker的各个层是有相关性的,在联合挂载的过程中,系统需要知道在什么样的基础上再增加新的文件。那么这就要求一个Docker镜像只能有一个起始层,只能有一个根。所以,Dockerfile中,就只允许一个FROM指令。因为多个FROM指令会造成多根,则是无法实现的。但为什么 Docker 17.05 版本以后允许 Dockerfile支持多个FROM指令了呢,莫非已经支持了多根?

多个 FROM 指令的意义

多个 FROM 指令并不是为了生成多根的层关系,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃,那么之前的FROM 又有什么意义呢?

每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,但是,能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。

最大的使用场景是将编译环境和运行环境分离,比如,之前我们需要构建一个Go语言程序,那么就需要用到go命令等编译环境,我们的Dockerfile可能是这样的:

# Go语言环境基础镜像
FROM golang:1.10.3

# 将源码拷贝到镜像中
COPY server.go /build/

# 指定工作目录
WORKDIR /build

# 编译镜像时,运行 go build 编译生成 server 程序
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

# 指定容器运行时入口程序 server
ENTRYPOINT ["/build/server"]

基础镜像golang:1.10.3是非常庞大的,因为其中包含了所有的Go语言编译工具和库,而运行时候我们仅仅需要编译后的server程序就行了,不需要编译时的编译工具,最后生成的大体积镜像就是一种浪费。

使用脉冲云的解决办法是将程序编译和镜像打包分开,使用脉冲云的编译构建服务,选择增加构Go语言构建工具,然后在构建步骤中编译。

最后将编译接口拷贝到镜像中就行了,那么Dockerfile的基础镜像并不需要包含Go编译环境:

# 不需要Go语言编译环境
FROM scratch

# 将编译结果拷贝到容器中
COPY server /server

# 指定容器运行时入口程序 server
ENTRYPOINT ["/server"]
提示:scratch是内置关键词,并不是一个真实存在的镜像。FROM scratch会使用一个完全干净的文件系统,不包含任何文件。 因为Go语言编译后不需要运行时,也就不需要安装任何的运行库。FROM scratch可以使得最后生成的镜像最小化,其中只包含了server程序。

在 Docker 17.05版本以后,就有了新的解决方案,直接一个Dockerfile就可以解决:

# 编译阶段
FROM golang:1.10.3
COPY server.go /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

# 运行阶段
FROM scratch

# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=0 /build/server /
ENTRYPOINT ["/server"]

这个 Dockerfile 的玄妙之处就在于 COPY 指令的--from=0参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,0代表第一个阶段。除了使用数字,我们还可以给阶段命名,比如:

# 编译阶段 命名为 builder
FROM golang:1.10.3 as builder

# ... 省略

# 运行阶段
FROM scratch

# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=builder /build/server /

更为强大的是,COPY--from不但可以从前置阶段中拷贝,还可以直接从一个已经存在的镜像中拷贝。比如,

FROM ubuntu:16.04
COPY --from=quay.io/coreos/etcd:v3.3.9 /usr/local/bin/etcd /usr/local/bin/

我们直接将etcd镜像中的程序拷贝到了我们的镜像中,这样,在生成我们的程序镜像时,就不需要源码编译etcd了,直接将官方编译好的程序文件拿过来就行了。

有些程序要么没有apt源,要么apt源中的版本太老,要么干脆只提供源码需要自己编译,使用这些程序时,我们可以方便地使用已经存在的Docker镜像作为我们的基础镜像。但是我们的软件有时候可能需要依赖多个这种文件,我们并不能同时将 nginx 和 etcd 的镜像同时作为我们的基础镜像(不支持多根),这种情况下,使用 COPY--from就非常方便实用了。

3.2、其他建议

  • 不要安装不必要的软件包
  • 考虑应用解耦
  • 在较旧的Docker版本中,务必最小化镜像中的层数以确保其性能
  • 对于特别多的参数的指令,可以使用 “ \” 来进行换行

3.3、各个指令的建议

  • FROM:使用官方镜像,尽量使用较小的镜像
  • LABLE:可以有多行 LABLE,也可以单行 LABLE 中多个标签
  • RUN:复杂的语句以“ \”分割成多行,易读

4、构建 Docker 镜像

Dockerfile 文件内容如下

$ sudo cat Dockerfile
FROM centos
LABEL MAINTAINER="liu"
RUN yum update -y && yum -y install nginx
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]

构建Docker镜像

$ sudo docker build -t my_nginx:1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM centos
 ---> 0f3e07c0138f
Step 2/5 : LABEL MAINTAINER="liu"
 ---> Using cache
 ---> b331b67a50df
Step 3/5 : RUN yum update -y && yum -y install nginx
 ---> Using cache
 ---> 2cb2a7a24e64
Step 4/5 : EXPOSE 80
 ---> Running in a737a3fb5651
Removing intermediate container a737a3fb5651
 ---> d21ac1194380
Step 5/5 : ENTRYPOINT ["nginx", "-g", "daemon off;"]
 ---> Running in 6d9b3a8689cd
Removing intermediate container 6d9b3a8689cd
 ---> 2378b531443c
Successfully built 2378b531443c
Successfully tagged my_nginx:1.0

        运行一个容器,检查我们构建的镜像是否可以正常使用

$ sudo docker run --name t1 -d -p 81:80 my_nginx:1.0
0ed3f35d968ecfa400c976c467487682e22ffde07283d7a698682a829caeaf19
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
0ed3f35d968e        my_nginx:1.0        "nginx -g 'daemon of…"   3 seconds ago       Up 2 seconds        0.0.0.0:81->80/tcp   t1
$ sudo curl 127.0.0.1:81 -I
HTTP/1.1 200 OK
Server: nginx/1.14.1
Date: Thu, 26 Dec 2019 06:02:40 GMT
Content-Type: text/html
Content-Length: 4057
Last-Modified: Mon, 07 Oct 2019 21:16:24 GMT
Connection: keep-alive
ETag: "5d9bab28-fd9"
Accept-Ranges: bytes

5、官方 Dockerfile 参考

5.1、MySQL 8.0 官方 Dockerfile

地址:MySQL 8.0 Dockerfile

FROM debian:stretch-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mysql && useradd -r -g mysql mysql

RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*

# add gosu for easy step-down from root
ENV GOSU_VERSION 1.7
RUN set -x \
    && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
    && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \
    && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \
    && export GNUPGHOME="$(mktemp -d)" \
    && gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
    && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
    && gpgconf --kill all \
    && rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true \
    && apt-get purge -y --auto-remove ca-certificates wget

RUN mkdir /docker-entrypoint-initdb.d

RUN apt-get update && apt-get install -y --no-install-recommends \
# for MYSQL_RANDOM_ROOT_PASSWORD
        pwgen \
# for mysql_ssl_rsa_setup
        openssl \
# FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db:
# File::Basename
# File::Copy
# Sys::Hostname
# Data::Dumper
        perl \
    && rm -rf /var/lib/apt/lists/*

RUN set -ex; \
# gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported
    key='A4A9406876FCBD3C456770C88C718D3B5072E1F5'; \
    export GNUPGHOME="$(mktemp -d)"; \
    gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
    gpg --batch --export "$key" > /etc/apt/trusted.gpg.d/mysql.gpg; \
    gpgconf --kill all; \
    rm -rf "$GNUPGHOME"; \
    apt-key list > /dev/null

ENV MYSQL_MAJOR 8.0
ENV MYSQL_VERSION 8.0.18-1debian9

RUN echo "deb http://repo.mysql.com/apt/debian/ stretch mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list

# the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql)
# also, we set debconf keys to make APT a little quieter
RUN { \
        echo mysql-community-server mysql-community-server/data-dir select ''; \
        echo mysql-community-server mysql-community-server/root-pass password ''; \
        echo mysql-community-server mysql-community-server/re-root-pass password ''; \
        echo mysql-community-server mysql-community-server/remove-test-db select false; \
    } | debconf-set-selections \
    && apt-get update && apt-get install -y mysql-community-client="${MYSQL_VERSION}" mysql-community-server-core="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \
    && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \
    && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
# ensure that /var/run/mysqld (used for socket and lock files) is writable regardless of the UID our mysqld instance ends up having at runtime
    && chmod 777 /var/run/mysqld

VOLUME /var/lib/mysql
# Config files
COPY config/ /etc/mysql/
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306 33060
CMD ["mysqld"]

5.2、WordPress 官方 Dockerfile

FROM php:7.2-apache

# persistent dependencies
RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends \
# Ghostscript is required for rendering PDF previews
        ghostscript \
    ; \
    rm -rf /var/lib/apt/lists/*

# install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions)
RUN set -ex; \
    \
    savedAptMark="$(apt-mark showmanual)"; \
    \
    apt-get update; \
    apt-get install -y --no-install-recommends \
        libfreetype6-dev \
        libjpeg-dev \
        libmagickwand-dev \
        libpng-dev \
    ; \
    \
    docker-php-ext-configure gd --with-freetype-dir=/usr --with-jpeg-dir=/usr --with-png-dir=/usr; \
    docker-php-ext-install -j "$(nproc)" \
        bcmath \
        exif \
        gd \
        mysqli \
        opcache \
        zip \
    ; \
    pecl install imagick-3.4.4; \
    docker-php-ext-enable imagick; \
    \
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
    apt-mark auto '.*' > /dev/null; \
    apt-mark manual $savedAptMark; \
    ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \
        | awk '/=>/ { print $3 }' \
        | sort -u \
        | xargs -r dpkg-query -S \
        | cut -d: -f1 \
        | sort -u \
        | xargs -rt apt-mark manual; \
    \
    apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
    rm -rf /var/lib/apt/lists/*

# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN { \
        echo 'opcache.memory_consumption=128'; \
        echo 'opcache.interned_strings_buffer=8'; \
        echo 'opcache.max_accelerated_files=4000'; \
        echo 'opcache.revalidate_freq=2'; \
        echo 'opcache.fast_shutdown=1'; \
    } > /usr/local/etc/php/conf.d/opcache-recommended.ini
# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging
RUN { \
# https://www.php.net/manual/en/errorfunc.constants.php
# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670
        echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \
        echo 'display_errors = Off'; \
        echo 'display_startup_errors = Off'; \
        echo 'log_errors = On'; \
        echo 'error_log = /dev/stderr'; \
        echo 'log_errors_max_len = 1024'; \
        echo 'ignore_repeated_errors = On'; \
        echo 'ignore_repeated_source = Off'; \
        echo 'html_errors = Off'; \
    } > /usr/local/etc/php/conf.d/error-logging.ini

RUN set -eux; \
    a2enmod rewrite expires; \
    \
# https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html
    a2enmod remoteip; \
    { \
        echo 'RemoteIPHeader X-Forwarded-For'; \
# these IP ranges are reserved for "private" use and should thus *usually* be safe inside Docker
        echo 'RemoteIPTrustedProxy 10.0.0.0/8'; \
        echo 'RemoteIPTrustedProxy 172.16.0.0/12'; \
        echo 'RemoteIPTrustedProxy 192.168.0.0/16'; \
        echo 'RemoteIPTrustedProxy 169.254.0.0/16'; \
        echo 'RemoteIPTrustedProxy 127.0.0.0/8'; \
    } > /etc/apache2/conf-available/remoteip.conf; \
    a2enconf remoteip; \
# https://github.com/docker-library/wordpress/issues/383#issuecomment-507886512
# (replace all instances of "%h" with "%a" in LogFormat)
    find /etc/apache2 -type f -name '*.conf' -exec sed -ri 's/([[:space:]]*LogFormat[[:space:]]+"[^"]*)%h([^"]*")/\1%a\2/g' '{}' +

VOLUME /var/www/html

ENV WORDPRESS_VERSION 5.3.2
ENV WORDPRESS_SHA1 fded476f112dbab14e3b5acddd2bcfa550e7b01b

RUN set -ex; \
    curl -o wordpress.tar.gz -fSL "https://wordpress.org/wordpress-${WORDPRESS_VERSION}.tar.gz"; \
    echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c -; \
# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress
    tar -xzf wordpress.tar.gz -C /usr/src/; \
    rm wordpress.tar.gz; \
    chown -R www-data:www-data /usr/src/wordpress

COPY docker-entrypoint.sh /usr/local/bin/

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]

参考文章:

https://docs.docker.com/engine/reference/builder/

http://www.dockerinfo.net/dockerfile介绍

https://www.runoob.com/docker/docker-dockerfile.html

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/


Liu_wt
35 声望3 粉丝

业余选手,多多指教!