Skip to content
5월 29, 2026chattiboypython

파이썬 프로세스가 `python3`로만 보이는 문제 해결하기 — `comm`, `cmdline`, 그리고 `setproctitle`

파이썬 프로세스가 python3로만 보이는 문제 해결하기 — comm, cmdline, 그리고 setproctitle

서버에서 ssps로 포트를 확인했을 때 모든 파이썬 데몬이 똑같이 python3로만 보여서 구분이 안 됐던 경험, 한 번쯤 있으실 겁니다. 이 글에서는 그 원인을 리눅스 커널 수준에서 짚어보고, 프로세스 이름을 의미 있는 이름으로 바꾸는 방법을 정리합니다.


1. 문제 상황

로컬에서 특정 포트(예: 8999)를 누가 쓰는지 확인해 봅니다.

$ ss -ltnp 'sport = :8999'
State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port  Process
LISTEN  0       2048        127.0.0.1:8999        0.0.0.0:*      users:(("python3",pid=1723,fd=6))

프로세스 이름이 그냥 python3 입니다. 파이썬으로 돌아가는 데몬이 여러 개라면 어느 것이 어느 것인지 구분할 수 없습니다. 실제로 어떤 스크립트인지 알려면 매번 이렇게 추가 조회를 해야 합니다.

$ ps -fp 1723
UID    PID   PPID  ...  CMD
ubuntu 1723  1395  ...  /home/ubuntu/brave_shim/venv/bin/python3 /home/ubuntu/brave_shim/brave_shim.py

목표는 간단합니다. ss/ps에서 바로 brave_shim이라고 보이게 만드는 것.


2. 배경 지식: 프로세스 이름은 사실 "두 개"다

리눅스에서 프로세스 "이름"은 하나가 아니라 두 가지 개념이 섞여 있습니다. 이걸 구분하는 것이 이 문제의 핵심입니다.

2-1. comm — 커널이 들고 있는 짧은 이름

  • 위치: /proc/<PID>/comm
  • 커널 내부 구조체(task_struct)에 저장되는 이름입니다.
  • 최대 15글자 (TASK_COMM_LEN = 16, 마지막 1바이트는 널 종료 문자).
  • 기본값은 실행한 바이너리의 파일 이름입니다. 그래서 파이썬 스크립트를 실행하면 인터프리터 바이너리 이름인 python3가 됩니다.
  • ss, netstat, top(기본), ps -o comm 등이 이 값을 보여줍니다.
$ cat /proc/1723/comm
python3

2-2. cmdline — 실행 시 전달된 전체 명령행

  • 위치: /proc/<PID>/cmdline (인자들이 널 문자 로 구분됨)
  • 프로세스를 띄울 때 넘긴 argv 전체입니다.
  • ps -ef, ps aux, ps -o args 등이 이 값을 보여줍니다.
$ tr '' ' ' < /proc/1723/cmdline
/home/ubuntu/brave_shim/venv/bin/python3 /home/ubuntu/brave_shim/brave_shim.py

정리

개념 위치 길이 제한 누가 보여주나
comm /proc/PID/comm 15자 ss, netstat, top, ps -o comm
cmdline /proc/PID/cmdline 사실상 무제한 ps -ef, ps aux, ps -o args

핵심: ss가 보여주는 건 comm입니다. 따라서 ss 출력만 바꾸려면 comm만 바꾸면 되고, ps -ef까지 바꾸려면 cmdline(argv)도 손봐야 합니다.


3. 방법 A — 의존성 없이 comm만 바꾸기 (prctl)

커널은 프로세스가 스스로 자기 comm을 바꿀 수 있는 시스템 콜을 제공합니다. 바로 prctl(PR_SET_NAME, ...) 입니다. (PR_SET_NAME의 상수 값은 15)

파이썬은 표준 라이브러리 ctypes로 libc 함수를 직접 호출할 수 있으므로, 추가 패키지 설치 없이 가능합니다.

if __name__ == "__main__":
    try:
        import ctypes
        ctypes.CDLL("libc.so.6").prctl(15, b"brave_shim", 0, 0, 0)  # 15 = PR_SET_NAME
    except Exception as e:
        logger.warning(f"프로세스 이름 설정 실패: {e}")
    ...
  • 장점: 표준 라이브러리만으로 동작, 외부 의존성 없음.
  • 한계: comm만 바뀝니다. ps -ef의 cmdline은 여전히 python3 ...로 보입니다.
  • 주의: comm스레드 단위라서, 소켓을 들고 있는 메인 스레드에서 호출해야 ss에 반영됩니다. (uvicorn.run()처럼 메인 스레드에서 도는 경우 시작 직전에 호출하면 됩니다.) 또한 15자 제한이 있으니 긴 이름은 잘립니다.

4. 방법 B — 표준 라이브러리 setproctitle (이번에 채택)

setproctitle은 파이썬 생태계에서 프로세스 이름 변경의 사실상 표준 라이브러리입니다. Celery, Gunicorn 같은 유명 프로젝트들이 워커 프로세스를 구분하려고 내부적으로 사용합니다.

동작 원리

