Proxmox 로 홈 서버 구축하기: (8) GitHub Self-hosted Build

ArgoCD 까지의 파이프라인 구축을 위한 첫번째.


1. GitHub Self-Hosted 란?

기존에 GitHub Action 을 만들어둔 게 있지만 GitHub 자체에서 repo 가 private 일 경우, 무료는 횟수나 용량이 제한이 있었다.

항목 제한 내용 주기/기준
실행 시간 (Minutes) 2,000분 월별 (Per month)
아티팩트 및 패키지 스토리지 (Artifacts & Packages Storage) 500 MB 고정 (계정 전체)
빌드 속도를 높이기 위한 의존성 파일(node_modules 등) 캐시 (Cache) 10GB 고정 (계정 전체)
GitHub Actions(자동화 스크립트)가 실행 중에 GitHub 기능을 사용하는 횟수 1,000 시간 (Per hour)

 

ArgoCD 로도 private repository 를 땡겨올 수 있었기에  tailscale은 삭제했고, 혼자 쓰기에는 넉넉할 것 같지만 GitHub Actions 에서 제공하는 self hosted 를 사용 하기로 했다.

 

우선, 기존의 GitHub Actions Build & Deploy 와 GitHub Self-hosted Build & Deploy 는 뭐가 다른지 알아보자.

접속의 방향이 정반대가 된다.

기존 방식 (SSH Push)
  • 흐름: GitHub Actions → (접속 시도) → 홈서버
  • 포트포워딩 안 해놓으면 내부로 들어올 수 없어서 접속 불가하여 Tailscale로 터널 생성.
바꾼 방식 (ArgoCD Pull)
  • 흐름: 홈서버 (ArgoCD) → GitHub 레포지토리
  • ArgoCD 에서 주기적으로 GitHub 레포지토리를 체크하여 Pull. 아웃바운드는 가능하므로 인바운드 신경 쓸 필요가 없다.

 

2. 준비할 것

  • GitHub 프로젝트 본체 서비스 repo 1개, 배포용 repo 1개
  • Docker Hub 서비스 repo 1개

 

3. GitHub 서비스 repo 준비

서비스 repo에는 Dockerfile 을 작성하고(이건 추후에...) 아래의 경로에 yaml 파일을 생성한다.

파일 이름은 상관 없지만 폴더 경로는 변경하면 안 된다.

.github\workflows\build.yaml
build.yaml
name: Build and Push

on:
  push:
    branches: [ "master" ]

