GNU/Linux >> Belajar Linux >  >> Linux

Apa yang terjadi di balik layar wadah Podman tanpa akar?

Itu selalu berharga untuk mengetahui apa yang terjadi di balik layar. Mari kita lihat apa yang terjadi di bawah kap wadah Podman tanpa akar. Kami akan menjelaskan setiap komponen, lalu menguraikan semua langkah yang terlibat.

Contoh

Dalam contoh kita, kita akan mencoba menjalankan container yang sudah menjalankan Buildah untuk membangun image container. Pertama, kita membuat Dockerfile sederhana yang disebut Containerfile yang menarik gambar ubi8 dan menjalankan perintah yang memberi tahu Anda bahwa Anda sedang menjalankan dalam sebuah wadah:

$ mkdir containers
$ cat > ~/Containerfile << _EOF
FROM ubi8
RUN echo “in buildah container”
_EOF

Selanjutnya, jalankan container dengan perintah Podman berikut:

$ podman run --device /dev/fuse -v ~/Containerfile:/Containerfile:Z \
     -v ~/containers:/var/lib/containers:Z buildah buildah bud /

Perintah ini menambahkan perangkat tambahan /dev/fuse , yang diperlukan untuk menjalankan Buildah di dalam container. Kami memasang volume di Containerfile agar Buildah dapat menemukannya, dan gunakan flag SELinux :Z untuk memberitahu Podman untuk memberi label ulang. Untuk menangani penyimpanan container Buildah di luar container, kami juga memasang containers lokal direktori yang saya buat di atas. Dan terakhir, kita jalankan perintah Buildah.

Berikut adalah output aktual yang saya lihat saat menjalankan perintah ini:

$ podman run -ti --device /dev/fuse -v ~/Containerfile:/Containerfile:Z -v ~/containers:/var/lib/containers:Z buildah/stable buildah bud /
Trying to pull docker.io/buildah/stable...
   denied: requested access to the resource is denied
Trying to pull registry.fedoraproject.org/buildah/stable...
   manifest unknown: manifest unknown
Trying to pull quay.io/buildah/stable...
Getting image source signatures
Copying blob 907e338ec93d done
Copying blob a3ed95caeb02 done
Copying blob a3ed95caeCob02 done
Copying blob a3ed95caeb02 skipped: already exists
Copying blob d318c91bf2a8 done
Copying blob e721a8015139 done
Copying blob a3ed95caeb02 done
Copying blob 8dd367492bc7 done
Writing manifest to image destination
Storing signatures
STEP 1: FROM ubi8
Getting image source signatures
Copying blob c65691897a4d done
Copying blob 641d7cc5cbc4 done
Copying config 11f9dba4d1 done
Writing manifest to image destination
Storing signatures
STEP 2: RUN echo "in buildah container"
in buildah container
STEP 3: COMMIT
Getting image source signatures
Copying blob 6866631b657e skipped: already exists
Copying blob 48905dae4010 skipped: already exists
Copying blob 5f70bf18a086 skipped: already exists
Copying config 9c54016647 done
Writing manifest to image destination
Storing signatures
9c5401664748e032b43b8674dba90e9b853d6b47b679d056cb2a1e3118f9dab7

Sekarang, mari kita gali lebih dalam apa yang sebenarnya terjadi di dalam perintah Podman.

Menyiapkan pengguna dan memasang ruang nama

Saat mengatur user dan mount namespace, Podman pertama-tama memeriksa apakah sudah ada user namespace yang dikonfigurasi. Ini dilakukan dengan melihat apakah ada proses jeda yang berjalan untuk pengguna. Peran proses jeda adalah untuk menjaga ruang nama pengguna tetap hidup, karena semua wadah tanpa akar harus dijalankan di ruang nama pengguna yang sama. Jika tidak, beberapa hal (seperti berbagi namespace jaringan dari wadah lain) tidak mungkin dilakukan.

Ruang nama pengguna diperlukan untuk memungkinkan rootless memasang jenis sistem file tertentu dan mengakses lebih dari satu UID dan GID.

Jika proses jeda ada, maka namespace penggunanya bergabung. Tindakan ini dilakukan sangat awal dalam eksekusinya sebelum waktu proses Go dimulai karena program multithread tidak dapat mengubah namespace penggunanya. Namun, jika proses jeda tidak ada, maka Podman membaca /etc/subuid dan /etc/subgid file, mencari nama pengguna atau UID pengguna yang menjalankan perintah Podman. Setelah Podman menemukan entri tersebut, Podman akan menggunakan konten serta UID/GID pengguna saat ini untuk menghasilkan ruang nama pengguna untuk mereka.

