Skip to Content

alpineイメージにgo build済みのバイナリを載せて実行

概要

alpineイメージにgo build済みのシングルバイナリだけを載せて動かす

ポイント

alpineで動かすには…
go build時に CGO_ENABLED=0 で、cgoを無効にする必要があった

> GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go

目的

alpineイメージにGoのシングルバイナリ一発構成をやってみたかった
KubernetesのPodで動かしてみたい

TCPサーバを作る

Goを始めたばかりなので参考サイト見ながら簡素な作りなもの作った

package main

import (
	"fmt"
	"net"
	"os"
)

func main() {
	// マシンのホスト名取得
	hostname, err := os.Hostname()
	if err != nil {
		fmt.Println("ホスト名不明")
	}

	// localhost:9999でリッスン
	listen, err := net.Listen("tcp", ":9999")
	if err != nil {
		fmt.Println("ポート9999のリッスンに失敗")
		return
	}
	defer listen.Close()
	fmt.Println("ポート9999で受付開始")

	// コネクションを確率する
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("コネクションを確立できませんでした")
			return
		}
		buf := make([]byte, 1024)

		// 1リクエスト処理中に他のリクエストのAccept()が行えるように
		// Goroutineを使って非同期にレスポンスを処理する
		go func() {
			// リクエスト元のアドレス
			fmt.Printf("[Remote Address]\n%s\n", conn.RemoteAddr())

			// Readerを作成して、送られてきたメッセージを出力する
			n, _ := conn.Read(buf)
			fmt.Print("[Message]\n%s", string(buf[:n]))

			// メッセージを返す
			// res := fmt.Sprintf("Hello, %s\n", conn.RemoteAddr())
			res := fmt.Sprintf("Hostname: %s", hostname)
			conn.Write([]byte(res))

			// コネクション切断
			conn.Close()
		}()
	}
}
alpineで動作する用にbuildする

CGO_ENABLEDを知らなくビルドしてもホストでは動くけど、alpineでは動かないと嘆いた
ついでにリネームする

> GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go

> mv main tcp_server
Dockerfileを作成

先程ビルドした”tcp_server”をコピーして、ただ実行するだけの内容
TCPサーバはポート9999で受け付ける

FROM alpine:3.9

WORKDIR /app
COPY tcp_server /app

EXPOSE 9999

CMD ["/app/tcp_server"]
ディレクトリ構成
> tree -a
.
├── .dockerignore
├── Dockerfile
├── main.go
└── tcp_server
.dockerignoreの中身

main.goを除きたかった

> cat .dockerignore
*.go
コンテナイメージ作成

ここに至るまで試行錯誤の末、タグがversion7である

> docker build -t tcp_server:v7 .
省略
Successfully built 5049824a8a80
Successfully tagged tcp_server:v7
イメージ確認

alpineイメージを利用したから、サイズがたったの9MBである

docker images
REPOSITORY                                TAG                 IMAGE ID            CREATED             SIZE
tcp_server                                v7                  5049824a8a80        3 seconds ago       8.44MB
コンテナ起動

自前のTCPサーバはポート9999で受け付けるので、”任意:9999”にする必要がある

> docker run -it -d -p 9999:9999 tcp_server:v7
acdfea076a9b1e385c62cadeb0887af3f32eb314cf8275f7e2d761c1fe809e22

> docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
acdfea076a9b        tcp_server:v7       "/app/tcp_server"   6 seconds ago       Up 4 seconds        0.0.0.0:9999->9999/tcp   tender_ellis
コンテナにアクセス

curlでアクセスすると、ホスト名としてコンテナIDが表示される

curl localhost:9999
Hostname: acdfea076a9b

感想

シンプル極まりない

参考

https://kuroeveryday.blogspot.com/2018/08/how-to-build-tcp-server-with-goilang.html https://ascii.jp/elem/000/001/276/1276572/ https://www.systutorials.com/241704/how-to-get-the-hostname-of-the-node-in-go/ https://stackoverflow.com/questions/36279253/go-compiled-binary-wont-run-in-an-alpine-docker-container-on-ubuntu-host