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
- shell 格式:
RUN <命令行命令>
,等同于,在终端操作的 shell 命令。 - exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
,例如RUN ["./test.php", "dev", "offline"]
,等价于RUN ./test.php dev offline
提示:当命令较长时可以使用\
来换行;Dockerfile 的指令每执行一次都会在 docker 上新建一层,过多无意义的层,会造成镜像膨胀过大,所以在写 RUN 指令时,尽量将多条命令用&&
来链接。(此提示仅针对较旧的Docker版本,新版本请使用多阶段构建,下面有解释多阶段构建的意义)
2.4、CMD
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD
命令,如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD
指定的命令。
支持三种格式
-
CMD ["executable","param1","param2"]
使用exec
执行,推荐方式; -
CMD command param1 param2
在/bin/sh
中执行,提供给需要交互的应用; -
CMD ["param1","param2"]
提供给ENTRYPOINT
的默认参数;
2.5、EXPOSE
EXPOSE指令通知Docker运行时容器在指定的网络端口上进行侦听,你可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。
格式:EXPOSE <port> [<port>/<protocol>...]
例子:EXPOSE 80/tcp
、EXPOSE 80/udp
、EXPOSE 80
EXPOSE指令实际上不会开放端口,它充当构建映像和运行容器之间的一种文档类型,实际开放哪些端口,要在运行容器时,在docker run
后面跟上-p
参数开放并映射一个或多个端口,或使用-P
开放并映射到随机端口。
2.6、ENV
ENV指令将环境变量<key>设置为值<value>,此值将在构建阶段中所有后续指令的环境中使用,并且在许多情况下也可以内联替换。
ENV指令有两种形式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
例子:
ENV myName John Doe
ENV myName="John Doe" myDog=Rex\ The\ Dog \ myCat=fluffy
2.7、COPY
复制指令,从源目录中复制文件或者目录到容器里指定路径。
格式:
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
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
。
为后续的 RUN
、CMD
、ENTRYPOINT
指令配置工作目录。
可以使用多个 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
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/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。