brunch

You can make anything
by writing

C.S.Lewis

by 박지훈 Mar 31. 2017

TCP/IP

                    


1-1 : TCP/UDP

1-2 : TCP 연결과정

1-3 : TCP Header(Segment) Format

1-4 : 흐름제어

1-5 : 혼잡제어

1-6 : Tcpdump로 보는 TCP처리 과정

1-7 : TCP Syn flooding 

1-8 : TCP 트러블슈팅

1-9 : Linux TCP packet 흐름

1-10 : Linux TCP 성능 튜닝1 - offload 





1-1 TCP/UDP
TCP vs UDP
TCP UDP는 OSI7 계층 중 L4 Layer에 속함.


커넥션 오리엔티드와 리스 용도 설명


TCP/IP suite linux
TCP /IP Layer 흐름 예제. http://microchipdeveloper.com/tcpip:tcp-ip-five-layer-model 참고





ex) 클라이언트 => 서버로의 end - end 간 TCP 전송 과정의 예제.

 -> TCP 3way handshake 과정 이후 설명

==============================================================================

 - 송신 측 과정

==============================================================================

(1) Application :Application 을 통해 전송 해야 할 data를 Application Buffer 에 저장

 + 전송할 data 또는 file을 데이터를 읽어오고 (read) Application Buffer 에 저장 (send() 함수)

(2) Socket : File I/O을 위한 Kernel Buffer 에 저장

 + (1)에서 read 하여 Buffer 에 저장된 데이터를 Socket Kernel Buffer 에 저장(write) 진행

 + Socket Kernel Buffer 에 free 공간을 window size 라고 합니다.

(3) TCP : data 를 Segment 로 나눠서 Kernel Buffer 에 저장

 + (2) 과정에서 Buffer 에 저장 된 data을 쪼개서 Kernel Buffer 에 저장하고 이 쪼개는 즉 분해하는 과정을 Segment , segmentation 이라 한다. 이 과정에서 TCP 전송에 필요한 일부정보 (포트 정보 등)도 같이 저장된다. 

(4) IP : Segment 된 data 에 IP header 정보 등을 저장 해서 Packet 으로 변환

 + (3)번 과정에서 생성된 Segment data에 수신 받을 곳의 정보를 입혀 포장을 한 과정을 Packet 즉 패킷화 한다고 합니다.

 + 하나의 Packet 은 최대 크기는 1500byte 입니다.

(5) Device 레벨 : Packet 에 수신측 MAC 정보등을 추가로 담는 과정 Frame(실제 라우터를 통해 전송하는 단위) 화 한다.

 + Frame 하나당 packet 가 하나 일수도 있고, packet 을 여러개 담아 한번에 전송(jumbo frame) 할수도 있다.


==============================================================================

 - 수신 측 과정 

 + 수신 => 송신 과정에서 NIC 레벨이후 실제 스위치를 거처 라우터-인터넷-라우터로 수신측 장비까지 도달하는 과정을 거칩니다. 해당 과정에 라우팅 경로 거리 네트워크 장비 등의 특성으로 전송속도 (response time)에 영향을 줍니다.

==============================================================================


(6) 수신 측 NIC 레벨

 + 송신 측에서 보낸 Frame의 data을 NIC 내부 알고리즘에 의해 Frame을 rxBuffer(NIC 자체 buffer) 에 저장하고, packet 으로 까서 이걸 다시 IP 레벨 kernel buffer에 넣어 주는 과정을 거친다.

 + 일부 NIC H/W 성능이 낮거나 TX buffer size 가 적은 경우 loss 가 발생할 수 있다.

(7) 수신 측 Socket Buffer 레벨

 + 수신된 frame을 - packet - segment 까지 까고, socket buffer 로 올라가면서 분해됐던 data segment을 순서에 맞춰서 socket buffer 저장됩니다. 

 + socket buffer까지 올라온 후 커널에선 해당 요청을 잘 받았다 다음 data을 보내 달라라고 송신 측에 ack 

을 보냅니다.

 + 이 과정에서 socket buffer에 남은 사이즈를 계산해서 ack을 보낼 때 window size 정보도 같이 보냅니다.

 + ack을 보내는 과정은 이 보다 더 복잡합니다. (wait, timer  등 TCP 내부 정책(protocol)에 따라 실제로는 틀리게 동작할 수 있습니다.)

(8) 수신 측 Application Buffer 레벨

 + (7) 번의 socket buffer data을 Application Buffer로 move 하는 과정을 거칩니다. (recv())

