GCP 서버에서 실행 중인 Nginx 서버의 인증서를 자동갱신하는 설정을 추가했다.
2025.05.20 - [공부] - [GCP] Gitea + Nginx(SSL)
[GCP] Gitea + Nginx(SSL)
포트폴리오용 프로젝트를 만들기에 앞서 프로젝트 파일들을 업로드하기 위해 클라우드 서버에 Git (UI) 환경을 구축하고 싶었다. [준비물]GCP 서버(Docker, 인증서) -- 사설 인증서 만들기로 검색하면
gooduck.net
1. Certbot 컨테이너 추가: docker-compose.yml 에 Certbot 서비스를 정의한다.
version: '3'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__server__ROOT_URL=https://<<<서버>>>/
volumes:
- ./gitea:/data
expose:
- "3000"
restart: always
networks:
- gitea
nginx:
image: nginx:latest
container_name: nginx
ports:
- "80:80"
- "443:443"
volumes:
# Nginx 설정 파일
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
# Let's Encrypt 인증서 공유 (읽기-쓰기 권한으로 변경)
- ./certbot/conf:/etc/letsencrypt/:rw
# 도메인 인증을 위한 웹루트 공유
- ./certbot/www:/var/www/certbot/:ro
restart: always
depends_on:
- gitea
networks:
- gitea
certbot:
image: certbot/certbot:latest
container_name: certbot
volumes:
# Let's Encrypt 인증서 공유
- ./certbot/conf:/etc/letsencrypt/:rw
# 도메인 인증을 위한 웹루트 공유
- ./certbot/www:/var/www/certbot/:rw
networks:
gitea:
2. 인증서를 적용해야 하는 nginx의 설정 파일도 수정해 준다.
http {
...
# HTTP (Port 80) 서버 -> HTTPS로 리디렉션
server {
listen 80;
server_name <<<서버>>>;
# Let's Encrypt 인증용 경로
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 그 외 모든 요청은 HTTPS로 리디렉션
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS (Port 443) 서버
server {
listen 443 ssl;
server_name <<<서버>>>;
# SSL 인증서 경로 (Certbot 표준 경로)
ssl_certificate /etc/letsencrypt/live/<<<서버>>>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<<<서버>>>/privkey.pem;
# Gitea로 프록시 패스
location / {
proxy_pass http://gitea:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
...
}
docker-compose.yml에 설정한 경로와 동일하게 위의 설정을 맞춰주면 된다.
nginx 서비스를 종료하고, 첫 인증서를 획득하기 위해 server {listen 443 ssl;...} 블록 전체를 주석 처리한다.
3. 볼륨 공유: Nginx와 Certbot이 인증서와 웹루트(인증용 임시 파일 경로)를 공유하도록 볼륨 설정한다.
docker-compose.yml이 있는 위치에서 certbot과 관련된 폴더를 생성해 준다.
mkdir -p certbot/conf certbot/www
4. 초기 인증서 발급 및 실행
nginx의 http {listen 443 ssl;... } 블록을 모두 주석처리하고, 서버가 꺼진 것을 확인한 후, 재시작한다.
docker-compose up -d --build nginx gitea
** 만약 gitea의 설정 파일, DB 등의 데이터를 로컬의 폴더와 연동하지 않았다면 위의 컨테이너 삭제에 주의해야 한다.
1) Certbot으로 초기 인증서 발급
docker-compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot \
--email <<<이메일>>> -d <<<서버>>> --agree-tos --no-eff-email
다음의 메시지와 저장 경로 등이 출력되는 것을 확인한다.
2) Nginx 설정 복원 및 재시작
인증서 발급 성공 이후, 주석 처리했던 nginx.conf의 HTTPS 서버 블록을 다시 원래대로 복원한다.
전체 서비스를 다시 시작한다.
docker-compose up -d --force-recreate --build
** 만약 gitea의 설정 파일, DB 등의 데이터를 로컬의 폴더와 연동하지 않았다면 위의 컨테이너 삭제에 주의해야 한다.
여기서부터는 cron과 shellscript를 이용하여 주기적으로 갱신 로직을 실행한다.
1. 권한 설정
cron과 sh를 실행하는 사용자는 docker에 명령할 권한을 갖고 있어야 한다.
sudo usermod -aG docker $USER
2. 변경 사항을 시스템에 저장
그룹 변경 사항은 새로운 로그인 세션부터 적용된다.
나는 간단하게 서버에서 로그아웃하고 다시 로그인했다.
- 서버에서 docker 명령어를 sudo 없이 실행할 수 있으면 된다.
3. 쉘 스크립트 작성, 먼저 certbot을 이용해서 renew 명령을 실행하고, nginx를 리로드 한다.
# /home/사용자이름/renew_certs.sh
#!/bin/bash
# docker-compose.yml 파일이 있는 절대 경로로 이동
cd /path/to/your/project_directory/
# 1. 'run' 명령으로 일회용 certbot 컨테이너를 실행하여 갱신 시도
# --rm 옵션으로 실행이 끝나면 컨테이너가 자동으로 삭제됩니다.
docker-compose run --rm certbot renew --quiet
# 2. 항상 실행 중인 nginx 컨테이너에 'exec'로 reload 명령 전달
docker-compose exec nginx nginx -s reload
echo "Certificate renewal process finished at $(date)"
쉘 스크립트의 실행 권한을 부여한다.
- 이걸 놓쳐서 오래 걸렸다. putty 기준으로 실행 권한이 생기면 초록색으로 파일의 색이 변한다.
chmod +x /home/사용자이름/renew_certs.sh
4. cron 작업 등록
crontab -e
크론 편집기는 처음 써봐서 조금 헤맸다.
맨 아래에 다음의 줄을 추가한다. (매일 새벽 3시 30분, 오후 3시 30분에 실행), /var/log/cert_renewal.log 파일로 결과 기록
30 3,15 * * * /home/사용자이름/renew_certs.sh >> /var/log/cert_renewal.log 2>&1
잘 동작하는 것을 보니 뿌듯하다.
그래도 며칠간 로그를 살펴보며 제대로 동작하는지 계속 모니터링해야겠다.
'공부' 카테고리의 다른 글
[이미지변환] HEIC To JPG (1) | 2025.06.20 |
---|---|
[CI/CD] Gitea + Act_Runner (1) | 2025.06.13 |
[REST] API 요청, 응답 (2) | 2025.06.12 |
[BE] SSE 재연결과 관련된 시간 (0) | 2025.06.12 |
[BE] 알림 기능 리팩토링 - 분산 환경 지원을 위한 Redis 추가 (0) | 2025.06.10 |