프로젝트에 https
를 적용하는 과정에서 겪었던 오류에 대해 작성해두려고 한다.
우선 내 프로젝트는 docker-compose
를 통해 관리되고 있었고, 웹서버는 nginx
를 활용하고 있다.
https와 ssl 인증서에 관해서는 블로그 글을 참고하였다.
HTTP Over 'Secure Socket Layer'(SSL)
, SSL
을 사용하는 HTTP
HTTPS
를 사용하기 위한 SSL
인증서를 발급해주는 공인된 기관Let's Encrypt
에서 SSL인증서를 발급받는 자동화 툴certbot
을 활용하면 CLI를 통해 발급 받을 수 있지만, 90일 마다 재발급을 해주어야 한다. 다행히 docker compose
+ nginx
+ certbot
을 사용하여 이 과정을 자동화 해둔 설정 파일이 올라와 있어서 편하게 사용하였다.
가이드가 상당히 자세히 작성되어 있기 때문에, 발급을 시도하기 전에 먼저 읽고 시도하길 권한다.
미래의 나를 위해 정리해보자면,
사전 준비 사항 - 도메인 발급 (A or AAAA record)
인증서 발급을 위한 certbot
컨테이너 만들기
https는 443포트를 이용하고 있기 때문에 nginx
컨테이너에 443포트 추가로 열어두기
nginx
설정파일 변경
기존 80포트에 작성되었던 내용을 전부 443 포트로 변경하고, 80으로 오는 요청을 전부 443으로 리다이렉션 해준다.
server {
listen 80;
server_name example.org;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name example.org;
이하 기존 80포트에 작성했던 것들...
}
certbot
이 발급한 인증서를 nginx
가 브라우져의 요청에 따라 반환할 수 있어야 하므로 certbot
컨테이너와 nginx
컨테이너는 같은 폴더를 공유해야 한다.
따라서 nginx
와 certbot
에 volume
추가
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
nginx
설정 파일 수정
certbot
이 발급한 challenge 파일을 nginx
가 서빙하도록 80포트에 추가
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
재발급이 가능하도록 command 추가
인증서 만료가 될 때쯤 자동으로 다시 SSL인증서를 다시 발급하도록 nginx
, certbot
컨테이너에 커맨드를 추가해주어야 한다.
nginx:
...
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
certbot:
...
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
.init-letsencrypt.sh
1~6까지만 하면 SSL재발급 자동화가 완성된다. 그렇지만 재발급을 하기 위해서는 첫번째 발급을 해야 하는데, 현재 nginx
폴더에 SSL 관련 정보가 있기 때문에 nginx
를 돌리기 위해서는 SSL 인증서가 필요하다. 따라서 이걸 위해서 더미 인증서를 발급받아 nginx
를 구동하고, 더미 인증서를 삭제하고 SSL인증서를 발급 받을 것이다. 이 과정을 자동화 한 쉘 스크립트가 .init-letsencrypt.sh
이다.
위에 단계가 여럿 있지만, 크게 수정 & 작성 해야 하는 파일은 세가지이다.
nginx.conf
변경사항
server {
listen 80;
server_name <your domain>;
server_tokens off;
location /.well-known/acme-challenge/ {
allow all;
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name <your domain>;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/<your domain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<your domain>/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
<이하 기존 80포트 내용 채워넣기>
}
docker-compose.yml
version: "3"
services:
nginx:
container_name: nginx
restart: unless_stopped
...
volumes:
...
- <data_path>/conf:/etc/letsencrypt
- <data_path>/www:/var/www/certbot
ports:
- 80:80
- 443:443
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
certbot:
container_name: certbot
image: certbot/certbot:arm32v6-latest
restart: unless-stopped
volumes:
- <data_path>/conf:/etc/letsencrypt
- <data_path>/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
...
.init-letsencrypt.sh
domain
- 서비스 도메인 기재data_path
- certbot 폴더 위치 기재.docker-compose.yml
의 volumes의 data_path
와 일치시켜야 함email
- 발급되면 메일이 온다는데 나는 안왔다...staging
- 발급 시도 횟수가 일정 수준을 넘어가면 한동안 발급을 받을 수 없다. 따라서 테스트 중에는 1로 두고 오류 없이 발급이 끝나고 https로 접속이 잘 되는 것을 확인한 후 0으로 바꿔서 다시 실행하자.여기까지 다 마쳤으면
docker compose build
./init-letsencrypt.sh
docker compose up
으로 실행하면 된다. 만약 성공했다면, 축하한다.
하지만 난 엄청난 시행착오를 거쳤기 때문에, 기록을 남겨놓겠다.
./init-letsencrypt.sh
실행 중 발생한 에러들...
Error: docker compose is not installed.
이건 3번줄에서 걸리는 건데, 나는 docker-compose
가 매우 잘 깔려있는 것을 확인 했음에도 번번히 이 명령줄에 걸려 스크립트가 종료되었다.
한가지 다른 것은 내 환경에서는 명령어가 docker-compose
가 아닌 docker compose
였기 때문에 그 부분을 고쳐주었으나, 전혀 먹지 않았다. 쉘 스크립트를 잘 다루지 못하는 나의 탓이니 그냥 주석처리해서 넘어갔다.
docker-compose not found
위에서 언급한 명령어 차이 때문이다. 스크립트 내의 모든 docker-compose
를 docker compose
로 바꿔주었다.
container nginx not found
이건 내 세팅 상의 문제인데, 나는 nginx
컨테이너와 서비스 이름을 frontend
로 주었는데, 이 쉘 스크립트에서는 nginx
라고 되어 있어서 문제가 생겼다. 쉘 스크립트에 언급된 nginx
컨테이너를 frontend
로 바꾸었더니 해결
echo "### Starting nginx ..."
docker compose up --force-recreate -d frontend
echo
...
echo "### Reloading nginx ..."
docker compose exec frontend nginx -s reload
실행했는데 아무 변화가 없음
docker compose build
빌드 하고 스크립트를 실행하는게 좋다. 빌드하다가 에러가 나는 경우도 있으니 디버깅에 유리.
exec /usr/bin/openssl: exec format error
certbot
버전 문제가 있는거 같다. docker-compose.yml
파일에서 버전을 수정했더니 해결되었다.
image: certbot/certbot:arm32v6-latest
디스크 용량 부족
갑자기 빌드 에러가 났다. 읽어보니까 디스크에 용량이 없단다. 아니 내가 뭘했다고...?
디스크 사용량을 조회해보자
df -h
usage가 100%가 나왔다...
도커 이미지, 볼륨 캐시파일 등 뭐가 잔뜩 있어서 용량이 꽉 차버렸다.
# 우선 실행중인 컨테이너 중단
docker compose down
# 중단된 컨테이너, 볼륨 다 삭제
docker system prune -a
이거 한방에 51%로 줄었다.
Fetching connection refused
이게 제일 시간을 많이 잡아 먹었는데, 정확한 해결 원인을 모르겠다. 우선 시도한 모든 사항을 리스팅할 예정.
우선 살펴보니까 nginx
컨테이너가 죽어서 /.well-known/acme-challenge/
으로 요청을 보냈을 때 응답이 없었던거 같다.
7-1. docker-compose.yml
nginx: restart: unless_stopped
추가
전혀 해결되지 않았다.
7-2. 로그를 보니까 ssl_certificate 등 파일을 찾을 수 없어서 에러가 났다고 나왔다. ssl 발급 전이라서 그런가? 하고 우선 nginx.conf에서 443부분을 모두 주석처리하고 스크립트 실행 -> 성공
이후 주석을 해제하고
docker compose exec nginx nginx -s reload
리로딩 -> 실패.
생각해보니까 nginx.conf
는 볼륨 연결을 하지 않아서 초기에 빌드할 때 설정파일을 복사하여 컨테이너에 따로 저장되어 있으니 변경사항이 반영될 리 X
따라서 volume에 nginx 설정파일을 연결한 뒤 다시 443 주석처리 -> 스크립트 실행 -> 주석해제 -> nginx reload => 성공.
우선... 성공했다.
근데 대체 이유를 알 수가 없다. 분명 쉘 스크립트에서 더미 ssl을 받아서 nginx
를 구동한 뒤 삭제하고 있는데...?
7-3. 이유를 찾다가 도커 파일에 nginx
컨테이너에 command 부분을 봤다. 생각해보니까 Dockerfile
에 CMD가 있는데...? 혹시 이것 때문인가 싶어서 Dockerfile
에 CMD를 주석처리한 후에 nginx
설정파일을 443부분을 주석처리하지 않고 스크립트를 실행해봤다.
됐다...
근데 찾아보니까 command와 CMD가 동시에 있으면 docker-comsose의 command가 실행된다는데 그럼 상관 없는게 아닐까...?
아무튼... 나는 CMD를 주석처리한 후에는 한번에 쉘 스크립트가 실행되고 있다.
init-letsencrypt.sh
는 이름대로 초기화, 즉 최초의 ssl 발급 시에 사용되는 스크립트이다. 따라서 이후에 새로 빌드할 일이 있는 경우 그냥 docker compose build
, docker compose up
등으로 실행하면 된다.
HTTPS, SSL 인증서: 아주 쉽고 간단하면서도, 매우 상세한 정리.
Docker-compose + Nginx SSL 적용하기 (certbot)
Boilerplate for nginx with Let’s Encrypt on docker-compose
Nginx and Let’s Encrypt with Docker in Less Than 5 Minutes
은인이십니다...