ERROR 2002 (HY000) MySQL 소켓 연결 실패, 5분 원인별 해결 가이드
배포 중이거나 새벽에 알림을 받고 서버에 붙었는데 이 한 줄이 떠 있으면 등골이 서늘합니다.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)이 글은 추측 없이 위에서부터 순서대로 명령을 복붙해 5분 안에 원인을 좁히고 복구하는 실전 매뉴얼입니다. ERROR 1045(인증)·1205(락)·HikariPool(커넥션풀)은 다루지 않고, 오직 소켓 레이어 문제만 집중해서 끝냅니다.
1. 에러 원문부터 읽기: "소켓이냐 TCP냐"
가장 먼저 알아야 할 핵심 통찰입니다. mysql 클라이언트는 호스트를 지정하지 않거나 -h localhost로 붙으면 TCP가 아니라 유닉스 도메인 소켓 파일로 접속을 시도합니다. 즉 위 에러는 "포트가 막혔다"가 아니라 "소켓 파일이 없거나 못 읽었다"는 뜻입니다.
괄호 안 숫자가 결정적 단서입니다.
| 표시 | errno | 의미 | 방향 |
|---|---|---|---|
(2) | ENOENT | 파일이 없음 | 서비스 미기동 / 경로 불일치 |
(13) | EACCES | 파일은 있는데 권한 거부 | 소유권·AppArmor·SELinux |
그리고 비슷한 에러들과 헷갈리지 않게 한 줄로 정리합니다.
| 에러 | 레이어 | 의미 |
|---|---|---|
ERROR 2002 ... through socket ... (2) | 유닉스 소켓 | 소켓 파일 없음 |
ERROR 2003 ... can't connect ... (111) | TCP 3306 | 포트로 붙는데 거부됨 |
... through socket ... Connection refused | 소켓 | 파일은 있으나 서버가 안 받음 |
2. 원인 분기표: 60초에 후보 좁히기
| # | 원인 | 대표 증상 |
|---|---|---|
| ① | mysqld 서비스 미기동/크래시 | 소켓 파일 자체가 없음 (2) |
| ② | 잘못된 소켓 경로 (my.cnf 불일치) | 서비스는 떴는데 클라이언트가 엉뚱한 경로 탐색 |
| ③ | 권한·소유권·AppArmor | 파일은 있는데 (13) Permission denied |
| ④ | TCP를 써야 하는 환경 | 원격/컨테이너 분리 구성 |
| ⑤ | Docker 컨테이너 내부 소켓 부재 | 앱 컨테이너에 mysqld가 없음 |
| ⑥ | 디스크풀·stale 소켓 잔존 | 크래시 후 .sock이 남아 재기동 실패 |
원칙: 위에서 아래로. ①~③을 건너뛰고 ④ TCP로 우회하면 당장은 붙어도 같은 문제가 반복됩니다.
3. 진단 명령 모음 (복붙용)
(1) 서비스가 살아있나
systemctl status mysql # 또는 mariadb
journalctl -u mysql -n 50 --no-pager정상이면 Active: active (running)이 보입니다. 죽었다면 ①번 확정.
(2) 진짜 응답하나 — 핑
mysqladmin ping
# mysqld is alive
mysqladmin -h 127.0.0.1 -P 3306 ping # TCP로도 확인(3) 소켓이 실제로 리스닝 중인가
ss -lx | grep mysql
# u_str LISTEN 0 70 /var/run/mysqld/mysqld.sock 12345 * 0
ss -ltnp | grep 3306ss -lx에 소켓이 안 보이면 서버가 소켓을 안 열고 있는 것입니다.
(4) 클라이언트가 기대하는 경로 vs 서버가 만든 경로 대조 — ②번 핵심
mysqld --verbose --help | grep '^socket' # 서버가 만드는 경로
my_print_defaults mysql mysqld | grep socket # 설정 파일 실제 값
find / -name 'mysqld.sock' 2>/dev/null # 실제 파일 위치세 값이 서로 다르면 ②번 확정입니다.
(5) 권한·AppArmor·디스크
ls -la /var/run/mysqld/
aa-status | grep mysqld
dmesg | grep -i denied
df -h /var /tmp4. 원인별 처방전
① 서비스 기동 / 크래시 복구
sudo systemctl start mysql && systemctl is-active mysql크래시라면 로그에서 [ERROR] 라인을 추적하세요.
journalctl -u mysql -n 100 --no-pager | grep -i errormysqld_safe --skip-grant-tables는 인증 무력화이므로 마지막 수단으로만, 복구 직후 즉시 정상 재기동해야 합니다.
② 소켓 경로 정렬 (가장 흔한 진짜 원인)
[mysqld]가 만드는 경로와 [client]/[mysql]이 찾는 경로를 반드시 동일하게 맞춥니다. /etc/mysql/my.cnf:
[mysqld]
socket = /var/run/mysqld/mysqld.sock
[client]
socket = /var/run/mysqld/mysqld.sock
[mysql]
socket = /var/run/mysqld/mysqld.sock급할 때 임시 우회로 심볼릭 링크도 가능합니다(단, 재기동 시 사라질 수 있으니 근본 해결은 위 설정으로):
sudo ln -s /tmp/mysql.sock /var/run/mysqld/mysqld.sock③ 권한·소유권·AppArmor
(13)이면 소유권부터:
sudo chown mysql:mysql /var/run/mysqld && sudo chmod 755 /var/run/mysqldAppArmor/SELinux가 막는 경우(최근 배포판 기본 정책이 강해졌습니다):
sudo aa-complain /usr/sbin/mysqld # 또는 프로파일에 소켓 경로 추가⚠️ chmod 777 mysqld.sock은 보안 사고로 가는 지름길이니 절대 금지.
④ TCP로 전환 — localhost vs 127.0.0.1의 결정적 차이
이 글의 핵심입니다. localhost는 유닉스 소켓, 127.0.0.1은 TCP로 붙습니다. 소켓이 말썽이면 TCP로 우회하세요.
mysql -h 127.0.0.1 -P 3306 --protocol=TCP -u user -p애플리케이션 설정도 동일 원리입니다.
JDBC: jdbc:mysql://127.0.0.1:3306/db
PHP PDO: mysql:host=127.0.0.1;port=3306;dbname=dbPHP에서 host=localhost를 쓰면 PDO가 소켓으로 붙는다는 점이 ERROR 2002의 단골 원인입니다.
⑤ Docker 환경
컨테이너는 systemd 환경과 다릅니다. 앱 컨테이너 안에는 mysqld 소켓이 존재하지 않습니다. 따라서 소켓이 아니라 서비스명으로 TCP 접속해야 합니다.
# docker-compose: db 서비스로 TCP 접속
mysql -h db -P 3306 -u root -p
# 같은 호스트라면 소켓 볼륨 마운트도 가능
# -v /var/run/mysqld:/var/run/mysqld
# 컨테이너 내부 직접 접속 (이건 소켓이 존재)
docker exec -it mysql mysql -u root -prootless 컨테이너에서는 소켓 마운트 권한 문제가 추가로 생기니 가급적 TCP 구성을 권장합니다.
⑥ 디스크풀 / stale 소켓
df -h /var /tmp # 100% 이면 정리 먼저
sudo rm /var/run/mysqld/mysqld.sock # 크래시 잔존 소켓 제거
sudo systemctl restart mysql실무 한마디
현장에서 ERROR 2002의 70%는 사실 ②번(경로 불일치)과 ④번(localhost/127.0.0.1 혼용)입니다. 한 번은 PHP 앱은 localhost(소켓), 배치 스크립트는 127.0.0.1(TCP)로 붙어 있어 "어떤 건 되고 어떤 건 안 되는" 유령 같은 장애를 며칠 쫓은 적이 있습니다. 결론은 단순합니다. 팀 전체가 접속 방식을 하나로 통일(소켓이면 소켓, TCP면 127.0.0.1)하면 이 에러는 거의 사라집니다.
4단 체크리스트 & 안티패턴
복구 순서를 외우세요:
- 서비스 살아있나 (
systemctl status) - 경로 맞나 (
mysqld --verbose --helpvsmy_print_defaults) - 권한 맞나 (
ls -la, errno(13)) - TCP로 우회 가능한가 (
-h 127.0.0.1)
피해야 할 안티패턴:
- ❌ 무작정
chmod 777 mysqld.sock— 보안 위험 - ❌ 원인 모른 채
--skip-grant-tables상시 운영 - ❌ stale 소켓 안 지우고 재기동만 반복
- ❌
localhost/127.0.0.1혼용으로 같은 에러 무한 반복
자주 묻는 질문 (FAQ)
Q. 왜 localhost는 안 되고 127.0.0.1은 되나요?
A. localhost는 유닉스 소켓 파일로, 127.0.0.1은 TCP 3306으로 접속합니다. 소켓 파일에 문제가 있으면 localhost만 실패하고 127.0.0.1은 정상 동작합니다.
Q. errno (2)와 (13)은 어떻게 다른가요?
A. (2)는 ENOENT로 소켓 파일 자체가 없는 경우(서비스 미기동·경로 불일치), (13)은 EACCES로 파일은 있으나 권한이 막힌 경우(소유권·AppArmor/SELinux)입니다.
Q. Docker 앱 컨테이너에서 ERROR 2002가 떠요.
A. 앱 컨테이너 안에는 mysqld 소켓이 없습니다. -h db처럼 DB 서비스명으로 TCP 접속하거나, 같은 호스트라면 /var/run/mysqld를 볼륨 마운트하세요.
JSON-LD(FAQPage) 스니펫:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{"@type":"Question","name":"왜 localhost는 안 되고 127.0.0.1은 되나?","acceptedAnswer":{"@type":"Answer","text":"localhost는 유닉스 소켓, 127.0.0.1은 TCP 3306으로 접속하기 때문에 소켓 파일 문제 시 127.0.0.1만 정상 동작합니다."}},
{"@type":"Question","name":"errno (2)와 (13)의 차이는?","acceptedAnswer":{"@type":"Answer","text":"(2) ENOENT는 소켓 파일이 없는 경우, (13) EACCES는 파일은 있으나 권한이 거부된 경우입니다."}},
{"@type":"Question","name":"소켓 경로가 어디인지 어떻게 확인하나?","acceptedAnswer":{"@type":"Answer","text":"mysqld --verbose --help | grep '^socket' 와 my_print_defaults mysql mysqld | grep socket 값을 비교하면 됩니다."}},
{"@type":"Question","name":"Docker에서 ERROR 2002가 나면?","acceptedAnswer":{"@type":"Answer","text":"앱 컨테이너에는 소켓이 없으므로 DB 서비스명으로 TCP 접속하거나 /var/run/mysqld를 볼륨 마운트하세요."}}
]
}이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...