지난 편까지 프로토 타입 앱을 배포하고 외부 접속에 성공했습니다. 이제 매번 터미널에서 'kubectl apply -f ...' 를 치는 원시적인 배포 방식과 작별할 시간입니다. 오늘은 쿠버네티스 배포 GitOps를 ArgoCD로 구현하겠습니다.
Foundry 프로젝트를 진행하면서 코드를 수정할 때마다 이런 과정을 반복했습니다.
- 코드 수정 & 커밋
- 도커 이미지 빌드 & 푸시
- foundry.yaml 의 이미지 태그 수정
- kubectl apply -f ... 실행
매번 이 과정이 반복되기도 하고, 태그 수정을 까먹기도 합니다. 그래서 Git 에 있는 내용이 곧 서버의 상태가 되도록 하는 GitOps 를 구현하겠습니다.
1. ArgoCD 설치
kubectl create namespace cicd-system
kubectl apply -n cicd-system -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
설치가 끝나면 초기 비밀번호를 확인하여 로그인합니다. 초기 계정은 'admin' 입니다.
# 초기 비번 확인
kubectl -n cicd-system get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
2. ArgoCD 대시보드 외부 노출
ArgoCD는 시각화된 대시보드가 있습니다. 이번에는 Cloudflare Tunnel + Ingress 조합을 사용해 보겠습니다.
'argocd.heejunp.com' 도메인으로 접속할 수 있도록 Ingress를 만들었습니다. 여기서 중요한 건 ArgoCD 서버가 기본적으로 HTTPS를 쓰기 때문에, 백엔드 프로토콜을 HTTPS로 지정해줘야 합니다.
저는 개발을 편하게 하기 위해 외부 노출을 했습니다. 하지만 ArgoCD 는 권한이 크기때문에, 'Cloudflare Access' 기능을 켜서 안전하게 만들었습니다.
# argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: cicd-system
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # ArgoCD는 자체 인증서를 씀
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
spec:
ingressClassName: nginx
rules:
- host: argocd.heejunp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443
3. Git Repo 연결
이제 ArgoCD에게 "내 Git 리포지토리의 k8s/ 폴더를 감시해!" 라고 알려줄 차례입니다. 그래서 기존의 'foundry.yaml' 파일을 분리를 했습니다. ArgoCD 가 Sync 를 맞추면서 기존의 pod 를 제거하지 않을까? 걱정이 있었는데, ArgoCD 는 "무중단 배포(Rolling Update)" 혹은 "기존 자원 입양(Adoption)" 방식을 사용합니다. 그래서 서버 설정과 내용이 똑같으면 관리 flag 만 꽂고 재시작하지 않습니다.
- Repositories 연결: GitHub의 Foundry 리포지토리 URL 등록.
- Application 생성:
- Application: foundry
- Project: default
- Source: 내 Git 리포지토리 URL + 경로(k8s/)
- Destination: in-cluster (로컬 클러스터) + Namespace(apps)
- Sync Policy: Automatic (자동 동기화) + Prune (Git에서 지우면 실제 리소스도 삭제) + Self Heal (누가 서버에서 몰래 고치면 다시 원상복구)


하지만 한 번에 되면 이상하죠. 어김없이 오류가 생겼습니다.

ArgoCD 를 설치하면서 namespace 를 argocd 에서 cicd-system 으로 변경 했습니다. 하지만 정작 권한 설정(ClusterRoleBinding)은 기본값인 argocd 네임스페이스를 가리키고 있어서 발생한 문제입니다. 해결 하기 위해
네임스페이스 수정
kubectl edit clusterrolebinding argocd-application-controller
파일이 열리면 subjects 부분을 찾아서 namespace를 수정합니다.
서버 권한 수정
kubectl edit clusterrolebinding argocd-server
마찬가지로 namespace: argocd를 찾아서 namespace: cicd-system 으로 바꾸면 됩니다.
그리고 Refresh 를 누르면