jobs:
  build:
    runs-on: self-hosted
    # 👇 [중요] 나중에 k8s yaml 파일을 수정해서 푸시하려면 쓰기 권한 필수
    permissions:
      contents: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # -----------------------------------------------------------
      # 👇 JAR 파일 생성 단계
      # -----------------------------------------------------------
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Build with Gradle
        run: ./gradlew build

      # 👇 jar 파일 2개 생기는 문제 방지 (도커 빌드 실패 방지)
      - name: Remove plain jar
        run: rm -f build/libs/*-plain.jar

      # 👇 (디버깅용) 진짜 파일이 생겼는지 눈으로 확인
      - name: Check build result
        run: ls -al build/libs/
      # -----------------------------------------------------------
      # ⭐ [중요!!!!] abc-service 자리에 본인 서비스 이름 집어넣을 것!!!!
      - name: Build Docker image
        run: |
          docker build . -t ${{ secrets.DOCKER_HUB_USERNAME }}/abc-service:${{ github.sha }}

      - name: Log into Docker Hub
        run: |
          echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin

      # ⭐ [중요!!!!] abc-service 자리에 본인 서비스 이름 집어넣을 것!!!!
      - name: Push Docker image
        run: |
          docker push ${{ secrets.DOCKER_HUB_USERNAME }}/abc-service:${{ github.sha }}

      # -----------------------------------------------------------
      # 👇 ArgoCD용 Manifest 수정 단계
      # -----------------------------------------------------------
      - name: Update Manifest in Ops Repo
        env:
          GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
        run: |
          # 1. 배포용 repo
          git clone https://$GIT_TOKEN@${{ secrets.OPS_REPO_URL }} temp_ops
          
          # 2. 폴더 이동
          cd temp_ops
          
          # ⭐ [핵심] 폴더 안으로 들어온 다음에 신분증(Config) 설정!
          # (--global을 빼고 현재 레포에만 적용. 본인 push 와 구분을 위해 아래와 똑같이 해도 됨.)
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git config user.name "github-actions[bot]"
          
          # 3. 파일 수정 (디버깅 포함)
          echo "=== 수정 전 deployment.yaml ==="
          cat k8s/deployment.yaml
          
          if [ -f "k8s/deployment.yaml" ]; then
            # 이미지 태그 변경
            # ⭐ [중요!!!!] abc-service 자리에 본인 서비스 이름 집어넣을 것!!!!
            sed -i "s|image: .*abc-service:.*|image: ${{ secrets.DOCKER_HUB_USERNAME }}/abc-service:${{ github.sha }}|g" k8s/deployment.yaml
          
            echo "=== 수정 후 deployment.yaml ==="
            cat k8s/deployment.yaml
          
            # 4. 변경사항 푸시
            git add k8s/deployment.yaml
          
            if git diff --staged --quiet; then
              echo "No changes to commit."
            else
              git commit -m "Update image tag to ${{ github.sha }} [skip ci]"
              git push origin master
            fi
          else
            echo "❌ Error: k8s/deployment.yaml 파일이 없습니다."
            ls -R
            exit 1
          fi
          
          # 5. 뒷정리
          cd ..
          rm -rf temp_ops

build.yaml 내부의 k8s/deployment.yaml 은 나중에 만들 예정인데, 만약 서비스를 GitHub 여러 repo 를 이용할 것이라면 폴더 이름을 구분하기 위한 것으로 바꾸어도 상관 없다.

 

build.yaml 를 올리기 전에 repository 환경 변수 등록을 해주자.

우선, Docker Hub 사이트에 가입한 뒤, docker 로 build 를 하기 위한 이미지를 땡겨올 repo 를 만들어주자.

Docker Hub Repository 생성
Repositories > Create a repository > Public 선택 > Create 클릭
Docker Hub Token 발급
프로필 클릭 > Account Settings > Personal access tokens > Generate new token > Access permissions
 > Read, Write, Delete 선택 > Generate 클릭

token 이 보이면, 다시 볼 수 없기 때문에 복사해둬야 한다.

 

GitHub Repository 환경 변수 등록

이제 GitHub 에서 배포하고자 하는 Repository 선택 후, 아래의 메뉴에서 버튼을 선택한다.

Settings > Secrets and variables > Actions > [New repository secret] 선택

DOCKER_HUB_USERNAME : 본인 Docker Hub ID (repo 만들 때 슬러시'/' 앞에 적힌 것.  ex: myid/abc-service)

DOCKER_HUB_TOKEN : Docker Hub Token

OPS_REPO_URL : 배포용 repo url (https:// 이후부터)

GIT_TOKEN : GitHub 본인 계정 Token

 

4. GitHub 배포 repo 준비

service repo 가 준비 되었으면, 아직 push 하지 않은 상태에서 이제 deploy repo 를 만들어주자.

아래와 같은 구조로 만들어준다. 서비스를 여러개 올릴 거라면 폴더 이름을 바꿔서 구분해주는 걸 추천.

deploy/				# root 폴더
 └── k8s/			# deployment, service 정보 yaml 에 대한 폴더
     ├── deployment.yaml	# 배포 정보
     └── service.yaml		# 서비스 정보
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: abc-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: abc-service
  template:
    metadata:
      labels:
        app: abc-service
    spec:
      containers:
        - name: abc-service
          # 👇 여기가 핵심입니다. 처음엔 latest로 두면 GitHub Actions가 덮어씁니다.
          image: [docker hub id]/abc-service:latest
          ports:
            - containerPort: 8080
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: abc-service
  namespace: default
spec:
  type: NodePort
  selector:
    # deployment.yaml에 적힌 라벨이랑 똑같아야 합니다!
    app: abc-service
  ports:
    - port: 8080
      targetPort: 8080
      nodePort: 30090   # ⭐ 우리가 접속할 문 번호

여기까지 됐으면 준비가 완료되었고, 이게 Runner 를 세팅해주자.

 

5. GitHub Actions Runners Self-hosted

서비스 repo 의 Settings > Actions > Runners > New self-hosted runner 클릭

본인 환경에 따라 Windows, Linux든 선택 하고 Download 에 해당하는 명령어만 차례로 입력한다.
이 때, 서비스를 여러개 self-hosted 할 계획이라면 mkdir actions-runner 대신 폴더 이름을 식별가능하게 바꾸어주자.

mkdir actions-runner-abc && actions-runner-abc

여기서 부터 중요하다. Configure 에 해당하는 부분 명령어를 실행하기 전에 Runner 를 여러개 돌릴 생각이라면, Runner 의 이름을 각각 지정해줘야 따로 돌릴 수 있다.

./config.sh --url [본인 git 저장소 링크] --token [본인 토큰] --name [Runner 이름]

 ./run.sh 을 실행하게 되면 터미널로 연결될 때에만 실행이 가능하게 되기 때문에, 백그라운드에서 실행되게 설정해주자.

sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status

 

아래 사진과 같이 나타난다면 정상이다. Idle 은 유휴 상태라는 뜻이다.

 

여기까지 했으면 이제 끝난 거다. 배포 repo 를 먼저 push 하고, 그 다음에 서비스 repo 를 배포하자.

build 가 에러 없이 끝났으면 성공!

 

당연한 얘기지만 build 를 내 홈서버에서 하는 것이기 때문에 홈서버에는 docker 가 설치 되어 있어야 한다.

 

(k8s 설치 및 ArgoCD 와의 연결은 다음에...)


Runner 가 build 할 때 차지하는 용량이 생각보다 커서 GitHub Actions 의 호스팅을 다시 쓰게 될 지도 모르겠다...