+ 이 과정에서 application buffer 만큼 socket buffer로 move 된 data가 socket buffer에서 삭제되고 수신받을 수 있는 window size 가 늘어납니다. 

 + 해당 과정에서 NIC 및 kernel 레벨에 송수신은 빠르게 처리되지만, Application buffer로 move 하는 과정이 느려서 즉 window size  가 부족해 순간 수신을 못하는 상황이 올 수 있습니다. (이는 송신 측에 ack을 보낼 때 수신받을 수 있는 window size가 적다는 걸 보내기 (zero window 상태) 때문에 수신 측에선 이게 해소될 때까지 wait 상태로 대기합니다.)  

 + user kernel 레벨 즉 socket 레벨부터 Application 레벨 까진 사용자가 직접 하는 구간이며, 그 이하 TCP ~ device driver 레벨까진 kernel 에서 처리 합니다. 즉 TCP 트러블슈팅시에 문제 또는 튜닝 할 구간을 잘 파악하고, user 레벨 구간은 응용~socket 프로그래밍 디버깅 등으로 검증 해보고, kernel 레벨로 내려 갈 경우 사용하는 응용단의 트레픽 특징에 따라 kernel 파라미터 수정 또는 eth레벨의 옵션 항목을 변경 해 가면서 처리 합니다. 이 아래 즉 네트워크 장비 레벨로 갈 경우 L2~백본~LB 레벨까지 네트워크 구간을 본다.

트러블슈팅은 구간(end-end 사이에 모든 장비 마다 TCPDUMP 떠서 트러블 슈팅을 하고 대부분 문제 구간 확인이 가능하다.


=> TCP receive & send 하는 자세한 과정 및 튜닝 포인트는 아래  [1-9 Linux TCP packet 흐름 , 1-10 : Linux TCP 성능 튜닝1 - offload] 과정을 참고합시다.



<송신 시 각 레이어별로 header를 붙이면서 캡슐화(encapsulation) 하는 과정이며, 수신 측인 반대로 header를 하나씩 까면서 역 캡슐화(de encapsulation) 과정을 거친다. 예를 들면 A => B 서버로 파일을 전송 시, A서버는 파일을 쪼개서 나열 (TCP header에) 합니다. 이를 세그먼트라고 합니다. 이걸 또 포장하는 과정 즉 A, B IP 정보 (IP header)를 입히고, 이게 패킷화 한다고 하며, 여기에 추가로 A, B의 MAC 정보를 포장하는 과정을 프레임화 합니다. 나중에 TCP 분석 시 세그먼트, 패킷, 프레임 에러가 났을 경우 어떤 레벨의 문제인지 파악해야 합니다. > 




















1-2 TCP 연결과정

Establishing connection : 3 way handshake 연결 과정

Transmitting data : 데이터 전송 과정

Terminating connection : 4 way handshark 종료 과정


TCP 플로우 tcpdump

http://google.com 으로 호출했을 때 캡처


- TCP 데이터의 encapsulation 과정

 +  TCP 세그먼트(Segment) : 데이터를 네트워크를 통한 실질적인 전송을 위하여 적절한 크기로 분할한 조각. ( 아래 TCP Header Format 참조)

 + 패킷(Packet) : OSI 7 계층의 네트워크 계층(Network Layer)에서 다루는 내용
=> 전송을 위해 분할된 데이터 조각(세그먼트)에 목적지까지의 전달을 위하여 Source IP와 Destination IP가 포함된 IP Header가 붙은 형태의 메시지

 + 프레임(Frame) : OSI 7 계층의 데이터링크 계층(Data Link Layer)에서 다루는 내용
=> 최종적으로 데이터를 전송하기 전에 패킷에 Header(Mac Address 포함)와  CRC를 위한 Trailer가 붙은 메시지

L4 TCP Header


- Source Port : src 포트

- Destination Port : dst 포트

- Sequence Number : 전송되는 데이터의 Byte 순서 번호, 랜덤 한 숫자가 들어감.

- Acknowledgement Number : 수신 측에서 앞으로 받고자 하는 byte 순서 번호, 마지막으로 받은 데이터에 seq+1을 더함

- Herder Length - 목적지 호스트에게 TCP헤더 크기를 4byte 단위로 표현해 알림 (TCP헤더 시작부터 데이터 이전까지 길이)

- Reserved : 예약된 필드 offset

- TCP Flags

SYN(Synchronization:동기화) - S : 연결 요청 플래그

TCP에서 세션을 성립할 때  가장 먼저 보내는 패킷, 시퀀스 번호를 임의적으로 설정하여 세션을 연결하는 데에 사용되며 초기에 시퀀스 번호를 보내게 된다. 


ACK(Acknowledgement) - Ack : 응답

상대방으로부터 패킷을 받았다는 걸 알려주는 패킷, 다른 플래그와 같이 출력되는 경우도 있습니다.
받는 사람이 보낸 사람 시퀀스 번호에 TCP 계층에서 길이 또는 데이터 양을 더한 것과 같은 ACK를 보냅니다.(일반적으로 +1 하여 보냄) ACK 응답을 통해 보낸 패킷에 대한 성공, 실패를 판단하여 재전송하거나 다음 패킷을 전송한다. 


RST(Reset) - R : 강제 종료
재설정(Reset)을 하는 과정이며 양방향에서 동시에 일어나는 중단 작업이다. 비 정상적인 세션 연결 끊기에 해당한다. 이 패킷을 보내는 곳이 현재 접속하고 있는 곳과 즉시 연결을 끊고자 할 때 사용한다. 


PSH(Push) - P : 밀어 넣기

TELNET과 같은 상호작용이 중요한 프로토콜의 경우 빠른 응답이 중요한데, 이때 받은 데이터를 즉시 목적지인 OSI 7 Layer의 Application 계층으로 전송하도록 하는 FLAG. 대화형 트랙픽에 사용되는 것으로 버퍼가 채워지기를 기다리지 않고 데이터를 전달한다. 데이터는 버퍼링 없이 바로 위 계층이 아닌 7 계층의 응용프로그램으로 바로 전달한다. 


URG(Urgent) - U : 긴급 데이터
Urgent pointer 헤더를 참조하여 어디~어디까지 우선 보내달라고 우선순위로 요청.


FIN(Finish) - F : 연결 종료 요청
세션 연결을 종료시킬 때 사용되며 더 이상 전송할 데이터가 없음을 나타낸다.

Placeholder             
패킷의 플래그에 SYN, FINISH, RESET, PUSH 등의 플래그가 설정되어 있지 않은 경우 이 플래그가 세팅된다. 이 플래그는 ACK플래그와 함께 사용되는 경우도 있다.


- Window Size : 이만큼 보낼 수 있다.(송신윈도우). 이만큼 받을 수 있다.(수신윈도우).  (최대 크기 65535)

흐름 제어, 오류제어, 혼잡 제어를 사용하기 위해 필요하다.

- TCP Checksum : TCP헤더 + 데이터를 포함한 세그먼트 전체에 대하여 계산한 값이다. 에러 체크 시 사용

- Urgent Point : 긴급히 처리해야 할 필요가 있는 데이터의 마지막 바이트의 위치를 나타낸다. 

TCP Option : 연결이 구성되는 동안 협상할 최대 세그먼트 크기(MSS) 옵션을 정의 (Herder Length가 4byte 단위, ex) Herder Length가 1~4면 4byte, 6이면 8byte, 9면 12byte)











1-4 TCP 흐름 제어


흐름 제어 : Clint <-> Server 간 데이터 주고받을 때, 서로 간의 속도와 크기의 균형을 맞추는 것.

  - 종류

    + 정지 대기 [Stop-and-wait] : 한 번에 1개씩 프레임을 전송하는 방식.

   + 슬라이딩 윈도우 [Sliding Window] - 파이프라이닝 이라고도 하며, 서버 측 ack을 기다리지 않고 여러 패킷을 전송하는 방식.  (GBN, SR 오류제어)

TCP 흐름제어 종류

(그림 1). 정지대기

정지대기 :한 번에 1개씩 프레임을 전송하는 방식


(그림2). 슬라이딩 윈도우

윈도우(버퍼)크기는 일정하며, 수신 측에 받은 패킷 응답만큼 전송 측 윈도우 사이즈도 원래 대로 간다.

보내면 움츠러 들고, 받았다는 응답을 받으면 다시 펴지는 듯한...



- 슬라이딩 윈도우의 오류제어 종류

 + Go-Back-N : 중간에 누락된 패킷이 있으면, 그 패킷부터 재전송된다.

