docker學習3,打包一個docker鏡像

之前使用docker安裝過mysql,使用的是別人製作好的鏡像。今天使用Dockerfile自己打包一個docker的鏡像。這個鏡像是一個web的鏡像,使用go編寫。go非常適合用來寫docker的鏡像程序,因爲go編譯後的二進制程序不依賴外外部庫(劃重點,後面會用到這個知識點),可以非常方便的打包進docker中。
這次打包鏡像會用到docker的端口映射和目錄掛載這兩個特性,後面用到會指出
我的編譯和打包是在linux中完成的,文中所有的介紹都是以linux系統爲基礎

先上web程序的代碼

package main

import (
    "net/http"
    "os"
    "strconv"
    "time"
)

const BASE_DIR = "./data/"

func main() {
    http.HandleFunc("/", Hand)
    http.ListenAndServe("0.0.0.0:8088", nil)
}

func Hand(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query()
    one := query.Get("one")
    now := time.Now().Unix()
    fileName := strconv.FormatInt(now, 10)

    file, err := os.OpenFile(BASE_DIR+fileName+".txt", os.O_RDWR|os.O_CREATE, 0666)
    if err == nil {
        file.WriteString(one)
        file.Close()
        w.Write([]byte("ok"))
    } else {
        w.Write([]byte("open file error,"))
        w.Write([]byte(err.Error()))
    }
}

簡單說一下邏輯,web會監聽8080 端口,當收到客戶端的請求時,會在當前目錄的/data/下創建一個當前時間戳.txt文件,並把獲取到的數據寫入到文件中,並把結果返回到網頁中

先把代碼編譯成可執行文件,執行 go build -ldflags "-w -s" main.go-ldflags "-w -s" 這個參數會自動優化代碼,這樣編譯出來的可執行文件體積會變小。
先在本地運行一下看一下效果


瀏覽器顯示正確,再看一下data目錄下有沒有對應的文件

文件也正常寫入了,說明程序是沒有問題的

下面開始打包docker

開始編寫Dockerfile文件

FROM scratch

WORKDIR /app

VOLUME /app/data

ADD main /app

COPY data /app

EXPOSE 8088

CMD ["/app/main"]

打包docker鏡像需要在一個鏡像的基礎上打包,scratch這個鏡像是一個很特殊的鏡像,是一個空的鏡像,大小是0,這樣我們打包出來的鏡像會很小,如果使用Ubuntu作爲基礎鏡像,打包出來的鏡像體積會很大,因爲Ubuntu鏡像就要有60M+了。更多關於scratch可以查看這裏。 用到的Dockerfile命令

  1. FROM 指定以哪個鏡像爲基礎開始構建鏡像
  2. WROKDIR 指定我們docker中的工作目錄,如果這個目錄不存在,會自動創建
  3. VOLUME 指定對外映射的目錄
  4. ADD 將本地的文件添加到docker中
  5. COPY 和ADD命令相似也是將本地的文件添加到docker中
  6. EXPOSE 指定對外映射的端口
  7. CMD 在docker中執行命令,如果有多個CMD,只有最有一個生效

別的命令沒什麼特殊的,簡單說一下ADDCOPY的區別,相同點都是講文件複製到docker中,不同的是ADD命令功能更多,如果複製到文件是tar,zip等壓縮文件時,ADD命令會自動解壓縮,ADD還可以從網絡上加載文件

Dockerfile文件寫好了,下一步開始構建了
使用 docker build -t goweb .命令構建 goweb 是指定構建後鏡像的名字


沒有報錯,說明構建完成

可以在本地的鏡像中看到剛纔構建的鏡像了,大小隻有5.4M

有了鏡像開始創建容器
現在用戶目錄下新建一個go_web目錄
使用docker run -idt --name goweb -v ~/go_web:/app/data -p 8088:8088 goweb命令創建容器
之前的文章已經介紹過了,不在贅述了,文章傳送門
看一下容器的運行情況

what,爲啥容器退出了,應該是出錯了
看一下錯誤


還記得前面畫的重點嗎,go編譯後的二進制程序不依賴外外部庫,其實這句話是錯誤的
看一下剛纔編譯的程序依賴那些外部庫

現在的main文件依賴4個外部文件,因爲我們是基於scratch打包的,scratch是一個空白的鏡像,沒有這些文件,所以運行就報錯了。有兩種解決辦法

  1. 使用Ubuntu等有這些庫的基礎鏡像,但是這樣會增加打包後鏡像的體積,很顯然不划算
  2. 使用靜態編譯,把這些庫編譯進main文件中

重新編譯go文件
先把原來的main改一個名字mv main main2
重新編譯CGO_ENABLED=0 go build -ldflags "-w -s" main.go CGO_ENABLED=0 指定爲靜態編譯
現在看一下main的外部依賴


現在的main不依賴外部文件了
把之前的docker鏡像和容器都刪掉,重新打包docker,重新生成容器

現在看一下



容器已經運行了,並且完成了端口映射
在瀏覽器中試一下


看一下go_web目錄


在go_web目錄中已經創建了文件,並且內容也正確寫入了

到這,自己打包docker鏡像就完成了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章