feat(lps): P2 시크릿 env 주입 + Chrome 프로필 영속 볼륨
프로덕션에서 시크릿을 이미지에 굽지 않고 env 로 주입, 재시작 시 챌린지 쿠키 유지. - server_configs: 시크릿 env override 추가(house 의 DB override 패턴 확장) — OPENAI_API_KEY/MODEL, DECODO_HOST/USERNAME/PASSWORD/COST_PER_GB, NAVER_KEYS="id:sec,...". 로컬은 env 미설정 → config.local.toml 그대로. - worker_main: Chrome 프로필 경로를 LPS_PROFILE_DIR(기본 /tmp)로 env 화 → 영속 볼륨 마운트 가능. - docker-compose: lps-worker 에 lps-profiles 볼륨(/profiles) + 시크릿 env 주입 주석 예시. - config.example: 프로덕션 env 주입 목록 문서화. 검증: env override 동작(OpenAI/DECODO/Naver), 프로필이 /profiles 볼륨에 Cookies 포함 저장(cf_clearance 유지). 89 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
94d4e54d5e
commit
3a8d669970
@ -143,6 +143,15 @@ services:
|
||||
DB_HOST: host.docker.internal
|
||||
PYTHONUNBUFFERED: "1"
|
||||
WORKER_CONCURRENCY: "1" # 상품 동시 검색 수(워커별 브라우저 세트, Chrome 4×N)
|
||||
LPS_PROFILE_DIR: /profiles # Chrome 프로필을 영속 볼륨에 → 재시작해도 cf_clearance 유지(재웜업 회피)
|
||||
# ── 프로덕션 시크릿 주입(이미지에 안 굽고 env 로). 아래 주석 해제 후 값 채우거나 secrets 매니저 연동 ──
|
||||
# OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
# DECODO_USERNAME: ${DECODO_USERNAME}
|
||||
# DECODO_PASSWORD: ${DECODO_PASSWORD}
|
||||
# DECODO_HOST: ${DECODO_HOST}
|
||||
# NAVER_KEYS: ${NAVER_KEYS} # "id1:secret1,id2:secret2"
|
||||
volumes:
|
||||
- lps-profiles:/profiles # Chrome 프로필(쿠키) 영속
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
shm_size: "1gb" # Chrome 는 /dev/shm 을 많이 씀 — 부족하면 탭 크래시
|
||||
@ -150,3 +159,6 @@ services:
|
||||
logging:
|
||||
driver: json-file
|
||||
options: { max-size: "10m", max-file: "5" }
|
||||
|
||||
volumes:
|
||||
lps-profiles:
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
# 복사해서 사용: cp config.local.toml.example config.local.toml
|
||||
# 실제 config.local.toml 은 시크릿 포함이라 커밋하지 않는다(.gitignore: *.toml).
|
||||
# 모든 서버는 APP_ENV=local 로 띄우며 이 파일을 읽는다.
|
||||
#
|
||||
# ── 프로덕션: 시크릿을 이미지에 굽지 말고 env 로 주입(server_configs 가 override) ──
|
||||
# DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME
|
||||
# OPENAI_API_KEY / OPENAI_MODEL
|
||||
# DECODO_HOST / DECODO_USERNAME / DECODO_PASSWORD / DECODO_COST_PER_GB
|
||||
# NAVER_KEYS="id1:secret1,id2:secret2"
|
||||
# LPS_PROFILE_DIR=/profiles (Chrome 프로필 영속 볼륨), LPS_CHROME_EXECUTABLE=/usr/bin/chromium
|
||||
# → 배포 시엔 아래 시크릿 값을 비워두고 위 env 로 채우면 이미지에 시크릿이 안 남는다.
|
||||
[WebServerConfig]
|
||||
server_name = "LpsServer"
|
||||
port = 9600
|
||||
|
||||
@ -2,7 +2,7 @@ import os
|
||||
|
||||
from config.config_loader import Configs
|
||||
from config.config_models import (
|
||||
WebServerConfig, LogConfig, MainDBConfig, NaverConfig, OpenAIConfig, DecodoConfig,
|
||||
WebServerConfig, LogConfig, MainDBConfig, NaverConfig, NaverKey, OpenAIConfig, DecodoConfig,
|
||||
)
|
||||
|
||||
# 실행 환경 결정 (기본 local). 환경변수 APP_ENV 로 변경.
|
||||
@ -41,4 +41,25 @@ def _apply_db_env_override(cfg: MainDBConfig):
|
||||
cfg.name = os.environ["DB_NAME"]
|
||||
|
||||
|
||||
# 시크릿 env override — 프로덕션에선 API 키를 이미지에 굽지 않고 env(또는 시크릿매니저)로 주입한다.
|
||||
# 로컬은 env 미설정 → config.local.toml 값 그대로. (배포 시 toml 의 시크릿은 비워두고 아래 env 로 주입 권장)
|
||||
def _apply_secret_env_override():
|
||||
if os.environ.get("OPENAI_API_KEY"):
|
||||
openai_config.api_key = os.environ["OPENAI_API_KEY"]
|
||||
if os.environ.get("OPENAI_MODEL"):
|
||||
openai_config.model = os.environ["OPENAI_MODEL"]
|
||||
for k in ("host", "username", "password"):
|
||||
v = os.environ.get(f"DECODO_{k.upper()}")
|
||||
if v:
|
||||
setattr(decodo_config, k, v)
|
||||
if os.environ.get("DECODO_COST_PER_GB"):
|
||||
decodo_config.cost_per_gb = float(os.environ["DECODO_COST_PER_GB"])
|
||||
# NAVER_KEYS="id1:secret1,id2:secret2" 형식으로 키 로테이션 주입
|
||||
nk = os.environ.get("NAVER_KEYS")
|
||||
if nk:
|
||||
naver_config.keys = [NaverKey(id=i, secret=s)
|
||||
for i, s in (p.split(":", 1) for p in nk.split(",") if ":" in p)]
|
||||
|
||||
|
||||
_apply_db_env_override(main_db_config)
|
||||
_apply_secret_env_override()
|
||||
|
||||
@ -40,7 +40,9 @@ def _build_worker(i: int, concurrency: int, has_openai: bool, neg_cache, history
|
||||
suffix = f"_w{i}" if concurrency > 1 else ""
|
||||
|
||||
def _pf(source): # 워커별 Chrome 프로필 경로(중복 실행 시 ProcessSingleton 충돌 방지)
|
||||
return f"/tmp/lps_{source}{suffix}"
|
||||
# LPS_PROFILE_DIR 를 영속 볼륨으로 마운트하면 재시작해도 cf_clearance 등 쿠키 유지(재웜업 회피).
|
||||
base = os.environ.get("LPS_PROFILE_DIR", "/tmp")
|
||||
return f"{base}/lps_{source}{suffix}"
|
||||
|
||||
adapters = {
|
||||
"coupang": CoupangAdapter(headless=False, user_data_dir=_pf("coupang"), proxy=proxy, on_detect=bot_log.record),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user