欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Go語言編寫一個極簡版的容器Container

 更新時間:2023年10月13日 10:31:31   作者:奇舞精選  
Docker作為一種流行的容器化技術,對于每一個程序開發(fā)者而言都具有重要性和必要性,因為容器化相關技術的普及大大簡化了開發(fā)環(huán)境配置、更好的隔離性和更高的安全性,對于部署項目和團隊協(xié)作而言也更加方便,本文將嘗試使用Go語言編寫一個極簡版的容器

前置知識儲備:

  • Linux 基礎知識

Docker 是基于 Linux 容器技術構建的,因此了解 Linux 操作系統(tǒng)的基本原理、命令和文件系統(tǒng)等知識對于理解本文乃至于Docker 源碼非常重要。

  • 容器技術基礎

了解容器技術的基本概念、原理和實現(xiàn)方式對于理解 Docker 源碼非常有幫助??梢詤⒖?Docker 官方文檔[1]中的容器概述部分,以及相關的教程和文章。

  • Go 語言基礎

Docker 的源碼主要是用 Go 語言編寫的,具體可以參考Go 語言官方文檔[2]。

[圖片來源:Docker架構概覽[3]]

什么是容器化

容器化是作為一種虛擬化技術,允許應用程序和其依賴的資源(如庫、環(huán)境變量等)被封裝在一個獨立的運行環(huán)境中,稱為容器。其核心概念主要包括:

  • 隔離性

容器使用操作系統(tǒng)級別的虛擬化技術,如Linux的命名空間和控制組(cgroup),實現(xiàn)隔離。每個容器都有自己的進程空間、文件系統(tǒng)、網(wǎng)絡和用戶空間,使得容器之間相互隔離,不會相互干擾。

  • 輕量性

相比傳統(tǒng)的虛擬機(VM),容器更加輕量級。容器共享主機操作系統(tǒng)的內(nèi)核,因此啟動更快、占用更少的資源。

  • 可移植性

容器可以在不同的環(huán)境中運行,包括開發(fā)、測試和生產(chǎn)環(huán)境。容器以相同的方式運行,不受底層基礎設施的影響,提供了更好的可移植性。

  • 可擴展性

容器可以根據(jù)需求進行擴展和縮減。容器編排工具(如Kubernetes)可以自動管理容器的部署、伸縮和負載均衡,提供彈性和可擴展性。

"如果創(chuàng)建一個容器就像系統(tǒng)調(diào)用 create_container 一樣簡單就好了"[4]

Guideline

這里我們粗略的估算一下可能涉及到的步驟會有:導入必要的包、main函數(shù)、子進程及其命名空間、掛載文件系統(tǒng)、運行子進程命令等。

我們知道真正的容器實現(xiàn)要復雜得多。它可能會涉及更多的命名空間設置、資源限制、文件系統(tǒng)掛載、網(wǎng)絡配置等方面的工作。

但是本文,“刪繁就簡”,主要是為了了解容器的基本原理。

按照這種實現(xiàn)的思路,我們開始一步步用代碼實現(xiàn):

package?main
import?(
?"fmt"
?"os"
?"os/exec"
?"syscall"
)
func?main()?{
?//?根據(jù)命令行參數(shù)選擇執(zhí)行不同的操作
?switch?os.Args[1]?{
?case?"run":
??parent()?//?執(zhí)行parent函數(shù)
?case?"child":
??child()?//?執(zhí)行child函數(shù)
?default:
??panic("wat?should?I?do")?//?拋出異常,程序無法繼續(xù)執(zhí)行
?}
}
func?parent()?{
?cmd?:=?exec.Command("/proc/self/exe",?append([]string{"child"},?os.Args[2:]...)...)
?cmd.Stdin?=?os.Stdin
?cmd.Stdout?=?os.Stdout
?cmd.Stderr?=?os.Stderr
?//?運行命令并檢查錯誤
?if?err?:=?cmd.Run();?err?!=?nil?{
??fmt.Println("ERROR",?err)
??os.Exit(1)
?}
}
func?child()?{
?cmd?:=?exec.Command(os.Args[2],?os.Args[3:]...)
?cmd.Stdin?=?os.Stdin
?cmd.Stdout?=?os.Stdout
?cmd.Stderr?=?os.Stderr
?//?運行命令并檢查錯誤
?if?err?:=?cmd.Run();?err?!=?nil?{
??fmt.Println("ERROR",?err)
??os.Exit(1)
?}
}
func?must(err?error)?{
?//?如果錯誤不為空,拋出panic異常
?if?err?!=?nil?{
??panic(err)
?}
}

