Git-sync와 사이드카 패턴
6 min read
이번 포스트에서는 사이드카 패턴 중 하나인 git-sync에 대해서 알아보고 어떤 경우에 유용하게 사용할 수 있는 알아보도록 하겠습니다.
사이드카 패턴?
사이드카 패턴이란 쿠버네티스와 같이 컨테이너 오케스트레이션 툴에서 구성할 수 있는 컨테이너 배치 패턴으로 마치 오토바이 옆에 붙어 있는 사이드카와 비슷한 형태의 컨테이너 구성을 가졌다고 하여 이름이 붙여졌습니다. 쿠버네티스에서 사이드카 패턴을 구현하는 방법은 무척 간단합니다. containers
keyword에 컨테이너를 하나 더 리스트의 원소로 추가만 하면 됩니다. (아래 git-sync 예시에서 더 자세히 다루겠습니다.) 이때 Pod안에의 두개 컨테이너가 서로 통신하기 위해 네트워크를 공유(이 경우는 아무런 작업을 하지 않아도 localhost
로 접근이 가능하죠)하거나 volume을 공유하기도 합니다. (이 경우 volume을 서로 연결해 줘야합니다.) 그렇다면 사이드카 컨테이너를 추가하면 어떤 장점이 있기에 이런 패턴을 사용하는 것 일까요?
사이드카 패턴의 활용도
** 사이드카 패턴의 활용도를 소개할 때, 쿠버네티스 founder 중 한분인 Brendan Burns의 책, Designing Distributed System에서 많은 부분 참고하여 작성하였습니다. 분산 환경 시스템에서 어떻게 컴포넌트들을 설계하면 좋을지 잘 소개되어 있는 책으로 한번 살펴보시길 추천 드립니다.**
1. 기존 로직의 변경 없이 새로운 기능 추가
사이드카 컨테이너를 통해 기존의 로직은 그대로 놔둔체 새로운 기능을 덧붙일 수 있습니다. 가장 대표적인 예로 기존 http 프로토콜에 대해서만 서비스를 하는 웹서버에 tls layer를 추가하고 싶은 경우, 메인 컨테이너인 기존의 legacy 웹서버는 그대로 놔둔체 사이드카 컨테이너를 통해 https 서비스를 클라이언트에게 제공할 수 있습니다.
2. 동적 설정값 변경
사이드카 컨테이너를 이용하여 동적으로 application 설정값을 변경시킬 수도 있습니다. 메인 컨테이너에서는 특정 시그널에 따라 미리 정의된 디렉토리의 설정파일을 새롭게 읽어 들여 설정값을 변경하도록 만들고 사이드 컨테이너에서는 Config 저장소의 설정값을 항상 지켜보다 변경사항이 발생한 경우, 설정값을 업데이트하여 메인 컨테이너에게 이를 알리는 역할을 담당합니다. 이를 통해 사용자가 원할 때에 Config 저장소의 설정값만 변경하면 동적으로 application의 설정을 변경할 수 있게 됩니다.
3. 컨테이너 재사용성
사이드카 컨테이너를 단일한 기능을 하게 모듈화를 잘하면 여러 다른 곳에서 재사용하기가 좋습니다. 대부분의 app에서는 로깅, 실행 프로세스 정보 확인 등의 작업들이 필요합니다. 이때, 미리 인터페이스만 잘 맞춰 놓으면 매번 로깅 컴포넌트를 개발할 필요 없이, 하나의 사이드카 컨테이너로 다 해결할 수 있습니다. 예를 들어, 로그 수집 사이드카 컨테이너를 생각해 볼 수 있습니다. 메인 컨테이너에서 미리 지정된 디렉토리에 어플리케이션 로그를 쌓으면 동일한 사이드카 컨테이너로 해당 로그를 로그 저장소에 저장하여 따로 로그를 분석하거나 더 오랜 기간 로그를 확인을 할 수 있게 됩니다.
4. 간단한 PaaS 구현
마지막으로 사이드카 컨테이너를 비즈니스 로직을 제공 컨테이너로 활용하고 메인 컨테이너에서는 단지 실행환경을 제공하는 역할만 담당하게 하는 PaaS 서비스를 생각해 볼 수 있습니다. PaaS라는 것이 결국 Platform을 제공해주고 그 안에 들어가는 application 로직은 사용자가 정의하는 서비스라고 할 수 있습니다. 사이드카 패턴에서 이를 비교해 보자면 메인 컨테이너가 비즈니스 로직의 실행 환경을 제공해 주는 plaltform으로써 존재하고 사이드카 컨테이너가 사용자가 입맛에 따라 로직을 정의하여 플랫폼에 올리는 역할을 합니다. 이를 통해 비즈니스 상황에 따라 바뀌게 되는 비즈니스 로직을 손쉽게 업데이트할 수 있고 비교적 자주 바뀌지 않는 런타임 환경은 안정적으로 서비스할 수 있게 만들어 줍니다.
Git-sync란
git-sync란 사이드카 컨테이너로, 깃 저장소의 코드 및 데이터를 주기적으로 로컬 디렉토리와 싱크를 맞춰주는 컨테이너입니다. 메인 컨테이너가 어떤 역할을 가졌던지 상관하지 않고 오직 원격 깃 저장소의 정보와 싱크를 맞추는 것에만 관심을 가집니다. git-sync 사이드카 앱은 공식 쿠버네티스 프로젝트 아래에서 관리될 정도로 표준적이며 많이 쓰이는 패턴입니다. https://github.com/kubernetes/git-sync
Git-sync 사용 방법
git-sync를 사용하는 방법은 직관적이고 정말 간단합니다. 먼저 메인 컨테이너로 ubuntu:18.04
컨테이너를 사용하고 사이드카 컨테이너로 git-sync를 생성합니다. 이때 /git
이라는 디렉토리를 emptyDir로 서로 마운트 시켜 연결합니다.
apiVersion: v1
kind: Pod
metadata:
name: git-sync-test
spec:
containers:
###########################
# 첫번째, 메인 컨테이너
###########################
- name: ubuntu
image: ubuntu:18.04
args:
- sleep
- "1000000"
volumeMounts:
- name: mygit
mountPath: "/git"
###########################
# 두번째, 사이드카 컨테이너
###########################
- name: git-sync
image: k8s.gcr.io/git-sync:v3.1.1
env:
- name: GIT_SYNC_REPO # git sync할 리모트 저장소
value: https://github.com/hongkunyoo/gitops-argocd.git
- name: GIT_SYNC_BRANCH # target branch
value: master
- name: GIT_SYNC_ROOT # 로컬 디렉토리 위치
value: "/git"
volumeMounts:
- name: mygit
mountPath: "/git"
restartPolicy: Never
# 서로 연결할 volume
volumes:
- name: mygit
emptyDir: {}
이때 사용되는 환경변수는 다음과 같습니다.
GIT_SYNC_REPO
: 싱크할 git 리모트 저장소입니다.GIT_SYNC_BRANCH
: 어떤 branch를 바라보고 sync할지 결정합니다.GIT_SYNC_REV
: 예시에는 넣지 않았지만 어떤revision
(tag, hash)를 기준으로 sync할 것인지 결정할 수도 있습니다.GIT_SYNC_ROOT
: 내 로컬 어느 위치의 디렉토리에 sync를 할지 결정합니다.GIT_SYNC_DEST
:GIT_SYNC_ROOT
아래에 어떤 이름의 디렉토리로 생성할지 결정합니다. 생략하게 되면 remote git repository의 이름이 쓰이게 됩니다. (예시에서는gitops-argocd.git
이라는 이름으로 디렉토리가 생성됩니다.) 그 외 자세한 환경변수 설정값은 다음 링크를 참고하세요. https://github.com/kubernetes/git-sync#parameters
Pod를 생성했다면 이제 직접 들어가서 확인을 해보겠습니다.
# 메인 컨테이너로 직접 접속하여
kubectl exec -it git-sync-test -c ubuntu -- bash
# /git 디렉토리를 살펴보면 다음과 같은 디렉토리 구조를 확인할 수 있습니다.
ls -l /git
# lrwxrwxrwx 1 65533 65533 44 Feb 22 07:38 gitops-argcd.git -> rev-XXXXXX
# drwxr-xr-x 2 65533 65533 4096 Feb 22 07:38 rev-XXXXXX
# DEST 디렉토리 아래를 살펴보면 리모트 git 저장소의 코드가 clone된 것을 확인할 수 있습니다.
ls -l /git/gitops-argcd.git/
# -rw-r--r-- 1 65533 65533 100 Feb 22 07:38 README.md
# -rw-r--r-- 1 65533 65533 306 Feb 22 07:38 deployment.yaml
# -rw-r--r-- 1 65533 65533 149 Feb 22 07:38 service.yaml
cat /git/gitops-argocd.git/service.yaml
# apiVersion: v1
# kind: Service
# metadata:
# name: mynginx
# spec:
# ports:
# - port: 80
# protocol: TCP
# targetPort: 80
# selector:
# run: mynginx
이제 github 페이지에서 직접 리모트 저장소의 코드를 수정하여 commit
하고 다시 메인 컨테이너의 GIT_SYNC_DEST
디렉토리를 확인해 보면 수정된 코드가 바로 반영되어 있는 것을 확인할 수 있습니다.
cat /git/gitops-argocd.git/service.yaml
# apiVersion: v1
# kind: Service
# metadata:
# name: mynginx
# spec:
# ports:
# - port: 80
# protocol: TCP
# targetPort: 8080 # 80 --> 8080
# selector:
# run: mynginx
참고하실 점은, 반대로 git-sync 컨테이너에서의 소스 코드 변경이 자동으로 리모트 저장소의 변경으로 이어지지는 않는 점을 유념하시기 바랍니다. git-sync는 upstream의 저장소를 pull하는 역할만 담당합니다.
Git-sync 활용 방법
지금까지 git-sync의 사용법에 대해서 알아보았는데 이를 어디에서 활용할 수 있을지 알아보겠습니다. 저는 git-sync의 장점을 극대화할 수 있는 곳은 바로 로직 개발 및 코드 수정이 빈번히 일어나는 프로젝트라 생각합니다. cloud native이라는 새로운 패러다임의 변화로 점점 더 많은 프로젝트에서 컨테이너 기반으로 app을 개발하게 되었습니다. 이때 아무리 도커 이미지를 빌드를 할 때 layer 캐싱을 통해 매번 새롭게 빌드를 하지 않는다고는 하지만 여전히 코드 한줄을 수정하고 다시 이미지를 빌드하고 업로드&다운로드하는 작업은 번거롭습니다. 이때 git-sync를 통하여 app을 개발하게 되면 작은 수정에 대해 매번 빌드 > 업로드 > 다운로드
작업 없이 git을 통하여 바로 코드를 배포할 수 있게 됩니다. 물론 이러한 방법도 완전 공짜가 아니라 매번 깃 commit
& push
작업을 해줘야겠지만 이미지 자체를 변경하는 시간보다는 비교적 가벼운 작업이라 생각합니다. 또한 깃 커밋 로그가 지저분해질 수도 있겠지만 적절한 브랜치 구분과 squash
및 rebase
명령을 통해 이러한 문제를 해결할 수 있습니다. 점점 더 소프트웨어 개발에서 lean하고 애자일한 방법이 중요해지는 상황에서 (네, 그렇습니다. fancy해 보이는 말은 다 넣어봤습니다.) 빠르게 비즈니스 로직을 적용할 수 있는 방법으로 git-sync를 한번 고민해 보는 것도 나쁘지 않아 보입니다.