Mengapa conmon di cgroup berbeda ketika podman dimulai dengan systemd?

11

Podman yang diberikan diinstal pada sistem linux dan unit systemd bernama baz.service:

# /etc/systemd/system/baz.service
[Service]
ExecStart=/usr/bin/podman run --rm --tty --name baz alpine sh -c 'while true; do date; sleep 1; done'
ExecStop=/usr/bin/podman stop baz

Dan layanan baz.ser dimulai:

# systemctl daemon-reload
# systemctl start baz.service

Kemudian ketika saya memeriksa status unit saya tidak melihat shatau sleepproses di /system.slice/baz.service cgroup

# systemctl status baz
● baz.service
   Loaded: loaded (/etc/systemd/system/baz.service; static; vendor preset: enabl
   Active: active (running) since Sat 2019-08-10 05:50:18 UTC; 14s ago
 Main PID: 16910 (podman)
    Tasks: 9
   Memory: 7.3M
      CPU: 68ms
   CGroup: /system.slice/baz.service
           └─16910 /usr/bin/podman run --rm --tty --name baz alpine sh -c while
# ...

Saya mengharapkan untuk melihat shdan sleepanak - anak dalam status baz.service saya karena saya telah mendengar orang-orang dari redhat mengatakan podman menggunakan model fork-exec tradisional.

Jika podman melakukan fork dan exec, maka bukankah proses saya shdan sleepmenjadi anak-anak podman dan berada dalam kelompok yang sama dengan proses podman asli?

Saya berharap dapat menggunakan systemd dan podman untuk dapat mengelola wadah saya tanpa anak-anak pergi ke orang tua yang berbeda dan melarikan diri dari unit ssystemd baz.service saya.

Melihat output dari pssaya dapat melihat itu shdan sleepsebenarnya adalah anak-anak dari proses yang berbeda yang disebut conmon. Saya tidak yakin dari mana conmon berasal, atau bagaimana itu dimulai tetapi systemd tidak menangkapnya.

# ps -Heo user,pid,ppid,comm
# ...
root     17254     1   podman
root     17331     1   conmon
root     17345 17331     sh
root     17380 17345       sleep

Dari outputnya jelas bahwa unit baz.service saya tidak mengelola conmon -> sh -> sleep chain.

  • Bagaimana podman berbeda dari model server klien buruh pelabuhan?
  • Apa perbedaan antara podman podman dengan isi docker?

Mungkin mereka berdua runtimes kontainer dan dockerddaemon adalah apa yang orang ingin singkirkan.

Jadi mungkin buruh pelabuhan itu seperti:

  • daemon dockerd
  • buruh pelabuhan cli
  • berisi runtime kontainer

Dan podman seperti:

  • podman cli
  • runtime wadah conmon

Jadi mungkin podman menggunakan model fork exec tradisional tapi itu bukan podman cli yang forking dan exec, itu proses pemanggilan.

Saya bingung.

mbigras
sumber
Ada diskusi tentang pertanyaan ini di milis podman
mbigras

Jawaban:

8

Seluruh ide di belakang podmanadalah untuk menjauh dari arsitektur terpusat dengan pengawas super kuat (misalnya dockerd), di mana daemon terpusat adalah satu titik kegagalan. Bahkan ada tagar tentang ini - " #nobigfatdaemons ".

Bagaimana cara menghindari manajemen kontainer terpusat? Anda menghapus daemon utama tunggal (lagi, dockerd) dan mulai wadah secara mandiri (pada akhir hari, wadah hanya proses, sehingga Anda tidak perlu daemon untuk memijahkannya).

Namun, Anda masih membutuhkan cara untuk itu

  • kumpulkan log kontainer - seseorang harus memegang stdoutdan stderrdari wadah;
  • kumpulkan kode keluar penampung - seseorang harus wait(2)menggunakan PID 1 penampung;

Untuk tujuan ini, setiap wadah podman masih diawasi oleh daemon kecil, yang disebut conmon(dari "monitor kontainer"). Perbedaannya dengan daemon Docker adalah bahwa daemon ini sekecil mungkin (periksa ukuran kode sumber ), dan daemon ini dihasilkan per-kontainer. Jika conmonuntuk satu kontainer macet, sisa sistem tetap tidak terpengaruh.

Selanjutnya, bagaimana wadah itu akan melahirkan?

Mempertimbangkan bahwa pengguna mungkin ingin menjalankan wadah di latar belakang, seperti dengan Docker, podman runproses bercabang dua kali dan hanya kemudian dijalankan conmon:

$ strace -fe trace=fork,vfork,clone,execve -qq podman run alpine
execve("/usr/bin/podman", ["podman", "run", "alpine"], 0x7ffeceb01518 /* 30 vars */) = 0
...
[pid  8480] clone(child_stack=0x7fac6bffeef0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[8484], tls=0x7fac6bfff700, child_tidptr=0x7fac6bfff9d0) = 8484
...
[pid  8484] clone(child_stack=NULL, flags=CLONE_VM|CLONE_VFORK|SIGCHLD <unfinished ...>
[pid  8491] execve("/usr/bin/conmon", ... <unfinished ...>
[pid  8484] <... clone resumed>)        = 8491

Proses tengah antara podman rundan conmon(yaitu induk langsung dari conmon- dalam contoh di atas adalah PID 8484) akan keluar dan conmonakan diperbaiki oleh init, sehingga menjadi daemon yang dikelola sendiri. Setelah ini, conmonjuga memotong runtime (eg runc) dan, akhirnya, runtime mengeksekusi entrypoint penampung (eg /bin/sh).

Ketika wadah berjalan, podman runtidak lagi diperlukan dan dapat keluar, tetapi dalam kasus Anda tetap online, karena Anda tidak meminta untuk melepaskan dari wadah.

Selanjutnya, podmangunakan cgroup untuk membatasi wadah. Ini berarti bahwa ia menciptakan cgroup baru untuk wadah baru dan memindahkan proses di sana . Dengan aturan cgroup, proses tersebut mungkin hanya anggota dari satu cgroup pada suatu waktu, dan menambahkan proses ke beberapa cgroup menghapusnya dari cgroup lain (di mana sebelumnya) dalam hierarki yang sama. Jadi, ketika wadah dimulai, tata letak akhir cgroup terlihat seperti berikut: podman runtetap di cgroup baz.service, dibuat oleh systemd, conmonproses ditempatkan di cgroup sendiri, dan proses kemasukan ditempatkan di cgroup mereka sendiri:

$ ps axf
<...>
 1660 ?        Ssl    0:01 /usr/bin/podman run --rm --tty --name baz alpine sh -c while true; do date; sleep 1; done
 1741 ?        Ssl    0:00 /usr/bin/conmon -s -c 2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6 <...>
 1753 pts/0    Ss+    0:02  \_ sh -c while true; do date; sleep 1; done
13043 pts/0    S+     0:00      \_ sleep 1
<...>

$ cd /sys/fs/cgroup/memory/machine.slice
$ ls -d1 libpod*
libpod-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope
libpod-conmon-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope

$ cat libpod-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope/cgroup.procs 
1753
13075

$ cat libpod-conmon-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope/cgroup.procs 
1741

Catatan: PID 13075 di atas sebenarnya adalah sebuah sleep 1proses, yang muncul setelah kematian PID 13043.

Semoga ini membantu.

Danila Kiver
sumber
1
"membuat cgroup baru untuk wadah baru dan memindahkan proses di sana" Saya tidak mengerti mengapa podman melakukan pekerjaan itu alih-alih systemd. Bisakah Anda menambahkan penjelasan tentang mengapa kami menggunakan conmon untuk menahan stdout dan stderr alih-alih systemd? Dari belajar tentang systemd saya pikir tujuan dari systemd adalah untuk mengelola proses dan melakukan tugas-tugas seperti menangkap stdout / stderr, mencari tahu status keluar, menangani restart.
mbigras
2
Podman mengelola cgroup karena ia memiliki wadah dan harus menjamin bahwa wadah tersebut akan berfungsi dengan mengabaikan sistem init apa yang Anda miliki. Systemd mengelola cgroup untuk layanan karena ia memiliki layanan (dan layanan tidak seharusnya mengelola cgroup secara default, meskipun systemd mendukung beberapa jenis delegasi - lihat systemd.io/CGROUP_DELEGATION ). Jika Anda ingin podman menggunakan kembali cgroup yang dibuat oleh systemd untuk layanan, harus ada dukungan dari sisi podman, dan saat ini saya tidak melihatnya (walaupun saya mungkin salah).
Danila Kiver
1
Adapun stdout/ stderrstream - lagi, podmanmemiliki wadah dan menangkap aliran dari proses kemas. systemdmemiliki layanan dan menangkap aliran proses utama layanan (dalam kasus Anda, systemdsebenarnya menangkap stdout/ stderrdari podman runproses). Ini berfungsi persis seperti seharusnya, karena conmonmenangkap aliran wadah, podman runmenempel conmon, systemdmenangkap aliran podman run, jadi, akhirnya, semua log dari wadah ditangkap systemd, dan Anda melihatnya systemctl status baz.service.
Danila Kiver