Docker Съвети: Почистете машината си от боклука

Docker Съвети: Почистете машината си от боклука

Здравей, Хабър! Представям ви превод на статия "Docker съвети: Почистете локалната си машина" автор Люк Джъгъри.

Днес ще говорим за начина, по който Docker използва дисковото пространство на хост машината, както и как да освободим това пространство от остатъци от неизползвани образи и контейнери.


Docker Съвети: Почистете машината си от боклука

Общо потребление

Docker е страхотно нещо, вероятно малко хора вече се съмняват в това. Само преди няколко години този продукт ни предостави напълно нов начин за изграждане, доставка и стартиране на всяка среда, позволявайки значителна икономия на процесорни ресурси и оперативна памет. В допълнение на това (а за някои дори това ще бъде най-важното) Docker също така ни позволи да опростим и унифицираме управлението на жизнения цикъл на използваните работни среди.

Въпреки това, за всички тези предимства на съвременния живот, трябва да плащаме. Когато стартираме контейнери, сваляме или създаваме собствени образи, разгръщаме сложни екосистеми, трябва да плащаме. И плащаме, включително и с дисково пространство.

Ако никога не сте се замисляли колко пространство реално е заето на вашата машина от Docker, можете да бъдете неприятно изненадани от резултата на тази команда:

$ docker system df

Docker Съвети: Почистете машината си от боклука

Тук е показано използването на диска от Docker в различни аспекти:

  • образи (images) – общия размер на образите, които са свалени от хранилища и построени във вашата система;
  • контейнери (containers) – общия обем дисково пространство, използвано от стартираните контейнери (става въпрос за общия обем на слоевете за четене и запис на всички контейнери);
  • локални тома (local volumes) – обема на локалните хранилища, монтирани към контейнерите;
  • кеш на изграждане (build cache) – временни файлове, генерирани от процеса на изграждане на образи (при използване на инструмента BuildKit, наличен от Docker версия 18.09).

Готов съм да се състезавам, че вече след това просто изброяване вие горите от желание да почистите диска от боклука и да възстановите ценните гигабайти (бележка: особено, ако плащате месечен наем за тези гигабайти).

Използване на диска от контейнери

Всеки път, когато създавате контейнер на хост машината, в каталога /var/lib/docker се създават няколко файла и директории, сред които са важни следните:

  • Каталог /var/lib/docker/containers/ID_контейнера – при използване на стандартния драйвер за логване, точно тук се съхраняват журналите на събитията в JSON формат. Прекалено подробните журнали, както и тези, които никой не чете и не обработва по друг начин, често причиняват преливане на дисковото пространство.
  • Каталог /var/lib/docker/overlay2 – съдържа слоеве за четене и запис на контейнерите (overlay2 – предпочитаният драйвер в повечето дистрибуции на Linux). Ако контейнерът съхранява данни в собствената си файлова система, точно тук те ще бъдат разположени.

Нека си представим система, на която е инсталиран девствено чист Docker, който никога не е бил използван за стартиране на контейнери или изграждане на образи. Отчетът му за използване на дисково пространство ще изглежда така:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         0          0          0B         0B
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Нека стартираме някой контейнер, например, NGINX:

$ docker container run --name www -d -p 8000:80 nginx:1.16

Какво се случва с диска:

  • образите (images) заемат 126 Мб, това е същият NGINX, който стартирахме в контейнера;
  • контейнерите (containers) заемат смешните 2 байта.

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          2B         0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Съдя по изхода, нямаме пространство, което можем да освободим. Тъй като 2 байта са напълно незначителни, нека си представим, че нашият NGINX по непредвидим начин е записал 100 Мегабайта данни и е създал файл test.img точно с такъв размер.

$ docker exec -ti www 
  dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]

Снова изследваме използването на дисково пространство на хоста. Ще видим, че контейнерът (containers) заема 100 Мегабайта.

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          104.9MB    0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Мисля, че вашият любопитен ум вече се пита, къде ли е нашият файл test.img. Нека го потърсим:

$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img

Не вдавайки се в подробности, можем да отбележим, че файлът test.img е удобно разположен на нивото на четене-запис, управлявано от драйвера overlay2. Ако спрем контейнера, хостът ще ни подскаже, че това място може да бъде освободено.

