티스토리 뷰

2022.08 ~ 2022.09 진행
   - 푸 님의 클래스101 백엔드 시스템 개발 실무 정리
   - GCP, Jenkins, 무중단 배포(로드밸런싱), Git Sourcetree, MQ, Elasticsearch 개념과
      왜 사용하는지, 어떻게 사용하는지에 대해 알 수 있었던 강의였다. 

1주차 - Docker, Docker Hub, GCP 세팅

  • Docker 개념
    - 각각의 Docker 애플리케이션을 마치 '프로세스' 간주
    - db, redis, rabbitMQ 등 docker를 통해 프로세스를 실행하여 local, VM 환경에서 손 쉽게 사용 가능
  • Docker Hub
    - docker image 저장하는 곳 
    - jenkins에서 docker hub의 이미지를 가져와서 실행시키는 구조
  • GCP
    - 각 서버를 만들기 위해 제공하는 구글 클라우드 플랫폼

 

2주차- 애플리케이션 무중단 배포 - #1

  • 무중단 배포란
    - 새로운 버전의 어플리케이션을 배포할 때 중단없이 진행하는 것
    - 중단없이 배포할려면 서버가 2개가 필요함 -> Nginx 
    - 이전 V1 애플리케이션을 종료하고 새로운 버전 V2 실행하고 요청을 받을 준비가 될 때까지 서비스가 중단되는 시간을 "다운타임"
  • 리버스 프록시
    - 애플리케이션 서버와 사용자 사이의 중계 해줄 서버가 필요한데, 그 중간 역할을 하는 게 "리버스 프록시"
    -
    서버를 분산하여 각 서버가 받는 부하를 분산하는 것을 "로드밸런싱"
  • 여러가지 배포 방식
    - 대표적인 3개 : 롤링(Rolling) 배포, 블루 그린(Blue Green) 배포, 카라니(Canary) 배포
    - 토이 프로젝트 또는 트래픽이 적은 회사는 롤링 배포를 많이 사용한다.
  • Nginx와 트래픽 관계
    - Nginx는 서버 자원을 사용해서 동작하는 애플리케이션이기 때문에 트래픽을 무한정 받을 수 없음.
    - 그러면 어떻게 해야할까? 3가지 방법이 있다.
    Nginx 실행되는 서버 Scale-up, 네트워크 장치로 로드 밸런싱(Scale-out) - L4/L7, DNS 리다이렉션

2주차 - 애플리케이션 무중단 배포 - #2

  • GCP 머신 이미지란
    - 인스턴스의 모든 구성을 똑같이 맞추기 위해 이미지를 생성한다.(필요한 인스턴스를 잘 선택해야한다)
    - 이미지 생성 후 "인스턴스 만들기" 기능을 사용하여 새로운 인스턴스를 생성
  • GCP 메터 데이터 SSH 설정
    - GCP 인스턴스의 키는 매일 초기화 되기 때문에 jenkins 공개키를 담고 있는 파일이 삭제된다.
    - 그러므로 GCP 메타 데이터에 SSH 값을 넣어야한다.
  • 무중단 배포 과정

      - 3개의 서버의 실행하고 있을 때 1번과 2번 배포하게 되면 
         1번 : 서버 종료 -> 배포
         2번 : 서버 종료 -> 배포
         3번 : 모든 요청을 3번으로 받게 됨.
        이처럼 NGINX가 1번,2번이 요청을 받지 못하면 3번으로 가게 되므로 무중단 배포를 할 수 있다. 
         (기존 실행 포트를 종료하지 않는다는 가정하에)

  • Artillery 성능 측정하기
    상황 : 2~3분동안 매 초마다 8번 요청이 왔을 때
    1. 서버 1대

        2. 서버 2대

        3. 서버 3대

        - 서버가 1대일 경우 더 이상 트래픽을 받을 수 없어서 500 에러를 반환한다.
        - 서버가 많을수록 점점 부하가 줄어드는 것을 확인할 수 있다.

3주차 - Github Webhook과 Jenkins로 배포 자동화

  • Github Webhook 개념과 Jenkins 관계
    - local에서 push 했을 때 일어나는 기능을 의미한다.
    - http://{jenkins-ip}/github-webhook/ 설정하여 젠킨스에서 빌드를 하고 서버를 실행시키는 구조이다.
    - 빌드를 하고 jar 파일 실행할 때 기존 포트를 종료해야 한다.
sudo kill -15 $(sudo lsof -t -i:8080)
nohup sudo java -jar cpu-0.0.1-SNAPSHOT.jar > nohup.out 2>&1 &

       - kill : 실행하고 있는 8080 port 종료한다.
        - nohup ... & : 백 그라운드에서 즉, 젠킨스에서 실행하고 터미널이 꺼져도 계속해서 실행할 수 있는 명령어이다.
        - > nohup.out 2>&1 : nohup.out 파일에 표준 출력이 표준 에러로 출력한다는 의미이다.
          숫자 0은 표준입력(stdin), 숫자 1은 표준출력(stdout), 숫자 2는 표준오류(stderr)를 의미한다.
       
        만약 서버가 3대일 경우 무중단 배포를 어떻게 해야할까? 
        1. sleep {time} 시간을 줘서 하나씩 배포를 한다.
        2. jenkins pipeline을 사용하여 배포하는 방법

       Docker 인스턴스를 Webhook으로 배포하면?
        build를 젠킨스에서 하지 않고 Docker Hub에서 진행한다.
        
1. 소스 코드에서 이미지 빌드
        2. github webhook으로 docker hub 이벤트 실행

        3. docker hub 빌드 후 
        4. jenkins 기존 컨테이너 종료하고 docker hub에서 빌드된 이미지를 가져오고 새로운 컨테이너로 띄우기

3주차 - Message Queue 

  • 서버가 죽는 이유 -> tomcat queue 이해가 필요
    tomcat 기본 스펙 : queue 100 , Thread 200개 할당, Timeout 30s
    1. 사용자의 요청이 들어오면 Queue 저장
    2. 일 하고 있지 않는 쓰레드에게 요청
    3. 쓰레드 처리하는 양 < 사용자의 요청이 많으면 Queue 대기
    4. 대기 시간이 길수록 사용자의 요청에 대한 응답 시간이 길어짐
    5. Queue 100 size < 요청이 더 많아지면 해당 요청부터 실패 
        또는 Timeout 30초보다 길어지면 실패

  • Queue 사이즈를 늘리게 되면?
    많은 요청을 받을 수 있지만 timeout 으로 인해 실패
  • Timeout 시간을 늘리게 되면?
    실패 되어야 할 요청들이 큐에 계속 쌓이는 결과를 초래
  • Thread 사이즈를 늘리게 되면?
    쓰레드들은 CPU 공유하고 하나의 쓰레드가 CPU 일을 하게 되면
    다른 스레드들은 자기 차례가 오기까지 기다림

3주차 - Tomcat과 MessageQueue

  • Tomcat과 MessageQueue 차이
    Tomcat
    • 무엇을 저장하고 처리하는 게 특화 X
    • 메모리에 저장하기 때문에 톰캣이 종료되면 사라진다.

    MessageQueue
    • 저장 공간이 여러가지이다 (disk 등등)
    • 사용자의 요청이 몰려도 누락 없이 저장할 수 있다.
    • DB의 insert 하는 시간보다 Queue 넣는시간이 더 짧다.
    • Application 간의 의존성이 줄어든다.
      ex) app1 -> queue -> app2
      app2가 죽어도 queue 데이터는 유실되지 않는다.
    • 여러개의 Queue 두고 이중화 처리를 있다.
      한쪽 queue 죽더라도 다른 queue 죽지 않는다.
    • 스케일 아웃을 해도 하나의 큐는 유지된다(확장성)

3주차 - RabbitMQ 도입

  • 언제 사용하는 게 좋을까?
    ex) 주문 금액 결제
           주문 내역 생성
           재고 수량 변경
           주문 상품 배송 관련 등록
           회사 매출 시스템 같은 곳에 매출 금액 추가

    만약 모든 기능을 동기적으로 할 경우
    1. 고객이 결제 완료까지 오래 기다려야 하고
    2. 모든 서비스에 동시에 너무 많은 부하가 발생하고
    3. 저 요청 중 하나라도 실패하면 결제 자체를 처음부터 다시 진행해야 한다.

    주문 금액 결제 -> 동기적 처리
    나머지는 동기적으로 처리할 필요가 없기 때문에 비동기적으로 메세지 큐에 넣고 처리하는 방식이 좋다.

  • Producer와 Consumer
    - 메세지를 큐에 집어넣는 Producer
    - 메세지를 큐에 꺼내오는 Consumer
  • Producer <> Consumer 분리되어야 하나?
    분리 되지 않았을 때와 분리 되었을 때의 가장 큰 차이점은 Consumer 관련 기능을 배포할 때 얼마나 까다롭냐의 차이가 있다. 분리된 형태에서는 Consumer를 배포할 때 앞쪽에 RabbitMQ가 존재하기 때문에 모든 Consumer가 종료된 상태라도 문제가 없다. 하지만 분리되지 않은 형태의 경우 Consumer 기능을 배포하기 위해 애플리케이션을 배포할 때 무중단 배포를 위한 요소들이 고려가 되어야 한다.

  • 사용자 <-> Producer <-> RabbitMQ <-> Consumer
    1. 위와 같은 구조에서 사용자는 최초 요청을 Producer 보내기
    2. Producer는 그 메시지를 바로 RabbitMQ로 집어넣고, Consumer는 RabbitMQ에 있는 메시지를 가져와서 처리
    3. Consumer는 작업이 완료되었다는 것을 Producer 알려주기
    이를 Callback 이라고 하는데, 어떤 작업이 완료되었는지 알려주려면 처음부터 key는 Producer 쪽에서 유니크한 값을 갖고 있고 Counsumer가 작업이 끝나면 key를 통해 완료 여부를 알 수 있다.

4주차 - Elasticsearch(ES)

  • ES 개념

      - 역 색인 : DB 데이터 -> 내용, ES 내용 -> 단어로 찾을 수 있는 테이블이 추가되어 특정 단어를 빠르게 찾을 수 있음
      - 샤드(Shard) : 문서를 여러 곳에 쪼개어 저장하는 것, 리소스를 분산해서 검색 속도를 향상
      - 레플리카(Replica) : 동일한 데이터를 저장하는 의미로, 다른 노드가 문제가 생겨도 해당 노드에 데이터가 있으면
                                          서비스 유지

  • DB와 ES 차이
    1. 실시간 처리 불가능
    2. 트랜잭션과 롤백을 제공하지 않음
    3. 데이터를 수정할 수 없어서 삭제하고 추가해야 함

  • 실무에서 DB와 ES 활용
    ES 조회를 할 때 사용하기 때문에 cud를 할 때 ES에서 하지 않는다.
    ex) 1. 우선 DB로 데이터가 들어간다.
           2. DB에 들어간 데이터를 DB 쪽과는 다른 Queue 보낸다.
           3. 이 큐를 메세지를 컨슘하여 ES에 색인한다.
     
    여기까지 보면 데이터는 DB, ES 같은 데이터를 들고 있다. 
    이 상태에서 데이터를 업데이트를 할 경우, DB를 먼저 변경한다.
    그 이유는 트랜잭션 처리가 용이하기 때문이다.
    DB에 데이터가 변했다면 그 데이터를 Queue에 담아 ES에도 동일하게 반영한다.

 

기타 - 명령어 정리

  • GCP 인스턴스 명령어 
# CentOS 7 버전
sudo yum install wget
sudo yum install maven
sudo yum install git
sudo yum install docker

# docker 권한 및 실행 명령어
sudo systemctl start docker
sudo chmod 666 /var/run/docker.sock
# docker 컨테이너 종료
docker ps
docker container kill -s 15 {ports}

# Jenkins 패키지 설치 및 실행 명령어
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
sudo yum install jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins

# Jenkins 실행 키 값
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

# GCP SSH 명령어

# nginx 설치 및 로드밸런싱 설정
sudo yum install nginx
sudo systemctl start nginx
sudo vi /etc/nginx/nginx.conf
# 위 명령어를 입력하여 nginx 설정 파일로 진입 후 다음과 같이 내용을 변경
http {
   ... 
    upstream cpu-bound-app {
      server {설정 ip}:8080 weight=100 max_fails=3 fail_timeout=3s;
    }
    server {
        ...
        location / {
          proxy_pass http://cpu-bound-app;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection 'upgrade';
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
    }
}

# postgresql을 도커로 실행시키는 명령어
docker run --name pgsql -d -p 5432:5432 -e POSTGRES_USER=postgresql -e POSTGRES_PASSWORD=postgrespassword postgres

# Nginx의 에러로그를 확인하기 위한 명령어
sudo tail -f /var/log/nginx/error.log

# nginx error - connect()
sudo setsebool -P httpd_can_network_connect on

# JAVA 설치 버전별 확인 및 설치
sudo update-alternatives --config java
sudo yum install java-1.8.0-openjdk
  • RabbitMQ 
# local docker MQ, monitoring 설치
docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3-management

# 접속
localhost:15672
id : guest
pw : guest

# application.yaml
spring.rabbitmq
host: localhost
username: guest
password: guest
port: 5672
  • linux 기본적인 명령어
# pid 특정 조회 및 종료
ps -aux | grep java
sudo kill -9 {pid}

#CentOS 7 port 조회 및 삭제
sudo yum install -y lsof
netstat -lntp | grep 8080
sudo kill -15 $(sudo lsof -t -i:8080)
  • linux 알아두면 좋은 단축키
# 명령어 맨 앞, 뒤 가기
ctrl + A
ctrl + E

# vi 문자 맨 앞,뒤 가기(esc 누른 상태에서)
shift + ( 
shift + )

# vi 현재 문장에서 앞,뒤 가기(esc)
shift + ^ 
shift + $

# vi 맨 위로, 아래로 커서 이동(esc)
gg
shift + g
  • 알아두면 좋은 것
    - artillery(node), nGrinder(Java) - 스트레스 테스트 툴 
    - RabbitMQ vs Kafka