Что такое Unix Domain Socket: секретный туннель в Linux
В нашем путешествии по миру сокетов мы начали с «верхнего этажа» — WebSocket в браузере, затем спустились к фундаменту интернета — TCP/IP сокетам. Но есть еще один, особый вид сокетов, который живет глубоко в недрах вашего сервера и о котором часто забывают.
Вы наверняка видели в конфигах Nginx или MySQL странные пути, оканчивающиеся на .sock, например /var/run/php/php8.2-fpm.sock. Это не картинка, не текстовый файл и не папка. Это — Unix Domain Socket (UDS). В этой статье я расскажу, почему эти «файлы» являются секретным оружием для ускорения локальной коммуникации.
Аналогия: Почта против Записки
Чтобы понять разницу между TCP-сокетом и Unix-сокетом, представим офис.
- TCP/IP Сокет (127.0.0.1:80): Вы хотите передать документ коллеге, который сидит за соседним столом. Вы кладете документ в конверт, пишете адрес офиса, клеите марку, бросаете в почтовый ящик. Почта сортирует его и приносит обратно в ваш офис вашему коллеге. Это надежно, но бессмысленно долго, если вы в одной комнате.
- Unix Сокет (/tmp/mysql.sock): Вы просто передаете документ коллеге прямо в руки. Никаких конвертов, адресов, марок и почтальонов.
Как это работает технически?
Unix-сокет — это стандартный механизм межпроцессного взаимодействия (IPC) в POSIX-совместимых системах (Linux, macOS). В отличие от сетевых сокетов, использующих IP-адреса и порты, Unix-сокеты используют файловую систему как адресное пространство.
Вот почему они быстрее:
- Нет сетевого оверхеда: Ядру ОС не нужно создавать TCP-пакеты, считать контрольные суммы, управлять порядком пакетов и подтверждением доставки (ACK).
- Прямое копирование памяти: Данные просто копируются из буфера одной программы в буфер другой через ядро.
- Меньше переключений контекста: Процессор выполняет меньше лишних операций.
Безопасность: права доступа как у файлов
Это одно из главных преимуществ. Так как Unix-сокет отображается как файл в файловой системе, к нему применяются стандартные права доступа Linux (chmod, chown).
Например, если у вас есть файл сокета базы данных /tmp/mysql.sock, вы можете настроить права так, что только пользователь www-data (ваш веб-сервер) сможет в него писать. Любой другой пользователь на сервере даже не сможет попытаться подключиться к базе. С TCP-портом (например, 3306) это сделать сложнее — он открыт для всех на localhost.
Практическое применение: где мы это используем?
1. Nginx + PHP-FPM
Это классика. В моей статье про FastCGI мы настраивали связь между веб-сервером и PHP. Вы можете сделать это двумя способами:
| Метод | Конфигурация Nginx | Когда использовать |
|---|---|---|
| TCP Сокет | fastcgi_pass 127.0.0.1:9000; |
Если Nginx и PHP-FPM на разных серверах (распределенная архитектура). |
| Unix Сокет | fastcgi_pass unix:/var/run/php/php-fpm.sock; |
Если они на одном сервере. Дает прирост производительности. |
2. Docker Daemon
Вы когда-нибудь задумывались, как Docker CLI общается с самим Docker движком? По умолчанию это происходит через Unix-сокет /var/run/docker.sock. Именно поэтому, чтобы запустить Docker без sudo, вам нужно добавить пользователя в группу, имеющую права на этот файл-сокет.
3. Базы данных (MySQL, PostgreSQL, Redis)
Если ваш сайт подключается к базе данных на том же сервере («localhost«), использование сокета (localhost:/tmp/mysql.sock) будет быстрее, чем подключение через TCP (127.0.0.1:3306).
Практика: общаемся через файл-сокет на Python
Давайте создадим сервер, который слушает не порт, а файл. Обратите внимание на использование socket.AF_UNIX вместо AF_INET.
Код сервера (unix_server.py)
import socket
import os
socket_path = "/tmp/demo_socket.sock"
# Убедимся, что файл сокета не существует
if os.path.exists(socket_path):
os.remove(socket_path)
# Создаем Unix-сокет (AF_UNIX)
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Привязываем к файлу
server.bind(socket_path)
# Слушаем
server.listen(1)
print(f"Слушаю на {socket_path}")
while True:
connection, client_address = server.accept()
try:
print("Соединение установлено!")
data = connection.recv(1024)
print(f"Получено: {data.decode()}")
connection.sendall(b"Hello from Unix Socket!")
finally:
connection.close()
Клиент для такого сервера выглядит почти так же, только вместо IP и порта он подключается к пути файла:
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect("/tmp/demo_socket.sock")
client.sendall(b"Hello Server!")
Вывод: локальный герой
Unix Domain Sockets — это незаменимый инструмент для оптимизации высокопроизводительных систем. Они убирают лишних посредников в общении программ на одном компьютере.
Если ваши сервисы (веб-сервер, база данных, кэш) живут на одной машине, всегда отдавайте предпочтение Unix-сокетам перед TCP. Это бесплатный и простой способ снизить задержки (latency) и нагрузку на процессор.