Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

Wireshark + C#으로 확인하는 TCP 통신의 Receive Window

검색해 보면, 이에 관해 자세한 설명이 나온 글을 쉽게 찾을 수 있습니다.

TCP series #4: TCP receive window and everything you need to know about it
; https://accedian.com/blog/tcp-receive-window-everything-need-know/

그래도, 이쪽 분야가 워낙 빠른지라 무턱대고 저 내용을 그대로 믿을 수는 없으니 정말 그런지 한 번 테스트를 해보겠습니다. ^^

이를 위해 지난 글의 예제 코드를 그대로 재사용해 서버는 Azure VM에 올려 두고, 클라이언트도 제가 가진 물리 서버의 VM에 올려 둔 후, 연결해 패킷 캡처를 해봤습니다.

2016 48.517893 ..client_ip... ..server_ip.. TCP	66 2865 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
2018 48.522650 ..server_ip.. ..client_ip... TCP	66 15000 → 2865 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1440 WS=256 SACK_PERM=1
2019 48.522716 ..client_ip... ..server_ip.. TCP	54 2865 → 15000 [ACK] Seq=1 Ack=1 Win=263424 Len=0

예전에는 Window 크기를 TCP Header에 2바이트로 보관해 총 65535 바이트까지 지정할 수 있었다고 합니다. 하지만 네트워크의 발전으로 64K는 너무 작아 TCP Header에 추가 Option 필드로 "Window scale" 값을 포함해 이를 곱하는 방식으로 보완했습니다. 현재는 Scale 값으로 1바이트를 점유하는데, 재미있는 것은 이 값이 2의 제곱을 나타내기 때문에 Wireshark에서 출력한 WS=256에 대해 실제 패킷에는 log2(256) = 8이 담겨 있습니다. 그러니까 이론 상 Win = 65535, WS=14(Scale 허용 최댓값 14: 2^14 = 16384)라면 최대 1,073,725,440(1GB)까지 Window Size를 지정할 수 있습니다.

이를 감안해 위의 2016번과, 2018번 패킷에 있는 Window 크기는 다음과 같이 "정상적이지 않게" 계산할 수 있습니다.

[클라이언트 측의 Receive Window 크기]
Win=64240
WS=256, (Scale = 8, 2^8 == 256)
윈도우 크기 = 64240 * 256 = 16,445,440 (약 16MB)

[서버 측의 Receive Window 크기]
Win=8192
WS=256, (Scale = 8, 2^8 == 256)
윈도우 크기 = 8192 * 256 = 2,097,152 (2MB)

왜 저것이 "정상적이지 않은" 크기인지 다음의 문서에서 이를 설명합니다.

Windows scaling
; https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features#windows-scaling

It's important to note that the window size used in the actual three-way handshake is NOT the window size that is scaled. This is per RFC 1323 section 2.2, "The Window field in a SYN (for example, a [SYN] or [SYN,ACK]) segment itself is never scaled."
This means that the first data packet sent after the three-way handshake is the actual window size. If there is a scaling factor, the initial window size of 65,535 bytes is always used. The window size is then multiplied by the scaling factor identified in the three-way handshake.


따라서 (3-way handshake의) SYN 패킷에 있는 Window Size는 (양측 모두 scale 값을 설정한 경우) 버리는 값이고, 오직 Scale 값만이 유효해 이후 통신에서 해당 Scale 값을 재사용한다는 점입니다. 예를 들어, 저렇게 연결을 맺은 통신에서 5,000 바이트를 클라이언트에서 서버로 보내면 이런 패킷이 나옵니다.

252 5.875126 ..client_ip... ..server_ip.. TCP 5054 1093 → 15000 [PSH, ACK] Seq=1 Ack=1 Win=263424 Len=5000
253 5.880317 ..server_ip.. ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=4321 Win=262656 Len=0
254 5.880317 ..server_ip.. ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=5001 Win=261888 Len=0

위에서 wireshark가 보여주는 Win 값은 편의상 계산된 값이며 실제로는 해당 패킷에 window size만 각각 다음과 같이 2바이트로 표현해 전송합니다.

252번 패킷 Window size value = 0x0405(1029)
253번 패킷 Window size value = 0x0402(1026)
254번 패킷 Window size value = 0x03ff(1023)

그리고, 각각 연결 시에 알렸던 scale 값을 곱하면 wireshark가 보여준 Win=... 값을 확인할 수 있습니다.

252번 패킷 1029 * 256 = 263424
253번 패킷 1026 * 256 = 262656
254번 패킷 1023 * 256 = 261888

이런 이유로 네트워크 성능 도구들은 처음 연결 단계의 패킷을 잡지 못한 경우 이후 TCP 패킷들의 Window 크기를 정상적으로 보여줄 수 없습니다. 일례로, wireshark도 TCP 연결이 맺어진 다음 패킷 모니터링을 시작하면 다음과 같이 Win 크기를 scale 계산이 안 된 값으로 보여줘 매우 작게 나옵니다.

1 0.000000 ..client_ip... ..server_ip... TCP 1054 34069 → 15000 [PSH, ACK] Seq=1 Ack=1 Win=1026 Len=1000
2 0.059503 ..server_ip... ..client_ip... TCP 60 15000 → 34069 [ACK] Seq=1 Ack=1001 Win=509 Len=0

그리고 이때의 TCP 상세 설명을 보면 "[Window size scaling factor: -1 (unknown)]"라고 출력합니다.

참고로, 어느 한쪽이라도 (3-way handshake 단계에서) scale을 사용한다고 명시하지 않으면 양 측 모두 이 기능을 사용하지 않습니다. (아마도 이를 대비해 Window Size에도 기본적으로는 값을 설정해서 보내는 듯합니다.)




이쯤에서 다시 3-way handshake 패킷을 볼까요?

2016 48.517893 ..client_ip... ..server_ip.. TCP 66 2865 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
2018 48.522650 ..server_ip... .client_ip... TCP 66 15000 → 2865 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1440 WS=256 SACK_PERM=1
2019 48.522716 ..client_ip... ..server_ip.. TCP 54 2865 → 15000 [ACK] Seq=1 Ack=1 Win=263424 Len=0

마지막에 (기준 scale 값을 제공하는) SYN 패킷이 아닌 ACK 하나가 클라이언트에서 서버로 전달되는 과정이 있는데요, 바로 저기에 포함된 263,424는 정확히 클라이언트의 TCP Receive Window 크기를 의미합니다. 이렇게 따지고 보면, TCP 통신에서 3-way handshake 단계 중 상대방의 TCP Receive Window 크기를 알 수 있는 것은 서버 측만 가능합니다. 달리 말하면, 클라이언트의 경우에는 직접 통신하면서 받게되는 이후의 패킷에서만 서버의 Receive Window 크기를 알 수 있다는 건데요, 실제로 위에서 5,000 바이트를 전송했을 때 서버로부터의 ACK 패킷을 보면,

252 5.875126 ..client_ip... ..server_ip..  TCP 5054    1093 → 15000 [PSH, ACK] Seq=1 Ack=1 Win=263424 Len=5000
253 5.880317 ..server_ip..  ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=4321 Win=262656 Len=0
254 5.880317 ..server_ip..  ..client_ip... TCP 60  15000 → 1093 [ACK] Seq=1 Ack=5001 Win=261888 Len=0

그제서야 클라이언트는 서버의 Receive Window 크기를 인지하게 됩니다. 그렇다면, 클라이언트가 먼저 서버로 데이터를 보내는 경우 어떻게 서버의 TCP Receive Window 크기를 알고 흐름 제어를 할 수 있을까요? 이에 대한 답도 문서에 잘 보면 나옵니다. ^^

This means that the first data packet sent after the three-way handshake is the actual window size. If there is a scaling factor, the initial window size of 65,535 bytes is always used.


즉, TCP 협상에서 scale 사용을 지정했으면 기본 윈도우 크기를 65,535로 사용한다는 것입니다. 따라서 (서버로부터 scale을 사용하겠다고 했으므로) 클라이언트는 서버의 최초 TCP Receive Window의 크기를 65,535로 가정하고 흐름 제어를 하다가 서버로부터 수신한 패킷에 Window Size 필드가 있으면 그것에 scale 값을 곱해 새로운 윈도우 크기로 인식하게 되는 것입니다.




기왕 하는 김에, Receive Window 크기를 소진해 볼까요? 그런데 아쉽게도 아주 매끄러운 재현 방법이 없습니다.

