最近很長一段時間一直經過各類渠道去了解國內外有關devops方面的實踐,感覺不少的知識點都特別的分散,因此想經過系統的整理來鞏固相應的知識體系。接下來會撰寫一系列有關容器化的文章。
一、 利用docker部署一個簡單的go程序,而且利用阿里雲的平臺,進行鏡像的生成
二、 利用docker-compose部署一個複雜的go程序,同時部署包含多個不一樣子容器的集成
三、 利用gitlab和Harbor來作ci/cdlinux
容器是獨立的軟件包,可以將應用程序以及全部的依賴項、工具、庫、配置文件 以及運行該應用程序所須要的全部的其餘內容捆綁在一塊兒。
經過docker能夠實現將您 的應用環境從運行的物理操做系統上面抽象一層虛擬操做環境。從而保證你的程序從開發到測試以及現場的部署,保證了環境 的一致性問題。git
在開始演習以前,咱們經過下面命令來建立一個新的目錄,接下來咱們全部的文件都將存放在這個目錄當中。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.工具
# 構建該項目的基礎鏡像 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"]
上面咱們已經定義了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 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
如今咱們開始一個新的實例,在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