---
title: "GitHub Actions로 첫 변경 배포"
description: "GitHub Actions로 첫 배포를 실행합니다. 애플리케이션에 범위를 제한한 서비스 토큰을 만들고 GitHub 리포에 시크릿을 설정하세요. 워크플로를 실행한 뒤 Alpacon에서 명령 기록과 토큰 상태를 확인하고, 실패했을 때 점검할 항목도 살펴봅니다. 실습에 활용하세요."
---

> Full documentation index: https://docs.alpacax.com/llms.ko.txt

# GitHub Actions로 첫 변경 배포

<span id="github-actions로-cicd-파이프라인-구축하기" />

# GitHub Actions로 첫 변경 배포

Alpacon GitHub Actions를 이용한 단계별 CI/CD 파이프라인 구축 튜토리얼입니다.

## 학습 목표

이 튜토리얼을 완료하면 다음을 할 수 있습니다:
- SSH 키 없이 안전한 배포 파이프라인 구축
- 명령어 수준의 세밀한 권한 제어
- 자동화된 빌드 및 배포 프로세스 구현

## 사전 준비사항

- [Alpacon 워크스페이스 준비 완료](/learn/quickstart/#step-1-get-a-workspace)
- [서버 등록 및 에이전트 설치 완료](/learn/quickstart/#step-2-첫-서버-등록-2분)
- 이 파이프라인이 사용할 [Application](/use/iam/applications/), 또는 Application을 만들 수 있는 관리자 권한
- GitHub 저장소 접근 권한

## Step 1: 서비스 토큰 생성 (5분 + 승인 대기)

### 1-1. 서비스 토큰 생성

CI와 자동화는 서비스 토큰으로 동작합니다. 서비스 토큰은 사람이 아니라 Application에 속하기 때문에 담당자가 바뀌어도 그대로 유지되며, 파이프라인이 무엇까지 할 수 있는지는 ACL이 정확히 정합니다.

1. Alpacon 워크스페이스에 로그인
2. **IAM → Applications**에서 이 파이프라인이 속한 Application 열기 또는 새로 생성
3. **토큰** 탭에서 **새로 만들기** 클릭

네 단계를 차례로 진행합니다.

```yaml
용도: 이름 github-actions-deploy, 만료 30일
권한: 배포 파이프라인 템플릿에서 시작해 조정
접근 규칙: prod-server와 1-2의 업로드 경로
검토: 이 파이프라인에 토큰이 필요한 이유 작성
```

만료 기본값은 30일이며 90일, 365일을 고르거나 시각을 직접 지정할 수 있습니다. 서비스 토큰에는 만료일이 필요하지만 워크스페이스의 토큰 최대 사용 기간은 적용되지 않습니다.

**토큰 만들기**를 누르면 키가 가려진 채로 한 번만 표시됩니다. 키는 `alpst-`로 시작합니다. 복사하거나 내려받은 뒤 **키를 안전한 곳에 보관했어요**에 체크해야 창을 닫을 수 있습니다.

CI 범위로 만든 토큰은 검토를 거쳐야 동작합니다. 요청은 **대기중**(pending) 상태로 생성되고 승인 권한이 있는 사람이 활성화하기 전까지는 어디에도 인증되지 않습니다. 파이프라인 도중에 발견하지 않도록 이 단계를 미리 감안하세요.

### 1-2. ACL 규칙 추가

새 서비스 토큰은 ACL 규칙이 적용되기 전까지 아무 곳에도 닿지 못합니다. 서버, 명령, 파일 경로가 각각 기본 거부이므로 규칙이 하나도 없는 토큰은 어디서든 거부됩니다.

**접근 규칙** 단계에서 고른 규칙은 토큰이 승인될 때까지 대기합니다. 승인 후 토큰의 **접근 규칙** 탭을 열고 **준비된 접근 규칙**에서 **적용**을 선택하거나, 서버와 파일 규칙을 직접 추가하세요. Alpacon Cloud에서는 **명령 ACL 관리자**에 등록된 관리자가 같은 탭의 **사전 승인된 명령**에 아래 명령어를 추가합니다. 이 권한은 워크스페이스 역할과 별개입니다. 자체 호스팅에서는 **접근 규칙** 탭의 **허용된 명령**에 각 명령어를 패턴으로 추가하세요. 다음 접근 범위를 설정합니다.

| 규칙 | 이 파이프라인에 필요한 항목 |
|------|---------------------------|
| Server ACL | `prod-server` |
| 명령어 접근 | 아래 명령어 |
| File ACL | 빌드 결과물을 올릴 `/var/www/app/dist/` |

각 명령어 항목은 제출하는 한 줄 전체와 일치해야 합니다. 아래 워크플로에서 각 토큰에 필요한 명령어를 한 항목씩 추가하세요.

```text
pm2 restart app
pm2 status
curl -f http://localhost:3000/health || exit 1
cd /app && git pull && npm ci && npm run build && pm2 restart app
cd /app && if [ "$ROLLBACK_VERSION" = "previous" ]; then git reset --hard HEAD~1; else git reset --hard "$ROLLBACK_VERSION"; fi && npm ci && npm run build && pm2 restart app
```

파일 전송은 서버 규칙을 먼저 확인한 다음 파일 규칙을 확인합니다. 따라서 **Upload to server** 단계에는 둘 중 하나가 아니라 Server ACL 항목과 File ACL 항목이 모두 필요합니다.

개발과 프로덕션용 토큰을 분리하여 관리하세요.

### 1-3. 토큰 활성화 확인

토큰의 **개요** 탭에서 상태가 더 이상 **대기중**이 아닌지 확인하세요. 대기중인 토큰은 어디에도 인증되지 않으므로 그 상태로 만든 파이프라인은 첫 Alpacon 단계에서 실패합니다.

## Step 2: GitHub 저장소 설정 (3분)

### 2-1. GitHub Secrets 추가

GitHub 저장소에서:

1. **Settings** > **Secrets and variables** > **Actions** 이동
2. **New repository secret** 클릭
3. 다음 시크릿 추가:

| Name | Value |
|------|-------|
| `ALPACON_WORKSPACE_URL` | https://your-workspace.ap1.alpacon.io |
| `ALPACON_SERVICE_TOKEN` | Step 1에서 복사한 `alpst-` 키 |

아래 액션의 입력값 이름은 `api-token`이며 서비스 토큰을 그대로 받습니다. 이미 배포된 워크플로우와의 호환을 위해 이름은 그대로 둡니다.

### 2-2. 워크플로우 파일 생성

저장소 루트에 `.github/workflows` 디렉터리 생성 후:

`.github/workflows/deploy.yml`:
```yaml
name: Deploy to Production

on:
  push:
    branches: [main]

env:
  NODE_VERSION: '18'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

    - name: Run tests
      run: npm test

    - name: Build application
      run: npm run build

    # 빌드 결과물을 아티팩트로 저장
    - name: Upload build artifacts
      uses: actions/upload-artifact@v4
      with:
        name: build-artifacts
        path: dist/

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    # 빌드 아티팩트 다운로드
    - name: Download artifacts
      uses: actions/download-artifact@v4
      with:
        name: build-artifacts
        path: dist/

    # Alpacon CLI 설치
    - name: Setup Alpacon CLI
      uses: alpacax/alpacon-setup-action@v1

    # 빌드 결과물을 서버로 전송
    - name: Upload to server
      uses: alpacax/alpacon-cp-action@v1
      with:
        workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
        api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
        source: './dist/'
        target-server: 'prod-server'
        target-path: '/var/www/app/dist/'
        recursive: true

    # 애플리케이션 재시작
    - name: Restart application
      uses: alpacax/alpacon-websh-action@v1
      with:
        workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
        api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
        target: 'prod-server'
        script: |
          pm2 restart app
          pm2 status

    # 헬스 체크
    - name: Health check
      uses: alpacax/alpacon-websh-action@v1
      with:
        workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
        api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
        target: 'prod-server'
        script: |
          curl -f http://localhost:3000/health || exit 1
```

## Step 3: 첫 배포 실행 (2분)

### 3-1. 코드 푸시

```bash
git add .github/workflows/deploy.yml
git commit -m "Add CI/CD pipeline with Alpacon"
git push origin main
```

### 3-2. 배포 모니터링

1. GitHub 저장소의 **Actions** 탭 이동
2. 실행 중인 워크플로우 클릭
3. 각 단계별 로그 확인

### 3-3. Alpacon에서 확인

1. Alpacon 워크스페이스의 **Audit** > **Events** 열기
2. **명령** 탭에서 실행된 명령어와 결과 확인. 토큰을 소유한 Application 이름으로 기록됨
3. 토큰의 **개요** 탭에서 사용 내역 확인

## Step 4: 고급 설정 (선택사항)

### 환경별 배포

`.github/workflows/deploy-multi-env.yml`:
```yaml
name: Multi-Environment Deploy

on:
  push:
    branches: [main, develop]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Setup Alpacon CLI
      uses: alpacax/alpacon-setup-action@v1

    # 브랜치별 환경 설정
    - name: Set environment
      id: env
      run: |
        if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
          echo "target=prod-server" >> $GITHUB_OUTPUT
          echo "token=${{ secrets.ALPACON_PROD_TOKEN }}" >> $GITHUB_OUTPUT
        else
          echo "target=dev-server" >> $GITHUB_OUTPUT
          echo "token=${{ secrets.ALPACON_DEV_TOKEN }}" >> $GITHUB_OUTPUT
        fi

    - name: Deploy to ${{ steps.env.outputs.target }}
      uses: alpacax/alpacon-websh-action@v1
      with:
        workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
        api-token: ${{ steps.env.outputs.token }}
        target: ${{ steps.env.outputs.target }}
        script: |
          cd /app && git pull && npm ci && npm run build && pm2 restart app
```

두 토큰은 각자의 ACL을 가지므로, 개발용 토큰에는 `prod-server`에 대한 Server ACL 항목을 넣지 않아도 됩니다.

### 롤백 워크플로우

`.github/workflows/rollback.yml`:
```yaml
name: Rollback Deployment

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to rollback to'
        required: true
        default: 'previous'

jobs:
  rollback:
    runs-on: ubuntu-latest

    steps:
    - name: Setup Alpacon CLI
      uses: alpacax/alpacon-setup-action@v1

    - name: Rollback application
      uses: alpacax/alpacon-websh-action@v1
      env:
        ROLLBACK_VERSION: ${{ github.event.inputs.version }}
      with:
        workspace-url: ${{ secrets.ALPACON_WORKSPACE_URL }}
        api-token: ${{ secrets.ALPACON_SERVICE_TOKEN }}
        target: 'prod-server'
        env: |
          ROLLBACK_VERSION
        script: |
          cd /app && if [ "$ROLLBACK_VERSION" = "previous" ]; then git reset --hard HEAD~1; else git reset --hard "$ROLLBACK_VERSION"; fi && npm ci && npm run build && pm2 restart app
```

## 문제 해결

### 배포 실패 시

1. **GitHub Actions 로그 확인**
   - Actions 탭에서 실패한 단계 확인
   - 에러 메시지 확인

2. **Alpacon 감사 이벤트 확인**
   - Audit > Events에서 명령어 실행 로그 확인
   - 서버 연결 상태 확인

3. **토큰의 ACL과 상태 확인**
   - 실행하려는 명령에 대응하는 Command ACL 항목이 있는지, 파일 전송에는 Server ACL과 File ACL 항목이 모두 있는지 확인
   - 토큰이 대기중이 아니라 활성 상태인지, 만료되지 않았는지 확인

### 일반적인 문제

| 문제 | 원인 | 해결 방법 |
|------|------|----------|
| `Permission denied` | 대응하는 Command ACL 항목 없음. 항목이 하나도 없으면 모든 명령이 거부됨 | 토큰의 **접근 규칙** 탭에서 항목 추가 |
| 업로드 단계 거부 | 대상 경로에 대한 Server ACL 또는 File ACL 누락 | 둘 다 추가. 전송은 Server ACL을 먼저 확인한 뒤 File ACL을 확인 |
| 토큰 생성 직후 인증 실패 | 토큰이 아직 **대기중**이고 활성화되지 않음 | 승인 권한이 있는 사람에게 활성화 요청 |
| `Server not found` | 잘못된 서버 이름 | 워크스페이스에서 서버 이름 확인 |
| `Token expired` | 토큰 만료 | 새 토큰 발급 및 GitHub Secret 업데이트 |

## 다음 단계

- [배포 개요](/integrate/)
- [서비스 토큰](/use/iam/applications/service-tokens/)
- [보안 모범 사례](/learn/best-practices/security/)
- [모니터링 및 알림 설정](/learn/best-practices/monitoring/)

## 요약

이 튜토리얼에서 다음을 완료했습니다:

✅ 서비스 토큰 생성 및 ACL 구성
✅ GitHub Secrets 구성
✅ CI/CD 파이프라인 작성
✅ 자동 배포 실행 및 모니터링

SSH 키 대신 범위가 제한된 서비스 토큰을 사용하여 더 안전하고 관리하기 쉬운 배포 파이프라인을 구축했습니다.

Source: https://docs.alpacax.com/ko/learn/tutorials/github-actions-setup/
