Docker 컨테이너는 종종 외부 리소스에 접근하기 위해 프록시가 필요합니다 — 데이터 수집, 다양한 지역에서의 테스트 또는 제한 우회를 위해서입니다. 잘못된 프록시 설정은 연결 오류, 실제 IP 유출 및 애플리케이션 작동 중단을 초래할 수 있습니다. 이 기사에서는 Docker에서 프록시를 설정하는 모든 방법을 살펴보겠습니다: 간단한 환경 변수부터 docker-compose 및 사용자 정의 네트워크를 사용한 고급 시나리오까지.
Docker 컨테이너에서 프록시가 필요한 이유
Docker 컨테이너는 프록시가 필수적인 다양한 시나리오에서 사용됩니다. 컨테이너화된 애플리케이션에서 프록시가 해결하는 주요 작업을 살펴보겠습니다.
데이터 수집 및 파싱: 마켓플레이스(Wildberries, Ozon), 소셜 미디어 또는 검색 엔진에서 데이터를 수집하기 위해 Docker 컨테이너에서 파서를 실행하는 경우, 프록시는 IP 차단으로부터 보호합니다. 컨테이너를 사용하면 파싱을 확장할 수 있습니다 — 10-50개의 인스턴스를 동시에 실행할 수 있으며, 각 인스턴스는 고유한 프록시를 사용합니다.
다양한 지역에서의 테스트: 웹 애플리케이션 또는 모바일 API를 개발할 때, 서비스가 다양한 국가에서 어떻게 작동하는지 확인해야 할 경우가 많습니다. 다양한 지리적 위치의 프록시를 사용하는 Docker 컨테이너는 CI/CD 파이프라인에서 이러한 테스트를 자동화할 수 있게 해줍니다.
자동화 및 봇: Selenium, Puppeteer 또는 Playwright를 사용하는 브라우저 자동화를 위한 컨테이너는 여러 계정으로 작업하기 위해 프록시가 필요합니다. 각 컨테이너는 고유한 프록시와 격리된 환경을 가집니다.
기업 제한 우회: 일부 인프라에서는 Docker 컨테이너가 인터넷에 접근하기 위해 기업 프록스를 통과해야 합니다. 올바른 설정이 없으면 컨테이너는 패키지를 다운로드하거나 외부 API에 접근할 수 없습니다.
중요: Docker 컨테이너는 호스트 시스템의 네트워크 설정을 상속하지만, 프록시는 명시적으로 설정해야 합니다. 호스트에 프록시가 있다고 해서 컨테이너가 자동으로 이를 사용하는 것은 아닙니다.
환경 변수를 통한 기본 설정
Docker 컨테이너에서 프록시를 설정하는 가장 간단한 방법은 실행 시 환경 변수를 전달하는 것입니다. 이 방법은 표준 HTTP_PROXY, HTTPS_PROXY 및 NO_PROXY 변수를 존중하는 대부분의 애플리케이션에서 작동합니다.
docker run을 통한 프록시가 설정된 컨테이너 실행:
docker run -d \
-e HTTP_PROXY="http://username:password@proxy.example.com:8080" \
-e HTTPS_PROXY="http://username:password@proxy.example.com:8080" \
-e NO_PROXY="localhost,127.0.0.1,.local" \
your-image:latest
매개변수 설명:
HTTP_PROXY— HTTP 요청을 위한 프록시HTTPS_PROXY— HTTPS 요청을 위한 프록시 (http:// 사용, https:// 사용 금지)NO_PROXY— 프록스를 통과하지 않아야 하는 주소 목록
SOCKS5 프록시 예제:
docker run -d \
-e HTTP_PROXY="socks5://username:password@proxy.example.com:1080" \
-e HTTPS_PROXY="socks5://username:password@proxy.example.com:1080" \
your-image:latest
자주 발생하는 오류: HTTPS_PROXY의 URL에서 https:// 사용. HTTPS 트래픽에 대해서도 http:// 또는 socks5://를 올바르게 지정해야 합니다. URL의 프로토콜은 프록시 서버의 유형을 나타내며, 트래픽 유형이 아닙니다.
Docker 데몬을 위한 프록시 설정 (모든 컨테이너에 영향을 미침):
모든 컨테이너가 기본적으로 프록시를 사용하도록 설정하려면 Docker 데몬을 설정하십시오. /etc/systemd/system/docker.service.d/http-proxy.conf 파일을 생성하십시오:
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080"
Environment="HTTPS_PROXY=http://proxy.example.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,.local"
변경 후 Docker를 재시작하십시오:
sudo systemctl daemon-reload
sudo systemctl restart docker
Dockerfile에서 프록시 설정
프록시를 통해 패키지를 다운로드해야 하는 Docker 이미지를 빌드할 때(예: apt-get, pip, npm), 빌드 단계에서 프록시를 설정해야 합니다. Docker는 이를 위한 빌드 타임 인수를 지원합니다.
프록시 지원 Dockerfile 예제:
FROM python:3.11-slim
# 프록시를 위한 인수 (빌드 시 전달됨)
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY
# 빌드를 위한 환경 변수 설정
ENV HTTP_PROXY=${HTTP_PROXY}
ENV HTTPS_PROXY=${HTTPS_PROXY}
ENV NO_PROXY=${NO_PROXY}
# 프록시를 통해 종속성 설치
RUN apt-get update && apt-get install -y curl
# Python 패키지 설치
COPY requirements.txt .
RUN pip install --proxy ${HTTP_PROXY} -r requirements.txt
# 애플리케이션 복사
COPY . /app
WORKDIR /app
# 런타임을 위한 프록시 변수 제거 (선택 사항)
ENV HTTP_PROXY=
ENV HTTPS_PROXY=
CMD ["python", "app.py"]
프록시를 전달하여 이미지 빌드:
docker build \
--build-arg HTTP_PROXY=http://proxy.example.com:8080 \
--build-arg HTTPS_PROXY=http://proxy.example.com:8080 \
--build-arg NO_PROXY=localhost,127.0.0.1 \
-t my-app:latest .
Node.js 애플리케이션 (npm을 통한 프록시):
FROM node:18-alpine
ARG HTTP_PROXY
ARG HTTPS_PROXY
# 프록시를 통해 작업하기 위해 npm 설정
RUN npm config set proxy ${HTTP_PROXY}
RUN npm config set https-proxy ${HTTPS_PROXY}
COPY package*.json ./
RUN npm install
# npm을 위한 프록시 설정 삭제
RUN npm config delete proxy
RUN npm config delete https-proxy
COPY . .
CMD ["node", "server.js"]
조언: 프록시가 애플리케이션의 작동이 아닌 빌드에만 필요하다면, Dockerfile의 끝에서 환경 변수를 지우십시오. 이는 컨테이너 로그에서 자격 증명이 유출되는 것을 방지합니다.
docker-compose.yml에서 프록시 구성
Docker Compose는 다중 컨테이너 애플리케이션의 프록시 관리를 간소화합니다. 프록시를 전역적으로 또는 개별 서비스에 대해 설정할 수 있습니다.
단일 서비스에 대한 프록시의 기본 구성:
version: '3.8'
services:
parser:
image: python:3.11-slim
environment:
- HTTP_PROXY=http://username:password@proxy.example.com:8080
- HTTPS_PROXY=http://username:password@proxy.example.com:8080
- NO_PROXY=localhost,127.0.0.1,db
volumes:
- ./app:/app
working_dir: /app
command: python parser.py
db:
image: postgres:15
# 데이터베이스는 프록시를 사용하지 않음
environment:
- POSTGRES_PASSWORD=secret
자격 증명을 안전하게 저장하기 위한 .env 파일 사용:
docker-compose.yml이 있는 디렉토리에 .env 파일을 생성하십시오:
PROXY_URL=http://username:password@proxy.example.com:8080
NO_PROXY=localhost,127.0.0.1
docker-compose.yml에서 변수를 참조하십시오:
version: '3.8'
services:
parser:
image: python:3.11-slim
environment:
- HTTP_PROXY=${PROXY_URL}
- HTTPS_PROXY=${PROXY_URL}
- NO_PROXY=${NO_PROXY}
volumes:
- ./app:/app
working_dir: /app
command: python parser.py
docker-compose에서 이미지 빌드를 위한 프록시 구성:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
- HTTP_PROXY=${PROXY_URL}
- HTTPS_PROXY=${PROXY_URL}
- NO_PROXY=${NO_PROXY}
environment:
- HTTP_PROXY=${PROXY_URL}
- HTTPS_PROXY=${PROXY_URL}
ports:
- "3000:3000"
각 인스턴스에 대해 서로 다른 프록시로 확장:
여러 파서를 실행해야 하고 각 파서가 고유한 프록시를 사용해야 하는 경우, 개별 서비스를 사용하십시오:
version: '3.8'
services:
parser-1:
image: my-parser:latest
environment:
- PROXY_URL=http://user:pass@proxy1.example.com:8080
volumes:
- ./data:/data
parser-2:
image: my-parser:latest
environment:
- PROXY_URL=http://user:pass@proxy2.example.com:8080
volumes:
- ./data:/data
parser-3:
image: my-parser:latest
environment:
- PROXY_URL=http://user:pass@proxy3.example.com:8080
volumes:
- ./data:/data
애플리케이션 수준에서 프록시 설정
일부 애플리케이션은 표준 HTTP_PROXY 환경 변수를 지원하지 않습니다. 이러한 경우, 애플리케이션 코드 또는 구성 파일에서 프록시를 설정해야 합니다.
Python (requests 라이브러리):
import os
import requests
# 환경 변수에서 프록시 가져오기
proxy_url = os.getenv('PROXY_URL', 'http://proxy.example.com:8080')
proxies = {
'http': proxy_url,
'https': proxy_url
}
# 요청에서 프록시 사용
response = requests.get('https://api.example.com/data', proxies=proxies)
print(response.json())
# SOCKS5 프록시의 경우 pip install requests[socks]를 설치하십시오.
# proxies = {
# 'http': 'socks5://user:pass@proxy.example.com:1080',
# 'https': 'socks5://user:pass@proxy.example.com:1080'
# }
Python (aiohttp를 통한 비동기 요청):
import os
import aiohttp
import asyncio
async def fetch_with_proxy():
proxy_url = os.getenv('PROXY_URL')
async with aiohttp.ClientSession() as session:
async with session.get(
'https://api.example.com/data',
proxy=proxy_url
) as response:
data = await response.json()
print(data)
asyncio.run(fetch_with_proxy())
Node.js (axios 라이브러리):
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxyUrl = process.env.PROXY_URL || 'http://proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);
axios.get('https://api.example.com/data', {
httpsAgent: agent
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error:', error.message);
});
Node.js (내장 https 모듈):
const https = require('https');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxyUrl = process.env.PROXY_URL;
const agent = new HttpsProxyAgent(proxyUrl);
const options = {
hostname: 'api.example.com',
port: 443,
path: '/data',
method: 'GET',
agent: agent
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => console.log(JSON.parse(data)));
});
req.on('error', (error) => console.error(error));
req.end();
Docker 컨테이너에서 프록시와 함께 Selenium 사용:
from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType
import os
proxy_url = os.getenv('PROXY_URL', 'proxy.example.com:8080')
# Chrome을 위한 프록시 설정
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument(f'--proxy-server={proxy_url}')
# 인증을 위해 확장 프로그램 또는 SSH 터널을 사용하십시오.
driver = webdriver.Chrome(options=chrome_options)
driver.get('https://example.com')
print(driver.title)
driver.quit()
Puppeteer와 프록시:
const puppeteer = require('puppeteer');
(async () => {
const proxyUrl = process.env.PROXY_URL || 'proxy.example.com:8080';
const browser = await puppeteer.launch({
args: [`--proxy-server=${proxyUrl}`],
headless: true
});
const page = await browser.newPage();
// 프록시 인증
await page.authenticate({
username: 'your-username',
password: 'your-password'
});
await page.goto('https://example.com');
console.log(await page.title());
await browser.close();
})();
Docker에 적합한 프록시 유형 선택
프록시 유형 선택은 Docker 컨테이너에서 애플리케이션이 해결하려는 작업에 따라 다릅니다. 주요 시나리오와 권장 사항을 살펴보겠습니다.
| 프록시 유형 | 사용 시기 | 장점 | 단점 |
|---|---|---|---|
| 데이터 센터 | 파싱, API 요청, 테스트 | 높은 속도, 낮은 가격, 안정성 | 쉽게 감지되고, 보호된 사이트에서 차단됨 |
| 주거지 | 소셜 미디어, 마켓플레이스, 복잡한 사이트 작업 | 실제 IP, 낮은 차단 위험, 넓은 지리적 범위 | 더 비쌈, 데이터 센터보다 느림, 제한된 트래픽 |
| 모바일 | 모바일 API 테스트, 강력한 차단 우회 | 최대 익명성, 모바일 운영자의 IP | 높은 가격, 제한된 지리적 범위 |
특정 작업에 대한 선택 권장 사항:
마켓플레이스 및 상품 카탈로그 파싱: Docker 컨테이너를 통해 Wildberries, Ozon 또는 기타 마켓플레이스를 파싱하는 경우, 주거지 프록시를 사용하십시오. 이러한 플랫폼은 데이터 센터 IP를 적극적으로 차단합니다. 다양한 사용자를 시뮬레이션하기 위해 5-10분마다 프록시를 회전하도록 설정하십시오.
API 테스트 및 개발: 자체 API 또는 외부 서비스와의 통합을 테스트하는 경우, 데이터 센터 프록시가 적합합니다. 이들은 높은 속도와 안정성을 제공하여 CI/CD에서 자동화된 테스트에 중요합니다.
Selenium/Puppeteer 자동화: 보호된 사이트(소셜 미디어, 은행, 복잡한 웹 애플리케이션)에서 브라우저 자동화를 실행할 때는 주거지 프록시를 선택하십시오. 이는 CAPTCHA 및 차단 가능성을 줄입니다.
지리적으로 분산된 테스트: 다양한 국가에서 서비스의 접근성을 확인해야 하는 경우, 특정 지리적 위치를 선택할 수 있는 주거지 프록시를 사용하십시오. 각 국가의 프록시로 Docker 컨테이너를 여러 개 실행하십시오.
확장성에 대한 조언: 10개 이상의 컨테이너를 프록시와 함께 실행할 경우, 자동 회전이 가능한 프록시 풀을 사용하십시오. 이는 관리가 용이하고 여러 컨테이너에서 동일한 IP를 재사용하는 것을 방지합니다.
자주 발생하는 문제 해결
Docker 컨테이너에서 프록시와 함께 작업할 때 발생하는 일반적인 문제들이 있습니다. 가장 흔한 문제와 해결 방법을 살펴보겠습니다.
문제 1: 컨테이너가 프록시에 연결할 수 없음
증상: "Connection refused", "Proxy connection failed" 오류, 컨테이너 실행 시 타임아웃.
해결 방법:
- 호스트에서 프록시의 접근 가능성을 확인하십시오:
curl -x http://proxy:port https://example.com - 프록시 서버가 Docker 네트워크에서 접근 가능한지 확인하십시오. 호스트의 localhost에 프록시가 있는 경우,
host.docker.internal(Mac/Windows) 또는 브리지 네트워크에서 호스트의 IP를 사용하십시오. - 방화벽 규칙을 확인하십시오 — Docker 컨테이너가 외부 연결을 차단당할 수 있습니다.
- 인증이 필요한 프록시의 경우, 사용자 이름과 비밀번호가 올바른지 확인하고 URL에서 특수 문자를 이스케이프하십시오.
호스트에서 프록시 접근 예:
# Mac/Windows
docker run -e HTTP_PROXY=http://host.docker.internal:8080 my-image
# Linux (브리지 네트워크에서 호스트의 IP를 확인하십시오)
ip addr show docker0 # 일반적으로 172.17.0.1
docker run -e HTTP_PROXY=http://172.17.0.1:8080 my-image
문제 2: DNS가 프록시를 통해 해결되지 않음
증상: "Could not resolve host" 오류, DNS 요청이 프록시를 통과하지 않음.
해결 방법:
- HTTP 대신 SOCKS5 프록시를 사용하십시오 — SOCKS5는 DNS 요청을 프록시합니다.
- Docker에서 DNS를 설정하십시오: 컨테이너 실행 시
--dns 8.8.8.8를 추가하십시오. - 프록시를 통해 DNS를 지원하지 않는 애플리케이션의 경우, 컨테이너 내에서 proxychains를 사용하십시오.
# proxychains가 포함된 Dockerfile
FROM python:3.11-slim
RUN apt-get update && apt-get install -y proxychains4
# proxychains 설정
RUN echo "strict_chain\nproxy_dns\n[ProxyList]\nsocks5 proxy.example.com 1080" > /etc/proxychains4.conf
# proxychains를 통해 애플리케이션 실행
CMD ["proxychains4", "python", "app.py"]
문제 3: 컨테이너의 실제 IP 유출
증상: 대상 서비스가 프록시 IP 대신 호스트 또는 컨테이너의 IP를 봄.
해결 방법:
- 애플리케이션이 실제로 프록시를 사용하고 있는지 확인하십시오 — https://api.ipify.org에 요청을 보내십시오.
- 코드 내의 모든 HTTP 클라이언트가 프록시 사용으로 설정되어 있는지 확인하십시오.
- WebRTC 및 WebSocket 연결의 경우 프록시가 작동하지 않을 수 있습니다 — 브라우저에서 WebRTC를 비활성화하십시오.
- 요청 헤더를 확인하십시오 — 일부 라이브러리는 실제 IP와 함께 X-Forwarded-For를 추가합니다.
컨테이너에서 IP 유출 테스트:
# 프록시 없이
docker run --rm curlimages/curl:latest curl https://api.ipify.org
# 프록시와 함께
docker run --rm \
-e HTTPS_PROXY=http://proxy.example.com:8080 \
curlimages/curl:latest curl https://api.ipify.org
문제 4: 프록시를 통한 느린 작업
증상: 높은 지연 시간, 타임아웃, 느린 데이터 로드.
해결 방법:
- 호스트에서 프록시의 속도를 직접 확인하십시오 — 문제는 프록시에 있을 수 있습니다.
- 느린 프록시와 작업하기 위해 애플리케이션의 타임아웃을 늘리십시오.
- TCP 연결을 재사용하기 위해 keep-alive 연결을 사용하십시오.
- 주거지 프록시의 경우 느린 속도는 정상입니다 — 요청 수를 최적화하십시오.
- HTTP 클라이언트에서 병렬 요청을 위한 연결 풀을 설정하십시오.
문제 5: HTTP에서는 작동하지만 HTTPS에서는 작동하지 않는 프록시
증상: HTTP 요청은 통과하지만, HTTPS는 SSL/TLS 오류를 반환합니다.
해결 방법:
- HTTPS_PROXY 변수가 설정되어 있는지 확인하십시오 (HTTP_PROXY만이 아님).
- HTTPS_PROXY의 프록시 URL에서 http://를 사용하고 https://는 사용하지 마십시오.
- 프록시가 HTTPS 터널링을 위한 CONNECT 메서드를 지원하는지 확인하십시오.
- 자체 서명된 인증서가 있는 프록시의 경우 SSL 검증을 비활성화하십시오 (테스트용으로만!).
보안 및 자격 증명 관리
Docker 컨테이너에서 프록시 자격 증명을 저장하는 것은 보안에 특별한 주의를 요구합니다. 비밀번호를 잘못 관리하면 로그, 이미지 또는 리포지토리에 유출될 수 있습니다.
프로덕션에서는 Docker Secrets를 사용하십시오:
Docker Swarm은 비밀번호를 안전하게 저장하기 위한 secrets 메커니즘을 지원합니다. secret을 생성하십시오:
echo "http://username:password@proxy.example.com:8080" | docker secret create proxy_url -
Swarm을 위한 docker-compose에서 사용하십시오:
version: '3.8'
services:
app:
image: my-app:latest
secrets:
- proxy_url
environment:
- PROXY_URL_FILE=/run/secrets/proxy_url
command: sh -c 'export HTTP_PROXY=$(cat $$PROXY_URL_FILE) && python app.py'
secrets:
proxy_url:
external: true
파일을 통한 환경 변수 (.env):
개발 시 .env 파일을 사용하되, 절대 Git에 커밋하지 마십시오. .gitignore에 추가하십시오:
# .gitignore
.env
.env.local
*.env
실제 자격 증명이 없는 .env.example 파일을 생성하십시오:
# .env.example
PROXY_URL=http://username:password@proxy.example.com:8080
NO_PROXY=localhost,127.0.0.1
이미지에 자격 증명을 하드코딩하지 마십시오:
위험: ENV를 사용하여 Dockerfile에 비밀번호를 직접 기록하지 마십시오. 이러한 값은 이미지의 레이어에 저장되며 삭제 후에도 접근할 수 있습니다.
# ❌ 나쁨 - 비밀번호가 이미지에 남아있음
FROM python:3.11
ENV HTTP_PROXY=http://user:secretpass@proxy.com:8080
COPY . /app
# ✅ 좋음 - 빌드 인수 또는 런타임 변수를 통해 전달하십시오
FROM python:3.11
ARG HTTP_PROXY
# 빌드 중에만 사용되며 최종 이미지에 저장되지 않음
자격 증명을 정리하기 위해 다단계 빌드를 사용하십시오:
# 프록시가 있는 빌드 단계
FROM python:3.11 AS builder
ARG HTTP_PROXY
ENV HTTP_PROXY=${HTTP_PROXY}
COPY requirements.txt .
RUN pip install -r requirements.txt
# 프록시 변수가 없는 최종 단계
FROM python:3.11-slim
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . /app
WORKDIR /app
CMD ["python", "app.py"]
프록시 자격 증명 회전:
프록시 제공자가 임시 자격 증명을 생성하는 API를 지원하는 경우, 정적 비밀번호 대신 이를 사용하십시오. 컨테이너에서 init 스크립트를 생성하십시오:
#!/bin/bash
# entrypoint.sh
# API에서 임시 자격 증명 가져오기
PROXY_CREDS=$(curl -s https://api.proxyservice.com/generate-temp-auth)
export HTTP_PROXY="http://${PROXY_CREDS}@proxy.example.com:8080"
# 애플리케이션 실행
exec python app.py
비밀번호 유출 없이 로깅:
프록시 변수가 출력에 포함되지 않도록 로깅을 설정하십시오:
import os
import re
def safe_log_env():
"""비밀번호 없이 환경 변수 로깅"""
for key, value in os.environ.items():
if 'PROXY' in key:
# URL에서 비밀번호 마스킹
safe_value = re.sub(r'://([^:]+):([^@]+)@', r'://\1:****@', value)
print(f"{key}={safe_value}")
else:
print(f"{key}={value}")
safe_log_env()
결론
Docker 컨테이너에서 프록시 설정은 데이터 수집, 자동화 및 분산 시스템에서 작업하는 개발자에게 중요한 기술입니다. 환경 변수를 통해 프록시를 설정하는 방법, Dockerfile 및 docker-compose를 통해 프록시를 통합하는 방법, Python 및 Node.js 애플리케이션 코드에서 프록시를 통합하는 방법, 연결 및 보안과 관련된 일반적인 문제를 해결하는 방법을 배웠습니다.
Docker에서 프록시와 함께 성공적으로 작업하기 위한 핵심 사항: 유연성을 위해 환경 변수를 사용하고, secrets 또는 .env 파일을 통해 자격 증명을 안전하게 저장하며, 작업에 따라 프록시 유형을 선택하고, 프로덕션 실행 전에 실제 IP 유출이 없는지 항상 테스트하십시오.
대부분의 데이터 수집 및 자동화 작업을 위해 Docker 컨테이너에서 주거지 프록시를 사용하는 것이 좋습니다 — 이는 보호된 사이트 및 API에서 작업할 때 높은 익명성과 최소한의 차단 위험을 보장합니다. API 테스트나 내부 작업을 위한 최대 속도가 필요한 경우, 데이터 센터 프록시를 고려하십시오.