하나의 Nginx 인스턴스를 로드밸런서로 설정하고, 나머지 두 인스턴스는 백엔드 서버로 설정합니다. 이 과정에서 Docker를 사용하여 각 인스턴스에 자바 애플리케이션이 실행되도록 합니다. aws에서의 nginx 설치방법을 다룹니다.
- 인스턴스 3개를 만듭니다.
- 인스턴스 설정: 인스턴스 3개를 생성합니다. 하나는 Nginx 로드밸런서로, 나머지 두 개는 백엔드 서버로 사용됩니다.
webserver 만들기
- 인스턴스 편집을 위한 연결창 이동하기
- 인스턴스 클릭 후 연결
- 도커 설치: 인스턴스에 연결한 후,
apt update
와curl
을 사용하여 Docker를 설치합니다. 이후 Docker를 사용하여 자바 애플리케이션을 실행하는 컨테이너를 구동합니다.
Bash
sudo apt update
curl https://get.docker.com/ | sudo sh
sudo docker run -d -p 8080:8080 ioshe/myjavarun:5.0
nginx 만들기
- Nginx 설치: 로드밸런서 역할을 할 인스턴스에 Nginx를 설치합니다.
Bash
# ec2에서
$ sudo apt install nginx -y
$ cd /etc/nginx/sites-available
$ sudo apt install vim
$ sudo vi test
$ cd cd /etc/nginx/sites-enabled
$ sudo rm default
$ sudo ln -s /etc/nginx/sites-available/test
$ sudo systemctl restart nginx
# 접속 후
$ cat /var/log/nginx/access.log
- Nginx 구성:
/etc/nginx/sites-available
에 새로운 구성 파일을 생성하고, 로드밸런싱을 위한 설정을 추가합니다. 이 설정에는 백엔드 서버들로의 요청 분산을 위한upstream
섹션이 포함됩니다. - 서버 연결:
/etc/nginx/sites-enabled
에서 기본 설정을 새 구성 파일로 대체합니다. 이를 통해 로드밸런서가 백엔드 서버로의 요청을 적절히 분산하게 됩니다.
test 코드
Bash
upstream backend { # backend자리에 프로젝트 이름
least_conn; # 접속을 위해 사용할 알고리즘 (기본: 라운드 로빈)
server localhost:8801; # 우회시켜줄 Server 1 정보
server localhost:8802; # 우회시켜줄 Server 2 정보
server localhost:8803; # 우회시켜줄 Server 3 정보
}
server {
listen 80; # 클라이언트가 요청하는 포트
location / {
proxy_set_header Host $host; # 클라이언트의 호스트 설정
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection ""; # upstream서버를 사용하겠다고 지정!!!!!!!!
proxy_pass http://backend; # 설정한 이름으로 요청 보내기
}
}
upstream
블록
upstream
블록은 백엔드 서버 그룹을 정의합니다. 여기서는backend
라는 이름으로 정의되어 있으며, 실제 프로젝트에서는 이 부분을 프로젝트의 이름으로 변경할 수 있습니다.least_conn
: 이 알고리즘은 가장 적은 연결을 가진 서버에 요청을 보내는 방식입니다. 기본적으로 Nginx는round-robin
방식을 사용하지만, 여기서는 연결 수가 가장 적은 서버를 우선하여 부하를 분산시키는least_conn
방식을 사용합니다.server localhost:8801
,server localhost:8802
,server localhost:8803
: 이 줄들은 개별 백엔드 서버의 주소와 포트를 정의합니다. 실제 환경에서는localhost
대신 각 서버의 IP 주소나 호스트네임을 사용해야 할 수도 있습니다. 여기서는 세 개의 서버가 동일한 기계에서 다른 포트(8801
,8802
,8803
)를 통해 동작한다고 가정합니다.
server
블록
listen 80
: 이 설정은 Nginx 서버가 클라이언트의 요청을 기다리는 포트를80
으로 설정합니다. 웹 트래픽에 표준 HTTP 포트를 사용하는 것입니다.location /
: 이 디렉티브는 모든 요청(URL 경로)을 처리하는 방법을 정의합니다.proxy_set_header
: 이 명령들은 프록시 서버가 백엔드 서버로 요청을 전달할 때, HTTP 헤더를 어떻게 설정할지 정의합니다. 클라이언트의 원래 IP 주소, 호스트 이름 등을 백엔드 서버로 전달하므로, 백엔드 서버가 마치 직접 클라이언트와 통신하는 것처럼 동작할 수 있습니다.proxy_pass http://backend;
: 이 설정은 들어오는 모든 요청을upstream
블록에서 정의한backend
그룹의 서버로 전달하도록 지시합니다. 이로써 로드밸런싱이 수행됩니다.
- server의 부분을 본인이 돌릴 서버의 주소로 바꾼다.
- /etc/nginx/sites-enabled에서 $ sudo ln -s /etc/nginx/sites-available/test 명령어를 통해 링크를 만들어준다.
결과
- 웹서버 접근: 로드밸런서를 통해 웹서버에 접근하면, Docker를 사용하지 않고 포트 할당 없이도 백엔드 서버에서 실행 중인 웹 애플리케이션에 접근할 수 있습니다.
- 로그 확인:
cat /var/log/nginx/access.log
명령을 사용하여 Nginx 로드밸런서를 통해 들어오는 요청 로그를 확인할 수 있습니다. 이를 통해 로드밸런서가 사용자 요청을 어떻게 처리하는지 파악할 수 있습니다.
log 출력하기
- 로그인 처리 과정에서 발생하는 이벤트를 로그 파일에 기록하는 방법
application.properties
- application.properties에 설정
Plaintext
# logging trace < debug < info < warn < error < fatal - 특별히 설정하지 않으면 info
# 로그를 기록할 레벨 순서
logging.level.root=info
# 로그파일을 유지할 기간
logging.logback.rollingpolicy.max-history=30
# 로그파일 1개의 최대 용량
logging.logback.rollingpolicy.max-file-size=100MB
# 로그파일 이름
logging.file.name=logs/demo.log
# 로그파일이 설정한 용량을 초과하거나 날짜가 변경될 경우 새로 만들어질 파일명
logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}-%i.log
# 로그파일에서 사용할 날짜 시간 형식과 타임존
logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS,Asia/Seoul
코드 설명
logging.level.root=info
: 로그 레벨을info
로 설정합니다.info
레벨 이상의 로그만 기록됩니다 (warn
,error
,fatal
).logging.logback.rollingpolicy.max-history=30
: 로그 파일을 30일 동안 유지합니다. 30일이 지난 로그 파일은 자동으로 삭제됩니다.logging.logback.rollingpolicy.max-file-size=100MB
: 각 로그 파일의 최대 크기를 100MB로 설정합니다. 파일 크기가 이를 초과하면 새 파일이 생성됩니다.logging.file.name=logs/demo.log
: 로그 파일의 이름을demo.log
로 설정하고,logs
디렉토리에 저장합니다.logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}-%i.log
: 새 로그 파일을 생성할 때의 이름 패턴을 설정합니다. 날짜와 인덱스를 포함한 파일 이름으로 롤링됩니다.logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS,Asia/Seoul
: 로그에 기록되는 날짜와 시간의 형식 및 타임존을 설정합니다.
필요한 곳에 log 출력
Java
@Slf4j
// 로그인 from에서 받은 정보를 함께 전달 -login
@PostMapping("/login")
public String login(@ModelAttribute UserDto dto, Model model, HttpSession session) {
try {
// 로그인 됐으면 -> /
UserDto result = service.login(dto);
session.setAttribute("loginUser", result);
log.info("id: {}", result.getId()); // 필요한 곳에 같은 패턴으로 로그 달기
return "redirect:/";
} catch (RuntimeException e){
// 로그인 되지 않은 경우 -> /
model.addAttribute("loginmsg", e.getMessage());
return "index";
}
}
@Slf4j
애너테이션은 Lombok 라이브러리를 사용하여 클래스에 자동으로 로거 인스턴스를 제공합니다. 이를 통해 로그를 쉽게 출력할 수 있습니다.log.info("id: {}", result.getId());
: 로그인 성공 시, 사용자 ID를info
레벨로 로그에 기록합니다. 이는 설정된 로그 레벨(info
)에 따라 로그 파일에 출력됩니다.
로그 레벨의 단계
- TRACE
- 가장 낮은 레벨로, 시스템의 동작을 상세하게 추적하고 싶을 때 사용합니다. 코드의 실행 흐름을 세밀하게 파악하기 위한 상세한 정보를 제공합니다.
- DEBUG
- 개발 과정에서 버그를 찾거나 시스템의 동작 상태를 확인하기 위해 사용합니다. TRACE보다는 덜 상세하지만, 개발자가 문제를 진단하기 위해 필요한 중요 정보를 포함합니다.
- INFO
- 시스템의 정상적인 작동 정보를 제공합니다. 사용자의 요청 처리, 시스템의 상태 변경 등 일반적인 운영 정보를 로깅합니다.
- WARN
- 잠재적인 문제를 경고합니다. 현재는 시스템에 치명적이지 않지만, 향후 문제가 될 수 있는 상황을 나타냅니다. 예를 들어, 사용되지 않는 API의 사용, 예상치 못한 상황 등이 있습니다.
- ERROR
- 예상치 못한 상황에서 발생한 오류를 나타냅니다. 시스템의 일부 기능이 제대로 작동하지 않거나 예외 상황이 발생했을 때 사용됩니다. 이 레벨의 로그는 즉시 조치가 필요한 문제를 나타냅니다.
- FATAL
- 시스템에 매우 심각한 문제가 발생했음을 나타내는 가장 높은 레벨입니다. 이러한 문제는 애플리케이션의 실행을 계속할 수 없을 정도로 치명적일 수 있습니다. FATAL 레벨의 이벤트는 매우 드물게 사용됩니다.