我們從 main.go 開始,讀取第一個參數(shù)。如果是 "run",我們就運行Parent函數(shù),如果是 "child",我們就運行子方法。父方法運行"/proc/self/exe",這是一個包含當前可執(zhí)行文件內(nèi)存映像的特殊文件。

換句話說,我們重新運行自己,但將 child 作為第一個參數(shù)傳遞。

我們可以借此執(zhí)行另外一個執(zhí)行用戶請求的程序(在 os.Args[2:] 中提供)。有了這個簡單的腳手架,我們就可以創(chuàng)建一個容器了。

命名空間

在 Linux 中,命名空間(Namespace)[5]是一種內(nèi)核功能,用于隔離進程的資源視圖。它允許在同一系統(tǒng)上運行的進程具有獨立的資源副本,如進程 ID、網(wǎng)絡接口、文件系統(tǒng)掛載點等。這種隔離性可以提供更好的安全性和資源管理。 以下是一些常見的 Linux 命名空間類型:

  • PID命名空間:每個進程在 PID 命名空間中都有一個唯一的進程 ID。不同的 PID 命名空間中的進程 ID 可以重復,因此進程在其所屬的命名空間中可以認為是唯一的。
  • 網(wǎng)絡命名空間:每個網(wǎng)絡命名空間都有自己的網(wǎng)絡設備、IP 地址、路由表和防火墻規(guī)則。這使得在不同的網(wǎng)絡命名空間中可以進行網(wǎng)絡隔離和配置。
  • 文件系統(tǒng)命名空間:文件系統(tǒng)命名空間允許在不同的命名空間中使用不同的文件系統(tǒng)視圖。這意味著一個進程可以在一個命名空間中看到的文件和目錄,在另一個命名空間中可能是不可見的。
  • UTS 命名空間:UTS 命名空間用于隔離主機名和域名。每個 UTS 命名空間可以有自己獨立的主機名,這在容器化環(huán)境中非常有用。
  • IPC 命名空間:IPC 命名空間用于隔離不同進程之間的進程間通信(IPC)機制,如信號量、消息隊列和共享內(nèi)存等。
  • 用戶命名空間:用戶命名空間允許在不同命名空間中重新映射用戶和組 ID。這提供了更好的用戶隔離和權限管理。 通過使用這些命名空間,可以創(chuàng)建獨立的容器環(huán)境,每個容器都有自己的資源副本,從而實現(xiàn)更好的隔離和資源管理。

UTS命名空間

Linux UTS Namespace[6]。在 UTS 命名空間中,每個命名空間都有自己的主機名和域名。UTS 命名空間的使用場景包括:容器化和網(wǎng)絡隔離等。

要在程序中添加命名空間,我們只需在 parent() 方法的第二行,添加下面的這幾行代碼,以便于在Go運行子進程時傳遞給其一些額外的標識。

cmd.SysProcAttr?=?&syscall.SysProcAttr{
?Cloneflags:?syscall.CLONE_NEWUTS?|?syscall.CLONE_NEWPID?|?syscall.CLONE_NEWNS、
}

如果現(xiàn)在運行程序,程序將在 UTS、PID 和 MNT 命名空間內(nèi)運行。

在 Docker 中,根文件系統(tǒng)是由 Docker 鏡像提供的,并且在容器啟動時被掛載到容器的根目錄上。Docker 根文件系統(tǒng)一般具有分層結構、只讀性和寫時復制等特性。

現(xiàn)在,雖然我們的進程處于一組孤立的命名空間中,但文件系統(tǒng)看起來與主機相同。為了解決這個問題,我們需要以下四行代碼來實現(xiàn)根文件系統(tǒng):

