n8n에서 Python 실행하기: Docker 환경에서 Python Code 노드 완벽 활용 가이드
핵심 키워드: n8n Python 실행, n8n Python Code 노드, n8n Docker Python, n8n 커스텀 이미지, n8n 마이크로서비스, n8n Queue Mode Python
n8n은 강력한 워크플로우 자동화 도구로, JavaScript뿐만 아니라 Python Code 노드도 지원합니다. 하지만 많은 사용자들이 n8n에서 Python 코드를 실행하려고 할 때 예상치 못한 문제에 직면하게 됩니다. 공식 n8n Docker 이미지에는 Python 런타임이 포함되어 있지 않기 때문입니다. 이 글에서는 n8n Docker 환경에서 Python을 실행하는 세 가지 방법을 상세히 다루며, 각 방법의 장단점과 실제 적용 시나리오를 함께 살펴보겠습니다. 아래에 쓰여진 블로그 글은 n8n v1.x대에서 대부분 가능합니다. 그러나 n8n v2.x대 부터는 보안강화를 위한 내부실행 보다는 외부실행을 위한 별도의 격리 보안조치들이 시행되고 있습니다.
Code노드에서 Python code를 꼭 실행하셔야 하는 분은 "n8n에서 Python Code노드 실행하기 (2/2) - External Mode"를 참조해주세요.
목 차
1. n8n에서 Python이 필요한 이유
n8n의 Code 노드는 JavaScript와 Python 두 가지 언어를 지원합니다. JavaScript만으로도 대부분의 데이터 처리 작업을 수행할 수 있지만, Python이 필요한 상황이 분명히 존재합니다. 데이터 과학 분야에서는 pandas, numpy, scikit-learn 같은 Python 라이브러리가 사실상 표준으로 자리 잡았습니다. 머신러닝 모델 추론, 복잡한 통계 분석, 자연어 처리(NLP) 작업 등에서 Python은 JavaScript보다 훨씬 풍부한 생태계를 제공합니다.
또한 기존에 Python으로 작성된 스크립트나 라이브러리를 n8n 워크플로우에 통합해야 하는 경우도 많습니다. 예를 들어, 회사 내부에서 사용하던 데이터 전처리 스크립트를 자동화 파이프라인에 포함시키거나, 특정 API와 통신하기 위해 Python 전용 SDK를 사용해야 하는 상황이 있을 수 있습니다. 이런 경우 해당 코드를 JavaScript로 다시 작성하는 것보다 Python을 직접 실행하는 것이 훨씬 효율적입니다.