# Stopping the www container
$ docker stop www

# Visualizing the impact on the disk usage
$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          0          104.9MB    104.9MB (100%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Как можем да направим това? Чрез изтриване на контейнера, което ще доведе до почистване на съответното пространство на нивото на четене-запис.

С помощта на следната команда можете да изтриете всички инсталирани контейнери наведнъж и да почистите диска си от всички файлове, създадени от тях на нивото на четене-запис:

$ docker container prune
WARNING! Това ще премахне всички спряни контейнери.
Сигурни ли сте, че искате да продължите? [y/N] y
Изтрити контейнери:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2

Общо освободено пространство: 104.9MB

И така, освободихме 104.9 мегабайта чрез изтриване на контейнера. Но тъй като вече не използваме изтегления образ, той също става кандидат за изтриване и освобождаване на нашите ресурси:

$ docker system df
ТИП           ОБЩО      АКТИВЕН     РАЗМЕР       ОСВОБОДИМО
Образи         1          0          126M       126M (100%)
Контейнери     0          0          0B         0B
Локални обеми  0          0          0B         0B
Кеша на етапа  0          0          0B         0B

Внимание: докато образът се използва от поне един контейнер, няма да можете да използвате този трик.

Субкомандата prune, която използвахме по-горе, дава ефект само на спрени контейнери. Ако искаме да изтрием не само спрени, а и работещи контейнери, трябва да използваме една от тези команди:

# Historical command
$ docker rm -f $(docker ps –aq)

# More recent command
$ docker container rm -f $(docker container ls -aq)

Бележки на полета: ако при стартиране на контейнера използвате параметъра —rm, при спирането му ще бъде освободено всичкото дисково пространство, което той е заемал.

Използване на диска от образи

Преди няколко години размерът на образа от няколко стотици мегабайта беше напълно нормален: образът Ubuntu тежеше 600 мегабайта, а образът на Microsoft .Net – няколко гигабайта. В тези времена изтеглянето на само един образ можеше да нанесе сериозен удар на свободното ви място на диска, дори ако споделяте нива между образите. Днес – слава на великите – образите тежат значително по-малко, но дори и в този случай можете бързо да запълните наличните ресурси, ако не вземете някои мерки за предпазливост.

Има няколко типа образи, които не са директно видими за крайния потребител:

  • Интермедийни образи, на базата на които са събрани други образи, не могат да бъдат изтрити, ако използвате контейнери, базирани на същите "други" образи;
  • Остатъчни образи – това са интермедийни образи, на които не се позовава нито един активен контейнер – те могат да бъдат изтрити.
  • С помощта на следната команда можете да проверите наличието на остатъчни образи в системата си:

$ docker image ls -f dangling=true
REPOSITORY  TAG      IMAGE ID         CREATED             SIZE
none      none   21e658fe5351     12 minutes ago      71.3MB

Можете да ги изтриете по следния начин:

$ docker image rm $(docker image ls -f dangling=true -q)

Можем да използваме и подкомандата prune:

$ docker image prune
WARNING! Това ще премахне всички остатъчни образи.
Сигурни ли сте, че искате да продължите? [y/N] y
Изтрити образи:
изтрит: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
изтрит: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
изтрит: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
изтрит: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
изтрит: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f

Общо възстановено пространство: 12.9kB

Ако случайно искаме да изтрием всички образи (а не само остатъчните) с една команда, можем да направим така:

$ docker image rm $(docker image ls -q)

Използване на диск за томове

Томовете (volumes) се използват за съхранение на данни извън файловата система на контейнера. Например, ако искаме да запазим резултатите от работата на приложение, за да ги използваме по-късно. Чест пример са бази данни.

Нека стартираме контейнер MongoDB, да монтираме към него външен том, и да възстановим от него резервно копие на базата данни (на нас е налично в файла bck.json):

# Running a mongo container
$ docker run --name db -v $PWD:/tmp -p 27017:27017 -d mongo:4.0

# Importing an existing backup (from a huge bck.json file)
$ docker exec -ti db mongoimport 
  --db 'test' 
  --collection 'demo' 
  --file /tmp/bck.json 
  --jsonArray

Данните ще се намират на хост машината в каталога /var/lib/docker/volumes. Но защо не на ниво четене-запис в контейнера? Защото в Dockerfile на образа MongoDB каталогът /data/db (в който MongoDB по подразбиране съхранява данните си) е определен като том (volume).

Docker Съвети: Почистете машината си от боклука

Бележки на полето: много образи, в резултат на които трябва да се създадат данни, използват томове (volumes) за запазване на тези данни.

Когато се наиграем с MongoDB и спрем (или дори изтрием) контейнера, томът няма да бъде изтрит. Той ще продължи да заема нашето ценно дисково пространство, докато не го изтрием изрично с такава команда:

$ docker volume rm $(docker volume ls -q)

Или можем да използваме познатата ни подкоманда prune:

$ docker volume prune
WARNING! Това ще премахне всички локални обеми, които не се използват от поне един контейнер.
Сигурни ли сте, че искате да продължите? [y/N] y
Изтрити обеми:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da

Общо възстановено пространство: 25.82GB
luc@saturn:~$

Използване на дисково пространство за кеша на изграждане на образи

В Docker 18.09 процесът на създаване на образи претърпя някои промени благодарение на инструмента BuildKit. С помощта на този инструмент се увеличава скоростта на процеса, оптимизира се управлението на съхранението на данни и сигурността. Тук няма да разглеждаме всички детайли на този забележителен инструмент, ще се спрем само на това как той засяга въпросите за използването на дисковото пространство.

Нека предположим, че имаме изключително просто приложение Node.Js:

  • файл index.js стартира прост HTTP сървър, който отговаря с текст на всяко получено запитване:
  • файл package.json определя зависимостите, от които се използва само expressjs за стартиране на HTTP сървъра:

$ cat index.js
var express = require('express');
var util    = require('util');
var app = express();
app.get('\\', function(req, res) {
  res.setHeader('Content-Type', 'text/plain');
  res.end(util.format("%s - %s", new Date(), 'Got Request'));
});
app.listen(process.env.PORT || 80);

$ cat package.json
    {
      "name": "testnode",
      "version": "0.0.1",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "dependencies": {
        "express": "^4.14.0"
      }
    }

Dockerfile за изграждане на образа изглежда така:

FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]

