Proxmox 로 홈 서버 구축하기: (6) GitHub Actions + 자동 CI/CD

홈서버 구축 시에 가장 문제가 되는 점이 외부접속 문제였다.

자동 배포 역시 Cloudflare Tunnel 과 GitHub 을 이용하면 된다고 생각했으나 안타깝게도 Cloudflare Tunnelhttp 통신만 무료이고 ssh 는 유료였다.

돈을 내고 쓸까하다가 애초에 홈서버를 제작하려고 했던 목적이 매달 결제 비용은 도메인 주소 비용만 지출하는 것이었기에 다른 방법을 찾아보았다.

그리고 Tailscale 을 통해서 배포가 가능한 방법이 있음을 알게 된다.

 

1. 홈서버에 docker 설치

# docker 설치
curl -fsSL https://get.docker.com | sh
# sudo 명령어 없이 docker 실행할 수 있도록 현재 유저[user]에게 권한 부여
sudo usermod -aG docker [user]
# 아래의 명령어가 잘 실행되면 이상 없음
docker ps -a

 

2. ssh-key 등록

# ssh-key 등록 파일이 있는지 조회
cat ~/.ssh/authorized_keys
# 없으면 폴더 생성
mkdir -p ~/.ssh
# 파일 생성
touch ~/.ssh/authorized_keys
# 권한 추가
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# 수정 모드
sudo apt install nano
sudo nano ~/.ssh/authorized_keys

  C:\Users\[본인 윈도우 PC 유저이름 폴더]\.ssh  경로에서  id_rsa.pub  파일 내용 복사 후, 붙여넣기.

저장 명령어는 [Ctrl] + X

 

3. tailscale 설치 및 세팅

tailscale 가입 후, 내 PC(홈서버 말고 접속하고자 하는 Client 서버)에 설치

# tailscale 설치
curl -fsSL https://tailscale.com/install.sh | sh
# 해당 서버 등록
sudo tailscale up

위 명령어 실행 후 콘솔에 표시되는 url 로 접속하게 되면 해당 서버와의 터널링이 생성된다.

Owner 포함 유저 최대 3명, 터널링 최대 100개가 무료 서비스라 연습용으로 넉넉히 쓸 수 있을 것 같다.

Settings > OAuth clients > Generate OAuth client > [All] - [Read] 선택,  [Keys] - [Auth Keys] - [Write] 선택 > [Generate client] 선택

Clinet-idClient-secret 을 복사해둔다.

 

4. workflows 작성 + private submodule (선택)

여기서부터 중요하다.

repository 에 .github 폴더 생성. 그 아래에 workflows 폴더를 생성해주고 yaml 파일을 만들어준다.

Spring Boot 프로젝트 기준으로 하겠다.

name: [배포할 이미지 이름]

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write # tailscale 접속 위해 추가

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: 'recursive' # 서브모듈 추가
          token: ${{ secrets.PRIVATE_GITHUB_REPO }} # 서브 모듈 가져오기 위해 필요한 키
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Add execute permission to Gradle Wrapper
        run: chmod +x ./gradlew
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3
      - name: Build with Gradle Wrapper
        run: ./gradlew build

      - name: Build Docker image
        run: |
          docker build . -t ${{ secrets.DOCKER_REGISTRY_URL }}/[배포할 이미지 이름]

      - name: Log into Docker Hub
        run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}

      - name: Push Docker image to registry
        run: docker push ${{ secrets.DOCKER_REGISTRY_URL }}/[배포할 이미지 이름]

      - name: Connect to Tailscale
        uses: tailscale/github-action@v2
        with:
          oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
          oauth-secret: ${{ secrets.TS_OAUTH_CLIENT_SECRET }}

      - name: Deploy Docker container on server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST_TAILSCALE }} 
          username: ${{ secrets.SERVER_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }} # SSH 접속에 필요한 Private Key 추가
          timeout: 120s
          script: |
            sudo docker pull ${{ secrets.DOCKER_REGISTRY_URL }}/[배포할 이미지 이름]:[배포할 이미지 태그]
            sudo docker stop [배포할 이미지 이름] || true
            sudo docker rm [배포할 이미지 이름] || true
            sudo docker run -d --name [배포할 이미지 이름] -p 8081:8080 ${{ secrets.DOCKER_REGISTRY_URL }}/[배포할 이미지 이름]

Submoule 세팅은 https://haema-dev.tistory.com/54 이 포스팅을 참고 부탁드립니다.

 

5. repository 환경 변수 등록

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

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

PRIVATE_GITHUB_REPO : 서브모듈 가져오기 위해 등록하는 키

GitHub 프로필 > Settings > Developer Settings > Personal access tokens > Tokens (classic) > [Generate new token (classic)] 선택

권한은 repository 권한만 넣어줘도 된다. 참고로, 저랑 같은 Key 이름으로 안 해도 됩니다.

DOCKER_REGISTRY_URL, DOCKER_USERNAME, DOCKER_PASSWORD : 도커 이미지 허브에 관한 url 과 계정 정보인데 이건 추후에 설명하도록 하고, 일단 Pass.

TS_OAUTH_CLIENT_ID : tailscale 설치 및 세팅할 때 저장했던 Clinet-id
TS_OAUTH_CLIENT_SECRET : tailscale 설치 및 세팅할 때 저장했던 Client-secret
SSH_PRIVATE_KEY :  C:\Users\[본인 윈도우 PC 유저이름 폴더]\.ssh  경로에서  id_rsa  파일 내용 (-----BEGIN OPENSSH PRIVATE KEY----- 로 시작하는 key)

 

6. 장점

  • Private Repository 여도 배포가 된다.
  • 빌드할 때만 터널이 생성되고, tailscale 에서도 태그를 붙이거나 유저 권한을 설정할 수 있어서 보안적으로도 좋다.

 

전부 등록했으면 이제 한 번 더 push 해주고 GitHub Actions 이 돌아가도록 한다.

# docker 컨테이너 조회. 제대로 배포 되었는지 확인
docker ps -a
# docker logs 조회. 빌드는 성공했으나 배포가 제대로 안 되었다면 logs 를 조회.
docker logs [배포한 이미지 이름]
# 로그가 없다면 이미지만 생성되고 배포 자체에 실패했을 수도 있으므로 이미지 여부 확인.
docker images

 

앞서 말했지만 홈서버 구축 시에 가장 문제가 되는 점이 외부접속 문제였다. 내 홈서버의 경우 아웃바운드(나가는 요청. 패키지를 불러올 때 등)는 되지만 인바운드(들어오는 요청. 외부에서 접속 및 요청이 들어올 때)가 되지 않아 GitHub 에서 홈서버로 통신을 시도할 때 늘 실패했다. 그런데 tailscale 을 쓰게 되면 가능한 이유는 VPN 처럼 터널링을 할 수 있게 해주기 때문이다.

 

GitHub Actions 는 빌드가 될 때, 가상환경이 생성되는데 이 가상환경에서 tailscale 을 설치하게 되면 터널이 생성돼서 접속이 가능해진다. 또한, 빌드가 끝이 나면 가상환경은 제거되므로 터널도 제거된다.