개발 및 AI · Clash 기술 블로그

VS Code Dev Containers에서 Clash 프록시를 사용하는 방법

Dev Container 빌드, 컨테이너 내부 Git, 확장 프로그램 다운로드는 서로 다른 프로세스에서 실행될 수 있습니다. 오류 위치에 따라 호스트 프록시 주소, 빌드 변수, 컨테이너 환경을 확인하고 NO_PROXY로 내부 접근을 유지한 뒤 다운로드와 실행을 각각 검증하세요.

  • VS Code
  • Dev Containers
  • Docker
  • 프록시
이 글의 목차

호스트 브라우저는 정상이어도 개발 컨테이너의 이미지 가져오기, 의존성 설치, 확장 기능 다운로드는 멈출 수 있습니다. 컨테이너 프록시 설정이 효과가 없다면 실제 요청을 보내는 프로그램에 적용되지 않았을 수 있습니다. 실패한 명령의 실행 위치와 호스트 프록시 접속 가능 여부를 확인하고 데이터베이스 등 내부 서비스는 직접 연결을 유지하세요.

Dev Containers는 어디서 멈추나요: 이미지 받기, 빌드, 컨테이너 내부 다운로드?

Dev Containers 출력이나 컨테이너 터미널에서 실패한 명령과 실행 위치를 기록하세요. 기본 이미지 가져오기, Dockerfile을 사용하는 빌드, 컨테이너 내부 Git, 원격 확장 기능 다운로드를 구분합니다. VS Code의 이 기능은 개발 환경을 컨테이너에 두지만 요청은 서로 다른 프로세스에서 나옵니다. 아래 표에 따라 실제 실패 위치에 프록시를 설정하세요.

다운로드 작업별 프록시 설정

실패한 작업실제로 요청하는 구성 요소구성 진입점
docker pull / 기본 이미지 가져오기Docker daemonDocker Desktop 또는 daemon 프록시
Dockerfile RUN apt/npm이미지 빌드 단계build args 또는 BuildKit 구성
컨테이너 내부 curl/Git/pip현재 실행 중인 컨테이너containerEnv, remoteEnv 또는 도구 구성
VS Code 원격 확장 기능 다운로드로컬 VS Code와 컨테이너 측 서비스로컬과 컨테이너의 네트워크 설정 및 확장 자체 설정

오류가 'Starting Dev Container' 전에 나타나면 컨테이너가 아직 실행되지 않았으므로 대개 .devcontainer/devcontainer.json의 containerEnv로 고칠 수 없습니다. 실패 명령과 VS Code 출력 패널의 단계를 먼저 기록하세요.

Dev Containers의 네 가지 네트워크 구간
  1. VS Code 호스트확장 기능을 가져오고 Docker를 시작
  2. Docker 빌드 단계build args 또는 Docker 프록시 설정 읽기
  3. 실행 중인 컨테이너containerEnv 읽기
  4. 원격 확장 기능과 터미널remoteEnv 또는 도구 자체 구성 읽기

다운로드가 호스트, 빌드 단계, 실행 중인 컨테이너 중 어디에서 실패하는지 확인한 뒤 해당 환경에 프록시를 설정하세요. 세 환경이 환경 변수를 자동으로 공유하지는 않습니다.

컨테이너의 127.0.0.1은 호스트가 아닙니다

Clash가 호스트에서 실행될 때 컨테이너의 http://127.0.0.1:7890은 컨테이너 자체를 가리킵니다. Windows와 macOS의 Docker Desktop은 host.docker.internal을 제공하며 Linux Docker에서는 host-gateway를 통해 같은 이름을 매핑할 수 있습니다.

Clash가 호스트 루프백 주소에서만 수신한다면 컨테이너에서 여전히 연결할 수 없을 수 있습니다. 로컬 네트워크 허용을 켜거나 mixed-port가 컨테이너에서 접근 가능한 인터페이스를 수신하게 할 때는 시스템 방화벽으로 출처도 제한하세요. 7890을 공용 네트워크에 직접 노출하면 안 됩니다.

