第五部分、Dockerfile

一、Dockerfile 簡介

Docker容器化技術做爲DevOps中的一個重要組成部分,具體表如今開發、測試、生產環境的統一這一大特色;php

實際上應用上線(應用構建和部署)用Docker實現時,就是基於某個運行環境或者操做系統的 Image 作一些配置調整,生成新的 Image,而後基於此 Image 運行一個 Container;html

你能夠 pull 一個基礎鏡像,而後基於此鏡像運行一個容器,進入此容器安裝一些軟件或者進行一些相應的環境配置,而後經過 docker commit 命令來基於這個容器建立一個新的鏡像;可是這樣操做有一些弊端,好比,其餘人員不知道你這個鏡像具體怎麼建立的,安全性、鏡像建立過程、鏡像內容不夠透明,因此,咱們通常基於 Dockerfile 來建立新鏡像文件。mysql

Dockerfile 中存放一條條指令,用於指定其基礎鏡像( image 是隻讀的,負責應用的存儲、分發,而 Container 則是經過 image 建立的可讀寫的層,建立新的鏡像的時候,須要運行一個 Container,事後會刪掉這個臨時運行的 Container)、建立者信息、須要執行的命令、啓動時的指令等信息;當你的 Dockerfile 建立好了之後,就能夠經過 docker build 命令來構建一個新的 image。linux

二、Dockerfile 指令

Dockerfile經常使用指令nginx

類型 命令
基礎鏡像信息 FROM
維護者信息 MAINTAINER
鏡像操做指令 RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等
容器啓動時執行指令 CMD、ENTRYPOINT

2.一、FROM

格式爲 FROM <image>FROM <image>:<tag>。第一條指令必須爲 FROM 指令git

2.二、MAINTAINER

建立鏡像的用戶信息,如:MAINTAINER docker_user docker_user@email.comgithub

LABEL 指令是此指令的更爲靈活的版本,你應該使用它,由於它能夠設置所需的任何元數據,而且可使用 docker inspect 查看相關內容。要設置與MAINTAINER字段相對應的標籤,可使用:LABEL maintainer="docker_user docker_user@email.com",一個 Dockerfile 能夠有多個 LABLE 標籤golang

2.三、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.四、CMD

指定啓動容器時執行的命令,每一個 Dockerfile 只能有一條 CMD 命令,若是指定了多條命令,只有最後一條會被執行。sql

若是用戶啓動容器時候指定了運行的命令,則會覆蓋掉 CMD 指定的命令。docker

支持三種格式

  1. CMD ["executable","param1","param2"] 使用 exec 執行,推薦方式;
  2. CMD command param1 param2/bin/sh 中執行,提供給須要交互的應用;
  3. CMD ["param1","param2"] 提供給 ENTRYPOINT 的默認參數;

2.五、EXPOSE

EXPOSE指令通知Docker運行時容器在指定的網絡端口上進行偵聽,你能夠指定端口是偵聽TCP仍是UDP,若是未指定協議,則默認值爲TCP。

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

例子:EXPOSE 80/tcpEXPOSE 80/udpEXPOSE 80

EXPOSE指令實際上不會開放端口,它充當構建映像和運行容器之間的一種文檔類型,實際開放哪些端口,要在運行容器時,在 docker run 後面跟上 -p 參數開放並映射一個或多個端口,或使用 -P 開放並映射到隨機端口。

2.六、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.七、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.八、ADD

ADD 指令和 COPY 的使用格式一致(一樣需求下,官方推薦使用 COPY),功能也相似,不一樣之處以下:

ADD 的優勢:在執行 <源文件> 爲 tar 壓縮文件的話,壓縮格式爲 gzip, bzip2 以及 xz 的狀況下,會自動複製並解壓到 <目標路徑>。

ADD 的缺點:在不解壓的前提下,沒法複製 tar 壓縮文件。會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。具體是否使用,能夠根據是否須要自動解壓來決定。

2.九、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"]  # 變參

一、不傳參運行:$ docker run nginx:test

容器內會默認運行如下命令,啓動主進程。

nginx -c /etc/nginx/nginx.conf

二、傳參運行:$ docker run nginx:test -c /etc/nginx/new.conf

容器內會默認運行如下命令,啓動主進程(/etc/nginx/new.conf:假設容器內已有此文件)

nginx -c /etc/nginx/new.conf

2.十、VOLUME

定義數據卷,在啓動容器時忘記掛載數據卷,會自動掛載到此處定義的數據卷。

做用:

  • 避免重要的數據,因容器重啓而丟失,這是很是致命的。
  • 避免容器不斷變大。

格式:

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

VOLUME <路徑>

在啓動容器 docker run 的時候,咱們能夠經過 -v 參數修改掛載點。

2.十一、USER

用於指定執行後續命令的用戶和用戶組,這邊只是切換後續命令執行的用戶(用戶和用戶組必須提早已經存在)。

格式:USER <用戶名>[:<用戶組>]

例子:USER jack

2.十二、WORKDIR

指定工做目錄,格式爲 WORKDIR /path/to/workdir

爲後續的 RUNCMDENTRYPOINT 指令配置工做目錄。

可使用多個 WORKDIR 指令,後續命令若是參數是相對路徑,則會基於以前命令指定的路徑。例如

# 最終路徑爲 /a/b/c
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

三、Dockerfile 編寫建議

3.一、多階段構建

本節內容徹底抄自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語言編譯後不須要運行時,也就不須要安裝任何的運行庫。FR OM 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.二、其餘建議

  • 不要安裝沒必要要的軟件包
  • 考慮應用解耦
  • 在較舊的Docker版本中,務必最小化鏡像中的層數以確保其性能
  • 對於特別多的參數的指令,可使用 「 \」 來進行換行

3.三、各個指令的建議

  • FROM:使用官方鏡像,儘可能使用較小的鏡像
  • LABLE:能夠有多行 LABLE,也能夠單行 LABLE 中多個標籤
  • RUN:複雜的語句以「 \」分割成多行,易讀

四、構建 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

五、官方 Dockerfile 參考

5.一、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.二、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/

相關文章
相關標籤/搜索