본문 바로가기

클라우드 기반

[네트워크] 커넥션 타임아웃 늪에서 벗어나기 feat. proxy settings

분명 서버는 인터넷이 잘 되는데, 이상하게 컨테이너 안에서만 curl 등 API 호출이 실패하는 경우가 있다. 이에 대한 원인은 보통 컨테이너가 가진 "고립"이라는 특성에 있다. 컨테이너는 호스트와 분리된 자신만의 네트워크 공간을 가지며, 호스트의 설정을 자동으로 물려받지 않기 때문. 특히나 프록시를 사용하는 기업 환경에서는 이 문제가 더욱 빈번히 발생한다. 이 글에서는 내가 당면했었던 네트워크 통신 관련한 프록시 설정 문제를 통과하는, 일련의 과정을 설명한다.

 

 

컨테이너 환경에서의 프록시 설정의 3단계 여정

 

1단계 - 호스트 - 모든 네트워크의 근원

 

컨테이너가 실행되는 물리서버(혹은 우리의 경우처럼 vm). 실제 네트워크 카드를 가지고 인터넷과 연결되는 첫번째 관문.

호스트 자체에 HTTP_PROXY, HTTPS_PROXY 같은 환경변수가 올바르게 설정되어 있어야 함. 여기가 모든 프록시 설정의 원본 데이터가 있는 곳. 여기서 문제가 생긴다면 당연히 컨테이너에서 어떤 기술을 써도 해결할 수 없음.

vm이라면 직접 확인해보는 게 제일 무난 - ssh 접속 후 curl https://google.com 등 터미널에서 실행했을 때 정상적으로 응답이 오는지 확인. 프록시 설정이 잘 되었는지 확인을 위해서는 env | grep -i proxy 명령으로 프록시 변수가 올바르게 설정되어 있는지 확인. 

 

github actions에서 이를 실행하는 방법은, github actions 워크플로우 파일에서 runner의 환경을 설정하는 부분에 해당. yml파일에 Docker Daemon 설정할 때 json 파일 설정하는 식으로 설정해주면 됨. github secrets에 저장된 프록시 정보를 가져와 러너 환경에 설정.

# Docker 데몬(2단계)이 사용할 프록시를 설정하는 부분
- name: Configure Docker Proxy
  run: |
    mkdir -p ~/.docker
    cat <<EOF > ~/.docker/config.json
    {
      "proxies": {
        "default": {
          "httpProxy": "${{ secrets.HTTP_PROXY }}",
          "httpsProxy": "${{ secrets.HTTPS_PROXY }}",
          "noProxy": "${{ secrets.NO_PROXY }}"
        }
      }
    }
    EOF

 

 

2단계 - 컨테이너 엔진(Docker) - 설정 문지기

 

컨테이너를 만들고 관리하는 두번째 관문 - 이 역할을 하는 건 우리의 경우 docker지만, podman 등도 모두 유사하다. 호스트와 컨테이너 사이에서 다리 역할을 하며, 모든 설정 전달을 통제함.

컨테이너 엔진은 호스트의 환경 변수를 컨테이너에 자동으로 넘겨주지 않기 때문에, 우리가 명령을 내릴 때 직접 설정을 통해 안으로 들여보내줘야 함. 예를 들어, docker run 명령어 시에 -e 혹은 --env 플래그가 이 역할을 함.

 

container build를 할때 - 현재는 python 파일로 진행 중 - --build-arg 가져다가 넘겨주면 됨.

# build_container 함수 내부
# os.environ에서 프록시 변수를 찾아 --build-arg로 추가
for proxy_var in ['http_proxy', ...]:
    if proxy_var in os.environ:
        cmd.extend(['--build-arg', f'{proxy_var.upper()}={os.environ[proxy_var]}'])

...

# _container_cmd 함수 내부
# os.environ에서 프록시 변수를 찾아 -e 플래그로 추가
for proxy_var in ['http_proxy', ...]:
    if proxy_var in os.environ:
        cmd.extend(['-e', f'{proxy_var}={os.environ[proxy_var]}'])

 

 

3단계 - 컨테이너 - 앱의 최종 목적지

 

우리 앱이 실제로 실행되는 목적지가 바로 이 컨테이너의 내부. 2단계에서 전달받은 프록시 설정을, 컨테이너는 내부 환경 변수로 가지게 됨. 이 일련의 과정은 ARG 와 ENV 등의 설정을 통해 일어남. 별다를 것 없이, 이 역시도 컨테이너 내부에서 env | grep -i proxy 명령을 실행하여 전달한 프록시 변수가 보이는지 확인하는 것이 중요함.

 

# 2단계의 --build-arg 값을 받기 위한 선언
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY

# 컨테이너 내부의 환경 변수로 최종 설정
ENV http_proxy=$HTTP_PROXY
ENV https_proxy=$HTTPS_PROXY
ENV no_proxy=$NO_PROXY

 

 

결국 호스트 - 엔진 - 컨테이너 이렇게 3단계의 레이어로 흐름만 기억하면 그리 어렵지 않은 부분이다. "러너" 라고 뭉뚱그려 생각할 것이 아니라, 저 레이어를 기본 골자로 생각하자.