Docker 安裝、鏡像、dockerfile、容器、倉庫

2018-05-30php

 

 參考:html

一、《docker從入門到實戰》node

二、菜鳥教程http://www.runoob.com/docker/docker-command-manual.html、python

三、docker官網https://docs.docker.com/install/linux/docker-ce/centos/#uninstall-old-versionsmysql

 

一、部署docker

一、卸載舊版本docker
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine
二、安裝依賴包
$ sudo yum install -y yum-utils \
           device-mapper-persistent-data \
           lvm2
三、添加國內 yum 軟件源
$ sudo yum-config-manager \
    --add-repo \
    https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repolinux

四、更新 yum 軟件源緩存,並安裝 docker-ce 。
$ sudo yum makecache fast
$ sudo yum install docker-ce
五、啓動 Docker CE:
$ sudo systemctl enable docker
$ sudo systemctl start docker
六、添加用戶到docker組,且注消、從新登陸。docker安裝完畢後會自動建立docker羣組、自動設置docker組執行docker相關命令。
$ sudo usermod -aG docker $USERnginx

或者$ sudo gpasswd -a $USER dockergit

從新登陸後驗證當前用戶已經添加上docker羣組:github

$ idgolang

另:退出docker羣組的命令(不須要執行):gpasswd -d $USER docker

七、測試 Docker 是否安裝正確
$ docker run hello-world

報錯:Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.37/images/json: dial unix /var/run/docker.sock: connect: permission denied。

  • 緣由:沒有x執行權限致使。
  • 解決步驟:
    • a、只添加用戶到docker組。
    • b、注消、從新登陸。

八、配置國內鏡像進行加速

新增/etc/docker/daemon.json文件且添加以下內容:

 {

   "registry-mirrors": [

     "https://registry.docker-cn.com"

   ]

 }

重啓服務:

$ sudo systemctl daemon-reload

$ sudo systemctl restart docker

檢查加速器是否生效:

$ docker info

若是從結果中看到了以下內容,說明配置成功

 Registry Mirrors:

 https://registry.docker-cn.com/

九、添加內核配置參數

默認配置下,若是在 CentOS 使用Docker CE 看到下面的這些警告信息:

WARNING: bridge-nf-call-iptables is disabled

WARNING: bridge-nf-call-ip6tables is disabled

請添加內核配置參數以啓用這些功能:

$ sudo tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

從新加載 sysctl.conf 便可:
$ sudo sysctl -p

十、docker卸載

$ sudo yum remove docker-ce
$ sudo rm -rf /var/lib/docker

 

2安裝指定版本

一、卸載舊版本docker
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine
二、安裝 docker-latest

$ yum list | grep docker |grep "1."
$ sudo yum install docker-latest.x86_64
五、啓動 Docker CE:
$ sudo systemctl enable docker-latest
$ sudo systemctl start docker-latest
三、注消、從新登陸。docker安裝完畢後會自動建立docker羣組、自動設置docker羣組執行docker相關命令。

另:退出docker羣組的命令(不須要執行):gpasswd -d $USER docker

 

三、Docker鏡像命令

一、docker search :從Docker Hub查找鏡像
語法

  • docker search [OPTIONS] TERM

OPTIONS說明:

  • --automated :只列出 automated build類型的鏡像;
  • --no-trunc :顯示完整的鏡像描述;
  • -s :列出收藏數不小於指定值的鏡像。

二、docker pull : 從鏡像倉庫中拉取或者更新指定鏡像
語法

  • docker pull [OPTIONS] NAME[:TAG|@DIGEST]

OPTIONS說明:

  • -a :拉取全部 tagged 鏡像
  • --disable-content-trust :忽略鏡像的校驗,默認開啓

從私有倉庫抓取鏡像:docker pull <path_to_registry>/<image>

三、docker push : 將本地的鏡像上傳到鏡像倉庫,要先登錄到鏡像倉庫
語法

  • docker push [OPTIONS] NAME[:TAG]

OPTIONS說明:

  • --disable-content-trust :忽略鏡像的校驗,默認開啓

四、docker commit :從容器建立一個新的鏡像。
語法

  • docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

OPTIONS說明:

  • -a :提交的鏡像做者;
  • -c :使用Dockerfile指令來建立鏡像;
  • -m :提交時的說明文字;
  • -p :在commit時,將容器暫停。

五、docker images : 列出本地鏡像。
語法

  • docker images [OPTIONS] [REPOSITORY[:TAG]]

OPTIONS說明:

  • -a :列出本地全部的鏡像(含中間映像層,默認狀況下,過濾掉中間映像層);
  • --digests :顯示鏡像的摘要信息;
  • -f :顯示知足條件的鏡像;
  • --format :指定返回值的模板文件;
  • --no-trunc :顯示完整的鏡像信息;
  • -q :只顯示鏡像ID。

docker images -f dangling=true            顯示虛懸鏡像
docker images prune                 刪除虛擬鏡像
docker images --format "{{.ID}}: {{.Repository}}"        直接列出鏡像結果,而且只包含鏡像ID和倉庫名
docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"        打算以表格等距顯示,而且有標題行,和默認同樣,不過本身定義列

六、docker diff : 檢查容器裏文件結構的更改。
語法

  • docker diff [OPTIONS] CONTAINER

七、docker rmi : 刪除本地一個或多少鏡像。
語法

  • docker rmi [OPTIONS] IMAGE [IMAGE...]

OPTIONS說明:

  • -f :強制刪除;
  • --no-prune :不移除該鏡像的過程鏡像,默認移除;

八、docker save : 將指定鏡像保存成 tar 歸檔文件。
語法

  • docker save [OPTIONS] IMAGE [IMAGE...]

OPTIONS說明:

  • -o :輸出到的文件。

九、docker load : 從.tar壓縮文件中加載鏡像

語法

  • docker load [ -i name.tar]

十、docker export :將文件系統做爲一個tar歸檔文件導出到STDOUT。
語法

  • docker export [OPTIONS] CONTAINER

OPTIONS說明:

  • -o :將輸入內容寫到文件。

 

十一、docker import : 從歸檔文件中建立鏡像。
語法

  • docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]

OPTIONS說明:

    • -c :應用docker 指令建立鏡像;
    • -m :提交時的說明文字;

十二、tag : 標記鏡像
語法

  • docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

實例:使用docker tag將ubuntu:latest這個鏡像標記爲127.0.0.1:5000/ubuntu:latest。
$ docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest

1三、docker history:查看鏡像內的歷史記錄

四、Dockerfile

FROM 指定基礎鏡像

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

Docker Store上有很是多的高質量的官方鏡像,有能夠直接拿來使用的服務類的鏡像,nginxredismongomysqlhttpdphptomcat;也有一些方便開發、構建、運行各類語言應用的鏡像,nodeopenjdkpythonrubygolang等。能夠在其中尋找一個最符合咱們最終目標的鏡像爲基礎鏡像進行定製。

若是沒有找到對應服務的鏡像,官方鏡像中還提供了一些更爲基礎的操做系統鏡像,ubuntudebiancentosfedoraalpine,這些操做系統的軟件庫爲咱們提供了更廣闊的擴展空間。

除了選擇現有鏡像爲基礎鏡像外,Docker還存在一個特殊的鏡像,名爲scratch。這個鏡像是虛擬的概念,並不實際存在,它表示一個空白的鏡像。

FROM scratch

...

