02 . DockerFile構建鏡像和Docker倉庫


為什么用DockerFile

Dockerfile 是一個用來構建鏡像的文本文件,文本內容包含了一條條構建鏡像所需的指令和說明。

說dockerfile之前我們先說一下dockercommit

利用commit理解鏡像構成

注意: docker commit 命令除了學習之外,還有一些特殊的應用場合,比如被入侵后保存現 場等。但是,不要使用 docker commit 定制鏡像,定制鏡像應該使用 Dockerfile 來完成

鏡像是容器的基礎,每次執行 docker run 的時候都會指定哪個鏡像作為容器運行的基礎。

我們之前所使用的鏡像都是docker hub等網站上的,直接使用這些鏡像可以滿足一定的需求,而當這些鏡像無法直接滿足需求時候,我們需要定制這些鏡像.之前有說過,鏡像是多層存儲,每一層是在前一層的基礎上進行的修改,而容器也是多層存儲,是在以鏡像為基礎層,在其基礎上加一層作為容器運行時的存儲層.

定制一個Web服務器
docker pull daocloud.io/library/nginx

docker run --name webserver -d -p 80:80 daocloud.io/library/nginx
# 我們訪問這個web服務,會看到nginx的默認歡迎界面,假如我不喜歡這個界面,想看到歡迎docker的文字,可以exec
curl 119.3.255.91
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

docker exec -it webserver bash
root@6e63ab0d5109:/# echo '<h1>Hello,Docker!</h1>'> /usr/share/nginx/html/index.html 
root@6e63ab0d5109:/# exit
exit

# 我們在訪問時候就發現內容改變了
curl 119.3.255.91
<h1>Hello,Docker!</h1>
# 我們修改了容器的文件,就是改動了容器的存儲層,我們可以通過docker diff命令看到具體變動
docker diff webserver
C /root
A /root/.bash_history
C /run
A /run/nginx.pid
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
A /var/cache/nginx/client_temp
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html

現在我們定制好了變化,我們希望將其保存下來形成鏡像.

要知道,當我們運行一個容器的時候(如果不使用卷的話),我們做的任何文件修改都會被 記錄於容器存儲層里。而 Docker 提供了一個 docker commit 命令,可以將容器的存儲層保 存下來成為鏡像。換句話說,就是在原有鏡像的基礎上,再疊加上容器的存儲層,並構成新 的鏡像。以后我們運行這個新鏡像的時候,就會擁有原有容器最后的文件變化(類似於虛擬機的快照)。

docker commit 的語法格式為:
docker commit  [選項] <容器ID或容器名> [<倉庫名>[:<標簽>]]
# 我們可以用下面的命令將容器保存為鏡像
docker commit -a="youmen <18621048481@163.com>" -m="edited nginx Default page" webserver nginx:v2
* -m 提交的描述信息   
* -a 指定鏡像作者   
* webserver為你要給那個容器做成鏡像的那個容器名字或者ID
* nginx:v2是形成的新鏡像名

# 接下來我們基於這個創建好的新鏡像再啟動一個容器.
docker run --name web2 -d -p 81:80 nginx:v2
curl `cat ip.txt`:81 
<h1>Hello,Docker!</h1>
# 就這樣,我們完成了一次基於容器構建定制鏡像的操作.
慎用docker commit

使用 docker commit 命令雖然可以比較直觀的幫助理解鏡像分層存儲的概念,但是實際環境 中並不會這樣使用。

首先,如果仔細觀察之前的 docker diff webserver 的結果,你會發現除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由於命令的執行,還有很多文件被改動或添加 了。這還僅僅是最簡單的操作,如果是安裝軟件包、編譯構建,那會有大量的無關內容被添 加進來,如果不小心清理,將會導致鏡像極為臃腫。

此外,使用 docker commit 意味着所有對鏡像的操作都是黑箱操作,生成的鏡像也被稱為黑 箱鏡像,換句話說,就是除了制作鏡像的人知道執行過什么命令、怎么生成的鏡像,別人根 本無從得知。而且,即使是這個制作鏡像的人,過一段時間后也無法記清具體在操作的。雖 然 docker diff 或許可以告訴得到一些線索,但是遠遠不到可以確保生成一致鏡像的地步。 這種黑箱鏡像的維護工作是非常痛苦的。