위에서 설명했던 내용대로 서버의 Receive Window 크기는 이후에 (위에서는 262,656으로) 변경이 되겠지만 초기에는 클라이언트에서 65,535로 가정할 것입니다. 그리고 TCP Receive Window란, 상대의 ACK 없이 보낼 수 있는 (이상적인 상황에서의) 최대 데이터 크기이기 때문에 TCP 연결 후 서버 측의 VM을 paused 상태로 만들고 send를 하면 이론상 유사하게 재현이 될 걸로 보입니다. 즉, 클라이언트가 바로 데이터를 전송하는 경우 65,535 바이트까지는 전송이 되어야 하는 것입니다.

하지만, 테스트로 (연결 후 처음) 20,000 바이트를 보냈을 때 분할된 첫 번째 패킷에 대한 ACK 대기로 다음과 같이 Retransmission 절차로 빠집니다.

1 0.000000   ..client_ip... ..server_ip... TCP 66 33692 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
2 0.005193   ..server_ip... ..client_ip... TCP 66 15000 → 33692 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=256 SACK_PERM=1
3 0.005281   ..client_ip... ..server_ip... TCP 54 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=0
4 15.104217  ..client_ip... ..server_ip... TCP 14654 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=14600
5 15.411778  ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
6 16.022453  ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
7 17.223054  ..client_ip... ..server_ip... TCP 590 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=536
8 18.427186  ..client_ip... ..server_ip... TCP 590 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=536
9 19.636720  ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
10 22.047373 ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
11 26.853856 ..client_ip... ..server_ip... TCP 1514 [TCP Retransmission] 33692 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=1460
12 36.466235 ..client_ip... ..server_ip... TCP 54 33692 → 15000 [RST, ACK] Seq=1461 Ack=1 Win=0 Len=0

오호~~~ 당황스럽군요. ^^ 동일 환경의 연결 상태에서 20,000 바이트를 보내면 첫 번째 패킷 보낸 후 마지막 패킷의 ACK를 받는 시간까지 다 합쳐도 10ms가 안 걸렸습니다. 즉, 적어도 Receive Window라고 65,535 크기를 가정했다면 20,000 바이트 정도는 모두 보냈어야 하고 ACK를 받지 못한 300ms가 지난 시점부터 Retransmission 절차가 나와야 하는데 그렇지 않은 것입니다.

혹시, Send Window의 초기 크기가 14600 정도로 잡혀 있던 걸까요? ^^ 이를 확인하기 위해서는 InitialCongestionWindow의 값을 봐야 합니다.

C:\Windows\System32> netsh interface tcp show supplemental

The TCP global default template is internet

TCP Supplemental Parameters
----------------------------------------------
Minimum RTO (msec)                  : 300
Initial Congestion Window (MSS)     : 10
Congestion Control Provider         : cubic
Enable Congestion Window Restart    : disabled
Delayed ACK timeout (msec)          : 40
Delayed ACK frequency               : 2
Enable RACK                         : enabled
Enable Tail Loss Probe              : enabled

Please use the 'netsh int tcp show supplementalports' and
'netsh int tcp show supplementalsubnets' commands to view active filters.

그러고 보니 3-way handshake 단계에서 협의된 MSS 크기는 1460입니다. 거기다 InitialCongestionWindow의 값이 10이니까, 14600만큼 전송하고 ACK를 대기하게 된 것입니다. 정확히 설명이 되었군요. ^^

참고로 예전에는 InitialCongestionWindow 값이 보통 1이었다고 합니다. 그래서 간혹 블로그 글들을 보면 첫 번째 패킷은 무조건 송신 후 ACK를 기다린다는 식의 설명이 나오는데 바로 icwnd 값이 1로 설정된 당시의 환경에서 기인한 것입니다. 이후 2로 증가한 후 근래에는 10(RFC6928)을 기본으로 쓰는 운영체제들이 늘었다고.




다소 과격하지만 ^^ 넉넉잡아 500,000 바이트 정도 send 하고 수신 측은 recv만 호출하지 않는다면 Receive Window를 소진하는 것을 쉽게 재현할 수 있습니다.

// 연결 단계의 패킷 3개
153 2.888173 ..client_ip... ..server_ip... TCP 66 11337 → 15000 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
154 2.893354 ..server_ip... ..client_ip... TCP 66 15000 → 11337 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=256 SACK_PERM=1
155 2.893446 ..client_ip... ..server_ip... TCP 54 11337 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=0