must(syscall.Mount("rootfs",?"rootfs",?"",?syscall.MS_BIND,?""))
?must(os.MkdirAll("rootfs/oldrootfs",?0700))
????//?將當前目錄?`/`?移到?`rootfs/oldrootfs`?并將新的?rootfs?目錄交換到?`/`
?must(syscall.PivotRoot("rootfs",?"rootfs/oldrootfs"))
?must(os.Chdir("/"))

所以完整代碼如下:

package?main
import?(
?"fmt"
?"os"
?"os/exec"
?"syscall"
)
func?main()?{
?//?根據(jù)命令行參數(shù)選擇執(zhí)行不同的操作
?switch?os.Args[1]?{
?case?"run":
??parent()?//?執(zhí)行parent函數(shù)
?case?"child":
??child()?//?執(zhí)行child函數(shù)
?default:
??panic("wat?should?I?do")?//?拋出異常,程序無法繼續(xù)執(zhí)行
?}
}
func?parent()?{
?cmd?:=?exec.Command("/proc/self/exe",?append([]string{"child"},?os.Args[2:]...)...)
?//?設置子進程的命名空間
?cmd.SysProcAttr?=?&syscall.SysProcAttr{
??Cloneflags:?syscall.CLONE_NEWUTS?|?syscall.CLONE_NEWPID?|?syscall.CLONE_NEWNS,
?}
?cmd.Stdin?=?os.Stdin
?cmd.Stdout?=?os.Stdout
?cmd.Stderr?=?os.Stderr
?//?運行命令并檢查錯誤
?if?err?:=?cmd.Run();?err?!=?nil?{
??fmt.Println("ERROR",?err)
??os.Exit(1)
?}
}
func?child()?{
?//?掛載文件系統(tǒng)
?must(syscall.Mount("rootfs",?"rootfs",?"",?syscall.MS_BIND,?""))
?must(os.MkdirAll("rootfs/oldrootfs",?0700))
?must(syscall.PivotRoot("rootfs",?"rootfs/oldrootfs"))
?must(os.Chdir("/"))
?cmd?:=?exec.Command(os.Args[2],?os.Args[3:]...)
?cmd.Stdin?=?os.Stdin
?cmd.Stdout?=?os.Stdout
?cmd.Stderr?=?os.Stderr
?//?運行命令并檢查錯誤
?if?err?:=?cmd.Run();?err?!=?nil?{
??fmt.Println("ERROR",?err)
??os.Exit(1)
?}
}
func?must(err?error)?{
?//?如果錯誤不為空,拋出panic異常
?if?err?!=?nil?{
??panic(err)
?}
}

是的,至此,基于golang實現(xiàn)的極簡版的容器代碼已經(jīng)有了基本骨架。

Cgroups

Linux Cgroups[7] 在 Docker 容器化中起著重要的作用,它提供了對容器的資源限制和隔離,使得容器可以在共享的宿主機上運行而不會相互干擾:

  • 資源限制

通過 Cgroups,Docker 可以對容器的資源使用進行限制,如 CPU、內(nèi)存、磁盤和網(wǎng)絡等。這樣可以避免容器過度占用宿主機資源,保證系統(tǒng)的穩(wěn)定性和公平性。

  • 隔離性

Cgroups 提供了容器級別的資源隔離,每個容器都可以被分配和限制其使用的資源。這樣,容器之間的資源使用不會互相干擾,一個容器的問題也不會影響其他容器或宿主機。

  • 容器管理

Docker 使用 Cgroups 對容器進行管理和監(jiān)控。通過讀取和設置 Cgroups 的屬性,Docker 可以實時了解容器的資源使用情況,并可以調(diào)整資源限制以滿足需求。

在cgroup(控制組)這部分,需要注意Cgroup 的掛載和層級結構等限制。

所以我們將Cgrous這一部分加入到代碼實現(xiàn)中來如下:

