[우아한테크코스] 9월 7일 TIL

6 minute read

[infra]성능 테스트

서비스는 안정성이 완벽할 수 없고 일정 부분 장애를 허용하며 그 장애에 대한 내성을 가진 서비스를 제공해야한다.

고가용성, 무중단 서비스의 핵심은 어느정도를 허용하며 배포 사이클을 유지하는 것이다.

cpu, memory, disk, network 등등의 부하를 에러로그를 이용해 확인할 수 있다.

응답시간이 20% 이상일 때 차이 인식하고, 3초 안에 로딩되어야 한다.

성능 테스트 방법

  1. 전구간

    브라우저로 직접 QA , 부하테스트

  2. 인터넷구간

    Webpagetest로 테스트

    가장 사용자 트래픽이 많은 페이지, 중요한 페이지에 집중해 성능테스트, 경쟁사이트 성능 조사

    웹 요청 시 성능에 영향을 주는 요소는 html, css, js, 이미지, 폰트 등이 있다.

  • webpagetest

    주로 웹 서버에 영향을 받는 지표로 정적 컨텐츠와 네트워크 상태에 영향받는 것으로 아래 요소들은 사용자 경험에 직접적으로 영향 끼침

    • Security score

      TLS, HTTP 헤더 보안성, JS 라이브러리 보안 취약성

    • First Byte Time

      서버 응답시간 + 네트워크 비용

    • Keep-Alive Enabled

      Tcp 연결 재사용을 위한 설정

    • Compress Tranfer

      스크립트 파일이 Content-Encoding으로 압축되어 있는가, gzip 압축

    • Compress Image

      이미지 압축했는가

    • Cache Static Content

      정적 파일 캐싱 설정

    • Effective use of CDN

      cdn을 사용하는지

    캐싱 설정, cdn 사용, keep-alive 설정, gzip 압축, 이미지 압축, 불필요한 다운로드 제거, 불필요한 작업 지연로딩, 스크립트 병합을 통한 성능 개선 가능

    여기까지는 프론트의 성능 테스트이다.

성능테스트

  • 가용성

    서비스를 정상적으로 제공할 수 있는 상태로 단일 장애점(SPOF)을 없애고 확장성 있는 서비스를 제작해야 한다.

    1. 서버 한대로 구축

      하나의 서버로 프로젝트를 가동하면 하나가 장애나면 프로그램을 구동할 수 없다.

    2. 서버 이중화

      단순한 장비 증설의 경우 db 데이터 분산으로 일관성 문제 발생

    3. Dns를 이용한 트래픽 분산

      서버 상태를 확인하지 않으므로 장애가 난 서버에 또 요청보낼 수 있음

    4. 어플리케이션 서버만 이중화

      db 서버가 단일장애점이 될 경우를 생각해야 한다.

    5. 결과적으로 모든 요소를 다중화해야 한다.

  • 다중화

    장애가 발생해도 예비 장비로 시스템을 지속할 수 있도록 하는 것으로 Server, Load balancer, Network device에 적용할 수 있다.

    db는 replication을 적용해 master-slave 관계를 갖도록 한다. 여기서 적용할 수 있다.

성능의 개선과 저하에 수익과 직접적인 연관이 있다. 참고

서비스가 얼마나 빠른지(Time), 초당 얼마나 많은 요청을 처리할 수 있는지(TPS), 얼마나 동시에 사용할 수 있는지(Users)에 따라 성능을 측정할 수 있다.

암달의 법칙은 시스템의 일부를 개선함으로써 얼마만큼의 최대 성능을 향상할 수 있는지 계산하는 것으로 (개선 후 실행시간 = 개선에 의해 영향을 받는 실행 시간 / 성능 향상 비율 + 영향을 받지 않는 실행 시간) 로 계산할 수 있다.

여기 서 부하에 대해서 볼 수 있다.

사용자(Users)

얼마나 동시에 사용할 수 있는지 관리해야 한다.

image

