-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsection_0.txt
More file actions
395 lines (265 loc) · 19.4 KB
/
Copy pathsection_0.txt
File metadata and controls
395 lines (265 loc) · 19.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
0. Konteyner fəlsəfəsi: Docker nədir və VM-dən fərqi nədir
----------------------------------------------------------
Birincisi, Docker öz-özünə sehrli bir “virtual maşın” deyil.
O, sadəcə Linux kernelində çoxdan olan iki imkanın — namespaces və cgroups — üstündə qurulmuş proqram təminatıdır.
Yəni Docker bu mexanizmləri sənə rahat idarə edilən formada təqdim edir.
İkincisi, bu primitivlərin üzərində Docker əlavə qat gətirir:
1. image formatı (lay-lar ilə qurulmuş), 2. fayl sisteminin overlay strukturu və 3. runtime orkestrasiyası.
Bu qatların hamısı birlikdə bir “təkrarlana bilən mühit” yaradır.
Bunu belə təsəvvür elə: əvvəllər bir tətbiqi başqa serverə daşıyanda fərqli OS versiyası, fərqli kitabxana və ya konfiqurasiya problemləri çıxırdı.
Ona görə də məşhur bir ifadə vardı — “məndə işləyir, səndə niyə işləmir?”.
Docker burada oyunu dəyişir, çünki tətbiqin özünü, istifadə etdiyi kitabxanaları və hətta userspace-i bir paket (image) halında saxlayır.
Bu image istənilən yerdə eyni formada açılır.
-detailed-
Docker yox idi, sən API-ni yazmısan. İndi onu başqa serverə köçürmək istəyirsən. Ne edirdin?
Əvvəlcə həmin serverdə uyğun .NET runtime quraşdırmalı idin.
Əgər serverdə .NET versiyası bir az köhnə idisə, proqram heç açılmırdı.
Bəzən lazımi kitabxanalar (məsələn, libssl və ya icu) həmin sistemdə yox idi, sən ayrıca onları tapıb quraşdırmalı idin.
Server Linux-un başqa distronu ola bilərdi (Ubuntu yerinə CentOS), onda paketlərin adları belə fərqli çıxırdı.
Hətta bəzi hallarda sadəcə kiçik konfiqurasiya fərqləri (fayl yolları, environment dəyişənləri, systemd xidmət faylları) üzündən proqram hostda işləmirdi.
Buna görə də o məşhur söz yaranmışdı: “məndə işləyir, səndə niyə işləmir?”.
Docker isə deyir ki: heç bir serverə əl ilə kitabxana, runtime və konfiqurasiya quraşdırma.
Sən tətbiqi və ona lazım olan hər şeyi bir image kimi paketlə.
Bu image-in içində sənin .NET runtime-ın, lazımi OS kitabxanaları və tətbiqin öz faylları olur.
İndi bu image-i harada açsan (istər Ubuntu, istər Debian, istər Fedora üzərində Docker olsun),
konteyner eyni görünüşlə qalxır və sənə eyni nəticəni verir.
Beləliklə, Docker problemin kökünü həll edir: artıq “o serverdə hansı OS versiyası var, hansı paketlər qurulub” sualları səni maraqlandırmır.
Çünki hər şey image-in içində gəlir.
Sən yalnız Docker-in özünü quraşdırırsan,
qalan iş Docker-in sənin image-ində gətirdiyi userspace ilə təmin olunur.
---
Namespaces anlayışı sadəcə belədir: prosesin gördüyü dünya kəsilir, ona “özəl eynək” taxılır.
Əslində bütün proseslər eyni kernel üzərində işləyir, amma kernel deyir ki, “sən bu proseslərdən, bu fayl sistemindən, bu şəbəkədən başqa heç nə görməyəcəksən”.
Beləliklə, hər konteynerin içindəki proses sanki öz kiçik əməliyyat sistemi varmış kimi davranır.
1. PID namespace — hər prosesin bir PID nömrəsi var. Normalda hostda “systemd” PID 1-dir.
Amma konteyner öz PID namespace-də doğulanda, həmin konteynerin içində çalışan proseslər üçün “PID 1” artıq sənin proqramındır.
Hostdakı minlərlə proses orada görünmür. Sanki sənə “sıfırdan başlayan proses siyahısı” verilmiş kimi olur.
---
2. Mount namespace
Hostda normal halda sən `ls /usr` və `ls /var` edəndə, öz Linux sisteminin kitabxana və log qovluqlarını görürsən. Məsələn:
```bash
hostda
ls /usr
bin games include lib local sbin share src
ls /var
backups cache lib log mail opt run snap tmp
```
Amma sən `docker run -it alpine sh` deyib konteyner açanda, eyni əmri konteyner içində versən:
```bash
konteynerin içində
ls /usr
bin lib sbin
ls /var
cache empty lib lock log run spool tmp
```
Görürsən ki, buradakı `/usr` və `/var` artıq hostdakının eynisi deyil.
Bunlar image-in qatlarından gələn xüsusi rootfs-dir.
Yəni mount namespace prosesi sənin hostun `/usr`-nı görməyə qoymur, onun yerinə konteynerə ayrıca fayl sistemi “xəritələyir”.
---
3. Network namespace
Hostda `ip addr` desən, real interfeysləri görəcəksən, məsələn:
```bash
hostda
ip addr
1: lo: <LOOPBACK,UP> ...
2: eth0: <BROADCAST,MULTICAST,UP> ...
```
Konteynerə girib eyni əmri desən:
```bash
konteynerdə
ip addr
1: lo: <LOOPBACK,UP> ...
39: eth0@if40: <BROADCAST,MULTICAST,UP> ...
```
Burada hostdakı `eth0` görünmür, əvəzində Docker-in verdiyi virtual `eth0@if40` görünür.
Bu, Docker bridge şəbəkəsi ilə bağlıdır.
Hər konteynerin öz loopback interfeysi də var (`lo`), amma bu konteynerin içindəki `127.0.0.1` yalnız özünə aiddir, hostdakına yox.
---
4. User namespace
Normal hostda `id` əmri sənin real UID/GID-ni göstərir, məsələn:
```bash
hostda
id
uid=1000(maham) gid=1000(maham) groups=1000(maham),27(sudo)
```
Konteynerə girdikdə:
```bash
konteynerdə
id
uid=0(root) gid=0(root) groups=0(root)
```
Burada sən konteynerin içində “root” görünürsən.
Amma əgər user namespace mapping açıqdırsa, kernel deyir: bu konteynerdəki `uid=0` əslində hostda məsələn `uid=100100`.
Yəni içəridə “root” olmağın host səviyyəsində sənə real root gücü vermir.
Bu texnika rootless konteynerlərin əsasını təşkil edir: sən konteynerdə superuser görünürsən, amma host səviyyəsində adi istifadəçisən.
Yekun olaraq: bütün bu namespaces mexanizmləri birlikdə prosesə “özünə aid kiçik bir sistem” hissi yaradır.
Amma arxa planda hamısı eyni Linux kernelini paylaşır.
Bu illüziya Docker-in “yüngül” olmasının səbəbidir:
yeni kernel yüklənmir, sadəcə mövcud kernel deyir “sənə özəl aləm yaratdım”.
---
Cgroups nədir?
Cgroups sözü “control groups”dan gəlir.
Bu, Linux kernelində bir mexanizmdir ki, prosesləri qruplaşdırıb onların istifadə etdiyi resursları idarə edir.
Sadə dillə desək, namespaces izolasiya verir (“nələri görə bilərsən?”), cgroups isə resurs limiti qoyur (“nə qədər istifadə edə bilərsən?”).
Cgroups-un əsas gücü odur ki, hər konteynerə ayrılıqda bu sərhədlər qoyula bilir:
CPU – neçə faiz gücdən istifadə edə bilər.
RAM – maksimum nə qədər yaddaş işlədə bilər.
Disk I/O – hansı sürətlə oxuyub-yaza bilər.
Network I/O – ötürmə məhdudiyyətləri də qoymaq olar.
--- Praktik nümunə – RAM limiti
Tutaq ki, konteyneri belə işə salırsan:
docker run -m 256m alpine stress --vm 1 --vm-bytes 300M --vm-hang 0
1. docker run – yeni konteyner açır.
2. -m 256m – bu konteynerin maksimum RAM istifadəsini 256 MB ilə məhdudlaşdırır.
Əslində Docker arxa planda Linux-un memory cgroup-una yazır ki, “bu qrup üçün sərhəd = 256 MB”.
3. alpine – sadə, yüngül Linux image-i.
4. stress – test üçün yazılmış utilitdir. Məqsədi sistemə yük verməkdir.
5. --vm 1 – bir virtual işçi proses yarat.
6. --vm-bytes 300M – həmin proses 300 MB RAM rezerv etməyə çalışsın.
7. --vm-hang 0 – yaddaşı aldısa, dayandırmasın, saxlasın.
-- Yəni sən konteynerin limitini 256 MB qoymusan, amma içindəki proqram 300 MB istəyəndə kernel bunu necə idarə edir?
Kernel hər prosesin yaddaş istifadəsini izləyir.
Konteynerin prosesləri müəyyən bir memory cgroup-a bağlıdır. Limit 256 MB-dır.
Proses 257-ci MB-a çıxmaq istəyəndə kernel deyir: “yox, icazə yoxdur”.
Əgər proqram bu məhdudiyyəti aşmaqda israr edirsə, kernel OOM killer-i işə salır (Out Of Memory killer). Bu, o konteynerdəki prosesi vurur.
Cgroups-un gözəlliyi budur ki, sərhəd konteyner-qrupla məhdudlaşır.
Yəni bir konteyner RAM limitini aşırsa, yalnız onun prosesləri vurulur. Digər konteynerlər normal işləyir.
Əgər cgroups olmasaydı, əksinə, bir konteyner hostdakı bütün RAM-ı yeyə bilərdi və
bütün sistemdə (o cümlədən digər konteynerlərdə) proseslər çökərdi.
Təsəvvür et ki, bir binada su anbarı var. Əgər cgroups yoxdursa, hamı həmin anbardan sərhədsiz su çəkə bilər,
bir qonşu bütün suyu açıb axıtsa, digərləri quru qalar.
Cgroups olan halda hər mənzil üçün ayrıca su sayğacı və limit var.
Bir mənzil öz limitini keçsə, yalnız o quru qalır; başqalarının payına toxunmur.
--- Praktik nümunə – CPU limiti
docker run --cpus="1.0" alpine stress --cpu 4
Burada deyirik ki, konteyner cəmi 1 CPU-nun gücünü işlədə bilər.
--- Məqsəd nədir?
Ən böyük məqsəd budur: bir konteynerin davranışı başqalarına zərər verməsin.
Məsələn, sənin PostgreSQL konteynerin var, yanında da ağır hesablama edən başqa bir konteyner.
Əgər cgroups olmasa, ikinci konteyner bütün CPU və RAM-ı yeyib Postgres-i çökdürə bilər.
Cgroups isə sərhəd çəkir – hər biri öz sandboxunda oynayır.
--- Yəni yekunda belədir:
Namespaces → konteynerin görəcəyi dünya.
Cgroups → konteynerin istifadə edə biləcəyi resurs miqdarı.
---
1. Image nədir, əslində?
Image sadəcə “bir fayl sistemi fotoşəklidir”.
Yəni sən kompüterini açanda /bin, /usr, /etc kimi qovluqlar var ha – image də bunların kiçik bir nüsxəsidir.
Fərq ondadır ki, image dəyişməzdir (read-only).
-- Docker image-in içində nə olur?
Ən sadə halda – bir Linux distributivinin “userspace” faylları (Debian, Alpine və s.).
Onun üzərinə – sənin əlavə kitabxanaların (məsələn, .NET runtime).
Daha sonra – sənin yazdığın tətbiqin faylları.
Bunların hamısı qat-qat saxlanır, sanki diff arxivləri kimi.
Ən altdakı baza lay, sonra onun üzərinə əlavə laylar.
Docker bunu buna görə edir ki, hər dəfə sıfırdan bütün rootfs-i kopyalamayaq.
Yəni ubuntu layını artıq yükləmisənsə, sabah başqa image də eyni ubuntu-ya əsaslansa, onu təkrar endirməyəcəksən.
Ona görə image = qat-qat arxiv + metadata (ENTRYPOINT, CMD və s.).
--
.NET image əslində nədir?
Məsələn, sən mcr.microsoft.com/dotnet/sdk:9.0 image-i çəkirsən.
Görünüşdə “bu sadəcə .NET SDK image” kimi gəlir, amma realda bu image bir neçə qatın birləşməsidir:
Ən altda baza Linux userspace olur.
Microsoft-un .NET image-ləri uzun illər Debian üzərində qurulurdu, indi bəzi versiyaları Alpine də olur.
Yəni image-in dərinliyində “debian:bookworm-slim” və ya “alpine:3.x” kimi qat var.
Onun üstündə .NET runtime və SDK quraşdırılmış qat gəlir.
Ən üst qatlarda isə image metadata olur (məsələn, ENTRYPOINT ["dotnet"]).
Yəni sən dotnet sdk image çəkəndə, Docker əvvəl yoxlayır: “sənin sistemində debian:bookworm-slim qatı var?”
Əgər əvvəllər başqa bir image (məsələn, node:20-bullseye) çəkmisənsə və
onda da eyni Debian bazası işlənibsə, həmin baza qat artıq mövcuddur və təkrar endirilmir.
“Bəs niyə sadəcə .NET kifayət edir?”
Çünki image-lərin qat mexanizmi var.
Sənə .NET SDK lazım olanda mcr.microsoft.com/dotnet/sdk:9.0 image-ində həm Debian baza qatını, həm .NET qatlarını birlikdə gətirir.
Docker sənin üçün bunu avtomatik həll edir.
Sən ayrıca “debian” image-i çəkməyə məcbur deyilsən.
Amma əgər sistemində həmin qat onsuz da varsa, Docker sadəcə onu reuse edir, yəni “yenidən endirmir”.
Beləliklə, sən sadəcə .NET image çəkdiyini zənn edirsən, amma arxa planda bu image çoxqatlıdır.
Docker isə hər qatı “digest” ilə yoxlayır, eynisi varsa təkrar gətirmir.
Ona görə download bəzən çox tez gedir – çünki bəzi laylar artıq hostunda var.
Nəticə
.NET SDK image = Debian/Alpine baza qatları + .NET runtime/SDK qatı + metadata.
Docker sənin üçün bu qatları birləşdirib “tam bir rootfs” kimi göstərir.
Əgər qat artıq varsa, təkrar endirilmir, sadəcə mövcud olan istifadə olunur.
--
Image = read-only qatlar
Image dedikdə təsəvvür elə ki, sənin qarşında dəyişdirilə bilməyən kitab səhifələri var.
Bu səhifələr qat-qat düzülüb:
birinci səhifə Debian baza fayllarıdır (/bin, /usr və s.), ikinci səhifə .NET runtime kitabxanalarıdır, üçüncü səhifə sənin tətbiq fayllarındır.
Bu səhifələri sən oxuya bilərsən, amma dəyişdirə bilməzsən. Yəni image read-only-dur.
2. Container nədir?
Container image-dən fərqli olaraq canlı prosesdir.
Docker image qatlarının üstündə copy-on-write adlı əlavə bir qat qoyur. Buna sadəcə “yazı qatı” da deyirlər.
Bu qat boş başlayır.
Əgər sən konteynerdə sadəcə fayl oxuyursansa (cat /usr/lib/libssl.so kimi), o fayl image qatlarından gəlir.
Əgər sən fayl dəyişməyə çalışsan (echo "hello" > /etc/motd), onda kernel həmin faylı image qatından götürür,
bir nüsxəsini “yazı qatı”na kopyalayır və dəyişiklik artıq orada edilir.
Yəni sənin dəyişdirdiyin fayl artıq container-ə məxsus olur.
-- Container silinəndə nə olur?
O yazı qatı konteynerlə birlikdə silinir. Yəni sən içində etdiyin dəyişikliklər uçur.
Image qatları isə toxunulmaz qalır. Buna görə image dəyişmir və yenə də eyni qalır.
---
Image və fayllar
Image əslində “root filesystem”dir.
Yəni bir əməliyyat sisteminin içində gördüyün /bin, /usr, /etc qovluqları — bunlar image qatlarında saxlanılır.
Məsələn, Alpine image-də bu qovluqlar var, çünki Linux userspace-in işləməsi üçün gərəkdir.
Deməli, konteyner açıldıqda onun gördüyü bütün fayllar — proqram faylları, kitabxanalar, konfiqurasiya faylları — image qatlarından gəlir.
-- 1. Container açanda nə baş verir?
docker run -it mcr.microsoft.com/dotnet/sdk:9.0 bash
Bu əmrlə sən deyirsən: “.NET SDK image götür, üstündə bir terminal (bash) işə sal”.
-- Docker nə edir?
dotnet/sdk:9.0 image qatlarını götürür.
Ən altda baza qat var — Debian və ya Alpine (Microsoft-un rəsmi image-ləri uzun illər Debian idi, bəzi versiyalarında Alpine də var).
Üstündə .NET runtime + SDK faylları qatı.
Ən yuxarıda isə metadata qat (məsələn, ENTRYPOINT ["dotnet"]).
Sonra bu qatların hamısını overlayfs ilə birləşdirib sənə bir root filesystem göstərir.
2. Sən terminalda nə görürsən?
Konteynerin içinə girəndə /bin, /usr, /etc, /app və s. görürsən. Bunların hamısı image qatlarından gəlir.
/usr/bin/dotnet → .NET SDK qatında əlavə olunmuş fayldır.
/bin/ls, /bin/bash → baza Debian/Alpine qatında olan fayllardır.
/etc/os-release → baza OS qatında mövcud olan sistem faylıdır.
Yəni sən konteynerdə gəzdiyin fayl sistemi image qatlarının üst-üstə qoyulmuş nüsxəsidir.
---
İşin başlanğıc nöqtəsi fiziki serverdir. Bu, sadəcə real dəmirdir:
CPU, RAM, disk və şəbəkə kartı olan, 24/7 işləmək üçün yığılmış güclü kompüter.
Bu dəmirin üzərinə ya bir host əməliyyat sistemi qoyursan və onun içində texnologiyalar işləyir, ya da elə birbaşa hardware-i idarə edən qat qurursan.
Virtual maşın yolunda səhnəyə hypervisor çıxır. Hypervisorın iki forması var.
1. Type 1 (bare-metal) birbaşa hardware üzərində dayanır və sanki özü host OS rolunu oynayır; VMware ESXi, Hyper-V Server, Xen bu qəbildəndir.
2. Type 2 (hosted) isə adi bir host OS-in içində sadə proqram kimi işləyir; VMware Workstation və VirtualBox buna nümunədir.
Hər iki halda hypervisor hostdakı real resursları tanıyır:
Type 1 birbaşa sürücülərlə hardware-ə toxunur, Type 2 isə host OS-in API-ləri vasitəsilə
Sən VM üçün, tutalım, 2 CPU, 4 GB RAM və 40 GB disk deyəndə hypervisor həmin miqdarda virtual hardware düzəldir:
virtual CPU vaxt dilimləri ayırır, RAM səhifələrini xəritələyir, disk üçün bir faylı (məsələn, vmdk) VM-ə “sənin diskindir” kimi göstərir,
şəbəkə kartını da NAT və ya bridge ilə bağlayır. Beləcə VM-in içində tam ayrıca Guest OS əsl kompüter kimi boot edir
Nəticə çox güclü izolasiya olsa da baha başa gəlir: hər VM öz kernelini və userspace-ni daşıdığı üçün, start vaxtı saniyələrdən dəqiqələrə qədər çəkə bilər,
üstəlik hər VM ayrıca OS olduğu üçün update, patch və monitorinq idarəçiliyi də artır.
Ən vacib məqam budur ki, hədəfin tək bir tətbiqi işə salmaq olsa belə, VM səndən tam bir OS daşımağını tələb edir.
Konteyner yolunda həmin əlavə OS qatı yox olur.
Burada Guest OS yoxdur, host kernel paylaşılır, amma tətbiq öz izolyasiya olunmuş userspace-də işləyir.
Bu izolyasiyanı Linux-un namespaces mexanizmi verir:
1. PID namespace prosesi öz proses siyahısı ilə məhdudlaşdırır,
2. mount namespace konteynerə öz root filesystem görünüşünü təqdim edir,
3. network namespace ona ayrıca şəbəkə stack verir,
4. UTS və IPC ad və proseslərarası ünsiyyəti kəsir,
5. user namespace isə içəridə root görünsə də hostda başqa UID/GID-ə xəritələmə ilə təhlükəsizliyi sərtləşdirir.
Resurs limiti isə cgroups-la qoyulur:
RAM, CPU və I/O sərhədləri kernel səviyyəsində həmin proses qrupuna tətbiq edilir,
limit aşılarsa o konteynerin prosesləri vurulur, digərləri təsirlənmir.
---
Docker run vaxtı ardıcıllıq belədir:
əvvəl seçdiyin image-in laylarından read-only rootfs hazırlanır, onun üzərinə boş yazı qatı qoyulur ki,
copy-on-write ilə dəyişikliklər yalnız konteynerə məxsus olsun;
sonra kernelə “bu prosesi bu namespaces və cgroups daxilində, bu rootfs ilə işə sal” deyilir və birbaşa execve ilə sənin tətbiqin start verir.
Yeni kernel boot edilmir, init sistemi açılmır, virtual hardware simulyasiyası yoxdur.
Bu, startı millisaniyə–saniyə miqyasında sürətləndirir,
RAM/disk izini kəskin azaldır və eyni host üzərində onlarla-yüzlərlə konteyneri sakitcə qaçırmağa imkan verir.
Burada image mexanizmi oyunu dəyişir. Image dəyişməz laylardan yığılmış userspace-dir:
altda Debian və ya Alpine kimi baza qatları, üstündə .NET runtime/SDK kimi kitabxanalar, ən üstündə isə sənin tətbiq faylların və metadata olur.
Eyni digest ilə çəkilən image hər yerdə eyni görünüşü verir.
Konteyner image-in üzərində canlı prosesdir: oxuma əməliyyatları read-only qatlarından gəlir,
yazmağa çalışanda kernel faylı yazı qatına kopyalayıb orada dəyişir;
konteyner silinəndə yazı qatı da gedir, image toxunulmaz qalır.
Bu model “məndə işləyir, səndə işləmir” problemini həll edir, çünki tətbiq və userspace bir yerdə, deterministik paket kimi daşınır.
Niyə Docker yarandı sualının cavabı da buradadır. VM-lər izolasiya baxımından mükəmməl olsa da, ağır və idarəetməsi çətindir;
bir maşında ancaq bir neçə VM rahat saxlanırdı.
Konteynerlər eyni kernel üzərində yüngül izolyasiya verdiyi üçün sıxlıq xeyli artır,
start vaxtı praktiki olaraq dərhal olur və image-lə mühit daşıma sabitləşir.