본문으로 바로가기

우아한테크캠프PRO 2기 7주차

이제 진짜 벌써 7주차 강의를 들었고, 다음주면 마지막 강의가 있는 주다,, 이번 미션이었던 서비스 진단하기 역시 정말 만만치 않았던 것 같다. 인프라를 구축하고 운영하는 것도 버거웠었는데 이제는 성능을 진단하고 예산을 판단해보고 타 경쟁사 사이트와 비교를 하면서, 어떤 부분을 개선하면 좋겠는지 예산은 얼마나 잡을 수 있을지 판단하는 미션이었다.
이번주는 5주차 미션과 6주차 미션을 병행중이 었고 5주차 미션인 인수테스트 기반의 TDD 는 3단계 PR 요청을 한 상태, 6주차 미션인 서비스 진단하기는 2단계 PR을 요청한 상태이다. 이번주 역시 어떤 것을 했고 무엇을 느꼈는지 회고를 해볼까 한다.

로깅과 모니터링

먼저, Logback 라이브러리를 이용해 로그 출력할 수 있는 환경을 만들었다. json, file, console 각 설정을 하게 되면 지정한 경로에 로그가 쌓이게 된다. 로그를 쌓을 때는 주의점이 있다.
  1. logging으로 인해 application 기능 동작에 영향을 미치지 않아야 한다.
    • logging을 하는 시점에 NullPointException이 발생하면 안된다.
  2. 각 logging 에는 데이터와 설명이 모두 포함되어야 한다.
  3. 로그에는 사용자의 전화번호, 계좌번호 등 개인정보를 남기면 안된다.
사실 로깅을 하는 것까지는 큰 무리가 없었다. 평소 현업에서도 로깅은 하고 있었기도 하고,, 하지만 다음이 문제였다. AWS의 Cloudwatch를 이용한 모니터링.. 인프라 미션을 진행하면서 미션 당 보통 한 두개로 새벽에 제대로 잠들지 못한 날들이 다분했는데.. 이번 Cloudewatch 역시..

Cloudwatch

Cloudwatch 는 EC2의 네트워크, 외/내부 통신, 메모리 사용량 등의 서버 내부의 데이터들을 수집해 시각화하여 모니터링을 할 수 있도록 도와주는 기능이다.
많이 어려웠던 만큼 사용법을 다시 복기해보자,,
  1. AWS콘솔에 로그인한 후 수집할 EC2의 보안에서 IAM role을 설정한다.
  2. 먼저 cloudwatch logs agent를 설치한다.
    • 이를 설치해야 AWS에서 우리가 실행하는 EC2 인스턴스에서 각 자원의 현황을 수집해 갈 수 있다.
$ curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O
$ sudo python ./awslogs-agent-setup.py --region  ap-northeast-2
  1. 로그를 수집하는 config 를 수정한다.
$ vi /var/awslogs/etc/awslogs.conf

[/var/log/syslog]
datetime_format = %b %d %H:%M:%S
file = /var/log/syslog
buffer_duration = 5000
log_stream_name = {instance_id}
initial_position = start_of_file
log_group_name = [로그그룹 이름]

[/var/log/nginx/access.log]
datetime_format = %d/%b/%Y:%H:%M:%S %z
file = /var/log/nginx/access.log
buffer_duration = 5000
log_stream_name = access.log
initial_position = end_of_file
log_group_name = [로그그룹 이름]

[/var/log/nginx/error.log]
datetime_format = %Y/%m/%d %H:%M:%S
file = /var/log/nginx/error.log
buffer_duration = 5000
log_stream_name = error.log
initial_position = end_of_file
log_group_name = [로그그룹 이름]

$ sudo service awslogs restart
  1. Metric를 수집한다
$ wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
$ sudo dpkg -i -E ./amazon-cloudwatch-agent.deb
# /opt/aws/amazon-cloudwatch-agent/bin/config.json
{
        "agent": {
                "metrics_collection_interval": 60,
                "run_as_user": "root"
        },
        "metrics": {
                "metrics_collected": {
                        "disk": {
                                "measurement": [
                                        "used_percent",
                                        "used",
                                        "total"
                                ],
                                "metrics_collection_interval": 60,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent",
                                        "mem_total",
                                        "mem_used"
                                ],
                                "metrics_collection_interval": 60
                        }
                }
        }
}
$ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json
$ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status
{
  "status": "running",
  "starttime": "2021-03-20T15:12:07+00:00",
  "configstatus": "configured",
  "cwoc_status": "stopped",
  "cwoc_starttime": "",
  "cwoc_configstatus": "not configured",
  "version": "1.247347.5b250583"
}
  1. AWS 콘솔로 들어가 다음 순서 대로 시각화할 위젯을 추가한다.
    • cloudwatch -> 위젯 추가 > 유형으로 행 선택 > 원본데이터로 지표 선택 > CPU Utilization, Network In / Out, mem_used_percent, disk_used_percent 등을 추가
  • Metrix를 수직하기 위한 Dependency
    • application.properties
      cloud.aws.stack.auto=false  # 로컬에서 실행시 AWS stack autoconfiguration 수행과정에서 발생하는 에러 방지
      cloud.aws.region.static=ap-northeast-2
      management.metrics.export.cloudwatch.namespace=  # 해당 namespace로 Cloudwatch 메트릭을 조회 가능
      management.metrics.export.cloudwatch.batch-size=20
      management.endpoints.web.exposure.include=*
  • dependencies { implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.cloud:spring-cloud-starter-aws:2.2.1.RELEASE") implementation("io.micrometer:micrometer-registry-cloudwatch") }
모든 설정을 끝나면 Cloudwatch를 통해 각 자원을 수집한다. 설정은 그렇게 어렵지 않았는데 EC2서버에서 WAS를 RUN하니 자꾸 오류가 났던.. 후.. 코드야 잘 못된 부분이 있으면 어떻게든 찾을 수 있겠는데 서버는 찾기가 참 힘들어서 많은 고생을 한 것 같다..

성능 테스트

성능 테스트 역시 오래 걸렸다. 성능 테스트는 우선 웹 성능 예산을 작성하고, 이를 작성하는 것은 PageSpeed 에서 내 어플리케이션과 경쟁사 어플리케이션의 성능 점수를 비교하고 개선점을 찾는다.
두번째는 Client와 동일한 환경에서 K6 TEST를 진행한다. K6 테스는 smoke, load, stress 테스트가 있고 js로 만들어진 파일에 vusduration을 설정하고 http_resq_duration, http_reqs_failed 등을 체크를 할 수 있다. vus는 실제 client와 동일한 가상 사용자를 만들고 js에 내 웹어플리케이션의 시나리오대로 메서드를 절차대로 구현하면 vus가 각각 정해놓은 duration 만큼 요청을 진행한다. js에는 로그인 -> 노선 검색 -> 구간 검색을 구현해 놔서 vus 가 각각의 시나리오 순서대로 메서드를 호출한다. 실제로 was log를 보면 엄청 빠르게 요청하는 것을 볼 수 있다.
말로는 쉬워 보이지만,,, 실제로 오류도 많이 발생하고,, 너무 어려웠다.. 처음이라 그런거라 생각한다..
import http from 'k6/http';
import { check, group, sleep, fail } from 'k6';

export let options = {
  vus: 1, // 1 user looping for 1 minute
  duration: '10s',

  thresholds: {
    http_req_duration: ['p(99)<1500'], // 99% of requests must complete below 1.5s
  },
};

const BASE_URL = 'https://chungsun.kro.kr';
const USERNAME = 'cndtjs0218@naver.com';
const PASSWORD = '8513';

export default function ()  {

  var payload = JSON.stringify({
    email: USERNAME,
    password: PASSWORD,
  });

  var params = {
    headers: {
      'Content-Type': 'application/json',
    },
  };


  let loginRes = http.post(`${BASE_URL}/login/token`, payload, params);

  check(loginRes, {
    'logged in successfully': (resp) => resp.json('accessToken') !== '',
  });


  let authHeaders = {
    headers: {
      Authorization: `Bearer ${loginRes.json('accessToken')}`,
    },
  };
  let myObjects = http.get(`${BASE_URL}/members/me`, authHeaders).json();
  check(myObjects, { 'retrieved member': (obj) => obj.id != 0 });

    라인조회(loginRes);
    즐겨찾기조회(loginRes);
    경로조회(loginRes);
    sleep(1);
};

export function 라인조회(loginRes) {
    let authHeaders = {
        headers: {
            Authorization: `Bearer ${loginRes.json('accessToken')}`,
        },
    };
    return http.get(`${BASE_URL}/lines/1`, authHeaders).json();
};

export function 즐겨찾기조회(loginRes){
    let authHeaders = {
        headers: {
            Authorization: `Bearer ${loginRes.json('accessToken')}`,
        },
    };
    return http.get(`${BASE_URL}/favorites`, authHeaders).json();
};

export function 경로조회(loginRes){
    let authHeaders = {
        headers: {
          Authorization: `Bearer ${loginRes.json('accessToken')}`,
        },
    };
    return http.get(`${BASE_URL}/paths/?source=1&target=6`, authHeaders).json();
};
  • 결과물

회고

아직은 처음이었어서 어려웠지만 실제로 현업에서도 이 테스트를 해보면 재밌기도 하고 실제로 부하테스트로 인한 오류를 사전에 처리할 수 있을 것 같기도하다, 이 미션을 하면서 웹 어플리케이션에서 js에 설정해놓은 메서드를 cashing 처리해보기도 했다. was log와 htop을 통한 EC2 실시간 메모리 등을 눈으로 직접 확인 하니 뭔가 재밌는 것 같기도.....?
K6 뿐만 아니라 부하테스트에 좀 더 공부해서 실제로 내가 만든 서비스가 어느정도 부하를 견디는지, 그 부하를 어떻게 더 개선할 수 있을지 까지의 선까지 닿을 수 있는 개발자가 되고 싶다는 생각을 했다,,

정리우테캠PRO 회고카테고리의 다른글

[우테캠 PRO 2기] 9주차  (2) 2021.07.20
[우테캠 PRO 2기] 지원  (0) 2021.07.05
[우테캠 PRO 2기] 5주차  (0) 2021.07.03
[우테캠 PRO 2기] 6주차  (0) 2021.06.25