웹/배포

[Final 16] 코드만 push하세요, 배포는 GitHub Actions가 알아서 합니다

jumemory 2025. 9. 11. 11:00

[Final 16] 코드만 push하세요, 배포는 GitHub Actions가 알아서 합니다

매번 배포할 때마다 "아 맞다, 마이그레이션 안 했네", "서버 접속이 왜 안 되지?"라며 당황하신 적 없나요? **CI/CD(지속적 통합/지속적 배포)**를 구축하면 사람이 하는 실수를 줄이고, 단 몇 분 만에 새로운 기능을 고객에게 전달할 수 있습니다.


1. CI/CD란 무엇인가?

  • CI (Continuous Integration): 개발자가 수정한 코드를 공유 저장소에 올리면 자동으로 빌드하고 테스트하여 결함이 없는지 확인하는 과정입니다.
  • CD (Continuous Deployment): CI를 통과한 코드를 실제 운영 서버에 자동으로 반영하는 과정입니다.

2. GitHub Actions를 선택하는 이유

  1. GitHub 통합: 별도의 서버(Jenkins 등)를 구축할 필요 없이 GitHub 레포지토리에서 바로 설정 가능합니다.
  2. YAML 기반: 워크플로우를 코드로 관리하므로 설정이 쉽고 공유가 간편합니다.
  3. 풍부한 마켓플레이스: AWS, Docker, Slack 연동 등 이미 만들어진 수만 개의 액션을 가져다 쓸 수 있습니다.

3. 사전 준비: 보안을 위한 Secrets 설정

서버 IP, SSH 키, DB 비밀번호 등 민감한 정보를 코드에 직접 적으면 절대 안 됩니다. GitHub의 Settings > Secrets and variables > Actions에서 아래 항목들을 등록합니다.

  • EC2_HOST: 서버의 탄력적 IP
  • EC2_USERNAME: 보통 ubuntu
  • EC2_SSH_KEY: .pem 키 파일의 전체 내용
  • DOCKER_USERNAME: Docker Hub 아이디
  • DOCKER_PASSWORD: Docker Hub 비밀번호 또는 토큰

4. 워크플로우 파일 작성 (.github/workflows/deploy.yml)

프로젝트 폴더에 .github/workflows/ 폴더를 만들고 deploy.yml 파일을 작성합니다.

YAML
 
name: Django CI/CD Pipeline

on:
  push:
    branches: [ "main" ] # main 브랜치에 푸시될 때 실행

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    # 1. Docker 이미지 빌드 및 푸시
    - name: Login to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Build and push Docker image
      run: |
        docker build -t ${{ secrets.DOCKER_USERNAME }}/my-django-app:latest .
        docker push ${{ secrets.DOCKER_USERNAME }}/my-django-app:latest

    # 2. EC2 서버 접속 및 배포
    - name: Deploy to EC2
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          cd /home/ubuntu/my-project
          docker-compose pull
          docker-compose up -d
          docker-compose exec -T web python manage.py migrate
          docker-compose exec -T web python manage.py collectstatic --noinput

5. 배포 프로세스 이해하기

  1. Push: 개발자가 git push origin main을 실행합니다.
  2. Build: GitHub Actions 서버가 구동되며 최신 코드로 Docker 이미지를 빌드합니다.
  3. Push Image: 빌드된 이미지를 Docker Hub(이미지 저장소)에 업로드합니다.
  4. Remote SSH: GitHub Actions가 우리 EC2 서버에 SSH로 접속합니다.
  5. Update: 서버에서 최신 이미지를 받아오고(pull), 컨테이너를 재시작(up -d)한 뒤 마이그레이션까지 자동으로 수행합니다.

6. [선생님의 심화 보충] 무중단 배포(Zero Downtime)에 대하여

위 방식은 컨테이너가 재시작되는 몇 초 동안 서비스가 중단될 수 있습니다.

  • 해결책: Nginx를 활용한 Blue-Green 배포Docker Compose의 롤링 업데이트를 사용하면, 새로운 버전이 완전히 뜰 때까지 기존 버전을 유지하여 사용자에게 끊김 없는 서비스를 제공할 수 있습니다. 프로젝트가 커지면 반드시 도전해 봐야 할 과제입니다.

✍️ 블로그 작성을 위한 마지막 조언

독자들에게 **"이제 여러분은 '자동화의 맛'을 보았습니다"**라고 축하해 주세요. 초록색 체크 표시(Success)가 GitHub 액션 탭에 떴을 때의 짜릿함을 강조하면 독자들의 실습 의욕을 고취할 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

🛠 설치 및 서비스 실행 단계

# 크론(cron) 데몬 설치 (cronie 패키지)
sudo yum install cronie -y  

# crond(크론 데몬) 상태 확인 (활성/비활성, 실행 중 여부 등)
sudo systemctl status crond  

# 부팅 시 자동 실행(enable) + 즉시 시작(now)
sudo systemctl enable --now crond
  • cron : 리눅스에서 스케줄러 역할(주기적으로 작업 실행).
  • enable --now : 한 번만 시작이 아니라, 서버 재부팅 시 자동으로 실행되도록 등록 + 지금 바로 실행.

📁 작업 디렉토리 이동 및 Python 스크립트 작성

# 작업 디렉토리로 이동
cd ~/workspace  

# test.py 작성 (vim 편집기 실행)
vim test.py
# test.py 내용
import os