Misalnya, jika pengguna menjalankan sebagai UID 1000 dan memiliki entri USER:100000:65536 , Podman mengeksekusi aplikasi setuid dan setgid, /usr/bin/newuidmap dan /usr/bin/newgidmap , untuk mengonfigurasi ruang nama pengguna. Namespace pengguna kemudian mendapatkan pemetaan berikut:

0     3267      1
1     100000    65536

Perhatikan bahwa Anda dapat melihat ruang nama pengguna dengan menjalankan:

$ podman unshare cat /proc/self/uid_map

Selanjutnya, Podman membuat proses jeda untuk menjaga namespace tetap hidup, sehingga semua container dapat berjalan dari konteks yang sama dan melihat mount yang sama. Proses Podman selanjutnya akan langsung bergabung dengan namespace tanpa perlu membuatnya terlebih dahulu. Namun, jika ruang pengguna tidak dapat dibuat, maka Podman akan memeriksa apakah perintah tersebut masih dapat berjalan tanpa ruang nama pengguna. Beberapa perintah seperti podman version tidak perlu satu. Dalam kasus lain, perintah tanpa ruang nama pengguna akan gagal.

Kemudian, Podman memproses opsi baris perintah, memverifikasi bahwa opsi tersebut benar. Anda dapat menggunakan podman-help dan podman run --help untuk membuat daftar opsi yang tersedia, dan gunakan halaman manual untuk deskripsi lebih lanjut.

Terakhir, Podman membuat mount namespace untuk memasang container storage.

Menarik gambar

Saat menarik image, Podman memeriksa apakah image container buildah/stable ada di penyimpanan kontainer lokal. Jika ya, maka Podman menyiapkan jaringan (lihat bagian berikutnya). Namun, jika image container tidak ada, Podman membuat daftar kandidat image untuk ditarik menggunakan registry pencarian yang ditentukan di /etc/containers/registries.conf .

containers/image library akan digunakan untuk menarik gambar kandidat ini satu per satu, dalam urutan yang ditentukan oleh registries.conf . Gambar pertama yang berhasil ditarik akan digunakan.

  1. containers/image skrip menggunakan DNS untuk menemukan alamat IP untuk registri.
  2. Skrip TCP ini terhubung ke alamat IP melalui httpd pelabuhan (80).
  3. containers/image mengirimkan permintaan HTTP untuk manifes /buildah/stable:latest gambar kontainer.
  4. Jika skrip tidak dapat menemukan gambar, skrip akan menggunakan registri berikutnya sebagai pengganti dan kembali ke langkah 1. Namun, jika gambar adalah ditemukan, ia mulai menarik setiap lapisan gambar menggunakan containers/image perpustakaan.

Dalam contoh ini, buildah/stable ditemukan di quay.io/buildah/stable . containers/image skrip menemukan bahwa ada tujuh lapisan di quay.io/buildah/stable dan mulai menyalin semuanya secara bersamaan dari registri penampung ke host. Menyalinnya secara bersamaan itu efisien.

Karena setiap layer disalin ke host, Podman memanggil containers/storage Perpustakaan. containers/storage script memasang kembali lapisan secara berurutan, dan untuk setiap lapisan. Ini membuat titik pemasangan overlay di ~/.local/share/containers/storage di atas lapisan sebelumnya. Jika tidak ada lapisan sebelumnya, itu membuat lapisan awal.

Catatan: Di Podman tanpa root, kami sebenarnya menggunakan fuse-overlayfs dieksekusi untuk membuat layer. Rootfull menggunakan overlayfs kernel pengemudi. Saat ini, kernel tidak mengizinkan pengguna rootless untuk memasang sistem berkas overlay, tetapi mereka dapat memasang sistem berkas FUSE.

Selanjutnya, containers/storage untar isi lapisan ke dalam lapisan penyimpanan baru. Karena lapisan tidak dilapisi, containers/storage chown UID/GID file di tarball ke direktori home. Perhatikan bahwa proses ini dapat gagal jika UID atau GID yang ditentukan dalam file tar tidak dipetakan ke dalam ruang nama pengguna. Lihat Mengapa Podman rootless tidak dapat menarik gambar saya?

Membuat wadah