package?main
import?(
????"fmt"
????"io/ioutil"
????"os"
????"os/exec"
????"strconv"
????"syscall"
)
func?main()?{
????//?創(chuàng)建?cgroup
????err?:=?createCgroup("mycontainer")
????if?err?!=?nil?{
????????fmt.Println("Failed?to?create?cgroup:",?err)
????????return
????}
????defer?func()?{
????????//?退出時刪除?cgroup
????????err?:=?deleteCgroup("mycontainer")
????????if?err?!=?nil?{
????????????fmt.Println("Failed?to?delete?cgroup:",?err)
????????}
????}()
????//?限制?CPU?使用率為?50%
????err?=?setCPULimit("mycontainer",?50)
????if?err?!=?nil?{
????????fmt.Println("Failed?to?set?CPU?limit:",?err)
????????return
????}
????//?在容器中運行命令
????cmd?:=?exec.Command("/bin/bash")
????cmd.Stdin?=?os.Stdin
????cmd.Stdout?=?os.Stdout
????cmd.Stderr?=?os.Stderr
????cmd.SysProcAttr?=?&syscall.SysProcAttr{
????????Cloneflags:?syscall.CLONE_NEWNS?|?syscall.CLONE_NEWPID?|?syscall.CLONE_NEWUTS?|?syscall.CLONE_NEWIPC?|?syscall.CLONE_NEWNET,
????????Cgroup:?????"mycontainer",
????}
????err?=?cmd.Run()
????if?err?!=?nil?{
????????fmt.Println("Failed?to?run?command?in?container:",?err)
????}
}
func?createCgroup(name?string)?error?{
????cgroupPath?:=?"/sys/fs/cgroup/cpu/"?+?name
????err?:=?os.Mkdir(cgroupPath,?0755)
????if?err?!=?nil?{
????????return?err
????}
????//?將當前進程加入到?cgroup?中
????err?=?ioutil.WriteFile(cgroupPath+"/tasks",?[]byte(strconv.Itoa(os.Getpid())),?0644)
????if?err?!=?nil?{
????????return?err
????}
????return?nil
}
func?deleteCgroup(name?string)?error?{
????cgroupPath?:=?"/sys/fs/cgroup/cpu/"?+?name
????err?:=?os.Remove(cgroupPath)
????if?err?!=?nil?{
????????return?err
????}
????return?nil
}
func?setCPULimit(name?string,?limit?int)?error?{
????cgroupPath?:=?"/sys/fs/cgroup/cpu/"?+?name
????err?:=?ioutil.WriteFile(cgroupPath+"/cpu.cfs_quota_us",?[]byte(strconv.Itoa(limit*1000)),?0644)
????if?err?!=?nil?{
????????return?err
????}
????return?nil
}

在上面,我們將當前進程加入到新創(chuàng)建的"mycontainer" 的 cgroup,然后,設置該 cgroup 的 CPU 使用率限制為 50%。繼而實現(xiàn)在容器中運行一個交互式的 shell。

結語

編寫一個容器(container)是一個相當復雜的任務,涉及到許多底層的概念和技術?;仡櫛疚?,使用golang一步步“還原”一個mini版的container所需步驟基本如下:

  • 了解容器技術和相關概念:在開始編寫mini容器之前,強烈建議先了解一些容器技術的基本原理,如命名空間(namespaces)、控制組(cgroups)、文件系統(tǒng)隔離等。
  • 選擇編程語言和庫:之所以選擇使用 Golang 進行容器的編寫,因為它提供了強大的并發(fā)和系統(tǒng)編程能力。同時,還可以使用一些相關的庫,如os/execsyscall
  • 創(chuàng)建容器的基本結構:首先創(chuàng)建出一個基本的容器結構,該結構將包含容器的信息,如 ID、進程 ID、文件系統(tǒng)等。
  • 設置容器的命名空間:使用 Golang 的syscall包,設置容器的命名空間,如 PID 命名空間、網(wǎng)絡命名空間等。這樣可以將容器中的進程與主機系統(tǒng)的進程隔離開來。
  • 設置容器的文件系統(tǒng):創(chuàng)建一個文件系統(tǒng),可以是一個文件夾或鏡像文件,用于存儲容器內(nèi)的文件和目錄。這里我們可以借助于 Golang 的osio/ioutil包來操作文件系統(tǒng)。
  • 啟動容器中的進程:使用os/exec包,在容器的命名空間中啟動一個新的進程, 并指定要運行的可執(zhí)行文件和參數(shù)。
  • 設置容器的網(wǎng)絡:如果想讓容器具有網(wǎng)絡連接能力,我們還需要設置容器的網(wǎng)絡命名空間,并進行相關網(wǎng)絡配置。這可能涉及到創(chuàng)建虛擬網(wǎng)絡設備、配置 IP 地址等。
  • 處理容器的生命周期:需要考慮到容器的創(chuàng)建、啟動、停止和銷毀等生命周期事件。這可能涉及到信號處理、資源清理等操作。

