[Final 16] 코드만 push하세요, 배포는 GitHub Actions가 알아서 합니다
매번 배포할 때마다 "아 맞다, 마이그레이션 안 했네", "서버 접속이 왜 안 되지?"라며 당황하신 적 없나요? **CI/CD(지속적 통합/지속적 배포)**를 구축하면 사람이 하는 실수를 줄이고, 단 몇 분 만에 새로운 기능을 고객에게 전달할 수 있습니다.
1. CI/CD란 무엇인가?
- CI (Continuous Integration): 개발자가 수정한 코드를 공유 저장소에 올리면 자동으로 빌드하고 테스트하여 결함이 없는지 확인하는 과정입니다.
- CD (Continuous Deployment): CI를 통과한 코드를 실제 운영 서버에 자동으로 반영하는 과정입니다.
2. GitHub Actions를 선택하는 이유
- GitHub 통합: 별도의 서버(Jenkins 등)를 구축할 필요 없이 GitHub 레포지토리에서 바로 설정 가능합니다.
- YAML 기반: 워크플로우를 코드로 관리하므로 설정이 쉽고 공유가 간편합니다.
- 풍부한 마켓플레이스: 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. 배포 프로세스 이해하기
- Push: 개발자가 git push origin main을 실행합니다.
- Build: GitHub Actions 서버가 구동되며 최신 코드로 Docker 이미지를 빌드합니다.
- Push Image: 빌드된 이미지를 Docker Hub(이미지 저장소)에 업로드합니다.
- Remote SSH: GitHub Actions가 우리 EC2 서버에 SSH로 접속합니다.
- 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는 속도 향상
'웹 > 배포' 카테고리의 다른 글
| 프로젝트 정리 (0) | 2025.09.15 |
|---|---|
| [Final 17] 서비스 모니터링 기법과 17일간의 로드맵 회고,"로그 관리 (0) | 2025.09.12 |
| [Final 15] 아직도 HTTP? Certbot으로 5분 만에 HTTPS 적용하기 (0) | 2025.09.10 |
| [Final 14] 서버 용량 걱정 끝! S3로 Static & Media 파일 관리하기 (0) | 2025.09.10 |
| [Cloud 13] Nginx 리버스 프록시 설정으로 서버 트래픽 안정화하기 (1) | 2025.09.08 |