11장. 폐쇄망 문서 파이프라인과 폐쇄형 RAG
바깥으로 나가는 연결 막기
집 전화에 발신 제한을 걸면 정해 둔 번호로만 전화가 걸립니다. 폐쇄망의 프로그램도 사내 주소로만 연결되어야 합니다. 그런데 라이브러리 가운데에는 우리가 모르는 사이에 바깥으로 연결하려는 것이 있습니다.
네 카드는 몰래 바깥으로 나가려는 연결입니다. 없는 모델을 허브에서 찾는 것, 라이브러리가 사용 통계를 보내는 것, 코드가 외부 LLM API 를 부르는 것, 새 판이 있는지 묻는 것입니다. 폐쇄망에서는 이 연결이 막혀 오류가 나거나 한참 기다리다 실패합니다. 여기서 발신 제한이 오프라인 설정과 방화벽, 정해 둔 번호가 사내 주소(이 장에서는 localhost)입니다.
끄는 법
| 바깥 연결 | 끄는 법 | 이 장에서 |
|---|---|---|
| Hugging Face 허브 | HF_HUB_OFFLINE=1, TRANSFORMERS_OFFLINE=1 | 실습 1에서 켠다 |
| Chroma 사용 통계 | Settings(anonymized_telemetry=False) 또는 ANONYMIZED_TELEMETRY=False | 실습 1, 4에서 켠다 |
| 외부 LLM API | 코드에서 부르지 않고 사내 Ollama 주소만 쓴다 | http://localhost:11434/v1 |
| pip 저장소 | PIP_NO_INDEX=1 또는 --no-index | 안내만 한다 |
나가는 연결 막아 보기
파이썬에서는 주소를 IP 로 바꾸는 socket.getaddrinfo 를 바꿔 끼워, localhost 가 아닌 주소를 막을 수 있습니다. 실습 1에서 이것을 파일로 만들어 모든 실습에 씁니다.
import socket
real = socket.getaddrinfo # 원래 함수를 챙겨 둔다
def only_local(host, *args, **kwargs):
"""localhost 가 아니면 연결하기 전에 오류를 낸다"""
if host not in ("localhost", "127.0.0.1", "::1"):
raise OSError(f"폐쇄망: {host} 로 나가는 연결을 막았습니다")
return real(host, *args, **kwargs)
socket.getaddrinfo = only_local # 바꿔 끼운다
try:
socket.getaddrinfo("huggingface.co", 443)
except OSError as e:
print(e)
socket.getaddrinfo = real # 예시가 끝나면 되돌린다코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
socket | 네트워크 연결을 다루는 표준 라이브러리다. socket 은 "전기 콘센트, 꽂는 구멍"이라는 뜻이다 |
socket.getaddrinfo | get address information(주소 정보 얻기)의 줄임이다. huggingface.co 같은 이름을 IP 주소로 바꾼다 |
real = socket.getaddrinfo | 원래 함수를 부르지 않고 이름표만 챙겨 둔다. 괄호가 없으면 실행하지 않는다 |
*args, **kwargs | 나머지 인자를 몇 개든 받아 그대로 넘긴다. args 는 arguments(인자), kwargs 는 keyword arguments(이름 붙인 인자)다 |
raise OSError(...) | 운영체제(OS) 쪽 오류를 일부러 일으킨다. 연결 실패와 같은 종류의 오류다 |
socket.getaddrinfo = only_local | 라이브러리의 함수 자리에 우리 함수를 끼운다. 이렇게 바꿔 끼우는 일을 몽키 패칭(monkey patching)이라고 한다 |
except OSError as e: | 오류가 나면 그 오류를 e 로 받아 멈추지 않고 이어 간다 |
이 코드가 뜻하는 것
회사의 전화 교환원으로 읽어 봅니다. 직원이 바깥에 전화하려면 교환원에게 번호를 물어야 합니다. 교환원 자리에 "사내 번호만 알려 주는" 새 교환원을 앉히면, 바깥 번호를 물을 때 "연결할 수 없습니다"라는 답만 돌아옵니다. 여기서 교환원 자리가 socket.getaddrinfo, 새 교환원이 only_local, 원래 교환원이 real 입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
real = socket.getaddrinfo | 원래 함수를 챙겨 둔다 | 원래 교환원의 연락처를 적어 둔다 |
def only_local(...) | localhost 가 아니면 오류를 낸다 | 사내 번호만 알려 주는 새 교환원 |
return real(host, ...) | localhost 면 원래 함수에 넘긴다 | 사내 번호는 원래 교환원에게 넘긴다 |
socket.getaddrinfo = only_local | 함수를 바꿔 끼운다 | 새 교환원을 자리에 앉힌다 |
socket.getaddrinfo("huggingface.co", 443) | 바깥 주소를 물어본다 | 바깥 번호를 물어본다 |
socket.getaddrinfo = real | 원래 함수로 되돌린다 | 원래 교환원을 다시 앉힌다 |
교안을 만든 PC 에서는 폐쇄망: huggingface.co 로 나가는 연결을 막았습니다 가 찍혔습니다. 주소를 IP 로 바꾸기 전에 막았으므로 실제로 바깥에 묻는 일은 일어나지 않았습니다. 443 은 HTTPS(HyperText Transfer Protocol Secure, 암호화한 웹 통신)가 쓰는 포트 번호입니다.
주의할 점
- 이 방법은 같은 파이썬 프로그램 안의 연결만 막습니다. 다른 프로그램이나 IP 를 바로 쓰는 연결은 막지 못합니다. 실제 폐쇄망의 차단은 방화벽과 네트워크 장비가 합니다. 여기서는 "우리 코드가 바깥을 찾는지" 확인하는 시험 도구로 씁니다.
- 오프라인 설정은 라이브러리를 불러오기(
import) 전에 켭니다. 라이브러리가 불러올 때 설정을 읽어 두는 경우가 있기 때문입니다.