Bagaimana proses pembuatan container buruh pelabuhan bekerja (dari docker run hingga runc)

Penerjemahan artikel disiapkan untuk mengantisipasi dimulainya kursus "Platform infrastruktur berdasarkan Kubernetes" .








Selama beberapa bulan terakhir, saya menghabiskan banyak waktu pribadi saya mempelajari cara kerja wadah Linux. Secara khusus, apa sebenarnya fungsinya docker run. Dalam artikel ini, saya akan meringkas apa yang telah saya temukan dan mencoba menunjukkan bagaimana masing-masing elemen membentuk gambaran besar. Kami akan memulai perjalanan kami dengan membuat wadah alpine menggunakan docker run:



$ docker run -i -t --name alpine alpine ash


Penampung ini akan digunakan pada keluaran di bawah ini. Ketika perintah docker run dipanggil, ia akan mem-parsing parameter yang diteruskan padanya di baris perintah dan membuat objek JSON untuk mewakili objek yang perlu dibuat oleh pekerja galangan itu. Objek ini kemudian dikirim ke daemon buruh pelabuhan melalui soket domain UNIX /var/run/docker.sock. Untuk memantau panggilan API, kita dapat menggunakan utilitas strace :



$ strace -s 8192 -e trace=read,write -f docker run -d alpine


[pid 13446] write(3, "GET /_ping HTTP/1.1\r\nHost: docker\r\nUser-Agent: Docker-Client/1.13.1 (linux)\r\n\r\n", 79) = 79
[pid 13442] read(3, "HTTP/1.1 200 OK\r\nApi-Version: 1.26\r\nDocker-Experimental: false\r\nServer: Docker/1.13.1 (linux)\r\nDate: Mon, 19 Feb 2018 16:12:32 GMT\r\nContent-Length: 2\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nOK", 4096) = 196
[pid 13442] write(3, "POST /v1.26/containers/create HTTP/1.1\r\nHost: docker\r\nUser-Agent: Docker-Client/1.13.1 (linux)\r\nContent-Length: 1404\r\nContent-Type: application/json\r\n\r\n{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[],\"Cmd\":null,\"Image\":\"alpine\",\"Volumes\":{},\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{},\"HostConfig\":{\"Binds\":null,\"ContainerIDFile\":\"\",\"LogConfig\":{\"Type\":\"\",\"Config\":{}},\"NetworkMode\":\"default\",\"PortBindings\":{},\"RestartPolicy\":{\"Name\":\"no\",\"MaximumRetryCount\":0},\"AutoRemove\":false,\"VolumeDriver\":\"\",\"VolumesFrom\":null,\"CapAdd\":null,\"CapDrop\":null,\"Dns\":[],\"DnsOptions\":[],\"DnsSearch\":[],\"ExtraHosts\":null,\"GroupAdd\":null,\"IpcMode\":\"\",\"Cgroup\":\"\",\"Links\":null,\"OomScoreAdj\":0,\"PidMode\":\"\",\"Privileged\":false,\"PublishAllPorts\":false,\"ReadonlyRootfs\":false,\"SecurityOpt\":null,\"UTSMode\":\"\",\"UsernsMode\":\"\",\"ShmSize\":0,\"ConsoleSize\":[0,0],\"Isolation\":\"\",\"CpuShares\":0,\"Memory\":0,\"NanoCpus\":0,\"CgroupParent\":\"\",\"BlkioWeight\":0,\"BlkioWeightDevice\":null,\"BlkioDeviceReadBps\":null,\"BlkioDeviceWriteBps\":null,\"BlkioDeviceReadIOps\":null,\"BlkioDeviceWriteIOps\":null,\"CpuPeriod\":0,\"CpuQuota\":0,\"CpuRealtimePeriod\":0,\"CpuRealtimeRuntime\":0,\"CpusetCpus\":\"\",\"CpusetMems\":\"\",\"Devices\":[],\"DiskQuota\":0,\"KernelMemory\":0,\"MemoryReservation\":0,\"MemorySwap\":0,\"MemorySwappiness\":-1,\"OomKillDisable\":false,\"PidsLimit\":0,\"Ulimits\":null,\"CpuCount\":0,\"CpuPercent\":0,\"IOMaximumIOps\":0,\"IOMaximumBandwidth\":0},\"NetworkingConfig\":{\"EndpointsConfig\":{}}}\n", 1556) = 1556
[pid 13442] read(3, "HTTP/1.1 201 Created\r\nApi-Version: 1.26\r\nContent-Type: application/json\r\nDocker-Experimental: false\r\nServer: Docker/1.13.1 (linux)\r\nDate: Mon, 19 Feb 2018 16:12:32 GMT\r\nContent-Length: 90\r\n\r\n{\"Id\":\"b70b57c5ae3e25585edba898ac860e388582391907be4070f91eb49f4db5c433\",\"Warnings\":null}\n", 4096) = 281