而且,回顧之前提及的鏡像所使用的分層存儲的概念,除當前層外,之前的每一層都是不會 發生改變的,換句話說,任何修改的結果僅僅是在當前層進行標記、添加、修改,而不會改 動上一層。如果使用 docker commit 制作鏡像,以及后期修改的話,每一次修改都會讓鏡像 更加臃腫一次,所刪除的上一層的東西並不會丟失,會一直如影隨形的跟着這個鏡像,即使 根本無法訪問到。這會讓鏡像更加臃腫.

使用Dockerfile定制鏡像

從剛才的 docker commit 的學習中,我們可以了解到,鏡像的定制實際上就是定制每一層所 添加的配置、文件。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入一個腳 本,用這個腳本來構建、定制鏡像,那么之前提及的無法重復的問題、鏡像構建透明性的問 題、體積的問題就都會解決。這個腳本就是 Dockerfile。

Dockerfile 是一個文本文件,其內包含了一條條的指令(Instruction),每一條指令構建一層, 因此每一條指令的內容,就是描述該層應當如何構建。

還以之前定制 nginx 鏡像為例,這次我們使用 Dockerfile 來定制。

在一個空白目錄中,建立一個文本文件,並命名為 Dockerfile :

cat DockerFile
mkdir mynginx
cd mynginx
vim Dockerfile
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
FROM指定基礎鏡像

FROM為指定基礎鏡像,我們定制鏡像,肯定要以一個鏡像為基礎,在其上做定制,而FROM就是指定基礎鏡像,因此一個Dockerfile中FROM是必備的命令,並且必須是第一條指令.

在Docker Store上有非常多高質量的官方鏡像,有可以直接拿來使用的服務類鏡像,如nginx,redis,mysql,mongo,tomcat等,也有方便開發、構建、運行各種語言的鏡像,如node,openjdk,python,ruby,golang等,可以在其中尋找一個最符合我們的鏡像為基礎鏡像進行定制.

如果還沒有找到對應服務的鏡像,官方鏡像中提供了一些更為基礎的操作系統鏡像,如ubuntu,debian,centos,fedora,alpine等,這些操作系統的軟件庫為我們提供了更廣闊的擴展空間.

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

如果你以 scratch 為基礎鏡像的話,意味着你不以任何鏡像為基礎,接下來所寫的指令將作 為鏡像第一層開始存在。 不以任何系統為基礎,直接將可執行文件復制進鏡像的做法並不罕見,比如 swarm 、 coreos/etcd 。對於 Linux 下靜態編譯的程序來說,並不需要有操作系統提供運行時 支持,所需的一切庫都已經在可執行文件里了,因此直接 FROM scratch 會讓鏡像體積更加小 巧。使用 Go 語言 開發的應用很多會使用這種方式來制作鏡像,這也是為什么有人認為 Go 是特別適合容器微服務架構的語言的原因之一。

RUN 執行命令

RUN 指令是用來執行命令行命令的。由於命令行的強大能力, RUN 指令在定制鏡像時是最 常用的指令之一。其格式有兩種:

shell 格式: RUN <命令> ,就像直接在命令行中輸入的命令一樣。剛才寫的 Dockerfile 中 的 RUN 指令就是這種格式。

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

exec 格式: RUN ["可執行文件", "參數1", "參數2"] ,這更像是函數調用中的格式。 既然 RUN 就像 Shell 腳本一樣可以執行命令,那么我們是否就可以像 Shell 腳本一樣把每個 命令對應一個 RUN 呢?比如這樣:

cat Dockerfile
FROM debian:jessie 

RUN apt-get update 
RUN apt-get install -y gcc libc6-dev make 
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.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

之前說過,Dockerfile 中每一個指令都會建立一層, RUN 也不例外。每一個 RUN 的行為, 就和剛才我們手工建立鏡像的過程一樣:新建立一層,在其上執行這些命令,執行結束 后, commit 這一層的修改,構成新的鏡像。

而上面的這種寫法,創建了 7 層鏡像。這是完全沒有意義的,而且很多運行時不需要的東 西,都被裝進了鏡像里,比如編譯環境、更新的軟件包等等。結果就是產生非常臃腫、非常 多層的鏡像,不僅僅增加了構建部署的時間,也很容易出錯。 這是很多初學 Docker 的人常 犯的一個錯誤。

Union FS 是有最大層數限制的,比如 AUFS,曾經是最大不得超過 42 層,現在是不得超過 127 層。因此上面Dockerfile可以這樣寫:

