كيف تعمل عملية إنشاء حاوية عامل إرساء (من Docker run إلى runc)

تم إعداد ترجمة المقال عشية بدء الدورة التدريبية "منصة البنية التحتية على أساس Kubernetes" .








على مدار الأشهر القليلة الماضية ، قضيت الكثير من وقتي الشخصي في تعلم كيفية عمل حاويات Linux. على وجه الخصوص ، ما الذي تفعله بالضبط docker run. في هذه المقالة ، سألخص ما اكتشفته وسأحاول إظهار كيف تشكل العناصر الفردية صورة كبيرة. سنبدأ رحلتنا بإنشاء حاوية جبال الألب باستخدام Docker Run:



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


سيتم استخدام هذه الحاوية في الإخراج أدناه. عندما يتم استدعاء أمر Docker run ، فإنه يوزع المعلمات التي تم تمريرها إليه في سطر الأوامر وينشئ كائن JSON لتمثيل الكائن الذي يحتاج عامل الإرساء إلى إنشائه. ثم يتم إرسال هذا الكائن إلى Docker daemon عبر مقبس مجال UNIX /var/run/docker.sock. لمراقبة استدعاءات API ، يمكننا استخدام الأداة المساعدة 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


هذا هو المكان الذي تبدأ المتعة الحقيقية. بمجرد أن يتلقى برنامج Docker daemon الطلب ، سيقوم بتحليل الإخراج والتواصل مع containerd عبر واجهة برمجة تطبيقات gRPC لتهيئة وقت التشغيل (أو وقت التشغيل) للحاوية باستخدام المعلمات التي تم تمريرها في سطر الأوامر. لمراقبة هذا التفاعل ، يمكننا استخدام الأداة المساعدة 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" 


يعد تكوين وقت تشغيل الحاوية مهمة مهمة إلى حد ما. يجب تكوين مساحات الأسماء ، ويجب تثبيت الصورة ، ويجب تمكين عناصر التحكم في الأمان (ملفات تعريف حماية التطبيق ، وملفات تعريف seccomp ، والقدرات) ، وما إلى ذلك ، وما إلى ذلك ، وما إلى ذلك ، يمكنك الحصول على فكرة جيدة جدًا كل ما هو مطلوب لإعداد وقت التشغيل من خلال النظر في الإخراج docker inspect containeridوملف مواصفات وقت التشغيل config.json(المزيد عن ذلك في لحظة ).



بالمعنى الدقيق للكلمة، containerd لا يخلق وقت الحاوية. يقوم بإعداد البيئة ثم يستدعي containerd-shimلتشغيل وقت تشغيل الحاوية من خلال وقت تشغيل OCI الذي تم تكوينه (يتم التحكم فيه بواسطة معلمة "–runtime"). تعمل معظم الأنظمة الحديثة على تشغيل الحاوية بناءً على runc . يمكننا ملاحظة ذلك باستخدام الأداة المساعدة 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


نظرًا لأن شرائط pstree خارج اسم العملية ، يمكننا التحقق من PID باستخدام 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


عندما بدأت في البحث عن التفاعلات بين dockerd و containerd و shim ، لم أفهم تمامًا الغرض من الرقائق . لحسن الحظ ، أدت Google إلى كتابة ممتازة بقلم مايكل كروسبي . يخدم شيم عدة أغراض:



  1. يسمح ببدء تشغيل الحاويات بدون شياطين.
  2. STDIO FD containerd docker.
  3. containerd .


النقاط الرئيسية الأولى والثانية مهمة للغاية. هذه الميزات تسمح لك لفصل الحاوية من عامل ميناء الخفي ، مما يسمح dockerd ل يتم تحديث أو إعادة دون أن يؤثر ذلك حاويات التشغيل. مؤثر جدا! لقد ذكرت أن shim مسؤول عن تشغيل runc لبدء الحاوية بالفعل. يحتاج Runc إلى شيئين للقيام بعمله : ملف المواصفات والمسار إلى صورة نظام ملفات الجذر (يُطلق على المجموعة اسم الحزمة ). لمعرفة كيفية عمل ذلك ، يمكننا إنشاء ملفات rootf عن طريق تصدير صورة عامل إرساء جبال الألب :



$ 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
.....


يقبل خيار التصدير حاوية يمكنك العثور عليها في الإخراج docker ps -a. لإنشاء ملف المواصفات ، يمكنك استخدام الأمر runc :



$ runc spec


سيؤدي هذا إلى إنشاء ملف المواصفات المسمى config.jsonفي دليلك الحالي. يمكن تخصيص هذا الملف وفقًا لاحتياجاتك ومتطلباتك. بمجرد أن تصبح راضيًا عن الملف ، يمكنك تشغيل runc مع دليل rootfs باعتباره الوسيطة الوحيدة (سيتم قراءة تكوين الحاوية من الملف config.json):



$ runc run rootfs


هذا المثال البسيط سينشئ غلاف رماد جبال الألب:



$ 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"


القدرة على إنشاء حاويات واللعب مع runc وقت التشغيل مواصفات هي قوية بشكل لا يصدق. يمكنك تقييم ملفات تعريف التطبيق المختلفة ، واختبار قدرات Linux ، وتجربة كل جانب من جوانب وقت تشغيل الحاوية دون الحاجة إلى تثبيت docker. لقد خدشت السطح قليلاً فقط وسأوصي بشدة بقراءة وثائق runc و containerd . أدوات رائعة جدا!






تعلم المزيد عن الدورة.







All Articles