티스토리 뷰
2016년 1월 아마존 웹서비스(이하 'aws'로 표기)가 서울 지역 서비스를 시작했다. 서울 지역 서버는 국외에 있는 서버보다 빠르고 저렴하다. aws에서 사설 가상 서버(vpc)를 만들고, LEMP 스택을 설치한다. 이 과정을 프로비전(provision)이라 한다.
1. 계정 발급
aws에 처음 회원 가입하면 1년 동안 프리 티어(무료)를 이용할 수 있다. 가입할 때 해외 결제가 가능한 신용카드가 필요하니 미리 준비한다. 신용카드는 신분 확인을 위한 절차일 뿐, 결제가 되는 것은 아니다.
라라벨 프로젝트는 독립 서버에서 서비스할 것을 권장한다(공유 서버에서도 쓸 수 있다). 서버를 ssh 콘솔로 온전히 제어할 수 있다면 독립 서버라 할 수 있다. aws 프리 티어로도 온전히 제어할 수 있는 서버를 만드는 것이 가능하다.
2. 서버 만들기
회원 가입을 마치고 '콘솔에 로그인'하면 모든 서비스가 나열된 aws 관리 콘솔 화면을 볼 수 있다. 화면 오른쪽 위에서 '서울(Seoul)' 지역을 선택하고, 이어서 여러 제품 중 'EC2'를 선택한다.
'인스턴스 시작(Launch Service)' 버튼을 눌러 새로운 EC2 인스턴스(가상 사설 서버)를 만든다.
1단계: 아마존 머신 이미지 선택(Step 1: Choose an Amazon Machine Image)
선택할 수 있는 운영체제 목록이 나타났다. 우리는 우분투 로고 아래 '프리 티어 사용 가능'이라 꼬리표가 붙은 'Ubuntu Server 18.04 LTS...'를 선택한다.
2단계: 인스턴스 타입 선택(Step 2: Choose an Instance Type)
서버의 성능을 고르는 과정인데, 역시 프리 티어 사용 가능 꼬리표가 붙은 't2 micro'를 선택하자. 지금부터는 화면 아래쪽에 표시된 버튼을 이용해서 다음 단계로 넘어가야 한다. '다음: 인스턴스 세부 정보 구성' 버튼을 누른다.
3단계: 인스턴스 세부 설정(Step 3: Configure Instance Details)
서버 대수, 네트워크 인터페이스 등을 설정하는 페이지인데, 여기서 수정할 내용이 없다. '다음: 인스턴스 세부 정보 구성' 버튼을 눌러 다음으로 넘어간다.
4단계: 저장 장치 추가(Step 4: Add Storage)
8GiB가 기본값인데 그대로 두고 다음 단계로 넘어간다.
5단계: 인스턴스 이름(Step 5: Tag Instance)
인스턴스 목록에서 다른 인스턴스와 구분하기 위해 인스턴스 이름이 필요하다. '태그추가' 버튼을 누르고 적당한 키와 값을 임의의 값으로 입력한다.(ex - 키: name, 값: webserver). 다음으로 넘어간다.
6단계: 보안 그룹 설정(Step 6: Configure Security Group)
보안 그룹이란 일종의 방화벽이다. 이 서버가 외부에 열어 줄 TCP 포트와, 이 서버에 접속할 수 있는 클라이언트 IP 주소를 설정한다. 보안 그룹은 나중에 수정할 수도 있다.
기본값으로 SSH 하나만 있는데, '규칙 추가(Add Rule)' 버튼을 눌러 HTTP, (또는 HTTPS)와 MySQL을 추가하자. 0.0.0.0은 IP를 제한하지 않는다는 의미다.
7단계: SSH 키(Select an existing key pair or create a new key pair)
앞 단계에서 '시작하기(Launch)'버튼을 누르는 순간 팝업이 뜨는데, 서버에 접속하기 위한 SSH 키를 만드는 과정이다. 키 이름을 정하고, '키 페어 다운로드' 버튼을 누른다. 키_이름.확장자 파일은 ~/.ssh 디렉터리에 자장한다(이하 aws_seoul.pem이라 한다). 디렉터리가 없으면 만들면 되고, Mac에서는 Cmd+Shift+g 키를 누르면 ~/.ssh의 숨은 디렉터리를 열 수 있다. SSH 키를 잃어버리면 재발급을 받을 수 없으니 주의해서 보관하자.
SSH 키를 저장한 후, 알림창에서 '인스턴스 시작(Launch Instance)' 버튼을 누른다. '인스턴스 보기(View Instance)' 버튼을 누르면 인스턴스 목록이 표시된다.
다시 콘솔로 돌아와서 소유자만이 SSH 키를 사용할 수 있도록 권한을 설정한다(Windows 사용자는 깃 배시에서 아래 콘솔 명령을 수행한다). chmod 는 권한을 변경하는 명령이다. 400은 소유자만 읽을 수 있다는 의미다.
chmod 400 ~/.ssh/aws_seoul.pem
서버 접속 정보 확인
aws 관리 콘솔의 EC2 > Instance 메뉴로 들어가면, 앞서 만든 인스턴스를 볼 수 있다. 'Public DNS' 와 'Public IP' 를 잘 기록해 둔다.
3. 서버 접속하기
$ ssh ubuntu@PUBLIC_DNS -i ~/.ssh/aws_seoul.pem
# The authenticity of host 'PUBLIC_DNS (PUBLIC_DNS)' can't be established.
# ECDSA key fingerprint is SHA256:...
# Are you sure you want to continue connecting (yes/no)?
# yes를 타이핑하고 엔터
ubuntu@SERVER_HOSTNAME:~$
ssh ubuntu@PUBLIC_DNS -i ~/.ssh/aws_seoul.pem 에서 PUBLIC_DNS 부분은 실제 'Public DNS' 또는 'Public IP' 를 기입한다. ubuntu@PUBLIC_DNS는 ssh 명령의 인자다. 해당 도메인 이름을 가진 서버의 ubuntu 사용자라는 뜻이다. aws 가 제공하는 EC2 Ubuntu 서버의 루트 사용자 계정이 ubuntu다. -i ~/.ssh/aws_seoul.pem은 ubuntu 사용자의 비밀번호를 대신하는 SSH 키 옵션이다.
서버에 로그인한 상태에서 로그아웃 할 때는 exit 명령을 타이핑하고 Enter 키를 누른다.
짧은 접속 구문
다음은 접속 명령을 간단하게 만드는 방법이다.
~/.ssh/config 파일을 만들고 아래 코드의 내용을 각자의 서버에 맞게 작성한다. 이 파일은 IP 주소 또는 도메인 이름, 키 사용자 등이 기억나지 않을 때 기역력을 보조하기도 한다.
# ~/.ssh/config
Host myapp # 별칭 / 임의의 이름을 정한다.
Hostname PUBLIC_DNS # 서버의 Public DNS 나 Public IP 또는 도메인 이름
User ubuntu # 서버에 로그인할 사용자 계정
IdentityFile ~/.ssh/aws_seoul.pem
이제 아래 콘솔과 같이 접속한다.
$ ssh myapp # myapp은 앞서 만든 별칭이다.
4. 웹 서버 만들기
사용자 계정 만들기
$ ssh myapp
ubuntu@SERVER_HOSTNAME:~$ sudo -s
root@SERVER_HOSTNAME:~#
ubuntu 계정으로 웹 서비스를 할 수도 있지만, 새로운 사용자를 만드는 것이 더 낫다.
root@SERVER_HOSTNAME:~# adduser deployer
# 오류메세지가 출력된다면, 운영체제에 로캘이 없어서인데 무시해도 좋다.
# Enter new UNIX password: # 타이핑해도 화면에 표시되지 않는다.
# Retype new UNIX password: # 비밀번호 확인 여기서는 편의상 'secret'으로 한다.
# passwd: password updated successfully
# Changing the user information for deployer
# Enter the new value, or press ENTER for the default
# Full Name []: # Enter.
# Room Number []: # Enter.
# Work Phone []: # Enter.
# Home Phone []: # Enter.
# Other []: # Enter.
# Is the information correct? [Y/n] # Y
deployer 사용자를 www-data 그룹에 추가한다. www-data는 웹 사용자 그룹으로 웹 서버방문자에게 적절한 권한을 부여하기 위함이다. 가령 웹 서버 방문자에게 파일 업로드 서비스를 제공하려면, 파일을 저장할 디렉터리에 www-data 권한을 부여하면 된다.
root@SERVER_HOSTNAME:~# usermod -G www-data deployer
id 명령으로 확인해 본다.
root@SERVER_HOSTNAME:~# id deployer
# uid=1001(deployer) gid=1001(deployer) groups=1001(deployer),33(www-data)
다음으로 deployer 계정이 sudo를 붙이지 않고 몇 가지 필요한 명령을 수행할 수 있도록 허가한다.
root@SERVER_HOSTNAME:~# visudo
열린 파일 끝에 아래 내용을 입력하고 저장한다. deployer 사용자가 sudo 나 비밀번호 없이 명령을 입력할 수 있도록 권한을 부여하는 과정이다. 그리고 www-data 그룹도 php7.2-fpm과 nginx를 sodo 나 비밀번호 없이 다시 시작할 수 있도록 설정했다. 파일 수정이 끝나면 저장하고(Ctrl+o) 콘솔로 빠져나온다(Ctrl+x).
# /etc/sudoers
deployer ALL=(ALL:ALL) NOPASSWD: ALL
%www-data ALL=(ALL:ALL) NOPASSWD:/usr/sbin/service php7.2-fpm restart,/usr/sbin/service nginx restart
LEMP 스택 설치
root@SERVER_HOSTNAME:~# wget https://raw.githubusercontent.com/Austin-Kho/redem/master/provision-script/provision.sh
# ...
root@SERVER_HOSTNAME:~# bash provision.sh deployer secret | tee log.txt
# 전체 과정은 5분 정도 걸린다.
- wget URL - 명령줄에서 실행하는 HTTP 클라이언트다. 아무 옵션 없이 실행하면 URL에 있는 리소스를 내려받아 현재 경로에 저장한다.
- bash provision.sh deployer secret - bash 는 셸 애플리케이션의 이름이다. 그 뒤에 달린 문자열은 전부 bash 의 인자다. provision.sh 는 LEMP와 기본 패키지를 설치하는 자동화 스크립트다. deployer secret은 이스크립트의 실행에 필요한 인자다. 전자는 사용자 이름, 후자는 MySQL의 비밀번호이며, MySQL 비밀번호는 나중에 바꿀 수 있다.
- | tee log.txt - 스크립트 실행 결과를 화면에도 출력하고, log.txt 파일에도 기록하기 위한 구문이다.
root@SERVER_HOSTNAME:~# nginx -v # nginx version ...
root@SERVER_HOSTNAME:~# mysql --version # mysql Ver ...
root@SERVER_HOSTNAME:~# php --version # ... php ... v7.2...
root@SERVER_HOSTNAME:~# composer --version # Composer version ...
다음으로 nginx 관련 설정을 하기 전에 웹 브라우저에서 'Public DNS' 나 'Public IP' 를 입력해서 현재 상태를 확인해본다. 만약 아파치의 설치 화면이 로드된다면 아래 콘솔의 명령으로 아파치 서버를 정지 시킨 후 다음 과정을 진행한다.
root@SERVER_HOSTNAME:~# vi /etc/apache2/ports.conf # Listen 80 => Listen 8080
root@SERVER_HOSTNAME:~# /etc/init.d/apache2 restart
root@SERVER_HOSTNAME:~# /etc/init.d/apache2 stop
다음 명령 블록의 serve.sh는 nginx 사이트(아파치 웹 서버의 가상 호스트와 같음)를 빠르게 만들기 위한 스크립트다.
root@SERVER_HOSTNAME:~# wget https://raw.githubusercontent.com/Austin-Kho/redem/master/provision-script/serve.sh
root@SERVER_HOSTNAME:~# bash serve.sh PUBLIC_DNS /home/deployer/www/myapp/public
- PUBLIC_DNS - 실제 'Public DNS' 나 'Public IP' 를 기입한다.
- /home/deployer/www/myapp/public - 서버에서 웹 루트로 사용할 디렉터리의 절대 경로를 입력한다.
root@SERVER_HOSTNAME:~# mkdir -p /home/deployer/www/myapp/public
root@SERVER_HOSTNAME:~# echo "<?php phpinfo();" > /home/deployer/www/myapp/public/index.php
- mkdir -p path - '-p' 옵션을 붙이면 만들 디렉터리의 부모 디렉터리가 없을 경우, 자동으로 만들어 준다.
- echo "<?php phpinfo();" > /home/deployer/www/myapp/public/index.php - 웹 서버 루트 경로에 index.php 파일을 만들고 '<?php phpinfo();' 를 기입하여 저장한다.
5. Let's Encrypt 로 Nginx 암호화하기 (SSL 적용)
Certbot 설치 및 설정
add-apt-repository ppa:certbot/certbot
해당 저장소에 담긴 패키지 정보를 확인할 수 있도록 다음과 같이 업데이트를 수행한다.
apt update
apt(또는 apt-get)를 이용해서 Certbot을 설치한다.
apt install python-certbot-nginx
설치된 certbot을 이용해서 'Public DNS'나 'Public IP' 또는 도메인(여기서는 편의상 domain.com으로 한다)에 대한 SSL 인증서를 발급받는다.
certbot certonly --nginx -d domain.com -d www.domain.com -d api.domain.com
# 위 명령을 실행하면 해당 도메인을 관리하는 사람의 이메일 주소를 입력하라고 나온다. 입력해 준다.
# 만료일에 대한 정보를 보내줍니다.
# 약관에는 동의(A)하여야 하며,
# EFF 재단과의 이메일을 공유할지 여부를 묻는 부분에 대해서는 원하시는대로 진행하면 된다.
아래와 같은 메시지들이 출력된다.
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/domain.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/domain.com/privkey.pem
listen 443;
Your cert will expire on 2019-06-22. 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"
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.
- 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
위 명령을 수행하고 나면, 아래 경로에 총 5개의 파일이 생성되어 있음을 확인할 수 있다. (실제로는 /etc/letsencrypt/archive/domain.com 디렉터리 내 파일에 대한 심볼릭 링크이다).
# ls -al /etc/letsencrypt/live/domain.com/
total 12
drwxr-xr-x 2 root root 4096 Mar 24 05:33 .
drwx------ 3 root root 4096 Mar 24 05:33 ..
-rw-r--r-- 1 root root 692 Mar 24 05:33 README
lrwxrwxrwx 1 root root 34 Mar 24 05:33 cert.pem -> ../../archive/redem.shop/cert1.pem
lrwxrwxrwx 1 root root 35 Mar 24 05:33 chain.pem -> ../../archive/redem.shop/chain1.pem
lrwxrwxrwx 1 root root 39 Mar 24 05:33 fullchain.pem -> ../../archive/redem.shop/fullchain1.pem
lrwxrwxrwx 1 root root 37 Mar 24 05:33 privkey.pem -> ../../archive/redem.shop/privkey1.pem
이제 domain.com이라는 도메인에 대한 SSL 요청을 처리하기 위해 필요한 인증서가 준비된 상태가 되었다. nginx가 이 인증서를 통해 https 요청을 처리할 수 있도록, 아까 설정했던 /etc/nginx/sites-available/domain.com 파일을 아래와 같이 수정해 보자.
# 아래 명령어로 기존 설정파일 편집
$ vi /etc/nginx/sites-available/domain.com
# 다음과 같은 형태가 되어야한다.
server {
# http://domain.com 요청에 대해
# https://www.domain.com 으로 리다이렉트 처리한다.
listen 80;
server_name domain.com www.domain.com;
return 301 https://www.domain.com;
}
server {
# https://domain.com 으로의 요청을 리스닝한다.
listen 443;
server_name domain.com *.domain.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
# 예제에서는 이 외에 별도 설정을 추가하지 않고, SSL 요청이 정상적으로 처리되는지만 확인한다.
# 일반적으로 아래에는 현재 도메인과 연결된 document_root 등에 대한 설정 기술하거나
# proxy 설정을 통해 동적 처리가 가능한 다른 WAS와 연동하여 응답을 핸들링한다.
}
위 설정을 저장한 뒤, conf 파일에 오타는 없는지 확인하고 문제가 없다면 설정 내용을 적용시킨다.
# 설정 파일들에 오류가 없는지 확인한 뒤
$ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# 오류가 없다면 설정 파일 내용을 적용합니다.
$ nginx -s reload
SSL 적용 확인
브라우저의 주소창에 https://domain.com(실제 적용한 도메인)을 입력하여 연결이 제대로 이루어지는지 확인한다. 브라우저 캐시로 인해서 https로 리다이렉트 처리가 되지 않은 페이지를 보여줄 수 있으니, 이런 경우 캐시를 삭제하고 새로고침을 실행한다.
브라우저 주소표시줄 좌측에 자물쇠가 활성화 된 모습을 확인할 수 있다.
Certbot 자동 갱신 확인
Encrypt의 인증서는 90일 동안만 유효하다. 이는 사용자가 인증서 갱신 프로세스를 자동화 하도록 권장하기 위한 것이라고 한다. 우리가 /etc/cron.d 에 설치한 certbot 패키지는 갱신 스크립트를 추가하여 우리를 위해 이것을 처리한다. 이 스크립트는 하루에 두 번 실행되며, 만료 후 30일 이내에 모든 인증서를 자동으로 갱신한다.
갱신 프로세스를 테스트하려면 다음과 같이 실행한다
sudo certbot renew --dry-run
오류가 표시되지 않으면 모든 설정이 완료된 것이다. 필요한 경우 Certbot은 인증서를 갱신하고 Nginx를 다시 로드하여 변경사항을 적용한다. 자동 갱신 프로세스가 실패하면 Let's Encrypt는 지정된 이메일에 메시지를 보내 인증서가 만료 될 때 경고한다.
테스트가 끝났으면 웹 서버 루트 디렉터리를 삭제한다(사이트 설정은 그대로 둔다)
root@SERVER_HOSTNAME:~# rm -rf /home/deployer/www/myapp
6. 데이터베이스와 사용자 키
root@SERVER_HOSTNAME:~# mysql -uroot -p
# Enter password:
mysql > CREATE DATABASE myapp;
# 사용자 추가 및 권한 부여
create user 'user'@'localhost' identified by 'password';
grant all privileges on *.* to 'user'@'localhost';
# grant all privileges on dbname.* to 'user'@'localhost';
mysql > quit;
deployer 사용자도 SSH 키를 이용해서 로그인할 수 있도록 설정해 놓자.
root@SERVER_HOSTNAME:~# mkdir /home/deployer/.ssh/
root@SERVER_HOSTNAME:~# cp /home/ubuntu/.ssh/authorized_keys /home/deployer/.ssh/
root@SERVER_HOSTNAME:~# chown -R deployer:deployer /home/deployer
root@SERVER_HOSTNAME:~# exit
ubuntu@SERVER_HOSTNAME:~$ exit
$
- Total
- Today
- Yesterday