docker 學習筆記——第六集(dockerfile 定製鏡像)

使用 Dockerfile 定製鏡像

什麼是 Dockerfile 呢?

Dockerfile 是一個文本文檔,其中包含用戶能夠在命令行上調用以組裝映像的全部命令。Docker 能夠經過閱讀該文件中的指令來自動構建映像。(相似於 Linux 上的 bash 腳本,Docker 經過該腳本構建鏡像)html

使用 dockerfile 製做一個 nginx 鏡像

$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile   //首字母必須大寫

Dockerfile 文件內容以下nginx

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

這個 Dockerfile 很簡單,一共就兩行。涉及到了兩條指令,FROM 和 RUN。程序員

FROM

功能:指定基礎鏡像redis

所謂定製鏡像,那必定是以一個鏡像爲基礎,在其上進行定製。就像咱們以前運行了一個 nginx 鏡像的容器,再進行修改同樣,基礎鏡像是必須指定的。而 FROM 就是指定 基礎鏡像,所以一個 Dockerfile 中 FROM 是必備的指令,而且必須是第一條指令。

通常使用中咱們經過 Docker Hub 來查找相關鏡像。以下圖所示,紅標中標識的爲官方鏡像
docker hubdocker

除了選擇現有鏡像爲基礎鏡像外,Docker 還存在一個特殊的鏡像,名爲 scratch(該鏡像不能經過 docker pull 命令直接拉取)。這個鏡像是虛擬的概念,並不實際存在,它表示一個空白的鏡像。shell

由於本人只對 PHP 較爲熟悉,沒有使用過 go,這個也不是很瞭解,就先跳過了json

RUN

功能:執行命令bash

用來執行命令行命令的

實際使用下有兩種格式app

  1. shell 格式: RUN <命令>
  2. exec 格式:RUN ["可執行文件", "參數1", "參數2"]

Dockerfile 中每個指令都會創建一層,RUN 也不例外。每個 RUN 執行結束後,都會 commit 這一層的修改,構成新的鏡像。因此在使用中盡力減小指令。ui

FROM debian:stretch

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install

上面的這種寫法,建立了 7 層鏡像。這是徹底沒有意義的,並且不少運行時不須要的東西,都被裝進了鏡像裏,好比編譯環境、更新的軟件包等等。結果就是產生很是臃腫、很是多層的鏡像,不只僅增長了構建部署的時間,也很容易出錯。咱們在之後的使用應該避免。正確的寫法以下所示:

FROM debian:stretch

RUN buildDeps='gcc libc6-dev make wget' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

在上一個 Dockerfile 中全部的命令只有一個目的,就是編譯、安裝 redis 可執行文件。所以沒有必要創建不少層,這只是一層的事情。所以,這裏咱們僅僅使用一個 RUN 指令,並使用 && 將各個所需命令串聯起來。將以前的 7 層,簡化爲了 1 層。

在撰寫 Dockerfile 的時候,要常常提醒本身,這並非在寫 Shell 腳本,而是在定義每一層該如何構建。

構建

命裏格式: docker build [選項] <上下文路徑/URL/->

$ docker build -t mynginx:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM nginx
 ---> 08393e824c32
Step 2/2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in 1d7edd724b5f
Removing intermediate container 1d7edd724b5f
 ---> e29ba82c8e43
Successfully built e29ba82c8e43
Successfully tagged mynginx:v1

// 查看剛剛建立的鏡像
$ docker image ls
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
mynginx                 v1                  e29ba82c8e43        3 minutes ago       132MB
  1. -t 表示指定鏡像的名稱和標籤,格式爲 name:tag
  2. 最後有個點,表示構建的上下文路徑爲當前目錄

其餘構建方式

  1. 經過 Git repo 進行構建
  2. 使用 tar 包進行構建

經常使用指令介紹

COPY 複製文件

格式:

  • COPY [--chown=<user>:<group>] <源路徑> <目標路徑> (經常使用)
  • COPY [--chown=<user>:<group>] ["<源路徑1>", "<目標路徑>"]

構建上下文目錄中 <源路徑> 的文件/目錄複製到新的一層的鏡像內的 <目標路徑> 位置。

