Golang 容器部署

Go 使用 Docker部署的意義(優點)在哪?

因爲 go 最終是編譯爲一個二進制可執行文件,沒有運行時依賴,也不須要管理庫,丟到服務器上就能夠直接運行。因此,若是你有一個二進制文件,那麼在容器中打包二進制文件的要點是什麼?若是使用 docker 的話,還得在服務器上裝 docker,那麼把最終程序打包成 docker 有什麼好處呢?html

我想有這麼幾個好處:node

依賴打包
若是你的應用程序(二進制文件)依賴配置文件或一些靜態文件,那使用docker就很方便的把這些文件一塊兒打包進容器裏。python

版本控制
啓動Docker就和運行一個進程同樣快,咱們能夠在幾秒鐘的時間內運行整個服務器集羣。除此以外,Docker 鏡像的註冊中心使Docker容器還能夠像git倉庫同樣,可讓你提交變動到Docker鏡像中,並經過不一樣的版原本管理它們。設想若是你由於完成了一個組件的升級而致使你整個環境都損壞了,Docker可讓你輕鬆地回滾到這個鏡像的前一個版本。這整個過程能夠在幾分鐘內完成。mysql

隔離性
容器包含了應用程序的代碼、運行環境、依賴庫、配置文件等必需的資源。容器之間達到進程級別的隔離,在容器中的操做,不會影響道宿主機和其餘容器,這樣就不會出現應用之間相互影響的情形!linux

可移植性
能夠實現開發、測試和生產環境的統一化和標準化。鏡像做爲標準的交付件,可在開發、測試和生產環境上以容器來運行,最終實現三套環境上的應用以及運行所依賴內容的徹底一致。在如今微服務的架構中,一個應用拆成幾十個微服務,每一個微服務都對應有開發、測試、生產三套環境須要搭建。本身算算,若是採用傳統的部署方式,有多少環境須要部署。nginx

輕量和高效
和虛擬機相比,容器僅須要封裝應用和應用須要的依賴文件,實現輕量的應用運行環境,且擁有比虛擬機更高的硬件資源利用率。在微服務架構中,有些服務負載壓力大,須要以集羣部署,可能要部署幾十臺機器上,對於某些中小型公司來講,使用虛擬機,代價太大。若是用容器,一樣的物理機則能支持上千個容器,對中小型公司來講,省錢!git

安全性
Docker容器不能窺視運行在其餘容器中的進程。從體系結構角度來看,每一個容器只使用着本身的資源(從進程到網絡堆棧)。做爲緊固安全的一種手段,Docker將宿主機操做系統上的敏感掛載點(例如/proc和/sys)做爲只讀掛載點,而且使用一種寫時複製系統來確保容器不能讀取其餘容器的數據。Docker也限制了宿主機操做系統上的一些系統調用,而且和SELinux與AppArmor一塊兒運行的很好。此外,在Docker Hub上可使用的Docker鏡像都經過數字簽名來確保其可靠性。因爲Docker容器是隔離的,而且資源是受限制的,因此即便你其中一個應用程序被黑,也不會影響運行在其它Docker容器上的應用程序。github

Part 1: Dockerize

對於許多編程語言(包括 Go ),有幾個很好的官方和社區支持的容器。咱們在容器化Go apps的時候,能夠選擇基於 Golang 官方鏡像構建,如:golang:onbuild,golang:latest。可是這有一個很大的缺點:這些容器可能很大,因此基於它們的鏡像建立的鏡像文件將會很是大。golang

這是由於咱們的應用程序是在容器內編譯的。這意味着該容器須要安裝 Go ,以及 Go 的依賴關係,同時這也意味着咱們須要一個程序包管理器和整個操做系統。實際上,若是您查看 Golang 的 Dockerfile,它將以 Debian Jessie 開頭,安裝 GCC 編譯器和一些構建工具,壓縮 Go 並安裝它。所以,咱們幾乎有一個完整的 Debian 服務器和 Go 工具包來運行咱們的小型應用程序。redis

因此咱們應該使用一種靜態構建 Go 容器化應用的方法,這種方法生成的鏡像文件很是小。

Part 2: Our 「app」

我以 Passport 應用爲例,下面是應用程序結構:

$ tree -L 2
.
├── Makefile
├── control
├── app
│   ├── boot
│   ├── kernel
│   ├── lib
│   ├── main.go
│   ├── server
├── output
│   └── bin
└── vendor
    ├── appengine
    ├── cloud.google.com
    ├── github.com
    ├── go.etcd.io
    ├── go.uber.org
    ├── golang.org
    ├── golang_org
    ├── google.golang.org
    ├── gopkg.in
    └── vendor.json

咱們要作的是在工做目錄中編譯 Go ,而後將二進制文件添加到容器中。這種方式比直接使用官方鏡像麻煩一些,不過體積要小不少,因此建議這麼作。

go build -o output/bin/go_service_passport ./app

Part 3: Builder

.dockerignore file

首先,在項目的根目錄下,新建一個文本文件.dockerignore,寫入下面的內容。

# comment
.git
.gitignore

output
*.out
*.log

