티스토리 뷰

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 클라이언트로 서버에 접속해 보자. 처음 한 번은 ECDSA key fingerprint is ... Are you sure you want to continue connecting (yes/no)? 메세지를 만나는데, 'yes' 라고 답한다.
$ 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 서버에 접속해서 한다. 로컬 콘솔과 구분하기 위해 프롬프트를 사용자_이름@SERVER_HOSTNAME:경로라고 쓴다.

서버에 접속한다. 관리자 권한이 필요한 명령을 실행할 때마다 sudo 를 붙여야 하는 번거로움을 피하기 위해 sudo -s 명령으로 root 사용자로서 로그인한다. 프롬프트 기호가 샵(#) 기호로 바뀐다.  프롬프트의 달러($) 기호는 사용자, 샵(#) 기호는 관리자(root)임을 뜻한다.
$ 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 스택 설치

LEMP는 리눅스, 엔진엑스, Mysql, PHP를 줄여서 부르는 말이다. 미리 만들어 놓은 설치 스크립트를 내려받고 실행한다. 아래 예제의 provision.sh 스크립트의 코드는 각자 필요에 따라 수정해서 사용한다.
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 - 서버에서 웹 루트로 사용할 디렉터리의 절대 경로를 입력한다.
PHP 인터프리터가 잘 동작하는지 확인한다. 아래 콘솔 명령을 실행하고 웹 브라우저를 열어 서버 도메인 이름이나 IP 주소로 접속한다.
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 설치 및 설정

먼저 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는 지정된 이메일에 메시지를 보내 인증서가 만료 될 때 경고한다.

[참고: https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04]


테스트가 끝났으면 웹 서버 루트 디렉터리를 삭제한다(사이트 설정은 그대로 둔다)

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
링크