// 송신이 진행되면서 수신 측은 내부 버퍼인 SO_RCVBUF의 크기 정도는 처리되면서 Receive Window의 크기가 그다지 변화가 없다가,
290 7.682747 ..client_ip... ..server_ip... TCP 14654 11337 → 15000 [ACK] Seq=1 Ack=1 Win=262656 Len=14600
291 7.689219 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=2921 Win=131328 Len=0
...[생략]...                                   
316 7.702878 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=55481 Win=131328 Len=0
317 7.702897 ..client_ip... ..server_ip... TCP 11734 11337 → 15000 [ACK] Seq=113881 Ack=1 Win=262656 Len=11680
318 7.702932 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=58401 Win=131328 Len=0
319 7.702932 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=61321 Win=131328 Len=0
320 7.702932 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=64241 Win=131328 Len=0

// SO_RCVBUF가 마침내 꽉 차게 되면, 이후 Receive Window가 점차로 줄어들기 시작
...[생략]...
362 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=175201 Win=21760 Len=0
363 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=178121 Win=18944 Len=0
364 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=181041 Win=15872 Len=0
365 7.714849 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=183961 Win=13056 Len=0
366 7.714910 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=186881 Win=9984 Len=0
367 7.714910 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=189801 Win=7168 Len=0
368 7.716539 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=192721 Win=4352 Len=0
369 7.716539 ..server_ip... ..client_ip... TCP 60 15000 → 11337 [ACK] Seq=1 Ack=195641 Win=1280 Len=0

// 송신 측이 수신자의 Receive Window의 크기를 마지막으로 1280 바이트를 채워 보내는 것으로,
500 12.718148 ..client_ip... ..server_ip... TCP 1334 [TCP Window Full] 11337 → 15000 [ACK] Seq=195641 Ack=1 Win=262656 Len=1280

// Receive Window 소진 
501 12.778449 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] 15000 → 11337 [ACK] Seq=1 Ack=196921 Win=0 Len=0

// 이후, 수신 측의 Receive Window 크기 조회를 일정 시간 동안 반복
507 13.079354 ..client_ip... ..server_ip... TCP 55 [TCP ZeroWindowProbe] 11337 → 15000 [ACK] Seq=196921 Ack=1 Win=262656 Len=1
510 13.137834 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196922 Win=0 Len=0
522 13.749622 ..client_ip... ..server_ip... TCP 55 [TCP Previous segment not captured] 11337 → 15000 [ACK] Seq=196922 Ack=1 Win=262656 Len=1
526 13.809922 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196923 Win=0 Len=0
553 15.015558 ..client_ip... ..server_ip... TCP 55 [TCP ZeroWindowProbe] 11337 → 15000 [ACK] Seq=196923 Ack=1 Win=262656 Len=1
554 15.076075 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196924 Win=0 Len=0
606 17.481915 ..client_ip... ..server_ip... TCP 55 [TCP Previous segment not captured] 11337 → 15000 [ACK] Seq=196924 Ack=1 Win=262656 Len=1
609 17.529619 ..server_ip... ..client_ip... TCP 60 [TCP ZeroWindow] [TCP ACKed unseen segment] 15000 → 11337 [ACK] Seq=1 Ack=196925 Win=0 Len=0

보다시피 수신 측은 "Ack=196921 Win=0"을 마지막 ACK로 응답하면서 Receive Window가 모두 소진되었습니다. 이후, 송신 측은 주기적으로 (wireshark에서 "TCP ZeroWindowProbe"라고 표시된) ACK 패킷을 수신 측으로 날리며 receive window의 변화가 있는지 묻습니다. 그리고 이에 대해 수신 측은 (wireshark에서 "ZeroWindow"라고 표시된) ACK 패킷으로 여전히 공간이 없음을 확인해 줍니다. ("ZeroWindow", "ZeroWindowProbe"라는 것은 wirehshark가 서비스로 붙여주는 문구이고 실제 이런 이름의 필드는 없습니다.)

물론, 저 상태에서 수신 측에 recv 호출을 해주면 다시 Receive Window 공간이 생겼음을 송신 측으로 보내고 이후 데이터 전송이 재개됩니다.