COPY package.json /usr/src/app/

加上 --chown=<user>:<group> 選項來改變文件的所屬用戶及所屬組

COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/

ADD 更高級的複製

和 COPY 的格式和性質基本一致。可是在 COPY 基礎上增長了一些功能。

  • <源路徑> 能夠是一個 URL。Docker 引擎會試圖去下載這個連接的文件放到 <目標路徑> 去。下載後的文件權限自動設置爲 600
  • <源路徑> 爲一個 tar 壓縮文件的話,壓縮格式爲 gzip, bzip2 以及 xz 的狀況下,ADD 指令將會自動解壓縮這個壓縮文件到 <目標路徑> 去。

在 Docker 官方的 Dockerfile 最佳實踐文檔 中要求,儘量的使用 COPY,由於 COPY 的語義很明確,就是複製文件而已,而 ADD 則包含了更復雜的功能,其行爲也不必定很清晰。最適合使用 ADD 的場合,就是所說起的須要自動解壓縮的場合。

CMD 容器啓動命令

Docker 不是虛擬機,容器就是進程。既然是進程,那麼在啓動容器的時候,須要指定所運行的程序及參數。CMD 指令就是用於指定默認的容器主進程的啓動命令的。

格式:

  • shell 格式:CMD <命令>
  • exec 格式:CMD ["可執行文件", "參數1", "參數2"...] (推薦,必定要使用雙引號)

ENV 設置環境變量

用於設置環境變量,不管是後面的其它指令,如 RUN,仍是運行時的應用,均可以直接使用這裏定義的環境變量。

格式:

  • ENV <key> <value>
  • ENV <key1>=<value1> <key2>=<value2>...

ARG 構建參數

格式:

  • ARG <參數名>[=<默認值>]
與 ENV 指令同樣,都是設置環境變量。所不一樣的是,ARG 所設置的構建環境的環境變量,在未來容器運行時是不會存在這些環境變量的。

Dockerfile 中的 ARG 指令是定義參數名稱,以及定義其默認值。該默認值能夠在構建命令 docker build 中用 --build-arg <參數名>=<值>來覆蓋。

VOLUME 定義匿名卷

格式:

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

示例:

容器中的 /data 目錄自動掛載到匿名卷中

VOLUME /data

該指令能夠在運行時被覆蓋

docker run -d -v mydata:/data xxxx

EXPOSE 暴露端口

格式:

  • EXPOSE <端口1> [<端口2>...]

聲明運行時容器提供服務端口,這只是一個聲明,在運行時並不會由於這個聲明就會開啓這個端口的服務。

寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;另外一個用處則是在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。

WORKDIR 指定工做目錄

格式:

  • WORKDIR <工做目錄路徑>。

使用 WORKDIR 指令能夠來指定工做目錄(或者稱爲當前目錄),之後各層的當前目錄就被改成指定的目錄,如該目錄不存在,WORKDIR 會幫你創建目錄。

RUN cd /app
RUN echo "hello" > world.txt

使用上面的內容構建鏡像後會發現根本找不到 /app/world.txt文件。緣由其實很簡單,在 Shell 中,連續兩行是同一個進程執行環境,所以前一個命令修改的內存狀態,會直接影響後一個命令;而在 Dockerfile 中,這兩行 RUN 命令的執行環境根本不一樣,是兩個徹底不一樣的容器。這就是對 Dockerfile 構建分層存儲的概念不瞭解所致使的錯誤。

所以若是須要改變之後各層的工做目錄的位置,那麼應該使用 WORKDIR 指令。

USER 指定當前用戶

格式:

  • USER <用戶名>[:<用戶組>]

USER 指令和 WORKDIR 類似,都是改變環境狀態並影響之後的層。WORKDIR 是改變工做目錄,USER 則是改變以後層的執行 RUN, CMD 以及 ENTRYPOINT 這類命令的身份。固然,和 WORKDIR 同樣,USER 只是幫助你切換到指定用戶而已,這個用戶必須是事先創建好的,不然沒法切換。

其餘指令參考官方文檔

程序員的藝術人生

相關文章
相關標籤/搜索