-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsection_2.txt
More file actions
431 lines (283 loc) · 22.3 KB
/
section_2.txt
File metadata and controls
431 lines (283 loc) · 22.3 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
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
2. Docker CLI məntiqi: imajlar, konteynerlər, laylar, keş
---------------------------------------------------------
Image operations
------------------------------------------------------------------------------------------------------------
2.1. Image adı necə qurulur?
Image üçün 4 əsas əməliyyat var: pull, build, tag, push. Hərəsi eyni obyekt üzərində fərqli mərhələdir:
registry-dən gətirmək, sıfırdan yaratmaq, ad vermək, registriyə yollamaq.
1. Image adı necə qurulur?
Bir image-in adı həmişə üç hissədən ibarət olur:
[registry]/[repo adı]:[tag]
registry – image haradan gəlir (məsələn, docker.io → Docker Hub). Yazmasan default olaraq Docker Hub götürülür.
repo adı – layihənin və ya proqramın adı (ubuntu, nginx, microsoft/dotnet).
tag – versiya işarəsi (20.04, 9.0, latest).
Məsələn: docker pull ubuntu:20.04
Bu əmrlə sən Docker Hub-dan (registry) ubuntu repozitoriyasının 20.04 tag-lı image-in çəkirsən.
Qeydlər:
Əgər :tag yazmasan → avtomatik :latest gəlir. Amma “latest” heç vaxt zəmanət deyil, sadəcə bir etiketdir. Sabah dəyişə bilər.
Həqiqi dəyişməz identifikator isə digest-dir. Digest sha256:xxxx... formasında olur. Digest ilə çəkilən (pull edilən) image hər yerdə eyni baytları gətirir.
Pull əməliyyatı əslində nə edir?
Məsələn, sən yazırsan: docker pull nginx:1.25
Arxada addım-addım bunlar olur:
1. Client → Daemon
Sənin docker əmrini yazmağın sadəcə client-dir. O, arxa plandakı dockerd-ə deyir: “nginx:1.25 image-ni çək (pull)”.
2. Manifest istənilir
Docker daemon Docker Hub API-yə sorğu göndərir və deyir: “Bu ad və tag üçün manifest ver”.
Manifest sadəcə “bu image neçə laydan ibarətdir, hər layın hash-ı və ölçüsü nədir” siyahısıdır.
3. Layların yoxlanması
Daemon əvvəl lokaldakı store-a baxır: bu hash-lı lay artıq varmı?
Əgər var, yenidən endirmir. Əgər yoxdur, həmin layı registridən çəkir.
Məsələn:
Ən altda debian:bookworm-slim bazası.
Üstündə nginx serverin faylları.
4. Layların yığılması
Laylar endirilib dekompress edilir və lokal store-a yazılır.
5. İstifadəyə hazır olur
İndi sən docker run nginx:1.25 desən, həmin laylardan root filesystem hazırlanacaq və konteyner işə düşəcək.
Platforma məsələsi
Bəzən eyni ad altında fərqli arxitektura imajlar olur. Məsələn, nginx:1.25 həm linux/amd64, həm də linux/arm64 üçün mövcuddur.
Əgər sənin kompüterin amd64-dür, Docker uyğun olan manifesti seçəcək.
Əgər sən ARM cihazındasan (məsələn, Apple M1/M2), Docker oradakı arm64 qatını çəkəcək.
Əgər xüsusi olaraq amd64 istəyirsənsə, belə yaza bilərsən:
docker pull --platform linux/arm64 nginx:1.25
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
2.2. Build – sıfırdan image yaratmaq
“Build” dedikdə məqsəd odur ki, kodunu və ona lazım olan faylları götürüb dəyişməz bir image-ə çevirəsən.
Bu image sabit olacaq, yəni eyni Dockerfile və eyni fayllarla yenidən quranda eyni nəticəni verəcək.
Bunun üçün iki anlayışı bir yerdə başa düşmək kifayətdir: 1. build konteksti və 2. Dockerfile.
Build konteksti sənin göndərdiyin iş qovluğudur. docker build -t myapp:1.0 . yazanda sondakı nöqtə həmin qovluq deməkdir.
Docker client bu qovluğu arxivləyib daemon-a ötürür. Kontekstdə nə varsa, hash hesabına daxil olur.
Ona görə lazımsız və iri qovluqları konteksdən kənar saxlamaq vacibdir.
Bunun üçün kökdə .dockerignore faylı saxlayırsan.
Məsələn, .NET layihəsində bin, obj, .vs, .git və oxşar kataloqları .dockerignore-a yazırsan ki, hər build-də bu tonlarla fayl göndərilməsin və
Dockerfile sənin “reseptindir”. Docker onu sətir-sətir oxuyur, hər sətir deterministik bir addımdır və nəticəsi ayrı bir lay kimi yadda qalır.
Eyni sətir eyni giriş faylları ilə təkrar gəlirsə, Docker layı keşdən götürür və vaxt itirmir.
Buna görə sətir sırası performansdır.
Nadir dəyişən addımlar yuxarıda, tez-tez dəyişən addımlar altda olmalıdır.
1. Docker-da RUN əmrləri və cache
Dockerfile-ı oxuyanda hər sətir (RUN, COPY, ADD, və s.) yeni bir qat (layer) yaradır.
: Docker hər qatın girişini (hansı fayllar konteksdən kopyalandı + əmrin özü) hash-layır.
: Əgər həmin hash eyni qalırsa, Docker əvvəlki nəticəni (cache) istifadə edir.
: Əgər hansısa giriş dəyişibsə (məsələn, kopyalanan fayl dəyişdi, ya da əmrin özü dəyişdi), onda həmin qat yenidən icra olunur və nəticə yeni qat kimi saxlanılır.
RUN dotnet restore
qatının cache-də qalması üçün bu əmrin girişləri eyni olmalıdır. Onun girişləri nədir?
1. Ondan əvvəlki qatın faylları (biz COPY ilə yalnız *.csproj gətirmişik).
2. Əmr mətninin özü (“dotnet restore”).
Əgər .csproj faylları dəyişməyibsə və əmr də eynidirsə → Docker görür ki, “bu qatın nəticəsi artıq var” və təkrar işləmir, cache-dən götürür.
2. Niyə restore-u koddan əvvəl edirik?
Əgər sən əvvəlcə bütün kodu COPY . . ilə gətirsən və sonra RUN dotnet restore desən:
1. Kodda hər xırda dəyişiklik (məsələn, Program.cs-də bir sətir) COPY qatını dəyişəcək.
2. O zaman həmin qatın digest-i dəyişəcək və ondan sonra gələn bütün qatların cache-i pozulacaq, yəni restore da təkrar icra olunacaq.
Halbuki NuGet paketləri heç dəyişməyib.
Bu səbəbdən restore-u .csproj-ları kopyalayandan dərhal sonra edirik. .csproj-lar nadir dəyişir, ona görə bu qat çox vaxt cache-dən gəlir.
Kod isə ayrı COPY ilə sonradan gətirilir. Beləliklə, kod dəyişəndə yalnız publish qatı pozulur, restore qatının cache-i qorunur.
3. “no-restore” nə üçündür?
Sən dotnet publish yazanda əslində o, default olaraq əvvəlcə restore edir, sonra publish.
Amma biz artıq restore-u ayrıca etmişik və cache-də saxlamışıq.
Ona görə publish-də --no-restore yazırıq. Bu, “yenidən paketləri yoxlama, keçmiş qatdakı restore nəticəsindən istifadə et” deməkdir.
Əgər --no-restore yazmasan, publish hər dəfə restore etməyə çalışacaq → cache mexanikasının qazancı gedəcək.
--------------------------------------------------
İkinci hissə .dockerignore-un nə iş gördüyüdür.
Docker build vaxtı nöqtə (.) ilə göstərdiyin qovluq arxivlənib daemon-a göndərilir; buna build konteksti deyirik.
.dockerignore bu arxivə daxil olmayacaq fayl və qovluqları sadə pattern-lərlə çıxarır.
Məna çox sadədir: nə qədər az lazımsız fayl göndərsən, bir o qədər az hash dəyişər və cache qorunar.
.dockerignore yalnız build kontekstdən çıxarır; lokal diskindəki fayllar silinmir. Nəticədə build sürətlənir.
--------------------------------------------------
Üçüncü hissə docker build komandası və flaqların mənasıdır.
Əsas forma belədir: docker build -t myapi:1.0 .
Burada -t image-ə ad və tag verir; bu ad sonradan run, tag, push üçün kimlikdir. Nöqtə build kontekstidir.
Başqa fayl adı ilə build etmək istəsən -f Dockerfile.prod deyərsən.
məsələn:
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 29.08.2025 15:33 76 .dockerignore
-a--- 30.08.2025 23:12 0 docker-compose-prod.yml
-a--- 30.08.2025 23:12 0 docker-compose.yml
-a--- 30.08.2025 23:11 0 Dockerfile.api
-a--- 30.08.2025 23:11 0 Dockerfile.client
Biz burada Dockerfile.api və ya Dockerfile.client istifadə edə bilərik.
--------------------------------------------------
Əvvəlcə “build konteksti” ilə “WORKDIR” arasındakı fərqi qısaca yerləşdirim ki, qalan hamısı aydın olsun.
1. Build konteksti sənin hostdakı iş qovluğundur; docker build ... . yazanda sondakı nöqtə həmin qovluğu daemon-a göndərmək deməkdir.
2. WORKDIR isə imajın içində “hazırda işlədiyim qovluq” deməkdir.
Yəni COPY A B əməlində A hostdakı kontekstdədir, B isə imajın içindədir və WORKDIR-ə nisbidir.
Bu ikisi eyni şey deyil: biri kənardakı material, digəri konteynerin içində istifadə edilən ünvan anlayışıdır.
Sənin həndəsənə görə ən rahat yol Dockerfile-ı repozitoriyanın kökünə qoyub build kontekstini də kök qovluq etməkdir.
Onda src/... yollarını birbaşa COPY edə biləcəyik. .dockerignore da elə kökdə durmalıdır. Bu .dockerignore Docker-a “kontekstə bunları qatma” siyahısı verir.
Fayllar diskindən silinmir, sadəcə build üçün arxivə düşmür. Nəticə ikiqatdır: göndərilən material balacalaşır, COPY . . kimi sətirlərin hash-ı xırda səbəblərlə dəyişməyib cache pozulmur.
-------------------------
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# Copy solution file
COPY ./src/server/ApiSolution.sln ./server/
# Copy csproj files
COPY ./src/shared/LearnKazakh.Shared/*.csproj ./shared/LearnKazakh.Shared/
COPY ./src/server/LearnKazakh.API/*.csproj ./server/LearnKazakh.API/
COPY ./src/server/LearnKazakh.Application/*.csproj ./server/LearnKazakh.Application/
COPY ./src/server/LearnKazakh.Core/*.csproj ./server/LearnKazakh.Core/
COPY ./src/server/LearnKazakh.Domain/*.csproj ./server/LearnKazakh.Domain/
COPY ./src/server/LearnKazakh.Infrastructure/*.csproj ./server/LearnKazakh.Infrastructure/
COPY ./src/server/LearnKazakh.Persistence/*.csproj ./server/LearnKazakh.Persistence/
# Restore dependencies for ALL projects
RUN dotnet restore ./server/ApiSolution.sln
# Copy the rest of the source code
COPY ./src/server ./server/
COPY ./src/shared ./shared/
# Publish stage: Publish API project
RUN dotnet publish ./server/LearnKazakh.API/LearnKazakh.API.csproj -c Release -o /out --no-restore
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
WORKDIR /app
# Copy published app
COPY --from=build /out ./
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "LearnKazakh.API.dll"]
-------------------------
docker build -t test-api-image:1.0 . -f .\docker\Dockerfile.api
docker faylından yeni image yaradır
powershell command: [docker image ls]
REPOSITORY TAG IMAGE ID CREATED SIZE
test-api-image 1.0 974ad39e9d51 36 minutes ago 387MB
Port mapping necə işləyir
--------------------------------------------------
Port məsələsini belə düşünmək ən rahatdır:
tətbiq içəridə hansı “qulaq”dan eşidir, Docker isə çöldən gələn səsi həmin qulağa necə gətirir.
İkisi uyğun gəlməsə, bağlantı alınmır.
İçəridə Kestrel var hansı ki, container içərisndə run olan asp net app orda işləyir.
Ona “hansı ünvanda və portda qulaq as” deməlisən.
`ASPNETCORE_URLS=http://+:8080` yazanda mənası budur: “bütün şəbəkə interfeyslərində 8080-də qulaq as”.
Buradakı `+` praktikdə `0.0.0.0` kimidir, yəni konteynerin içində hansı IP varsa hamısından gələn trafiki qəbul et.
Əgər `http://localhost:8080` desən, Kestrel yalnız konteynerin öz “loopback”ində eşidər;
konteynerin şəbəkə kartından gələn trafik ora çatmaz.
Ona görə konteynerdə ən təhlükəsiz ünvan `http://+:PORT`-dur; bütün qapıları içəridə həmin porta açıq saxlayır.
Çöldə işi Docker görür. `-p 8080:8080` deyəndə hostdakı 8080-i konteynerdəki 8080-ə bağlayırsan.
Brauzerdən `http://localhost:8080` vurursan, sorğu hosta düşür,
Docker onu konteynerin 8080-nə ötürür, içəridə Kestrel artıq `0.0.0.0:8080`-də qulaq asdığı üçün cavab verir.
Əsas qayda sadədir: sağ tərəf konteynerin içində Kestrel-in dinlədiyi portdur, sol tərəf hostda istifadə edəcəyin portdur.
Kestrel içəridə 5000-də dinləyirsə, ya `ASPNETCORE_URLS=http://+:5000` verməlisən, ya da `-p 8080:5000` deyib host 8080-ni konteyner 5000-ə yönləndirməlisən.
Eyni məntiqdə hostda 80-dən açmaq istəsən `-p 80:8080` yazırsan; içəridə heç nə dəyişmir, yalnız çöldə rahat URL alırsan.
“localhost” sözünün kimə aid olduğunu da dəqiq ayır.
Konteynerin içində “localhost” konteynerin özüdür; hostun “localhost”u isə sənin maşınındır.
Konteynerdən hostdakı servislərə getmək üçün Windows/macOS-da `host.docker.internal` xüsusi adı var;
əks istiqamətdə isə hostdan konteynerə keçid `-p` ilə edilir.
İçəridə `0.0.0.0` demək “hansı IP ilə gəlsə, qəbul et” mənasındadır, yəni Docker-in yönləndirdiyi paketlər Kestrel-ə çatır.
Bir cümləlik yadda saxla.
1. `ASPNETCORE_URLS` içəridə Kestrel-in eşitdiyi adres və portu təyin edir,
2. `-p host:container` isə çöldən o porta yol açır. Sağdakı rəqəm Kestrel-ə uyğun gəlməlidir, soldakı isə hostda istifadə etdiyin rəqəmdir.
host.docker.internal nədir, harada işləyir
--------------------------------------------------
Docker Desktop olan Windows və macOS-da konteynerin içindən “host” maşına getmək üçün xüsusi bir ad var: host.docker.internal.
Sənin API konteynerdən hostdakı Postgres-ə qoşulmaq istəyəndə connection string-də Host=host.docker.internal yazanda işin məğzi budur:
“konteynerdən çıx, Docker-in host qapısına get, ordan da hostun Postgres-ə çat”.
Linux serverdə bu ad mövcud deyil, çünki Docker Desktop yoxdur. Ona görə istehsalda iki yol praktik olur.
1. Ya Postgres-i də konteynerdə saxlayıb eyni şəbəkədə servis adı ilə çağırırsan (Compose-da db servisi varsa, Host=db yazırsan və bu, Docker-in daxili DNS-i ilə işləyir).
2. Ya da tam hostdankənar bir DB-ə qoşulursan və normal DNS/IP yazırsan (məsələn, Host=10.0.0.12 və ya Host=prod-db.mycompany.com).
ASP.NET Core konfiqurasiyada üstünlük qaydası və --env-file
--------------------------------------------------
ASP.NET Core konfiqurasiyanı qat-qat yığır.
1. Əsas appsettings.json oxunur,
2. sonra mühitə görə fayl gəlir (məsələn, appsettings.Production.json),
3. daha sonra environment dəyişənləri, ən sonda isə command-line arqumentləri.
Eyni açar bir neçə yerdə varsa, ən son gələn qalibdir.
Konteynerdə praktiki olaraq ən əlçatan qat environment dəyişənləridir, çünki bir komanda və ya bir fayl ilə hər şeyi üstələyə bilirsən.
Docker-da hər dəfə PowerShell-də uzun-uzadı -e yazmaq əzabdır, üstəlik quoting problemi olur.
Ona görə kökdə sadə bir app.env faylı saxlayırsan və --env-file ilə verirsən.
ASP.NET Core-da dərin açarları environment şəklinə çıxarmaq üçün iki nöqtə : yerinə iki alt xətt __ istifadə olunur.
Məsələn, ConnectionStrings:DefaultConnection açarı environment-də belə yazılmalıdır:
ConnectionStrings__DefaultConnection=…
Praktiki app.env belə görünür:
ConnectionStrings__DefaultConnection=Host=host.docker.internal;Database=LearnKazakh;Username=postgres;Password=supersecret
Authentication__Jwt__Issuer=http://localhost:8080
Authentication__Jwt__IssuerSigningKey=super secret signing key, change on production
Authentication__Jwt__Audience=http://localhost:8080
Authentication__Jwt__AccessTokenExpirationMinutes=15
Authentication__Jwt__RefreshTokenExpirationDays=7
ASPNETCORE_URLS=http://+:8080
ASPNETCORE_ENVIRONMENT=Production
Burada diqqət ediləsi iki incə məqam var. Birincisi, --env-file daxilində dəyərləri dırnaqlamırsan;
sətri necə yazırsansa, Docker bütöv dəyər kimi götürür, boşluqlar da problem yaratmır.
İşə salmaq artıq çox sadədir:
docker run --rm -p 8080:8080 --env-file app.env learn-kazakh-api:latest
hansı ki, əvvəl bu formatda etməli idin.
docker run --rm -p 8080:8080 `
-e ConnectionStrings__DefaultConnection="Host=host.docker.internal;Database=LearnKazakh;Username=postgres;Password=supersecret" `
-e Authentication__Jwt__Issuer="http://localhost:8080" `
-e Authentication__Jwt__IssuerSigningKey="super secret signing key, change on production" `
-e Authentication__Jwt__Audience="http://localhost:8080" `
-e Authentication__Jwt__AccessTokenExpirationMinutes="15" `
-e Authentication__Jwt__RefreshTokenExpirationDays="7" `
-e HealthChecks__SelfUrl="http://127.0.0.1:8080/api/health" `
learn-kazakh-api:latest
build image: docker build -t learn-kazakh-api:latest -f docker/Dockerfile.api .
run a container: docker run --rm --env-file .\docker\app.env -p 8080:8080 learn-kazakh-api:latest
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
2.3. Tag anlayışı
Tag əslində ad göstəricisidir.
Image dediyimiz obyekt dəyişməz məzmundur: laylar, konfiqurasiya və manifestdən ibarət bir paket.
Bu paketin həqiqi kimliyi digest-dir (sha256 ilə hesablanan məzmun xülasəsi).
Tag isə həmin obyektə yönələn işarədir.
docker build -t myapp:1.0 .
------------------------------------------------------------------------------------------------------------
Container Operations
------------------------------------------------------------------------------------------------------------
“docker run” deyəndə beynində belə bir axın canlandır. Sən əmri yazırsan, Docker əvvəlcə image-ə baxır:
lokaldadırsa götürür, yoxdursa səssizcə pull edir.
Sonra image-in read-only laylarını overlayfs ilə birləşdirib üstünə boş yazı qatı qoyur ki,
içəridə etdiyin dəyişikliklər yalnız bu konteynerə aid olsun.
Ardınca kernel səviyyəsində konteyner üçün ayrı “dünya” açılır: PID, mount, user və s. namespaces qurulur, cgroups ilə RAM/CPU limitləri yazılır.
Şəbəkə hissəsini ən sadə, amma dəqiq dildə belə düşün.
Docker heç nə deməsən, konteyneri “bridge” adlı daxili şəbəkəyə qoşur.
Hostla konteyner arasında görünməz bir “kabel” var, texniki adı veth cütüdür:
bir ucu konteynerin içindəki eth0, o biri ucu hostdakı bridge-ə taxılıb.
Konteyner həmin daxili şəbəkədən özünə lokal IP alır; məsələn, 172.17.0.5 kimi.
Bu IP yalnız host və eyni bridge-də olan başqa konteynerlər üçündür, kənar dünyadan görünmür.
Hostdan və ya internetdən konteynerə giriş ancaq port dərc edəndə mümkün olur.
-p 8080:80 yazanda hostun 8080 portuna gələn trafiki Docker qaydaları (iptables/NAT) konteynerin 80 portuna yönəldir.
Yəni brauzerdə http://localhost:8080 açanda sorğu hosta düşür, host isə onu konteynerin içindəki 80-ə ötürür.
İstəsən yalnız yerli maşından giriş olsun deyə -p 127.0.0.1:8080:80 yaza bilərsən.
Əksinə bütün interfeyslərdə açmaq üçün -p 0.0.0.0:8080:80 yazılır.
EXPOSE 80 Dockerfile-da yalnız metadata-dır, şəbəkə qaydası yaratmır.
--------------------------------------------------
Docker run parameterləri
-i (interactive)
-i deməkdir ki, konteynerin STDIN kanalı açıq saxlanacaq.
Normalda konteyner içindəki proqram yalnız öz işini görüb çıxır.
Amma sən -i versən, terminaldan yazdığın komandalar konteynerin içindəki prosesə ötürülür. Yəni proseslə “ünsiyyət” qurmaq olur.
--- docker run --rm --env-file .\docker\app.env -p 8080:8080 learn-kazakh-api:latest
məsələn burada -interactive olmadığı üçün sən heçbir command göndərə bilməzsən konteynerin içində işləyən prosesə qarşı.
-i default deyil
Əgər sən -i yazmasan, konteynerin STDIN kanalı bağlıdır. Yəni terminaldan input göndərə bilməyəcəksən.
Amma bir çox servis prosesləri (məsələn, ASP.NET Core API) onsuz da STDIN-dən nəsə gözləmir,
sadəcə başlayıb stdout-a log yazır. Ona görə də -i verməsən belə işləyir.
-t (tty)
-t deməkdir ki, konteyner prosesinə pseudo-TTY (terminal emulyasiyası) verilir.
Bu, real terminal təcrübəsini təmin edir: rənglər, kursor hərəkəti, interaktiv shell davranışı.
docker run -i alpine sh
Bu halda sadəcə sh açılır, amma görünüş “qarışıq” olur, çünki TTY yoxdur.
docker run -it alpine sh
Bu dəfə sanki normal Linux terminalına düşmüş kimi təcrübə alırsan.
TTY olmadan bəzi interaktiv proqramlar (bash, top, nano və s.) işləyə bilmir və ya çox qəribə görünür.
-d və terminal
Buradakı -d detach rejimidir. “Arxa fon” deməkdir.
Komanda terminalı bloklamır, sənə dərhal konteyner ID qaytarır, servis isə fonda işləməyə davam edir.
Əgər detach olmasaydı, Kestrel-in stdout-u birbaşa terminalda qalardı və pəncərə bağlı qalana qədər proses də işləyərdi.
Detach rejimində logları terminala deyil, Docker toplayır; istədiyin an docker logs ilə baxırsan.
--name api konteynerə sabit ad verir. Bu, əmrləri rahatlaşdırır.
ID əvəzinə adla müraciət edirsən: docker logs api, docker exec -it api sh, docker stop api.
Eyni adda ikinci konteyner açıla bilməz; bu, təsadüfi qarışıqlığın qarşısını alır.
--restart unless-stopped servis davranışı verir. Dockerd yenidən başlasa və ya proses qəfil düşsə, Docker konteyneri avtomatik qaldıracaq.
Yalnız sən özün docker stop etmisənsə, “dayandırılıb” statusu yadda saxlanılır və avtomatik qaldırılmır.
Bu, inkişaf mühitində qəfil rebootlardan sonra servisin qalxmasına kömək edir.
İndi bu konteynerlə gündəlik işə baxaq. Əvvəlcə işlədiyini yoxlamaq üçün aktiv siyahını görmək olur:
docker ps
Logları oxumaq üçün:
docker logs api
docker logs -f api
docker logs --tail=100 -f api
Birincisi bu günə kimi toplanan stdout/stderr çıxışını verir.
-f axını canlı izləmək üçündür.
--tail=100 yalnız son 100 sətri göstərərək başlayır, sonra axını izləyir.