cat DockerFile
FROM debian:jessie 
RUN buildDeps='gcc libc6-dev make' \ 
&& apt-get update \ && apt-get install -y $buildDeps \ 
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.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

首先,之前所有的命令只有一個目的,就是編譯、安裝 redis 可執行文件。因此沒有必要建立 很多層,這只是一層的事情。因此,這里沒有使用很多個 RUN 對一一對應不同的命令,而是 僅僅使用一個 RUN 指令,並使用 && 將各個所需命令串聯起來。將之前的 7 層,簡化為了 1 層。在撰寫 Dockerfile 的時候,要經常提醒自己,這並不是在寫 Shell 腳本,而是在定義每 一層該如何構建。

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

此外,還可以看到這一組命令的最后添加了清理工作的命令,刪除了為了編譯構建所需要的 軟件,清理了所有下載、展開的文件,並且還清理了 apt 緩存文件。這是很重要的一步,我 們之前說過,鏡像是多層存儲,每一層的東西並不會在下一層被刪除,會一直跟隨着鏡像。 因此鏡像構建時,一定要確保每一層只添加真正需要添加的東西,任何無關的東西都應該清 理掉。

很多人初學 Docker 制作出了很臃腫的鏡像的原因之一,就是忘記了每一層構建的最后一定要 清理掉無關文件。

構建鏡像

在之前Dockerfile文件所在目錄執行

cat DockerFile
mkdir mynginx
cd mynginx
# 我們繼續編輯一下DockerFile
vim DockerFile
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html


docker build -t nginx:v3 .
# 最后的.表示本文執行的上下文路徑
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM nginx
 ---> 231d40e811cd
Step 2/2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in 3a439666c4da
Removing intermediate container 3a439666c4da
 ---> 78990dc4a6a6
Successfully built 78990dc4a6a6
Successfully tagged nginx:v3

從命令的輸出結果中,我們可以清晰的看到鏡像的構建過程。在 Step 2 中,如同我們之前 所說的那樣, RUN 指令啟動了一個容器231d40e811cd,執行了所要求的命令,並最后提交 了這一層 3a439666c4da ,隨后刪除了所用到的這個容器78990dc4a6a6。

docker build [選項] <上下文路徑/URL/->

構建成功后我們可以跟nginx:v2那樣運行這個鏡像,結果一樣.

這只是默認行為,實際上 Dockerfile 的文件名並不要求必須為 Dockerfile ,而且並不要求 必須位於上下文目錄中,比如可以用 -f ../Dockerfile.php 參數指定某個文件作為 Dockerfile

上下文路徑

是指在docker構建鏡像,有時候想要使用本機的文件(比如復制),docker build命令知道這個路徑后,會將這個路徑下所有內容打包.

由於 docker 的運行模式是 C/S。我們本機是 C,docker 引擎是 S。實際的構建過程是在 docker 引擎下完成的,所以這個時候無法用到我們本機的文件。這就需要把我們本機的指定目錄下的文件一起打包提供給 docker 引擎使用。

如果未說明最后一個參數,那么默認上下文路徑就是 Dockerfile 所在的位置。

注意:上下文路徑下不要放無用的文件,因為會一起打包發送給 docker 引擎,如果文件過多會造成過程緩慢。

Docker build的用法

直接用git repo 進行構建

yum -y install git
docker build https://github.com/twang2218/gitlab-ce-zh.git:8.14 docker 
build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14 
Sending build context to Docker daemon 2.048 kB Step 1 : 
FROM gitlab/gitlab-ce:8.14.0-ce.0 8.14.0-ce.0: 
Pulling from gitlab/gitlab-ce

這行命令指定了構建所需的Git repo,並且指定默認的master分支,構建目錄為/8.14/,然后Docker就會自己去git clone這個項目,切換到指定分支,並進入到指定目錄后開始構建.

用給定的tar壓縮包構建

docker build http://server/context.tar.gz
# 如果所給出的 URL 不是個 Git repo,而是個 tar 壓縮包,那么 Docker 引擎會下載這個包,
# 並自動解壓縮,以其作為上下文,開始構建。

從標准輸入中讀取Dockerfile進行構建

docker build - < Dockerfile
or
cat Dockerfile | docker build -

如果標准輸入傳入的是文本文件,則將其視為 Dockerfile ,並開始構建。這種形式由於直接 從標准輸入中讀取 Dockerfile 的內容,它沒有上下文,因此不可以像其他方法那樣可以將本 地文件 COPY 進鏡像之類的事情。

