Vue: parent component get child component data using slot & v-bind
本文介紹瞭如何使用Docker
以及Docker Compose
部署我們的 Go Web 程式。
為什麼需要Docker?
使用docker的主要目標是容器化。也就是為你的應用程式提供一致的環境,而不依賴於它執行的主機。
想象一下你是否也會遇到下面這個場景,你在本地開發了你的應用程式,它很可能有很多的依賴環境或包,甚至對依賴的具體版本都有嚴格的要求,當開發過程完成後,你希望將應用程式部署到web伺服器。這個時候你必須確保所有依賴項都安裝正確並且版本也完全相同,否則應用程式可能會崩潰並無法執行。如果你想在另一個web伺服器上也部署該應用程式,那麼你必須從頭開始重複這個過程。這種場景就是Docker發揮作用的地方。
對於執行我們應用程式的主機,不管是膝上型電腦還是web伺服器,我們唯一需要做的就是執行一個docker容器平臺。從以後,你就不需要擔心你使用的是MacOS,Ubuntu,Arch還是其他。你只需定義一次應用,即可隨時隨地執行。
Docker部署示例
準備程式碼
這裡我先用一段使用net/http
庫編寫的簡單程式碼為例講解如何使用Docker進行部署,後面再講解稍微複雜一點的專案部署案例。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", hello)
server := &http.Server{
Addr: ":8888",
}
fmt.Println("server startup...")
if err := server.ListenAndServe(); err != nil {
fmt.Printf("server startup failed, err:%v\n", err)
}
}
func hello(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte("hello liwenzhou.com!"))
}
上面的程式碼通過8888
埠對外提供服務,返回一個字串響應:hello liwenzhou.com!
。
建立Docker映象
映象(image)包含執行應用程式所需的所有東西——程式碼或二進位制檔案、執行時、依賴項以及所需的任何其他檔案系統物件。
或者簡單地說,映象(image)是定義應用程式及其執行所需的一切。
編寫Dockerfile
要建立Docker映象(image)必須在配置檔案中指定步驟。這個檔案預設我們通常稱之為Dockerfile
。(雖然這個檔名可以隨意命名它,但最好還是使用預設的Dockerfile
。)
現在我們開始編寫Dockerfile
,具體內容如下:
注意:某些步驟不是唯一的,可以根據自己的需要修改諸如檔案路徑、最終可執行檔案的名稱等
FROM golang:alpine
# 為我們的映象設定必要的環境變數
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# 移動到工作目錄:/build
WORKDIR /build
# 將程式碼複製到容器中
COPY . .
# 將我們的程式碼編譯成二進位制可執行檔案app
RUN go build -o app .
# 移動到用於存放生成的二進位制檔案的 /dist 目錄
WORKDIR /dist
# 將二進位制檔案從 /build 目錄複製到這裡
RUN cp /build/app .
# 宣告服務埠
EXPOSE 8888
# 啟動容器時執行的命令
CMD ["/dist/app"]
Dockerfile解析
From
我們正在使用基礎映象golang:alpine
來建立我們的映象。這和我們要建立的映象一樣是一個我們能夠訪問的儲存在Docker倉庫的基礎映象。這個映象執行的是alpine Linux發行版,該發行版的大小很小並且內建了Go,非常適合我們的用例。有大量公開可用的Docker映象,請檢視https://hub.docker.com/_/golang
Env
用來設定我們編譯階段需要用的環境變數。
WORKDIR,COPY,RUN
這幾個命令做的事都寫在註釋裡了,很好理解。
EXPORT,CMD
最後,我們宣告服務埠,因為我們的應用程式監聽的是這個埠並通過這個埠對外提供服務。並且我們還定義了在我們執行映象的時候預設執行的命令CMD ["/dist/app"]
。
構建映象
在專案目錄下,執行下面的命令建立映象,並指定映象名稱為goweb_app
:
docker build . -t goweb_app
等待構建過程結束,輸出如下提示:
...
Successfully built 90d9283286b7
Successfully tagged goweb_app:latest
現在我們已經準備好了映象,但是目前它什麼也沒做。我們接下來要做的是執行我們的映象,以便它能夠處理我們的請求。執行中的映象稱為容器。
執行下面的命令來執行映象:
docker run -p 8888:8888 goweb_app
標誌位-p
用來定義埠繫結。由於容器中的應用程式在埠8888上執行,我們將其繫結到主機埠也是8888。如果要繫結到另一個埠,則可以使用-p $HOST_PORT:8888
。例如-p 5000:8888
。
現在就可以測試下我們的web程式是否工作正常,開啟瀏覽器輸入http://127.0.0.1:8888
就能看到我們事先定義的響應內容如下:
hello liwenzhou.com!
分階段構建示例
我們的Go程式編譯之後會得到一個可執行的二進位制檔案,其實在最終的映象中是不需要go編譯器的,也就是說我們只需要一個執行最終二進位制檔案的容器即可。
Docker的最佳實踐之一是通過僅保留二進位制檔案來減小映象大小,為此,我們將使用一種稱為多階段構建的技術,這意味著我們將通過多個步驟構建映象。
FROM golang:alpine AS builder
# 為我們的映象設定必要的環境變數
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# 移動到工作目錄:/build
WORKDIR /build
# 將程式碼複製到容器中
COPY . .
# 將我們的程式碼編譯成二進位制可執行檔案 app
RUN go build -o app .
###################
# 接下來建立一個小映象
###################
FROM scratch
# 從builder映象中把/dist/app 拷貝到當前目錄
COPY --from=builder /build/app /
# 需要執行的命令
ENTRYPOINT ["/app"]
使用這種技術,我們剝離了使用golang:alpine
作為編譯映象來編譯得到二進位制可執行檔案的過程,並基於scratch
生成一個簡單的、非常小的新映象。我們將二進位制檔案從命名為builder
的第一個映象中複製到新建立的scratch
映象中。有關scratch映象的更多資訊,請檢視https://hub.docker.com/_/scratch
附帶其他檔案的部署示例
這裡以我之前《Go Web視訊教程》中的小清單專案為例,專案的Github倉庫地址為:https://github.com/Q1mi/bubble。
如果專案中帶有靜態檔案或配置檔案,需要將其拷貝到最終的映象檔案中。
我們的bubble專案用到了靜態檔案和配置檔案,具體目錄結構如下:
bubble
├── README.md
├── bubble
├── conf
│ └── config.ini
├── controller
│ └── controller.go
├── dao
│ └── mysql.go
├── example.png
├── go.mod
├── go.sum
├── main.go
├── models
│ └── todo.go
├── routers
│ └── routers.go
├── setting
│ └── setting.go
├── static
│ ├── css
│ │ ├── app.8eeeaf31.css
│ │ └── chunk-vendors.57db8905.css
│ ├── fonts
│ │ ├── element-icons.535877f5.woff
│ │ └── element-icons.732389de.ttf
│ └── js
│ ├── app.007f9690.js
│ └── chunk-vendors.ddcb6f91.js
└── templates
├── favicon.ico
└── index.html
我們需要將templates
、static
、conf
三個資料夾中的內容拷貝到最終的映象檔案中。更新後的Dockerfile
如下
FROM golang:alpine AS builder
# 為我們的映象設定必要的環境變數
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# 移動到工作目錄:/build
WORKDIR /build
# 複製專案中的 go.mod 和 go.sum檔案並下載依賴資訊
COPY go.mod .
COPY go.sum .
RUN go mod download
# 將程式碼複製到容器中
COPY . .
# 將我們的程式碼編譯成二進位制可執行檔案 bubble
RUN go build -o bubble .
###################
# 接下來建立一個小映象
###################
FROM scratch
COPY ./templates /templates
COPY ./static /static
COPY ./conf /conf
# 從builder映象中把/dist/app 拷貝到當前目錄
COPY --from=builder /build/bubble /
# 需要執行的命令
ENTRYPOINT ["/bubble", "conf/config.ini"]
簡單來說就是多了幾步COPY的步驟,大家看一下Dockerfile
中的註釋即可。
Tips: 這裡把COPY靜態檔案的步驟放在上層,把COPY二進位制可執行檔案放在下層,爭取多使用快取。
關聯其他容器
又因為我們的專案中使用了MySQL,我們可以選擇使用如下命令啟動一個MySQL容器,它的別名為mysql8019
;root使用者的密碼為root1234
;掛載容器中的/var/lib/mysql
到本地的/Users/q1mi/docker/mysql
目錄;內部服務埠為3306,對映到外部的13306埠。
docker run --name mysql8019 -p 13306:3306 -e MYSQL_ROOT_PASSWORD=root1234 -v /Users/q1mi/docker/mysql:/var/lib/mysql -d mysql:8.0.19
這裡需要修改一下我們程式中配置的MySQL的host地址為容器別名,使它們在內部通過別名(此處為mysql8019)聯通。
[mysql]
user = root
password = root1234
host = mysql8019
port = 3306
db = bubble
修改後記得重新構建bubble_app
映象:
docker build . -t bubble_app
我們這裡執行bubble_app
容器的時候需要使用--link
的方式與上面的mysql8019
容器關聯起來,具體命令如下:
docker run --link=mysql8019:mysql8019 -p 8888:8888 bubble_app
Docker Compose模式
除了像上面一樣使用--link
的方式來關聯兩個容器之外,我們還可以使用Docker Compose
來定義和執行多個容器。
Compose
是用於定義和執行多容器 Docker 應用程式的工具。通過 Compose,你可以使用 YML 檔案來配置應用程式需要的所有服務。然後,使用一個命令,就可以從 YML 檔案配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- 使用
Dockerfile
定義你的應用環境以便可以在任何地方複製。 - 定義組成應用程式的服務,
docker-compose.yml
以便它們可以在隔離的環境中一起執行。 - 執行
docker-compose up
命令來啟動並執行整個應用程式。
我們的專案需要兩個容器分別執行mysql
和bubble_app
,我們編寫的docker-compose.yml
檔案內容如下:
# yaml 配置
version: "3.7"
services:
mysql8019:
image: "mysql:8.0.19"
ports:
- "33061:3306"
command: "--default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql"
environment:
MYSQL_ROOT_PASSWORD: "root1234"
MYSQL_DATABASE: "bubble"
MYSQL_PASSWORD: "root1234"
volumes:
- ./init.sql:/data/application/init.sql
bubble_app:
build: .
command: sh -c "./wait-for.sh mysql8019:3306 -- ./bubble ./conf/config.ini"
depends_on:
- mysql8019
ports:
- "8888:8888"
這個 Compose 檔案定義了兩個服務:bubble_app
和 mysql8019
。其中:
bubble_app
使用當前目錄下的Dockerfile
檔案構建映象,並通過depends_on
指定依賴mysql8019
服務,宣告服務埠8888並繫結對外8888埠。
mysql8019
mysql8019 服務使用 Docker Hub 的公共 mysql:8.0.19 映象,內部埠3306,外部埠33061。
這裡需要注意一個問題就是,我們的bubble_app
容器需要等待mysql8019
容器正常啟動之後再嘗試啟動,因為我們的web程式在啟動的時候會初始化MySQL連線。這裡共有兩個地方要更改,第一個就是我們Dockerfile
中要把最後一句註釋掉:
# Dockerfile
...
# 需要執行的命令(註釋掉這一句,因為需要等MySQL啟動之後再啟動我們的Web程式)
# ENTRYPOINT ["/bubble", "conf/config.ini"]
第二個地方是在bubble_app
下面新增如下命令,使用提前編寫的wait-for.sh
指令碼檢測mysql8019:3306
正常後再執行後續啟動Web應用程式的命令:
command: sh -c "./wait-for.sh mysql8019:3306 -- ./bubble ./conf/config.ini"
當然,因為我們現在要在bubble_app
映象中執行sh命令,所以不能在使用scratch
映象構建了,這裡改為使用debian:stretch-slim
,同時還要安裝wait-for.sh
指令碼用到的netcat
,最後不要忘了把wait-for.sh
指令碼檔案COPY到最終的映象中,並賦予可執行許可權哦。更新後的Dockerfile
內容如下:
FROM golang:alpine AS builder
# 為我們的映象設定必要的環境變數
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# 移動到工作目錄:/build
WORKDIR /build
# 複製專案中的 go.mod 和 go.sum檔案並下載依賴資訊
COPY go.mod .
COPY go.sum .
RUN go mod download
# 將程式碼複製到容器中
COPY . .
# 將我們的程式碼編譯成二進位制可執行檔案 bubble
RUN go build -o bubble .
###################
# 接下來建立一個小映象
###################
FROM debian:stretch-slim
COPY ./wait-for.sh /
COPY ./templates /templates
COPY ./static /static
COPY ./conf /conf
# 從builder映象中把/dist/app 拷貝到當前目錄
COPY --from=builder /build/bubble /
RUN set -eux; \
apt-get update; \
apt-get install -y \
--no-install-recommends \
netcat; \
chmod 755 wait-for.sh
# 需要執行的命令
# ENTRYPOINT ["/bubble", "conf/config.ini"]
所有的條件都準備就緒後,就可以執行下面的命令跑起來了:
docker-compose up
完整版程式碼示例,請檢視我的github倉庫:https://github.com/Q1mi/deploy_bubble_using_docker。
總結
使用Docker容器能夠極大簡化我們在配置依賴環境方面的操作,但同時也對我們的技術儲備提了更高的要求。對於Docker不管你是熟悉抑或是不熟悉,技術發展的車輪都滾滾向前。
參考連結: