NEXTSTEP - 인프라공방 후기글이다.
인프라공방은 직접 인프라를 경험해보는 실습 강의로
AWS 사용, 성능 테스트 및 개선, 모니터링, 스케일 아웃, 쿼리 최적화, MySQL Replication 등을 진행해볼 수 있다.
미션 진행과정을 요약하기 때문에 자세한 내용이 궁금하면 수강을 추천한다.
👨🏻💻그럴듯한 인프라 만들기 - 학습 목표
🎯 AWS 상에서 네트워크를 구성하며, 네트워크 기본 개념들을 학습해보아요.
🎯 컨테이너를 학습하고 3 tier로 운영환경을 구성해봅니다.
🎯 개발 환경을 구성해보고 지속적 통합을 경험해봅니다.
첫 강의가 시작되기 전에 미리 OSI 7 계층을 학습하고 오면 좋다면서 youtube 링크를 공유한다.
1주차는 AWS를 이용하여 네트워크 망을 구성하고 배포하는 게 목표이기 때문에
OSI 7 Layer를 알아야 AWS를 세팅할 때 구성요소를 이해할 수 있다.
0단계 미션 - pem 키 생성하기
AWS 비용은 강의비에 포함되기 때문에 지급받은 AWS 계정으로 로그인한다.
0단계 미션으로 pem 키를 생성하는데 만드는 이유는 추후 생성하게 될 EC2 인스턴스에 접속하는데 필요하기 때문이다.
생성 방법은 단순해서 pem 키를 생성하고 다운받으면서 0단계 미션은 종료된다.
1단계 미션 - 망 구성하기
본격적인 미션으로 AWS으로 망을 구성한다.
다른 교육과는 다르게 인프라공방은 1주차 - 1단계를 안하면 다른 미션을 진행할 수 없다.
직접 구성한 망에서 미션을 진행해야하기 때문이다.
요구사항은 외부망 2개, 내부망 1개, 관리망 1개를 구성한다.
미션 진행 방법은 다음과 같다.
1. VPC 생성 - CIDR은 다른 수강생과 겹치기 않게 조심해야 한다
2. Subnet 생성 - 외부망은 64개씩 2개, 내부망과 관리망은 32개씩 한다.
3. Internet Gateway 생성
4. Route Table 생성
5. Security Group 설정 - 외부망, 관리망, 내부망 3개로 나눠서 포트를 오픈한다.
6. 서버 생성(EC2) - 이전에 생성한 네트워크 망에 맞게 인스턴스를 4개 생성한다.
EC2 인스턴스 4개가 생성되었으면 추가적으로 가이드에 따라 서버 환경 설정도 진행한다.
서버 환경설정
- 환경변수 적용
Sessio Timeout 설정을 변경하여 작업을 안 하면 터미널 연결을 해제하게 한다.
시간은 600초로 세팅한다.
$ sudo vi ~/.profile
HISTTIMEFORMAT="%F %T -- " ## history 명령 결과에 시간값 추가
export HISTTIMEFORMAT
export TMOUT=600 ## 세션 타임아웃 설정
$ source ~/.profile
$ env
- shell prompt 변경
접근한 망이 여러 개면 현재 보고 있는 망이 무엇인지 헷갈리지 않게 하게 위해서 다음과 같이 변경한다
$ sudo vi ~/.bashrc
USERNAME=BASTION
PS1='[\e[1;31m$USERNAME\e[0m][\e[1;32m\t\e[0m][\e[1;33m\u\e[0m@\e[1;36m\h\e[0m \w] \n\$ \[\033[00m\]'
$ source ~/.bashrc
USERNAME=값은 접속한 망 이름에 따라 변경해준다
- logger를 사용하여 감사로그 남기기
직접 서버에서 작업할 경우, 작업 이력 히스토리를 남겨야 하기 때문에 다음과 같이 감사로그를 기록하게 적용한다.
$ sudo vi ~/.bashrc
tty=`tty | awk -F"/dev/" '{print $2}'`
IP=`w | grep "$tty" | awk '{print $3}'`
export PROMPT_COMMAND='logger -p local0.debug "[USER]$(whoami) [IP]$IP [PID]$$ [PWD]`pwd` [COMMAND] $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"'
$ source ~/.bashrc
$ sudo vi /etc/rsyslog.d/50-default.conf
local0.* /var/log/command.log
# 원격지에 로그를 남길 경우
local0.* @원격지서버IP
$ sudo service rsyslog restart
작업이 끝났으면 tail -f /var/log/command.log 명령어로 히스토리가 쌓이는지 확인.
그 외에도...
소스코드를 관리할 디렉토리 생성과 git, java 설치 등이 가이드에 따라 진행된다.
git 명령어를 통해 강의에서 제공하는 애플리케이션을 clone하고 직접 쉘 스크립트로 빌드도 해보고 실행도 해보면서 외부망에 접근이 되는지 확인한다.
# 직접 애플리케이션을 빌드한다
$ ./gradlew clean build
# jar파일 위치를 찾아본다.
$ find ./* -name "*jar"
# nohup 명령어로 프로세스가 계속 실행하게 한다.
$ nohup java -jar /home/ubuntu/nextstep/[프로젝트명]/build/libs/[jar네임] 1> infra-log 2>&1 &
여기서 1단계 미션은 마무리된다.
2단계 - 배포하기
이번 미션은 운영 환경과 개발 환경을 구성한다.
요구사항
운영 환경 구성하기
- 웹 애플리케이션 앞단에 Reverse Proxy 구성하기
- 외부망에 Nginx로 Reverse Proxy를 구성
- Reverse Proxy에 TLS 설정
- 운영 데이터베이스 구성하기
개발 환경 구성하기
- 설정 파일 나누기
진행방법
도커 설치하기
도커를 이용해서 환경을 구성하기 때문에 도커부터 설치한다.
sudo apt-get update && \
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common && \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
sudo apt-key fingerprint 0EBFCD88 && \
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \
sudo apt-get update && \
sudo apt-get install -y docker-ce && \
sudo usermod -aG docker ubuntu && \
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose && \
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
Nginx Reverse Proxy
리버스 프록시를 구성하기 위해서 Dockerfile을 생성한다.
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
Dockerfile에서 COPY 대상인 nginx.conf 도 생성한다.
events {}
http {
upstream app {
server 172.17.0.1:8080;
}
server {
listen 80;
location / {
proxy_pass http://app;
}
}
}
간단히 생성한 nginx 리버스 프록시다. 이제 미션에 따라 TLS, cache, gzip 설정이 하나씩 추가될 예정이다.
TLS 설정
클라이언트와 서버 간 통신상의 암호화를 위해 TLS 인증서를 적용한다.
무료로 이용할 수 있는 letsencrypt를 사용한다.
$ docker run -it --rm --name certbot \
-v '/etc/letsencrypt:/etc/letsencrypt' \
-v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
certbot/certbot certonly -d '[도메인 입력]' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
여기서 중요한 것은 명령어에 도메인을 입력해야 하는데 내도메인.한국에서 무료로 발급받은 loopstudy.p-e.kr 도메인을 이용한다.
진행 중간에 DNS 값을 세팅하고 넘어가야 하는 순간이 있다.
세팅을 안하고 계속 진행하면 발급이 실패하니 주의하자.
정상적으로 진행되면 TLS 인증서가 발급되는데
인증서를 Dockerfile이 있는 곳으로 복사한다
$ cp /etc/letsencrypt/live/[도메인주소]/fullchain.pem ./
$ cp /etc/letsencrypt/live/[도메인주소]/privkey.pem ./
Dockerfile과 nginx.cof에 TLS 설정을 추가해준다.
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY fullchain.pem /etc/letsencrypt/live/loopstudy.p-e.kr/fullchain.pem
COPY privkey.pem /etc/letsencrypt/live/loopstudy.p-e.kr/privkey.pem
events {}
http {
upstream app {
server 172.17.0.1:8080;
}
# Redirect all traffic to HTTPS
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/loopstudy.p-e.kr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/loopstudy.p-e.kr/privkey.pem;
# Disable SSL
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 통신과정에서 사용할 암호화 알고리즘
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
# Enable HSTS
# client의 browser에게 http로 어떠한 것도 load 하지 말라고 규제합니다.
# 이를 통해 http에서 https로 redirect 되는 request를 minimize 할 수 있습니다.
add_header Strict-Transport-Security "max-age=31536000" always;
# SSL sessions
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://app;
}
}
}
도커로 TLS를 적용한 리버스 프록시를 실행한다.
$ sudo docker build -t nextstep/reverse-proxy:0.0.2 .
$ docker run -d -p 80:80 -p 443:443 --name proxy nextstep/reverse-proxy:0.0.2
운영 DB 사용하기
관리망으로 접근해서 내부망에 docker를 설치하고 실습을 위해서 제공하는 컨테이너 DB를 이용한다.
$ docker run -d -p 3306:3306 bra**********/data-******:0.0.1
설정 파일 나누기
현재 프로젝트는 application.properties 하나로 사용되고 있다.
운영, 개발, 로컬 3가지로 구분할 수 있게 구성한다.
마지막으로 외부망 앱에 변경된 내역을 적용하고 빌드를 하는데 구분된 설정을 적용하기 위해서 -Dspring.profiles.active=값 을 추가한다.
$ nohup java -jar -Dspring.profiles.active=prod /home/ubuntu/nextstep/[프로젝트명]/build/libs/[jar네임] 1> infra-log 2>&1 &
TLS 적용된 도메인으로 정상적으로 접근되면 2단계 미션은 종료다.
3단계 - 배포 스크립트 작성하기
요구사항
- 배포 스크립트 작성하기
이전까지 직접 git pull -> ./gradlew clean build -> nohup ... (생략)
수동으로 배포하는 과정은 귀찮기도 하고 사람이기에 실수로 잘못될 경우도 있다.
이제 이런 과정을 배포 스크립트 하나로 통합하여 간단히 실행하게 만들어보는 미션이다.
배포 스크립트 만들기
외부망에 접속해서 배포 스크립트 파일을 생성 명령어를 실행하면 다음과 같이 빈 화면이 나온다.
sudo vi deploy.sh
이제 빈 화면에 배포 스크립트를 작성해서 저장하면 끝.
#!/bin/bash
## ...
## 변수 설정
txtrst='\033[1;37m' # White
txtred='\033[1;31m' # Red
txtylw='\033[1;33m' # Yellow
txtpur='\033[1;35m' # Purple
txtgrn='\033[1;32m' # Green
txtgra='\033[1;30m' # Gray
REPOSITORY=/home/ubuntu/nextstep/[프로젝트명]
JAR_REPOSITORY=${REPOSITORY}/build/libs
BRANCH=$1
PROFILE=$2
## 조건 설정
if [ $# -ne 2 ]
then
echo -e "${txtylw}=======================================${txtrst}"
echo -e "${txtgrn} << 스크립트 🧐 >>${txtrst}"
echo -e ""
echo -e "${txtgrn} $0 브랜치이름 ${txtred}{ prod | dev }"
echo -e "${txtylw}=======================================${txtrst}"
exit
fi
## ...
## 프로젝트 폴더로 move
## 저장소 pull
## gradle build
## 프로세스 pid를 찾고 종료하는 명령어
## 애플리케이션 배포 함수
function move() {
echo -e "${txtylw}=======================================${txtrst}"
echo -e ">> MOVE 🏃♂️ "
cd $REPOSITORY
echo -e "${txtylw}=======================================${txtrst}"
}
function pull() {
echo -e "${txtylw}=======================================${txtrst}"
echo -e ">> Pull Request 🏃♂️ "
git pull origin $BRANCH
echo -e "${txtylw}=======================================${txtrst}"
}
function build() {
echo -e "${txtylw}=======================================${txtrst}"
echo -e ">> Gradle Clean Build 🏃♂️ "
./gradlew clean build
echo -e "${txtylw}=======================================${txtrst}"
}
function findPidAndKillPid() {
echo -e "${txtylw}=======================================${txtrst}"
echo -e ">> Find Pid And Kill Pid 🏃♂️ "
CURRENT_PID=$(pgrep -f java)
echo -e "실행중인 프로세스 ${CURRENT_PID}"
if [ -z $CURRENT_PID ]; then
echo "> 현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
kill -2 $CURRENT_PID
sleep 3
fi
echo -e "${txtylw}=======================================${txtrst}"
}
function startServer() {
echo -e "${txtylw}=======================================${txtrst}"
echo -e ">> Start Server 🏃♂️ "
JAR_NAME=$(ls -tr $JAR_REPOSITORY/ | grep jar | tail -n 1)
echo -e "-Dspring.profiles.active=${PROFILE}"
echo -e "${JAR_REPOSITORY}${JAR_NAME}"
nohup java -jar -Dspring.profiles.active=${PROFILE} ${JAR_REPOSITORY}/${JAR_NAME} 1> infra-prod 2>&1 &
echo -e "${txtylw}=======================================${txtrst}"
}
move;
pull;
build;
findPidAndKillPid;
startServer;
배포 스크립트는 상황에 맞게 추가 및 변경하면 된다.
배포 스크립트가 정상적으로 진행되면 끝난다.
bash deploy.sh loop-study prod
이제 이전까지 수동으로 진행했던 과정은 위의 명령어로 단순하게 끝이 난다.
1주차 후기
1주차 주제에 맞게 네트워크와 AWS, 리눅스에 친해지는 시간이었다.
가이드에 따라서 네트워크 망을 어떻게 구성하는지 왜 이렇게 분리하는지 학습하고 직접 AWS에서 망을 하나씩 생성하고 구성하는 과정과
리눅스에서 개발환경을 구성하는 방법 등을 학습하고 배우는 과정에 많은 개념과 도구가 많이 스쳐 지나갔지만
인프라를 구성하고 실행되는 앱을 보면서 보람찬 기분을 느꼈다.
각 미션을 간략하게 언급하고 지나가서 간단해 보이지만 (실제로 다 하고 나면 간단한 미션이다)
생소한 AWS, 리눅스 환경에 적응하는데 많은 시간이 걸렸다.
스크립트 명령어부터 하나씩 검색하면서 알아보기도 했고 Dockerfile을 실행할 때 에러가 떠서 원인 찾는데만 몇 시간 걸린 적도 있는데 원인은 권한 문제로 sudo를 붙이니 제대로 실행이 되는 모습에 허탈하기도 했다.
배포 스크립트 실행하는데도 명령어 bash가 아닌 sh로 삽질한 적도 있다.
처음엔 작성한 스크립트를 어떻게 실행해야 하는지 몰라서 구글링으로 찾아봤는데 sh로 시작하는 줄 알고 열심히 했지만 Syntax 에러로 왜 안되나 시간 날리기도 했으며
TLS 인증을 포함하여 많은 곳에서 삽질을 했다.
1주차에서 정리할 내역
1주차에는 다양한 개념과 도구를 사용한다.
NEXTSTEP 다른 강의에 비교하면 인프라공방은 최소 3~4배 이상 되지 않을까싶다.
- 네트워크, OSI 7계층
- Cloud 사용이유 (AWS)
- 도커 개념
- 리버스 프록시
- 기타 등등
심지어 기간도 한 달밖에 안되다보니 인프라 환경에 익숙해지는 게 최우선 목표였지, 매주 언급된 개념을 익히고 사용된 도구에 익숙해지기엔 많이 부족했다.
'교육 및 인강 > 인프라 공방' 카테고리의 다른 글
인프라 공방 4주차 - ♾️ 확장하는 인프라 만들기 후기 (0) | 2022.05.23 |
---|---|
인프라 공방 3주차 - ⏱️안정적인 인프라 만들기 후기 2부 (0) | 2022.05.19 |
인프라 공방 3주차 - ⏱️안정적인 인프라 만들기 후기 1부 (0) | 2022.05.18 |
인프라 공방 2주차 - 🕵🏻♂️ 성능 진단하기 후기 (0) | 2022.05.16 |
인프라공방 시작글... (0) | 2022.05.13 |