[ 배경 ]
학교 팀 프로젝트로 웹 애플리케이션을 만들기 위해 Godaddy를 활용해 AWS와 도메인을 연결했다.
AWS에서 nodejs로 만든 애플리케이션을 도커를 활용해 배포하는 환경까지 만들었다.
google map api 사용을 위해서 https를 지원해줘야 하는데
도메인을 발급 받을 때 ssl 적용이 안되는 상품을 구매했나보다..
우리의 지갑은 언제나 넉넉한 것은 아니므로 무료로 해결할 수 있는 방법을 찾게 되었다.
찾아보니 let's encrypt라는 곳에서 발급 받을 수 있다고 한다.
[ SSL 인증서 발급 ]
<1번째 레퍼런스 글 참조>
Certbot을 활용해 인증서를 생성할 수 있다.
AWS의 인스턴스에 연결해준 뒤 아래와 같이 certbot을 설치해준다.
sudo snap install certbot --classic
이후 standalone 방식으로 인증서를 생성한다.
다른 방법도 있지만 필자는 standalone 방식을 사용했으며 다른 방법은 아래 레퍼런스 링크에 나와있다.
sudo certbot certonly --standalone
dev@vultr:~$ sudo certbot certonly --standalone
[sudo] password for dev: <root password>
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): vompressor@gmail.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y <- ACME 약관에 동의하는지 N선택시 진행불가
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N <- 이메일을 통해 Let's Encrypt 프로젝트 정보를 받아볼지
Please enter in your domain name(s) (comma and/or space separated)
(Enter 'c' to cancel): vompressor.com www.vompressor.com <- {1} 인증서를 발급할 도메인 입력
Requesting a certificate for vompressor.com and www.vompressor.com
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/vompressor.com/fullchain.pem <- {2} 발급된 인증서 경로
Your key file has been saved at:
/etc/letsencrypt/live/vompressor.com/privkey.pem <- {2} 발급된 인증서 경로
Your certificate will expire on 2021-05-16. To obtain a new or
tweaked version of this certificate in the future, simply run
certbot again. To non-interactively renew *all* of your
certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
다른 방법으로 인증서를 받으려고 삽질한 것을 제외하면 이 과정에서 특별한 문제는 없었다.
인증서 경로로 가서 "ls -l"로 확인해보면 해당 파일들이 심볼릭 링크 파일임을 알 수 있다.
이 심볼릭 링크 파일은 도커 내부로 공유할 수 없기 때문에 원본 파일을 복사해야할 필요가 있다.
fullchain1.pem과 privkey1.pem 파일을 /var/www/custom_name 밑으로 옮겨준다.
(pem 파일의 이름은 약간 다를 수 있음)
[ dockerfile docker-compose.yml ]
먼저 nodejs에 대한 도커파일이다.
FROM node:16
# 앱 의존성 설치
# 가능한 경우(npm@5+) package.json과 package-lock.json을 모두 복사하기 위해
# 와일드카드를 사용
COPY package.json .
RUN npm install
# 프로덕션을 위한 코드를 빌드하는 경우
# RUN npm ci --only=production
# 앱 소스 추가
COPY . .
EXPOSE 3000
CMD [ "node", "app.js" ]
특별한 내용은 없고 포트 번호를 3000번으로 지정했다.
다음으로 docker-compose.yml 파일이다.
version: '3'
services:
nodejs:
container_name: "nodejs"
build:
context: "."
dockerfile: "dockerfile"
image: nodejs
restart: unless-stopped
nginx:
image: "nginx:latest"
container_name: "webserver"
restart: unless-stopped
ports:
- 80:80
- 443:443
depends_on:
- nodejs
volumes:
- ./data/nginx/conf.d:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
nginx에 대해선 아래에서 따로 컨테이너를 생성하니 없어도 될 것 같기도 하다.
(확실하진 않음)
[ nginx: container 생성 및 conf 파일 설정 ]
<2, 4번째 레퍼런스 글 참조>
다음과 같은 명령어로 빌드 및 실행이 가능하다.
docker-compose up -d --build
다음과 같은 명령어로 현재 실행되고 있는 컨테이너를 확인할 수 있다.
docker ps
아무튼 이 nginx라는 친구를 활용해서 https 서비스를 활성화 시켜줘야 한다.
다음과 같은 명령어로 컨테이너를 만들어준다.
docker run --name webserver -it -d -p 80:80 -p 443:443 -p 3000:3000 -v /var/www/custom_name:/var/www/custom_name nginx:mainline-alpine
webserver라는 이름으로 80, 443, 3000번 포트를 개방했다.
다음으로 exec 명령을 사용해 해당 컨테이너의 쉘로 접근한다.
docker exec -it [container name] sh
/var/www/custom_name 폴더에 가보면 아까 복사한 pem 파일들이 있을 것이다.
이제 nginx의 설정 파일을 바꿔주면 끝이다.
/etc/nginx/conf.d/default.conf 파일을 다음과 같이 바꿔줬다.
server {
listen 80;
listen [::]:80;
server_name Domain_name www.Domain_name;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/tripmate;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name Domain_name www.Domain_name;
server_tokens off;
ssl_certificate /var/www/tripmate/fullchain1.pem;
ssl_certificate_key /var/www/tripmate/privkey1.pem;
location / {
proxy_pass http://nodejs:3000;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
}
}
servername 옆에 있는 Domain_name을 본인의 도메인으로 바꿔주면 된다.
맨 아래 location의 proxy_pass의 nodejs는 본인의 노드 컨테이너 명으로 바꿔주면 된다.
수정을 끝낸 뒤 아래와 같이 테스트 및 적용할 수 있다.
nginx -T // conf 파일 테스트
nginx -s reload // conf 파일 적용
(만약 오류가 생긴다면 아래 네트워크 설정을 한 뒤 다시 시도하면 될 수도 있다..)
네트워크는 상관 없는 것 같습니다.
(참고만 해주세요)
conf 파일을 적용한 뒤 도메인으로 접속하니 그토록 찾아 헤맸던 자물쇠를 발견할 수 있었다..!
[ docker network ]
<3번째 레퍼런스 글 참조>
이 2개의 도커 컨테이너에 네트워크 연결을 해줘야 한다.
(같은 호스트의 도커끼리도 통신이 안되나 보다)
다음과 같은 명령어로 현재 생성되어 있는 도커의 네트워크 목록을 확인할 수 있다.
docker network ls
bridge, host, none은 Docker 데몬이 실행되면서 디폴트로 생성되는 네트워크하고 한다.
이런 디폴트 네트워크를 이용하는 것 보다는 사용자가 직접 네트워크를 생성해서 사용하는 것이 권장된다고 하니!
적혀있는 방법을 따라서 직접 만들어주자.
bridge 네트워크는 하나의 호스트 컴퓨터 내에서 여러 컨테이너들이 서로 소통할 수 있도록 해준다고 한다.
현재 하나의 aws 인스턴스에 2개의 도커 컨테이너가 있는 상황이므로 bridge를 사용할 것이다.
다음과 같은 명령어로 네트워크를 하나 만들어준다.
docker network create our-net
docker network ls로 확인해보면 bridge 네트워크가 생긴 것을 확인할 수 있다.
다음과 같은 명령어로 생성한 네트워크 our-net에 대한 상세 정보를 확인할 수 있다.
docker network inspect our-net
현재는 도커 컨테이너가 연결되지 않은 상태일 것이다.
다음과 같은 명령어로 컨테이너를 네트워크에 연결할 수 있다.
docker network connect our-net container_name
이 container_name은 docker ps의 결과의 name에서 확인할 수 있다.
nodejs 컨테이너와 nginx 컨테이너의 이름을 사용해서 연결해준 뒤
docker network inspect our-net으로 확인해주면
컨테이너들이 연결된 것을 확인할 수 있을 것이다.
도커에서 지원하는 ping 명령어로 테스트해볼 수 있다.
docker exec container_name ping container_name
ping이 정상적으로 이루어 진다면 실제 도커 환경에서도 통신이 정상적으로 이루어질 것이다.
[ dockerfile, docker-compose.yml 수정 ]
도커 파일을 통해서 nodejs와 nginx를 빌드 및 실행 할 수도 있다.
처음에는 잘 안 됐었는데 좀 더 시도해보니 성공했다.
먼저 nodejs 프로젝트 디렉터리에 nodejs에 대한 dockerfile과 설정을 위한 docker-compose.yml 파일을 만든다.
다음은 아까도 봤던 nodejs에 대한 dockerfile이다.
FROM node:16
# 앱 의존성 설치
# 가능한 경우(npm@5+) package.json과 package-lock.json을 모두 복사하기 위해
# 와일드카드를 사용
COPY package.json .
RUN npm install
# 프로덕션을 위한 코드를 빌드하는 경우
# RUN npm ci --only=production
# 앱 소스 추가
COPY . .
EXPOSE 3000
CMD [ "node", "app.js" ]
다음은 수정된 docker-compose.yml 파일이다.
version: '3'
services:
nodejs:
container_name: "nodejs"
build:
context: "."
dockerfile: "dockerfile"
image: nodejs
restart: unless-stopped
nginx:
container_name: "webserver"
image: "nginx:mainline-alpine"
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "3000:3000"
depends_on:
- nodejs
volumes:
- ./nginx:/etc/nginx/conf.d
- /var/www/tripmate:/var/www/tripmate
nginx 서비스에 대해 ports 옵션으로 80,443,3000번 포트를 열고
volumes 옵션으로 nginx의 기본 설정 파일을 변경시켜주고 인증서 키가 존재하는 디렉토리를 복사한다.
필자는 nginx 디렉터리를 만든 뒤 nginx.conf라는 파일을 만들었다.
(nginx의 기본 설정 파일은 /etc/nginx/conf.d 디렉터리에 존재함)
본인의 nginx 설정 파일과 인증서 키 파일의 위치에 따라 디렉터리를 바꿔주면 된다.
nginx 설정 파일은 위에서 설정했듯이 해주면 된다.
server {
listen 80;
listen [::]:80;
server_name Domain_name www.Domain_name;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/tripmate;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name Domain_name www.Domain_name;
server_tokens off;
ssl_certificate /var/www/tripmate/fullchain1.pem;
ssl_certificate_key /var/www/tripmate/privkey1.pem;
location / {
proxy_pass http://nodejs:3000;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
}
}
이제 다음과 같은 명령어로 빌드 및 실행을 진행한다.
docker-compose up -d --build
# 권한 부족으로 실패 시 sudo 사용
docker ps 명령으로 확인해보면 컨테이너들이 잘 실행되고 있으며 접속 또한 잘 된다!
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8d7aa79659d nginx:mainline-alpine "/docker-entrypoint.…" 45 hours ago Up 45 hours 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp webserver
84b1cfd50f2c nodejs "docker-entrypoint.s…" 45 hours ago Up 9 hours 3000/tcp nodejs
레퍼런스
https://www.vompressor.com/tls1/
https://anomie7.tistory.com/59
https://www.daleseo.com/docker-networks/
https://robot-log.tistory.com/4