797 lines
22 KiB
Markdown
797 lines
22 KiB
Markdown
---
|
||
title: "Ubuntu 24.04: настройка Docker-хоста для production"
|
||
date: 2026-07-29
|
||
draft: false
|
||
description: "Пошаговая настройка Ubuntu 24.04 LTS как Docker-хоста для production: установка Docker, hardening daemon, UFW + Docker интеграция, systemd-resolved и DNS, изоляция контейнеров, docker.sock."
|
||
summary: "Пошаговая настройка Ubuntu 24.04 LTS как Docker-хоста для production: установка Docker, hardening daemon, UFW + Docker интеграция, systemd-resolved и DNS, изоляция контейнеров, docker.sock."
|
||
tags: ["ubuntu 24.04", "docker", "security", "hardening", "ufw", "production", "containers", "linux"]
|
||
categories: ["Безопасность"]
|
||
series: ["Linux Hardening"]
|
||
series_order: 4
|
||
---
|
||
|
||
# Ubuntu 24.04: настройка Docker-хоста для production
|
||
|
||
Статья предполагает что базовый hardening уже сделан по статье: SSH на нестандартном порту, UFW включён, fail2ban настроен. Если нет - сначала туда.
|
||
{{< article link="/posts/ubuntu-24-base-hardening/">}}
|
||
|
||
В этой статье - Docker и всё что нужно чтобы контейнеры не стали дырой в безопасности хоста.
|
||
|
||
**Что получишь на выходе:**
|
||
- Docker установлен из официального репозитория (не snap)
|
||
- daemon.json настроен безопасно - без userns-remap и icc:false по умолчанию
|
||
- UFW и Docker работают вместе
|
||
- systemd-resolved и DNS в контейнерах - рабочее решение
|
||
- docker.sock - понимание риска и контроль доступа
|
||
- Контейнеры изолированы по сетям, ограничены по ресурсам
|
||
- Образы проверяются на уязвимости
|
||
|
||
## Исходные данные
|
||
|
||
- Ubuntu 24.04 LTS с выполненным базовым hardening
|
||
- Минимум 2GB RAM, 20GB диска
|
||
- Пользователь с sudo
|
||
|
||
Проверь версию:
|
||
|
||
```bash
|
||
lsb_release -a
|
||
```
|
||
|
||
```
|
||
Distributor ID: Ubuntu
|
||
Description: Ubuntu 24.04.4 LTS
|
||
Release: 24.04
|
||
Codename: noble
|
||
```
|
||
|
||
> [!info] ВАЖНО!
|
||
> Все команды выполняются с `sudo` если не указано иное.
|
||
> Если работаешь от root - `sudo` можно опустить.
|
||
|
||
## Установка Docker
|
||
|
||
> [!danger] ОЧЕНЬ ВАЖНО!
|
||
> Не используй snap версию в production!
|
||
|
||
В Ubuntu можно поставить Docker через snap (`sudo snap install docker`).
|
||
|
||
Snap-версия Docker работает в собственном confinement-окружении со своими путями (`/var/snap/docker/`), хуже интегрируется с системным `iptables`/`UFW`, и обновляется по графику snap, а не когда нужно тебе. Используй официальный репозиторий Docker - там стандартный путь `/var/lib/docker`, предсказуемое поведение, полный контроль над версией.
|
||
|
||
Проверь что snap-версии нет:
|
||
|
||
```bash
|
||
snap list | grep docker
|
||
```
|
||
|
||
Если есть - удали:
|
||
|
||
```bash
|
||
sudo snap remove docker
|
||
```
|
||
|
||
### Удаление старых версий
|
||
|
||
```bash
|
||
sudo apt remove -y docker docker-engine docker.io containerd runc
|
||
```
|
||
|
||
### Установка из официального репозитория
|
||
|
||
Зависимости
|
||
```bash
|
||
sudo apt install -y ca-certificates curl gnupg lsb-release
|
||
```
|
||
|
||
Создать директорию для ключей
|
||
```bash
|
||
sudo install -m 0755 -d /etc/apt/keyrings
|
||
```
|
||
|
||
Добавить GPG ключ Docker
|
||
```bash
|
||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
|
||
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||
```
|
||
|
||
```bash
|
||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||
```
|
||
|
||
Добавить репозиторий
|
||
```bash
|
||
echo \
|
||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
|
||
https://download.docker.com/linux/ubuntu \
|
||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||
```
|
||
|
||
Обновить индекс и установить
|
||
```bash
|
||
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||
```
|
||
|
||
Проверь версию:
|
||
|
||
```bash
|
||
docker --version
|
||
```
|
||
Ожидаем:
|
||
|
||
```
|
||
# Docker version 29.x.x, build xxxxxxx
|
||
```
|
||
|
||
Проверь daemon:
|
||
|
||
```bash
|
||
sudo systemctl status docker
|
||
```
|
||
|
||
Ожидаем:
|
||
|
||
```bash
|
||
● docker.service - Docker Application Container Engine
|
||
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: enabled)
|
||
Active: active (running) since Wed 2026-07-29 MSK; 3h 38min ago
|
||
TriggeredBy: ● docker.socket
|
||
Docs: https://docs.docker.com
|
||
Main PID: 182611 (dockerd)
|
||
Tasks: 8
|
||
Memory: 23.4M (peak: 24.0M)
|
||
CPU: 2.661s
|
||
CGroup: /system.slice/docker.service
|
||
└─182611 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
|
||
```
|
||
|
||
### Добавление пользователя в группу docker
|
||
|
||
```bash
|
||
sudo usermod -aG docker $USER
|
||
```
|
||
|
||
Выйди и зайди снова (Выполни `newgrp docker` для текущей сессии без перелогина). Проверь:
|
||
|
||
```bash
|
||
docker run --rm hello-world
|
||
```
|
||
|
||
## docker.sock - главный риск
|
||
|
||
Прежде чем настраивать daemon - разберёмся с самым опасным местом.
|
||
|
||
`/var/run/docker.sock` - Unix сокет Docker daemon. Любой процесс с доступом к нему имеет **полный root на хосте**. Через один вызов API можно смонтировать корень хоста и получить туда доступ:
|
||
|
||
```bash
|
||
# Пример атаки - одна команда и ты root на хосте
|
||
docker run --rm -v /:/host alpine chroot /host
|
||
```
|
||
|
||
**Правила работы с docker.sock:**
|
||
|
||
> [!info] ВАЖНО
|
||
> Никогда не монтируй docker.sock в контейнер без явной необходимости.
|
||
|
||
Если сервис требует его (Portainer, Watchtower, Traefik с автообнаружением) - этот сервис имеет root-доступ к хосту.
|
||
|
||
Проверь права на сокет:
|
||
|
||
```bash
|
||
ls -la /var/run/docker.sock
|
||
```
|
||
|
||
```
|
||
srw-rw---- 1 root docker 0 Jul 29 11:38 /var/run/docker.sock
|
||
```
|
||
|
||
Проверь кто в группе docker:
|
||
|
||
```bash
|
||
getent group docker
|
||
```
|
||
|
||
Только доверенные пользователи должны быть в этой группе - фактически это равно правам root.
|
||
|
||
Проверь какие контейнеры монтируют сокет:
|
||
|
||
```bash
|
||
docker ps -q | xargs -I {} docker inspect {} \
|
||
--format '{{.Name}}: {{range .Mounts}}{{if eq .Source "/var/run/docker.sock"}}DOCKER_SOCK_MOUNTED{{end}}{{end}}' \
|
||
2>/dev/null | grep DOCKER_SOCK_MOUNTED
|
||
```
|
||
|
||
## Hardening daemon.json
|
||
|
||
```bash
|
||
sudo nano /etc/docker/daemon.json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"log-driver": "json-file",
|
||
"log-opts": {
|
||
"max-size": "50m",
|
||
"max-file": "3"
|
||
},
|
||
"live-restore": true,
|
||
"userland-proxy": false,
|
||
"no-new-privileges": true,
|
||
"storage-driver": "overlay2",
|
||
"default-ulimits": {
|
||
"nofile": {
|
||
"Name": "nofile",
|
||
"Hard": 65536,
|
||
"Soft": 65536
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Что и почему:**
|
||
|
||
`log-driver` + лимиты - без них логи контейнеров забивают `/var/lib/docker/containers/` за дни на активном сервисе.
|
||
|
||
`live-restore: true` - контейнеры продолжают работать пока daemon перезапускается.
|
||
|
||
`userland-proxy: false` - iptables вместо userland proxy, быстрее, меньше процессов.
|
||
|
||
`no-new-privileges: true` - контейнеры не поднимают привилегии через setuid/setgid бинарники.
|
||
|
||
`storage-driver: overlay2` - зафиксирован явно, хотя на Ubuntu 24.04 он и так используется по умолчанию.
|
||
|
||
### Почему нет userns-remap
|
||
|
||
Root в контейнере мапится на непривилегированный UID на хосте (например 100000). Звучит безопасно, но ломает bind mounts: файлы на хосте принадлежат UID 1000, контейнер видит UID 100000 и получает `Permission denied`. Каждый сервис с записью на хост требует `chown -R 100000:100000`, и пересчёт нужен для каждого нового сервиса.
|
||
|
||
> [!info] ВАЖНО
|
||
> Включай только если осознанно готов управлять UID маппингом для каждого тома.
|
||
|
||
### Почему нет icc:false
|
||
|
||
Глобальный `icc: false` ломает общение контейнеров внутри одного docker-compose стека - Compose создаёт свою bridge сеть, и без `icc` контейнеры в ней не видят друг друга без явных `--link`.
|
||
|
||
Правильная изоляция - через отдельные сети Docker, не через глобальный флаг daemon. Раздел ниже.
|
||
|
||
Примени:
|
||
|
||
```bash
|
||
sudo systemctl restart docker
|
||
```
|
||
|
||
Проверь:
|
||
|
||
```bash
|
||
docker info | grep -E "Storage Driver|Logging Driver|live-restore"
|
||
```
|
||
|
||
## sysctl для Docker-хоста
|
||
|
||
```bash
|
||
sudo nano /etc/sysctl.d/99-docker.conf
|
||
```
|
||
|
||
```
|
||
# IP forwarding - обязательно для Docker
|
||
net.ipv4.ip_forward = 1
|
||
|
||
# IPv6 forwarding - только если используешь IPv6 в контейнерах
|
||
# net.ipv6.conf.all.forwarding = 1
|
||
|
||
# Лимиты inotify
|
||
fs.inotify.max_user_instances = 512
|
||
fs.inotify.max_user_watches = 524288
|
||
|
||
# Для Java приложений (Elasticsearch и подобных) в контейнерах
|
||
vm.max_map_count = 262144
|
||
```
|
||
|
||
```bash
|
||
sudo sysctl -p /etc/sysctl.d/99-docker.conf
|
||
```
|
||
|
||
## systemd-resolved и DNS в контейнерах
|
||
|
||
Это самая частая проблема на Ubuntu специфично.
|
||
Ubuntu 24.04 использует `systemd-resolved` - DNS резолвер слушает на `127.0.0.53`. Контейнер не может достать этот адрес - `127.0.0.53` это loopback хоста, контейнер находится в своём network namespace и видит свой собственный loopback, не хостовый.
|
||
|
||
Симптом:
|
||
|
||
```bash
|
||
docker run --rm alpine nslookup google.com
|
||
# ;; connection timed out; no servers could be reached
|
||
```
|
||
|
||
**Решение 1 (быстрое, для отдельных контейнеров):**
|
||
|
||
```bash
|
||
docker run --dns 8.8.8.8 --dns 8.8.4.4 alpine nslookup google.com
|
||
```
|
||
|
||
**Решение 2 (для всех контейнеров через daemon.json):**
|
||
|
||
```bash
|
||
sudo nano /etc/docker/daemon.json
|
||
```
|
||
|
||
Добавь:
|
||
|
||
```json
|
||
{
|
||
"dns": ["8.8.8.8", "8.8.4.4"]
|
||
}
|
||
```
|
||
|
||
```bash
|
||
sudo systemctl restart docker
|
||
```
|
||
|
||
**Решение 3 (если нужен внутренний DNS, не публичный):**
|
||
|
||
Узнай реальный upstream DNS на хосте:
|
||
|
||
```bash
|
||
resolvectl status | grep "DNS Server"
|
||
```
|
||
|
||
Используй именно его IP (не `127.0.0.53`) в `daemon.json`.
|
||
|
||
Docker Compose поддерживает то же самое на уровне сервиса:
|
||
|
||
```yaml
|
||
services:
|
||
app:
|
||
dns:
|
||
- 8.8.8.8
|
||
- 8.8.4.4
|
||
```
|
||
|
||
## UFW + Docker интеграция
|
||
|
||
Та же проблема что и на любом дистрибутиве - Docker пишет свои iptables правила напрямую, обходя UFW.
|
||
|
||
**Подход 1 (рекомендуемый): порты только на localhost**
|
||
|
||
```bash
|
||
# Плохо - обходит UFW
|
||
docker run -p 80:80 nginx
|
||
|
||
# Хорошо
|
||
docker run -p 127.0.0.1:80:80 nginx
|
||
```
|
||
|
||
Reverse proxy на хосте слушает 0.0.0.0:443, UFW разрешает только 443, контейнеры недоступны напрямую.
|
||
|
||
**Подход 2: DOCKER-USER chain**
|
||
|
||
```bash
|
||
sudo nano /etc/ufw/after.rules
|
||
```
|
||
|
||
Добавь в конец:
|
||
|
||
```
|
||
# BEGIN UFW AND DOCKER
|
||
*filter
|
||
:DOCKER-USER - [0:0]
|
||
|
||
-A DOCKER-USER -s 10.0.0.0/8 -j RETURN
|
||
-A DOCKER-USER -s 172.16.0.0/12 -j RETURN
|
||
-A DOCKER-USER -s 192.168.0.0/16 -j RETURN
|
||
|
||
-A DOCKER-USER -j ufw-user-forward
|
||
|
||
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
|
||
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
|
||
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
|
||
|
||
-A DOCKER-USER -j RETURN
|
||
COMMIT
|
||
# END UFW AND DOCKER
|
||
```
|
||
|
||
```bash
|
||
sudo ufw reload
|
||
```
|
||
|
||
Для большинства случаев достаточно **подхода 1**.
|
||
|
||
## Сетевая изоляция контейнеров
|
||
|
||
Вместо глобального `icc: false` - отдельные сети под каждое приложение:
|
||
|
||
```bash
|
||
docker network create --driver bridge app_network
|
||
|
||
docker run -d --name app --network app_network myapp
|
||
docker run -d --name db --network app_network postgres
|
||
```
|
||
|
||
Контейнеры из разных сетей не видят друг друга. В docker-compose сети создаются автоматически для каждого стека - это и есть рабочая изоляция.
|
||
|
||
## Безопасность образов
|
||
|
||
### Trivy
|
||
|
||
```bash
|
||
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
|
||
gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
|
||
|
||
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] \
|
||
https://aquasecurity.github.io/trivy-repo/deb \
|
||
$(lsb_release -sc) main" | \
|
||
sudo tee /etc/apt/sources.list.d/trivy.list > /dev/null
|
||
|
||
sudo apt update
|
||
sudo apt install -y trivy
|
||
```
|
||
|
||
```bash
|
||
trivy image nginx:alpine
|
||
```
|
||
|
||
Перед деплоем:
|
||
|
||
```bash
|
||
trivy image --exit-code 1 --severity CRITICAL,HIGH nginx:alpine
|
||
```
|
||
|
||
Правило: CRITICAL уязвимости - образ не идёт в production.
|
||
|
||
### Выбор образов
|
||
|
||
```dockerfile
|
||
# Большой attack surface
|
||
FROM ubuntu:24.04
|
||
|
||
# Минимальный
|
||
FROM alpine:3.20
|
||
|
||
# Ещё меньше - нет shell, нет пакетного менеджера
|
||
FROM gcr.io/distroless/static-debian12
|
||
```
|
||
|
||
Конкретные теги, не `latest`:
|
||
|
||
```bash
|
||
docker pull nginx:1.27-alpine
|
||
```
|
||
|
||
## Изоляция контейнеров
|
||
|
||
### Capabilities
|
||
|
||
```bash
|
||
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx
|
||
```
|
||
|
||
### Read-only filesystem
|
||
|
||
```bash
|
||
docker run --read-only --tmpfs /tmp --tmpfs /var/run nginx
|
||
```
|
||
|
||
### Seccomp
|
||
|
||
Docker применяет дефолтный seccomp профиль автоматически:
|
||
|
||
```bash
|
||
docker info | grep seccomp
|
||
```
|
||
|
||
### Никогда не использовать `--privileged`
|
||
|
||
```bash
|
||
# НИКОГДА в production - отключает всю изоляцию
|
||
docker run --privileged nginx
|
||
```
|
||
|
||
## Ресурсные ограничения
|
||
|
||
```bash
|
||
# Memory с soft limit
|
||
docker run -m 512m --memory-reservation 256m nginx
|
||
|
||
# CPU
|
||
docker run --cpus="0.5" nginx
|
||
|
||
# PID limit - защита от fork bomb
|
||
docker run --pids-limit 200 nginx
|
||
```
|
||
|
||
Проверка OOM:
|
||
|
||
```bash
|
||
dmesg | grep -i "oom\|killed process"
|
||
docker inspect CONTAINER --format='{{.State.OOMKilled}}'
|
||
```
|
||
|
||
## Docker Compose с hardening
|
||
|
||
```yaml
|
||
services:
|
||
web:
|
||
image: nginx:1.27-alpine
|
||
container_name: web
|
||
restart: unless-stopped
|
||
|
||
ports:
|
||
- "127.0.0.1:8080:80"
|
||
|
||
dns:
|
||
- 8.8.8.8
|
||
- 8.8.4.4
|
||
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: "0.5"
|
||
memory: 256M
|
||
reservations:
|
||
memory: 128M
|
||
|
||
cap_drop:
|
||
- ALL
|
||
cap_add:
|
||
- NET_BIND_SERVICE
|
||
read_only: true
|
||
tmpfs:
|
||
- /tmp
|
||
- /var/cache/nginx
|
||
- /var/run
|
||
|
||
security_opt:
|
||
- no-new-privileges:true
|
||
|
||
pids_limit: 100
|
||
|
||
logging:
|
||
driver: "json-file"
|
||
options:
|
||
max-size: "50m"
|
||
max-file: "3"
|
||
|
||
networks:
|
||
- frontend
|
||
|
||
db:
|
||
image: postgres:16-alpine
|
||
container_name: db
|
||
restart: unless-stopped
|
||
|
||
environment:
|
||
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
|
||
|
||
volumes:
|
||
- db_data:/var/lib/postgresql/data
|
||
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: "1.0"
|
||
memory: 512M
|
||
|
||
cap_drop:
|
||
- ALL
|
||
cap_add:
|
||
- SETUID
|
||
- SETGID
|
||
- DAC_OVERRIDE
|
||
- CHOWN
|
||
security_opt:
|
||
- no-new-privileges:true
|
||
|
||
pids_limit: 200
|
||
|
||
logging:
|
||
driver: "json-file"
|
||
options:
|
||
max-size: "50m"
|
||
max-file: "3"
|
||
|
||
networks:
|
||
- backend
|
||
|
||
app:
|
||
image: myapp:1.2.3
|
||
container_name: app
|
||
restart: unless-stopped
|
||
|
||
networks:
|
||
- frontend
|
||
- backend
|
||
|
||
depends_on:
|
||
- db
|
||
|
||
cap_drop:
|
||
- ALL
|
||
security_opt:
|
||
- no-new-privileges:true
|
||
|
||
pids_limit: 100
|
||
|
||
logging:
|
||
driver: "json-file"
|
||
options:
|
||
max-size: "50m"
|
||
max-file: "3"
|
||
|
||
networks:
|
||
frontend:
|
||
driver: bridge
|
||
backend:
|
||
driver: bridge
|
||
internal: true
|
||
|
||
volumes:
|
||
db_data:
|
||
|
||
secrets:
|
||
db_password:
|
||
file: ./secrets/db_password.txt
|
||
```
|
||
|
||
`internal: true` для backend - база данных без доступа к интернету.
|
||
|
||
## Регулярные задачи обслуживания
|
||
|
||
```bash
|
||
# Что занимает место
|
||
docker system df
|
||
|
||
# Очистка неиспользуемого
|
||
docker system prune -f
|
||
|
||
# Полная очистка включая неиспользуемые образы
|
||
docker system prune -a -f
|
||
|
||
# Volumes - ОСТОРОЖНО, удаляет данные безвозвратно
|
||
docker volume prune -f
|
||
```
|
||
|
||
Cron:
|
||
|
||
```bash
|
||
sudo crontab -e
|
||
```
|
||
|
||
```
|
||
0 4 * * 0 docker system prune -f >> /var/log/docker-prune.log 2>&1
|
||
```
|
||
|
||
Метрики:
|
||
|
||
```bash
|
||
docker stats --no-stream
|
||
```
|
||
|
||
### Аудит через auditd
|
||
|
||
```bash
|
||
sudo apt install -y auditd
|
||
```
|
||
```bash
|
||
sudo nano /etc/audit/rules.d/docker.rules
|
||
```
|
||
|
||
```
|
||
-w /usr/bin/docker -p rwxa -k docker
|
||
-w /var/lib/docker -p rwxa -k docker
|
||
-w /etc/docker -p rwxa -k docker
|
||
-w /var/run/docker.sock -p rwxa -k docker
|
||
```
|
||
|
||
```bash
|
||
sudo systemctl enable auditd
|
||
```
|
||
```bash
|
||
sudo systemctl restart auditd
|
||
```
|
||
|
||
```bash
|
||
sudo ausearch -k docker | tail -20
|
||
```
|
||
|
||
## Troubleshooting
|
||
|
||
### DNS не работает в контейнере
|
||
|
||
См. раздел "systemd-resolved и DNS в контейнерах" выше - это специфика Ubuntu, самая частая проблема на этом дистрибутиве.
|
||
|
||
---
|
||
|
||
### Docker обходит UFW
|
||
|
||
**Причина:** Docker пишет напрямую в iptables.
|
||
|
||
**Решение:** `127.0.0.1:port:port` или DOCKER-USER chain.
|
||
|
||
---
|
||
|
||
### Permission denied на bind mount
|
||
|
||
**Причина:** UID процесса в контейнере не совпадает с владельцем файлов на хосте.
|
||
|
||
```bash
|
||
docker run --rm IMAGE id
|
||
```
|
||
```bash
|
||
sudo chown -R UID:GID /path/to/mount
|
||
```
|
||
|
||
---
|
||
|
||
### OOM killer убивает контейнер
|
||
|
||
```bash
|
||
dmesg | grep -i "oom\|killed process"
|
||
```
|
||
```bash
|
||
docker inspect CONTAINER --format='{{.State.OOMKilled}}'
|
||
```
|
||
|
||
Увеличить `memory` limit или оптимизировать приложение.
|
||
|
||
---
|
||
|
||
### newgrp docker не сработал после usermod
|
||
|
||
**Симптом:** `docker: permission denied` несмотря на `usermod -aG docker $USER`.
|
||
|
||
**Причина:** Группа применяется только к новым сессиям. Текущая сессия не знает о изменении.
|
||
|
||
**Решение:**
|
||
|
||
```bash
|
||
newgrp docker
|
||
```
|
||
|
||
Или просто выйти и зайти заново по SSH.
|
||
|
||
---
|
||
|
||
### Диск забит логами
|
||
|
||
```bash
|
||
sudo find /var/lib/docker/containers/ -name "*.log" -exec du -sh {} \; | sort -rh | head -10
|
||
```
|
||
|
||
Экстренно:
|
||
|
||
```bash
|
||
sudo truncate -s 0 /var/lib/docker/containers/CONTAINER_ID/CONTAINER_ID-json.log
|
||
```
|
||
|
||
Постоянно - `max-size`/`max-file` в daemon.json (уже настроено).
|
||
|
||
### Финальная проверка
|
||
```bash
|
||
sudo systemctl status docker
|
||
docker info | grep -E "Storage Driver|Logging Driver|live-restore"
|
||
docker run --rm alpine nslookup google.com
|
||
ss -tulnp | grep docker
|
||
docker system df
|
||
```
|
||
|
||
## Отличия от Debian 12
|
||
|
||
| Аспект | Debian 12 | Ubuntu 24.04 |
|
||
|--------|-----------|--------------|
|
||
| Snap версия Docker | Не существует | Существует, избегать |
|
||
| DNS resolver | Обычно прямой `/etc/resolv.conf` | systemd-resolved (127.0.0.53), требует явной настройки DNS в Docker |
|
||
| Группа docker | `newgrp docker` работает | `newgrp docker` работает, та же команда |
|
||
| UFW | Нужно устанавливать | Предустановлен |
|
||
| GPG ключ репозитория Docker | `download.docker.com/linux/debian/gpg` | `download.docker.com/linux/ubuntu/gpg` |
|
||
|
||
## Что дальше
|
||
|
||
Docker-хост готов к production нагрузке.
|
||
|
||
- Reverse proxy - Traefik или nginx
|
||
- Мониторинг - Prometheus + cAdvisor + Grafana
|
||
- Централизованные логи - Loki + Promtail
|
||
- Секреты - HashiCorp Vault или Docker Secrets
|
||
|
||
**Стек этой статьи:** Ubuntu 24.04 LTS (Noble Numbat) · Docker CE · UFW · systemd-resolved · auditd · Trivy · docker-compose
|