若是你以scratch爲基礎鏡像的話,意味着你不以任何鏡像爲基礎,接下來所寫的指令將做爲鏡像第一層開始存在。

不以任何系統爲基礎,直接將可執行文件複製進鏡像的作法並不罕見,好比swarmcoreos/etcd。對於Linux下靜態編譯的程序來講,並不須要有操做系統提供運行時支持,所需的一切庫都已經在可執行文件裏了,所以直接FROM scratch會讓鏡像體積更加小巧。使用Go語言開發的應用不少會使用這種方式來製做鏡像,這也是爲何有人認爲Go是特別適合容器微服務架構的語言的緣由之一。

RUN 執行命令行命令

RUN 多條的寫法是徹底沒有意義的,不少運行時不須要的東西都被裝進了鏡像裏,好比編譯環境、更新的軟件包等等。結果就是產生很是臃腫、很是多層的鏡像,不只僅增長了構建部署的時間,也很容易出錯。這是不少初學Docker的人常犯的一個錯誤。

Union FS是有最大層數限制的,好比AUFS,曾經是最大不得超過42,如今是不得超過127層。

首先,以前全部的命令只有一個目的,就是編譯、安裝redis可執行文件。所以沒有必要創建不少層,這只是一層的事情。所以,這裏沒有使用不少個RUN對一一對應不一樣的命令,而是僅僅使用一個RUN指令,並使用&&將各個所需命令串聯起來。將以前的7,簡化爲了1層。在撰寫Dockerfile的時候,要常常提醒本身,這並非在寫Shell腳本,而是在定義每一層該如何構建。

而且,這裏爲了格式化還進行了換行。Dockerfile支持Shell類的行尾添加\的命令換行方式,以及行首#進行註釋的格式。良好的格式,好比換行、縮進、註釋等,會讓維護、排障更爲容易,這是一個比較好的習慣。

此外,還能夠看到這一組命令的最後添加了清理工做的命令,刪除了爲了編譯構建所須要的軟件,清理了全部下載、展開的文件,而且還清理了apt緩存文件。這是很重要的一步,咱們以前說過,鏡像是多層存儲,每一層的東西並不會在下一層被刪除,會一直跟隨着鏡像。所以鏡像構建時,必定要確保每一層只添加真正須要添加的東西,任何無關的東西都應該清理掉,就是每一層構建的最後必定要清理掉無關文件。

COPY 複製文件

格式:

COPY <源路徑>... <目標路徑

COPY ["<源路徑1>",... "<目標路徑>"] 

和 RUN 指令同樣,也有兩種格式,一種相似於命令行,一種相似於函數調用。COPY 指令將從構建上下文目錄中 <源路徑的文件/目錄複製到新的一層的鏡像內的 <目標路徑位置。好比:

COPY package.json /usr/src/app/

 

<源路徑> 能夠是多個,甚至能夠是通配符,其通配符規則要知足 Go 的 filepath.Match 規則,:

COPY hom* /mydir/

COPY hom?.txt /mydir/

<目標路徑> 能夠是容器內的絕對路徑,也能夠是相對於工做目錄的相對路徑(工做目錄能夠用 WORKDIR 指令來指定)。目標路徑不須要事先建立,若是目錄不存在會在複製文件前先行建立缺失目錄。

此外,還須要注意一點,使用 COPY 指令,源文件的各類元數據都會保留。好比讀、寫、執行權限、文件變動時間等。這個特性對於鏡像定製頗有用。特別是構建相關文件都在使用 Git進行管理的時候。

 

ADD 更高級的複製文件

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

若是 <源路徑是一個 URL,這種狀況下,Docker 引擎會試圖去下載這個連接的文件放到 <目標路徑去。下載後的文件權限自動設置爲 600,若是這並非想要的權限,那麼還須要增長額外的一層 RUN 進行權限調整;另外,若是下載的是個壓縮包,須要解壓縮,也同樣還須要額外的一層 RUN 指令進行解壓縮。因此不如直接使用 RUN 指令,而後使用 wget 或者 curl 工具下載,處理權限、解壓縮、而後清理無用文件更合理。所以,這個功能其實並不實用,並且不推薦使用。

若是 <源路徑爲一個 tar 壓縮文件的話,壓縮格式爲 gzip, bzip2 以及 xz 的狀況下,ADD 指令將會自動解壓縮這個壓縮文件到 <目標路徑去。

在某些狀況下,這個自動解壓縮的功能很是有用,好比官方鏡像 ubuntu :

FROM scratch

ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /

...

但在某些狀況下,若是咱們真的是但願複製個壓縮文件進去,而不解壓縮,這時就不可使用 ADD 命令了。

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

另外須要注意的是,ADD 指令會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。

所以在 COPY 和 ADD 指令中選擇的時候,能夠遵循這樣的原則,全部的文件複製均使用COPY指令,僅在須要自動解壓縮的場合使用ADD

 

CMD 容器啓動命令

也是兩種格式:

l shell格式:CMD <命令>

l exec格式:CMD ["可執行文件", "參數1", "參數2"...]

l 參數列表格式:CMD ["參數1", "參數2"...]。在指定了ENTRYPOINT指令後,CMD指定具體的參數。

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

在運行時能夠指定新的命令來替代鏡像設置中的這個默認命令,好比,ubuntu鏡像默認的CMD/bin/bash,若是咱們直接docker run -it ubuntu的話,會直接進入bash。咱們也能夠在運行時指定運行別的命令,docker run -it ubuntu cat /etc/os-release。這就是用cat /etc/os-release命令替換了默認的/bin/bash命令了,輸出了系統版本信息。

在指令格式上,通常推薦使用exec格式,這類格式在解析時會被解析爲 JSON 數組,所以必定要使用雙引號",而不要使用單引號。

若是使用shell格式的話,實際的命令會被包裝爲sh -c的參數的形式進行執行。好比:

CMD echo $HOME

在實際執行中,會將其變動爲:

CMD [ "sh", "-c", "echo $HOME" ]

這就是爲何咱們可使用環境變量的緣由,由於這些環境變量會被 shell 進行解析處理。提到CMD就不得不提容器中應用在前臺執行和後臺執行的問題。這是初學者常出現的一個混淆。

Docker 不是虛擬機,容器中的應用都應該之前臺執行,而不是像虛擬機、物理機裏面那樣,用 upstart/systemd 去啓動後臺服務,容器內沒有後臺服務的概念。

一些初學者將CMD寫爲:

CMD service nginx start

而後發現容器執行後就當即退出了。甚至在容器內去使用 systemctl 命令結果卻發現根本執行不了。這就是由於沒有搞明白前臺、後臺的概念,沒有區分容器和虛擬機的差別,依舊在以傳統虛擬機的角度去理解容器。

對於容器而言,其啓動程序就是容器應用進程,容器就是爲了主進程而存在的,主進程退出,容器就失去了存在的意義,從而退出,其它輔助進程不是它須要關心的東西。

而使用 service nginx start 命令,則是但願 upstart 來之後臺守護進程形式啓動 nginx 服務。而剛纔說了 CMD service nginx start 會被理解爲 CMD [ "sh", "-c", "service nginx start"] ,所以主進程其實是 sh 。那麼當 service nginx start 命令結束後, sh 也就結束了, sh 做爲主進程退出了,天然就會令容器退出。

正確的作法是直接執行 nginx 可執行文件,而且要求之前臺形式運行。好比:

CMD ["nginx", "-g", "daemon off;"]

 

ENTRYPOINT 入口點