*/temp*
*/*/temp*
temp?

*.md
!README.md

在Docker CLI將上下文發送到Docker守護程序以前,它將在上下文的根目錄中查找名爲.dockerignore的文件。若是此文件存在,則CLI會修改上下文以排除與其中的模式匹配的文件和目錄。這有助於避免沒必要要地將大型文件或敏感文件和目錄發送到守護程序,並避免使用ADD或COPY將它們添加到映像中。

若是.dockerignore文件中的行以第1列中的#開頭,則該行將被視爲註釋,而且在CLI解釋以前將被忽略。

官方說明見:.dockerignore file

Dockerfile

接下來就是編寫 Dockerfile 文件了。Dockerfile是一個文本文檔,Docker能夠經過閱讀Dockerfile中的指令來自動構建映像。

該文檔內的指令不區分大小寫。可是,習慣是大寫,以便更輕鬆地將它們與參數區分開。

Docker 按順序在 Dockerfile 中運行指令。 Dockerfile 必須以 「FROM」 指令開頭。固然,FROM 前面能夠有一個或多個 ARG 指令或註釋,ARG 指令聲明 Dockerfile 中 FROM 行中使用的參數。

在開始以前咱們應該想清楚到底使用哪一個基礎鏡像?通常狀況下,都會從如下三個基礎鏡像開始。

  • 鏡像 scratch(空鏡像), 大小 0B
  • 鏡像 busybox(空鏡像 + busybox), 大小 1.4MB
  • 鏡像 alpine (空鏡像 + busybox + apk), 大小 3.98MB

So what’s scratch? Scratch is a special docker image that’s empty. It’s truly 0B:

REPOSITORY          TAG         IMAGE ID            CREATED              VIRTUAL SIZE
scratch             latest      511136ea3c5a        22 months ago        0 B

目前 Docker 官方已開始推薦使用 Alpine 替代以前的 Ubuntu 作爲基礎鏡像環境。這樣會帶來多個好處。包括鏡像下載速度加快,鏡像安全性提升,主機之間的切換更方便,佔用更少磁盤空間等。

下面,不如以上三個鏡像咱們都嘗試一下吧。在項目的根目錄下,新建一個文本文件 Dockerfile,寫入下面的內容。

FROM scratch

FROM scratch
LABEL maintainer="tobeabme@gmail.com" version="1.0"

ENV RUNMODE dev
ENV CONSUL_ADDR 127.0.0.1:8500
ENV ETCD_ADDR 127.0.0.1:2379

ADD output/bin/go_service_passport /
EXPOSE 8080 9080
CMD ["/go_service_passport"]

下面這種寫法是錯誤的,爲何呢?

FROM scratch
MAINTAINER weizi

ENV APP_RUN_DIR /data/app/go/work
ENV APP_LOG_DIR /data/app/go/log

RUN mkdir -p ${APP_RUN_DIR} \
    && mkdir -p ${APP_LOG_DIR}
   
COPY output/bin/go_service_passport ${APP_RUN_DIR}

WORKDIR ${APP_RUN_DIR}
EXPOSE 8080 9080
CMD ["${APP_RUN_DIR}/go_service_passport"]

這是因爲 scratch 鏡像幾乎不包含任何東西,不支持環境變量,也沒有 shell 命令。 所以,基於 scratch 的鏡像經過 ADD 指令進行添加,以此繞過目錄建立。更完整的緣由說明見以下:

FROM scratch is a completely empty filesystem. You have no installed libraries, and no shell (like /bin/sh) included in there. To use this as your base, you'd need a statically linked binary, or you'll need to install all of the normal tools that are included with a linux distribution.

The latter is what is prepackaged in the various busybox, debian, ubuntu, centos, etc images on the docker hub. The fast way to make your image work with a minimal base image is to change the from to FROM busybox and change your /bin/bash to /bin/sh.

FROM busybox

FROM busybox
MAINTAINER weizi

ENV APP_RUN_DIR /data/app/go/work
ENV RUNMODE dev
ENV CONSUL_ADDR 127.0.0.1:8500
ENV ETCD_ADDR 127.0.0.1:2379

#RUN  mkdir -p /data/app/go/work

WORKDIR $APP_RUN_DIR
ADD output/bin/go_service_passport .

EXPOSE 8080 9080
CMD ["./go_service_passport","-g","daemon off;"]

多階構建

ARG GO_VERSION=1.10.3
  
FROM golang:${GO_VERSION} AS builder
MAINTAINER weizi
LABEL author="name@gmail.com"

ENV GO111MODULE=off
ENV GO15VENDOREXPERIMENT=1

WORKDIR $GOPATH/src/code.qschou.com/peduli/go_service_passport
COPY . .

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o output/bin/go_service_passport ./app

#------------------------------------------

FROM alpine
MAINTAINER weizi
LABEL author="name@gmail.com"

ENV APP_RUN_DIR /data/app/go/work
ENV RUNMODE dev
ENV CONSUL_ADDR 127.0.0.1:8500
ENV ETCD_ADDR 127.0.0.1:2379

RUN apk update \
        && apk --no-cache add wget ca-certificates \
        && apk add -f --no-cache git \
        && apk add -U tzdata \
        && ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime

WORKDIR $APP_RUN_DIR

COPY --from=builder /go/src/code.qschou.com/peduli/go_service_passport/output/bin/go_service_passport .

EXPOSE 8080 9080
CMD ["./go_service_passport","-g","daemon off;"]

最後,讓咱們看下上面三種鏡像生成後的大小:

$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
passport-busybox       1.0.1               1028fbd88847        32 seconds ago      36.3MB
passport-scratch       1.0.1               aa407fee8d95        33 minutes ago      35.1MB
passport-multi-stage   1.0.9               dd8a070d96e9        2 days ago          59.4MB

Go應用自己的二進制文件爲33MB

$ ls -lh   output/bin/go_service_passport
-rwxr-xr-x  1 will  staff    33M Nov 18 11:09 output/bin/go_service_passport

scratch size = 35.1-33 = 2.1M
busybox size = 36.3-33 = 3.3M
alpine size = 59.4-33 = 26.1M

指令說明:

FROM

FROM指令初始化一個新的構建階段,併爲後續指令設置基本映像。所以,有效的 Dockerfile 必須以 FROM 指令開頭。

格式:

FROM <image> [AS <name>]
Or
FROM <image>[:<tag>] [AS <name>]
Or
FROM <image>[@<digest>] [AS <name>]
  • ARG 是 Dockerfile 中惟一能夠出如今 FROM 指令以前的指令。
  • FROM能夠在單個Dockerfile中屢次出現,以建立多個映像或將一個構建階段用做對另外一個構建階段的依賴。 只需在每一個新的FROM指令以前記錄一次提交輸出的最後一個圖像ID。 每一個FROM指令清除由先前指令建立的任何狀態。
  • 經過將AS名稱添加到FROM指令中,能夠選擇爲新的構建階段指定名稱。 該名稱能夠在後續的FROM和COPY --from = <名稱|索引>指令中使用,以引用在此階段構建的映像。
  • tag or digest 值是可選的。 若是您忽略其中任何一個,那麼缺省狀況下構建器都會採用 latest 標籤。 若是構建器找不到標籤值,則返回錯誤。

MAINTAINER

MAINTAINER指令設置生成圖像的「做者」字段。 LABEL指令是此指令的更爲靈活的版本,您應該使用它,由於它能夠設置所需的任何元數據,而且能夠輕鬆查看,例如使用docker inspect。 要設置與MAINTAINER字段相對應的標籤,可使用:

LABEL maintainer="SvenDowideit@home.org.au"

LABEL

LABEL指令將元數據添加到圖像。 標籤是鍵值對。 要在LABEL值中包含空格,請使用引號和反斜槓。 一些用法示例:

LABEL "com.example.vendor"="ACME Incorporated"

LABEL description="This text illustrates \
that label-values can span multiple lines."

一個鏡像能夠有多個標籤。 如下兩種方式之一在一條指令中指定多個標籤:

基本或父圖像(FROM行中的圖像)中包含的標籤由您的圖像繼承。 若是標籤已經存在但具備不一樣的值,則最近應用的值將覆蓋任何先前設置的值。

LABEL multi.label1="value1" multi.label2="value2" other="value3"

OR

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

被包含在基礎鏡像或父鏡像(images in the FROM line)的 Labels 是被你的鏡像繼承的。若是一個標籤已經存在,但具備不一樣的值,則最近應用的值將覆蓋任何先前設置的值。

去查看一個鏡像的 Labels,請使用docker inspect命令。

ENV

設置環境變量。將環境變量<key>設置爲值<value>。 此值將在構建階段中全部後續指令(RUN、ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD)的環境中使用。

格式:

ENV <key> <value>
ENV <key>=<value> ...

第一種形式,ENV <鍵> <值>,將單個變量設置爲一個值。 第一個空格以後的整個字符串將被視爲<value>-包括空格字符。 該值能夠被其餘環境變量解釋,所以若是不對引號字符進行轉義,則將其刪除。

第二種格式,ENV <key> = <value> ...,容許一次設置多個變量。 請注意,第二種形式在語法中使用等號(=),而第一種形式則不使用等號(=)。 與命令行解析同樣,引號和反斜槓可用於在值中包含空格。

For example:

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy
and

ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

當一個容器是從產生的鏡像運行時,使用ENV設置的環境變量將持續存在。您可使用docker inspect查看值,並使用docker run --env <key> = <value>更改它們。

Environment replacement

環境變量(用ENV語句聲明)也能夠在某些指令中用做Dockerfile解釋的變量。經過在字面上將相似變量的語法包含到語句中來處理。

在 Dockerfile 文檔中,可使用 $variable_name 或 ${variable_name} 引用環境變量,它們是等同的。其中大括號的變量是用在沒有空格的變量名中的,如${foo}_bar。

${variable_name}變量也支持一些標準的bash修飾符,如:

  • ${variable:-word} 表示若是variable設置了,那麼結果就是設置的值。不然設置值爲word
  • ${variable:+word} 表示若是variable設置了,那麼結果是word值,不然爲空值。

word能夠是任意的字符,包括額外的環境變量。

轉義符(Escaping)能夠添加在變量前面:&dollar;foo or &dollar;{foo},例如,會分別轉換爲$foor和${foo}。示例:

FROM busybox
ENV foo /bar
WORKDIR ${foo}   # WORKDIR /bar
ADD . $foo       # ADD . /bar
COPY \$foo /quux # COPY $foo /quux

在此實例中:

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

將致使def的值爲hello,而不是bye。可是,ghi的值是bye。

Dockerfile中的如下指令列表支持環境變量:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD(在1.4以前,ONBUILD指令不支持環境變量)

官方說明見: Environment replacement

ARG

ARG指令定義了一個變量,用戶能夠在構建時使用--build-arg <varname> = <value>標誌使用docker build命令將其傳遞給構建器。若是用戶指定了未在Dockerfile中定義的構建參數,則構建會輸出警告。

[Warning] One or more build-args [foo] were not consumed.

格式:

ARG <name>[=<default value>]

Dockerfile可能包含一個或多個ARG指令。例如,

FROM busybox
ARG user1
ARG buildno
...

警告:不建議使用構建時變量來傳遞諸如github密鑰,用戶憑據等機密。構建時變量值對於使用docker history命令的映像的任何用戶都是可見的。

默認值

ARG指令能夠選擇包含默認值:

FROM busybox
ARG user1=someuser
ARG buildno=1
...

若是ARG指令具備默認值,而且在構建時未傳遞任何值,則構建器將使用默認值。

ARG指令在定義它的構建階段結束時超出範圍。要在多個階段中使用arg,每一個階段都必須包含ARG指令。

FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS

使用ARG變量

您可使用ARG或ENV指令來指定RUN指令可用的變量。使用ENV指令定義的環境變量始終會覆蓋同名的ARG指令。

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER v1.0.0
4 RUN echo $CONT_IMG_VER

而後,假定此映像是使用如下命令構建的:

$ docker build --build-arg CONT_IMG_VER=v2.0.1 .

在這種狀況下,RUN指令使用v1.0.0而不是用戶傳遞的ARG設置:v2.0.1此行爲相似於shell腳本,其中局部做用域的變量會覆蓋從參數傳遞過來的做爲參數。

使用上面的示例,但使用不一樣的ENV規範,您能夠在ARG和ENV指令之間建立更有用的交互:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
4 RUN echo $CONT_IMG_VER

與ARG指令不一樣,ENV值始終保留在生成的映像中。考慮不帶--build-arg標誌的Docker構建:

$ docker build .

使用此Dockerfile示例,CONT_IMG_VER仍保留在映像中,但其值爲v1.0.0,由於它是ENV指令在第3行中設置的默認值。

在此示例中,變量擴展技術使您能夠從命令行傳遞參數,並利用ENV指令將其保留在最終映像中。

預約義的ARG

Docker具備一組預約義的ARG變量,您能夠在Dockerfile中使用它們而無需相應的ARG指令。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

默認狀況下,這些預約義變量從docker history記錄的輸出中排除。排除它們能夠下降意外泄漏HTTP_PROXY變量中的敏感身份驗證信息的風險。

--build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com

COPY

COPY指令從<src>複製新文件或目錄,並將它們添加到容器的文件系統中,路徑爲<dest>。因爲咱們這裏是拷貝Go構建好的二進制文件,因此不用將當前目錄下的全部文件(除了.dockerignore排除的路徑),都拷貝進入 image 文件的 $WORKPATH 目錄。若是須要拷貝後自動解壓,用 ADD 指令。

COPY有兩種形式:

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (this form is required for paths containing whitespace)

每一個<src>均可以包含通配符,而且將使用Go的filepath.Match規則進行匹配。例如:

COPY hom* /mydir/        # adds all files starting with "hom"
COPY hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"

<dest>是絕對路徑,或相對於WORKDIR的路徑,源將在目標容器內複製到該路徑。

COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # adds "test" to /absoluteDir/

複製包含特殊字符 (such as [ and ]), 的文件或目錄時,須要遵循Golang規則轉義那些路徑,以防止將它們視爲匹配模式。例如,要複製名爲 arr[0].txt 的文件,請使用如下命令:

COPY arr[[]0].txt /mydir/    # copy a file named "arr[0].txt" to /mydir/

可選地,COPY接受 --from=<name|index> 標誌,該標誌可用於將源位置設置爲先前的構建階段 (created with FROM .. AS <name>) ,該階段將用於代替由發送的構建上下文用戶。若是找不到具備指定名稱的構建階段,則嘗試改用具備相同名稱的圖像。

COPY遵照如下規則:

  • <src>路徑必須在構建的上下文中。您沒法 COPY ../something /something,由於Docker構建的第一步是將上下文目錄(和子目錄)發送到docker守護程序。
  • 若是<src>是目錄,則將複製目錄的整個內容,包括文件系統元數據。注意:目錄自己不會被複制,只是其內容被複制。
  • 若是<src>是任何其餘類型的文件,則將其與其元數據一塊兒單獨複製。在這種狀況下,若是<dest>以尾斜槓/結束,則它將被視爲目錄,而且<src>的內容將寫入<dest>/base(<src>)
  • 若是直接或因爲使用通配符而指定了多個<src>資源,則<dest>必須是目錄,而且必須以斜槓/結尾。
  • 若是<dest>不以斜槓結尾,它將被視爲常規文件,而且<src>的內容將寫入<dest>。
  • 若是<dest>不存在,它將與路徑中全部缺乏的目錄一塊兒建立。

官方說明見:COPY

ADD

ADD指令從<src>複製新文件,目錄或遠程文件URL,並將它們添加到鏡像的文件系統中的路徑<dest>。

ADD有兩種形式:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (this form is required for paths containing whitespace)

ADD遵照如下規則:

  • 若是<src>是URL,而且<dest>不以斜槓結尾,則從URL下載文件並將其複製到<dest>。
  • 若是<src>是URL,而且<dest>確實以斜槓結尾,則從URL推斷文件名,並將文件下載到<dest>/<filename>。例如,ADD http://example.com/foobar / 將建立文件 /foobar。該URL必須具備不平凡的路徑,以便在這種狀況下能夠找到適當的文件名(http://example.com 將不起做用)。
  • 若是<src>是採用公認壓縮格式(身份,gzip,bzip2或xz)的本地tar歸檔文件,則將其解壓縮爲目錄。來自遠程URL的資源不會被解壓縮。注意:是否將文件識別爲可識別的壓縮格式僅根據文件的內容而不是文件的名稱來完成。例如,若是一個空文件碰巧以.tar.gz結尾,則該文件將不會被識別爲壓縮文件,而且不會生成任何類型的解壓縮錯誤消息,而是會將文件簡單地複製到目標位置。
  • 若是<dest>不存在,它將與路徑中全部缺乏的目錄一塊兒建立。

其它規則與COPY相同,不重複描述。

WORKDIR

WORKDIR指令爲Dockerfile中跟在其後的全部RUN,CMD,ENTRYPOINT,COPY和ADD指令設置工做目錄。至關於 cd 。如該目錄不存在,WORKDIR 會幫你創建目錄,即便隨後的Dockerfile指令未使用它。

WORKDIR指令可在Dockerfile中屢次使用。若是提供了相對路徑,則它將相對於上一個WORKDIR指令的路徑。例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

該Dockerfile中最後一個pwd命令的輸出爲 /a/b/c 。

WORKDIR指令能夠解析之前使用ENV設置的環境變量。您只能使用在Dockerfile中顯式設置的環境變量。例如:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

該Dockerfile中最後一個pwd命令的輸出爲 /path/$DIRNAME

RUN

RUN指令將在當前映像頂部的新層中執行任何命令,並提交結果。生成的提交映像將用於Dockerfile中的下一步。

RUN <command> (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows)
RUN ["executable", "param1", "param2"] (exec form)

分層運行RUN指令並生成提交符合Docker的核心概念,在Docker上,提交很便宜,而且能夠從映像歷史記錄的任何位置建立容器,就像源代碼控制同樣。

在shell形式中,可使用(反斜槓)將一條RUN指令繼續到下一行。

RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

Together they are equivalent to this single line:

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

注意:要使用 ‘/bin/sh’ 之外的其餘 shell,請使用 exec 形式傳入所需的 shell。例如,RUN ["/bin/bash", "-c", "echo hello"]

注意:不一樣與shell形式,exec形式不會調用命令shell。 這意味着正常的外殼處理不會發生。 例如,RUN ["echo", "$HOME"] 不會在$HOME上進行變量替換。 若是要進行shell處理,則可使用shell形式或直接執行shell,例如:RUN ["sh", "-c", "echo $HOME"] 。

注意:在JSON格式中,必須轉義反斜槓。 在Windows中,反斜槓是路徑分隔符,這一點尤爲重要。 因爲無效的JSON,如下行將被視爲shell形式,並以意外的方式失敗:RUN ["c:windowssystem32tasklist.exe"] 此示例的正確語法是:RUN ["c:\windows\system32\tasklist.exe"]

在下一個構建時,RUN指令的緩存不會自動失效。 諸如RUN apt-get dist-upgrade -y之類的指令的緩存將在下一次構建中重用。 可使用--no-cache標誌使RUN指令的緩存無效,例如docker build --no-cache。

EXPOSE

EXPOSE指令通知Docker運行時容器在指定的網絡端口上進行偵聽。您能夠指定端口是偵聽TCP仍是UDP,若是未指定協議,則默認值爲TCP。

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

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

默認狀況下,EXPOSE假定使用TCP。您還能夠指定UDP:

EXPOSE 80/udp

要同時在TCP和UDP上公開,請包括如下兩行:

EXPOSE 80/tcp
EXPOSE 80/udp

不管EXPOSE設置如何,均可以在運行時使用-p標誌覆蓋它們。例如

docker run -p 80:80/tcp -p 80:80/udp ...

docker network命令支持建立用於容器之間通訊的網絡,而無需暴露或發佈特定端口,由於鏈接到網絡的容器能夠經過任何端口相互通訊。有關詳細信息,請參閱此功能的概述。

CMD

用於指定默認的容器主進程的啓動命令。Dockerfile中只能有一條CMD指令。若是您列出多個CMD,則只有最後一個CMD纔會生效。

CMD的主要目的是爲執行中的容器提供默認值。這些默認值能夠包含一個可執行文件,也能夠忽略該可執行文件,在這種狀況下,您還必須指定ENTRYPOINT指令。

注意:若是使用CMD爲ENTRYPOINT指令提供默認參數,則CMD和ENTRYPOINT指令均應使用JSON數組格式指定。

CMD指令具備三種形式:

CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (在指定了 ENTRYPOINT 指令後,用 CMD 指定具體的參數。)
CMD command param1 param2 (shell form)

If you use the shell form of the CMD, then the <command> will execute in /bin/sh -c:

FROM ubuntu
CMD echo "This is a test." | wc -

若是要在沒有shell的狀況下運行<command>,則必須將命令表示爲JSON數組,並提供可執行文件的完整路徑。此數組形式是CMD的首選格式。任何其餘參數必須在數組中分別表示爲字符串:

FROM ubuntu
CMD ["/usr/bin/wc","--help"]

注意,指定了CMD命令之後,docker container run命令就不能附加命令了(好比 /bin/bash),不然它會覆蓋CMD命令。

RUN命令與CMD命令的區別在哪裏?

簡單說,RUN命令在 image 文件的構建階段執行,執行結果都會打包進入 image 文件;CMD命令則是在容器啓動後執行。另外,一個 Dockerfile 能夠包含多個RUN命令,可是隻能有一個CMD命令。

VOLUME

Volume,一般翻譯爲數據卷,用於保存持久化數據。當咱們將數據庫例如MySQL運行在Docker容器中時,通常將數據經過Docker Volume保存在主機上,這樣即便刪除MySQL容器,數據依然保存在主機上,有效保證了數據的安全性。

VOLUME指令建立具備指定名稱的掛載點,並將其標記爲保存來自本地主機或其餘容器的外部安裝的卷。該值能夠是JSON數組,VOLUME ["/var/log/"], 或具備多個參數的純字符串,例如VOLUME /var/log or VOLUME /var/log /var/db。

咱們知道,鏡像的每一層都是 ReadOnly 只讀的。只有在咱們運行容器的時候纔會建立讀寫層。文件系統的隔離使得:

  • 容器再也不運行時,數據將不會持續存在,數據很難從容器中取出。
  • 沒法在不一樣主機之間很好的進行數據遷移。
  • 數據寫入容器的讀寫層須要內核提供聯合文件系統,這會額外的下降性能。

docker 爲咱們提供了三種不一樣的方式將數據掛載到容器中:volume、bind mount、tmpfs。

volume 方式是 docker 中數據持久化的最佳方式。

  • docker 默認在主機上會有一個特定的區域(/var/lib/docker/volumes/ Linux),該區域用來存放 volume。
  • 非 docker 進程不該該去修改該區域。
  • volume 能夠經過 docker volume 進行管理,如建立、刪除等操做。
  • volume 在生成的時候若是不指定名稱,便會隨機生成。

volume 在容器中止或刪除的時候會繼續存在,如需刪除須要顯示聲明。

$ docker rm -v <container_id>
$ docker volume rm <volume_name>

格式:

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

能夠經過如下兩種方式建立 VOLUME:

  • 在Dockerfile中指定VOLUME
  • 使用 docker run --volume 命令來指定

這兩種方式有區別嗎?

根據官方文檔,Dockerfile生成目標鏡像的過程就是不斷 docker run + docker commit 的過程,當 Dockerfile 執行到 VOLUME /some/dir(這裏爲/var/lib/mysql)這一行時,輸出:

Step 6 : VOLUME /var/lib/mysql
 ---> Running in 0c842ec90849
 ---> 214e3dccd0f2

在這一步,docker生成了臨時容器0c842ec90849,而後commit容器獲得鏡像214e3dccd0f2。所以 VOLUME /var/lib/mysql 是經過 docker run -v /var/lib/mysql,即第二種方式來實現的,隨後因爲容器的提交,該配置被保存到了鏡像214e3dccd0f2中,經過inspcet能夠查看到:

"Volumes": {
    "/var/lib/mysql": {},
}

使用docker inspect命令,能夠查看Docker容器的詳細信息:

docker inspect --format='{{json .Mounts}}' test | python -m json.tool

"Mounts": [
    {
        "Name": "8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a",
        "Source": "/mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/data",
        "Destination": "/var/lib/mysql",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
]

Source表示主機上的目錄,Destination爲容器中的目錄。

因爲沒有指定掛載到的宿主機目錄,所以會默認掛載到宿主機的 /var/lib/docker/volumes 下的一個隨機名稱的目錄下,在這爲 /mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/data 。所以Dockerfile中使用VOLUME指令掛載目錄和docker run時經過-v參數指定掛載目錄的區別在於,run的-v能夠指定掛載到宿主機的哪一個目錄,而Dockerfile的VOLUME不能,其掛載目錄由docker隨機生成。

若指定了宿主機目錄,好比:

docker run --name mysql -v ~/volume/mysql/data:/var/lib/mysql -d mysql:5.7

那麼inspect以下:

"Mounts": [
    {
        "Source": "/Users/weizi/volume/mysql/data",
        "Destination": "/var/lib/mysql",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
]

這裏將 /var/lib/mysql 掛載到宿主機的 /Users/weizi/volume/mysql/data 目錄下,而再也不是默認的 /var/lib/docker/volumes 目錄。這樣作有什麼好處呢?咱們知道,將該目錄掛載到宿主機,可使數據在容器被移除時得以保留,而不會隨着容器go die。下次新建mysql容器,只需一樣掛載到 /Users/weizi/volume/mysql/data,便可複用原有數據。

在宿主機 /Users/weizi/volume/mysql/data 目錄中新建 hello.txt 文件,在容器/var/lib/mysql 目錄中可見。
在容器 /var/lib/mysql 目錄中新建 world.txt 文件,在宿主機 /Users/weizi/volume/mysql/data 目錄中可見。

經過VOLUME,咱們得以繞過docker的Union File System,從而直接對宿主機的目錄進行直接讀寫,實現了容器內數據的持久化和共享化。

關於Dockerfile中的卷,請記住如下幾點。

  • 從Dockerfile內更改卷:若是在聲明瞭卷後,有任何構建步驟更改了卷內的數據,則這些更改將被丟棄。
  • JSON格式:列表被解析爲JSON數組。您必須用雙引號(")而不是單引號(')括起單詞。
  • 主機目錄(host directory )在容器運行時(container run-time)聲明:主機目錄(掛載點)從本質上說是依賴於主機的。這是爲了保留鏡像的可移植性,由於不能保證給定的主機目錄在全部主機上均可用。所以,您沒法從Dockerfile中掛載主機目錄。VOLUME指令不支持指定host-dir參數。建立或運行容器時,必須指定安裝點。
  • 基於Windows的容器上的卷:使用基於Windows的容器時,容器內的卷的目的地必須是如下之一:

    • 不存在 或 空目錄
    • C: 之外的驅動器

Part 4: image

完成 Dockerfile 文檔以後,接下來咱們能夠基於 Dockerfile 文檔生成鏡像文件了。

docker build -t passport-scratch:1.0.1 -f Dockerfile .

以上,若是運行成功,就能夠看到新生成的 image 文件了。

$ docker image ls

REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
passport-busybox       1.0.1               1028fbd88847        10 minutes ago      36.3MB
passport-scratch       1.0.1               aa407fee8d95        42 minutes ago      35.1MB
passport-multi-stage   1.0.9               dd8a070d96e9        2 days ago          59.4MB
passport-busybox       1.0.0               d56a21693694        3 days ago          36.3MB
<none>                 <none>              492f4b83ea2d        16 minutes ago      34.9MB
nginx                  v1                  75b671fe9af3        9 days ago          126MB
busybox                latest              020584afccce        2 weeks ago         1.22MB
nginx                  latest              540a289bab6c        3 weeks ago         126MB
alpine                 latest              965ea09ff2eb        3 weeks ago         5.55MB
ubuntu                 19.10               09604a62a001        4 weeks ago         72.9MB
kong                   latest              03f9bc1cd4f7        2 months ago        130MB
postgres               9.6                 f5548544c480        3 months ago        230MB
pantsel/konga          latest              dc0af5db6ce9        7 months ago        389MB
pgbi/kong-dashboard    latest              f9e2977207e3        8 months ago        96.4MB
golang                 1.10                6fd1f7edb6ab        9 months ago        760MB
golang                 1.10-alpine         7b53e4a31d21        9 months ago        259MB
golang                 1.10.3-alpine       cace225819dc        15 months ago       259MB
golang                 1.10.3              d0e7a411e3da        16 months ago       794MB
soyking/e3w            latest              a123f3eeaad2        23 months ago       24.2MB
soyking/etcd-goreman   3.2.7               4c0139e55ed5        2 years ago         121MB

上面代碼中,-t參數用來指定 image 文件的名字(名字中不能報考下劃線_),後面還能夠用冒號指定標籤。若是不指定,默認的標籤就是latest。最後的那個點表示 Dockerfile 文件所在的路徑,上例是當前路徑,因此是一個點。

docker build 命令構建鏡像,其實並不是在本地構建,而是在服務端,也就是 Docker 引擎中構建的。那麼在這種客戶端/服務端的架構中,如何才能讓服務端得到本地文件呢?

這就引入了上下文的概念。當構建的時候,用戶會指定構建鏡像上下文的路徑(也就是上面命令中最後的那個圓點 "."),docker build 命令得知這個路徑後,會將路徑下的全部內容打包,而後上傳給 Docker 引擎。這樣 Docker 引擎收到這個上下文包後,展開就會得到構建鏡像所需的一切文件。

Part 5: container

Ok, 接下來咱們來生成容器。docker container run 命令會基於 image 文件生成容器。

$ docker run -p 80:8080 -it passport-scratch:1.0.1
no such file or directory

從上面能夠看出,運行容器時報錯了。這是爲何呢?

Go 二進制文件正在其運行的操做系統上尋找一些庫。咱們編譯了應用,但它仍動態連接到須要運行的庫(即,它綁定到的全部C庫)。不幸的是,scratch 是空的,所以沒有庫。咱們要作的是修改構建腳本,以使用全部內置庫靜態編譯咱們的應用程序。

CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o output/bin/go_service_passport ./app

OR

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o output/bin/go_service_passport ./app

上面的命令,咱們禁用了cgo,它爲咱們提供了靜態二進制文件。咱們還將操做系統設置爲Linux(以防有人在Mac或Windows上構建),-a標誌意味着能夠重建咱們正在使用的全部軟件包,這意味着全部導入將在禁用cgo的狀況下進行重建。

從新生成二進制文件後,讓咱們再次試一下:

# 後臺運行
$ docker container run -it -p 80:8080 -d passport-scratch:1.0.1

OR
# 前臺運行並刪除
$ docker container run --rm -p 8080:8080 -it passport-scratch:1.0.1

2019-11-15T17:25:33.747+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["127.0.0.1:2379"], "keyspace": "", "traceId": ""}

這時發現仍然有問題,容器內的主程序是能夠運行了,不過很快就退出了。什麼緣由呢?

這是由於 passport 程序內部有鏈接 etcd,consul,mysql 等服務,而這些基礎服務在宿主機器上默認監聽地址是127.0.0.1。而容器內也確實存在127.0.0.1/localhost地址。不過這和宿主機上的127.0.0.1是不同的,因此容器內的程序就沒法訪問宿主機上的127.0.0.1/localhost。解決方法是把宿主機器上的 etcd, consul 等服務的監聽地址修改成0.0.0.0 。如:

#consul
$ nohup /usr/local/opt/consul/bin/consul agent -dev -client 0.0.0.0
$ lsof -nP -iTCP -sTCP:LISTEN | grep consul
consul    79872 will    5u  IPv4 0x9c21088b2c0aa1b      0t0  TCP 127.0.0.1:8300 (LISTEN)
consul    79872 will    6u  IPv4 0x9c210888fd5863b      0t0  TCP 127.0.0.1:8302 (LISTEN)
consul    79872 will    8u  IPv4 0x9c21088b2c1137b      0t0  TCP 127.0.0.1:8301 (LISTEN)
consul    79872 will   11u  IPv6 0x9c21088832ff0d3      0t0  TCP *:8600 (LISTEN)
consul    79872 will   12u  IPv6 0x9c21088832fd413      0t0  TCP *:8500 (LISTEN)

#etcd
$ nohup ./etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2379

$ lsof -nP -iTCP -sTCP:LISTEN | grep etcd
etcd      80114 will    4u  IPv4 0x9c21088b0d6463b      0t0  TCP 127.0.0.1:2380 (LISTEN)
etcd      80114 will    6u  IPv6 0x9c21088832ff693      0t0  TCP *:2379 (LISTEN)

好,讓咱們再重啓下容器並加上環境變量

# 後臺運行
$ docker container run -it -p 80:8080 -d -e ETCD_ADDR=172.16.60.88:2379 -e CONSUL_ADDR=172.16.60.88:8500 passport-scratch:1.0.1

OR
# 前臺運行並刪除
$ docker container run --rm -p 8080:8080 -it -e ETCD_ADDR=172.16.60.88:2379 -e CONSUL_ADDR=172.16.60.88:8500 passport-scratch:1.0.1

2019-11-15T17:25:33.747+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["127.0.0.1:2379"], "keyspace": "", "traceId": ""}

###如下是啓動信息
2019-11-15T22:46:12.278+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "", "traceId": ""}
2019-11-15T22:46:12.290+0800    DEBUG    setting/etcd.go:65    setting.etcd: Retrieved mysql-key-val from etcd store    {"key": "root/config/common/database/mysql/passport", "config": {"master":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10},"slave":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10}}, "traceId": ""}
DEBU[0001] new passport mysql store                      MasterDB="&{<nil> <nil> 0 0xc42022ac80 false 2 {0xc42034a280} <nil> map[] 0xc4200e05a0 0x16c7aa0 0xc4201e39a0 false}" SlaveDB="&{<nil> <nil> 0 0xc42022ae60 false 2 {0xc42034a280} <nil> map[] 0xc4200e06c0 0x16c7aa0 0xc4201e3b60 false}"
2019-11-15T22:46:13.888+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "", "traceId": ""}
2019-11-15T22:46:13.894+0800    DEBUG    setting/etcd.go:102    setting.etcd: Retrieved redis-key-val from etcd store    {"key": "root/config/common/database/redis", "config": {"master":{"addr":"127.0.0.1:6379","password":"","db":1},"slave":{"addr":"127.0.0.1:6379","password":"","db":1}}, "traceId": ""}
2019-11-15T22:46:13.895+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "", "traceId": ""}
2019-11-15T22:46:13.902+0800    DEBUG    setting/etcd.go:102    setting.etcd: Retrieved redis-key-val from etcd store    {"key": "root/config/common/database/redis", "config": {"master":{"addr":"127.0.0.1:6379","password":"","db":1},"slave":{"addr":"127.0.0.1:6379","password":"","db":1}}, "traceId": ""}
2019-11-15 22:46:13.905270 I | Initializing logging reporter
INFO[0001] new command                                   Command="&{<nil> <nil>}"
2019-11-15T22:46:13.907+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "", "traceId": ""}
2019-11-15T22:46:13.916+0800    DEBUG    setting/etcd.go:65    setting.etcd: Retrieved mysql-key-val from etcd store    {"key": "root/config/common/database/mysql/passport", "config": {"master":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10},"slave":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10}}, "traceId": ""}
DEBU[0003] new passport mysql store                      MasterDB="&{<nil> <nil> 0 0xc4200ba6e0 false 2 {0xc42034a280} <nil> map[] 0xc4200e0a20 0x16c7aa0 0xc42019bee0 false}" SlaveDB="&{<nil> <nil> 0 0xc4201c3ea0 false 2 {0xc42034a280} <nil> map[] 0xc420376480 0x16c7aa0 0xc4201e2780 false}"
DEBU[0003] new store                                     MySQL="&{0xc4200e0a20 0xc420376480 0xc4200831e0}" config="&{dev [10.10.1.29:2379]    0 0               }"
2019-11-15T22:46:15.736+0800    DEBUG    setting/etcd.go:35    setting.etcd: Backend config    {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "", "traceId": ""}
2019-11-15T22:46:15.742+0800    DEBUG    setting/etcd.go:83    setting.etcd: Retrieved service-key-val from etcd store    {"key": "root/config/custom/go_service_passport", "config": {"Runmode":"","EtcdEndpoints":null,"AppConfigPath":"","ServiceName":"","ServiceIP":"","ServiceHttpPort":0,"ServiceRpcPort":0,"log_level":"debug","log_path":"","domain_www":"https://www.pedulisehat.id","domain_api":"","domain_passport":"https://passport-qa.pedulisehat.id","domain_project":"https://project-qa.pedulisehat.id","domain_trade":"https://trade-qa.pedulisehat.id","domain_static_avatar":"https://static-qa.pedulisehat.id/img/avatar","url_share_project":"","url_ico":"","host_passport":"","host_project":"","host_trade":"","url_share":"","domain_gtry":""}, "traceId": ""}
DEBU[0003] setting.NewConfig                             Config="&{dev [10.10.1.29:2379]  go_service_passport 0.0.0.0 8080 9080 debug  https://www.pedulisehat.id  https://passport-qa.pedulisehat.id https://project-qa.pedulisehat.id https://trade-qa.pedulisehat.id https://static-qa.pedulisehat.id/img/avatar       }"
INFO[0003] command run ...                               Command="&{<nil> 0xc4201e21c0}"
2019-11-15 22:46:15.744629 I | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:    export GIN_MODE=release
 - using code:    gin.SetMode(gin.ReleaseMode)

2019-11-15 22:46:15.745561 I | RPC Server has been started up. 172.17.0.2:9080

能夠發現咱們的 passport 服務能夠正常啓動了,查看下容器的運行狀態:

$ docker container ls --all
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                            NAMES
b58ee39088dd        passport-multi-stage:1.0.9   "./go_service_passpo…"   5 seconds ago       Up 4 seconds        9080/tcp, 0.0.0.0:80->8080/tcp   recursing_poitras

參數說明:

-p 參數: 容器的 8080 端口映射到本機的 80 端口。
-it 參數: 容器的 Shell 映射到當前的 Shell,而後你在本機窗口輸入的命令,就會傳入容器。其中,-t 選項讓Docker分配一個僞終端(pseudo-tty)並綁定到容器的標準輸入上, -i 則讓容器的標準輸入保持打開。
passport-scratch:1.0.1: image 文件的名字(若是有標籤,還須要提供標籤,默認是 latest 標籤)。

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

  • 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載
  • 利用鏡像建立並啓動一個容器
  • 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層
  • 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去
  • 從地址池配置一個 ip 地址給容器
  • 執行用戶指定的應用程序
  • 執行完畢後容器被終止

到此,完整的docker容器製做流程講完了!

發佈 image 文件

容器運行成功後,就確認了 image 文件的有效性。這時,咱們就能夠考慮把 image 文件分享到網上,讓其餘人使用。image 文件製做完成後,能夠上傳到網上的倉庫。

首先,去 Docker 的官方倉庫 Docker Hub 註冊一個帳戶,這是最重要、最經常使用的 image 倉庫。

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: isgiker
Password: 
Login Succeeded

接着,爲本地的 image 標註用戶名和版本。

$ docker image tag [imageName] [username]/[repository]:[tag]
# 實例
$ docker image tag passport-multi-stage:1.0.9 isgiker/passport-multi-stage:1.0.9

最後,發佈 image 文件。

$ docker image push isgiker/passport-multi-stage:1.0.9
The push refers to repository [docker.io/isgiker/passport-multi-stage]
e22072d3470d: Pushed 
9136612a4372: Pushed 
dac53910d311: Pushed 
77cae8ab23bf: Mounted from library/alpine 
1.0.9: digest: sha256:b5e9f0db2bd3e9ba684c8c359b087aa097adbb6a7426732b6d9246ca1b3dd6dc size: 1158

能夠經過 docker search 命令來查找官方倉庫中的鏡像,

$ docker search keywords[username/image name]

命令

image 命令

docker image COMMAND

Child commands

Command Description
docker image build Build an image from a Dockerfile
docker image history Show the history of an image
docker image import Import the contents from a tarball to create a filesystem image
docker image inspect Display detailed information on one or more images
docker image load Load an image from a tar archive or STDIN
docker image ls List images
docker image prune Remove unused images
docker image pull Pull an image or a repository from a registry
docker image push Push an image or a repository to a registry
docker image rm Remove one or more images
docker image save Save one or more images to a tar archive (streamed to STDOUT by default)
docker image tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

docker image 命令

container 命令

docker container COMMAND

Child commands

Command Description
docker container attach Attach local standard input, output, and error streams to a running container
docker container commit Create a new image from a container’s changes
docker container cp Copy files/folders between a container and the local filesystem
docker container create Create a new container
docker container diff Inspect changes to files or directories on a container’s filesystem
docker container exec Run a command in a running container
docker container export Export a container’s filesystem as a tar archive
docker container inspect Display detailed information on one or more containers
docker container kill Kill one or more running containers
docker container logs Fetch the logs of a container
docker container ls List containers
docker container pause Pause all processes within one or more containers
docker container port List port mappings or a specific mapping for the container
docker container prune Remove all stopped containers
docker container rename Rename a container
docker container restart Restart one or more containers
docker container rm Remove one or more containers
docker container run Run a command in a new container
docker container start Start one or more stopped containers
docker container stats Display a live stream of container(s) resource usage statistics
docker container stop Stop one or more running containers
docker container top Display the running processes of a container
docker container unpause Unpause all processes within one or more containers
docker container update Update configuration of one or more containers
docker container wait Block until one or more containers stop, then print their exit codes

docker container 命令

docker 命令

Child commands

Command Description
docker attach Attach local standard input, output, and error streams to a running container
docker build Build an image from a Dockerfile
docker builder Manage builds
docker checkpoint Manage checkpoints
docker commit Create a new image from a container’s changes
docker config Manage Docker configs
docker container Manage containers
docker context Manage contexts
docker cp Copy files/folders between a container and the local filesystem
docker create Create a new container
docker deploy Deploy a new stack or update an existing stack
docker diff Inspect changes to files or directories on a container’s filesystem
docker engine Manage the docker engine
docker events Get real time events from the server
docker exec Run a command in a running container
docker export Export a container’s filesystem as a tar archive
docker history Show the history of an image
docker image Manage images
docker images List images
docker import Import the contents from a tarball to create a filesystem image
docker info Display system-wide information
docker inspect Return low-level information on Docker objects
docker kill Kill one or more running containers
docker load Load an image from a tar archive or STDIN
docker login Log in to a Docker registry
docker logout Log out from a Docker registry
docker logs Fetch the logs of a container
docker manifest Manage Docker image manifests and manifest lists
docker network Manage networks
docker node Manage Swarm nodes
docker pause Pause all processes within one or more containers
docker plugin Manage plugins
docker port List port mappings or a specific mapping for the container
docker ps List containers
docker pull Pull an image or a repository from a registry
docker push Push an image or a repository to a registry
docker rename Rename a container
docker restart Restart one or more containers
docker rm Remove one or more containers
docker rmi Remove one or more images
docker run Run a command in a new container
docker save Save one or more images to a tar archive (streamed to STDOUT by default)
docker search Search the Docker Hub for images
docker secret Manage Docker secrets
docker service Manage services
docker stack Manage Docker stacks
docker start Start one or more stopped containers
docker stats Display a live stream of container(s) resource usage statistics
docker stop Stop one or more running containers
docker swarm Manage Swarm
docker system Manage Docker
docker tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
docker top Display the running processes of a container
docker trust Manage trust on Docker images
docker unpause Unpause all processes within one or more containers
docker update Update configuration of one or more containers
docker version Show the Docker version information
docker volume Manage volumes
docker wait Block until one or more containers stop, then print their exit codes

docker container 命令

參考

Go 語言應用 Docker化部署

Building Docker Containers for Go Applications

Deploying a containerized Go app on Kubernetes

Building Minimal Docker Containers for Go Applications

構建安全可靠、最小化的 Docker 鏡像

Docker ARG, ENV and .env - a Complete Guide

How To Pass Environment Info During Docker Builds
跟我一塊兒學Docker——Volume

相關文章
相關標籤/搜索