Нека изградим образа по обикновения начин, без използване на BuildKit:

$ docker build -t app:1.0 .

Ако проверим използването на дисковото пространство, ще видим, че мястото заемано само от базовия образ (node:13-alpine) и крайния образ (app:1.0):

ТИП           ОБЩО      АКТИВНО     РАЗМЕР       ПОДЛОЖИМО ЗА ВЪЗСТАНОВЯВАНЕ
Образи       2          0          109.3MB    109.3MB (100%)
Контейнери     0          0          0B         0B
Локални обеми  0          0          0B         0B
Кеш на изграждане 0          0          0B         0B

Нека изградим втора версия на нашето приложение, вече с използване на BuildKit. За целта ни е необходимо само да зададем променливата DOCKER_BUILDKIT на 1:

$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .

Ако сега проверим дисковото използване, ще видим, че сега участва кеша на изграждане (build-cache):

$ docker system df
ТИП           ОБЩО      АКТИВНИ     РАЗМЕР      ВЪЗСТАНОВИМ
Образи       2          0          109.3MB    109.3MB (100%)
Контейнери    0          0          0B         0B
Локални томове 0          0          0B         0B
Кеш на билдове 11         0          8.949kB    8.949kB

За да го изчистим, ще използваме следната команда:

$ docker builder prune
ВНИМАНИЕ! Това ще премахне всички висящи кешове на билдове.
Сигурни ли сте, че искате да продължите? [y/N] y
Изтеглени обекти на кеша на билдове:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi

Общо възстановено пространство: 8.949kB

Изчисти всичко!

И така, разгледахме почистването на дисковото пространство, заето от контейнери, образи и томове. В това ни помага подкомандата prune. Но тя може да се използва и на системно ниво docker и ще изчисти всичко, което само може:

$ docker system prune
ВНИМАНИЕ! Това ще премахне:
  - всички спрени контейнери
  - всички мрежи, не използвани от поне един контейнер
  - всички висящи образи
  - всички висящи кешове на билдове

Сигурни ли сте, че искате да продължите? [y/N]

Ако по някаква причина спестявате дисково пространство на машината с Docker, периодичният запуск на тази команда е полезен навик.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster