Oracle Cloud EC2에서 OpenAI API 요청 실패 문제 해결기

90736

최근 교내 수강후기 공유 시스템의 수강후기 작성의 욕설 검증과정을 위해 OpenAI API와 통신하는 기능을 개발하고 배포하는 과정에서 겪었던 특이한 IOException 트러블 슈팅 경험을 공유하고자 합니다.

로컬 환경에서는 아무 문제가 없었지만, 특정 지역에 배포했을 때만 발생했던, 원인 추적이 매우 어려웠던 케이스였습니다.

결론부터 말씀드리자면, API 키의 발급 계정 지역과 API 요청을 수행하는 서버의 지역 간의 지리적 불일치가 문제의 핵심이었습니다.

배경

현재 수강후기 공유 시스템의 환경

수강후기 공유 시스템에서는 수강후기 작성의 욕설 검증을 위해 프롬프트를 통한 욕설 맥락을 찾는 작업을 수행하고 있습니다.

  • 기능: Spring AI의 ChatClient를 사용하여 프롬프트를 OpenAI API로 전송하고 응답을 처리하는 기능
  • 로컬 환경 : 개발자 Mac(한국)에서 정상 동작
  • 초기 배포 환경 :
    • Oracle Cloud EC2 Instance, Ubuntu 22.04
    • 리전: 아시아 태평양 (오사카)
    • API Key : 한국 계정으로 발급받은 OpenAI API Key 연동
  • 배포 환경 성능 : Oracle Cloud EC2 instance (OCPU 개수 : 1, 네트워크 대역폭(Gbps):0.48, 메모리(GB): 1, 메모리 스왑 4GB)

배포 후 기능을 테스트했을 때, API 요청 라인에서 즉시 에러가 발생하며 기능이 정상적으로 동작하지 않았습니다.

nullIOException 의 미궁

발생한 에러를 추적했을 때, 가장 큰 어려움은 에러 메시지가 null 이었다는 점입니다.

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://api.openai.com/v1/chat/completions": null
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.createResourceAccessException(DefaultRestClient.java:534) ~[spring-web-6.1.1.jar!/:6.1.1]

에러 발생 타임 라인

DefaultRestClient 는 네트워크 I/O 문제를 감지하면 내부적으로 IOException을 던집니다. 이 경우, 네트워크 단에서 연결이 갑자기 끊기거나(Connection Reset), 연결이 불가능하여 즉시 실패했음에도 불구하고, 예외 처리 계층에서 상세 오류 메시지를 제대로 포착하지 못하고 상위 null 메시지의 IOException을 전달한 것으로 추정되었습니다.

image

ResourceAccessException 에 대한 예외 처리는 IOException에 대한 예외 발생을 받아온 것임을 확인하였습니다.

IOException: https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html Signals that an I/O exception of some sort has occurred. This class is the general class of exceptions produced by failed or interrupted I/O operations. 일반적으로, 입출력 연산이 실패하거나 중단되었을때 생성되는 예외이다.

이는 통신 과정 중의 입/출력(I/O) 와 관련된 문제가 발생한 것임을 알 수 있었습니다.

image

createResourceAccessException을 살펴보면 에러 메시지의 문자열들을 조합하는 모습을 볼 수 있었고, 마지막 조합인 \: 에서 ex.getMessage()를 끝으로 null 이라는 메시지를 받은 것을 확인할 수 있었습니다.

정확한 원인을 파악하기 위해 Trace Log 전체를 살펴보았으나, 요청을 보내자마자 응답 없이 연결이 종료되는 '요청 실패' 이상의 정보를 얻을 수 없었습니다.

문제의 원인 가설 : 지리적 제한?

네트워크 관련 IOException 에서 상세 메시지가 null 이거나 모호할 때, 서버 방화벽, 네트워크 라우팅 문제, 또는 지리적/정책적 차단을 의심해볼 수 있습니다.

로컬 개발 환경(한국) 에서는 정상 작동했기 때문에, 배포 환경(오사카)의 특정 조건이 문제를 유발한다는 가설을 세웠습니다.

가설: 한국 계정으로 발급된 OpenAI API Key를 사용하여, 지리적으로 먼 오사카 리전에서 요청을 수행하는 것이 OpenAI 측의 API 사용 정책(지역적 제한 또는 비정상 트래픽 감지)에 의해 즉시 차단된 것이 아닐까?

트러블 슈팅 : 리전 변경

가설을 검증하기 위해 가장 큰 차이점이었던 EC2 인스턴스의 리전을 변경했습니다.

변경한 환경 : 아시아 태평양(서울)로 변경하여 재배포.

결과 : EC2 인스턴스를 서울 리전으로 변경한 후, 기능이 완벽하게 정상 동작했습니다. 오사카 리전에서는 발생했던 IOException이 사라졌으며, 프롬프트 통신이 원활하게 이루어졌습니다.

근본 원인과 해결 방안 검토

추정되는 근본 원인:

  1. 지리적 정책 차단 : 한국 계정으로 발급된 API Key에 대해, 지리적으로 멀거나 예상치 못한 리전(오사카) 에서 발생하는 트래픽을 보안 또는 정책상의 이유로 즉시 차단(Drop) 했을 가능성.
  2. API 서버 엔드포인트 라우팅 오류 : 한국 계청의 트래픽이 한국 주변의 API 엔드포인트로 라우팅되어야 하는데, 오사카에서 요청을 보냈을 때 라우팅 경로가 꼬이거나, 요청이 API Gateway 단에서 처리되지 못하고 네트워크 레벨에서 끊겼을 가능성.

어떤 경우든, IOException: null 이라는 메시지는 내부 HTTP 클라이언트가 자세한 응답코드(403 Forbidden 이라던지..)를 받기 전에 OS의 TCP/IP 스택 또는 방화벽/네트워크 단에서 연결이 강제로 종료되었을 가능성이 있습니다.

배운것:

  • API Key와 서버 리전의 일치 : 외부 서비스의 API를 연동할 때, 특히 민감한 트래픽이나 지리적 제한이 있을 수 있는 서비스는 API Key를 발급받은 계정의 국가/지역과 요청을 수행하는 서버의 리전을 최대한 가깝게, 또는 동일하게 유지 하는 것이 좋다.
  • 모호한 IOException : IOException 이 발생하고 에러 메시지가 null 이거나 모호하다면, 코드의 문제가 아니라 서버 환경(네트워크, 방화벽, 리전, 정책) 의 문제를 의심하고 환경변수를 하나씩 변경하며 트러블 슈팅해볼 수 있다.

저와 비슷한 미궁을 겪지 않도록, Spring AI Issues에도 해당 문제를 작성해두었습니다. : Spring AI Issues