Go dalam container
Kemarin ini nyobain bikin project go kecil-kecilan, intinya sih nyobain bikin koneksi ke mysql dan melakukan crud lah. Code nya saya ambil dari sini karena males bikin code sendiri.. wkwkwk.. (yes, i’m from wekaweka land)
kira-kira begini code main.go-nya
/*taken from here with minor edit*/package main
import (
_ "github.com/go-sql-driver/mysql"
"database/sql"
"fmt"
)
func main() {
db, err := sql.Open("mysql", "root:hai@/test?charset=utf8")
checkErr(err)
// insert
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
checkErr(err)
res, err := stmt.Exec("astaxie", "telo", "2012-12-09")
checkErr(err)
id, err := res.LastInsertId()
checkErr(err)
fmt.Println(id)
// update
stmt, err = db.Prepare("update userinfo set username=? where uid=?")
checkErr(err)
res, err = stmt.Exec("astaxieupdate", id)
checkErr(err)
affect, err := res.RowsAffected()
checkErr(err)
fmt.Println(affect)
// query
rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err)
for rows.Next() {
var uid int
var username string
var department string
var created string
err = rows.Scan(&uid, &username, &department, &created)
checkErr(err)
fmt.Println(uid)
fmt.Println(username)
fmt.Println(department)
fmt.Println(created)
}
// delete
stmt, err = db.Prepare("delete from userinfo where uid=?")
checkErr(err)
res, err = stmt.Exec(id)
checkErr(err)
affect, err = res.RowsAffected()
checkErr(err)
fmt.Println(affect)
db.Close()
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}
Tapi selain bikin code itu, saya juga iseng bikin Dockerfile agar golang script itu jalan di container. Kira-kira Dockerfile-nya seperti ini
FROM alpine:3.5WORKDIR /appCOPY ./test /appENTRYPOINT ["/app/test]
Kemudian saya compile dengan nama output test
go build -o test main.goBuild & run docker-nya
// build docker image
docker build --no-cache -t connect-mysql:v1.0// run docker image
docker run --net=host connect-mysql:v1.0
Dan akhirnya muncul error
standard_init_linux.go:178: exec user process caused "no such file or directoryError ini terjadi karena binary go yang sudah compile itu tidak dapat di-execute oleh linux alpine. Ada beberapa solusi yang bisa diambil,
- Menggunakan flag CGO_ENABLED=0
Saat meng-compile code golang yang di dalamnya tidak menggunakan library C sama sekali, sebaiknya menggunakan flag ini, sehingga cara compile nya
env CGO_ENABLED=0 go build -o test main.goJika env development nya menggunakan windows, maka ada tambahan flag menjadi
env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o test main.go2. Menggunakan image alpine untuk compile
Solusi ini menggunakan fitur multistage-build docker. Jadi yang perlu dilakukan adalah menggunakan image docker golang 1.8 yang sudah didalam alpine kemudian copy binary nya kedalam target image.
Tetapi sebelum itu, usahakan gunakan fitur vendor untuk golang agar semua dependency nya masuk ke dalam image tersebut dan tidak perlu melakukan go get didalam image builder. Untuk melakukan vendoring dependency, saya menggunakan govendor
govendor init
govendor add +externalKemudian Dockerfile-nya berubah menjadi
FROM golang:1.8-alpine3.5 AS builder
WORKDIR /go/src/test-mysql-docker
COPY . .
RUN go build -o test main.goFROM alpine:3.5
WORKDIR /app
COPY --from=builder /go/src/test-mysql-docker/test .
ENTRYPOINT ["/app/test"]
Solusi mana yang baik?
Kalau menurut saya sih tergantung selera. Untuk solusi nomor 1, mungkin lebih enak jika dimasukkan ke dalam Makefile. Contoh misal
.PHONY:allall: compile buildcompile:
CGO_ENABLED=0 go build -o test main.go
build:
docker build --no-cache -t connect-mysql:v1.0
Sehingga mudah saat melakukan operasi compile dan build nya. Problemnya adalah saat env development-nya berubah menggunakan windows atau osx misal, khususnya windows, karena jika di compile di windows maka jadinya adalah executable windows (file.exe), ga akan jalan di linux.
Untuk solusi nomor 2, tidak perlu melakukan compilasi di local, sehingga tidak masalah saat harus di compile di windows, linux, atau osx semua pasti jalan asal ada docker di host-nya.