1. Какво всъщност е Docker Container
Docker Container е runtime инстанция на Docker Image. Това определение е кратко, но зад него стои доста повече. Container има собствен process namespace, filesystem view, network namespace и runtime state, а Docker Engine управлява неговия жизнен цикъл.
Важно е да не смесвам трите различни неща:
- Image – неизменяемият шаблон;
- Container – стартираната runtime инстанция;
- Volume – мястото, където мога да държа постоянни данни.
шаблон
създаване
runtime обект
2. Жизнен цикъл на Container
Един от най-честите източници на объркване е, че docker stop, docker start,
docker restart, docker rm и docker run не са взаимозаменяеми.
Всеки от тях работи с различно състояние на container.
създаден, но не е стартиран
процесът работи
container съществува, но процесът е спрян
container вече не съществува
Това разграничение е особено важно при диагностика. Stopped container не е изтрит container. Мога да го стартирам отново, да разгледам logs и да проверя неговата конфигурация.
3. docker run – създаване и стартиране
Най-познатата команда е:
docker run nginx:alpine
Това не е просто „стартирай image“. Ако няма подходящ container, Docker създава нов, конфигурира го и след това го стартира.
Например:
docker run -d \
--name web \
-p 8080:80 \
nginx:alpine
Тук имам няколко отделни действия в една команда:
-d– стартира container в background;--name web– задава име;-p 8080:80– публикува host порт 8080 към container порт 80;nginx:alpine– image, от който се създава container.
-p 8080:80 не означава, че приложението „слуша на 8080“ вътре в container-а.
То означава host порт 8080 → container порт 80.
4. docker create и docker start
Ако искам да разделя създаването от стартирането, използвам:
docker create \
--name web \
-p 8080:80 \
nginx:alpine
Сега container съществува, но не работи. Мога да проверя това:
docker ps
docker ps -a
След това:
docker start web
На практика docker run е удобният shortcut за типичния сценарий,
докато create и start ми дават по-ясен контрол върху отделните стъпки.
5. docker ps – какво реално работи
Първата команда, която използвам при проблем с containers, е:
docker ps
Тя показва само работещите containers. Ако искам и спряните:
docker ps -a
Това е дребна, но важна разлика.
Когато някой каже „container-ът го няма“, първо проверявам с docker ps -a.
Може просто да е stopped.
6. stop, kill и restart – не са едно и също
6.1. docker stop
docker stop изпраща сигнал към основния процес и му дава възможност да приключи нормално.
docker stop web
Това е нормалният ми избор за спиране на container.
6.2. docker kill
docker kill е по-грубо средство.
Използвам го, когато процесът не реагира нормално или имам конкретна причина да прекратя container-а веднага.
docker kill web
6.3. docker restart
Когато искам да спра и веднага да стартирам отново:
docker restart web
7. docker rm – кога container-ът наистина изчезва
docker rm премахва container-а.
docker rm web
Ако container-ът все още работи, Docker по подразбиране няма да го премахне. Мога да използвам:
docker rm -f web
Това вече е принудително действие. Не го използвам механично, защото при production системи предпочитам първо да разбера какво ще изгубя.
8. docker logs – първата линия на диагностика
Ако container не се държи както очаквам, гледам logs:
docker logs web
За следене в реално време:
docker logs -f web
За последните 100 реда:
docker logs --tail 100 web
А когато искам timestamp:
docker logs -t --tail 100 web
docker logs е един от най-бързите начини да разбера какво се случва вътре.
9. docker inspect – конфигурацията, а не само status
docker ps ми казва какво работи.
docker inspect ми показва как е конфигуриран конкретният container.
docker inspect web
Това връща JSON структура с много информация – например:
- container ID;
- image;
- state;
- environment;
- mounts;
- network settings;
- published ports;
- restart policy;
- entrypoint и command.
При нужда мога да извлека само конкретно поле:
docker inspect web \
--format '{{.State.Status}}'
Или IP адреса:
docker inspect web \
--format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
10. docker exec – влизане в работещ Container
Когато трябва да проверя какво се случва вътре в работещ container, използвам docker exec.
docker exec -it web /bin/sh
Ако image-ът съдържа Bash:
docker exec -it web /bin/bash
Това не е SSH.
Аз изпълнявам процес вътре в namespace-овете на съществуващия container.
Ако container-ът няма shell, docker exec няма магически да му добави такъв.
docker exec е чудесен за диагностика, но не е начин за изграждане на deployment.
11. Ports – как container-ът става достъпен отвън
Container има собствен network namespace. Това означава, че порт 80 вътре в container не означава автоматично порт 80 на host системата.
docker run -d \
--name web \
-p 8080:80 \
nginx:alpine
Сега заявка към:
http://localhost:8080
достига до порт 80 на nginx вътре в container-а.
| Запис | Значение |
|---|---|
-p 8080:80 |
Host 8080 → Container 80 |
-p 127.0.0.1:8080:80 |
Достъп само през localhost на host-а |
-p 8080:80/udp |
UDP mapping |
12. Writable layer срещу Volume
Всеки container има writable layer, но това не го прави подходящо място за постоянни данни. При databases, uploads, документи и други важни файлове използвам volume или bind mount.
docker volume create app-data
docker run -d \
--name app \
-v app-data:/var/lib/app \
myapp:1.0
При bind mount:
docker run -d \
--name app \
-v /srv/app/data:/var/lib/app \
myapp:1.0
Разликата между named volume и bind mount е важна за поддръжката, backup стратегията и начина, по който управлявам данните.
13. Environment variables
Конфигурацията на приложението често се подава чрез environment variables:
docker run -d \
--name app \
-e APP_ENV=production \
-e APP_PORT=8080 \
myapp:1.0
Проверка:
docker inspect app \
--format '{{range .Config.Env}}{{println .}}{{end}}'
-e DB_PASSWORD=... като универсален начин за работа със secrets.
При чувствителни данни използвам подходящ механизъм за secrets според конкретната инфраструктура.
14. Restart Policy – кога Docker стартира Container отново
Това е особено важно при сървъри. Ако container трябва да се стартира отново след crash или reboot, задавам подходяща restart policy.
docker run -d \
--name app \
--restart unless-stopped \
myapp:1.0
Най-често срещаните политики са:
| Policy | Поведение |
|---|---|
no |
Без автоматичен restart. |
on-failure |
Restart при ненулев exit code. |
always |
Docker се опитва да стартира container-а отново. |
unless-stopped |
Restart, освен ако container-ът не е бил умишлено спрян. |
Това е и причината при планирано спиране да обръщам внимание как е конфигуриран container-ът. „Спрях го“ и „няма да се стартира след reboot“ не са непременно едно и също нещо.
15. Exit Code – защо Container-ът е спрял
Container-ът живее, докато основният му процес живее. Когато този процес приключи, container-ът преминава в exited state.
docker ps -a
docker inspect app \
--format '{{.State.ExitCode}}'
Exit code 0 обикновено означава нормално приключване, докато ненулев код показва грешка. Самият exit code обаче не е достатъчна диагноза – следва да се видят logs и command/configuration.
16. HEALTHCHECK – работи ли приложението, а не само процесът
Running container не означава непременно healthy application. Процесът може да е жив, но приложението да не отговаря правилно.
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -q --spider http://127.0.0.1:8080/ || exit 1
След това мога да видя health състоянието:
docker inspect app \
--format '{{json .State.Health}}'
Това е важна граница между „процесът не е умрял“ и „услугата реално работи“.
17. Containers и Docker Network
Не е необходимо containers да комуникират през публикувани host портове. При една и съща Docker network те могат да използват собствената вътрешна мрежа.
docker network create app-net
docker run -d \
--name db \
--network app-net \
postgres:17
docker run -d \
--name app \
--network app-net \
myapp:1.0
В такъв сценарий приложението може да използва името db като hostname.
Не е необходимо PostgreSQL портът да бъде публикуван към host-а само заради комуникацията между двата containers.
18. docker cp – копиране на файлове
За бърза диагностика или извличане на файл мога да използвам:
docker cp app:/var/log/app.log ./app.log
И обратно:
docker cp ./config.yml app:/etc/app/config.yml
Това е удобно за временна работа, но не го използвам като заместител на правилно конфигуриран volume, bind mount или deployment процес.
19. docker commit – защо не го използвам като deployment метод
Docker може да създаде image от текущото състояние на container:
docker commit app myapp:debug
Технически това работи.
Но ако променя production container ръчно и след това направя commit,
получавам image, чийто build история вече не е ясно описана в Dockerfile.
За възпроизводими deployment-и предпочитам:
Dockerfile
↓
docker build
↓
Image
↓
Registry
↓
docker pull
↓
Container
20. Моят ред за диагностика
Когато container не работи, не започвам с docker rm -f.
Това е последното, не първото действие.
Какво е състоянието?
Какво казва приложението?
Как е конфигуриран container-ът?
Какво се случва вътре?
Има ли външна причина?
След това вече решавам дали проблемът е в image, command, configuration, network, volume, permissions или самото приложение.
21. Типични грешки
- Да се мисли, че stopped container е изтрит.
- Да се използва
docker rm -fпреди диагностика. - Да се пазят важни данни само в writable layer.
- Да се публикуват всички вътрешни портове към host-а.
- Да се използва
docker execза постоянна конфигурация. - Да се приема, че running container означава healthy application.
- Да се използва
restartза прикриване на проблем, вместо за реално управление. - Да се смесва lifecycle на container с lifecycle на Image.
22. Най-важното от тази част
- Container е runtime инстанция на Image.
docker runобикновено създава и стартира нов container.docker createсъздава, аdocker startстартира вече съществуващ container.docker stopспира нормално, докатоdocker killпрекратява по-грубо.docker rmпремахва container-а.docker logsиdocker inspectса основни диагностични инструменти.docker execе за работа вътре в работещ container, не за постоянна конфигурация.- Persistent data трябва да бъде отделена чрез volumes или bind mounts.
- Container network позволява вътрешна комуникация без излишно публикуване на host портове.
- Restart policy трябва да бъде избрана съзнателно.
- Running не означава автоматично healthy.
Заключение
Docker Container е временна runtime среда, а не виртуална машина в малък пакет. Той споделя kernel-а на host системата и използва Linux namespaces, cgroups и filesystem механизми, за да изолира процесите и ресурсите.
Именно затова container-ите са толкова леки и бързи, но и затова трябва да се разбира какво реално се случва. Когато знам разликата между Image, Container, Volume и Network, командите на Docker престават да бъдат набор от магически параметри.
За мен това е и границата между „използвам Docker“ и „разбирам Docker“. В първия случай запомням команди. Във втория знам какво променям с всяка команда и какви последствия има това.
В следващата част ще премина към Docker Volumes и persistent data – къде реално живеят данните, какво е named volume, какво е bind mount, как се прави backup и защо database без правилно storage управление е покана за неприятности.