참고로, Vista/Windows Server 2008부터는 자동으로 Receive Window에 대한 크기를 조절한다고 합니다.

The Cable Guy TCP Receive Window Auto-Tuning
; https://docs.microsoft.com/en-us/previous-versions/technet-magazine/cc162519(v=msdn.10)

암튼... 너무나 복잡해진 TCP 스택입니다. ^^;




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]


donaricano-btn



[최초 등록일: ]
[최종 수정일: 2/5/2021 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 쓴 사람
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12597정성태4/14/202133개발 환경 구성: 569. csproj의 내용을 공통 설정할 수 있는 Directory.Build.targets 파일
12596정성태4/12/202154개발 환경 구성: 568. Windows의 80 포트 점유를 해제하는 방법
12595정성태4/12/202151.NET Framework: 1036. SQL 서버 - varbinary 타입에 대한 문자열의 CAST, CONVERT 변환을 C# 코드로 구현
12594정성태4/11/202194.NET Framework: 1035. C# - kubectl 명령어 또는 REST API 대신 Kubernets 클라이언트 라이브러리를 통해 프로그래밍으로 접근파일 다운로드1
12593정성태4/10/202140개발 환경 구성: 567. Docker Desktop for Windows - kubectl proxy 없이 k8s 대시보드 접근 방법
12592정성태4/10/202179개발 환경 구성: 566. Docker Desktop for Windows - k8s dashboard의 Kubeconfig 로그인 및 Skip 방법
12591정성태4/9/2021110.NET Framework: 1034. C# - byte 배열을 Hex(16진수) 문자열로 고속 변환하는 방법파일 다운로드1
12590정성태4/9/202159.NET Framework: 1033. C# - .NET 4.0 이하에서 Console.IsInputRedirected 구현
12589정성태4/8/202194.NET Framework: 1032. C# - Environment.OSVersion의 문제점 및 윈도우 운영체제의 버전을 구하는 다양한 방법
12588정성태4/7/202171개발 환경 구성: 565. PowerShell - New-SelfSignedCertificate를 사용해 CA 인증서 생성 및 인증서 서명 방법
12587정성태4/7/2021111개발 환경 구성: 564. Windows 10 - ClickOnce 배포처럼 사용할 수 있는 MSIX 설치 파일
12586정성태4/5/202154오류 유형: 710 . Windows - Restart-Computer / shutdown 명령어 수행 시 Access is denied(E_ACCESSDENIED)
12585정성태4/5/202171개발 환경 구성: 563. 기본 생성된 kubeconfig 파일의 내용을 새롭게 생성한 인증서로 구성하는 방법
12584정성태4/12/202191개발 환경 구성: 562. kubeconfig 파일 없이 kubectl 옵션만으로 실행하는 방법
12583정성태3/29/2021157개발 환경 구성: 561. kubectl 수행 시 다른 k8s 클러스터로 접속하는 방법
12582정성태3/29/202164오류 유형: 709. Visual C++ - 컴파일 에러 error C2059: syntax error: '__stdcall'
12581정성태3/28/2021273.NET Framework: 1031. WinForm/WPF에서 Console 창을 띄워 출력하는 방법 (2) - Output 디버깅 출력을 AllocConsole로 우회 [2]
12580정성태3/28/202157오류 유형: 708. SQL Server Management Studio - Execution Timeout Expired.
12579정성태3/28/202183오류 유형: 707. 중첩 가상화(Nested Virtualization) - The virtual machine could not be started because this platform does not support nested virtualization.
12578정성태3/27/2021164개발 환경 구성: 560. Docker Desktop for Windows 기반의 Kubernetes 구성 (2) - WSL 2 인스턴스에 kind가 구성한 k8s 서비스 위치
12577정성태3/29/2021201개발 환경 구성: 559. Docker Desktop for Windows 기반의 Kubernetes 구성 - WSL 2 인스턴스에 kind 도구로 k8s 클러스터 구성
12576정성태3/25/2021176개발 환경 구성: 558. Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성 (2) - k8s 서비스 위치
12575정성태3/24/2021159개발 환경 구성: 557. Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성
12574정성태3/28/2021229.NET Framework: 1030. C# Socket의 Close/Shutdown 동작 (동기 모드)
12573정성태4/1/2021106개발 환경 구성: 556. WSL 인스턴스 초기 설정 명령어
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...