# 현재 디렉토리에 a.txt 파일 생성 후 "Hi"라는 문자열 작성
with open("./a.txt", "w") as f:
    f.write("Hi")
# Python 스크립트를 실행 → a.txt 파일이 생성됨
python test.py  

# a.txt 내용 확인 → "Hi" 출력
cat a.txt

🖊 쉘 스크립트 작성

 
# 쉘 스크립트 생성 (vim 편집기 실행)
vim test.sh
 
#!/bin/bash
# test.sh 내용
# miniconda 환경의 Python을 이용해 test.py 실행
/home/ec2-user/miniconda3/bin/python /home/ec2-user/workspace/test.py
  • #!/bin/bash : 이 파일이 bash 쉘로 실행될 스크립트임을 선언.
  • 절대 경로를 사용 → 크론 같은 환경에서도 정확한 실행이 가능.

🔐 권한 부여 및 실행 테스트

# 실행 권한 부여 (+x)
chmod +x ./test.sh  

# 기존 a.txt 삭제 → 새로 만들어지는지 확인 준비
rm a.txt  

# test.sh 실행 → 내부에서 test.py를 실행해 a.txt를 생성
./test.sh

 

크론 스케줄(자동화)

 

🧾 명령어별 설명

1️⃣ crontab -e

  • 기능: 현재 사용자의 크론 작업을 편집.
  • 실행 후 vi/vim 편집기가 열리며, 아래 형식으로 스케줄을 추가/수정합니다.
분 시 일 월 요일 실행할_명령


0, 7 : 일요일 
1 : 월요일
2 : 화요일
3 : 수요일
4 : 목요일
5 : 금요일
6 : 토요일

 


2️⃣ crontab -l

  • 기능: 현재 사용자 계정에 등록된 크론 작업 목록 확인.

3️⃣ watch -n 1 date

  • 기능: date 명령의 결과(현재 날짜/시간)를 1초마다 반복 실행하여 화면에 표시.
  • 형식: watch -n [초 단위 간격] [명령어]

4️⃣ find / -name "a.txt" 2>/dev/null

  • find: 지정된 경로에서 파일을 검색하는 명령.
  • /: 루트 디렉토리부터 전체 파일 시스템 검색.
  • -name "a.txt": 이름이 정확히 "a.txt" 인 파일을 찾음.
  • 2>/dev/null: 오류 메시지(권한 거부 등)를 무시하고 결과만 출력.
    • 2 = 표준 오류(stderr)
    • > = 리다이렉션(출력 방향 변경)
    • /dev/null = "검은 구멍" (출력을 버림)

 

주피터 접속하기

사용 명령 bash<br>ssh -i .\skn15-scw.pem ec2-user@54.180.139.54<br> bash<br>ssh jupyter<br>
설정 위치 명령 실행 시 옵션으로 직접 지정 ~/.ssh/config 파일에 사전 등록
키 파일 지정 -i .\skn15-scw.pem 옵션으로 매번 지정 IdentityFile C:/Users/kww88/.ssh/skn15.pem에 저장
사용자명 지정 ec2-user@ 부분에서 직접 입력 User ec2-user에 저장
호스트/IP 54.180.139.54를 직접 입력 HostName 13.125.107.203에 저장
명령어 길이 길고 매번 입력 필요 짧고 간단 (ssh jupyter)
관리 편의성 서버가 많아지면 불편 (매번 키, 사용자, IP 기억 필요) 여러 서버를 쉽게 관리 가능
적용 범위 일회성 사용, 설정 파일 불필요 설정 파일이 필요하지만 한 번만 등록하면 됨
예시 상황 - 임시/한 번만 접속할 때- 다른 설정 없이 빠르게 접속할 때 - 자주 접속하는 서버- 여러 서버를 관리할 때

 

 

구분 /etc/hosts .ssh/config

위치 /etc/hosts (시스템 전역) ~/.ssh/config (사용자 홈 디렉토리)
대상 프로그램 운영체제 네트워크 스택 전체 (웹 브라우저, ping, curl 등 모든 앱) SSH 클라이언트(OpenSSH) 전용 (ssh, scp, sftp, Git 등)
기능/역할 - 도메인 이름 → IP 주소 수동 매핑- DNS 조회 전에 먼저 참조 - SSH 접속 설정 단축/자동화- 사용자명, 포트, 키 파일 등 저장
사용 시점 모든 네트워크 요청 시 자동 참조 SSH 명령 실행 시에만 참조
적용 범위 시스템 전역 현재 사용자 계정(또는 해당 홈 디렉토리)
예시 설정 ```  
192.168.1.10 myserver Host myserver  
  HostName myserver  
  User ec2-user  
  Port 2222  
  IdentityFile ~/.ssh/id_rsa  
```    
장점 - 모든 앱에서 동일 이름으로 IP 대신 사용 가능- DNS 필요 없음 - 자주 쓰는 SSH 옵션을 단축 가능- 여러 서버 관리에 편리
단점 - 포트/사용자/키 파일은 지정 불가- 시스템 전체 영향 - SSH 이외 프로그램에는 적용 안 됨
사용 예시 ping myserver, curl http://myserver ssh myserver → 별칭으로 바로 접속

 

ssh jupyter로 접속 가능 -> vscode에서 remote로 내가 ~/.ssh/config에 저장한 호스트들로 접속가능

 

 

 

pymysql은 속도 느림.mysqlclient는 속도 향상