除此之外,還需要考慮到安全性、權限管理、資源限制等多方面因素。

當然,實際的容器實現(xiàn)要更加復雜和完善。在實際項目應用中,我們可能還需要考慮到如文件系統(tǒng)隔離、網(wǎng)絡隔離等遠比這些復雜的場景。

以上就是使用Go語言編寫一個極簡版的容器Container的詳細內(nèi)容,更多關于Go編寫容器的資料請關注腳本之家其它相關文章!

相關文章

  • VScode下配置Go語言開發(fā)環(huán)境(2023最新)

    VScode下配置Go語言開發(fā)環(huán)境(2023最新)

    在VSCode中配置Golang開發(fā)環(huán)境是非常簡單的,本文主要記錄了Go的安裝,以及給vscode配置Go的環(huán)境,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • 詳解Golang五種原子性操作的用法

    詳解Golang五種原子性操作的用法

    本文主要介紹了詳解Golang五種原子性操作的用法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • OpenTelemetry-go的SDK使用方法詳解

    OpenTelemetry-go的SDK使用方法詳解

    這篇文章主要介紹了OpenTelemetry-go的SDK使用方法,OpenTelemetry幫我們實現(xiàn)了相應語言的SDK,所以我們只需要進行調(diào)用即可,本文根據(jù)官方文檔實例講解,需要的朋友可以參考下
    2022-09-09
  • golang數(shù)據(jù)結構之golang稀疏數(shù)組sparsearray詳解

    golang數(shù)據(jù)結構之golang稀疏數(shù)組sparsearray詳解

    這篇文章主要介紹了golang數(shù)據(jù)結構之golang稀疏數(shù)組sparsearray的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • Go語言中緩沖bufio的原理解讀與應用實戰(zhàn)

    Go語言中緩沖bufio的原理解讀與應用實戰(zhàn)

    Go語言標準庫中的bufio包提供了帶緩沖的I/O操作,它通過封裝io.Reader和io.Writer接口,減少頻繁的I/O操作,提高讀寫效率,本文就來詳細的介紹一下,感興趣的可以學習
    2024-10-10
  • go編程中go-sql-driver的離奇bug解決記錄分析

    go編程中go-sql-driver的離奇bug解決記錄分析

    這篇文章主要為大家介紹了go編程中go-sql-driver的離奇bug解決記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • golang 阻止主goroutine退出的操作

    golang 阻止主goroutine退出的操作

    這篇文章主要介紹了golang 阻止主goroutine退出的操作方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang?strings包常用字符串操作函數(shù)

    Golang?strings包常用字符串操作函數(shù)

    Golang?中的字符串統(tǒng)一使用?UTF-8?(屬于Unicode編碼的一種實現(xiàn)方式)進行編碼,本篇文章將結合具體實例對常用的字符串操作函數(shù)進行介紹,感興趣的可以了解一下
    2021-12-12
  • 一文帶你了解Go語言中的單元測試

    一文帶你了解Go語言中的單元測試

    寫過單元測試的開發(fā)人員應該理解,單元測試最核心的價值是為了證明:為什么我寫的代碼是正確的?也就是從邏輯角度幫你檢查你的代碼。本文就來和大家詳細聊聊Go語言中的單元測試,需要的可以參考一下
    2022-07-07
  • GO語言的IO方法實例小結

    GO語言的IO方法實例小結

    這篇文章主要介紹了GO語言的IO方法實例小結,Docker的火爆促成了當下新興的Go語言人氣的大幅攀升,需要的朋友可以參考下
    2015-10-10

最新評論