2. 왜 기본 이미지에 Python이 없는가
n8n 공식 Docker 이미지(n8nio/n8n)를 살펴보면, Node.js 런타임만 포함되어 있고 Python은 설치되어 있지 않습니다. 이것은 실수나 누락이 아니라 의도적인 설계 결정입니다. Docker 이미지의 크기를 최소화하고, 보안 표면을 줄이기 위해 필수적이지 않은 구성 요소는 제외하는 것이 컨테이너 설계의 모범 사례입니다.
n8n의 핵심 기능은 Node.js 기반으로 동작하며, 대부분의 사용자는 JavaScript Code 노드와 내장 노드만으로도 충분히 복잡한 워크플로우를 구성할 수 있습니다. Python Code 노드는 n8n 2.x 버전에서 추가된 기능이지만, 이를 위한 Python 런타임 설치는 사용자의 선택에 맡겨져 있습니다. 이러한 접근 방식은 필요한 사용자만 추가 구성을 하도록 함으로써, 기본 이미지의 경량화와 빠른 시작을 가능하게 합니다.
Docker Hardened Images 적용 (2025년 변경사항)
2025년 현재 n8n 공식 이미지는 Docker Hardened Images (Alpine) 기반으로 제공됩니다. 이 이미지는 보안 강화를 위해 일반 Alpine Linux와 달리 패키지 관리자(apk)가 의도적으로 제거되어 있습니다. 따라서 기존처럼 apk add 명령으로 Python이나 다른 패키지를 설치할 수 없습니다.
# n8n 공식 이미지 OS 확인
docker run --rm -it --entrypoint /bin/sh docker.n8n.io/n8nio/n8n:latest
cat /etc/os-release
# 결과: NAME="Docker Hardened Images (Alpine)"
| 항목일반 | AlpineDocker | Hardened Alphine(n8n) |
| apk 패키지 관리자 | 사용 가능 | 제거됨 |
| apt-get | 없음 | 없음 |
| 추가 패키지 설치 | 가능 | 불가능 |
| 보안 수준 | 표준 | 강화됨 |
이러한 변경으로 인해 n8n 공식 이미지를 직접 확장하여 Python을 설치하는 것이 불가능해졌습니다. 대신 Node.js 베이스 이미지에서 시작하여 n8n과 Python을 함께 설치하는 방식을 사용해야 합니다.
3. 방법 1 | 커뮤니티 Python 포함 이미지 사용
가장 빠르고 간단하게 n8n에서 Python을 사용하는 방법은 커뮤니티에서 제공하는 Python 포함 이미지를 사용하는 것입니다. 대표적인 이미지로 naskio/n8n-python이 있으며, 이 이미지는 공식 n8n 이미지를 기반으로 Python 3.10과 함께 numpy, pandas 같은 자주 사용되는 데이터 과학 라이브러리가 미리 설치되어 있습니다.
docker-compose.yml 설정
기존 n8n 설정에서 이미지 이름만 변경하면 바로 Python을 사용할 수 있습니다.
version: '3.8'
services:
n8n:
# 기존: image: docker.n8n.io/n8nio/n8n:latest
# Python 포함 버전으로 변경
image: naskio/n8n-python:latest-debian
# 경량 버전을 원하는 경우 아래 이미지 사용
# image: naskio/n8n-python:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
- GENERIC_TIMEZONE=Asia/Seoul
- TZ=Asia/Seoul
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
사용 가능한 이미지 태그
커뮤니티 이미지는 여러 가지 태그를 제공하여 사용 환경에 맞게 선택할 수 있습니다. 다만, 현재 n8n 공식 이미지가 Debian 기반으로 변경되었으므로 커뮤니티 이미지도 Debian 기반 태그 사용을 권장합니다.
| 태그 | 설명 | 포함 패키지 |
| latest-debian | Debian 기반 버전으로, 공식 이미지와 호환성이 좋습니다. (권장) | Python 3.10 + numpy + pandas |
| latest | 경량 버전으로, 기본적인 Python 실행 환경만 필요한 경우 적합합니다. | Python 3.10 기본 |
| latest-full | 모든 데이터 과학 패키지가 포함된 완전한 버전으로, 이미지 크기가 가장 큽니다. | Python 3.10 + numpy + pandas + scipy + matplotlib |
장점과 단점
이 방법은 설정이 매우 간단하고, 별도의 빌드 과정 없이 즉시 사용할 수 있다는 것이 가장 큰 장점입니다. Docker Hub에서 이미지를 pull하기만 하면 되므로, Docker와 docker-compose에 대한 기본적인 이해만 있으면 누구나 쉽게 적용할 수 있습니다.
하지만 n8n 공식 버전 업데이트에 비해 커뮤니티 이미지의 업데이트가 다소 지연될 수 있다는 점을 고려해야 합니다. 보안 패치나 새로운 기능이 포함된 n8n 버전이 출시되어도, 커뮤니티 이미지에 반영되기까지 며칠에서 몇 주가 걸릴 수 있습니다. 프로덕션 환경에서 최신 버전을 즉시 적용해야 하는 경우에는 다음에 소개할 커스텀 Dockerfile 방법을 고려하는 것이 좋습니다.
4. 방법 2 | 커스텀 Dockerfile로 Python 설치
n8n 최신 버전을 즉시 사용하면서도 Python을 포함시키고 싶다면, 직접 커스텀 Dockerfile을 작성하여 이미지를 빌드하는 방법이 있습니다. 이 방법은 초기 설정에 약간의 노력이 필요하지만, 필요한 Python 패키지를 정확히 선택하여 설치할 수 있고, n8n 버전 업데이트도 자유롭게 할 수 있습니다.
프로젝트 디렉토리 구조
커스텀 이미지를 빌드하기 위해 다음과 같은 디렉토리 구조를 생성합니다.
n8n-python/
+-- Dockerfile
+-- docker-compose.yml
+-- requirements.txt (선택사항)
Dockerfile 작성
n8n 공식 이미지가 Docker Hardened Images로 변경되어 패키지 관리자가 제거되었으므로, Node.js 베이스 이미지에서 시작하여 n8n과 Python을 함께 설치하는 방식을 사용해야 합니다.
중요: n8n 공식 이미지는 Node.js 22를 사용합니다. 커스텀 이미지도 동일한 버전을 사용해야 커뮤니티 노드 호환성 문제가 발생하지 않습니다.
# Dockerfile
# =============================================================================
# n8n + Python 커스텀 이미지
# =============================================================================
# 기반: Node.js 22 Alpine (공식 이미지와 동일한 버전)
# 추가: n8n + Python 3 + pip + git + 사용자 정의 패키지
# 빌드: docker build --no-cache -t n8n-python:latest .
# 버전 지정 빌드: docker build --build-arg N8N_VERSION=2.2.6 -t n8n-python:2.2.6 .
# =============================================================================
FROM node:22-alpine
# n8n 버전 지정 (기본값: latest)
ARG N8N_VERSION=latest
# 시스템 패키지 설치 (Python + git + 필요한 도구)
# git: 일부 커뮤니티 노드가 GitHub 의존성을 가짐
RUN apk add --no-cache \
python3 \
py3-pip \
py3-setuptools \
git \
&& ln -sf /usr/bin/python3 /usr/bin/python
# Python 패키지 설치
COPY requirements.txt /tmp/requirements.txt
RUN pip3 install --no-cache-dir --break-system-packages -r /tmp/requirements.txt \
&& rm /tmp/requirements.txt
# n8n 설치 (버전 지정)
RUN npm install -g n8n@${N8N_VERSION}
# n8n 데이터 디렉토리 설정
RUN mkdir -p /home/node/.n8n \
&& chown -R node:node /home/node
# node 사용자로 전환
USER node
WORKDIR /home/node
# 환경 변수
ENV N8N_USER_FOLDER=/home/node/.n8n
# ENTRYPOINT로 n8n 명령어 설정 (webhook, worker 모드 호환)
ENTRYPOINT ["n8n"]
CMD ["start"]
Node.js 버전이 중요한 이유
| 항목 | 공식 이미지 | 커스텀 이미지 |
| Node.js | v22.x | v22.x (일치 필수) |
| 커뮤니티 노드 호환 | O | O |
Node.js 버전이 다르면 커뮤니티 노드의 native 모듈(C++ 바인딩)이 호환되지 않아 재설치가 필요합니다.
ENTRYPOINT vs CMD 차이
# 변경 전: CMD만 사용
CMD ["n8n"]
# docker-compose에서 command: webhook 시 -> "webhook" 실행 (에러)
# 변경 후: ENTRYPOINT + CMD 사용
ENTRYPOINT ["n8n"]
CMD ["start"]
# docker-compose에서 command: webhook 시 -> "n8n webhook" 실행 (정상)
Queue Mode에서 worker, webhook 인스턴스를 운영하려면 반드시 ENTRYPOINT를 설정해야 합니다.
git 패키지가 필요한 이유
일부 커뮤니티 노드(예: n8n-nodes-puppeteer)는 GitHub에서 직접 의존성을 가져옵니다:
puppeteer-extra-plugin-human-typing@github:0x7357/puppeteer-extra-plugin-human-typing
git이 없으면 npm install 시 다음 에러가 발생합니다.
npm error code ENOENT
npm error syscall spawn git
npm error path git
n8n 버전 지정 빌드
Dockerfile에서 ARG N8N_VERSION을 사용하면 빌드 시 원하는 n8n 버전을 유연하게 지정할 수 있습니다.
# 기본 빌드 (latest 버전)
docker build -t n8n-python:latest .
# 특정 버전 지정 빌드
docker build --build-arg N8N_VERSION=1.73.1 -t n8n-python:1.73.1 .
# 버전 태그와 latest 태그 모두 지정
docker build --build-arg N8N_VERSION=1.73.1 -t n8n-python:1.73.1 .
docker tag n8n-python:1.73.1 n8n-python:latest
n8n 버전 확인 방법
# npm에서 n8n 최신 버전 확인
npm view n8n version
# 사용 가능한 버전 목록 확인 (최근 20개)
npm view n8n versions --json | tail -20
# 특정 메이저 버전의 최신 확인
npm view n8n@1 version
npm 버전 지정 문법
| 문법 | 설명 | 예시 |
| n8n | 최신 버전 | 현재 최신 |
| n8n@1.73.1 | 정확한 버전 | 1.73.1 고정 |
| n8n@1.73 | 마이너 버전 최신 | 1.73.x 중 최신 |
| n8n@1 | 메이저 버전 최신 | 1.x.x 중 최신 |
requirements.txt 예시
# requirements.txt
# =============================================================================
# n8n Python Code Node용 패키지
# =============================================================================
# 기본 유틸리티
requests>=2.31.0
httpx>=0.27.0
# JSON/YAML 처리
pyyaml>=6.0
# 날짜/시간 처리
python-dateutil>=2.8.2
# 웹 스크래핑
beautifulsoup4>=4.12.0
lxml>=5.1.0
# 데이터 처리 (필요시 주석 해제)
# pandas>=2.1.0
# numpy>=1.26.0
pandas/numpy가 필요한 경우 (Debian 기반 권장)
pandas, numpy 같은 대용량 패키지는 Alpine에서 빌드 시간이 오래 걸리고 호환성 문제가 발생할 수 있습니다. 이런 경우 Debian 기반 이미지를 사용하는 것이 좋습니다.
# =============================================================================
# n8n + Python 커스텀 이미지 (Debian 기반 - pandas/numpy 포함)
# =============================================================================
FROM node:20-bookworm-slim
# 시스템 패키지 및 Python 설치
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 \
python3-pip \
python3-venv \
python3-numpy \
python3-pandas \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& ln -sf /usr/bin/python3 /usr/bin/python
# 추가 Python 패키지 설치
COPY requirements.txt /tmp/requirements.txt
RUN pip3 install --no-cache-dir --break-system-packages -r /tmp/requirements.txt \
&& rm /tmp/requirements.txt
# n8n 설치
RUN npm install -g n8n
# n8n 데이터 디렉토리 설정
RUN mkdir -p /home/node/.n8n && chown -R node:node /home/node
# node 사용자로 전환
USER node
WORKDIR /home/node
# 환경 변수
ENV N8N_USER_FOLDER=/home/node/.n8n
# n8n 실행
CMD ["n8n"]
왜 Node.js 베이스 이미지를 사용하는가?
n8n 공식 이미지(docker.n8n.io/n8nio/n8n)는 Docker Hardened Images 기반으로, 보안 강화를 위해 패키지 관리자가 제거되어 있습니다. 따라서 공식 이미지를 FROM으로 사용하여 Python을 추가 설치하는 것이 불가능합니다. 대신 일반 Node.js 이미지에서 시작하여 n8n을 npm으로 설치하고, Python도 함께 설치하는 방식을 사용합니다.
| 방식 | 가능여부 | 이유 |
| FROM n8nio/n8n + apk add python3 | 불가능 | Docker Hardened Images에서 apk 제거됨 |
| FROM n8nio/n8n + apt-get install python3 | 불가능 | Alpine 기반이라 apt-get 없음 |
| FROM node:20-alpine + n8n + Python 설치 | 가능 | 일반 Alpine이라 apk 사용 가능 |
| FROM node:20-bookworm-slim + n8n + Python 설치 | 가능 | Debian이라 apt-get 사용 가능 |
docker-compose.yml 설정
빌드한 커스텀 이미지를 사용하도록 docker-compose.yml을 설정합니다.
version: '3.8'
services:
n8n:
build:
context: .
dockerfile: Dockerfile
# image 라인은 주석 처리하거나 제거
# image: docker.n8n.io/n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
- GENERIC_TIMEZONE=Asia/Seoul
- TZ=Asia/Seoul
- NODE_ENV=production
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
이미지 빌드 및 실행
터미널에서 다음 명령어를 실행하여 이미지를 빌드하고 컨테이너를 시작합니다.
# 디렉토리 이동
cd n8n-custom-image
# 이미지 빌드
docker build -t n8n-python:latest .
# 버전 태그 추가 (선택사항)
docker tag n8n-python:latest n8n-python:$(date +%Y%m%d)
# 로그 확인
docker-compose logs -f n8n
빌드된 이미지 확인
이미지가 정상적으로 빌드되었는지 다양한 방법으로 확인할 수 있습니다.
# 1. 이미지 목록에서 확인
docker images | grep n8n-python
# 2. Python 버전 확인
docker run --rm n8n-python:latest python3 --version
# 3. pip 패키지 목록 확인
docker run --rm n8n-python:latest pip3 list
# 4. n8n 버전 확인
docker run --rm n8n-python:latest n8n --version
# 5. 이미지 내부 직접 접속하여 확인
docker run --rm -it n8n-python:latest /bin/sh
# 접속 후 실행: python3 --version, pip3 list, which n8n
# 나가기: exit
n8n 버전 업데이트 시
n8n 새 버전이 출시되면, Dockerfile의 베이스 이미지 태그를 변경하고 다시 빌드하면 됩니다.
# 새 버전으로 이미지 재빌드
docker-compose build --no-cache
# 컨테이너 재시작
docker-compose up -d
5. 권장 구성 | 빌드와 실행의 분리
앞서 설명한 방법 2(커스텀 Dockerfile)를 실제 프로덕션 환경에 적용할 때는 빌드와 실행을 분리하는 것이 가장 효율적입니다. docker-compose.yml 파일에 build 섹션을 포함하는 대신, CLI에서 먼저 이미지를 빌드하고 docker-compose.yml에서는 빌드된 이미지만 참조하는 방식입니다. 이 접근법은 관심사 분리(Separation of Concerns) 원칙을 따르며, 여러 스택에서 동일한 이미지를 재사용할 수 있게 해줍니다.
왜 빌드와 실행을 분리해야 하는가
docker-compose.yml 파일에 build 섹션을 포함하면 docker compose up 명령을 실행할 때마다 빌드 여부를 확인하는 과정이 추가됩니다. 또한 여러 서비스(main, worker)에서 동일한 이미지를 사용할 때 각각 build 섹션을 정의해야 하므로 설정이 중복됩니다. 빌드와 실행을 분리하면 docker-compose.yml 파일이 훨씬 간결해지고, 이미지 버전 관리도 태그를 통해 명확하게 할 수 있습니다.
권장 디렉토리 구조
빌드용 디렉토리와 실행용 디렉토리를 분리하여 관리합니다.
project-root/
|
+-- n8n-custom-image/ # 이미지 빌드 전용 디렉토리
| +-- Dockerfile
| +-- requirements.txt
|
+-- n8n-queue-stack/ # 실행용 스택 디렉토리
+-- docker-compose.yml # 이미지 참조만 (build 섹션 없음)
+-- .env
이 구조의 장점은 이미지 빌드에 필요한 파일들과 서비스 실행에 필요한 설정 파일들이 명확하게 분리된다는 것입니다. 이미지를 수정해야 할 때는 n8n-custom-image 디렉토리만, 서비스 설정을 변경할 때는 n8n-queue-stack 디렉토리만 수정하면 됩니다.
이미지 빌드 (CLI에서 실행)
n8n-custom-image 디렉토리에서 다음 명령어로 이미지를 빌드합니다.
cd n8n-custom-image
# 이미지 빌드 및 태그 지정
docker build -t n8n-python:latest .
# 버전 태그 추가 (선택사항 - 롤백에 유용)
# docker tag n8n-python:latest n8n-python:$(date +%Y%m%d)
docker tag n8n-python:latest n8n-python:2.3.0
# 빌드된 이미지 확인
docker images | grep n8n-python
> WARNING: This output is designed for human readability. For machine-readable output, please use --format.
> n8n-python:latest 8e4f8d635874 3.03GB 561MB
# Python 버전 확인
docker run --rm n8n-python:latest python3 --version
> 2.2.6
# 설치된 패키지 확인
docker run --rm n8n-python:latest pip3 list
> Package Version
---------- -------
packaging 25.0
pip 25.1.1
pyparsing 3.2.3
setuptools 80.9.0
이미지 태그에 버전을 명시해두면, 문제가 발생했을 때 이전 버전으로 쉽게 롤백할 수 있습니다. 예를 들어 n8n-python:2.3.0에서 문제가 발생하면 docker-compose.yml의 이미지 태그만 n8n-python:2.2.0으로 변경하고 재시작하면 됩니다.
docker-compose.yml (이미지 참조만)
실행용 docker-compose.yml 파일에서는 build 섹션 없이 빌드된 이미지만 참조합니다.
version: '3.8'
services:
# ---------------------------------------------------------------------------
# n8n Main Instance
# ---------------------------------------------------------------------------
n8n:
image: n8n-python:latest # 빌드된 커스텀 이미지 참조
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
- GENERIC_TIMEZONE=Asia/Seoul
- TZ=Asia/Seoul
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n-network
depends_on:
- postgres
- redis
# ---------------------------------------------------------------------------
# n8n Worker Instance
# ---------------------------------------------------------------------------
n8n-worker:
image: n8n-python:latest # main과 동일한 커스텀 이미지 사용
container_name: n8n-worker
restart: unless-stopped
command: worker
environment:
- GENERIC_TIMEZONE=Asia/Seoul
- TZ=Asia/Seoul
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n # main과 동일한 볼륨 공유
networks:
- n8n-network
depends_on:
- n8n
- redis
# ---------------------------------------------------------------------------
# PostgreSQL Database
# ---------------------------------------------------------------------------
postgres:
image: postgres:16-alpine
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
# ---------------------------------------------------------------------------
# Redis Queue
# ---------------------------------------------------------------------------
redis:
image: redis:7-alpine
container_name: n8n-redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- n8n-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
networks:
n8n-network:
driver: bridge
volumes:
n8n_data:
postgres_data:
redis_data:
이 설정에서 주목할 점은 n8n과 n8n-worker 모두 동일한 n8n-python:latest 이미지를 사용한다는 것입니다. build 섹션이 없으므로 docker-compose.yml 파일이 훨씬 간결해졌고, 이미지 변경이 필요할 때는 태그만 수정하면 됩니다.
업데이트 워크플로우
n8n 새 버전이 출시되거나 Python 패키지를 추가해야 할 때의 작업 절차입니다.
# 1단계: Dockerfile 또는 requirements.txt 수정 (필요한 경우)
cd n8n-custom-image
# 베이스 이미지 버전 변경 또는 패키지 추가
# 2단계: 새 이미지 빌드
docker build -t n8n-python:latest --no-cache .
# 3단계: 서비스 재시작
cd ../n8n-queue-stack
docker compose down
docker compose up -d
# 4단계: Python 환경 확인
docker exec -it n8n python3 --version
docker exec -it n8n-worker python3 --version
docker exec -it n8n pip3 list
--no-cache 옵션을 사용하면 캐시를 무시하고 모든 레이어를 새로 빌드합니다. 베이스 이미지가 업데이트되었거나 requirements.txt가 변경된 경우에 사용합니다.
이 방식의 장점 요약
빌드와 실행을 분리하는 이 권장 구성은 여러 가지 측면에서 장점을 제공합니다.
| 항목 | 설명 |
| 관심사 분리 | 빌드 설정(Dockerfile)과 실행 설정(docker-compose.yml)이 명확히 분리됩니다. |
| 재사용성 | 한 번 빌드한 이미지를 여러 스택이나 환경에서 재사용할 수 있습니다. |
| 유지보수 용이성 | docker-compose.yml이 간결해져 가독성이 높아지고, 이미지 교체가 간편합니다. |
| 버전 관리 | 이미지 태그를 통해 버전을 관리하고, 필요시 쉽게 롤백할 수 있습니다. |
| 환경 일관성 | main과 worker가 정확히 동일한 이미지를 사용하여 환경 일관성이 보장됩니다. |
6. 방법 3 | 별도 Python API 서비스 구축
마이크로서비스 아키텍처 방식으로 Python 실행 환경을 n8n과 완전히 분리하는 방법입니다. 별도의 Python 컨테이너를 구성하고, n8n에서 HTTP Request 노드를 통해 이 서비스를 호출합니다. 이 방법은 n8n 업그레이드에 전혀 영향을 받지 않으며, Python 환경을 독립적으로 관리할 수 있어 복잡한 Python 작업이 필요한 프로덕션 환경에 적합합니다.
프로젝트 디렉토리 구조
별도의 Python 서비스를 위한 디렉토리 구조를 생성합니다.
python-runner/
+-- Dockerfile
+-- main.py
+-- requirements.txt
docker-compose.yml
Python API 서버 코드 (main.py)
FastAPI를 사용하여 Python 코드를 실행하고 결과를 반환하는 간단한 API 서버를 구성합니다. 이 서버는 n8n의 HTTP Request 노드에서 호출할 수 있는 REST 엔드포인트를 제공합니다.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Any, Optional
import subprocess
import sys
from io import StringIO
import json
app = FastAPI(
title="Python Runner API",
description="n8n 워크플로우에서 Python 코드를 실행하기 위한 API 서비스",
version="1.0.0"
)
class CodeRequest(BaseModel):
"""Python 코드 실행 요청 모델"""
code: str
input_data: dict = {}
class CodeResponse(BaseModel):
"""Python 코드 실행 결과 모델"""
success: bool
result: Optional[Any] = None
error: Optional[str] = None
output: Optional[str] = None
@app.post("/execute", response_model=CodeResponse)
async def execute_code(request: CodeRequest):
"""
Python 코드를 실행하고 결과를 반환합니다.
- code: 실행할 Python 코드 문자열
- input_data: 코드에서 사용할 입력 데이터 (dict)
코드 내에서 'result' 변수에 값을 할당하면 해당 값이 반환됩니다.
'input_data' 변수를 통해 입력 데이터에 접근할 수 있습니다.
"""
try:
# 입력 데이터를 코드에서 사용할 수 있도록 local 변수로 설정
local_vars = {
"input_data": request.input_data,
"json": json
}
# stdout 캡처를 위한 설정
old_stdout = sys.stdout
sys.stdout = captured_output = StringIO()
# 코드 실행
exec(request.code, {"__builtins__": __builtins__}, local_vars)
# stdout 복원 및 출력 수집
output = captured_output.getvalue()
sys.stdout = old_stdout
# result 변수가 정의되어 있으면 해당 값 반환
# 그렇지 않으면 stdout 출력 반환
result = local_vars.get("result", output if output else None)
return CodeResponse(
success=True,
result=result,
output=output
)
except Exception as e:
# 에러 발생 시 stdout 복원
sys.stdout = old_stdout
return CodeResponse(
success=False,
error=f"{type(e).__name__}: {str(e)}"
)
@app.get("/health")
async def health_check():
"""서비스 상태 확인 엔드포인트"""
return {
"status": "healthy",
"python_version": sys.version,
"service": "python-runner"
}
@app.get("/packages")
async def list_packages():
"""설치된 Python 패키지 목록 조회"""
import pkg_resources
packages = [
{"name": pkg.key, "version": pkg.version}
for pkg in pkg_resources.working_set
]
return {"packages": sorted(packages, key=lambda x: x["name"])}
requirements.txt
필요한 Python 패키지를 정의합니다.
fastapi>=0.109.0
uvicorn>=0.27.0
pydantic>=2.5.0
requests>=2.31.0
pandas>=2.1.0
numpy>=1.26.0
beautifulsoup4>=4.12.0
Python 서비스 Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 시스템 의존성 설치
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# Python 패키지 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 애플리케이션 코드 복사
COPY main.py .
# 비특권 사용자로 실행
RUN useradd -m -u 1000 appuser
USER appuser
# 서비스 포트 노출
EXPOSE 8000
# 서비스 시작
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml (통합 구성)
n8n과 Python Runner 서비스를 함께 구성하는 docker-compose 파일입니다.
version: '3.8'
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
- GENERIC_TIMEZONE=Asia/Seoul
- TZ=Asia/Seoul
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n-network
depends_on:
python-runner:
condition: service_healthy
python-runner:
build:
context: ./python-runner
dockerfile: Dockerfile
container_name: python-runner
restart: unless-stopped
ports:
- "8000:8000"
networks:
- n8n-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
n8n-network:
driver: bridge
volumes:
n8n_data:
n8n에서 Python 서비스 호출하기
n8n 워크플로우에서 HTTP Request 노드를 사용하여 Python 코드를 실행합니다.
HTTP Request 노드 설정:
| 설정 항목 | 값 |
| Method | POST |
| URL | http://python-runner:8000/execute |
| Body Content Type | JSON |
| Specify Body | Using Fields Below 또는 JSON |
요청 본문 예시:
{
"code": "import pandas as pd\ndata = input_data['items']\ndf = pd.DataFrame(data)\nresult = df.describe().to_dict()",
"input_data": {
"items": [
{"name": "A", "value": 10},
{"name": "B", "value": 20},
{"name": "C", "value": 30}
]
}
}
응답 데이터 접근:
// 실행 성공 여부 확인
{{ $json.success }}
// 결과 데이터 접근
{{ $json.result }}
// 에러 메시지 (실패 시)
{{ $json.error }}
실용적인 Python 코드 예시
n8n 워크플로우에서 자주 사용할 수 있는 Python 코드 패턴들입니다.
데이터 변환 및 분석:
import pandas as pd
import json
# input_data에서 데이터 가져오기
items = input_data.get('items', [])
# DataFrame 생성 및 분석
df = pd.DataFrame(items)
# 통계 분석 수행
analysis = {
'total_count': len(df),
'numeric_summary': df.describe().to_dict() if len(df) > 0 else {},
'column_types': df.dtypes.astype(str).to_dict()
}
# 결과 반환
result = analysis
텍스트 처리:
from bs4 import BeautifulSoup
import re
# HTML 콘텐츠 파싱
html_content = input_data.get('html', '')
soup = BeautifulSoup(html_content, 'html.parser')
# 텍스트 추출 및 정제
text = soup.get_text(separator=' ')
cleaned = re.sub(r'\s+', ' ', text).strip()
result = {
'text': cleaned,
'word_count': len(cleaned.split()),
'links': [a.get('href') for a in soup.find_all('a', href=True)]
}
7. Queue Mode 환경에서의 주의사항
n8n을 Queue Mode로 운영하는 경우, 주의해야 할 중요한 사항이 있습니다. Queue Mode에서는 워크플로우 실행이 main 인스턴스가 아닌 worker 인스턴스에서 처리됩니다. 따라서 Python Code 노드를 사용하려면 main 인스턴스뿐만 아니라 모든 worker 인스턴스에도 Python이 설치되어 있어야 합니다.
Queue Mode에서 Python 사용 시 구성
방법 1(커뮤니티 이미지) 또는 방법 2(커스텀 Dockerfile)를 사용하는 경우, main과 worker 모두 동일한 Python 포함 이미지를 사용해야 합니다.
version: '3.8'
services:
n8n:
build:
context: .
dockerfile: Dockerfile.python
container_name: n8n-main
environment:
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
# ... 기타 설정
n8n-worker:
build:
context: .
dockerfile: Dockerfile.python # main과 동일한 이미지 사용
container_name: n8n-worker
command: worker
environment:
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
# ... 기타 설정
redis:
image: redis:7-alpine
container_name: redis
# ... 기타 설정
방법 3 사용 시 이점
별도 Python API 서비스 방식(방법 3)을 사용하면 이러한 복잡성을 피할 수 있습니다. Python 서비스는 n8n과 독립적으로 운영되므로, main이든 worker든 어떤 인스턴스에서 HTTP Request 노드가 실행되더라도 동일한 Python 서비스를 호출하게 됩니다.
version: '3.8'
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest # 공식 이미지 사용 가능
# ... Queue Mode 설정
n8n-worker:
image: docker.n8n.io/n8nio/n8n:latest # 공식 이미지 사용 가능
command: worker
# ... Queue Mode 설정
python-runner:
build: ./python-runner
# ... Python 서비스 설정
# main과 worker 모두 이 서비스 호출
redis:
image: redis:7-alpine
이 구성에서는 n8n 인스턴스들은 공식 경량 이미지를 그대로 사용하고, Python 작업만 별도 서비스에 위임합니다. 이렇게 하면 n8n 업그레이드가 훨씬 간단해지고, Python 환경의 변경이 n8n 서비스에 영향을 주지 않습니다.
8. 네 가지 방법 비교 및 선택 가이드
지금까지 살펴본 네 가지 방법을 종합적으로 비교하여, 상황에 맞는 최적의 선택을 할 수 있도록 정리하겠습니다.
비교표
| 기준 방법 | 1: 커뮤니티 이미지 | 2: 커스텀 Dockerfile | 2+: 빌드/실행 분리 (권장) | 3: 별도 API 서비스 |
| 설정 난이도 | 매우 쉬움 | 중간 | 중간 | 높음 |
| n8n 버전 업데이트 | 커뮤니티 대응 대기 | 즉시 가능 | 즉시 가능 | n8n과 독립적 |
| 패키지 커스터마이징 | 제한적 | 완전 자유 | 완전 자유 | 완전 자유 |
| Queue Mode 호환성 | 동일 이미지 필요 | 동일 이미지 필요 | 동일 이미지 자동 보장 | 공식 이미지 사용 가능 |
| 유지보수 용이성 | 높음 | 중간 | 높음 | 낮음 |
| 이미지 재사용 | 불가 | 제한적 | 완전 지원 | 해당 없음 |
| 버전 롤백 | 어려움 | 어려움 | 태그로 쉽게 가능 | 별도 관리 |
상황별 추천
빠른 테스트 및 학습 목적
- 추천: 방법 1 (커뮤니티 이미지)
- 이유: 가장 빠르게 Python을 사용해볼 수 있으며, 추가 구성이 거의 필요 없습니다. n8n과 Python의 통합을 학습하거나 개념 증명(PoC)을 진행할 때 적합합니다.
프로덕션 환경 - 단일 인스턴스
- 추천: 방법 2 (커스텀 Dockerfile)
- 이유: 최신 n8n 버전을 즉시 사용할 수 있고, 필요한 Python 패키지만 정확히 설치할 수 있습니다. 단일 인스턴스 환경에서 간단하게 운영할 때 적합합니다.
프로덕션 환경 - Queue Mode (강력 권장)
- 추천: 방법 2+ (빌드와 실행 분리)
- 이유: main과 worker가 정확히 동일한 이미지를 사용하는 것이 보장되며, 이미지 버전 관리와 롤백이 용이합니다. docker-compose.yml이 간결해지고 여러 환경에서 동일한 이미지를 재사용할 수 있습니다.
복잡한 Python 작업 또는 ML 추론
- 추천: 방법 3 (별도 API 서비스)
- 이유: Python 환경을 완전히 독립적으로 관리할 수 있어, 머신러닝 모델 서빙이나 대용량 데이터 처리 같은 무거운 작업에 적합합니다. n8n 업그레이드와 무관하게 Python 환경을 관리할 수 있습니다.
9. 트러블슈팅: 커스텀 이미지 적용 시 자주 발생하는 문제
커스텀 n8n-python 이미지를 적용할 때 자주 발생하는 문제들과 해결 방법을 정리했습니다.
문제 1 | webhook/worker 서비스가 시작되지 않음
증상:
Error: Cannot find module '/home/node/webhook'
원인: Dockerfile에서 CMD만 사용하고 ENTRYPOINT를 설정하지 않았기 때문입니다. docker-compose.yml에서 command: webhook을 지정하면 n8n webhook이 아닌 webhook 파일을 찾으려고 합니다.
해결: Dockerfile 마지막 부분을 다음과 같이 수정합니다:
# 잘못된 설정
CMD ["n8n"]
# 올바른 설정
ENTRYPOINT ["n8n"]
CMD ["start"]
문제 2 | 커뮤니티 노드가 인식되지 않음 (재설치 요구)
증상: n8n UI의 Settings > Community Nodes에서 패키지가 보이지만 "Reinstall" 버튼이 나타나고, 워크플로우에서 해당 노드가 "?" 상태로 표시됩니다.
원인: Node.js 버전 불일치입니다. 공식 n8n 이미지는 Node.js 22를 사용하는데, 커스텀 이미지에서 Node.js 20을 사용하면 native 모듈이 호환되지 않습니다.
확인 방법:
# 커스텀 이미지의 Node.js 버전
docker exec n8n-main node --version
# 공식 이미지의 Node.js 버전
docker run --rm --entrypoint node n8nio/n8n:latest --version
해결: Dockerfile에서 Node.js 22를 사용하도록 수정합니다:
# 잘못된 설정
FROM node:20-alpine
# 올바른 설정
FROM node:22-alpine
이미지 재빌드 후 커뮤니티 노드 볼륨을 삭제하고 재설치합니다:
# 이미지 재빌드
docker build --no-cache -t n8n-python:latest .
# 커뮤니티 노드 볼륨 삭제 및 재시작
docker compose down
docker volume rm n8n-queue-stack_n8n_community_nodes
docker compose up -d
# UI에서 커뮤니티 노드 재설치
문제 3 | npm install 시 git 에러
증상:
npm error code ENOENT
npm error syscall spawn git
npm error path git
원인: 일부 커뮤니티 노드(예: n8n-nodes-puppeteer)는 GitHub에서 직접 의존성을 가져옵니다. 공식 n8n 이미지에는 git이 포함되어 있지만, node:alpine 베이스 이미지에는 없습니다.
해결: Dockerfile에 git 패키지를 추가합니다:
RUN apk add --no-cache \
python3 \
py3-pip \
py3-setuptools \
git \ # 추가
&& ln -sf /usr/bin/python3 /usr/bin/python
문제 4 | Python Task Runner 에러
증상:
Failed to start Python task runner in internal mode.
because its virtual environment is missing from this system.
원인: n8n 2.0+에서 Python Code 노드는 별도의 Task Runner가 필요합니다. 단순히 Python만 설치하는 것으로는 부족합니다.
해결 방법 A: 경고 무시 (Python Code 노드를 사용하지 않는 경우)
이 메시지는 경고일 뿐 에러가 아닙니다. Python Code 노드를 사용하지 않는다면 무시해도 됩니다. JS Code 노드와 커뮤니티 노드는 정상 동작합니다.
해결 방법 B: External Mode로 Task Runner 설정 (Python Code 노드가 필요한 경우)
docker-compose.yml에 runners 서비스를 추가합니다:
n8n-runners:
image: n8nio/runners:latest
container_name: n8n-runners
restart: unless-stopped
environment:
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_SERVER_URI=http://n8n:5679
networks:
- n8n-internal-network
depends_on:
n8n:
condition: service_healthy
문제 5 | 이미지 변경 후에도 이전 설정이 적용됨
증상: Dockerfile을 수정하고 docker build를 실행했지만, 컨테이너에 변경사항이 반영되지 않습니다.
원인: Docker 레이어 캐시가 이전 빌드를 재사용하거나, 컨테이너가 재생성되지 않았습니다.
해결:
# 캐시 없이 빌드
docker build --no-cache -t n8n-python:latest .
# 컨테이너 완전 재생성
docker compose down
docker compose up -d
# 버전 확인 (ENTRYPOINT 때문에 --entrypoint 필요)
docker run --rm --entrypoint node n8n-python:latest --version
docker run --rm --entrypoint python3 n8n-python:latest --version
이미지 변경 시 체크리스트
| 변경사항 | --no-cache 필요 | 노드 재설치 |
| Node.js 메이저 버전 변경 (20 -> 22) | O | O |
| Node.js 마이너 버전 변경 (22.20 -> 22.21) | X | X |
| Python 버전 변경 | O | X |
| requirements.txt 변경 | 권장 | X |
| n8n 버전 변경 | 권장 | △ (호환성 문제 시) |
| ENTRYPOINT/CMD 변경 | O | X |
10. 마무리 및 권장사항
n8n에서 Python을 실행하는 방법은 사용 환경과 요구사항에 따라 다르게 선택해야 합니다. 이 글에서 소개한 네 가지 방법 중 방법 2+(빌드와 실행 분리)가 대부분의 프로덕션 환경에서 가장 균형 잡힌 선택입니다. CLI에서 이미지를 미리 빌드하고, docker-compose.yml에서는 빌드된 이미지만 참조하는 이 방식은 관리의 단순함과 운영의 안정성을 모두 제공합니다.
시작하기 전 체크리스트
Python 환경을 구성하기 전에 다음 질문들을 스스로에게 던져보는 것이 좋습니다.
- Python에서 어떤 작업을 수행해야 하는가? 단순 데이터 변환인지, 복잡한 머신러닝 추론인지에 따라 필요한 패키지와 리소스가 달라집니다.
- n8n 버전 업데이트를 얼마나 자주 해야 하는가? 보안이 중요한 환경이라면 최신 버전을 즉시 적용할 수 있는 방법이 필요합니다.
- Queue Mode를 사용하고 있거나 사용할 계획이 있는가? Queue Mode에서는 모든 worker에 Python이 설치되어야 한다는 점을 고려해야 합니다.
- 운영 및 유지보수 역량은 어느 정도인가? 마이크로서비스 방식은 더 많은 관리 포인트가 생기지만, 그만큼 유연성도 높아집니다.
최종 권장사항
Queue Mode 프로덕션 환경에서는 방법 2+(빌드와 실행 분리)를 강력히 권장합니다. 이 방식의 핵심은 다음과 같습니다:
- n8n-custom-image 디렉토리에서 Dockerfile과 requirements.txt를 관리합니다.
- CLI에서 이미지를 빌드합니다: docker build -t n8n-python:latest .
- docker-compose.yml에서는 빌드된 이미지만 참조합니다: image: n8n-python:latest
- main과 worker 모두 동일한 이미지를 사용하여 환경 일관성을 보장합니다.
이 구성은 n8n 업그레이드 시에도 유연하게 대응할 수 있고, 문제 발생 시 이전 버전으로 쉽게 롤백할 수 있습니다. 또한 docker-compose.yml 파일이 간결해져 가독성이 높아지고, 여러 환경에서 동일한 이미지를 재사용할 수 있어 효율적입니다.
처음 n8n에서 Python을 시도하는 분들에게는 방법 1(커뮤니티 이미지)로 시작하여 Python Code 노드의 동작 방식을 충분히 이해한 후, 프로덕션 환경으로 전환할 때 방법 2+를 적용하는 것을 권장합니다. 이렇게 단계적으로 접근하면 불필요한 복잡성을 피하면서도, 요구사항이 증가할 때 유연하게 대응할 수 있습니다.
어떤 방법을 선택하든, 이 가이드가 n8n과 Python의 통합 여정에 도움이 되었기를 바랍니다. 궁금한 점이나 추가로 다뤄야 할 주제가 있다면 댓글로 알려주세요.
참고 자료
- n8n 공식 문서: https://docs.n8n.io
- n8n Docker 설치 가이드: https://docs.n8n.io/hosting/installation/docker/
- n8n Queue Mode 설정: https://docs.n8n.io/hosting/scaling/queue-mode/
- FastAPI 공식 문서: https://fastapi.tiangolo.com
- Docker Compose 공식 문서: https://docs.docker.com/compose/
관련 글:
'AI 활용' 카테고리의 다른 글
| n8n 워크플로우 생성을 위한 Claude Skill 활용하기 - Desktop (0) | 2026.01.17 |
|---|---|
| n8n에서 Python Code노드 실행하기 (2/2) - External Mode (1) | 2026.01.12 |
| n8n 커뮤니티노드 Crawl4AI를 활용한 웹 스크래핑 - FireCrawl 대체 (0) | 2026.01.11 |
| n8n 커뮤니티 노드 TOP21 정리 (2025년 기준) (0) | 2026.01.10 |
| n8n HTTP Request노드를 사용한 Google Maps API 호출 (1) | 2025.12.26 |