Sekarang, saatnya Podman membuat container baru berdasarkan gambar. Untuk melakukannya, Podman menambahkan wadah ke database, dan kemudian meminta containers/storage library untuk membuat dan memasang wadah baru di c/storage . Lapisan penampung baru berfungsi sebagai lapisan baca/tulis terakhir dan dipasang di atas gambar.

Menyiapkan jaringan

Selanjutnya, kita perlu mengatur jaringan. Untuk mencapai ini, Podman menemukan dan mengeksekusi /usr/bin/slirp4netns untuk mengatur jaringan kontainer. Di Podman rootless, kami tidak dapat membuat jaringan penuh dan terpisah untuk container, karena fitur ini tidak diperbolehkan untuk pengguna non-root. Di Podman rootless, kami menggunakan slirp4netns untuk mengonfigurasi jaringan host dan mensimulasikan VPN untuk container.

Catatan: Dalam wadah rootful, Podman menggunakan plugin CNI untuk mengonfigurasi jembatan.

Jika pengguna menentukan pemetaan port seperti -p 8080:80 , slirpnetns akan mendengarkan pada jaringan host pada port 8080 dan mengizinkan proses container untuk mengikat ke port 80. slirp4netns perintah membuat perangkat tap yang disuntikkan di dalam namespace jaringan baru, tempat wadah tinggal. Setiap paket dibaca kembali dari slirp4netns dan mengemulasi tumpukan TCP/IP di ruang pengguna. Setiap koneksi di luar ruang nama jaringan penampung diubah dalam operasi soket yang dapat dilakukan oleh pengguna yang tidak memiliki hak istimewa di ruang nama jaringan host.

Menangani volume

Untuk menangani volume, Podman membaca semua penyimpanan kontainer. Ini mengumpulkan label SELinux yang digunakan dan membuat label baru yang tidak digunakan untuk menjalankan wadah menggunakan opencontainers/selinux perpustakaan.

Karena pengguna menentukan dua volume untuk dipasang ke dalam wadah dan meminta Podman untuk memberi label ulang pada konten, Podman menggunakan opencontainers/selinux untuk menerapkan label SELinux secara rekursif ke file/direktori sumber volume. Podman kemudian menggunakan opencontainers/runtime-tools library untuk merakit spesifikasi Runtime Open Containers Initiative (OCI):

  1. Podman memberi tahu runtime-tools untuk menambahkan default hard-code untuk hal-hal seperti kemampuan, lingkungan, dan ruang nama ke spesifikasi.
  2. Podman menggunakan spesifikasi Gambar OCI yang ditarik dari buildah/stable image untuk mengatur konten dalam spesifikasi, seperti direktori kerja, titik masuk, dan variabel lingkungan tambahan.
  3. Podman mengambil input pengguna dan menggunakan runtime-tools library untuk menambahkan bidang dalam spesifikasi untuk setiap volume, dan menetapkan perintah wadah ke buildah bud / .

Dalam contoh kita, pengguna memberi tahu Podman bahwa mereka ingin menggunakan perangkat /dev/fuse bagian dalam wadah. Pada container rootful, Podman akan memberitahu runtime OCI untuk membuat /dev/fuse perangkat di dalam wadah, tetapi dengan pengguna Podman rootless tidak diizinkan untuk membuat perangkat, jadi Podman malah memberi tahu spesifikasi OCI untuk mengikat mount /dev/fuse dari host ke dalam wadah.

Memulai conmon monitor kontainer

Setelah volume ditangani, Podman menemukan dan mengeksekusi default conmon untuk wadah /usr/bin/conmon . Informasi ini dibaca dari /usr/share/containers/libpod.conf . Podman kemudian memberitahu conmon dapat dieksekusi untuk menggunakan runtime OCI yang juga terdaftar di libpod.conf; biasanya, /usr/bin/runc atau /usr/bin/crun . Podman juga memberi tahu conmon untuk menjalankan podman container cleanup $CTRID untuk kontainer saat kontainer keluar.

Conmon melakukan hal berikut saat memantau wadah:

  1. Conmon mengeksekusi runtime OCI, memberikannya jalur ke file spesifikasi OCI serta menunjuk ke titik pemasangan lapisan penampung di containers/storage . Titik pemasangan ini disebut rootfs.
  2. Conmon memantau penampung sampai keluar dan melaporkan kembali kode keluarnya.
  3. Pegangan umum saat pengguna menempel ke wadah, menyediakan soket untuk mengalirkan STDOUT dan STDERR wadah.
  4. STDOUT dan STDERR juga dicatat ke file untuk podman logs .