Di sinilah kesenangan sebenarnya dimulai. Segera setelah daemon buruh pelabuhan menerima permintaan tersebut, ia akan mengurai keluaran dan berkomunikasi dengan penampung melalui API gRPC untuk mengonfigurasi waktu proses (atau waktu proses) penampung menggunakan parameter yang diteruskan pada baris perintah. Untuk mengamati interaksi ini, kita dapat menggunakan utilitas ctr:



$ ctr --address "unix:///run/containerd.sock" events


TIME                           TYPE                           ID                             PID                            STATUS
time="2018-02-19T12:10:07.658081859-05:00" level=debug msg="Calling POST /v1.26/containers/create" 
time="2018-02-19T12:10:07.676706130-05:00" level=debug msg="container mounted via layerStore: /var/lib/docker/overlay2/2beda8ac904f4a2531d72e1e3910babf145c6e68dfd02008c58786adb254f9dc/merged" 
time="2018-02-19T12:10:07.682430843-05:00" level=debug msg="Calling POST /v1.26/containers/d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f/attach?stderr=1&stdin=1&stdout=1&stream=1" 
time="2018-02-19T12:10:07.683638676-05:00" level=debug msg="Calling GET /v1.26/events?filters=%7B%22container%22%3A%7B%22d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f%22%3Atrue%7D%2C%22type%22%3A%7B%22container%22%3Atrue%7D%7D" 
time="2018-02-19T12:10:07.684447919-05:00" level=debug msg="Calling POST /v1.26/containers/d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f/start" 
time="2018-02-19T12:10:07.687230717-05:00" level=debug msg="container mounted via layerStore: /var/lib/docker/overlay2/2beda8ac904f4a2531d72e1e3910babf145c6e68dfd02008c58786adb254f9dc/merged" 
time="2018-02-19T12:10:07.885362059-05:00" level=debug msg="sandbox set key processing took 11.824662ms for container d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f" 
time="2018-02-19T12:10:07.927897701-05:00" level=debug msg="libcontainerd: received containerd event: &types.Event{Type:\"start-container\", Id:\"d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f\", Status:0x0, Pid:\"\", Timestamp:(*timestamp.Timestamp)(0xc420bacdd0)}" 
2018-02-19T17:10:07.927795344Z start-container                d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f                                0
time="2018-02-19T12:10:07.930283397-05:00" level=debug msg="libcontainerd: event unhandled: type:\"start-container\" id:\"d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f\" timestamp:<seconds:1519060207 nanos:927795344 > " 
time="2018-02-19T12:10:07.930874606-05:00" level=debug msg="Calling POST /v1.26/containers/d1a6d87886e2d515bfff37d826eeb671502fa7c6f47e422ec3b3549ecacbc15f/resize?h=35&w=115" 


Mengonfigurasi runtime container adalah tugas yang cukup signifikan. Namespaces harus dikonfigurasi, gambar harus dipasang, kontrol keamanan harus diaktifkan (profil perlindungan aplikasi, profil seccomp, kapabilitas), dll., Dll., Dll. Anda bisa mendapatkan ide yang cukup bagus semua yang diperlukan untuk menyiapkan runtime dengan melihat output docker inspect containeriddan file spesifikasi runtime config.json(lebih lanjut tentang itu sebentar lagi ).



Sebenarnya, containerd tidak membuat runtime container. Ini mengatur lingkungan dan kemudian memanggil containerd-shimuntuk menjalankan runtime kontainer melalui runtime OCI yang dikonfigurasi (dikontrol oleh parameter "โ€“runtime" penampung). Sebagian besar sistem modern menjalankan runtime kontainer berdasarkan runc . Kita dapat mengamati ini menggunakan utilitas pstree :