從標准輸入中讀取上下文壓縮包進行構建

docker build - < context.tar.gz
# 如果發現標准輸入的文件格式是gzip、bzip2、以及xz的話,將會使其為上下文壓縮包,直接將其展開,
# 將里面視為上下文,並開始構建.

Dockerfile指令詳解

FROM 和 RUN 指令的作用

FROM:定制的鏡像都是基於 FROM 的鏡像,這里的 nginx 就是定制需要的基礎鏡像。后續的操作都是基於 nginx。

RUN:用於執行后面跟着的命令行命令。有以下倆種格式

shell格式

RUN <命令行命令>
# <命令行命令> 等同於,在終端操作的 shell 命令。

exec格式

RUN ["可執行文件", "參數1", "參數2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等價於 RUN ./test.php dev offline

注意: Dockerfile的指令是每執行一次都會在docker上新建一層,所以過多無意義的層,會造成鏡像膨脹過大,上面提到過,可以用&&符號鏈接命令,這樣執行后,只會創建一層鏡像

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)。功能也類似,不同之處如下:

ADD 的優點:在執行 <源文件> 為 tar 壓縮文件的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,會自動復制並解壓到 <目標路徑>。

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

CMD

類似於 RUN 指令,用於運行程序,但二者運行的時間點不同:

  • CMD 在docker run 時運行。
  • RUN 是在 docker build。

作用:為啟動的容器指定默認要運行的程序,程序運行結束,容器也就結束。CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋。

注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最后一個生效。

格式:

CMD <shell 命令> 
CMD ["<可執行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 該寫法是為 ENTRYPOINT 指令指定的程序提供默認參數
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:假設容器內已有此文件)
ENV

設置環境變量,定義了環境變量,那么在后續的指令中,就可以使用這個環境變量。

格式:

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

以下示例設置 NODE_VERSION = 7.2.0 , 在后續的指令中可以通過 $NODE_VERSION 引用:

ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
ARG

構建參數,與 ENV 作用一至。不過作用域不一樣。ARG 設置的環境變量僅對 Dockerfile 內有效,也就是說只有 docker build 的過程中有效,構建好的鏡像內不存在此環境變量。

構建命令 docker build 中可以用 --build-arg <參數名>=<值> 來覆蓋。

格式:

ARG <參數名>[=<默認值>]
VOLUME

定義匿名數據卷。在啟動容器時忘記掛載數據卷,會自動掛載到匿名卷。

作用:

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

格式

VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>
# 在啟動容器 docker run 的時候,我們可以通過 -v 參數修改掛載點。
EXPOSE

僅僅只是聲明端口。

作用:

  • 幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射。
  • 在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。

格式:

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

指定工作目錄。用 WORKDIR 指定的工作目錄,會在構建鏡像的每一層中都存在。(WORKDIR 指定的工作目錄,必須是提前創建好的)。

docker build 構建鏡像過程中的,每一個 RUN 命令都是新建的一層。只有通過 WORKDIR 創建的目錄才會一直存在。

格式:

WORKDIR <工作目錄路徑>
USER

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

格式:

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

用於指定某個程序或者指令來監控 docker 容器服務的運行狀態。

格式:

# HEALTHCHECK [選項] CMD <命令>:設置檢查容器健康狀況的命令
# HEALTHCHECK NONE:如果基礎鏡像有健康檢查指令,使用這行可以屏蔽掉其健康檢查指令

# HEALTHCHECK [選項] CMD <命令> : 這邊 CMD 后面跟隨的命令使用,可以參考 CMD 的用法。
ONBUILD

用於延遲構建命令的執行。簡單的說,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次構建鏡像的過程中不會執行(假設鏡像為 test-build)。當有新的 Dockerfile 使用了之前構建的鏡像 FROM test-build ,這是執行新鏡像的 Dockerfile 構建時候,會執行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

格式

ONBUILD <其它指令>

Docker倉庫

訪問倉庫

倉庫(Repository)是集中存放鏡像的地方

一個容易混淆的概念是注冊服務器( Registry )。實際上注冊服務器是管理倉庫的具體服務 器,每個服務器上可以有多個倉庫,而每個倉庫下面有多個鏡像。從這方面來說,倉庫可以 被認為是一個具體的項目或目錄。例如對於倉庫地址 dl.dockerpool.com/ubuntu 來 說, dl.dockerpool.com 是注冊服務器地址, Ubuntu 是倉庫名。