연동이 됩니다.
4. CI/CD 파이프라인 (GitHub Actions)
이 설정을 적용하면, 코드를 수정해서 main 브랜치에 푸시하는 순간 [이미지 빌드] -> [Docker Hub 푸시] -> [K8s 매니페스트 버전 수정] -> [Git 푸시] 가 자동으로 일어나고, ArgoCD가 변경된 버전을 감지해서 배포까지 끝냅니다.
GitHub Secrets 설정

워크플로우 파일 생성
name: Build and Update K8s Manifests
on:
push:
branches: [ "main" ]
# [중요] k8s 폴더가 변경될 때는 이 액션이 돌지 않도록 막아야 '무한 루프'를 방지합니다.
paths:
- 'server/**' # 백엔드 코드가 바뀔 때만
- 'client/**' # 프론트엔드 코드가 바뀔 때만
- '.github/workflows/**'
permissions:
contents: write # Git에 변경 사항(버전 태그)을 푸시하기 위해 쓰기 권한 필요
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate Date-based Tag
run: |
# 1. 타임존을 한국(Asia/Seoul)으로 설정
export TZ="Asia/Seoul"
# 2. 날짜 포맷 지정 (연월일시분)
DATE_TAG=$(date +'%Y%m%d%H%M')
# 3. 환경변수(IMAGE_TAG)에 저장하여 다음 단계들이 쓸 수 있게 함
echo "IMAGE_TAG=v1.0-$DATE_TAG" >> $GITHUB_ENV
# 확인용 출력
echo "Generated Tag: v1.0-$DATE_TAG"
# 1. Docker Hub 로그인
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# 2. Docker Buildx 설정 (멀티 플랫폼 빌드용)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# 3. [백엔드] 빌드 & 푸시
- name: Build and push Backend
uses: docker/build-push-action@v5
with:
context: ./server
push: true
tags: |
parkheejun/foundry-backend:latest
parkheejun/foundry-backend:${{ env.IMAGE_TAG }}
# 4. [프론트엔드] 빌드 & 푸시
- name: Build and push Frontend
uses: docker/build-push-action@v5
with:
context: ./client
push: true
tags: |
parkheejun/foundry-frontend:latest
parkheejun/foundry-frontend:${{ env.IMAGE_TAG }}
# 5. [GitOps] K8s 매니페스트 이미지 태그 수정
- name: Update Kubernetes manifests
run: |
# Git 설정 (커밋할 사람 정보)
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
# (1) 백엔드 YAML 파일 수정
sed -i "s|image: parkheejun/foundry-backend:.*|image: parkheejun/foundry-backend:${{ env.IMAGE_TAG }}|g" k8s/02-backend.yaml
# (2) 프론트엔드 YAML 파일 수정
sed -i "s|image: parkheejun/foundry-frontend:.*|image: parkheejun/foundry-frontend:${{ env.IMAGE_TAG }}|g" k8s/03-frontend.yaml
# 변경 사항 확인 (디버깅용)
echo "Changes in k8s manifests:"
git diff k8s/
# (3) Git Commit & Push
git add k8s/02-backend.yaml k8s/03-frontend.yaml
git commit -m "Update k8s image tags to ${{ env.IMAGE_TAG }} [skip ci]"
git push
레포지토리에 push 를 하면

오른쪽에 갈색 점이 생기고 파이프라인이 실행됩니다.

성공하면 새로운 이미지로 업데이트 됩니다.


그리고 ArgoCD 도 k8s/ 의 변경사항을 반영해서 새로운 pod 를 생성합니다.

이렇게 GitOps 파이프라인 구현을 성공했습니다!
'project > 본투비엔지니어' 카테고리의 다른 글
| [본투비엔지니어] 나에게 베어본만 주어진다면?? (10) (0) | 2026.02.01 |
|---|---|
| [본투비엔지니어] 나에게 베어본만 주어진다면?? (9) (0) | 2026.01.31 |
| [본투비엔지니어] 나에게 베어본만 주어진다면?? (8) (0) | 2026.01.29 |
| [본투비엔지니어] 나에게 베어본만 주어진다면?? (7) (0) | 2026.01.29 |
| [본투비엔지니어] 나에게 베어본만 주어진다면?? (6) (0) | 2026.01.29 |