ENTRYPOINT 的格式和 RUN 指令格式同樣,分爲 exec 格式和 shell 格式。

ENTRYPOINT 的目的和 CMD 同樣,都是在指定容器啓動程序及參數。 ENTRYPOINT 在運行時也能夠替代,不過比 CMD 要略顯繁瑣,須要經過 docker run 的參數 --entrypoint 來指定。

當指定了 ENTRYPOINT , CMD 的含義就發生了改變,再也不是直接的運行其命令,而是將CMD 的內容做爲參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變爲:

<ENTRYPOINT> "<CMD>"

那麼有了 CMD ,爲何還要有 ENTRYPOINT ?這種 <ENTRYPOINT> "<CMD>" 有什麼好處麼?讓咱們來看幾個場景。

 

場景一:讓鏡像變成像命令同樣使用

假設咱們須要一個得知本身當前公網 IP 的鏡像,那麼能夠先用 CMD 來實現:

FROM ubuntu:16.04

RUN apt-get update \

 && apt-get install -y curl \

 && rm -rf /var/lib/apt/lists/*

CMD [ "curl", "-s", "http://ip.cn" ]

假如咱們使用 docker build -t myip . 來構建鏡像的話,若是咱們須要查詢當前公網 IP,只須要執行:

$ docker run myip

當前 IP:61.148.226.66 來自:北京市 聯通

,這麼看起來好像能夠直接把鏡像當作命令使用了,不過命令總有參數,若是咱們但願加參數呢?好比從上面的 CMD 中能夠看到實質的命令是 curl ,那麼若是咱們但願顯示 HTTP頭信息,就須要加上 -i 參數。那麼咱們能夠直接加 -i 參數給 docker run myip ?

$ docker run myip -i

docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".

咱們能夠看到可執行文件找不到的報錯, executable file not found 。以前咱們說過,跟在鏡像名後面的是 command ,運行時會替換 CMD 的默認值。所以這裏的 -i 替換了原來的CMD ,而不是添加在原來的 curl -s http://ip.cn 後面。而 -i 根本不是命令,因此天然找不到。

那麼若是咱們但願加入 -i 這參數,咱們就必須從新完整的輸入這個命令:

$ docker run myip curl -s http://ip.cn -i

這顯然不是很好的解決方案,而使用 ENTRYPOINT 就能夠解決這個問題。如今咱們從新用ENTRYPOINT 來實現這個鏡像:

FROM ubuntu:16.04

RUN apt-get update \

 && apt-get install -y curl \

 && rm -rf /var/lib/apt/lists/*

ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

此次咱們再來嘗試直接使用 docker run myip -i :

$ docker run myip

當前 IP:61.148.226.66 來自:北京市 聯通

$ docker run myip -i

HTTP/1.1 200 OK

Server: nginx/1.8.0

Date: Tue, 22 Nov 2016 05:12:40 GMT

Content-Type: text/html; charset=UTF-8

Vary: Accept-Encoding

X-Powered-By: PHP/5.6.24-1~dotdeb+7.1

X-Cache: MISS from cache-2

X-Cache-Lookup: MISS from cache-2:80

X-Cache: MISS from proxy-2_6

Transfer-Encoding: chunked

Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006

Connection: keep-alive

當前 IP:61.148.226.66 來自:北京市 聯通

能夠看到,此次成功了。這是由於當存在 ENTRYPOINT , CMD 的內容將會做爲參數傳給ENTRYPOINT ,而這裏 -i 就是新的 CMD ,所以會做爲參數傳給 curl ,從而達到了咱們預期的效果。

場景二:應用運行前的準備工做

啓動容器就是啓動主進程,但有些時候,啓動主進程前,須要一些準備工做。

好比 mysql 類的數據庫,可能須要一些數據庫配置、初始化的工做,這些工做要在最終的mysql 服務器運行以前解決。

此外,可能但願避免使用 root 用戶去啓動服務,從而提升安全性,而在啓動服務前還須要以 root 身份執行一些必要的準備工做,最後切換到服務用戶身份啓動服務。或者除了服務外,其它命令依舊可使用 root 身份執行,方便調試等。

這些準備工做是和容器 CMD 無關的,不管 CMD 爲何,都須要事先進行一個預處理的工做。這種狀況下,能夠寫一個腳本,而後放入 ENTRYPOINT 中去執行,而這個腳本會將接到的參數(也就是 <CMD> )做爲命令,在腳本最後執行。好比官方鏡像 redis 中就是這麼作的:

FROM alpine:3.4

...

RUN addgroup -S redis && adduser -S -G redis redis

...

ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 6379

CMD [ "redis-server" ]

能夠看到其中爲了 redis 服務建立了 redis 用戶,並在最後指定了 ENTRYPOINT 爲 docker-

entrypoint.sh 腳本。

#!/bin/sh

...

# allow the container to be started with `--user`

if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then

 chown -R redis .

 exec su-exec redis "$0" "$@"

fi

exec "$@"

該腳本的內容就是根據 CMD 的內容來判斷,若是是 redis-server 的話,則切換到redis用戶身份啓動服務器,不然依舊使用 root 身份執行。好比:

$ docker run -it redis id

uid=0(root) gid=0(root) groups=0(root)

ENV 設置環境變量

格式有兩種:

 ENV <key> <value> 

 ENV <key1>=<value1> <key2>=<value2>... 

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

ENV VERSION=1.0 DEBUG=on \

 NAME="Happy Feet"

這個例子中演示瞭如何換行,以及對含有空格的值用雙引號括起來的辦法,這和 Shell 下的行爲是一致的。

定義了環境變量,那麼在後續的指令中,就可使用這個環境變量。好比在官方 node 鏡像Dockerfile ,就有相似這樣的代碼:

ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.ta

r.xz" \

 && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \

 && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \

 && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \

 && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=

1 \

 && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \

 && ln -s /usr/local/bin/node /usr/local/bin/nodejs

在這裏先定義了環境變量 NODE_VERSION ,其後的 RUN 這層裏,屢次使用 $NODE_VERSION 來進行操做定製。能夠看到,未來升級鏡像構建版本的時候,只須要更新 7.2.0 便可, Dockerfile 構建維護變得更輕鬆了。

下列指令能夠支持環境變量展開:

 ADD 、 COPY 、 ENV 、 EXPOSE 、 LABEL 、 USER 、 WORKDIR 、 VOLUME 、 STOPSIGNAL 、 ONBUILD 

能夠從這個指令列表裏感受到,環境變量可使用的地方不少,很強大。經過環境變量,咱們可讓一份 Dockerfile 製做更多的鏡像,只需使用不一樣的環境變量便可。

ARG 構建參數

格式: ARG <參數名>[=<默認值>] 

構建參數和 ENV 的效果同樣,都是設置環境變量。所不一樣的是, ARG 所設置的構建環境的環境變量,在未來容器運行時是不會存在這些環境變量的。可是不要所以就使用 ARG 保存密碼之類的信息,由於 docker history 仍是能夠看到全部值的。

Dockerfile 中的 ARG 指令是定義參數名稱,以及定義其默認值。該默認值能夠在構建命令

 docker build 中用 --build-arg <參數名>=<來覆蓋。

在 1.13 以前的版本,要求 --build-arg 中的參數名,必須在 Dockerfile 中用 ARG 定義過了,換句話說,就是 --build-arg 指定的參數,必須在 Dockerfile 中使用了。若是對應參數沒有被使用,則會報錯退出構建。從 1.13 開始,這種嚴格的限制被放開,再也不報錯退出,而是顯示警告信息,並繼續構建。這對於使用 CI 系統,用一樣的構建流程構建不一樣的Dockerfile 的時候比較有幫助,避免構建命令必須根據每一個 Dockerfile 的內容修改。

VOLUME 定義匿名卷

格式爲:

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

 VOLUME <路徑

以前咱們說過,容器運行時應該儘可能保持容器存儲層不發生寫操做,對於數據庫類須要保存動態數據的應用,其數據庫文件應該保存於卷(volume),後面的章節咱們會進一步介紹

Docker 卷的概念。爲了防止運行時用戶忘記將動態文件所保存目錄掛載爲卷,Dockerfile ,咱們能夠事先指定某些目錄掛載爲匿名卷,這樣在運行時若是用戶不指定掛

,其應用也能夠正常運行,不會向容器存儲層寫入大量數據。

VOLUME /data

這裏的 /data 目錄就會在運行時自動掛載爲匿名卷,任何向 /data 中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。固然,運行時能夠覆蓋這個掛載設置。好比:

docker run -d -v mydata:/data xxxx

在這行命令中,就使用了 mydata 這個命名卷掛載到了 /data 這個位置,替代了Dockerfile 中定義的匿名卷的掛載配置。

EXPOSE 聲明端口

格式爲 EXPOSE <端口1> [<端口2>...]

EXPOSE 指令是聲明運行時容器提供服務端口,這只是一個聲明,在運行時並不會由於這個聲明應用就會開啓這個端口的服務。在 Dockerfile 中寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;另外一個用處則是在運行時使用隨機端口映射時,也就是 docker run -P ,會自動隨機映射 EXPOSE 的端口。

此外,在早期 Docker 版本中還有一個特殊的用處。之前全部容器都運行於默認橋接網絡中,所以全部容器互相之間均可以直接訪問,這樣存在必定的安全性問題。因而有了一個 Docker引擎參數 --icc=false,當指定該參數後,容器間將默認沒法互訪,除非互相間使用了 --links 參數的容器才能夠互通,而且只有鏡像中 EXPOSE 所聲明的端口才能夠被訪問。這個 --icc=false 的用法,在引入了 docker network 後已經基本不用了,經過自定義網絡能夠很輕鬆的實現容器間的互聯與隔離。

要將 EXPOSE 和在運行時使用 -p <宿主端口>:<容器端口區分開來。-p,是映射宿主端口和容器端口,換句話說,就是將容器的對應端口服務公開給外界訪問,而 EXPOSE 僅僅是聲明容器打算使用什麼端口而已,並不會自動在宿主進行端口映射。

WORKDIR 指定工做目錄

格式爲 WORKDIR <工做目錄路徑>

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

以前提到一些初學者常犯的錯誤是把 Dockerfile 等同於 Shell 腳原本書寫,這種錯誤的理解還可能會致使出現下面這樣的錯誤:

RUN cd /app

RUN echo "hello" > world.txt

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

以前說過每個 RUN 都是啓動一個容器、執行命令、而後提交存儲層文件變動。第一層 RUN cd /app 的執行僅僅是當前進程的工做目錄變動,一個內存上的變化而已,其結果不會形成任何文件變動。而到第二層的時候,啓動的是一個全新的容器,跟第一層的容器更徹底不要緊,天然不可能繼承前一層構建過程當中的內存變化。

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

USER 指定當前用戶

格式:USER <用戶名

USER 指令和 WORKDIR 類似,都是改變環境狀態並影響之後的層。WORKDIR 是改變工做目錄,USER 則是改變以後層的執行 RUN, CMD 以及 ENTRYPOINT 這類命令的身份。

固然,和 WORKDIR 同樣,USER 只是幫助你切換到指定用戶而已,這個用戶必須是事先創建好的,不然沒法切換。

RUN groupadd -r redis && useradd -r -g redis redis

USER redis

RUN [ "redis-server" ]

若是以 root 執行的腳本,在執行期間但願改變身份,好比但願以某個已經創建好的用戶來運行某個服務進程,不要使用 su 或者 sudo,這些都須要比較麻煩的配置,並且在 TTY 缺失的環境下常常出錯。建議使用 gosu

創建 redis 用戶,並使用 gosu 換另外一個用戶執行命令

RUN groupadd -r redis && useradd -r -g redis redis

下載 gosu

RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/

gosu-amd64" \

&& chmod +x /usr/local/bin/gosu \

&& gosu nobody true

設置 CMD,並以另外的用戶執行

CMD [ "exec", "gosu", "redis", "redis-server" ]

HEALTHCHECK 健康檢查

格式:

 HEALTHCHECK [選項] CMD <命令>:設置檢查容器健康情況的命令

 HEALTHCHECK NONE:若是基礎鏡像有健康檢查指令,使用這行能夠屏蔽掉其健康檢查指令

HEALTHCHECK 指令是告訴 Docker 應該如何進行判斷容器的狀態是否正常,這是 Docker 1.12引入的新指令。

在沒有 HEALTHCHECK 指令前,Docker 引擎只能夠經過容器內主進程是否退出來判斷容器是否狀態異常。不少狀況下這沒問題,可是若是程序進入死鎖狀態,或者死循環狀態,應用進程並不退出,可是該容器已經沒法提供服務了。在 1.12 之前,Docker 不會檢測到容器的這種狀態,從而不會從新調度,致使可能會有部分容器已經沒法提供服務了卻還在接受用戶請求。

而自 1.12 以後,Docker 提供了 HEALTHCHECK 指令,經過該指令指定一行命令,用這行命令來判斷容器主進程的服務狀態是否還正常,從而比較真實的反應容器實際狀態。

當在一個鏡像指定了 HEALTHCHECK 指令後,用其啓動容器,初始狀態會爲 starting,在 HEALTHCHECK 指令檢查成功後變爲 healthy,若是連續必定次數失敗,則會變爲 unhealthy

HEALTHCHECK 支持下列選項:

l  --interval=<間隔>:兩次健康檢查的間隔,默認爲 30 ;

l  --timeout=<時長>:健康檢查命令運行超時時間,若是超過這個時間,本次健康檢查就被視爲失敗,默認 30 ;

l  --retries=<次數>:當連續失敗指定次數後,則將容器狀態視爲 unhealthy,默認 3次。

和 CMD, ENTRYPOINT 同樣,HEALTHCHECK 只能夠出現一次,若是寫了多個,只有最後一個生效。

在 HEALTHCHECK [選項] CMD 後面的命令,格式和 ENTRYPOINT 同樣,分爲 shell 格式,exec 格式。命令的返回值決定了該次健康檢查的成功與否:0:成功;1:失敗;2:保留,不要使用這個值。

假設咱們有個鏡像是個最簡單的 Web 服務,咱們但願增長健康檢查來判斷其 Web 服務是否在正常工做,咱們能夠用 curl 來幫助判斷,其 Dockerfile 的 HEALTHCHECK 能夠這麼寫:

FROM nginx

RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

HEALTHCHECK --interval=5s --timeout=3s \

CMD curl -fs http://localhost/ || exit 1

這裏咱們設置了每 秒檢查一次(這裏爲了試驗因此間隔很是短,實際應該相對較長),若是健康檢查命令超過 秒沒響應就視爲失敗,而且使用 curl -fs http://localhost/ || exit1 做爲健康檢查命令。

使用 docker build 來構建這個鏡像:

$ docker build -t myweb:v1 .

構建好了後,咱們啓動一個容器:

$ docker run -d --name web -p 80:80 myweb:v1

當運行該鏡像後,能夠經過 docker ps 看到最初的狀態爲 (health: starting):

$ docker ps

CONTAINER IDIMAGE COMMANDCREATED S

TATUSPORTS NAMES

03e28eb00bd0myweb:v1"nginx -g 'daemon off" 3 seconds ago U

p 2 seconds (health: starting) 80/tcp, 443/tcp web

在等待幾秒鐘後,再次 docker ps,就會看到健康狀態變化爲了 (healthy):

$ docker ps

CONTAINER IDIMAGE COMMANDCREATED S

TATUSPORTS NAMES

03e28eb00bd0myweb:v1"nginx -g 'daemon off" 18 seconds agoU

p 16 seconds (healthy) 80/tcp, 443/tcp web

若是健康檢查連續失敗超過了重試次數,狀態就會變爲 (unhealthy)

爲了幫助排障,健康檢查命令的輸出(包括 stdout 以及 stderr)都會被存儲於健康狀態裏,能夠用 docker inspect 來查看。

$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool

{

"FailingStreak": 0,

"Log": [

{

"End": "2016-11-25T14:35:37.940957051Z",

"ExitCode": 0,

"Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</titl

e>\n<style>\nbody {\nwidth: 35em;\nmargin: 0 auto;\nfont-f

amily: Tahoma, Verdana, Arial, sans-serif;\n}\n</style>\n</head>\n<body>\n<h1>Welc

ome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully inst

alled and\nworking. Further configuration is required.</p>\n\n<p>For online documentat

ion and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCo

mmercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n

<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",

"Start": "2016-11-25T14:35:37.780192565Z"

}

],

"Status": "healthy"

}

ONBUILD 爲他人作嫁衣裳

格式:ONBUILD <其它指令>

ONBUILD 是一個特殊的指令,它後面跟的是其它指令,好比 RUN, COPY ,而這些指令,在當前鏡像構建時並不會被執行。只有當以當前鏡像爲基礎鏡像,去構建下一級鏡像的時候纔會被執行。

Dockerfile 中的其它指令都是爲了定製當前鏡像而準備的,惟有 ONBUILD 是爲了幫助別人定製本身而準備的。

假設咱們要製做 Node.js 所寫的應用的鏡像。咱們都知道 Node.js 使用 npm 進行包管理,全部依賴、配置、啓動信息等會放到 package.json 文件裏。在拿到程序代碼後,須要先進行npm install 才能夠得到全部須要的依賴。而後就能夠經過 npm start 來啓動應用。所以,通常來講會這樣寫 Dockerfile:

FROM node:slim

RUN mkdir /app

WORKDIR /app

COPY ./package.json /app

RUN [ "npm", "install" ]

COPY . /app/

CMD [ "npm", "start" ]

把這個 Dockerfile 放到 Node.js 項目的根目錄,構建好鏡像後,就能夠直接拿來啓動容器運行。可是若是咱們還有第二個 Node.js 項目也差很少呢?好吧,那就再把這個 Dockerfile 複製到第二個項目裏。那若是有第三個項目呢?再複製麼?文件的副本越多,版本控制就越困難,讓咱們繼續看這樣的場景維護的問題。

若是第一個 Node.js 項目在開發過程當中,發現這個 Dockerfile 裏存在問題,好比敲錯字了、或者須要安裝額外的包,而後開發人員修復了這個 Dockerfile,再次構建,問題解決。第一個項目沒問題了,可是第二個項目呢?雖然最初 Dockerfile 是複製、粘貼自第一個項目的,可是並不會由於第一個項目修復了他們的 Dockerfile,而第二個項目的 Dockerfile 就會被自動修復。

那麼咱們可不能夠作一個基礎鏡像,而後各個項目使用這個基礎鏡像呢?這樣基礎鏡像更新,各個項目不用同步 Dockerfile 的變化,從新構建後就繼承了基礎鏡像的更新?好吧,能夠,讓咱們看看這樣的結果。那麼上面的這個 Dockerfile 就會變爲:

FROM node:slim

RUN mkdir /app

WORKDIR /app

CMD [ "npm", "start" ]

這裏咱們把項目相關的構建指令拿出來,放到子項目裏去。假設這個基礎鏡像的名字爲 my-node 的話,各個項目內的本身的 Dockerfile 就變爲:

FROM my-node

COPY ./package.json /app

RUN [ "npm", "install" ]

COPY . /app/

基礎鏡像變化後,各個項目都用這個 Dockerfile 從新構建鏡像,會繼承基礎鏡像的更新。

那麼,問題解決了麼?沒有。準確說,只解決了一半。若是這個 Dockerfile 裏面有些東西須要調整呢?好比 npm install 都須要加一些參數,那怎麼辦?這一行 RUN 是不可能放入基礎鏡像的,由於涉及到了當前項目的 ./package.json,難道又要一個個修改麼?因此說,這樣製做基礎鏡像,只解決了原來的 Dockerfile 的前4條指令的變化問題,然後面三條指令的變化則徹底沒辦法處理。

ONBUILD 能夠解決這個問題。讓咱們用 ONBUILD 從新寫一下基礎鏡像的 Dockerfile:

FROM node:slim

RUN mkdir /app

WORKDIR /app

ONBUILD COPY ./package.json /app

ONBUILD RUN [ "npm", "install" ]

ONBUILD COPY . /app/

CMD [ "npm", "start" ]

此次咱們回到原始的 Dockerfile,可是此次將項目相關的指令加上 ONBUILD,這樣在構建基礎鏡像的時候,這三行並不會被執行。而後各個項目的 Dockerfile 就變成了簡單地:

FROM my-node

是的,只有這麼一行。當在各個項目目錄中,用這個只有一行的 Dockerfile 構建鏡像時,以前基礎鏡像的那三行 ONBUILD 就會開始執行,成功的將當前項目的代碼複製進鏡像、而且針對本項目執行 npm install,生成應用鏡像。

五、Docker容器命令

當利用docker run來建立容器時,Docker 在後臺運行的標準操做包括:

l 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載。

l 利用鏡像建立並啓動一個容器。

l 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層。

l 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去。

l 從地址池配置一個 ip 地址給容器。

l 執行用戶指定的應用程序。

l 執行完畢後容器被終止。

一、docker run
語法:

  • docker run [options] IMAGE [command] [args]

OPTIONS說明:

  • -a stdin: 指定標準輸入輸出內容類型,可選 STDIN/STDOUT/STDERR 三項;
  • -d: 後臺運行容器,並返回容器ID;
  • -i: 以交互模式運行容器,一般與 -t 同時使用;
  • -t: 爲容器從新分配一個僞輸入終端,一般與 -i 同時使用;
  • --name="nginx-lb": 爲容器指定一個名稱;
  • --dns 8.8.8.8: 指定容器使用的DNS服務器,默認和宿主一致;
  • --dns-search example.com: 指定容器DNS搜索域名,默認和宿主一致;
  • -h "mars": 指定容器的hostname;
  • -e username="ritchie": 設置環境變量;
  • --env-file=[]: 從指定文件讀入環境變量;
  • --cpuset="0-2" or --cpuset="0,1,2": 綁定容器到指定CPU運行;
  • -m :設置容器使用內存最大值;
  • --net="bridge": 指定容器的網絡鏈接類型,支持 bridge(在容器內部、主機上建立一對網絡設備,在容器內部爲eth0,而外部則是veth*網絡設備,這個設備加在了主機上的Docker0網橋之中,從而實現容器與外部的通訊)、host(容器和主機共用一個Network Namespace)、none(擁有本身的Network Namespace,但不爲容器進行任何網絡配置,須要用戶自行添加)、container(容器不與主機共享Network Namespace地址空間,而是與一個指定的容器共享): 四種類型。 相關文章:四種網絡模式 https://blog.csdn.net/noob_f/article/details/52875664
  • --link=[]: 添加連接到另外一個容器;
  • --expose=[]: 開放一個端口或一組端口;
  • -p,--publish=[]:容器內的端口服務在主機OS上是沒法訪問的,這就須要提早對外發布端口,咱們也能夠認爲是端口映射(ip:hostport:containerport)
  • --rm:在容器運行完畢後自動刪除容器(這個選項不能與-d同時使用)
  • -v,--volume=[]:容器中的數據會隨着容器生命週期的結束而消失,咱們能夠經過該選項將外部存儲映射到容器內,將外部數據給容器訪問,或者將容器的數據保存到外部(/host:/container)
  • --volumes-from=[]:從另一個容器中mount卷
  • -w,--workdir=" ":設置容器中的工做文件夾
  • --name=" ":分配一個容器名字,若是不指定,則會自動生成
  • -u,--user=" ":指定容器運行後的uid或用戶名
  • -c,--cpu-shares=0:cpu-shares是一個權重值,當多個容器運行在相同的CPU資源上時,會依據此權重值進行資源分配
  • --restart:設定容器重起策略。
    • no,默認策略,在容器退出時不重啓容器。
    • on-failure,在容器非正常退出時(退出狀態非0),纔會重啓容器。
    • on-failure:3,在容器非正常退出時重啓容器,最多重啓3次。
    • always,在容器退出時老是重啓容器。
    • unless-stopped,在容器退出時老是重啓容器,可是不考慮在Docker守護進程啓動時就已經中止了的容器。

 

-P參數 外部訪問容器

容器中能夠運行一些網絡應用,要讓外部也能夠訪問這些應用,能夠經過-P-p參數來指定端口映射。

當使用-P標記時,Docker會隨機映射一個49000~49900的端口到內部容器開放的網絡端口。

使用docker ps -a能夠看到,本地主機的49155被映射到了容器的5000端口。此時訪問本機的49155端口便可訪問容器內web應用提供的界面。

-p參數 外部訪問容器

-p則能夠指定要映射的端口,而且,在一個指定端口上只能夠綁定一個容器。支持的格式有ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort

 

使用hostPort:containerPort格式,本地的5000端口映射到容器的5000端口,能夠執行:

$ docker run -d -p 5000:5000 training/webapp python app.py

此時默認會綁定本地全部接口上的全部地址。

 

可使用ip:hostPort:containerPort格式,指定映射使用一個特定地址,好比 localhost 地址127.0.0.1,能夠執行:

$ docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py

 

使用ip::containerPort格式,綁定localhost的任意端口到容器的5000端口,本地主機會自動分配一個端口。,能夠執行:

$ docker run -d -p 127.0.0.1::5000 training/webapp python app.py

 

還可使用udp標記來指定udp端口

$ docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py

 

-p標記能夠屢次使用來綁定多個端口

 

二、docker start
語法:

  • docker start [-i] [-a] <container(s)>

選項基本與run同樣。

三、docker stop

四、docker restart
選項基本與run同樣。

五、docker attach : 鏈接到正在運行中的容器。
選項:

  • --sig-proxy=false,確保CTRL-D或CTRL-C不會關閉容器。

 

六、docker ps : 列出容器
語法:

  • docker ps [OPTIONS]

OPTIONS說明:

  • -a,--all :顯示全部的容器,包括未運行的。
  • -f :根據條件過濾顯示的內容。
  • --format :指定返回值的模板文件。
  • -l,--latest :顯示最近建立的容器。
  • -n :列出最近建立的n個容器。
  • --no-trunc :不截斷輸出。
  • -q,--quit :靜默模式,只顯示容器編號。
  • -s,--size :顯示總的文件大小。
  • --before=" " :顯示在某個容器ID以前啓動的全部容器,包括中止的容器
  • --after=" " :顯示在某個容器ID以後啓動的全部容器,包括中止的容器

 

七、docker inspect : 獲取容器/鏡像的元數據。
語法:

  • docker inspect [OPTIONS] NAME|ID [NAME|ID...]

OPTIONS說明:

  • -f :指定返回值的模板文件。
  • -s :顯示總的文件大小。
  • --type :爲指定類型返回JSON。

實例,獲取鏡像mysql:5.6的元信息:
$ docker inspect mysql:5.6
實例,獲取正在運行的容器mymysql的 IP:
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mymysql

七、docker rm 刪除容器

$ docker rm trusting_newton

trusting_newton

若是要刪除一個運行中的容器,能夠添加 -f 參數。Docker 會發送 SIGKILL 信號給容器。

八、docker exec 進入容器

在使用 -d 參數時,容器啓動後會進入後臺。某些時候須要進入容器進行操做,此時須要使用docker exec 命令。

docker exec命令只用-i交互式操做參數時,因爲沒有分配僞終端,界面沒有咱們熟悉的Linux命令提示符,但命令執行結果仍然能夠返回。當-i交互式操做、-t終端參數一塊兒使用時,則能夠看到咱們熟悉的 Linux 命令提示符。

$ docker run -dit ubuntu

69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6

$ docker ps

CONTAINER IDIMAGE COMMAND CREATED STATUS

PORTS NAMES

69d137adef7aubuntu:latest "/bin/bash" 18 seconds agoUp 17 

seconds zealous_swirles

docker exec -i 69d1 bash

ls

bin

boot

dev

...

docker exec -it 69d1 bash

root@69d137adef7a:/#

九、docker diff 查看容器中具體的改動

咱們修改了容器的文件,也就是改動了容器的存儲層。咱們能夠經過docker diff命令看到具體的改動。

$ docker diff webserver

 

十、docker port 查看映射地址:端口配置

$ docker port nostalgic_morse 5000

127.0.0.1:49155.

十一、docker logs 獲取容器的輸出信息

 

十二、docker export 導出本地某個容器

$ docker export 7691a814370e > ubuntu.tar

這樣將導出容器快照到本地文件。

1三、docker commit 將容器保存爲鏡像

語法格式爲:

docker commit [選項] <容器ID或容器名> [<倉庫名>[:<標籤>]]

咱們能夠用下面的命令將容器保存爲鏡像:

$ docker commit --author "Tao Wang <twang2218@gmail.com>" --message "修改了默認網頁" webserver nginx:v2

sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214

其中--author是指定修改的做者,--message則是記錄本次修改的內容。這點和git版本控制類似,不過這裏這些信息能夠省略留空。

1四、docker import 從容器快照文件中再導入爲鏡像

$ cat ubuntu.tar | docker import - test/ubuntu:v1.0

此外,也能夠經過指定 URL 或者某個目錄來導入,例如

$ docker import http://example.com/exampleimage.tgz example/imagerepo

:用戶既可使用 docker load 來導入鏡像存儲文件到本地鏡像庫,也可使用 docker import 來導入一個容器快照到本地鏡像庫。這二者的區別在於容器快照文件將丟棄全部的歷史記錄和元數據信息(即僅保存容器當時的快照狀態),而鏡像存儲文件將保存完整記錄,體積也要大。此外,從容器快照文件導入時能夠從新指定標籤等元數據信息。

 

六、倉庫 registry

鏡像構建完成後,能夠很容易的在當前宿主機上運行,可是,若是須要在其它服務器上使用這個鏡像,咱們就須要一個集中的存儲、分發鏡像的服務,Docker Registry 就是這樣的服務。

一個 Docker Registry 中能夠包含多個倉庫(Repository);每一個倉庫能夠包含多個標籤(Tag);每一個標籤對應一個鏡像。

一般,一個倉庫會包含同一個軟件不一樣版本的鏡像,而標籤就經常使用於對應該軟件的各個版本。咱們能夠經過<倉庫名>:<標籤>的格式來指定具體是這個軟件哪一個版本的鏡像。若是不給出標籤,將以latest做爲默認標籤。

以 Ubuntu 鏡像 爲例,ubuntu是倉庫的名字,其內包含有不一樣的版本標籤,,14.04,16.04。咱們能夠經過ubuntu:14.04,或者ubuntu:16.04來具體指定所需哪一個版本的鏡像。若是忽略了標籤,好比ubuntu,那將視爲ubuntu:latest

倉庫名常常以 兩段式路徑 形式出現,好比jwilder/nginx-proxy,前者每每意味着 Docker Registry 多用戶環境下的用戶名,後者則每每是對應的軟件名。但這並不是絕對,取決於所使用的具體 Docker Registry 的軟件或服務。

公共倉庫 Docker Hub

Docker Registry公開服務是開放給用戶使用、容許用戶管理鏡像的 Registry 服務。通常這類公開服務容許用戶免費上傳、下載公開的鏡像,並可能提供收費服務供用戶管理私有鏡像。

最常使用的 Registry 公開服務是官方的 Docker Hub,這也是默認的 Registry,並擁有大量的高質量的官方鏡像。除此之外,還有 CoreOS 的 Quay.io,CoreOS 相關的鏡像存儲在這裏。Google 的 Google Container Registry,Kubernetes 的鏡像使用的就是這個服務。

因爲某些緣由,在國內訪問這些服務可能會比較慢。國內的一些雲服務商提供了針對Docker Hub的鏡像服務(Registry Mirror),這些鏡像服務被稱爲加速器。常見的有 阿里雲加速器、DaoCloud 加速器 等。使用加速器會直接從國內的地址下載Docker Hub的鏡像,比直接從Docker Hub下載速度會提升不少。在 安裝 Docker 一節中有詳細的配置方法。

國內也有一些雲服務商提供相似於Docker Hub的公開服務。好比 時速雲鏡像倉庫、網易雲鏡像服務、DaoCloud 鏡像市場、阿里雲鏡像庫 等。

目前 Docker 官方維護了一個公共倉庫 Docker Hub,其中已經包括了數量超過 15,000 的鏡像。大部分需求均可以經過在Docker Hub中直接下載鏡像來實現。

註冊

能夠在 https://cloud.docker.com 免費註冊一個 Docker 帳號。

一、docker login 登陸

能夠經過執行docker login命令交互式的輸入用戶名及密碼來完成在命令行界面登陸Docker Hub

二、docker logout 退出登陸

能夠經過docker logout退出登陸。

三、docker search 查找官方倉庫中的鏡像

能夠經過docker search命令來查找官方倉庫中的鏡像。例如以centos爲關鍵詞進行搜索:

$ docker search centos

能夠看到返回了不少包含關鍵字的鏡像,其中包括鏡像名字、描述、收藏數(表示該鏡像的受關注程度)、是否官方建立、是否自動建立。

官方的鏡像說明是官方項目組建立和維護的,automated 資源容許用戶驗證鏡像的來源和內容。

根據是不是官方提供,可將鏡像資源分爲兩類。

一種是相似centos這樣的鏡像,被稱爲基礎鏡像或根鏡像。這些基礎鏡像由 Docker 公司建立、驗證、支持、提供。這樣的鏡像每每使用單個單詞做爲名字。

還有一種類型,好比tianon/centos鏡像,它是由 Docker 的用戶建立並維護的,每每帶有用戶名稱前綴。能夠經過前綴username/來指定使用某個用戶提供的鏡像,好比 tianon 用戶。

另外,在查找的時候經過--filter=stars=N參數能夠指定僅顯示收藏數量爲N以上的鏡像。

四、docker pul拉取鏡像

利用docker pull命令來將它下載到本地。例以下載官方centos鏡像到本地

$ docker pull centos

五、docker push 推送鏡像

用戶也能夠在登陸後經過docker push命令來將本身的鏡像推送到 Docker Hub。如下命令中的username請替換爲你的 Docker 帳號用戶名。

$ docker tag ubuntu:17.10 username/ubuntu:17.10

$ docker images

$ docker push username/ubuntu:17.10

$ docker search username

六、自動建立

自動建立(Automated Builds)功能對於須要常常升級鏡像內程序來講,十分方便。

有時候,用戶建立了鏡像,安裝了某個軟件,若是軟件發佈新版本則須要手動更新鏡像。

而自動建立容許用戶經過Docker Hub指定跟蹤一個目標網站(目前支持 GitHub BitBucket)上的項目,一旦項目發生新的提交或者建立新的標籤(tag),Docker Hub 會自動構建鏡像並推送到Docker Hub中。

要配置自動建立,包括以下的步驟:

l 建立並登陸 Docker Hub,以及目標網站;

l 在目標網站中鏈接賬戶到 Docker Hub;

l 在Docker Hub中 配置一個自動建立;

l 選取一個目標網站中的項目(須要含Dockerfile)和分支;

l 指定Dockerfile的位置,並提交建立。

以後,能夠在Docker Hub的 自動建立頁面 中跟蹤每次建立的狀態。

私有倉庫

除了使用公開服務外,用戶還能夠在本地搭建私有 Docker RegistryDocker 官方提供了Docker Registry 鏡像,能夠直接使用作爲私有 Registry 服務。

開源的 Docker Registry 鏡像只提供了 Docker Registry API 的服務端實現,足以支持docker命令,不影響使用。但不包含圖形界面,以及鏡像維護、用戶管理、訪問控制等高級功能。在官方的商業化版本 Docker Trusted Registry ,提供了這些高級功能。

除了官方的 Docker Registry ,還有第三方軟件實現了 Docker Registry API,甚至提供了用戶界面以及一些高級功能。好比,VMWare Harbor 和 Sonatype Nexus

本文內容基於docker-registryv2.x 版本。

一、運行docker-registry

經過獲取官方registry鏡像來運行。

$ docker run -d -p 5000:5000 --restart=always -v /opt/data/registry:/var/lib/registry --name registry registry

這將使用官方的registry鏡像來啓動私有倉庫。-v參數,將上傳的鏡像文件放到指定的本地/opt/data/registry目錄,默認狀況下,倉庫會被建立在容器的/var/lib/registry目錄下

二、docker push 上傳鏡像

建立好私有倉庫以後,就可使用docker tag來標記一個鏡像,而後推送它到倉庫。例如私有倉庫地址爲127.0.0.1:5000

先在本機查看已有的鏡像。

$ docker images

REPOSITORY    TAG    IMAGE    IDCREATED    VIRTUAL    SIZE

ubuntu    latest   ba5877dc9bec6    weeks ago    192.7 MB

 

使用docker tagubuntu:latest這個鏡像標記爲127.0.0.1:5000/ubuntu:latest

格式爲docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest

docker images

 

使用docker push上傳標記的鏡像。

docker push 127.0.0.1:5000/ubuntu:latest

 

三、curl 搜索鏡像

curl查看倉庫中的鏡像。能夠看到{"repositories":["ubuntu"]},代表鏡像已經被成功上傳了。或者進入私有倉庫容器/var/lib/registry目錄下查看。

curl 127.0.0.1:5000/v2/_catalog

{"repositories":["ubuntu"]}

四、docker pull 下載鏡像

先刪除已有鏡像,再嘗試從私有倉庫中下載這個鏡像。

$ docker image rm 127.0.0.1:5000/ubuntu:latest

 

docker pull 127.0.0.1:5000/ubuntu:latest

 

$ docker images

 

五、向其餘主機的私有倉庫推送

若是你不想使用127.0.0.1:5000做爲倉庫地址,好比想讓本網段的其餘主機也能把鏡像推送到私有倉庫。你就得把例如192.168.199.100:5000這樣的內網地址做爲私有倉庫地址,這時你會發現沒法成功推送鏡像。

這是由於 Docker 默認不容許非HTTPS方式推送鏡像。咱們能夠經過 Docker 的配置選項來取消這個限制,或者查看下一節配置可以經過HTTPS訪問的私有倉庫。

Ubuntu 14.04, Debian 7 Wheezy

對於使用upstart的系統而言,編輯/etc/default/docker文件,在其中的DOCKER_OPTS 中增長以下內容:

DOCKER_OPTS="--registry-mirror=https://registry.docker-cn.com --insecure-registries=192.168.199.100:5000"

從新啓動服務。

$ sudo service docker restart

Ubuntu 16.04+, Debian 8+, centos 7

對於使用systemd的系統,請在/etc/docker/daemon.json中寫入以下內容(若是文件不存在請新建該文件)

{

"registry-mirror": [

 "https://registry.docker-cn.com"

],

"insecure-registries": [

 "192.168.199.100:5000"

]

}

注意:該文件必須符合json規範,不然 Docker 將不能啓動。

其餘

對於 Docker for Windows 、 Docker for Mac 在設置中編輯daemon.json增長和上邊同樣的字符串便可。

私有倉庫高級配置

上一節咱們搭建了一個具備基礎功能的私有倉庫,本小節咱們來使用Docker Compose搭建一個擁有權限認證、TLS 的私有倉庫。

新建一個文件夾,如下步驟均在該文件夾中進行。

準備站點證書

若是你擁有一個域名,國內各大雲服務商均提供免費的站點證書。你也可使用openssl自行簽發證書。

這裏假設咱們將要搭建的私有倉庫地址爲docker.domain.com,下面咱們介紹使用openssl自行簽發docker.domain.com的站點 SSL 證書。

 

第一步建立CA私鑰。

$ openssl genrsa -out "root-ca.key" 4096

 

第二步利用私鑰建立CA根證書請求文件。

$ openssl req \

 -new -key "root-ca.key" \

 -out "root-ca.csr" -sha256 \

 -subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=Your Company Name Doc

ker Registry CA'

以上命令中-subj參數裏的/C表示國家,CN;/ST表示省;/L表示城市或者地區;/O表示組織名;/CN通用名稱。

 

第三步配置CA根證書,新建root-ca.cnf

[root_ca]

basicConstraints = critical,CA:TRUE,pathlen:1

keyUsage = critical, nonRepudiation, cRLSign, keyCertSign

subjectKeyIdentifier=hash

 

第四步簽發根證書。

$ openssl x509 -req-days 3650-in "root-ca.csr" \

-signkey "root-ca.key" -sha256 -out "root-ca.crt" \

-extfile "root-ca.cnf" -extensions \

root_ca

 

第五步生成站點SSL私鑰。

$ openssl genrsa -out "docker.domain.com.key" 4096

 

第六步使用私鑰生成證書請求文件。

$ openssl req -new -key "docker.domain.com.key" -out "site.csr" -sha256 \

 -subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=docker.domain.com'

 

第七步配置證書,新建site.cnf文件。

[server]

authorityKeyIdentifier=keyid,issuer

basicConstraints = critical,CA:FALSE

extendedKeyUsage=serverAuth

keyUsage = critical, digitalSignature, keyEncipherment

subjectAltName = DNS:docker.domain.com, IP:127.0.0.1

subjectKeyIdentifier=hash

 

第八步簽署站點SSL證書。

$ openssl x509 -req -days 750 -in "site.csr" -sha256 \

 -CA "root-ca.crt" -CAkey "root-ca.key"-CAcreateserial \

 -out "docker.domain.com.crt" -extfile "site.cnf" -extensions server

 

這樣已經擁有了docker.domain.com的網站 SSL 私鑰docker.domain.com.key和 SSL 證書 docker.domain.com.crt

新建ssl文件夾並將docker.domain.com.keydocker.domain.com.crt這兩個文件移入,刪除其餘文件。

配置私有倉庫

私有倉庫默認的配置文件位於/etc/docker/registry/config.yml,咱們先在本地編輯config.yml,以後掛載到容器中。

version: 0.1

log:

accesslog:

 disabled: true

level: debug

formatter: text

fields:

 service: registry

 environment: staging

storage:

delete:

 enabled: true

cache:

 blobdescriptor: inmemory

filesystem:

 rootdirectory: /var/lib/registry

auth:

htpasswd:

 realm: basic-realm

 path: /etc/docker/registry/auth/nginx.htpasswd

http:

addr: :443

host: https://docker.domain.com

headers:

 X-Content-Type-Options: [nosniff]

http2:

 disabled: false

tls:

 certificate: /etc/docker/registry/ssl/docker.domain.com.crt

 key: /etc/docker/registry/ssl/docker.domain.com.key

health:

storagedriver:

 enabled: true

 interval: 10s

threshold: 3

生成 http 認證文件

$ mkdir auth

$ docker run --rm \

 --entrypoint htpasswd \

 registry \

 -Bbn username password > auth/nginx.htpasswd

將上面的usernamepassword替換爲你本身的用戶名和密碼。

編輯docker-compose.yml

version: '3'

services:

registry:

 image: registry

 ports:

- "443:443"

 volumes:

- ./:/etc/docker/registry

- registry-data:/var/lib/registry

volumes:

registry-data:

修改 hosts

編輯/etc/hosts 

docker.domain.com 127.0.0.1

啓動

$ docker-compose up -d

這樣咱們就搭建好了一個具備權限認證、TLS 的私有倉庫,接下來咱們測試其功能是否正常。

測試私有倉庫功能

登陸到私有倉庫。

$ docker login docker.domain.com

 

嘗試推送、拉取鏡像。

$ docker pull ubuntu:17.10

$ docker tag ubuntu:17.10 docker.domain.com/username/ubuntu:17.10

$ docker push docker.domain.com/username/ubuntu:17.10

$ docker image rm docker.domain.com/username/ubuntu:17.10

$ docker pull docker.domain.com/username/ubuntu:17.10

 

若是咱們退出登陸,嘗試推送鏡像。

$ docker logout docker.domain.com

$ docker push docker.domain.com/username/ubuntu:17.10

no basic auth credentials

發現會提示沒有登陸,不能將鏡像推送到私有倉庫中。

注意事項

若是你本機佔用了443端口,你能夠配置 Nginx 代理,這裏再也不贅述。

相關文章
相關標籤/搜索