concurrent user는 웹 페이지를 띄워놓은 사용자로 언제든 부하를 줄 수 있는 사용자

active user는 직접 실제로 서버에 부하를 주는 사용자(vuser와 유사)

처리량(TPS)

처리량은 서비스 처리 건수 / 측정 시간, 요청 사용자 수 / 평균 응답시간, 동시 사용자 수 / 서비스 요청 간격 으로 계산할 수 있다.

사용자 증가 시 tps는 일정 정도 증가하다가 증가하지 않는다.

시간은 일정하게 유지하다가 점차적으로 증가한다.

지연시간이 변곡점에 이르기 전에 처리해야 한다.

stress test에서는 그 병목점 확인을 위해 진행한다.

  • scale out

    요청들이 몰려와 처리하지 못하고 포화상태가 되면 대기하고 있는 프로세스를 분산해 처리하도록 하는 것

  • Scale up

    서버 자체를 키워서 처리 능력을 향상시키는 것, 더 빠른 cpu, 더 많은 ram 등 적용

성능에 문제가 있으면 단일 사용자에 대한 응답속도가 느려진다.

확장성에 문제가 있는 경우엔 부하가 많아질 때 응답속도가 느려진다.

시간(Time)

image

Think time은 사용자가 요청에 대해서 응답을 받은 후 웹 페이지를 다니는 작업을 하는 시간

성능 테스트 시엔 실제 지연시간이 발생하는 구간을 파악해야 한다.

여기에는 정적 파일 크기, Connection 관리, 네트워크 환경이 client-server 간에 있을 수 있다.

server에서는 DB와 애플리케이션 간 연결의 문제, 프로그램 로직 상의 문제 혹은 서버의 리소스 부족이 문제가 될 수 있다.

Request time: Client -> N/W Connect -> Request data send

Response time: Server -> Response data receive -> N/W Close -> Client

성능 테스트는 N/W Connect부터 N/W Close까지

부하테스트

  • smoke

    VUser 1~2로 구성해 최소한의 부하로 테스트 시나리오 오류 검증

  • load

    평소 트래픽과 최대 트래픽으로 구성해 기능이 정상 동작하는지 검증

    배포, 인프라 변경 시 성능 변화 확인

  • stress

    점진적으로 부하가 증가하도록 구성, 최대 사용자, 최대 처리량 등의 한계점을 확인

    스트레스 테스트 이후 시스템이 수동 개입 없이도 복구되는지 확인

도구 k6

시나리오 기반 테스트

동시 접속자 수, 요청 간격, 최대 처리량 등의 부하 조정 가능

부하 테스트 서버 scale out 지원

  • 설치
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
$ echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
$ sudo apt-get update
$ sudo apt-get install k6
  • script 작성
// smoke.js
import http from 'k6/http';
import {check, sleep} from 'k6';

export let options = {
    stages: [
        { duration: '1m', target: 500 }, // simulate ramp-up of traffic from 1 to 500 users over 1 minutes.
        { duration: '2m', target: 500 }, // stay at 500 users for 2 minutes
        { duration: '10s', target: 0 }, // ramp-down to 0 users for 10 seconds
    ],
    thresholds: {
        http_req_duration: ['p(99)<1500'], // 99% of requests must complete below 1.5s
        'logged in successfully': ['p(99)<1500'], // 99% of requests must complete below 1.5s
        'create map': ['p(99)<1500'],
    },
};

const BASE_URL = 'https://zzimkkong-proxy.o-r.kr';

export default function () {
    var params = {
        headers: {
            'Content-Type': 'application/json',
        },
    };

    let findReservations = http.get(`${BASE_URL}/api/guests/maps/7/spaces/reservations?date=2021-09-08`, params);
    check(findReservations, {'find reservations': (obj) => obj.status === 200});
    sleep(1);
};