Setelah menjalankan conmon , tetapi sebelum waktu proses OCI dimulai, Podman menempel pada soket "attach" karena container tidak dijalankan dengan -d . Kita perlu melakukan ini sebelum menjalankan penampung, jika tidak, kita berisiko kehilangan apa pun yang ditulis penampung ke aliran standarnya sebelum kita memasangnya. Melakukannya sebelum penampung dimulai memberi kita segalanya.

Meluncurkan waktu proses OCI

Runtime OCI membaca file spesifikasi OCI dan mengonfigurasi kernel untuk menjalankan container. Ini:

  1. Menyiapkan ruang nama tambahan untuk penampung.
  2. Mengonfigurasi cgroups jika container berjalan di cgroups V2 (cgroups V1 tidak mendukung rootless cgroups).
  3. Menyiapkan label SELinux untuk menjalankan container.
  4. Membaca seccomp.json file (default ke /usr/share/containers/seccomp.json ) dan menyiapkan aturan seccomp.
  5. Menyetel variabel lingkungan.
  6. Bind memasang dua volume yang ditentukan ke jalur di rootfs. Jika jalur tujuan tidak ada di rootfs, maka waktu proses OCI membuat direktori tujuan.
  7. Mengalihkan root ke rootfs (membuat rootfs / di dalam wadah).
  8. Menghentikan proses container.
  9. Mengeksekusi program hook OCI apa pun, meneruskan rootfs serta PID 1 container kepada mereka.
  10. Mengeksekusi perintah yang ditentukan oleh pengguna buildah bud / dengan PID penampung 1.
  11. Keluar dari runtime OCI, biarkan conmon untuk memantau wadah.

Dan akhirnya, conmon melaporkan keberhasilan kembali ke Podman.

Menjalankan buildah proses utama container

Sekarang untuk kelompok langkah terakhir. Itu dimulai ketika wadah meluncurkan proses Buildah awal. (Karena kami menggunakan Buildah dalam contoh kami.) Buildah membagikan containers/image yang mendasarinya dan containers/storage library dengan Podman, jadi Podman sebenarnya mengikuti sebagian besar langkah yang didefinisikan di atas yang digunakan Podman untuk menarik gambarnya dan membuat container-nya.

Podman menempel pada conmon socket dan terus membaca/menulis STDOUT ke conmon . Perhatikan bahwa jika pengguna telah menentukan -d Po Podman flag, Podman akan keluar, tetapi conmon akan terus memantau kontainer.

Ketika proses container keluar, kernel mengirimkan SIGCHLD ke conmon proses. Sebaliknya, conmon :

  1. Merekam kode keluar container.
  2. Menutup file log penampung.
  3. Menutup STDOUT/STDERR perintah Podman.
  4. Mengeksekusi podman container cleanup $CTRID perintah.

Pembersihan wadah Podman kemudian menghapus slirp4netns jaringan dan memberitahu containers/storage untuk melepas semua titik pemasangan kontainer. Jika pengguna menentukan --rm maka wadah sepenuhnya dihapus, sebagai gantinya. Lapisan penampung dihapus dari containers/storage , dan definisi container dihapus dari DB.

Karena perintah Podman asli berjalan di latar depan, Podman menunggu conmon untuk keluar, dapatkan kode keluar dari penampung, lalu keluar dengan kode keluar penampung.

Menutup

Semoga penjelasan ini membantu Anda memahami semua keajaiban yang terjadi di balik selimut saat menjalankan perintah Podman tanpa root.

Baru mengenal container? Unduh Containers Primer dan pelajari dasar-dasar container Linux.


Linux
  1. Apa itu pengguna Linux?

  2. Di balik layar dengan wadah Linux

  3. Mengapa Podman rootless tidak dapat menarik gambar saya?

  1. Cara men-debug masalah dengan volume yang dipasang pada wadah tanpa akar

  2. Mengontrol akses ke Podman tanpa root untuk pengguna

  3. Apa perbedaan antara wadah Linux dan gambar?

  1. Apa Yang Terjadi Saat Saya Menjalankan Perintah Cat /proc/cpuinfo?

  2. Apa yang Terjadi Saat Saya Mengeksekusi File Di Shell?

  3. Apa PID di host, dari proses yang berjalan di dalam container Docker?