大部分時候,並不需要這兩者的概念.

Docker Hub

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

注冊,可以直接在https://cloud.docker.com免費注冊一個Docker Hub賬號.

登陸

可以通過執行docker login 命令交互式的輸入用戶名及密碼來完成命令行界面登陸Docker Hub。docker logout退出登陸.

拉取鏡像

可以通過docker search命令來查找官方倉庫中的鏡像,並利用docker pull命令將他下載到本地.

docker search centos
NAME                 DESCRIPTION                        STARS            OFFICIAL    AUTOMATED
centos               The official build of CentOS.                         5717          [OK]    
ansible/centos7-ansible     Ansible on Centos7                              126        [OK]
jdeathe/centos-ssh       OpenSSH / Supervisor / EPEL/IUS/SCL Repos - …      114        [OK]
consol/centos-xfce-vnc    Centos container with "headless" VNC session…     101       [OK]

可以看到返回了很多包含關鍵字的鏡像,其中包括鏡像名字、描述、收藏數(表示該鏡像的受關注程度),是否官方創建、是否自動創建.

根據是否是官方提供,可將鏡像資源分為兩類.

一種是類似centos這樣的鏡像,被稱為基礎鏡像或根鏡像,這些基礎鏡像由Docker公司創建、驗證、支持、提供。這樣的鏡像往往使用單個單詞作為名字.

還有一種類型,比如tianon/centos鏡像,他是由Docker的用戶創建並維護的,往往帶有用戶名稱前綴,可以通過前綴username/來指定某個用戶提供的鏡像,比如tianon用戶.

另外,在查找的時候可以通過--filter=stars=N參數可以顯示收藏數量為N以上的鏡像.

推送鏡像

用戶可以在登陸后通過docker push命令將自己的鏡像推送到Docker Hub。

以下命令的flyingdreams請替換為你的Docker賬號用戶名.

docker login		# 登陸自己的賬號
docker tag nginx:v2 flyingdreams/nginx:v2
docker push flyingdreams/nginx:v2

自動創建

自動創建(Automated Builds)功能對於需要經常升級鏡像內程序來說,十分方便.

有時候,用戶創建了鏡像,安裝了某個軟件,如果程序發布新版本則需要手動更新鏡像.

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

要配置自動創建,包括如下的步驟:

# 1. 創建並登陸Docker Hub,以及目標網站:
# 2. 在目標網站中連接賬戶到Docker Hub;
# 3. 在Docker Hub中配置一個自動連接:
# 4. 選取一個目標網站中的項目(需要含Dockerfile)和分支
# 5. 指定Dockerfile的位置,並提交創建.

# 之后,可以在Docker Hub的自動創建頁面中跟蹤每次創建的狀態.

私有倉庫docker-registry

有時候使用Docker Hub這樣的公共倉庫可能不安全,用戶可以創建一個本地倉庫供私人使用.

通過官方提供的私有倉庫鏡像registry來搭建私有倉庫。通過 humpback 快速搭建輕量級的Docker容器雲管理平台

此外還有像Harbor,rancher等私有倉庫。

安裝運行docker-registry
# 自定義存儲位置$HOME,一般是root
docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v $HOME/_docker/registry:/var/lib/registry \
  registry:2.6
# 從官方倉庫拉去一個鏡像
docker  pull nginx
docker tag nginx:latest 47.92.24.137:5000/test_nginx:latest

# 在推送到的時候報錯誤,默認是使用`https`提交,這個搭建的默認使用的是 `http`,解決方法兩個:

# 創建一個https映射
# 將倉庫地址加入到不安全的倉庫列表中

# 我們使用第二種方法,加入到不安全的倉庫列表中,修改docker配置文件
# `vi /etc/docker/daemon.json` 添加 `insecure-registries`配置信息
docker push 47.92.24.137:5000/test_nginx:latest 
The push refers to repository [47.92.24.137:5000/test_nginx]
Get https://47.92.24.137:5000/v2/: http: server gave HTTP response to HTTPS client

cat /etc/docker/daemon.json 
{
"insecure-registries":[ 
    "47.92.24.137:5000"
  ]
}
systemctl stop docker
systemctl daemon-reload
systemctl start docker

