/인프라/ERROR 2002 (HY000) MySQL 소켓 연결 실패 원인별 해결 가이드
인프라MySQL ERROR 2002MySQL 소켓 연결 실패

ERROR 2002 (HY000) MySQL 소켓 연결 실패 원인별 해결 가이드

ERROR 2002 (HY000) Can't connect to local MySQL server through socket 에러를 errno (2)·(13)과 6가지 원인별로 진단합니다. systemctl·ss 복붙 명령과 소켓 경로·권한·TCP 전환으로 5분 안에 복구하세요.

ERROR 2002 (HY000) MySQL 소켓 연결 실패 원인별 해결 가이드

ERROR 2002 (HY000) MySQL 소켓 연결 실패, 5분 원인별 해결 가이드

배포 중이거나 새벽에 알림을 받고 서버에 붙었는데 이 한 줄이 떠 있으면 등골이 서늘합니다.

TEXT
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) 서비스가 살아있나

Bash
systemctl status mysql        # 또는 mariadb
journalctl -u mysql -n 50 --no-pager

정상이면 Active: active (running)이 보입니다. 죽었다면 ①번 확정.

(2) 진짜 응답하나 — 핑

Bash
mysqladmin ping
# mysqld is alive
mysqladmin -h 127.0.0.1 -P 3306 ping   # TCP로도 확인

(3) 소켓이 실제로 리스닝 중인가

Bash
ss -lx | grep mysql
# u_str LISTEN 0 70 /var/run/mysqld/mysqld.sock 12345 * 0
ss -ltnp | grep 3306

ss -lx에 소켓이 안 보이면 서버가 소켓을 안 열고 있는 것입니다.

(4) 클라이언트가 기대하는 경로 vs 서버가 만든 경로 대조 — ②번 핵심

Bash
mysqld --verbose --help | grep '^socket'      # 서버가 만드는 경로
my_print_defaults mysql mysqld | grep socket  # 설정 파일 실제 값
find / -name 'mysqld.sock' 2>/dev/null         # 실제 파일 위치

세 값이 서로 다르면 ②번 확정입니다.

(5) 권한·AppArmor·디스크

Bash
ls -la /var/run/mysqld/
aa-status | grep mysqld
dmesg | grep -i denied
df -h /var /tmp

4. 원인별 처방전

① 서비스 기동 / 크래시 복구

Bash
sudo systemctl start mysql && systemctl is-active mysql

크래시라면 로그에서 [ERROR] 라인을 추적하세요.

Bash
journalctl -u mysql -n 100 --no-pager | grep -i error

mysqld_safe --skip-grant-tables는 인증 무력화이므로 마지막 수단으로만, 복구 직후 즉시 정상 재기동해야 합니다.

② 소켓 경로 정렬 (가장 흔한 진짜 원인)

[mysqld]가 만드는 경로와 [client]/[mysql]이 찾는 경로를 반드시 동일하게 맞춥니다. /etc/mysql/my.cnf:

INI
[mysqld]
socket = /var/run/mysqld/mysqld.sock

[client]
socket = /var/run/mysqld/mysqld.sock

[mysql]
socket = /var/run/mysqld/mysqld.sock

급할 때 임시 우회로 심볼릭 링크도 가능합니다(단, 재기동 시 사라질 수 있으니 근본 해결은 위 설정으로):

Bash
sudo ln -s /tmp/mysql.sock /var/run/mysqld/mysqld.sock

③ 권한·소유권·AppArmor

(13)이면 소유권부터:

Bash
sudo chown mysql:mysql /var/run/mysqld && sudo chmod 755 /var/run/mysqld

AppArmor/SELinux가 막는 경우(최근 배포판 기본 정책이 강해졌습니다):

Bash
sudo aa-complain /usr/sbin/mysqld   # 또는 프로파일에 소켓 경로 추가

⚠️ chmod 777 mysqld.sock은 보안 사고로 가는 지름길이니 절대 금지.

④ TCP로 전환 — localhost vs 127.0.0.1의 결정적 차이

이 글의 핵심입니다. localhost는 유닉스 소켓, 127.0.0.1은 TCP로 붙습니다. 소켓이 말썽이면 TCP로 우회하세요.

Bash
mysql -h 127.0.0.1 -P 3306 --protocol=TCP -u user -p

애플리케이션 설정도 동일 원리입니다.

TEXT
JDBC: jdbc:mysql://127.0.0.1:3306/db
PHP PDO: mysql:host=127.0.0.1;port=3306;dbname=db

PHP에서 host=localhost를 쓰면 PDO가 소켓으로 붙는다는 점이 ERROR 2002의 단골 원인입니다.

⑤ Docker 환경

컨테이너는 systemd 환경과 다릅니다. 앱 컨테이너 안에는 mysqld 소켓이 존재하지 않습니다. 따라서 소켓이 아니라 서비스명으로 TCP 접속해야 합니다.

Bash
# 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 -p

rootless 컨테이너에서는 소켓 마운트 권한 문제가 추가로 생기니 가급적 TCP 구성을 권장합니다.

⑥ 디스크풀 / stale 소켓

Bash
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단 체크리스트 & 안티패턴

복구 순서를 외우세요:

  1. 서비스 살아있나 (systemctl status)
  2. 경로 맞나 (mysqld --verbose --help vs my_print_defaults)
  3. 권한 맞나 (ls -la, errno (13))
  4. 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) 스니펫:

JSON
{
  "@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를 볼륨 마운트하세요."}}
  ]
}
✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 14일

댓글

불러오는 중...