
docker run -d -p 3000:8080 -e OPENAI_API_KEY=your_secret_key -v open-webui:/app/backend/data --name open-webui --restart always [ghcr.io/open-webui/open-webui:main](http://ghcr.io/open-webui/open-webui:main)
Open-WebUI 컨테이너를 실행한다. OpenAI API 모델만 사용할 예정이라, 간단한 설정으로 진행하고 있다. 현재 설치된 상태에서는 기본적으로 localhost:3000에서 애플리케이션이 동작하게 된다. 별도로 yaml 파일을 수정하지 않는 한, 기본값으로 실행되므로 바로 확인이 가능하다.

관리자(Admin) 계정은 최초 가입 시 생성되며, UI에서 추가 계정을 등록할 수 있는 기능도 지원된다. Admin 계정을 통해 설정을 조금 더 찾아봐야 한다.
Open-WebUI가 단독으로 동작하지 않기 때문에, 파이프라인 기능을 지원하기 위한 추가 컨테이너를 실행한다. 공식 문서에 나온 대로 아래 명령어를 실행하면 된다.
docker run -d -p 9099:9099 --add-host=localhost:host-gateway -v pipelines:/app/pipelines --name pipelines --restart always ghcr.io/open-webui/pipelines:main
현재는 --add-host=localhost:host-gateway 옵션을 통해 컨테이너 내부에서 외부의 로컬호스트로 접근할 수 있도록 설정해야 한다. 이 부분이 컨테이너 네트워킹에서 조금 헷갈릴 수 있지만, 공식 문서에서도 이 옵션을 반드시 포함하라고 권장하고 있다.
먼저 공식 문서에 따르면
관리자 패널 > 설정 > 연결
탭으로 이동해서 pipelines 컨테이너랑 연결을 해야 한다고 한다.
https://docs.openwebui.com/features/pipelines/

도커 환경에서는 localhost 대신 host.docker.internal로 url에 설정하라고 하지만 네트워크 연결이 안되는 현상이 있었고,
관련 git issue를 찾아본 결과
https://github.com/open-webui/pipelines/issues/55

내 도커 가상IP를 직접 넣어줬다

성공~!
이렇게 컨테이너를 실행 및 각종 연결한 후, Open-WebUI의 관리자 패널에서 설정 > 파이프라인 메뉴로 들어가야 한다. 여기에선 직접 파이프라인 코드를 작성하거나, 커뮤니티에서 제공하는 설정을 참고할 수 있다. 커뮤니티 링크를 확인하며 필요한 파이프라인을 탐색하는 것도 고려 중이다.
파이프라인 코드를 작성하기에 앞서 UI에서 파이프라인을 등록하는 방법은 다음과 같다
좌측 하단 관리자 패널 > 설정 > 파이프라인
탭으로 이동


파이프라인은 Python 클래스로 작성되어 있으며, 런타임에서 동작하려면 반드시 Pipeline이라는 이름을 가져야 한다. 내부적으로는 Open-WebUI와 API 통신, 데이터 처리의 역할을 수행한다. 현재 주어진 기본 샘플 코드는 다음과 같은 구조를 가진다.
먼저, Valves 클래스가 API URL, API 키 같은 환경 변수들을 관리한다. UI에서 입력받은 설정을 이용하거나, 기본값을 코드에 포함시킬 수 있다.
python
RunCopy Code
class Pipeline:
class Valves(BaseModel):
DIFY_API_URL: str = Field(default="http://192.168.2.56/v1/chat-messages")
DIFY_API_KEY: str = Field(default="")
# IS_STREAMING: bool = Field(default=False)
def __init__(self):
self.valves = self.Valves()
self.name = "Dify Agent - Blocking"
self.debug = False
이 구조에서는 환경 변수 관리를 Valves라는 내부 클래스에 위임한다. 런타임에서 Valves 인스턴스를 생성해 필요한 API URL과 키 값을 동적으로 입력받을 수 있도록 설계되어 있다.
pipe 함수는 파이프라인의 핵심 부분이다. 입력된 사용자 메시지를 기반으로 Dify API를 호출하고, 응답 결과를 반환한다. 현재 설정은 기본적으로 Blocking 모드로 작동하지만, Streaming 모드로 확장할 수도 있다.
def pipe(self, user_message: str, model_id: str, messages: List[dict], body: dict) -> Union[str, Generator, Iterator]:
headers = {
'Authorization': f'Bearer {self.valves.DIFY_API_KEY}',
'Content-Type': 'application/json'
}
data = {
"inputs": {},
"query": user_message,
"response_mode": "blocking", # 현재는 blocking 모드
"user": body["user"]["email"],
}
try:
response = requests.post(self.valves.DIFY_API_URL, headers=headers, json=data, timeout=30)
if response.status_code == 200:
result = response.json().get("answer")
if result:
return str(result)
else:
return "No Key answer"
else:
return f"HTTP Error {response.status_code}: {response.text}"
except requests.exceptions.RequestException as e:
return f"Error: {e}"
현재 이 코드는 API 호출에 필요한 헤더와 데이터를 설정한 후 POST 요청을 보내는 구조다. JSON 형식의 응답을 파싱하여 결과값을 반환하며, 실패 시에는 HTTP 에러 메시지가 담긴 문자열을 반환하도록 처리되어 있다.
현재 Docker로 Open-WebUI와 파이프라인 컨테이너를 구성하고, 기본적인 파이프라인 코드를 검토하는 데까지 진행 중이다. 구조는 크게 어렵지 않지만, Blocking 모드의 내부 동작 방식과 Streaming 모드로의 확장을 어떻게 할지를 더 깊이 이해할 필요가 있다.
또한, 직접 나만의 사용자 정의 파이프라인을 작성하면서 다양한 데이터 처리 방식이나 API 호출 로직을 확장할 계획이다. 기존에는 단순히 데이터를 받아 리턴하는 구조였지만, 여기에 전처리나 추가 로직을 삽입하는 방법을 시도하고 싶다.
마지막으로 --add-host와 같은 Docker 네트워크 옵션을 조금 더 체계적으로 이해하여, 환경에 따라 발생할 수 있는 네트워킹 문제를 미리 방지해야 한다는 점도 느꼈다.
앞으로는 이러한 추가적인 테스트와 실험을 통해 조금 더 깊이 있는 운영 방법을 고민하고 문서를 업데이트할 예정이다.