# 推送到私有倉庫中
docker push 47.92.24.137:5000/test_nginx:latest 

ls /root/_docker/registry/docker/registry/v2/repositories/test_nginx/
_layers  _manifests  _uploads

安裝運行harbor(http方式)

Harbor(港口,港灣)是一個用於存儲和分發Docker鏡像的企業級Registry服務器

Harbor 可幫助用戶迅速搭建企業級的 Registry 服務, 它提供了管理圖形界面, 基於角色的訪問控制 ( Role Based Access Control), 鏡像遠程復制 (同步), AD/LDAP 集成, 以及審計日志等企業用戶需求的功能, 同時還原生支持中文, 深受中國用戶的喜愛;

Harbor相比於Registry有以下優勢

  1. 提供分層傳輸機制,優化網絡傳輸,Docker鏡像是分層的,而如果每次傳輸都使用全量文件,顯然效率不是很高,必須提供識別分層傳輸的機制,以層的UUID為標識,確認傳輸的對象.
  2. 提供WEB界面,優化用戶體驗,只用鏡像的名字來進行上傳下載顯然很不方便,需要有一個用戶界面可以支持登錄,搜索功能,包括區分公有,私有鏡像
  3. 支持水平擴展集群,當有用戶對鏡像的上傳下載操作集中在某服務器,需要對相應的訪問壓力工作分解.
  4. 良好的安全機制,企業中的開發團隊有很多不同的職位,對於不通的職位人員,分配不通的權限,具有更好的安全性.
安裝harbor

VMware 公司開源了企業級 Registry 項目, 其的目標是幫助用戶迅速搭建一個企業級的 Docker registry 服務。

由於 Harbor 是基於 Docker Registry V2 版本,所以 docker 版本必須 >=1.10.0 docker-compose >=1.6.0

下載最新版 Docker Compose
curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

下載最新版Docker Harbor
wget https://github.com/goharbor/harbor/releases/download/v1.10.0-rc1/harbor-offline-installer-v1.10.0-rc1.tgz

# 對二進制文件應用可執行權限:
sudo chmod +x /usr/local/bin/docker-compose
# 測試是否安裝成功
docker-compose --version
# 按照上面給的docker harbor地址,下載離線安裝包
tar xvf harbor-offline-installer-v1.8.1.tgz -C  /usr/local/
vim  /usr/local/harbor/harbor.yml
hostname: 47.92.24.137

# 運行安裝腳本
./install.sh 
[Step 0]: checking installation environment ...
Note: docker version: 19.03.4
Note: docker-compose version: 1.22.0
[Step 1]: loading Harbor images ...
Loaded image: goharbor/harbor-core:v1.8.1
Loaded image: goharbor/harbor-registryctl:v1.8.1
Loaded image: goharbor/redis-photon:v1.8.1
Loaded image: goharbor/notary-server-photon:v0.6.1-v1.8.1
Loaded image: goharbor/chartmuseum-photon:v0.8.1-v1.8.1
Loaded image: goharbor/harbor-db:v1.8.1
Loaded image: goharbor/harbor-jobservice:v1.8.1
Loaded image: goharbor/nginx-photon:v1.8.1
Loaded image: goharbor/registry-photon:v2.7.1-patch-2819-v1.8.1
Loaded image: goharbor/harbor-migrator:v1.8.1
Loaded image: goharbor/prepare:v1.8.1
Loaded image: goharbor/harbor-portal:v1.8.1
Loaded image: goharbor/harbor-log:v1.8.1
Loaded image: goharbor/notary-signer-photon:v0.6.1-v1.8.1
Loaded image: goharbor/clair-photon:v2.0.8-v1.8.1
[Step 2]: preparing environment ...
prepare base dir is set to /usr/local/harbor
Generated configuration file: /config/log/logrotate.conf
Generated configuration file: /config/nginx/nginx.conf
Generated configuration file: /config/core/env
Generated configuration file: /config/core/app.conf
Generated configuration file: /config/registry/config.yml
Generated configuration file: /config/registryctl/env
Generated configuration file: /config/db/env
Generated configuration file: /config/jobservice/env
Generated configuration file: /config/jobservice/config.yml
Generated and saved secret to file: /secret/keys/secretkey
Generated certificate, key file:/secret/core/private_key.pem, cert file:/secret/registry/root.crt
Generated configuration file: /compose_location/docker-compose.yml
Clean up the input dir
[Step 3]: starting Harbor ...
✔ ----Harbor has been installed and started successfully.----
Now you should be able to visit the admin portal at http://47.92.24.137. 
For more details, please visit https://github.com/goharbor/harbor

