摘要:與容器內第一個進程進程看創建了這么多子進程,然后到了我們期待的自己中的進程就要被創建了,想想都有點小激動,然而。。。
Docker架構分析
[root@docker-build-86-050 ~]# ls /usr/bin |grep docker docker docker-compose docker-containerd docker-containerd-ctr docker-containerd-shim dockerd docker-proxy docker-runc
大家一定很困惑 dockerd, containerd, ctr,shim, runc,等這幾個進程的關系到底是啥
初窺得出的結論是:
docker是cli沒啥可說的
dockerd是docker engine守護進程,dockerd啟動時會啟動containerd子進程。
dockerd與containerd通過rpc進行通信
ctr是containerd的cli
containerd通過shim操作runc,runc真正控制容器生命周期
啟動一個容器就會啟動一個shim進程
shim直接調用runc的包函數,shim與containerd之前通過rpc通信
真正用戶想啟動的進程由runc的init進程啟動,即runc init [args ...]
進程關系模型:
docker ctr | | V V dockerd -> containerd ---> shim -> runc -> runc init -> process |-- > shim -> runc -> runc init -> process +-- > shim -> runc -> runc init -> process
[root@docker-build-86-050 ~]# ps -aux|grep docker root 3925 0.0 0.1 2936996 74020 ? Ssl 3月06 68:14 /usr/bin/dockerd --storage-driver=aufs -H 0.0.0.0:2375 --label ip=10.1.86.50 -H unix:///var/run/docker.sock --insecure-registry 192.168.86.106 --insecure-registry 10.1.86.51 --insecure-registry dev.reg.iflytek.com root 3939 0.0 0.0 1881796 27096 ? Ssl 3月06 9:10 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --shim docker-containerd-shim --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --runtime docker-runc root 21238 0.0 0.0 487664 6212 ? Sl 4月20 0:00 docker-containerd-shim 48119c50a0ca8a53967364f75fb709017cc272ae248b78062e0dafaa22108d21 /var/run/docker/libcontainerd/48119c50a0ca8a53967364f75fb709017cc272ae248b78062e0dafaa22108d21 docker-runcdockerd 與 containerd 之間的基情
首先dockerd的main函數相信你能找到cmd/dockerd/docker.go
其它的先略過,直接進start看一看:
err = daemonCli.start(opts)
這函數里我們先去關注兩件事:
創建了多個Hosts,這是給client去連接的,dockerd啟動時用-H參數指定,可以是多個,如指定一個tcp 指定一個unix sock( -H unix:///var/run/docker.sock)
創建了containerd子進程
這個New很重要
containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
進去看看:
... err := r.runContainerdDaemon(); ... conn, err := grpc.Dial(r.rpcAddr, dialOpts...) if err != nil { return nil, fmt.Errorf("error connecting to containerd: %v", err) } r.rpcConn = conn r.apiClient = containerd.NewAPIClient(conn) ...
啟動了一個containerd進程,并與之建立連接。通過protobuf進行rpc通信, grpc相關介紹看這里
具體如何創建containerd進程的可以進入runContainerDaemon里細看
cmd := exec.Command(containerdBinary, args...) // redirect containerd logs to docker logs cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.SysProcAttr = setSysProcAttr(true) cmd.Env = nil // clear the NOTIFY_SOCKET from the env when starting containerd for _, e := range os.Environ() { if !strings.HasPrefix(e, "NOTIFY_SOCKET") { cmd.Env = append(cmd.Env, e) } } if err := cmd.Start(); err != nil { return err }
看不明白的話,去標準庫里惡補一下cmd怎么用。 cmd.Start()異步創建進程,創建完直接返回
所以創建一個協程等待子進程退出
go func() { cmd.Wait() close(r.daemonWaitCh) }() // Reap our child when neededdocker-containerd-shim是何方神圣 與containerd和runc又有什么關系?
代碼中的一句話解釋:shim for container lifecycle and reconnection, 容器生命周期和重連, 所以可以順著這個思路去看。
先看containerd/linux/runtime.go里的一段代碼:
Runtime 的Create方法里有這一行,這里的Runtime對象也是注冊到register里面的,可以看init函數,然后containerd進程啟動時去加載了這個Runtime
s, err := newShim(path, r.remote)
縮減版內容:
func newShim(path string, remote bool) (shim.ShimClient, error) { l, err := sys.CreateUnixSocket(socket) //創建了一個UnixSocket cmd := exec.Command("containerd-shim") f, err := l.(*net.UnixListener).File() cmd.ExtraFiles = append(cmd.ExtraFiles, f) //留意一下這個,非常非常重要,不知道這個原理可能就看不懂shim里面的代碼了 if err := reaper.Default.Start(cmd); err != nil { //啟動了一個shim進程 } return connectShim(socket) // 這里返回了與shim進程通信的客戶端 }
再去看看shim的代碼:
shim進程啟動干的最主要的一件事就是啟動一個grpc server:
if err := serve(server, "shim.sock"); err != nil {
進去一探究竟:
func serve(server *grpc.Server, path string) error { l, err := net.FileListener(os.NewFile(3, "socket")) logrus.WithField("socket", path).Debug("serving api on unix socket") go func() { if err := server.Serve(l); err != nil && } }() }
我曾經因為這個os.NewFile(3, "socket")看了半天看不懂,為啥是3?聯系cmd.ExtraFiles = append(cmd.ExtraFiles, f) 創建shim進程時的這句,問題解決了。
這個3的文件描述符,就是containerd用于創建UnixSocket的文件,這樣containerd的client剛好與這邊啟動的 grpc server連接上了,可以遠程調用其接口了:
type ContainerServiceClient interface { Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*containerd_v1_types1.Container, error) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) Events(ctx context.Context, in *EventsRequest, opts ...grpc.CallOption) (ContainerService_EventsClient, error) Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error) Pty(ctx context.Context, in *PtyRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) CloseStdin(ctx context.Context, in *CloseStdinRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) }
containerd與shim通信模型介紹
再看shim與runc的關系,這個比較簡單了,直接進入shim service 實現的Create方法即可
sv = shim.New(path)
func (s *Service) Create(ctx context.Context, r *shimapi.CreateRequest) (*shimapi.CreateResponse, error) { process, err := newInitProcess(ctx, s.path, r) return &shimapi.CreateResponse{ Pid: uint32(pid), }, nil }
進入到newInitProcess里面:
func newInitProcess(context context.Context, path string, r *shimapi.CreateRequest) (*initProcess, error) { runtime := &runc.Runc{ Command: r.Runtime, Log: filepath.Join(path, "log.json"), LogFormat: runc.JSON, PdeathSignal: syscall.SIGKILL, } p := &initProcess{ id: r.ID, bundle: r.Bundle, runc: runtime, } if err := p.runc.Create(context, r.ID, r.Bundle, opts); err != nil { return nil, err } return p, nil }
可以看到,在這里調用了runc的API去真正執行創建容器的操作。其本質是調用了runc create --bundle [bundle] [containerid] 命令,在此不多作介紹了
shim進程與runc進程之間上文可知,shim進程創建runc子進程。
runc 與 容器內第一個進程 init進程看docker創建了這么多子進程,然后到了runc我們期待的自己Dockerfile中的CMD進程就要被創建了,想想都有點小激動,然而。。。
runc進程啟動后會去啟動init進程,去創建容器,然后在容器中創建進程,那才是真正我們需要的進程
關于runc init進程關鍵看StartInitialization方法(main_unix.go)
docker-containerd-ctr 與 docker-containerdctr 是一個containerd的client,之間通過proto rpc通信, containerd監聽了unix:///run/containerd/containerd.sock。
[root@dev-86-201 ~]# docker-containerd --help NAME: containerd - High performance container daemon USAGE: docker-containerd [global options] command [command options] [arguments...] VERSION: 0.2.0 commit: 0ac3cd1be170d180b2baed755e8f0da547ceb267 COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --debug enable debug output in the logs --state-dir "/run/containerd" runtime state directory --metrics-interval "5m0s" interval for flushing metrics to the store --listen, -l "unix:///run/containerd/containerd.sock" proto://address on which the GRPC API will listen --runtime, -r "runc" name or path of the OCI compliant runtime to use when executing containers --runtime-args [--runtime-args option --runtime-args option] specify additional runtime args --shim "containerd-shim" Name or path of shim --pprof-address http address to listen for pprof events --start-timeout "15s" timeout duration for waiting on a container to start before it is killed --retain-count "500" number of past events to keep in the event log --graphite-address Address of graphite server --help, -h show help --version, -v print the version
[root@dev-86-201 ~]# docker-containerd-ctr --help NAME: ctr - High performance container daemon cli USAGE: docker-containerd-ctr [global options] command [command options] [arguments...] VERSION: 0.2.0 commit: 0ac3cd1be170d180b2baed755e8f0da547ceb267 COMMANDS: checkpoints list all checkpoints containers interact with running containers events receive events from the containerd daemon state get a raw dump of the containerd state version return the daemon version help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --debug enable debug output in the logs --address "unix:///run/containerd/containerd.sock" proto://address of GRPC API --conn-timeout "1s" GRPC connection timeout --help, -h show help --version, -v print the versionrunc 架構破析
比較復雜也比較重要,所以我將多帶帶寫一篇相關的介紹 這里
使用runc直接創建容器mkdir /mycontainer cd /mycontainer mkdir rootfs docker export $(docker create busybox) | tar -C rootfs -xvf - # 生成容器的配置文件config.json runc spec runc run mycontainerid容器狀態文件
默認存在/run/runc目錄下,不管是docker engine創建的容器還是通過runc直接創建的容器都會在/run/runc目錄下創建一個以容器名命名的目錄,下面有個state.json文件用于存儲文件狀態
更多問題歡迎關注我的github: https://github.com/fanux
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/27049.html
摘要:它使用機器學習來解釋用戶提出的問題,并用相應的知識庫文章來回應。使用一類目前較先進的機器學習算法來識別相關文章,也就是深度學習。接下來介紹一下我們在生產環境中配置模型的一些經驗。 我們如何開始使用TensorFlow ?在Zendesk,我們開發了一系列機器學習產品,比如的自動答案(Automatic Answers)。它使用機器學習來解釋用戶提出的問題,并用相應的知識庫文章來回應。當用戶有...
摘要:鏡像鏡像是構建的基石。公司運營公共的叫做。標準集裝箱將貨物運往世界各地,將這個模型運用到自己的設計中,唯一不同的是集裝箱運輸貨物,而運輸軟件。這一點在面向服務的架構和重度依賴微型服務的部署由其實用。用創建隔離的環境來進行測試。 Docker特點1)上手快 用戶只需要幾分鐘,就可以把自己的程序Docker 化。Docker 依賴于寫時復制 (copy-on-write)模型,使修改應用程...
摘要:無論這個連接是外部主動建立的,還是內部建立的。協議有表示層數據的表示安全壓縮。在整個發展過程中的所有思想和著重點都以一種稱為的文檔格式存在。 部署基礎知識url:協議://網站地址:端口(/)路徑地址?參數eg: http://www.baidu.com:80/abc/dd/ www.baidu.com找服務器 80端口:找服務器上提供服務的應用 nginx uri:/ab...
摘要:無論這個連接是外部主動建立的,還是內部建立的。協議有表示層數據的表示安全壓縮。在整個發展過程中的所有思想和著重點都以一種稱為的文檔格式存在。 部署基礎知識url:協議://網站地址:端口(/)路徑地址?參數eg: http://www.baidu.com:80/abc/dd/ www.baidu.com找服務器 80端口:找服務器上提供服務的應用 nginx uri:/ab...
閱讀 1572·2021-10-14 09:42
閱讀 3815·2021-09-07 09:59
閱讀 1292·2019-08-30 15:55
閱讀 572·2019-08-30 11:17
閱讀 3337·2019-08-29 16:06
閱讀 500·2019-08-29 14:06
閱讀 3123·2019-08-28 18:14
閱讀 3642·2019-08-26 13:55