현대 소프트웨어 개발에서 CI/CD(지속적 통합/지속적 배포) 파이프라인은 빠른 개발 사이클과 안정적인 배포를 위한 핵심 요소가 되었습니다. 특히 보안이 중요한 프로젝트에서는 VPN과 같은 보안 인프라를 CI/CD 파이프라인과 결합하는 것이 필수적입니다. 이 글에서는 GitHub Actions, FortiGate VPN, Docker Private Registry를 활용한 실제 CI/CD 파이프라인 구축 사례를 공유하고자 합니다.
이 CI/CD 파이프라인은 다음과 같은 구성 요소로 이루어져 있습니다:
다음은 실제 사용된 GitHub Actions 워크플로우 파일을 민감한 정보를 제거하고 분석한 내용입니다:
name: Deploy to Docker Private Registry via FortiGate VPN
on:
push:
branches:
- topic/ci-cd # 특정 브랜치에 push될 때 워크플로 실행
jobs:
build-and-deploy:
runs-on: ubuntu-latest # Ubuntu 최신 버전에서 실행
steps:
# 리포지토리 체크아웃
- name: Checkout repository
uses: actions/checkout@v2
# openfortivpn 설치
- name: Install openfortivpn
run: sudo apt-get update && sudo apt-get install -y openfortivpn
# Forti VPN 연결
- name: Connect to FortiGate VPN
run: |
sudo openfortivpn [VPN_SERVER]:[VPN_PORT] \
-u [VPN_USERNAME] \
-p "[VPN_PASSWORD]" \
--trusted-cert "[VPN_CERT_SHA1]" &
sleep 10
# Docker Private Registry 인증서 적용
- name: Apply Docker Private Registry Cert
run: |
echo "[CERTIFICATE_CONTENT]" | sudo tee /usr/local/share/ca-certificates/dpr.crt
sudo update-ca-certificates
sudo systemctl restart docker
# Docker 이미지 빌드 및 태그
- name: Build and tag Docker image
run: |
docker build -t api-service-dev .
docker tag api-service-dev [REGISTRY_URL]/api-service-dev:latest
# Docker Private Registry에 로그인
- name: Login to Docker Private Registry
run: echo [REGISTRY_PASSWORD] | docker login [REGISTRY_URL] -u [REGISTRY_USERNAME] --password-stdin
# SSH 설정
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "[SSH_PRIVATE_KEY]" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan [SERVER_IP] >> ~/.ssh/known_hosts
# 내부 서버 컨테이너 교체
- name: Deploy on Server
run: |
ssh -o StrictHostKeyChecking=no root@[SERVER_IP] "bash -s" << 'EOF'
cd /app/config
docker-compose -f app-service.yml pull api-service
docker-compose -f app-service.yml up -d --no-deps api-service
echo "api-service container deployment completed."
EOF
on:
push:
branches:
- topic/ci-cd
이 설정은 topic/ci-cd
브랜치에 코드가 푸시될 때만 워크플로우가 실행되도록 합니다. 특정 브랜치를 지정함으로써 모든 브랜치의 변경에 반응하지 않고, 배포 준비가 완료된 코드만 파이프라인에 들어가도록 제한합니다.
- name: Install openfortivpn
run: sudo apt-get update && sudo apt-get install -y openfortivpn
- name: Connect to FortiGate VPN
run: |
sudo openfortivpn [VPN_SERVER]:[VPN_PORT] \
-u [VPN_USERNAME] \
-p "[VPN_PASSWORD]" \
--trusted-cert "[VPN_CERT_SHA1]" &
sleep 10
이 단계에서는 openfortivpn
클라이언트를 설치하고 FortiGate VPN에 연결합니다. GitHub Secrets에 저장된 VPN 접속 정보를 사용하여 내부망에 안전하게 접근합니다. &
연산자를 사용하여 VPN 연결을 백그라운드로 실행하고, sleep 10
명령으로 연결이 완전히 설정될 때까지 기다립니다.
- name: Apply Docker Private Registry Cert
run: |
echo "[CERTIFICATE_CONTENT]" | sudo tee /usr/local/share/ca-certificates/dpr.crt
sudo update-ca-certificates
sudo systemctl restart docker
Docker Private Registry가 자체 서명된 인증서를 사용하는 경우, 이 단계에서 인증서를 시스템에 추가합니다. 이를 통해 Docker 클라이언트가 프라이빗 레지스트리를 신뢰할 수 있게 됩니다.
- name: Build and tag Docker image
run: |
docker build -t api-service-dev .
docker tag api-service-dev [REGISTRY_URL]/api-service-dev:latest
- name: Login to Docker Private Registry
run: echo [REGISTRY_PASSWORD] | docker login [REGISTRY_URL] -u [REGISTRY_USERNAME] --password-stdin
이 단계에서는 리포지토리의 코드로 Docker 이미지를 빌드하고, 적절한 태그를 붙인 후, 프라이빗 레지스트리에 로그인합니다. GitHub Secrets에 저장된 레지스트리 자격 증명을 사용하여 인증합니다.
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "[SSH_PRIVATE_KEY]" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan [SERVER_IP] >> ~/.ssh/known_hosts
- name: Deploy on Server
run: |
ssh -o StrictHostKeyChecking=no root@[SERVER_IP] "bash -s" << 'EOF'
cd /app/config
docker-compose -f app-service.yml pull api-service
docker-compose -f app-service.yml up -d --no-deps api-service
echo "api-service container deployment completed."
EOF
마지막 단계에서는 SSH를 설정하고 내부 서버에 접속하여 배포를 수행합니다. Docker Compose를 사용하여 이미지를 풀하고 컨테이너를 업데이트합니다. --no-deps
옵션은 종속 서비스를 재시작하지 않고 해당 서비스만 업데이트하도록 합니다.
이 CI/CD 파이프라인은 다음과 같은 보안 이점을 제공합니다:
이 파이프라인의 기술적 이점은 다음과 같습니다:
현재 CI/CD 파이프라인은 기본적인 기능을 제공하지만, 다음과 같은 방향으로 발전시킬 수 있습니다:
GitHub Actions와 FortiGate VPN을 결합한 CI/CD 파이프라인은 보안성과 자동화를 동시에 달성할 수 있는 효과적인 솔루션입니다. 내부망 접근이 필요한 프로젝트에서도 현대적인 CI/CD 프로세스를 구현할 수 있으며, 특히 Private Docker Registry와 연계하여 컨테이너 기반 애플리케이션의 배포를 안전하게 자동화할 수 있습니다.
이러한 접근 방식은 개발 생산성을 향상시키면서도 보안 요구사항을 충족시켜, 엔터프라이즈 환경에서 DevSecOps 문화를 정착시키는 데 큰 도움이 됩니다.