接下來我們可以直接瀏覽器訪問配置文件定義的IP或者域名加端口

修改harbor端口
# 因為harbor默認端口是80,而大多數時候是不希望使用80端口,修改方法如下
# vim harbor.yml
# 找到port選項修改端口,然后執行./install 就會使用配置文件端口

# 還有一種情況就是更改已有harbor的配置
vim docker-compose.yml
    dns_search: .
    ports:
      - 99:80

auth:
  token:
    issuer: harbor-token-issuer
    realm: http://47.92.24.137:99/service/token
    rootcertbundle: /etc/registry/root.crt
    service: harbor-registry

docker-compose down -v
docker-compose up -d

使用harbor

為了體現出效果,建議使用非harbor的另一台機器

# 鏡像推送
docker login 47.92.24.137:99 -u admin -p youmen
vim  /etc/docker/daemon.jsonyml
{
  "insecure-registries":["47.92.24.137"]
}
systemctl daemon-reload
systemctl restart docker
# 因為docker默認使用的是https協議,而搭建harbor是http提供服務的,
# 所以要配置可信任,或者強制docker login和docker push 走http的80端口,而不是443端口.
docker tag nginx:latest 47.92.24.137:99/library/nginx:latest
docker push 47.92.24.137:99/library/nginx:latest

安裝harbor(https方式)

DNS服務器安裝dnsmasq
yum -y install dnsmasq
mkdir -p /data/ssl && cd /data/ssl

vim harbor.cfg
hostname = harbor.youmen.com                 
ui_url_protocol = https                          
db_password = root123   
harbor_admin_password = baiyongjie               
ssl_cert = /usr/local/harbor/cert/harbor.youmen.com.crt      
ssl_cert_key = /usr/local/harbor/cert/harbor.youmen.com.key 

# grep -Ev '#|^$' harbor.yml 
hostname: harbor.youmen.com     # 本機外網IP或域名,該地址供用戶通過UI進行訪問,不要使用127.0.0.1
https:                             # 用戶訪問私倉時使用的協議,默認時http,配置成https
  port: 443                        # https使用的端口
  certificate: /usr/local/harbor/cert/harbor.youmen.com.crt    # 設置證書文件路徑
  private_key: /usr/local/harbor/cert/harbor.youmen.com.key    # 設置證書密鑰文件路徑
harbor_admin_password: youmen   # harbor的管理員賬戶密碼
database:
  password: root123     # 指定mysql數據庫管理員密碼
data_volume: /data      # image存儲目錄
clair: 
  updaters_interval: 12
  http_proxy:
  https_proxy:
  no_proxy: 127.0.0.1,localhost,core,registry
jobservice:
  max_job_workers: 10
chart:
  absolute_url: disabled
log:
  level: info
  rotate_count: 50
  rotate_size: 200M
  location: /var/log/harbor
_version: 1.8.0

生成harbor證書

mkdir /usr/local/harbor/cert/
cd /usr/local/harbor/cert/

#生成根證書
openssl req  -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 3650 -out ca.crt -subj "/C=CN/L=Shanghai/O=harbor/CN=harbor-registry"

#生成一個證書簽名, 設置訪問域名為 harbor.baiyongjie.com
openssl req -newkey rsa:4096 -nodes -sha256 -keyout harbor.youmen.com.key -out server.csr -subj "/C=CN/L=Shanghai/O=harbor/CN=harbor.youmen.com"

#生成主機證書
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out harbor.youmen.com.crt

# 接下來執行自動安裝腳本即可.
 添加本地解析到hosts
#echo "192.168.1.155 harbor.baiyongjie.com" >> /etc/hosts

#cd /usr/local/harbor
# ./install.sh

官方建議不要在Harbor上啟用https,而是將Harbor放置到一個SLB的后邊,配置SLB的端口轉發進行訪問,或者再裝一個Nginx,進行Nginx的端口轉發.

如果想做一個HA方案的話,可以按照如下方式構建一個(主從模式個人感覺很不靠譜

負載均衡同時還要承擔檢查的任務,而Redis用於數據的緩存和消息隊列的實現,Mysql存儲用戶信息和倉庫信息,雲存儲用來存儲Docker鏡像.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM