|
Public Cloud 시대에 접어들면서 사람들은 클릭 몇 번만 하면 네트워크 구축을 손쉽게 할 수 있는 시대가 되었습니다. 그래서 네트워크 통신은, 예전에도 그랬지만, 이젠 ‘당연히 되어야 하는 것’이 되었습니다. 그러면 이 당연한 것이 갑자기 안되면 어떻게 해야할까요? 예전엔 서버실에 가서 직접 Packet을 까보면 되었지만, 지금은 직접 갈 수 있는 서버실이 없어졌습니다. 우린 어떻게 네트워크 문제를 파악해야 할까요? 이번 글에서는 AWS에서 제공해주는 여러 가지 서비스 중, VPC Flow Logs 서비스를 활용하여 네트워크 트래픽을 효과적이고 빠르게 판단한 사례에 대해 소개합니다. |
들어가며
저희 Big Data Center에서는 다양한 환경의 데이터를 수집하고 있으며 삼성전자 내부망도 그중 하나입니다. 대규모 조직인 만큼 다양한 서비스가 내부망 안에 존재하고 데이터를 수집할 때 여러 가지 이슈를 겪게 되는데, 특히 통신 가능 여부에 대해 많은 문의를 받습니다. 오늘은 자주 나오는 이 문의에 신속히 답변하기 위해 어떤 고민을 했고 어떻게 자동화를 구현했는지 이야기하고자 합니다.
내부망 수집 구조와 두 가지 약점
본격적인 이야기에 앞서 내부망 수집 구조에 대해 살펴보겠습니다. 수집 시스템은 매우 복잡하지만 간략하게 아래와 같이 나타낼 수 있습니다.
Figure 1 내부망 수집 구조
위 구조도에서 알 수 있듯이, 데이터 수집 시 Direct Connect로 내부망에 연결된 계정을 이용해 삼성전자 서비스에 접근하고 있습니다. 이때 일반적인 주소를 사용하는 경우 AWS Network Load Balancer(이하 AWS NLB)에 직접 Target Group을 생성하여 접근하며, 특수한 주소를 사용하는 경우 Reverse Proxy Server를 이용해 우회적으로 접근합니다.
목적지가 잘 연결되었는지 확인하는 작업에는 AWS NLB의 Health Check 기능을 활용하고 있습니다. 이에 대해 다음과 같은 질문이 있을 수 있습니다.
“Reverse Proxy Server를 이용해 우회적으로 접근하면 NLB가 중간 경유지에 대해 Health Check를 수행하니까 최종 목적지의 상태를 알 수 없지 않나요?”
맞습니다. 이것이 위 구조도의 첫 번째 약점입니다.
그렇다면 두 번째 약점은 무엇일까요? AWS에 익숙한 분들은 이미 눈치채셨을 텐데요. ‘통신이 안될 때 그 상세 사유를 알 수 없다’는 점입니다. AWS NLB에서는 Target의 Health에 대해 Healthy/Unhealthy 등 다양한 상태 정보를 제공하지만 왜 Unhealthy인지는 이야기해주지 않습니다.
그렇다면 이 두 가지 약점을 이미 알고 있는 이슈라고 생각하며 감수하고 운영해야 할까요? 다행히 AWS에서는 이를 보완할 수 있는 훌륭한 기능을 제공합니다. 바로 VPC Flow Logs입니다.
약점 보완하기
AWS에서 제공하는 VPC Flow Logs는 VPC 내 네트워크 인터페이스 간의 트래픽 정보를 기록하는 서비스입니다. 이를 통해 VPC 내부에서 발생하는 트래픽의 출발지, 목적지, 프로토콜, 패킷 크기 등 다양한 데이터를 수집할 수 있습니다. 구체적으로 무슨 정보가 제공되는지에 대한 자세한 내용은 AWS 문서(Flow log records: https://docs.aws.amazon.com/vpc/latest/userguide/flow-log-records.html)를 참조해 주세요. 이러한 정보를 잘 조합하면 앞서 언급한 최종 목적지와 Reverse Proxy Server의 통신 여부 및 Target과 AWS NLB의 통신 상태를 직접 파악할 수 있습니다.
그럼 이 많은 정보 중에서 어디에 집중해야 할까요? 그걸 알려면 우선 TCP에 대한 이해가 필요합니다.
TCP Handshake
TCP는 OSI 7 Layer의 4번째 계층인 Transport Layer의 Protocol이라고 흔히 알려져 있습니다. Protocol이란 ‘기계 또는 시스템 간에 어떻게 데이터를 교환하고 통신할 것인지를 정의한 규칙과 약속’입니다.
TCP 통신 역시 고유한 Protocol 규칙을 따릅니다. 이를 TCP Handshake라고 부릅니다. TCP Handshake는 3-way Handshake와 4-way Handshake, 이렇게 2가지 종류로 나뉩니다. 이는 각각 TCP 통신을 연결하고 해제하는 방법에 대한 규칙입니다.
Figure 2 TCP Handshake
위와 같은 규칙을 이용하면 TCP 통신이 성립되고 해제됩니다. 이제 VPC Flow Logs를 설정하고 사용해 보도록 하겠습니다
VPC Flow Logs 설정 및 조회하기
VPC Flow Logs를 설정하기 위해서는 VPC에 Flow Log라는 객체를 만들어야 합니다. 퍼블릭 클라우드는 빠르게 변화하므로 설정 시에는 언제나 공식 문서(Working with flow logs: https://docs.aws.amazon.com/vpc/latest/userguide/working-with-flow-logs.html)를 함께 참조하는 것 좋습니다.
Flow logs settings 화면에서 아래와 같이 설정이 이루어집니다. (화면은 AWS update가 있으면 언제든 바뀔 수 있으니 유의하세요.)
Figure 3 Flow log setting (1)
여기서는 내부망 통신의 로그가 필요하므로 Log record format에 V2, V3 attribute 전체를 설정합니다. 만약 ECS나 Azure 등 다른 환경에 대한 추가 정보가 필요하다면 그에 맞는 정보를 Log record format에 추가하여 생성합니다.
VPC Flow Logs를 저장하는 방법에는 여러 가지가 있는데, 이 글에서는 VPC를 소유한 AWS Account의 지정된 S3 bucket에 저장하는 방법을 사용하겠습니다. 위 캡처 화면처럼 Destination을 S3 bucket으로 설정하고 적절한 ARN 및 prefix를 넣어줍니다. 위 캡처에서는 prefix를 넣지 않았지만 실제로 만들 때는 prefix를 넣어주었습니다.
이후에는 아래 캡처 화면처럼 Log format, Partition log 설정 시간 기준, tag 등을 지정할 수 있습니다.
Figure 4 Flow log setting (2)
설정을 모두 마치면 VPC Flow Logs 값이 S3 Bucket에 저장됩니다. 하지만 이 데이터를 그대로 읽어들이는 건 너무 비효율적이므로 Athena를 이용해 쉽게 조회할 수 있도록 해보겠습니다. 저는 아래와 같이 설정했습니다.
Figure 5 Glue table create query
Column에는 여러분이 설정한 Log record format의 attribute 값을 적절히 넣어주고, Daily partition이 적용되도록 Glue Table을 만들어줍니다. 이렇게 만든 Glue table을 이용해 Athena query를 실행하면 이제 로그가 예쁜 형태로 나올 겁니다. TCP 통신 기반으로 Health Check가 이루어지므로, 로그를 살펴보면 당연히 TCP Handshake의 로그가 보이겠죠?
임의의 목적지에 대한 request, response 로그를 한 번 확인해보겠습니다.
SQL Query
Figure 6 Select Query
결과
Figure 7 Athena query result
위 결과에서 tcp_flags 항목은 TCP 통신 시 기계 또는 시스템에 약속된 신호를 남기기 위한 용도로 사용되는 비트입니다. AWS에서 정의한 것처럼, 6비트를 10진수로 표현하다 보니 숫자 값이 들어가 있습니다. 예를 들면 3 = SYN & FIN, 19 = SYN & ACK & FIN 같은 식으로 표현됩니다.
그런데 예상과 달리 TCP 3-way/4-way Handshake의 패턴이 아닌 다른 값들이 보입니다. 왜 그럴까요? 이 현상을 이해하기 위해 Network Layer에 대해 잠시 살펴보겠습니다.
Layer라는 개념에 대해
Network Layer하면 가장 먼저 떠오르는 개념이 있죠. 바로 OSI 7 Layer입니다. OSI 7 Layer는 네트워크 통신을 7개의 Layer로 나누어 규격화한 것을 의미합니다.
하지만 현실에서 쓰이는 인터넷 통신 Protocol은 대부분 TCP/IP 모델을 사용합니다. 실질적인 표준으로 사용되고 있는 만큼 아주 많은 사람들이 사용하고 있으며 OSI 7 Layer에 비해 간소화된 Layer 구성을 갖습니다.
Figure 8 OSI and TCP/IP
TCP Protocol은 두 개념에서 모두 Transport Layer(Layer 4)에 속합니다. 즉, 인터넷 통신 Protocol의 중간 다리 역할을 담당하고 있습니다.
그렇다면 Application Layer에서 ‘Connection이 맺어진다’라는 개념은 Transport Layer에서 어떻게 표현될까요? 애석하게도 Application Layer의 의도를 Transport Layer에서 모두 표현할 수는 없습니다. 표현할 수 있는 정보가 서로 다르니까요.
예를 들면 JDBCConnection을 생성하는 통신을 시도했을 때 Transport Layer에서 SYN ACK 이외에 FIN, RST 같은 Flag가 관측될 수도 있다는 뜻입니다. 따라서 네트워크 트래픽을 정확히 분석하려면, 더 높은 Layer의 동작이 Transport Layer에서 다르게 표현될 수 있음을 감안하고 로그를 보아야 합니다.
VPC Flow Logs로 TCP 통신이 가능한지 분석하기
이제 Network Layer에 대해 알아보았으니 로그를 통해 통신 가능 여부를 확인하는 과정을 자동화해보겠습니다.
내부망 수집 구조도에 따르면 내부망에서 AWS Account로 들어오는 요청 패킷은 없으므로 VPC Flow Logs를 이용해 기록한 로그는 크게 2가지로 나뉩니다.
- AWS NLB가 Health Check를 하려고 보내는 패킷
- AWS NLB를 이용하여 데이터 수집을 하려고 주고받는 패킷
정상적인 VPC Flow Logs에는 다음과 같은 특징이 있습니다.
a. source <> destination 간 서로 패킷을 주고받음
b. destination이 주는 패킷의 TCP flag가 RST가 아님
c. source -> destination으로 향하는 패킷의 action 값이 ACCEPT
a에 대해서 예를 들자면, 출발지의 주소가 10.198.87.10:33058인 packet이 112.102.198.84:3306에 SYN 요청을 보냈다면 112.102.198.84:3306에서 10.198.87.10:33058에 ACK라는 응답을 한다는 뜻입니다. 이때 SYN 요청의 출발지 주소 및 포트 번호가 ACK 응답의 목적지 주소 및 포트 번호와 일치합니다. 서로 패킷을 주고받는지 확인하려면 이 조건을 이용해 자동화 로직을 만들 수 있습니다.
b의 경우는 목적지로 가는 데 방화벽으로 막혀 있진 않지만 목적지에서 이를 받아들이도록 설정되지 않아서 명시적으로 거부당하는 상황으로 볼 수 있습니다. 하나의 예를 들면, 10.198.87.10에서 MySQL 서버인 10.199.123.234:3306에 접근한다고 가정합니다. 이때 10.199.123.234:3306의 MySQL 설정값에서 bind_address 값이 127.0.0.1로 지정되어 있다면 요청이 정확하더라도 MySQL 서버가 이를 받아들이지 않게 됩니다.
c의 경우 AWS에서 Network 통신을 위한 객체가 없을 수 있습니다. 예를 들면, Security Group에서 112.102.198.84:3306으로 나가는 것을 명시적으로 허용하는 outbound rule이 없다면 이는 REJECT로 표기될 수 있습니다. 이 부분 역시 조건이 명확하므로 자동화 로직으로 만들 수 있습니다.
이제 위 조건들을 조합하여 자동화 로직을 만들어보도록 하죠. 로직이 조금 복잡하지만 최대한 풀어서 설명해보겠습니다.
3가지 조건을 확인하기 위해 Athena로 query를 수행해 결과를 갖고 있는 상태이고, 이를 List<VpcFlowResult>라는 객체에 Parsing해서 담고 있다고 가정하겠습니다.
Figure 9 VpcFlowResult class
Figure 10 unableToObserveRequestAndResponsePackets method
Figure 11 unableToObserve3wayAckPacket method
a 조건은 unableToObserveRequestAndResponsePackets method와 unableToObserve3wayAckPacket method에서 확인합니다. 우선 서로 주고받는 패킷이 존재하는지, 존재한다면 그 tcp flag 값이 무엇인지 분석하여 상호간 TCP 통신이 가능한 상황인지 판단합니다.
Figure 12 ableToWatchTcpFlagContainsSynAckOrUnsupportedTcpFlagsByAws
b 조건은 ableToWatchTcpFlagContainsSynAckOrUnsupportedTcpFlagsByAws method에서 확인합니다. AWS NLB의 Health Check 요청이 특정한 Application Layer의 명령어로 보여서 이를 목적지에서 처리한 tcp flag 값이 SYN과 ACK 값을 포함하고 있으면 TCP 통신이 가능한 상황으로 판단합니다.
이 로직은 주석에도 적혀 있다시피 현실 세계에서 관측 가능한 VPC Flow Logs의 응답을 토대로 만들었습니다. Application Layer의 동작이 Transport Layer에 완벽히 표현될 수 없기 때문이죠. 특이한 부분으로, AWS VPC Flow Logs에서 PUSH, URG와 같은 tcp flag 값은 0으로 표현되기 때문에 이를 통신이 되는 상황으로 간주하도록 만들었습니다.
Figure 13 ableToObserveRejectedPacketInBdcSide method
c 조건은 ableToObserveRejectedPacketInBdcSide method에서 확인합니다. AWS NLB에서 목적지로 요청하는 로그를 모두 가져와서 action 값(VpcFlowResult 객체에서는 packetStatus 값)을 모두 확인하여 REJECT 값이 존재하면 TCP 통신이 불가능한 상황으로 판단합니다.
Figure 14 analyzeNetworkStatus method
이 로직을 이용하여 반환되는 GuideStatus 값에 따라 후속 조치가 정해집니다. 만약 GuideStatus 값이 TCP_CONNECTION_IS_AVAILABLE 값이라면 TCP 통신이 가능하므로 이후 수집 단계를 실행합니다. 그렇지 않다면 GuideStatus 값에 맞는 운영 지침을 수행하도록 되어 있습니다.
맺으며
내부망의 서비스 데이터를 수집하기 위해 굉장히 많은 고민을 했었는데, 그 중 한 가지 솔루션을 소개할 수 있어서 기쁩니다.
일반적으로 네트워크는 한 번 설정하면 안정적으로 동작하는 경우가 많지만, 데이터를 수집하는 ETL Process를 운영해 보니 같은 목적지여도 상황에 따라 통신이 되지 않는 경우가 종종 발생했습니다. 그래서 VPC Flow Logs의 내용을 어떻게 판단하고 추상화할지에 대해 많은 고민을 하게 되었습니다.
이번 글이 저와 비슷한 고민을 하시는 분들께 작은 도움이 되었으면 좋겠습니다.
|
|
