2019年11月7日 星期四

使用 Go Get 在Docker 使用 multi-stage builds 搬檔案遇到的問題

source: mundodocker


前情提要


Docker 從 17.05 版本以後就有這個貼心的設計, multi-stage build(多階段建構),這對於長期因為 build docker image 太大而困擾的我們真的是很方便,所以現在 multi-stage build 幾乎已經成為 build docker image 標準配備。

在不支持多階段建構的年代,通常會有兩種作法:


暴力法


將所有的建構過程編寫在同一個 Dockerfile 中,包括項目及其依賴函式庫的編譯、測試、打包等流程,除了 docker image 肥大外還可能會帶來許多問題:
  • 臃腫的 Dockerfile
  • 太多層 image
  • 把不必要的東西包進去,造成 source code 泄露的風險

偽多階段建構


稍微優雅的一種方式,就是我們事先在外部將項目及其依賴函式庫編譯、測試、打包好後,再將其拷貝到建構的目錄中,這種雖然可以很好地規避第一種方式存在的風險點,但仍需要我們編寫兩套Dockerfile或者一些腳本才能將其兩個階段自動整合起來 ,還是很麻煩。


而現在我們有了 Multi stage build 可以使用,更多的教學可以參考:

使用Go get 安裝可執行的command 

go 從 1.8 就增加了一個貼心的功能 Go get ,原本是設計用來安裝需要的 library ,但是另外的功能就可以用來安裝使用 go 寫的小套件。


綜合兩個好用的東西,最近遇到了一個小問題.....

最近遇到的問題


情境:

最近的案子因為想要讓程式直接呼叫 rclone 這個好用的套件,所以最直觀的想法就是,把這個套件包到 docker 裡面,然後讓程式可以用 cmd 呼叫。

下面舉的範例,就是先建立一個 alpine 的 base image,然後在 builder 的 docker 裡面 build 程式以及安裝所需要的 rclone 執行檔(binary),最後再 copy 到需要執行的真正的 Docker image 裡面

FROM alpine AS base 
RUN apk add --no-cache bash
RUN apk add --no-cache ca-certificates  fuse

FROM gobuffalo/buffalo:v0.14.10 as builder
ARG GOPROXY=https://proxy.golang.org
RUN mkdir -p /go/src/github.com/howie/goth
WORKDIR /go/src/github.com/howie/goth
ADD . .
ENV GO111MODULES=on 
RUN go get -u -v github.com/rclone/rclone 
<--- br="">RUN go get $(go list ./... | grep -v /vendor/)
RUN buffalo build --static -o /bin/app
 

但是沒想到在執行的時候卻會產生以下的錯誤

fail to run in alpine docker with error "no such file or directory

From: https://github.com/gin-gonic/gin/issues/1178

上網研究了一下,找到了有人遇到類似的問題,Installed Go binary not found in path on Alpine Linux Docker 原因是

The installed Go version was compiled with glibc and on Alpine that is not installed by default. You can compile your Go with muslc, which is the default for Alpine, or do the symbolic link as above

所以只要多 copy libc.musl-x86 代替 ld-linux-x86-64 居然就可以了,真是神秘的 workaround


FROM base

RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
WORKDIR /bin/
COPY --from=builder /bin/app .
COPY --from=builder /go/bin/rclone /bin/rclone
# Uncomment to run the binary in "production" mode:
# ENV GO_ENV=production
# Bind the app to 0.0.0.0 so it can be seen from outside the container
ENV ADDR=0.0.0.0
ENV VERSION=0.0.0

EXPOSE 3000
# Uncomment to run the migrations before running the binary:
# CMD /bin/app migrate; /bin/app
CMD exec /bin/app


沒有留言 :