成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

24條Dockerfile及指令最佳實踐

云計算 云原生
如果服務不需要特權來運行,使用USER?指令切換到非root用戶。使用RUN groupadd -r mysql && useradd -r -g mysql mysql?之后用USER mysql切換用戶。

構建緩存

在鏡像的構建過程中,Docker會根據Dockerfile指定的順序執行每個指令。Dockerfile的每條指令都會將結果提交為新的鏡像。然后,下一條指令基于上一條指令的鏡像進行構建。

在執行每條指令之前,Docker都會在緩存中查找是否已經存在可重用的鏡像,如果存在就使用現存的鏡像,不再重復創建。

因此,為了有效地利用緩存,盡量保持Dockerfile一致,并且盡量在末尾修改:

FROM ubuntu

MAINTAINER author <somebody@company.com>

RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe"

RUN apt-get update

RUN apt-get upgrade -y

更改MAINTAINER指令會使Docker強制執行run指令來更新apt,而不是使用緩存。

如不希望使用緩存,在執行 docker build 時需加上參數--no-cache=true。

Docker中,構建緩存遵循的基本規則如下:

  1. 從緩存中存在的基礎鏡像(FROM指令指定)開始,下一條指令將和該基礎鏡像的所有子鏡像進行匹配,檢查這些子鏡像被創建時使用的指令是否和被檢查的指令完全一樣。如果不是,則緩存失效。
  2. 多數情況中,使用其中一個子鏡像來比較Dockerfile中的指令是足夠的。然而,特定的指令需要做更多的判斷。
  3. 對于ADD和COPY指令,鏡像中對應文件的內容也會被檢查,每個文件都會計算出一個校驗值,通常是檢查文件的校驗和(checksum)。在緩存的查找過程中,會將這些校驗和已存在鏡像中的文件校驗值進行對比。如果文件有任何改變,則緩存失效。
  4. 除了ADD和COPY指令,緩存匹配檢查并不檢查臨時容器中的文件。例如,當使用RUN apt-get -y update命令更新了容器中的文件,并不會被緩存檢查策略作為緩存匹配的依據。
  5. 一旦緩存失效,所有后續的Dockerfile指令都將產生新的鏡像,緩存不會被使用。

使用多階段構建

多階段構建可以大幅度減小最終的鏡像大小,而不需要去想辦法減少中間層和文件的數量。因為鏡像是在生成過程的最后階段生成的,所以可以利用生成緩存來最小化鏡像層。

例如,如果構建包含多個層,則可以將它們從變化頻率較低(以確保生成緩存可重用)到變化頻率較高的順序排序:

  • 安裝構建應用程序所需的依賴工具
  • 安裝或更新依賴項
  • 構建你的應用

比如構建一個Go應用程序的Dockerfile可能類似于這樣:

FROM golang:1.11-alpine AS build

# 安裝項目需要的工具
# 運行 `docker build --no-cache .` 來更新依賴
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# 通過 Gopkg.toml 和 Gopkg.lock 獲取項目的依賴
# 僅在更新 Gopkg 文件時才重新構建這些層
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# 安裝依賴庫
RUN dep ensure -vendor-only

# 拷貝整個項目進行構建
# 當項目下面有文件變化的時候該層才會重新構建
COPY . /go/src/project/
RUN go build -o /bin/project

# 將打包后的二進制文件拷貝到 scratch 鏡像下面,將鏡像大小降到最低
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]

使用標簽

除非是在用Docker做實驗,否則你應當通過 -t 選項來 docker build 新的鏡像以便于標記構建的鏡像。一個簡單可讀的標簽可以幫助管理每個創建的鏡像。

docker build -t="tuxknight/luckypython"

始終通過 -t 標記來構建鏡像。

公開端口

Docker的核心概念是可重復和可移植,鏡像應該可以運行在任何主機上并運行盡可能多的次數。在Dockerfile中可以映射私有和公有端口,但永遠不要通過Dockerfile映射公有端口。這樣運行多個鏡像的情況下會出現端口沖突的問題。

EXPOSE 80:8080  # 80映射到host的8080,不提倡這種用法 
EXPOSE 80 # 80會被docker隨機映射一個端口

EXPOSE指令用于聲明容器將監聽的端口。在EXPOSE指令中,端口號的格式為<容器端口>/<協議>。其中,容器端口是指在容器內部應用程序監聽的端口,而協議是可選的,默認為TCP。

示例中,EXPOSE 80:8080表示容器將監聽容器端口80,而宿主機可以使用端口8080來訪問容器的80端口。也就是,容器的80端口映射到了宿主機的8080端口。