setproctitle.setproctitle("이름")을 호출하면 내부적으로 두 가지를 동시에 처리합니다.

  1. argv 메모리 영역을 덮어쓰기cmdline이 바뀌어 ps -ef/ps aux에 반영됩니다.
  2. 리눅스에서는 prctl(PR_SET_NAME)도 호출comm이 바뀌어 ss/top에도 반영됩니다.

즉, 방법 A가 하던 일에 더해 ps의 cmdline까지 한 번에 정리해 줍니다.

설치

/home/ubuntu/brave_shim/venv/bin/pip install setproctitle

시스템 파이썬이 아니라 이 서비스가 쓰는 venv에 설치해야 한다는 점이 중요합니다. (서비스가 venv/bin/python3로 실행되기 때문)

코드 적용

if __name__ == "__main__":
    # ss/netstat의 comm과 ps/top의 cmdline에 보이는 프로세스 이름을 brave_shim으로 설정
    try:
        import setproctitle
        setproctitle.setproctitle("brave_shim")
    except Exception as e:
        logger.warning(f"프로세스 이름 설정 실패: {e}")

    logger.info(f"서버 시작: {config['server']['host']}:{config['server']['port']}")
    uvicorn.run(app, host=..., port=...)

try/except로 감싼 이유: 프로세스 이름 변경은 부가 기능일 뿐이라, 혹시 실패하더라도 서버 본체는 정상 기동되도록 하기 위함입니다.


5. systemd 서비스 환경에서의 적용

이 데몬은 systemd user 서비스로 관리됩니다.

# ~/.config/systemd/user/brave-shim.service
[Service]
Type=simple
WorkingDirectory=/home/ubuntu/brave_shim
ExecStart=/home/ubuntu/brave_shim/venv/bin/python3 /home/ubuntu/brave_shim/brave_shim.py
Restart=always
RestartSec=5

코드를 고쳤다고 바로 반영되지 않습니다. 이미 떠 있는 프로세스는 옛 코드로 실행 중이므로 재시작이 필요합니다.

systemctl --user restart brave-shim.service

Restart=always라서 프로세스가 죽으면 5초 뒤 자동으로 되살아납니다. 그래서 kill만으로는 코드가 갱신되지 않고, 반드시 restart를 써야 합니다.


6. 검증

재시작 후 두 명령으로 모두 확인합니다.

# comm (ss가 보여주는 값)
$ ss -ltnp 'sport = :8999'
LISTEN 0 2048 127.0.0.1:8999 0.0.0.0:* users:(("brave_shim",pid=574939,fd=6))

# cmdline (ps가 보여주는 값)
$ ps -p 574939 -o pid,comm,args
    PID COMMAND     COMMAND
 574939 brave_shim  brave_shim

ss(comm)와 ps(cmdline) 양쪽 모두 brave_shim으로 바뀐 것을 확인할 수 있습니다.


7. 두 방법 비교 요약

항목 A. ctypes + prctl B. setproctitle
추가 의존성 없음 (표준 라이브러리) 설치 필요 (C 확장, 보통 wheel로 제공)
ss/netstat/top (comm) ✅ 변경 ✅ 변경
ps -ef/ps aux (cmdline) ❌ 그대로 ✅ 변경
이름 길이 제한 15자 (comm) cmdline은 사실상 자유, comm은 15자로 잘림
이식성 리눅스 전용 크로스플랫폼 (Linux/BSD/macOS/Windows)
코드 복잡도 다소 로우레벨 한 줄, 직관적

결론

  • ss에서만 구분되면 충분하고 의존성을 늘리기 싫다 → 방법 A
  • ps, top 등 어디서 봐도 일관되게 보이길 원한다 → 방법 B (이번 채택)

8. 한 걸음 더 (심화)

  • comm이 15자인 이유: 커널 task_structchar comm[TASK_COMM_LEN]에서 TASK_COMM_LEN이 16으로 정의돼 있습니다. 널 종료 문자 1바이트를 빼면 15자가 실사용 한계입니다. 긴 서비스 이름은 잘리므로, comm용 이름은 짧게 잡는 게 좋습니다.
  • comm은 스레드별 값: 멀티스레드 프로그램에서 각 스레드가 자기 comm을 가질 수 있습니다(prctl은 호출한 스레드에만 적용). top -Hps -T로 스레드별 이름을 볼 수 있습니다.
  • setproctitle이 argv를 덮어쓰는 방식: C 레벨에서 argv/environ가 차지하던 메모리 영역을 새 문자열로 덮어씁니다. 그래서 원래 cmdline보다 긴 이름을 넣으면 잘릴 수 있고, 환경변수 영역까지 침범하지 않도록 라이브러리가 관리합니다.
  • 언제 굳이 이렇게 할까: 같은 인터프리터(파이썬/노드 등)로 도는 데몬이 여러 개일 때, 모니터링·kill·로그·oom-killer 추적에서 프로세스를 사람이 식별하기 쉬워집니다. 운영 편의성과 관측성(observability) 측면의 작은 투자입니다.

참고

  • man 2 prctlPR_SET_NAME, PR_GET_NAME
  • man 5 proc/proc/[pid]/comm, /proc/[pid]/cmdline
  • setproctitle (PyPI)