아래 앞부분은 이미 실행 중인 컨테이너 터미널에서 실행하세요. 마지막 docker run은 Linux 호스트 터미널용이며 임시 테스트 컨테이너를 만듭니다. 개발 컨테이너 안에서 다시 실행하지 마세요. 두 곳의 7890을 모두 클라이언트의 실제 포트로 바꾸세요.

컨테이너에서 포트부터 검증, CLASH_PORT는 클라이언트의 실제 mixed-port에 맞게 수정
CLASH_HOST=host.docker.internal
CLASH_PORT=7890
getent hosts "$CLASH_HOST" || true
curl -v -x "http://$CLASH_HOST:$CLASH_PORT" https://www.example.com/

# Linux Docker 临时测试
docker run --rm --add-host=host.docker.internal:host-gateway curlimages/curl:latest -v -x http://host.docker.internal:7890 https://www.example.com/

Linux 호스트에서는 host-gateway를 devcontainer.json에 기록

.devcontainer/devcontainer.json 일부
{
  "runArgs": [
    "--add-host=host.docker.internal:host-gateway"
  ]
}

수정 후 Dev Containers: Rebuild Container를 실행하세요. 기존 컨테이너에는 새 hosts 매핑이 자동으로 적용되지 않습니다.

Docker Compose 프로젝트에서는 서비스 아래에 extra_hosts: ["host.docker.internal:host-gateway"]를 사용합니다. 두 곳에 중복해서 추가하지 마세요.

컨테이너에서 호스트 포트에 접근할 수 있으면 containerEnv와 remoteEnv 설정하기

컨테이너의 curl이 프록시를 통해 HTTP 응답을 받은 뒤 도구 환경을 설정하세요. 아래 예제는 Dockerfile 또는 image 프로젝트용이며 도구별 호환성을 위해 대문자와 소문자 변수를 함께 제공합니다. 실제 mixed-port로 바꾸세요. Compose에서는 컨테이너 변수를 서비스의 environment에 넣고 devcontainer.json에는 remoteEnv만 둡니다.

프로젝트 수준 devcontainer.json 예시
{
  "containerEnv": {
    "HTTP_PROXY": "http://host.docker.internal:7890",
    "HTTPS_PROXY": "http://host.docker.internal:7890",
    "NO_PROXY": "localhost,127.0.0.1,::1,host.docker.internal",
    "http_proxy": "http://host.docker.internal:7890",
    "https_proxy": "http://host.docker.internal:7890",
    "no_proxy": "localhost,127.0.0.1,::1,host.docker.internal"
  },
  "remoteEnv": {
    "HTTP_PROXY": "${containerEnv:HTTP_PROXY}",
    "HTTPS_PROXY": "${containerEnv:HTTPS_PROXY}",
    "NO_PROXY": "${containerEnv:NO_PROXY}",
    "http_proxy": "${containerEnv:http_proxy}",
    "https_proxy": "${containerEnv:https_proxy}",
    "no_proxy": "${containerEnv:no_proxy}"
  }
}

HTTPS_PROXY를 http://로 시작하는 것이 일반적으로 맞습니다. HTTP CONNECT 프록시를 통해 HTTPS에 접근한다는 뜻이며 프록시 포트 자체가 HTTPS로 바뀌는 것은 아닙니다. NO_PROXY에는 Compose 서비스 이름, 회사 내부망과 로컬 개발 도메인도 추가해야 합니다. 그렇지 않으면 컨테이너가 데이터베이스 또는 API에 접근할 때 Clash를 우회 경로가 아니라 불필요한 경로로 사용할 수 있습니다.

저장 후 Rebuild Container를 실행하고 새 터미널에서 env | grep -i proxy를 실행합니다. 기존 터미널 프로세스에는 remoteEnv가 자동으로 갱신되지 않습니다.

이미지 가져오기와 Dockerfile 빌드는 별도로 구성