請注意,EXPOSE指令僅僅是聲明容器將監聽的端口,并不會自動進行端口映射。要實際進行端口映射,需要在運行容器時使用-p或-P選項。

CMD ENTRYPOINT語法

CMD和ENTRYPOINT支持兩種語法:

CMD /bin/echo 

CMD ["/bin/echo"]

在第一種方式下,Docker會在命令前加上 /bin/sh -c,可能會導致一些意想不到的問題。在第二種方式下,CMD ENTRYPOINT是一個數組,執行的命令完全和期待的一樣。

容器是短暫的

容器模型是進程而不是機器,不需要開機初始化。在需要時運行,不需要時停止,能夠刪除后重建,并且配置和啟動的最小化。

.dockerignore 文件

在docker build的時候,對于一些不需要提交構建的文件用.dockerignore來進行忽略。忽略部分無用的文件和目錄可以提高構建的速度。

不要在構建中升級版本

不在容器中更新,更新交給基礎鏡像來處理。

應用解耦

每個容器只運行一個進程,每個容器應用只關心一個方面的事情。將多個應用解耦到不同容器中,容器起到了隔離應用隔離數據的作用,可以更輕松地保證容器的橫向擴展和復用。

例如一個Web應用程序可能包含三個獨立的容器:Web應用、數據庫、緩存,每個容器都是獨立的鏡像,分開運行。但這并不是說一個容器就只能跑一個進程,因為有的程序可能會自行產生其他進程,比如Celery就可以有很多個工作進程。

雖然每個容器跑一個進程是一條很好的法則,但這并不是一條硬性的規定。主要是希望一個容器只關注一件事情,盡量保持干凈和模塊化。如果容器互相依賴,你可以使用 Docker 容器網絡 來把這些容器連接起來。

最小化鏡像層數

在很早之前的版本中盡量減少鏡像層數是非常重要的,不過現在的版本已經有了一定的改善了:

  • 只有RUN、COPY和ADD指令會創建層,其他指令會創建臨時的中間鏡像,但是不會直接增加構建的鏡像大小了。
  • 多階段構建的支持,允許我們把需要的數據直接復制到最終的鏡像中,這就允許在中間階段包含一些工具或者調試信息了,而且不會增加最終的鏡像大小。

需要掌握好Dockerfile的可讀性和文件系統層數之間的平衡。控制文件系統層數時會降低Dockerfile的可讀性。而Dockerfile可讀性高時,往往會導致更多的文件系統層數。

避免安裝不必要的包

為了降低復雜性、減少依賴、減小文件大小和構建時間,應該避免安裝額外的或者不必要的軟件包。例如,不要在數據庫鏡像中包含一個文本編輯器。

使用特定標簽

Dockerfile中FROM應始終包含依賴的基礎鏡像的完整倉庫名和標簽,如使用FROM debian:jessie而不是FROM debian。

多行參數排序

只要有可能,就將多行參數按字母順序排序。這可以避免重復包含同一個包,更新包列表時也更容易,也更容易閱讀和審查。建議在反斜杠符號 \ 之前添加一個空格,可以增加可讀性。

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion

Dockerfile指令最佳實踐

關于這些指令的使用建議可以幫助我們創建高效且可維護的Dockerfile。以下內容為Dockerfile指令部分的最佳實踐。

FROM

盡可能使用當前的官方鏡像作為基礎鏡像。推薦使用Debian鏡像,大小保持在100MB上下,且仍是完整的發行版。

另外,根據情況也可考慮使用Alpine映像,因為它受到嚴格控制且較小(當前小于5MB),同時仍是完整的Linux發行版。

LABEL標簽

可以給鏡像添加標簽來幫助組織鏡像、記錄許可信息、輔助自動化構建等。每個標簽一行,由LABEL開頭加上一個或多個標簽對。

下面的示例展示了各種不同的可能格式。#開頭的行是注釋內容。

# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor="ACME Incorporated"
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

一個鏡像可以包含多個標簽,當然以上內容也可以寫成下面這樣,但是不是必須的:

# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