GBN

 - 서버 측은 패킷을 순차적으로 받고 응답한다. 잘 받았다는 응답이 Lost 손실될 수 있으나 괜찮다. 그다음 패킷을 받으면 생략된 패킷은 받은 걸로 인지 한다. (위 그림의 Frame 0번에 대한 ack이 없는 것)


 - 서버 측에서 패킷 순서에 맞지 않는 경우 더 이상 요청을 받지 않고 무시한다. discarded~

  + 서버 측에선 순서에 맞는 패킷을 요청함(ACK), 

  + 클라측에서 보낸 패킷에 대한 ACK 또는 NAK 요청이 없으면 time-out에 걸려 재전송된다. (time out 걸리기 전 서버 측에서 3차례(대략) 재전송을 요청하면, 즉 누락된 패킷(100)이 Time out 걸리기 전 클라가 패킷을 100 이상의 패킷을 계속 보낼 때마다, NAK (100을 달라고) 날리면서 time out을 무시하고(3차례 이상 요청 시) 100을 재전송한다. (Fast Retransmit)


! 한 줄 요약

- 단순 심플하지만, 패킷 유실에 대한 복구 비용(재전송)이 높다. (이를 극복한 게 2번 Selective Repeat)





 + Selective Repeat : 서버 측에서 손실된 패킷만 송신자에게 다시 요청하는 기법.

SR (Selective Repeat)


- GBN과 달리 순차적이지 않아도, 버퍼에 저장해놓는다. 

- 버퍼는 순차적으로 처리한다. 중간에 빠진 패킷에 대해 버퍼 초기화가 되지 않고 윈도우(버퍼)크기 만큼 저장한다.

- 실패한 요청은 타이머 작동 또는 버퍼 풀일 경우... 인지하고 재요청 ack를 보낸다.


! 한 줄 요약

- 실패한 패킷만 재전송(성능 비용 좋다)
- 시스템 추상화 복잡. (구조가 복잡)











1-5 혼잡제어

송신 측의 데이터 전달과 네트워크의 데이터 처리 속도 차이를 해결하기 위한 기법이다.

송신 측의 데이터는 지역망이나 인터넷으로 연결된 대형 네트워크를 통해 전달된다. 

네트워크 상의 라우터가 항상 한가로운 상황은 아니다. 

한 라우터에 데이터가 몰릴 경우, 다시 말해 혼잡할 경우 라우터는 자신에게 온 데이터를 모두 처리할 수가 없다. 

호스트들은 재전송을 하게 되고 결국 더욱더 혼잡만 가중시켜 오버플로우나 데이터 손실을 발생시킨다.       

네트워크의 혼잡을 피하기 위해 송신 측에서 보내는 데이터의 전송 속도를 강제로 줄이게 되는데 이러한 작업을 혼잡 제어라고 한다. 


- 중간 정리!!
  + 흐름 제어 : 종단 <=> 종단 간의 처리 
  + 혼잡 제어 : 종단 <=> 네트워크 <=> 종단 간의 처리
혼잡 인지 제어 플로우
Time out 패킷 유실 사례

 + Ack Time out 이 너무 길거나(재전송 지연시간이 길어짐) 짧아도(False Alarm 가능성, 잘 전송해도 못 가는 경우) 문제, 적절한 값은 어떻게 측정하는가?


Retransmission : 재전송

Retransmission Time Out (RTO) : Timer의 시간 

round-trip time (RTT) : 세그먼트를 보내고 응답받은 시간

RTT와 RTO


 - RTO = RTT의 평균값 + RTT표준편차 X 4 = 1초보다 짧으면 1초가 된다. (MAX 1초보다 낮아지진 않는다.)

RTO 노말분포

RTT 평균은 어떻게 구하는가?

Weighted Moving Average (현재 값에 가까운 과거 값에 가중치를 더 높여서 계산) 코딩 시 평균 구하기 알고리즘에 많이 쓰인다고 한다.


RTT의 표준 편차는 어떻게 구하는가?

- 수학을 잘 못해서... 궁금하신 분은 아래 링크로...

http://ktword.co.kr/abbr_view.php?m_temp1=1687&id=1102


혼잡 제어 방식 : AIMD(Additive Increase / Multicative Decrease)

혼잡하면 전송률을 절반으로 줄임(Multicative Decrease) 비 혼잡 상황에선 1개씩 증가 (Additive Increase)


Multicative Decrease는 2종류로 나뉜다.

 + 패킷 유실이 네트워크 이상을 반영할 때는 CW(congestion window)를 1MSS 로 줄인 후 Slow Start!

 + Time out 내에 도착했지만 Fast Retransmit 발생 시 Multicative Decrease 발동.


Increase 개수의 단위는? : MSS (maximum segment size)

MSS 는 TCP 커넥션을 맺은 후 송수신의 협의에 의해 작은 값으로 설정. (중간에 네턱 장비의 패킷 사이즈를 뭘로 하느냐에 대부분 결정. 1kb~4kb 사이)

혼잡 제어 플로우
Slow Start와 일반 Additive Increase 












1-6 Tcpdump로 보는 TCP처리 과정

발췌 (http://elearning.kocw.net/document/lec/2012/AnDong/ChungJoongSoo/4.pdf)


- 아래 클라에서 서버(80 웹서버) 연결-데이터 교환-종료 과정을 설명한다.

TCP : Clint <-> Web 연결 과정 

(1) 연결 ( 클라 => Syn )

(2) 연결 ( 서버 => Syn,Ack )

(3) 연결 ( 클라 => Ack )

(4) 데이터 전송 ( 클라 => Psh,Ack )

(5) 데이터 전송  ( 서버 => Ack )


(6) 데이터 전송 ( 서버 => Psh,Ack )


(7) 데이터 전송 완료 후 종료 요청( 서버 => Fin,Ack ) 


(8) 종료 ( 클라 => Ack )


(9) 종료 ( 클라 => Fin,Ack )



(10) 종료 ( 서버 => Ack )


발췌 : 아래 pdf 참조했습니다.

http://elearning.kocw.net/document/lec/2012/AnDong/ChungJoongSoo/4.pdf





1-7 TCP Syn flooding


Syn Flooding 공격


위 왼쪽 그림은 정상적인 TCP를 맺는 Flow이다. 하지만 오른쪽처럼 서버의 Syn+Ack에 대해 클라쪽에서 Ack을 주지 않으면, 75초 동안 대기 후 해당 커넥션을 초기화를 한다.

문제는 위와 같이 수많은 요청이 발생할 경우, 커넥션 마다 75초를 기다리고, 메모리의 백로그큐(backlog queue)에 쌓이면서, 서버 쪽에 설정된 backlog 값을 넘어 서면 더 이상 Syn 요청을 받지 못하게 됩니다.


해결방안?


서버 syncookie 기능 활성화

 + syncookie : 클라의 Syn 요청에 대해 서버에서 응답할 때 Syn 패킷을 backlog에 저장하지 않고, Tcp Packet hearder의 option항목 (mss, timestamp 등)에 Initial Sequence Number(ISN)을 만들어 클라에게 보냅니다.

클라에서 ack이 왔을 때 다시 ISN 정보를 확인하여 클라의 정보를 확인하여 커넥션을 맺는다.


syncookies 기능은 syn flooding으로 backlog가 찼을 때만 활성화됩니다.


기본으로 해당 옵션을 켜는 게 좋습니다. 

굳이 syncookies의 단점이라고 하면... 클라=>서버로 ack 패킷이 유실될 경우, 클라 쪽 해당 커넥션이

freezing 상태가 됩니다. 또한 정상적인 3 way handshake 면 tcp packet hearder의 option 항목을 참조하여 서버 측 최적의 windows scailing 값 등을 사용하여 성능을 높지만, syncookie 가 활성화되면 해당 정보를 참조할 수 없어 성능면에선 좋진 않다고 하지만 큰 성능차는 나지 않을 것이다.


커널 옵션 및 어플단 backlog 항목 설정

 + 커널 옵션

   ㄱ. net.ipv4.tcp_syncookies = 1 (활성화 시 /var/log/messages에 SYN flooding 로그 남으며

 netstat로 SYN_RECV 개수가 비정상적으로 많은지 확인)

   ㄴ. net.ipv4.tcp_max_syn_backlog = 지정

   ㄷ. net.core.somaxconn = 지정


 + 어플 옵션

   ++ nginx : conf 파일에 backlog 옵션 지정

   ++ apache : server.xml  옵션 지정


중간에 LB를 두어 구성

 - LB도 서버와 마찬가지로 syncookie 옵션을 켜면 앞단에서 손쉽게 막을 수 있다. 물론 LB에 해당 옵션을 켜야 한다.

 - 서버와 마찬가지로 클라쪽 ack을 받지 않는 이상  LB 세션 테이블을 만들지 않는다.


정상적인 요청의 경우




SYN Flooding 공격 시



이 외에도 방화벽이나 보안장비들을 앞단에 놓는 것도 방법이긴 하지만, 고가의 비용이 들어간다.


TCP syncookies에 대해 좀 더 깊은 내용은 아래 블로그를 참조.

https://brunch.co.kr/@alden/5






1-8 TCP 트러블슈팅


서버를 운영하다 보면 TCP 기반의 서비스에서 reset 이 왜 났지? 왜 계속 Retransmission을 요청하지?

하면서 트러블슈팅을 진행하는 경우가 많다. TCP 문제는 tcpdump을 추출한 후 확인하는 게 최선의 방법이다. 다음에 와이어 샤크를 통한 TCP분석을 통해 다양한 사례를 써볼 예정이며, 아래는 간단한 사례와 확인 방법을 썼다.


Reset

reset 은 커넥션 관리 (time wait 최적화, 효율적인 커넥션 조정)을 위한 TCP메커니즘입니다.

하지만 클라 <->서버 간의 커넥션을 열고 통신이 잘되고 있는데, 어디선가 reset 들 날려 해당 커넥션의 요청이 실패하여 장애로 이어지는 경우가 문제가 되죠.

reset은 주로 서버 쪽 응용이나 클라의 어플에서 발생합니다. 개발 쪽 TCP관련 코딩의 경우가 대부분이나, 네트워크 문제 또는 순간 등이 원인이 되는 경우도 있습니다.


1. 네트워크 순간

 - 라우터나 스위치에선 reset을 보내는 기능이 없습니다. (하지만 LB에선 reset을 보냅니다.)

 - 수단으로 인해 응용 쪽 처리가 밀리거나 OOM 발생 시 응용 재시작이 되면서 reset를 보내는 경우.

 - LB에서 reset을 보내는 경우 (아래 그림 참조)

dsr 구조의 LB의 사례

+ 클라쪽에서 30초간 요청이 아무것도 없다면, LB 세션 테이블은 종료되고 LB => 클라&서버에 reset을 날린다. (만약 30초 이상 수단이 되면 서버는 LB로부터 reset 받아 커넥션이 종료되지만, 클라쪽은 좀비 세션으로 남을 수도 있다.)


 + 서버 쪽 keepalive 설정이 180초라 하고, 이를 기반으로 개발을 했는데, LB의 idle timeout 설정이 짧다면 LB로부터 reset을 받는다.


 + 반대로 서버 쪽 keepalive 설정값이 LB보다 짧다면 절대로 LB에서 먼저 idle 세션 테이블을 끊지 않는다.

 

+ 위와 같은 구조에서 LB 쪽 세션 테이블에 문제가 생기면, 서버=>클라쪽 통신은 되지만 반대로 클라=> 서버 쪽 통신이 되질 않는다. 이럴 경우 클라쪽에선 해당 커넥션을 대기하면서 서비스에 지장을 주고, 서버 입장에선 클라쪽 요청이 없으니 keepalive가 적용되어 나중엔 서버 쪽에서 클라로 reset를 날린다. LB세션 테이블에 문제가 생기면 LB에서 클라와 서버에 reset을 보낼 때도 있고 아닌 경우도 있다. (LB벤더사마다 설정이 틀리고, VIP내에 서버가 전부 죽었는지? 한대라도 살아 있는지 여부에 따라 reset을 보낼지 말지 판단한다.)



LB dsr 구조의 설명과 장애 사례들은 아래 블로그에 정리가 잘 되어 있다.

http://tech.kakao.com/2014/05/30/l4/


2. 서버 레벨의 문제

 - RX TX Ring buffer 크기가 작아서 발생

 + buffer full이 발생하게 되면 패킷이 pause 상태가 될 수 있다.

 + ethtool을 통해 버퍼 사이즈를 최대로 늘린다. (구형 NIC 또는 구버전 펌웨어&드라이버인 경우 해당 설정값의 MAX치에 한계가 있을 수 있다.)


 - CPU receiving NIC interrupts 

 + NIC의 동작을 1개의 CPU스레드만 사용하는 경우이다. 이럴 경우 CPU 부하로 문제가 될 수 있다. 이런 원인은 NIC driver 나 옵션 설정, 또는 OS레벨에서의 irqbalance을 이용한다.


 - NIC driver & Firmware 

 + 이 부분은 확인하기가 어렵다. NIC 벤더사의 드라이버, 펌웨어 릴리즈 노트를 보며 이슈를 확인해보며, 우선 최신으로 업데이트 후에 증상을 관찰한다.


 + 서버에서 ifconfig 출력 후 RX, TX에 dropped count 가 늘어났을 경우. 아래 3가지 사항을 확인한다.

   ++ NIC 카드 실제 장애

   ++ Lan 케이블 불량

   ++ 스위치 포트 불량


커넥션 풀


커넥션 풀 이란? 설정 한 만큼 TCP 커넥션을 맺어두고, 그 커넥션을 이용한다. 즉 매번 TCP을 연결하게 되면 그만큼 일도 많아지고 잡비용이 증가되므로, 일정한 커넥션 풀을 만들어 두고 그걸 사용한다.


아래 예제는 java 기반의 APP서버 <-> DB 연동 시 예제이다. min/max 설정을 5로 했다고 가정하고,

APP 구동 시 min 설정에 따라 기본적으로 5개의 커넥션 풀을 생성한다. 

max 값이 5를 넘으면... 기존 커넥션 중 하나가 종료될 때까지 대기 후 처리한다.

커넥션 풀 예제 (max 10 => max 5로 수정)

커넥션 풀을 어떤 걸 쓸지 결정하는 옵션 중 GenericObjectPool 등 옵션으로 LIFO, FIFO로 지정할 수 있다.

연결 단 중간에 LB나 GSLB 등의 분산장치를 뒀는데, 유난히 한쪽 서버에 트래픽이 몰리는 현상이 있을 수 있다.

이는 LIFO로 설정되어 최근에 반납된 idle connection pool을 계속 재사용하여 RR 하게 분배가 안되고 한쪽으로 몰릴 수 있다. (예를 들면 하나의 스레드에서 루프를 돌면... 같은 커넥션으로 계속 붙게 될 수 있음.)

커넥션 풀 큐 예제
















1-9 : Linux TCP packet 흐름  


 - 해당 챕터는 Linux TCP 송-수신이 내부적으로 어떻게 흐르는지에 대해 알아 보고, 그전에 필요한 용어 정리 부터 하겠습니다.


+ NIC buffer : NIC Card 에 따로 존재하는 memory 입니다. 송수신 패킷의 버퍼 역활을 하며, sk_buff 구조체의 ring buffer 매핑이 됩니다.


 + ring buffer : NIC buffer 와의 패킷 교환을 위해 sk_buff 구조체로 생성 되는 버퍼로 memory의 커널영역에 매핑 된다. 송신용(rx) 수신용(tx) ring buffer 가 존재하며 보통 receive(송신) 버퍼가 사용하고 남은 자리를 send(수신) 버퍼가 사용하며 수신용 버퍼가 즉 ethtool -g eth0 으로 나오는 항목 중 RX (송신) 버퍼가 TX보다 큰 경우가 많다. linux ethtool 커멘드로 rx tx ring size 조절이 가능 하다.

ring buffer 가 full 상태에서 송수신 패킷이 들어오면 drop 된다. 자세한건 밑에 다시 설명...

NIC ring buffer 확인 및 MAX 튜닝

ethtool -g 로 나오는 RX, TX 수치는 desctriptor 의 크기 또는 desctriptor 저장한 링버퍼의 섹터 크기? 로 이해 했다. (해당 단위가 정확한 해답은 찾지 못했고 아시는 분은 댓글로 좀 알려주세요.)

이는 카드 제조사 모델 마다 다르며 고가 모델 일수록 buffer 크기가 크다.

자세한 사항은 아래 블로그를 참조하면 좋습니다

-  http://beyond-syntax.com/blog/2011/03/diving-into-linux-networking-i/

-  https://slideplayer.com/slide/1592465/



 + socket buffer : ring buffer 는 NIC와의 패킷 교환을 위한 버퍼이고, socket buffer 는 커널레벨 (user 영역  <=> kernel 영역) 간의 패킷 처리를 위한 버퍼 이다. 송신용(rx) 수신용(tx) socket buffer 가 있다.


 + DMA : direct memory access 해석 그대로 CPU를 거치지 않고 다이렉트로 메모리에 접근 하는 방식이다. nic 나 기타 Disk 등의 파트에선 CPU 부하 및 간섭을 줄이기 위해 DMA 메모리 접근 방식을 사용 한다. 


 + PIO : programmed Input/Output 로 DMA와 반대로 장치들 간의 통신은 모두 CPU을 거쳐 간다. 


DMA , PIO


 (1) receive (수신) 과정


receive packet 흐름


(0) 커널은 NIC를 통해 데이터를 수신 할 준비를 하고 SKB (소켓버퍼) 데이터 구조를 준비 하고 이를 DMA를 통해 rx ring buffer 과 NIC를 매핑 작업을 합니다.


(1) 패킷이 수신 되면 NIC의 control 칩셋이 정합성 검증 (MAC 비교 등) 후 NIC 버퍼로 패킷을 보냅니다.

(전송 받는 (receive) UDP + TCP 패킷 대해 한번에 Merge 을 하여 처리 하고 이를 CPU가 아닌 NIC Card 가 처리 한다.)


(2) DMA를 통해 NIC 버퍼의 패킷을 커널메모리 (rx ring buffer)로 옮깁니다. 이때 rx ring buffer 가 full 상태이면 해당 패킷은 drop 됩니다.일단 rx ring buffer 로 올라오면 user 영역 (응용) 으로 이동 하기 까지 mem copy는 발생 하지 않는다.


(3~6) NIC가 패킷이 수신 됐다는 인터럽트를 발생 시키고, 상위 Layer 패킷 전달을 위한 IRQ 과정을 거친 후

드라이버는 ring buffer 에서 패킷 정보를 가져와서 받은 패킷을 처리 (상위 레이어 전달 IP => TCP => Application Layer)  한다.

네트워크 레벨 (L2) 에선 이러한 패킷을 찾고 적절하게 처리하여 결국 TCP 계측 까지 차례대로 처리 합니다.



                    <아래 이미지는 NIC buffer <=> Memory 간의 어떤식으로 구조화 하며 통신간 구조> 

Receive 과정
Send 과정
Network Card 아키텍처



(2) send (송신) 과정

 - receive 과정과 반대로 동작 한다고 보면 된다. 이중 틀린점은 송신(tx)시에는 txqueue 를 거치고, 구형 NIC이 아닌 이상 NAPI를 사용하기 때문에 (NAPI을 지원하지 않으면 backlog queue을 버퍼로 사용) receive 과정과 달리 드라이버<=>상위 레이어 레벨에 버퍼가 없다.

송신 과정 중 txqueue 구조 





















1-10 : Linux TCP 성능 튜닝1 - offload 




- 사전에 알아야 할 용어

 + MTU : Maximum Transmission Unit 말 그대로 전송 가능한 패킷의 최대 크기 입니다. 아래와 같이 확인 가능 합니다. MTU값은 유저가 변경 가능 합니다.


+ MSS : Maximum Segment Size 는 실제 전송 가능한 데이터(payload) 크기 입니다.  MTU 1500byte [ethernet 14Byte + FCS 4Byte 제외] - [IP+TCP = 40byte] 인 총 1460byte 가 MSS 크기 입니다.



(1) tcp-segmentation-offload (TSO) : TCP 전송시 (내부에선 패킷(mtu) 크기를 크게 해서 효율을 올리고) 실제로 밖으로 나가는 패킷은 분할(segmentation) 하는데, 이 작업을 CPU가 아닌 NIC Card 가 처리 합니다.

해당 기능으로 이득을 볼수 있지만, 반대로 (타사예제) 10G NIC 서비스 환경에서 TCP 패킷 유실&지연, TCP close, Load Average 증가 등의 현상이 나타나서 적용을 안하는 사례도 있다.

부하 테스트를 하면서 확인 해봐야 되지만, 실제로 CPU 사용율이 크게 낮아지는거 같지도 않다.


(2) udp-fragmentation-offload (UFO) : TSO에서 TCP가 아닌 UDP 일 경우.


(3) generic-segmentation-offload (GSO) : TSO+ UFO


(4) generic-receive-offload (GRO) : 전송 받는 (receive) UDP + TCP 패킷 대해 한번에 Merge 을 하여 처리 하고 이를 CPU가 아닌 NIC Card 가 처리 한다. 


(5) large-receive-offload (LRO) : 전송 받는 (receive) TCP 패킷 대해 한번에 Merge 을 하여 처리 하고 이를 CPU가 아닌 NIC Card 가 처리 한다.  


==> 해당 설정은 충분히 고민 후 적용 해야 될듯 하다. 보통 해당 설정으로 인해 성능 저하나 문제가 되는 경우도 많다. 


참조 블로그

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/performance_tuning_guide/network-nic-offloads





계속 추가 예정~



브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari