利用docker來部署go應用程序

利用docker實現go程序的快速部署

最近很長一段時間一直經過各類渠道去了解國內外有關devops方面的實踐,感覺不少的知識點都特別的分散,因此想經過系統的整理來鞏固相應的知識體系。接下來會撰寫一系列有關容器化的文章。
一、 利用docker部署一個簡單的go程序,而且利用阿里雲的平臺,進行鏡像的生成
二、 利用docker-compose部署一個複雜的go程序,同時部署包含多個不一樣子容器的集成
三、 利用gitlab和Harbor來作ci/cdlinux

容器是獨立的軟件包,可以將應用程序以及全部的依賴項、工具、庫、配置文件 以及運行該應用程序所須要的全部的其餘內容捆綁在一塊兒。
經過docker能夠實現將您 的應用環境從運行的物理操做系統上面抽象一層虛擬操做環境。從而保證你的程序從開發到測試以及現場的部署,保證了環境 的一致性問題。git

建立一個簡單的Go項目

在開始演習以前,咱們經過下面命令來建立一個新的目錄,接下來咱們全部的文件都將存放在這個目錄當中。github

mkdir go-docker
在建立完成目錄以後,咱們須要用go的原生命令來初始化相應的go模塊。go mod是go11新增長的功能,在這以前出現很好幾種模塊化的解決方案,在go11以後官方終於給出了本身的解決方案.
cd go-docker
go mod init github.com/wenchangshou2/go-docker
咱們如今建立一個簡單的hello world的服務,經過下面的命令來建立一個新的文件 
touch hello_server.gogolang

如下是hello_server.go的內容docker

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gorilla/mux"
    "gopkg.in/natefinch/lumberjack.v2"
)

func handler(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query()
    name := query.Get("name")
    if name == "" {
        name = "訪客"
    }
    log.Printf("接收一個新的請求 %s\n", name)
    w.Write([]byte(fmt.Sprintf("Hello, %s\n", name)))
}

func main() {
    // 建立服務和root事件
    r := mux.NewRouter()

    r.HandleFunc("/", handler)

    srv := &http.Server{
        Handler:      r,
        Addr:         ":8080",
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
    }

    // 配置日誌
    LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
    if LOG_FILE_LOCATION != "" {
        log.SetOutput(&lumberjack.Logger{
            Filename:   LOG_FILE_LOCATION,
            MaxSize:    500, // 總大小
            MaxBackups: 3,
            MaxAge:     28,   //最長保存時間
            Compress:   true, // 壓縮數據,默認關閉
        })
    }

    // 啓動服務
    go func() {
        log.Println("啓動服務")
        if err := srv.ListenAndServe(); err != nil {
            log.Fatal(err)
        }
    }()

    // 正常關閉
    waitForShutdown(srv)
}

func waitForShutdown(srv *http.Server) {
    interruptChan := make(chan os.Signal, 1)
    signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

    // 阻塞,直到接收到信號
    <-interruptChan

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()
    srv.Shutdown(ctx)

    log.Println("結束退出")
    os.Exit(0)
}

經過下面的命令來構建一個應用程序app

wcs@iMac  ~/demo/go-docker  go build  ✔  >4950  23:22:27
go: finding github.com/gorilla/mux v1.7.4
go: downloading github.com/gorilla/mux v1.7.4
go: extracting github.com/gorilla/mux v1.7.4
go: downloading gopkg.in/natefinch/lumberjack.v2 v2.0.0
go: extracting gopkg.in/natefinch/lumberjack.v2 v2.0.0
經過go build,會自動下載須要的包,而且生成相應的可執行程序,經過下面的命令運行
wcs@iMac  ~/demo/go-docker  ./go-docker  ✔  
2020/02/26 23:24:05 啓動服務curl

如今咱們嘗試經過curl 來測試功能是否可用tcp

wcs@iMac  ~  curl localhost:8080  ✔  4958 
Hello, 訪客
wcs@iMac  ~  curl http://localhost:8080\?name\=Rajeev
Hello, Rajeev模塊化

經過DockerFile文件來定義docker 鏡像文件

如今咱們須要在上面的根目錄下面新建一個文件,將文件的名稱命名爲Dockerfile.工具

# 構建該項目的基礎鏡像
FROM golang:latest

# 添加做者信息
LABEL maintainer="wcs <wenchangshou@gmail.com>"

# 設置當前的工做目錄
WORKDIR /app

# 複製 go mod和sum 文件 
COPY go.mod go.sum ./

# 下載全部的依賴
RUN go mod download
# 複製當前目錄源文件到工做目錄下面

COPY . .
# 編譯GO程序
RUN go build -o main .

# 經過EXPOSE對外暴露服務的端口號

EXPOSE 8080
# 經過下面的命令來運行可執行文件 
CMD ["./main"]

編譯和運行docker鏡像

上面咱們已經定義了DockerFile,如今咱們須要經過命令將DockerFile生成構建和運行相應的映像

編譯鏡像

docker build -t go-docker .

