LightSwitch Logo

 

안녕하세요. LightSwitch 서비스에서 인프라 개발에 참여했던 박현우 입니다. 

LightSwitch는 오픈소스 피쳐플래깅 솔루션으로, 도커 이미지 형태로 누구나 쉽게 서비스에 피쳐 플래깅을 도입할 수 있는 서비스입니다.

저는 인프라와 자바스크립트 SDK 개발자로서 LightSwitch 팀에서 함께 프로젝트를 진행했는데요, 서비스의 인프라를 구성하면서 겪은 문제를 공유하고자 정리해 보았습니다.

Nginx 구성하기

인프라 담당으로서 첫 번째로 담당했던 서비스 SPOPARTY에서는 호스트 운영체제에 바로 Nginx를 설치하여 사용했는데, LightSwitch 서비스에서는 nginx도 컨테이너로 관리하는 방식을 이용하게 되었습니다.

따라서, 컨테이너화된 nginx에서 리버스 프록시를 활용하여 backend 서버로 요청을 리다이렉트 해야 할 필요성이 있었습니다. 팀원들과의 대화 및 구글링을 통해 컨테이너 이름으로 리버스 프록시를 활용할 수 있겠다고 생각했지만, 생각만큼 쉽게 적용되지 않았습니다.

이슈 상황

첫번째 이슈 해결

먼저 Nginx와 backend의 네트워크 연결을 위해 첫 번째로 nginx에서 backend 컨테이너의 이름으로 리버스 프록시 설정을 했었습니다. 하지만 docker 컨테이너 생성 시 기본적으로 bridge 네트워크에 소속되며, 기본 네트워크에서는 컨테이너 기반의 DNS 해석이 지원되지 않기 때문에 컨테이너 간 직접적인 연결이 불가능한 상황이었습니다. 따라서 backend 컨테이너로의 직접적인 연결을 하지 않고, 호스트 운영체제의 도메인으로 redirect 시켜 문제를 해결했습니다.

첫번째 이슈 해결 문제점

하지만, 호스트 운영체제를 통해 nginx 컨테이너와 backend 컨테이너가 통신을 하게 된다면 불필요한 네트워크 요청이 추가되게 됩니다.

// 현재 문제점
외부 요청 -> Host OS -> Nginx -> Host OS -> Backend
// 개선 한다면 아래와 같이 될 것이다.
외부 요청 -> Host OS -> Nginx -> Backend

두번째 이슈 해결

Nginx 컨테이너에서 backend 컨테이너로 직접적으로 요청을 전달하기 위해서는 내부 IP를 이용할 수 있을 것 이라 생각하여 컨테이너의 IP로 직접 요청을 전달하도록 수정했습니다.

docker inspect ${Docker Container 이름}

"IPAddress": "172.17.0.2",

위 명령어를 통해 얻어낸 IP 주소를 nginx의 proxy_pass에 직접 설정하여 요청을 전달했고, 정상적으로 동작하는 것을 확인했습니다.

두번째 이슈 해결 문제점

하지만 위와 같은 방식을 이용한다면 컨테이너를 재시작 하는 경우 Nginx 설정을 변경해야 하는 번거로움이 있었습니다. 컨테이너에 할당되는 IPAddress는 고정된 값이 아니기 때문입니다.

세번째 이슈 해결

많은 예제 글에서 항상 docker-compose로 컨테이너들을 묶고, docker-compose 파일 내에서 network 설정을 통해 연결을 경우가 많이 있었는데, 동일한 방법으로 이슈를 해결할 수 있었습니다.

그렇다면 어떻게 docker-compose와 network 설정하는 방법으로 통신이 가능한 것인지 궁금했고, 이를 해결하기 위해서는 Docker Network에 대한 이해가 필요할 수 있음을 깨달을 수 있었습니다.

Docker Network란?

도커 네트워크(Docker Network)란 Docker 컨테이너 간의 통신을 관리하고 격리하기 위한 기능을 제공하는 것입니다.

컨테이너화된 애플리케이션은 여러 개의 컨테이너로 구성될 수 있는데, 이들 컨테이너가 서로 통신하고 데이터를 주고 받아야 할 경우가 있습니다.

도커 네트워크를 통해 이러한 컨테이너 간 통신을 쉽게 설정하고 관리할 수 있도록 도와줍니다.

정리하자면, 같은 호스트 내에서 실행 중인 컨테이너 간 연결할 수 있도록 돕는 논리적 네트워크 개념이다.

Docker Network Driver의 종류

  • bridge : 호스트 컴퓨터 내에서 여러 컨테이너들이 서로 소통할 수 있도록 해줍니다.(기본 네트워크 드라이버)
  • host : 컨테이너와 Docker 호스트 간의 네트워크 격리를 제거합니다.
  • none : 컨테이너를 호스트 및 다른 컨테이너로부터 완전히 격리합니다.
  • overlay : 오버레이 네트워크는 여러 Docker 데몬을 함께 연결합니다.
  • ipvlan : ipvlan 네트워크는 IPv4 및 IPv6 주소 지정에 대한 완전한 제어를 제공합니다.
  • macvlan : 컨테이너에 MAC 주소를 할당합니다.

실제 네트워크 확인해보기

현재 네트워크 목록을 확인하기 위해서 아래의 명령어를 통해 확인할 수 있습니다.

docker network ls

