git push 한 번에 일본 서버까지 자동 배포되는 파이프라인을 만들면 휴먼 에러가 줄고, 새벽 배포 부담도 사라집니다. 이 글에서는 GitHub Actions + SSH 키 조합으로 가장 단순하고 안정적인 자동 배포 파이프라인을 구성합니다.
전체 그림
배포 단계는 4가지입니다.
- 개발자가
main브랜치에 push - GitHub Actions가 빌드 및 테스트
- SSH로 일본 서버에 접속해 새 코드 동기화
- 서비스 재시작 (systemctl 또는 docker)
각 단계를 하나씩 살펴보겠습니다.
1단계 — 서버에 배포 전용 사용자 만들기
root로 직접 배포하면 사고 위험이 큽니다. 권한이 제한된 deploy 사용자를 따로 만듭니다.
# 일본 서버에서 실행
sudo adduser --disabled-password --gecos "" deploy
sudo mkdir -p /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chown -R deploy:deploy /home/deploy/.ssh
# 애플리케이션 디렉토리 권한 부여
sudo mkdir -p /var/www/myapp
sudo chown -R deploy:deploy /var/www/myapp
# systemctl restart 권한만 sudo 없이 허용
echo 'deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp' | sudo tee /etc/sudoers.d/deploy
NOPASSWD로 허용하는 명령은 딱 필요한 것만 명시해야 합니다. ALL을 주면 사실상 root입니다.
2단계 — SSH 키 페어 생성
로컬 PC에서 배포 전용 키를 만듭니다. 평소 개인 키와 분리하는 것이 좋습니다.
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N "" -C "github-actions-deploy"
생성된 공개키를 일본 서버의 deploy 사용자에게 등록합니다.
# 로컬에서 일본 서버로 전송
ssh-copy-id -i ~/.ssh/deploy_key.pub deploy@your-server-ip
# 또는 수동으로
cat ~/.ssh/deploy_key.pub | ssh root@your-server-ip "cat >> /home/deploy/.ssh/authorized_keys && chown deploy:deploy /home/deploy/.ssh/authorized_keys && chmod 600 /home/deploy/.ssh/authorized_keys"
연결 테스트:
ssh -i ~/.ssh/deploy_key deploy@your-server-ip "whoami && pwd"
# → deploy
# → /home/deploy
3단계 — GitHub Repository Secrets 등록
GitHub 저장소 → Settings → Secrets and variables → Actions에서 다음 시크릿을 등록합니다.
| 이름 | 값 |
|---|---|
SERVER_HOST |
일본 서버 IP 또는 도메인 |
SERVER_USER |
deploy |
SSH_PRIVATE_KEY |
~/.ssh/deploy_key 파일 내용 전체 |
SSH_PORT |
22 (또는 변경한 포트) |
SSH_PRIVATE_KEY는 cat ~/.ssh/deploy_key 결과를 그대로 복사해서 붙여넣습니다. -----BEGIN OPENSSH PRIVATE KEY----- 부터 -----END OPENSSH PRIVATE KEY----- 까지 모두 포함해야 합니다.
4단계 — GitHub Actions 워크플로
.github/workflows/deploy.yml 파일을 생성합니다. 아래는 Node.js 앱 기준 예시입니다.
name: Deploy to Japan Server
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install & build
run: |
npm ci
npm run build
npm test
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: $
username: $
key: $
port: $
script: |
cd /var/www/myapp
git fetch origin main
git reset --hard origin/main
npm ci --production
sudo systemctl restart myapp
PHP/Python/Go도 빌드 단계만 바꾸면 동일한 구조로 적용 가능합니다.
5단계 — 정적 사이트(Jekyll, Hugo, Next 빌드 결과)인 경우
빌드 결과물을 서버로 전송하는 형태가 더 깔끔합니다. rsync를 사용합니다.
- name: Build site
run: |
bundle install
bundle exec jekyll build
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "$" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p $ $ >> ~/.ssh/known_hosts
- name: Rsync to server
run: |
rsync -avz --delete -e "ssh -i ~/.ssh/deploy_key -p $" \
_site/ $@$:/var/www/myapp/
--delete 옵션은 서버 쪽 불필요 파일을 제거하므로 의도하지 않은 파일이 남는 일을 막습니다.
6단계 — Docker 사용 시
이미지를 GitHub Container Registry에 올리고, 서버에서 pull → restart 하는 방식이 일반적입니다.
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: $
password: $
- name: Build & push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/$:latest
- name: Deploy
uses: appleboy/ssh-action@v1.0.3
with:
host: $
username: $
key: $
script: |
cd /opt/myapp
docker compose pull
docker compose up -d
docker image prune -f
docker compose up -d는 변경된 컨테이너만 재생성하므로 다른 서비스에는 영향이 없습니다.
보안 체크리스트
배포 자동화는 잘못 만들면 자동 해킹 도구가 됩니다. 반드시 확인하세요.
deploy사용자에게 비밀번호 로그인 차단 (PasswordAuthentication no)deploy사용자의 sudo 권한은 재시작 명령으로만 제한- SSH 포트는 22 외 다른 포트로 변경 (자동화 도구의 무차별 공격 회피)
authorized_keys에는 GitHub Actions용 키 외 다른 키 없음- 워크플로 파일에 직접 IP/비밀번호를 적지 말고 항상 Secrets 사용
pull_request트리거에는 secrets가 노출될 수 있으므로 신중히 사용
흔한 트러블슈팅
Permission denied (publickey)
- 서버의
/home/deploy/.ssh/authorized_keys권한이 600인지, 디렉토리는 700인지 확인 - 등록한 시크릿이 공개키가 아닌 개인키인지 확인 (실수가 매우 잦습니다)
Host key verification failed
ssh-keyscan단계가 누락되었거나, 서버가 호스트 키를 변경했을 때appleboy/ssh-action사용 시 자동 처리되지만, 직접ssh호출 시에는 명시 필요
빌드는 성공했는데 서버에서 옛날 버전이 도는 경우
git pull이 충돌로 실패한 채 무시되었을 가능성git reset --hard origin/main으로 강제 동기화하거나, 빌드 결과를 rsync하는 방식으로 전환
마무리
자동 배포의 본질은 “에러는 자동화하지 않고, 안전한 동작만 자동화”입니다. 테스트 단계를 충실히 두면 잘못된 코드가 일본 서버에 닿기 전에 차단할 수 있습니다.
TCP-80.NET의 일본 VPS는 SSH 포트 변경, 방화벽 설정 등에 대해 텔레그램으로 한국어 안내를 제공합니다. CI/CD 환경 구성 중 문의가 있으시면 @tcp80net으로 부담 없이 문의해 주세요.