이미지 가져오기는 Docker Desktop 또는 빌더/daemon 쪽에서 설정하고 Dockerfile의 RUN에는 build.args를 사용합니다. Linux에서 앞의 runArgs 호스트 매핑은 실행 컨테이너에만 적용되므로 빌드에는 아래 build.options 매핑도 필요합니다. 개인 프록시나 인증 정보를 ENV로 이미지에 남기지 마세요.

devcontainer 빌드 매개변수 일부, 7890은 실제 mixed-port에 맞게 수정
{
  "build": {
    "dockerfile": "Dockerfile",
    "args": {
      "HTTP_PROXY": "http://host.docker.internal:7890",
      "HTTPS_PROXY": "http://host.docker.internal:7890",
      "NO_PROXY": "localhost,127.0.0.1",
      "http_proxy": "http://host.docker.internal:7890",
      "https_proxy": "http://host.docker.internal:7890",
      "no_proxy": "localhost,127.0.0.1"
    },
    "options": [
      "--add-host=host.docker.internal:host-gateway"
    ]
  }
}

curl 성공 후에도 Git, apt와 확장 기능을 각각 테스트

컨테이너 터미널에서 HTTP_PROXY, HTTPS_PROXY와 NO_PROXY가 나타나는지 확인한 뒤 Git과 패키지 관리자 명령을 각각 실행합니다. Git은 대개 환경 변수를 읽지만 계속 기존 주소를 가리킨다면 Git 자체 구성도 확인하세요.

확장 보기나 devcontainer.json으로 확장을 설치할 때는 로컬 컴퓨터와 컨테이너 측 VS Code Server 모두 Marketplace와 다운로드 CDN에 접속할 수 있어야 합니다. remoteEnv를 바꾼 뒤 다시 연결하고 새 터미널을 열어 출력 패널에서 실제로 실패한 요청을 확인하세요. remoteEnv만 바꿨다면 컨테이너 전체를 다시 빌드할 필요는 없습니다.

컨테이너 내부 Git 확인, REPO_URL은 프로젝트의 공개 저장소로 교체 가능
REPO_URL='https://github.com/octocat/Hello-World.git'
env | grep -i '_proxy'
git config --show-origin --get-regexp 'http.*proxy' || true
git ls-remote "$REPO_URL"

# 单次指定代理,不修改已有全局设置
git -c http.proxy=http://host.docker.internal:7890 ls-remote "$REPO_URL"

curl은 성공하지만 Git은 계속 기존 포트에 연결

git config --show-origin --get-regexp 'http.*proxy'를 실행하고 사용자 또는 저장소에 남은 설정을 정리합니다.

apt update 실패

실제 저장소 도메인, 인증서와 /etc/apt/apt.conf.d의 별도 프록시를 확인합니다.

프로젝트는 실행되지만 원격 확장 기능을 설치할 수 없음

Dev Containers 로그에서 marketplace 요청을 찾고 원격 서비스가 remoteEnv를 상속했는지 확인합니다.

컨테이너는 인터넷에 연결되지만 로컬 데이터베이스 연결 실패

데이터베이스 서비스 이름과 사설 대역을 NO_PROXY에 추가합니다.

Rebuild 단계에서 계속 실패

daemon의 이미지 가져오기 또는 Dockerfile build args로 돌아가고 실행 중인 컨테이너는 더 수정하지 않습니다.

공유 저장소에 개인 프록시를 고정하지 마세요

팀 프로젝트에서는 devcontainer.json의 ${localEnv:HTTP_PROXY}로 개발자 로컬 환경을 읽거나 실제 주소가 없는 .env.example을 제공할 수 있습니다.

README에 HTTP_PROXY가 http://host.docker.internal:7890 같은 전체 주소여야 한다고 명시하세요. 커밋 전에 프록시 URL에 사용자 이름, 비밀번호와 token이 없는지 확인합니다.

프로젝트를 공유하기 전에 docker pull, Rebuild Container, 컨테이너 내부 Git/패키지 관리자와 로컬 서비스 접근을 각각 확인하세요. 실패한 명령과 로그를 저장하고 이 글의 해당 설정 위치를 점검하면 됩니다. 모든 프록시 설정을 동시에 바꿀 필요는 없습니다.

참고 자료