PS:如果字符串包含空格,那么它必須被引用或者空格必須被轉義。如果字符串包含內部引號字符("),則也可以將其轉義。

RUN

為了保持Dockerfile文件的可讀性以及可維護性,建議將過長的或復雜的RUN指令用反斜杠\分割成多行,以提高可讀性和可維護性。

RUN指令最常見的用法是安裝包用的apt-get。因為RUN apt-get指令會安裝包,所以有幾個問題需要注意。

  • 避免運行apt-get upgrade或dist-upgrade,在無特權的容器中,很多必要的包不能正常升級。如果基礎鏡像過時了,應當聯系維護者。如果你確定某個特定的包,比如foo,需要升級,使用apt-get install -y foo就行,該指令會自動升級foo包。
  • 永遠將apt-get update和apt-get install一起執行,否則apt-get install會出現異常。
  • 推薦apt-get update && apt-get install -y package-a package-b這種方式,先更新,之后安裝最新的軟件包。
RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    btrfs-tools \
    build-essential \
    curl \
    dpkg-sig \
    git \
    iptables \
    libapparmor-dev \
    libcap-dev \
    libsqlite3-dev \
    lxc=1.0* \
    mercurial \
    parallel \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.0*

將apt-get update放在一條單獨的RUN聲明中會導致緩存問題以及后續的apt-get install失敗。比如,假設有一個Dockerfile文件:

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl

構建鏡像后,所有的層都在Docker的緩存中。假設后來又修改了其中的apt-get install添加了一個包:

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx

Docker發現修改后的RUN apt-get update指令和之前的完全一樣。所以,apt-get update不會執行,而是使用之前的緩存鏡像。因為apt-get update沒有運行,后面的apt-get install可能安裝的是過時的curl和nginx版本。

使用RUN apt-get update && apt-get install -y可以確保Dockerfiles每次安裝的都是包的最新的版本,而且這個過程不需要進一步的編碼或額外干預。這項技術叫做cache busting(緩存破壞)。

EXPOSE 指令

EXPOSE指令用于指定容器將要監聽的端口。因此,要為應用程序使用常見的端口。

例如,提供Apache web服務的鏡像應該使用EXPOSE 80,而提供MongoDB服務的鏡像使用EXPOSE 27017。

對于外部訪問,用戶可以在執行docker run時使用一個-p參數來指示如何將指定的端口映射到所選擇的端口。

ENV 指令

為了方便新程序運行,可以使用ENV指令來為容器中安裝的程序更新PATH環境變量。例如使用ENV PATH /usr/local/nginx/bin:$PATH來確保CMD ["nginx"]能正確運行。

ENV指令也可用于為容器化的服務提供必要的環境變量,比如Postgres需要的PGDATA。最后,ENV也能用于設置常見的版本號,比如下面的示例:

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

類似于程序中的常量,這種方法可以只需改變ENV指令來自動的改變容器中的軟件版本。

CMD

CMD指令是容器啟動以后,默認的執行命令,需要重點理解下這個默認的含義,意思就是如果我們執行docker run沒有指定任何的執行命令或者Dockerfile里面也沒有指定ENTRYPOINT,那么就會使用CMD指定的執行命令執行了。這也說明了ENTRYPOINT才是容器啟動以后真正要執行的命令。

所以經常遇到CMD會被覆蓋的情況。為什么會被覆蓋呢?主要還是因為CMD的定位就是默認,如果不額外指定,那么才會執行CMD命令,但是如果我們指定了的話那就不會執行CMD命令了,也就是說CMD會被覆蓋。

CMD總共有三種用法:

CMD ["executable", "param1", "param2"]  # exec 形式
CMD ["param1", "param2"] # 作為 ENTRYPOINT 的默認參數
CMD command param1 param2  # shell 形式

CMD推薦使用CMD ["executable","param1","param2"]這樣的格式。如果鏡像是用來運行服務,需要使用CMD["apache2","-DFOREGROUND"],這種格式的指令適用于任何服務性質的鏡像。

ENTRYPOINT 指令

根據官方定義來說ENTRYPOINT才是用于定義容器啟動以后的執行程序的,允許將鏡像當成命令本身來運行(用CMD提供默認選項),從名字也可以理解,是容器的入口。

ENTRYPOINT 一共有兩種用法:

ENTRYPOINT ["executable", "param1", "param2"] (exec 形式)
ENTRYPOINT command param1 param2 (shell 形式)

對應命令行exec模式,也就是帶中括號的,和CMD的中括號形式是一致的。但是這里貌似是在shell的環境下執行的,與cmd有區別。

如果run命令后面有執行命令,那么后面的全部都會作為ENTRYPOINT的參數。如果run后面沒有額外的命令,但是定義了CMD,那么CMD的全部內容就會作為ENTRYPOINT的參數,這同時是上面我們提到的CMD的第二種用法。

所以說ENTRYPOINT不會被覆蓋。當然如果要在run里面覆蓋,也是有辦法的,使用--entrypoint參數即可。

一般會用ENTRYPOINT的中括號形式作為Docker容器啟動以后的默認執行命令,里面放的是不變的部分,可變部分比如命令參數可以使用CMD的形式提供默認版本,也就是run里面沒有任何參數時使用的默認參數。如果我們想用默認參數,就直接run,否則想用其他參數,就run里面加上參數。

ADD COPY

雖然ADD與COPY功能類似,但推薦使用COPY。因為它比 ADD 更透明。COPY只支持基本的文件拷貝功能,更加的可控。而ADD具有更多特定,比如tar文件自動提取,支持URL。通常需要提取tarball中的文件到容器的時候才會用到ADD。

如果在Dockerfile中使用多個文件,每個文件應使用單獨的COPY指令。這樣,只有出現文件變化的指令才會不使用緩存。

為了控制鏡像的大小,不建議使用ADD指令獲取URL文件。正確的做法是在RUN指令中使用wget或curl來獲取文件,并且在文件不需要的時候刪除文件。

RUN mkdir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.gz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all

VOLUME

VOLUME指令用于聲明容器中的目錄將被持久化保存,即在容器中創建的目錄將被掛載到宿主機或其他容器中,以便數據可以在容器之間共享。

VOLUME指令應當暴露出數據庫的存儲位置,配置文件的存儲以及容器中創建的文件或目錄。由于容器結束后并不保存任何更改,應該把所有數據通過VOLUME保存到host中。

強烈建議使用VOLUME來管理鏡像中的可變部分和用戶可以改變的部分。

USER

如果服務不需要特權來運行,使用USER指令切換到非root用戶。使用RUN groupadd -r mysql && useradd -r -g mysql mysql之后用USER mysql切換用戶。

要避免使用sudo來提升權限,因為它不可預期的TTY和信號轉發行為可能造成的問題比它能解決的問題還多。如果你真的需要和sudo類似的功能(例如,以root權限初始化某個守護進程,以非root權限執行它),你可以使用gosu。我們可以去查看官方的一些鏡像,很多都是使用的gosu。

最后,不要反復地切換用戶,減少不必要的layers。

WORKDIR

為了清晰性和可靠性,WORKDIR的路徑應該始終使用絕對路徑。同時,使用WORKDIR來替代RUN cd ... && do-something這樣難以維護的指令。后者難以閱讀、排錯和維護。

責任編輯:武曉燕 來源: 程序新視界
相關推薦

2013-12-12 10:20:00

JavaScript學習

2023-07-25 11:22:31

2015-10-10 09:35:38

swift規范

2010-08-25 08:58:32

HTML

2015-10-10 10:05:03

Swift2.0實踐

2009-01-15 09:57:00

2011-12-21 09:38:31

HTML 5

2018-01-03 11:22:45

2024-05-13 13:13:13

APISpring程序

2024-01-05 09:08:48

代碼服務管理

2015-01-09 11:29:45

DockerDockerFile創建鏡像

2013-09-25 09:25:52

2025-01-09 11:58:19

2011-09-14 10:38:39

2021-10-12 10:43:38

區塊鏈技術智能

2011-08-18 11:05:21

jQuery

2023-07-21 01:12:30

Reactfalse?變量

2012-04-13 14:03:19

SOA

2013-09-30 09:33:33

云治理云安全斯諾登事件

2016-02-17 09:26:09

數據中心
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91一区二区三区 | 亚洲欧洲精品在线 | 日韩欧美国产精品一区二区 | 视频在线一区二区 | 国产一区二区三区视频 | 成人在线观看免费视频 | 精品久久久久久 | 粉嫩一区二区三区国产精品 | 人人九九精 | 国产日韩欧美 | 欧美专区日韩专区 | 国产精品成av人在线视午夜片 | www国产亚洲精品 | 国产福利观看 | www.日韩系列 | 日韩精品极品视频在线观看免费 | 天堂网色 | 北条麻妃一区二区三区在线视频 | 亚洲一区精品在线 | 欧美日韩在线视频观看 | 99亚洲 | 91久久久久久久久久久久久 | 91久久国产精品 | 最新日韩在线 | 99视频在线免费观看 | 一二三四在线视频观看社区 | 日韩二三区 | 日韩成人精品视频 | 国产精品日本一区二区不卡视频 | 美女黄网站 | 亚洲一区二区三区四区五区中文 | 婷婷色在线 | 成人免费av在线 | 久久99视频这里只有精品 | 青春草在线 | 精品视频在线一区 | 国产精品美女久久久久久久久久久 | 亚洲一区 中文字幕 | 秋霞影院一区二区 | 欧美在线综合 | 黄色一级免费 |