$ pstree -l -p -s -T
systemd,1 --switched-root --system --deserialize 24
  โ”œโ”€docker-containe,19606 --listen unix:///run/containerd.sock --shim /usr/libexec/docker/docker-containerd-shim-current --start-timeout 2m --debug
  โ”‚   โ”œโ”€docker-containe,19834 93a619715426f613646359863e77cc06fa85502273df931517ec3f4aaae50d5a /var/run/docker/libcontainerd/93a619715426f613646359863e77cc06fa85502273df931517ec3f4aaae50d5a /usr/libexec/docker/docker-runc-current


Karena pstree menghapus nama proses, kita dapat memeriksa PID dengan ps :



$ ps auxwww | grep [1]9606


root     19606  0.0  0.2 685636 10632 ?        Ssl  13:01   0:00 /usr/libexec/docker/docker-containerd-current --listen unix:///run/containerd.sock --shim /usr/libexec/docker/docker-containerd-shim-current --start-timeout 2m --debug


$ ps auxwww | grep [1]9834


root     19834  0.0  0.0 527748  3020 ?        Sl   13:01   0:00 /usr/libexec/docker/docker-containerd-shim-current 93a619715426f613646359863e77cc06fa85502273df931517ec3f4aaae50d5a /var/run/docker/libcontainerd/93a619715426f613646359863e77cc06fa85502273df931517ec3f4aaae50d5a /usr/libexec/docker/docker-runc-current


Ketika saya pertama kali mulai menjelajahi interaksi antara dockerd, containerd, dan shim , saya tidak sepenuhnya mengerti untuk apa shim itu . Untungnya Google menghasilkan tulisan yang sangat bagus oleh Michael Crosby . Shim memiliki beberapa tujuan:



  1. Mengizinkan memulai kontainer tanpa daemon.
  2. STDIO FD containerd docker.
  3. containerd .


Poin kunci pertama dan kedua sangat penting. Fitur-fitur ini memungkinkan Anda untuk memisahkan kontainer dari daemon buruh pelabuhan , memungkinkan dockerd untuk diperbarui atau dimulai ulang tanpa mempengaruhi kontainer yang sedang berjalan. Sangat efektif! Saya menyebutkan bahwa shim bertanggung jawab untuk menjalankan runc untuk benar-benar memulai wadah. Runc memerlukan dua hal untuk melakukan tugasnya : file spesifikasi dan jalur ke gambar sistem file root (kombinasi di antaranya disebut bundel ). Untuk melihat bagaimana ini bekerja, kita dapat membuat rootf dengan mengekspor gambar buruh pelabuhan alpine :



$ mkdir -p alpine/rootfs


$ cd alpine


$ docker export d1a6d87886e2 | tar -C rootfs -xvf -


time="2018-02-19T12:54:13.082321231-05:00" level=debug msg="Calling GET /v1.26/containers/d1a6d87886e2/export" 
.dockerenv
bin/
bin/ash
bin/base64
bin/bbconfig
.....


Opsi ekspor menerima penampung, yang dapat Anda temukan di keluaran docker ps -a. Untuk membuat file spesifikasi, Anda dapat menggunakan perintah runc spec :



$ runc spec


Ini akan membuat file spesifikasi bernama config.jsondi direktori Anda saat ini. File ini dapat disesuaikan dengan kebutuhan dan kebutuhan Anda. Setelah Anda puas dengan file tersebut, Anda dapat menjalankan runc dengan direktori rootfs sebagai satu-satunya argumen (konfigurasi container akan dibaca dari file config.json):



$ runc run rootfs


Contoh sederhana ini akan membuat pembungkus abu alpine:



$ runc run rootfs


/ # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.7.0
PRETTY_NAME="Alpine Linux v3.7"
HOME_URL="http://alpinelinux.org"
BUG_REPORT_URL="http://bugs.alpinelinux.org"


Kemampuan untuk membuat kontainer dan bermain dengan spesifikasi runtime runc sangat kuat. Anda dapat mengevaluasi berbagai profil aplikasi, menguji kemampuan Linux, dan bereksperimen dengan setiap aspek runtime container tanpa harus menginstal buruh pelabuhan. Saya hanya menggores permukaannya sedikit dan akan sangat merekomendasikan membaca dokumentasi runc dan containerd . Alat yang sangat keren!






Pelajari lebih lanjut tentang kursus tersebut.







All Articles