Go 實現容器技術的一個硬傷
比如,容器技術中需要用到的 setns
Docker 可以通過 exec 命令在一個存在的容器中運行一個進程,那麼這個進程就需要通過 setns 系統調用加入到容器對應的 namespace 中,然而 setns 並不能正確的在 Go runtime 這樣的多線程環境下工作,因此在實現一個容器的時候,這方面 Go 語言就遠沒有 C 語言來得直接、簡潔。
Docker 實現 setns 的原理
第一步就是需要用 os/exec 啟動一個新的進程。
cmd := &exec.Cmd{ Path: "/proc/self/exe", Args: []string{"setns"},}cmd.Start()
第二步最為關鍵,必須讓這個新的進程在啟動 runtime 多線程環境之前完成 setns 相關操作, Go 語言並沒有直接提供在一個程序啟動前執行某段代碼的機制,但 C 語言卻可以通過 gcc 的 擴展 __attribute__((constructor)) 來實現程序啟動前執行特定代碼,因此 Go 就可以通過 cgo 嵌入 這樣的一段 C 代碼來完成 runtime 啟動前執行特定的 C 代碼。
Docker 實現如下:
// +build linux,!gccgopackage nsenter/*#cgo CFLAGS: -Wallextern void nsexec();void __attribute__((constructor)) init(void) { nsexec();}*/import "C"
這段代碼就會在 Go 程序真正啟動前執行這裡定義的 init() 函數,然後執行 nsexec(), nsexec 函數里就可以干我們想乾的所有事情了,有興趣的看這裡 void nsexec() 。注意這裡定義的 nsenter 包並不需要被顯示使用,只需要 import 被編譯進去即可。
有人給 Go 提的 issue: proposal: runtime: add RunWithMainThreadOnly 和 討論 。
推薦閱讀:
※什麼是容器?為什麼我們關注它?
※Kubernetes 是什麼?
※CRI-O 1.0 簡介
※如何在 Windows 上運行 Linux 容器
※黃允松:雲計算的謊言與野心