다양한 네트워크가 존재하는 것을 확인할 수 있습니다. 이 중 기본으로 적용되는 네트워크인 bridge에 포함된 컨테이너들을 확인해보겠습니다.

docker network inspect bridge

현재 bridge 네트워크에 소속된 컨테이너들을 확인할 수 있으며, Options에서 기본 네트워크라는 사실을 알 수 있습니다.

위를 보면 컨테이너의 이름이 포함되어 있음에도 왜 Docker는 bridge 네트워크에 대해 DNS 기반 해석을 제공하지 않는 걸까요? 아시는 분은 댓글 좀…

마지막으로, 세 번째 이슈 해결에서 말했던 docker-compose로 실행 시에는 어떻게 DNS 접근이 가능했던 것인지 알아봅시다.

docker-compose up 명령어를 통해 컨테이너화 하면, docker network ls 를 통해 네트워크를 확인 시 아래와같이 새로운 network가 생성됩니다. 네트워크 생성 정책은 폴더명_default로 생성되는 듯합니다.

생성한 네트워크에 소속된 컨테이너 목록을 확인해보겠습니다.

네트워크 내에 docker-compose에 포함된 컨테이너들이 모두 있는 것을 확인할 수 있습니다.

문제의 원인 정리

즉, docker-compose로 컨테이너들을 함께 관리할 경우 기본적으로 하나의 bridge 타입 네트워크가 생성되며, 기본 네트워크가 아니기 때문에 DNS 해석이 가능하여 문제를 해결할 수 있었던 것입니다. 만약 DNS 해석이 필요한 경우 사용자 정의 Network Driver를 생성하거나, link 설정 또는 docker-compose로 컨테이너를 관리하는 것이 선행되어야 합니다.

마무리

Docker에 대한 이해가 부족한 상태로 개발을 진행했던 것이 부끄럽게 느껴지기도 하지만, 실제로 트러블 슈팅을 경험하면서 적은 글이 독자님들에게 더 깊은 공감을 끌어낼 수 있다고 생각하여 부족한 실력에도 글을 공유합니다.

또한, 재배포 없이 쉽게 A/B 테스팅, 타겟 테스팅을 지원하는 오픈소스 피쳐플래깅 서비스 LightSwitch에도 많은 관심 부탁드립니다.

감사합니다.

LightSwitch 레포지토리 바로가기

Reference

Networking overview

Docker 네트워크 사용법

Docker Network 구조(4) - container link 구조

Bridge network driver

개요

Jenkins Container 환경 설정 시 웹 페이지의 경로를 변경하고, nginx 프록시 할 수 있도록 구성하는 방법을 알아보자.

Host OS 내에 jenkins container를 이미 구성한 상태에서 접속할 수 있는 경로만 변경하는 작업을 포스팅 한다.

환경

필자의 경우 Host OS(Ubuntu Linux) 위에 Jenkins Container를 구성했고, Host OS에 Nginx를 설치하여 리버스 프록시를 활용하여 Jenkins 관리 웹 페이지로 이동시키게끔 구성했다.

Jenkins Container 설정

StackOverflow의 글에서 경로를 변경하기 위해서 환경변수를 설정할 수 있다는 글을 확인했다.

Change jenkins container deployment root path

Jenkins Container 생성 당시 입력했던 환경 변수를 확인해보자.

Host OS에서 아래의 명령어로 컨테이너 생성 당시 입력했던 환경변수를 확인 가능하다.

docker exec jenkins(컨테이너 이름) /usr/bin/env
PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=6719f87953bd
JENKINS_OPTS=--httpPort=8080
TZ=Asia/Seoul
LANG=C.UTF-8
JENKINS_HOME=/var/jenkins_home
JENKINS_SLAVE_AGENT_PORT=50000
REF=/usr/share/jenkins/ref
JENKINS_VERSION=2.447
JENKINS_UC=https://updates.jenkins.io
JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental
JENKINS_INCREMENTALS_REPO_MIRROR=https://repo.jenkins-ci.org/incrementals
COPY_REFERENCE_FILE_LOG=/var/jenkins_home/copy_reference_file.log
JAVA_HOME=/opt/java/openjdk
HOME=/root

위의 글에서 확인한 접속 prefix를 추가하기 위해서는 아래의 환경변수를 추가하면 된다.

JENKINS_OPTS="--prefix=/jenkins"

나는 아래의 문서 일부를 읽은 후 지원하지 않는 기능인 줄 알았는데, 최근 댓글을 보면 실행중인 컨테이너 위에 환경변수를 추가할 수 있는 방법이 있는 것 같다.

How to set an environment variable in a running docker container

필자의 경우 컨테이너에 환경변수를 추가하여 다시 올리는 방식으로 작업했다.

docker run -d --env JENKINS_OPTS="--httpPort=8080 --prefix=/build" --prefix=/build -v /etc/localtime:/etc/localtime:ro -e TZ=Asia/Seoul -p 8080:8080 -v /jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -v /usr/local/bin/docker-compose:/usr/local/bin/docker-compose --name jenkins -u root jenkins/jenkins:jdk17

또한 path/build 경로로 들어오는 요청을 nginx에서 8080 포트로 redirect 하도록 설정해줬다.

sudo vim /etc/nginx/sites-enabmed/default

...

location /build/ {
                proxy_pass <http://localhost:8080/build/>;
        }
...

이후 nginx를 재시작 해줬다.

sudo service nginx restart

+ Recent posts