성능의 목표 정하기

  • DAU(Daily Active Users), 피크 시간 집중률, 1명당 1일 평균 요청 수
  • Throughput: 1일 평균 rps ~ 1일 최대 rps
  • 시나리오 정하기
    • 접속 빈도가 높은 기능: 홈페이지 등
    • 서버 리소스 소비량이 높은 기능
      • CPU
        • 이미지, 동영상 변환
        • 인증
        • 파일 압축/해제
      • Network
        • 응답 컨텐츠 크기가 큰 페이지
        • 이미지, 동영상 업로드/다운로드
      • Disk
        • 로그가 많은 페이지
    • DB를 사용하는 기능
      • 많은 리소스를 조합하여 결과를 보여주는 페이지
      • 여러 사용자가 같은 리소스를 갱신하는 페이지
    • 외부 시스템과 통신하는 기능
      • 결제 기능, 알림 기능, 인증/인가

[infra] 로드밸런싱

로드밸런싱이란, was 서버를 여러 대 두고 요청이 들어왔을 때 적합한 알고리즘에 따라 분배함으로써 한 서버에 가는 부하를 줄이기 위한 것이다.

우리는 web server로 nginx를 사용하고 있고, ssl 보안을 처리해주는 프록시 서버의 역할과 동시에 nginx에서 제공하는 로드밸런싱을 사용하고자 한다.

nginx를 선택한 이유는 레퍼런스가 많고 빠르고 가벼운 프로그램이라서 사용하였다.

뿐만 아니라 was가 비즈니스 로직만 담당하도록 하고 TLS와 같은 부수적인 기능으로 영향을 주고 싶지 않았고, load balancing, 보안성(백엔드 infra 숨김), 확장성, 웹 속도 향상, 압축/ssl 처리로 백엔드 자원 확보/캐싱의 이점을 얻을 수 있었다.

nginx 설치 : sudo apt-get install nginx

nginx 설정 파일 수정: sudo vi /etc/nginx/nginx.conf

nginx 재시작: nginx -s reload

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 20000;
}

http {
        ##
        # Zzimkkong Settings
        ##

        upstream app {
                # Service Server
                server 13.124.84.95:8080; # prod
                server 3.34.234.148:8080; # prod2
        }

        # Redirect all traffic to HTTPS
        server {
                listen 80;
                return 301 https://$host$request_uri;
        }

        server {
                listen 443 ssl;
                ssl_certificate /etc/letsencrypt/live/zzimkkong-proxy.o-r.kr/fullchain.pem;
                ssl_certificate_key /etc/letsencrypt/live/zzimkkong-proxy.o-r.kr/privkey.pem;
                ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
                ssl_prefer_server_ciphers on;
                ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
                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;
                }
	}

        ##
        # Basic Settings
        ##

        # sendfile on;
        # tcp_nopush on;
        # tcp_nodelay on;
        # keepalive_timeout 65;
        # types_hash_max_size 2048;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        # include /etc/nginx/mime.types;
        # default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

	log_format upstreamlog '$server_name to: $upstream_addr {$request} '
        'upstream_response_time $upstream_response_time'
        ' request_time $request_time';

        access_log /var/log/nginx/access.log upstreamlog;
        error_log /var/log/nginx/error.log;

	##
	# gzip Settings
	##

        # gzip on;

        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        # include /etc/nginx/conf.d/*.conf;
        # include /etc/nginx/sites-enabled/*;
}

위와 같은 설정으로 로드밸런싱을 할 수 있고, 필요에 따라 default인 round robin이 아닌 다른 알고리즘도 least_conn; 와 같이 전달해서 적용할 수 있다.

공식문서

로드 밸런싱을 적용하고 서버가 정상 상태인지 알기 위해 health check가 필요하다.

적용방법은 아래와 같고 commercial 버전에만 적용 가능하다.

location / {
                        proxy_pass http://app;
                        health_check;
                }

이를 적용하면 /var/www/html/status.html을 통해 정상적으로 로드 밸런싱이 되고 있는지 확인할 수 있다.

주기는 얼마나 할지, 경로는 어디로 할지, 몇번 이상 실패하면 비정상인지 등도 설정할 수 있다. 참고