최근에 나는 Ubuntu 18.04 에서 CloudWatch Logs 를 어떻게 설정하는지에 대한 글을 썼다. 그 글에서 나는 Agent 가 */var/log/syslog* 를 CloudWatch Logs로 syslog log 파일을 푸쉬하도록 설정했다. 이전 글을 보고 여기로 오길 바라고 혹시나 여러분이 Ubuntu 를 사용하지 않는다면 여러분이 사용하는 OS 에 CloudWatch Logs Agent 를 설치하기 위해서 AWS 문서를 체크해야 한다.
여기서 나는 Nginx 의 액세스 로그(access log) 와 에러 로그(Error log) 를 AWS CloudWatch Logs Agent 를 통해서 CloudWatch Logs 로 어떻게 전송하는지를 보여줄 것이다.
우리는 에이전트가 소비하기 원하는 두개의 Nginx 로그 파일을 가진다고 가정하자: */var/log/nginx/access.log* 와 */var/log/nginx/error.log* 로 원하는 만큼 많은 Nginx 로그를 추가할 수 있다.
AWS CloudWatch Logs Agent 는 *amazon-cloudwatch-agent.json* 파일로부터 설정들을 가지고 오고 이것은 Ubuntu 에서 */opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json* 위치해 있다.
이미 파일이 그곳에 있을거라 생각하고 collect_list 배열 아래쪽에 다음을 추가하면 된다.
AWS CloudWatch Logs Agent 설정
Default
1
2
3
4
5
6
7
8
9
10
11
12
{
"file_path":"/var/log/nginx/access.log",
"log_group_name":"web-server-log-group",
"log_stream_name":"{hostname}/access.log",
"timestamp_format":"[%d/%b/%Y:%H:%M:%S %z]"
},
{
"file_path":"/var/log/nginx/error.log",
"log_group_name":"web-server-log-group",
"log_stream_name":"{hostname}/error.log",
"timestamp_format":"[%d/%b/%Y:%H:%M:%S %z]"
}
여기서 핵심은 Nginx 로그 파일에서 timestamp_format 과 일치하는 포맷을 찾는다는 것이고, 만약 Ubuntu 에서 Nginx 에 기본 로깅 설정을 사용중이라면 위 설정은 잘 맞는다.
또 log_group_name 이 CloudWatch Logs Agent 의 자격증명에 로그 스트림 생성을 위한 logs:CreateLogSteam, 로그 스트림 기술을 위한 logs:DescribeLogStream 그리고 로그 이벤트를 푸쉬하기 위한 logs:PutLogEvents 의 IAM 허가권을 가지는 로그 그룹과 일치하는지 확인해봐야 한다.
amazon-cloudwatch-agent.json 파일을 업로드 한 후에 agent 서비스를 재시작해줄 필요가 있다:
Nginx 는 전세계적으로 인기있는 웹 서버 입니다. 기존 웹 서버들과 달리 고속이며 대량의 접속을 적은 자원으로 처리해줍니다. 또, Nginx 는 Reverse Proxy 기능도 아주 훌륭하게 수행하며 이에 더해서 로드밸런싱 기능도 제공하는듯 다양한 기능을 다른 웹서버들보다 훌륭하게 수행합니다.
아키텍쳐(Architecture)
먼저 다음과 같은 아키텍쳐를 생각해 봅시다.
AWS 에 흔히 볼수 있는 기본적인 아키텍쳐입니다. 외부 접속은 External ELB가 담당하고 이것을 뒤에 Nginx 서버가 ELB의 접속을 받아서 Nginx 뒤에 있는 Jboss EAP 서버에 분배 접속을 하도록 하는 것입니다.
Nginx 로드 밸런싱(Load Balancing)
Nginx 는 자체적으로 로드 밸런싱 기능을 제공합니다. 이는 Nginx 의 Upstream 모듈을 통해서 제공 합니다. 기본적인 설정 방법은 다음과 같습니다.
Nginx 의 upstream 기본 설정.
Apache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http{
upstreammyapp1{
serversrv1.example.com;
serversrv2.example.com;
serversrv3.example.com;
}
server{
listen80;
location/{
proxy_passhttp://myapp1;
}
}
}
ELB 에서 Nginx 로 80 포트(port)로 접속을하면 proxy_pass 에 정의된 upstream 인 myapp1 으로 연결을 시켜주며 myapp1 upstream 에 정의된 서버 3대 중에 하나에 연결을 시켜줍니다.
upstream 에서 다양한 옵션을 제공 합니다. 대표적인 것이 Nginx 뒤에 서버의 연결 상태를 어떻게 체크할 것인가 하는 것입니다. 예를들면,
srv1.example.com 서버는 30초 동안 최대 3번 접속이 실패하면 30초 동안 접속이 불가한 것으로 판단 합니다. 30초가 지나면 또 최대 3번의 접속 실패가 발생하고 나서야 30초동안 접속을 하지 않습니다.
지속적인 서비스를 제공해야하는 상황에서 접속이 잘되는지 안되는지 실제로 접속을 해봐야만 한다면 고객들에게 불편을 제공할 것입니다.
AWS ELB 방식
AWS ELB 방식은 위 Nginx 의 max_fails 방법과는 다릅니다. ELB 는 뒤에 연결되는 인스턴스(Instance)가 살았는지 죽었는지를 판단하는 Health Check 기능을 제공합니다. 이것은 5초에 한번 Health Check 를 하고 10번이상 성공했다면 인스턴스가 살았다라고 판단해(InService 상태) 연결을 활성화 해줍니다. 만일 5초에 한번 Health Check 를 하는데 2번 실패를 했다면 인스턴스가 죽었다고 판단해(OutOfService 상태) 더 이상 연결을 시도하지 않습니다.
즉, 일정한 기준을 만족해야만 연결을 활성화 해준다는 겁니다. Nginx 에서도 이렇게 동작하면 얼마나 좋을까?
nginx_http_upstream_check_module
nginx_http_upstream_check_module 모듈은 Nginx.org 에서 배포하지 않고 개인 개발자가 개발한 Third Party 모듈 입니다. 이 모듈은 앞에서 언급했던 ELB 의 Health Check 기능을 제공해 줍니다. 특정한 기준을 통과하면 연결을 활성화 시켜주는 것입니다.
이를 활용하기 위해서는 Nginx 설치시에 같이 컴파일 설치를 해줘야 합니다.
Nginx 설치
이번 Nginx 설치는 Upstream Health Check Module 를 함께 설치하는 과정을 포함 합니다.
설치 환경은 다음과 같습니다.
Ubuntu 14.04 64bit
컴파일을 위해서 설치한 패키지들: build-essential, automake, unzip, patch, libpcre++-dev, zlib1g-dev, geoip-bin, libgeoip-dev
먼저 7번째 줄에 Nginx 에서 실제 IP를 ‘X-Forewarded-For’ 헤더 값이라고 알려 주도록 설정한 것입니다. 8번째 줄은 AWS ELB 의 주소 범위를 말합니다. Nginx 는 AWS ELB 로부터 접속을 받기 때문에 그것을 지정해줄 필요가 있는데 그것이 바로 8줄 설정입니다. 12줄 ~ 14줄은 뒤에 WAS 서버에 넘겨줄 헤더값을 지정해주고 있습니다. WAS 서버도 실제 Client IP가 필요하기 때문에 이것을 ‘X-Forewarded-For’ 에 지정해 줍니다. 그외에 ‘X-Forewarded-Server’, ‘X-Forewarded-Host’ 도 지정해 줍니다.
18번째 줄부터는 이제 upstream 설정하는 부분인데, 24줄이 Health Checker 를 해주는 부분으로 nginx_http_upstream_check_module 기능 입니다. 3초에 한번 체크를 하는데 그 방법은 TCP를 이용하고 5번 연속으로 실패할 경우에 뒤에 서버는 다운된것으로 하고 2번 연속 성공하면 뒤에 서버가 살아있는것으로 해서 연결을 시켜줍니다. 마치 AWS ELB 처럼 동작하는 것입니다.
/status URL 을 호출하면 다음과 같이 나옵니다.
Rise Counts, Fall counts 는 3초마다 체크해서 그 수를 센 것입니다. 현재 뒤에 WAS 서버들이 하나는 살아 있고, 하나는 죽은것으로 되어 있고 Down 된 서버는 빨강색으로 표시됩니다.
체크하난 프로토콜을 변경할 수 있는데, http 를 이용할 경우에 보내는 url 과 기대되는 리턴되는 http 의 상태값(status)를 지정해주면 됩니다.
먼저, Keepalive 는 HTTP 와 TCP 상에서 구현되어 있다. HTTP 의 경우에는 1.0 버전과 1.1 버전이 존재하는데, 1.0 버전에서는 KeepAlive 는 존재하지 않으며 1.1 에서는 연결시에 기본으로 KeepAlive 가 활성화 된다.
HTTP 는 “Connetionless” 방법을 취한다. 웹 컨텐츠를 전송 받기 위해서 서버에 연결을 한 후에 데이터를 전송받는다. 그리고는 연결을 바로 끊게 된다. 하지만 웹이라는게 HTML, Javascript, CSS, Image 파일등으로 수백개로 이루어진 상태에서 수백의 컨텐츠를 전송하기위해서 연결을 수백번을 한다면 비 효율적일 것이다. 그래서 한번의 연결로 수십개의 컨텐츠를 전송하도록 해주는것이 KeepAlive 이다.
그렇다면 의문이 들 것이다. KeepAlive 를 켜주기만할 것이지 왜 timout, request 와같은 설정이 필요한 것일까?
답은 네트워크 상황이 좋지 않을때에 나타난다. 만약 KeepAlive 연결이 된상태에서 데이터 전송에 문제가 생겼을 경우 또는 Client 에서 데이터 전송을 하지 않을 경우에 이 연결된 접속은 어떻게 해야 할까? Server 접속 자원은 무한정이 아니기에 이러한 접속을 계속 유지되는 것은 Server 에 막대한 손실을 발생시키고 이는 곧 접속 장애로 이어진다.
그래서 접속이 이루어진 후에 컨텐츠를 전송받은후에 얼마간 또 다시 컨텐츠 전송 요청이 없다면 Server 가 접속을 차단시키도록 해야하는데 이것이바로 keepalive_timeout 이다. 다시말해 처음 접속이 이루어지고 컨텐츠를 한번 전송한 이후에 타이머를 실행시켜서 timeout 시간까지 Client 가 컨텐츠 요청이 없다면 Server는 접속을 차단하게 된다.
keepalive_request 는 Server 에 접속이 이루어진 이후에 컨텐츠를 요청한 갯수를 계산하고 이값을 넘으면 접속을 차단하는 것이다.
이렇게 접속이 차단되면 다음 번 컨텐츠 요청을 위해선느 새로운 접속이 이루어져야 한다.
그렇다면, Nginx 에서는 어떻게 이것을 다룰까? 이를 위해서는 먼저 Nginx 를 debug 모드가 활성화 되도록 컴파일 설치해야 한다.
Nginx debug 모드 활성화
Shell
1
2
3
./configure--with-debug
make
makeinstall
그리고 다음과 같이 error_log 에 debug 를 활성화 해줍니다.
Nginx error_log debug 설정
Apache
1
error_loglogs/error.logdebug;
keepalive_timeout 설정을 5초로 해줍니다.
nginx keepalive_timeout 설정
Apache
1
keepalive_timeout5;
이렇게해서 nginx 를 실행하고 error.log 파일을 보면 nginx 가 처리하는 과정을 상세히 볼 수 가 있다.
이와 더불어 HTTP 연결 테스트는 Telnet 을 이용하면 된다. 테스트는 다음과 같이 진행하면 된다.
telnet localhost 80
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
]# telnet localhost 80
Trying127.0.0.1...
Connected tolocalhost.
Escape character is'^]'.
GET/index.htmlHTTP/1.1<--직접입력
Host:localhost<--직접입력
Connection:Keep-Alive<--직접입력
<--Enter쳐준다.
HTTP/1.1200OK
Server:nginx/1.4.2
Date:Sat,07Mar201515:56:37GMT
Content-Type:text/html
Content-Length:612
Last-Modified:Fri,06Mar201514:12:38GMT
Connection:keep-alive
ETag:"54f9b5d6-264"
Accept-Ranges:bytes
<!DOCTYPEhtml>
<html>
<head>
<title>Welcometonginx!</title>
<style>
body {
width:35em;
margin:0auto;
font-family:Tahoma,Verdana,Arial,sans-serif;
}
</style>
</head>
<body>
<h1>Welcome tonginx!</h1>
<p>Ifyou see thispage,the nginx web server issuccessfully installed and
working.Further configuration isrequired.</p>
<p>Foronline documentation andsupport please refer to
<ahref="http://nginx.org/">nginx.org</a>.<br/>
Commercial support isavailable at
<ahref="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you forusing nginx.</em></p>
</body>
</html>
Connection closed by foreign host.<---이메시지가나올때까지정확하게5초가걸리는데이는keepalive_timeout값이다.
마지막에 “Connection close by foreign host.” 메시지는 5초동안 아무런 요청이 없으면 나타나는데 이는 keepalive_timeout 설정된 값과 같다. 만일 5초가 지나기 전에 또 한번에 데이터 요청을 Server 에 보낸다면 keepalive_timeout 은 reset 되고 전송이 끝난후에 다시 타이머가 흐르기 시작한다.
이는 keepalive_timout 에 대해서 데이터 전송이 끝난 후에 다시 데이터 전송 요청때까지의 갭타임을 의미하기도 한다. 그 사이에 데이터 전송 요청이 없다면 Server 가 접속을 차단하게 된다.
위 실험에서 Nginx는 과연 어떻게 처리했을까? 로그에는 아주 많은 정보가 표시되는데, 전송이 모두 끝난후에 다음과 같은 로그가 보인다.