你懂的國內應該各類緣由,訪問會特別的慢,你能夠利用aliyun和daocloud來進行回事。
經過下面的命令來查看鏡像是否生成成功.

docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
go-docker lates 7654e880c09b 2 minutes ago 826MB
golang latest c3474fb0f20e 15 hours ago 809MB

運行docker鏡像

如今咱們終端能夠運行以前生成的鏡像了

docker run -d -p 8888:8080 go-docker

3c80b9cb6b21a4fd50ea8b157ace4f3a9757e92b0e3cf9192639d30299d03d73

查看運行的容器

你能夠經過下面的命令來查看正在運行的容器
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c80b9cb6b21 go-docker "./main" 45 seconds ago Up 45 seconds 0.0.0.0:8888->8080/tcp quirky_shamir

測試容器功能

如今咱們能夠經過curl來測試應用是否正常

curl http://localhost:8888\?name\=wcs
Hello, wcs

中止容器

如今運行完成以後咱們就能夠中止相應的容器。在每次建立容器的時候都會產生一個惟一的id,那個id用來標識相應的容器,以後能夠經過該id進行相應的操做

docker container stop 3c80b9cb6b21

掛載本地目錄到docker鏡像中

如今咱們開始一個新的實例,在dockerfile裏面提供了一個volume的字段,經過該字段能夠定義容器內部的共享的目錄。定義完成以後,你能夠在建立容器的時候指定volume目錄與本地目錄的綁定。
在如下Dockerfile中,咱們聲明一個volume路徑/app/logs。容器會將日誌文件寫入/app/logs/app.log。運行docker映像時,咱們能夠將宿主的目錄掛載到該捲上。完成此操做後,咱們將可以從宿主的已掛載目錄訪問全部日誌文件。

# 構建該項目的基礎鏡像
FROM golang:latest

# 添加做者信息
LABEL maintainer="wcs <wenchangshou@gmail.com>"

# 設置當前的工做目錄
WORKDIR /app

# 編譯參數
ARG LOG_DIR=/app/logs
# 建立log目錄
RUN mkdir -p ${LOG_DIR}

#環境變量
ENV LOG_FILE_LOCATION=${LOG_DIR}/app.log

# 複製 go mod和sum 文件 
COPY go.mod go.sum ./

# 下載全部的依賴
RUN go mod download
# 複製當前目錄源文件到工做目錄下面

COPY . .
# 編譯GO程序
RUN go build -o main .

# 經過EXPOSE對外暴露服務的端口號

EXPOSE 8080
# 聲明Volumes
VOLUME [${LOG_DIR}]
# 經過下面的命令來運行可執行文件 
CMD ["./main"]

咱們從新編譯一個新的鏡像

docker build -t go-docker-voluume -f Dockerfile.volume

如今咱們建立宿主將要綁定的目錄

mkdir -p ~/logs/go-docker
docker run -d -p 8889:8080 -v ~/logs/go-docker:/app/logs go-docker-volume 
394433aa0804dd24443d65090006e8becbe2aa26a883efe4324e79d4e085e261

如今咱們發現docker已經將日誌寫入到宿主對象的目錄當中

tail -f ~/logs/go-docker/app.log

2020/02/26 16:01:38 啓動服務

優化鏡像

經過上面的示例,咱們生成了二個鏡像,可是大家仔細查看,會發現即便這麼小功能的一個程序,也會佔用很大的存儲空間。
docker image ls  ✔  4992  00:04:52
REPOSITORY TAG IMAGE ID CREATED SIZE
go-docker-volume latest 0b534f6bceaf 4 minutes ago 826MB
go-docker latest 7654e880c09b 19 minutes ago 826MB
golang latest c3474fb0f20e 16 hours ago 809MB
僅golang:latest鏡像就佔用了809MB,,而咱們生成程序也佔用了826M的空間。
上面之因此這樣大,是由於golang:latest裏面包含了整個go的運行環境以及相應的編譯環境,而咱們的應用程序僅在編譯時須要這些,在編譯完成以後僅運行就能夠。根據這些需求咱們能夠將原有的鏡像拆分紅多步,第一步是編譯,以後咱們能夠經過Alpine linux來進行最小化運行。
Dockerfile.multistage

FROM golang:latest as builder

LABEL maintainer="wcs<wenchangshou@gmail.com>"

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .



# 開始一個新的步驟
FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/
# 複製上一階段的預構建二進制文件
COPY --from=builder /app/main .

EXPOSE 8080

CMD ["./main"]

如今咱們生成新的鏡像

docker build -t go-docker-optimized -f Dockerfile.multistage .

如今咱們再看壓縮後的鏡像

docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE go-docker-optimized latest ed8e431271dc 23 seconds ago 14.1MB go-docker-volume latest 0b534f6bceaf 11 minutes ago 826MB go-docker latest 7654e880c09b 27 minutes ago 826MB golang latest c3474fb0f20e 16 hours ago 809MB alpine latest e7d92cdc71fe 5 weeks ago 5.59MB 如今你驚訝的發現壓縮後的鏡像僅有14.1M

相關文章
相關標籤/搜索