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經常使用指令nginx
類型 | 命令 |
---|---|
基礎鏡像信息 | FROM |
維護者信息 | MAINTAINER |
鏡像操做指令 | RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等 |
容器啓動時執行指令 | CMD、ENTRYPOINT |
格式爲 FROM <image>
或FROM <image>:<tag>
。第一條指令必須爲 FROM
指令git
建立鏡像的用戶信息,如:MAINTAINER docker_user docker_user@email.com
github
LABEL
指令是此指令的更爲靈活的版本,你應該使用它,由於它能夠設置所需的任何元數據,而且可使用 docker inspect
查看相關內容。要設置與MAINTAINER字段相對應的標籤,可使用:LABEL maintainer="docker_user docker_user@email.com"
,一個 Dockerfile 能夠有多個 LABLE
標籤golang
RUN <命令行命令>
,等同於,在終端操做的 shell 命令。RUN ["可執行文件", "參數1", "參數2"]
,例如 RUN ["./test.php", "dev", "offline"]
,等價於 RUN ./test.php dev offline
提示:當命令較長時可使用\
來換行;Dockerfile 的指令每執行一次都會在 docker 上新建一層,過多無心義的層,會形成鏡像膨脹過大,因此在寫 RUN 指令時,儘可能將多條命令用&&
來連接。(此提示僅針對較舊的Docker版本,新版本請使用多階段構建,下面有解釋多階段構建的意義)
指定啓動容器時執行的命令,每一個 Dockerfile 只能有一條 CMD
命令,若是指定了多條命令,只有最後一條會被執行。sql
若是用戶啓動容器時候指定了運行的命令,則會覆蓋掉 CMD
指定的命令。docker
支持三種格式
CMD ["executable","param1","param2"]
使用 exec
執行,推薦方式;CMD command param1 param2
在 /bin/sh
中執行,提供給須要交互的應用;CMD ["param1","param2"]
提供給 ENTRYPOINT
的默認參數;EXPOSE指令通知Docker運行時容器在指定的網絡端口上進行偵聽,你能夠指定端口是偵聽TCP仍是UDP,若是未指定協議,則默認值爲TCP。
格式:EXPOSE <port> [<port>/<protocol>...]
例子:EXPOSE 80/tcp
、EXPOSE 80/udp
、EXPOSE 80
EXPOSE指令實際上不會開放端口,它充當構建映像和運行容器之間的一種文檔類型,實際開放哪些端口,要在運行容器時,在docker run
後面跟上-p
參數開放並映射一個或多個端口,或使用-P
開放並映射到隨機端口。
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
複製指令,從源目錄中複製文件或者目錄到容器裏指定路徑。
格式:
COPY [--chown=<user>:<group>] <源路徑1>... <目標路徑>
COPY [--chown=<user>:<group>] ["<源路徑1>",... "<目標路徑>"]
[--chown=<user>:<group>]:可選參數,用戶改變複製到容器內文件的擁有者和屬組。
例子:
COPY hom* /mydir/
COPY hom?.txt /mydir/
ADD 指令和 COPY 的使用格式一致(一樣需求下,官方推薦使用 COPY),功能也相似,不一樣之處以下:
ADD 的優勢:在執行 <源文件> 爲 tar 壓縮文件的話,壓縮格式爲 gzip, bzip2 以及 xz 的狀況下,會自動複製並解壓到 <目標路徑>。
ADD 的缺點:在不解壓的前提下,沒法複製 tar 壓縮文件。會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。具體是否使用,能夠根據是否須要自動解壓來決定。
相似於 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
定義數據卷,在啓動容器時忘記掛載數據卷,會自動掛載到此處定義的數據卷。
做用:
格式:
VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>
在啓動容器 docker run 的時候,咱們能夠經過 -v 參數修改掛載點。
用於指定執行後續命令的用戶和用戶組,這邊只是切換後續命令執行的用戶(用戶和用戶組必須提早已經存在)。
格式:USER <用戶名>[:<用戶組>]
例子:USER jack
指定工做目錄,格式爲 WORKDIR /path/to/workdir
。
爲後續的 RUN
、CMD
、ENTRYPOINT
指令配置工做目錄。
可使用多個 WORKDIR
指令,後續命令若是參數是相對路徑,則會基於以前命令指定的路徑。例如
# 最終路徑爲 /a/b/c WORKDIR /a WORKDIR b WORKDIR c RUN pwd
本節內容徹底抄自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
就很是方便實用了。
「 \」
來進行換行「 \」
分割成多行,易讀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
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"]
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/