← 전체 글
Modbus/약 10분 읽기/— 조회

Modbus TCP PLC 하나는 client를 몇 개까지 받을까?

Modbus TCP 장비의 동시 연결 수는 생각보다 적다. Client를 미리 세고, 실제 socket 동작을 확인하고, historian이 HMI를 밀어내지 않게 하는 법.

ModbusSCADA네트워킹문제 해결프로젝트 노트

나를 다시 현장으로 부르는 건 대개 잘못된 register map이 아니다. Port 502의 PLC에 다섯 번째로 붙은 무언가다. Bench test는 잘 됐다 — 노트북 한 대가 장비 한 대를 읽었으니까. 그다음 HMI server, standby HMI, historian, 켜 둔 채 잊어버린 engineering 노트북, VPN 위의 vendor 진단 tool이 전부 같은 주소를 가리키고, 이제 operator는 멈춘 데이터를 보며 network 탓을 한다.

Modbus TCP는 이 문제를 숨긴다. MBAP header에는 transaction identifier가 있어 socket 하나로도 여러 요청을 동시에 띄울 수 있고, 그냥 두면 client는 poll마다 새 socket을 열기도 한다. 그런데 server가 socket을 몇 개까지 참아줄지는 protocol 어디에도 없다. 그건 장비 구현에 달린 값이고, 대개 사람들이 짐작하는 것보다 작다. 저가 전력계량기나 serial-to-Ethernet gateway는 동시 연결을 1~4개로 막는 경우가 흔하고, 큰 숫자를 광고하는 PLC Ethernet module도 뒤에서는 요청을 하나씩 처리하는 경우가 많다.

Tag보다 socket을 먼저 센다

Tag list는 connection 압박을 전혀 알려주지 않는다. Register를 보기 전에 나는 누가 socket을 여는지부터 적는다.

Client왜 연결하나무엇이 물리나
Primary SCADA server화면, alarm, operator write항상 예측 가능하게 연결돼 있어야 함
Standby SCADA serverFailoverActive-active면 request rate가 두 배
Historian interface시계열 수집SCADA가 이미 가진 register를 다시 읽음
Engineering 노트북Online troubleshooting부족한 slot을 잡고는 꽂힌 채 방치됨
MES / report connector생산 데이터인수 몇 달 뒤, 도면에 없던 채로 추가됨
Vendor remote toolService, 진단불안정한 VPN 위에서 공격적으로 reconnect

각 client마다 source IP, software, polling 역할, 그리고 사람들이 빠뜨리는 칸 — write 허용 여부 — 를 적는다. 시운전 때 종이에 이걸 해 두면, 나중에 세 번째 socket을 누가 열었는지 아무도 모르는 말싸움을 하지 않는다.

Datasheet는 "Modbus TCP 지원"이라 쓰고 끝난다

그 한 줄은 용량 계획에 아무 의미가 없다. 시운전 때 나는 이런 것을 실제로 시험한다.

  • 동시에 받아들이는 TCP 연결 수.
  • 한계에 닿았을 때의 동작 — 새 연결을 거절하는가, 아니면 조용히 가장 오래된 연결을 끊는가? "마지막 연결이 이긴다"식 축출이 가장 고약하다. 방금 꽂은 tool이 잘 돌던 HMI를 걷어차기 때문이다.
  • 요청을 병렬로 처리하는가, 내부에서 순차로 처리하는가. Socket 8개를 받으면서도 PDU는 한 번에 하나씩만 처리할 수 있다.
  • 비정상 단절 뒤 half-open socket이 얼마나 남는가. Application idle timeout이 아니라 TCP keepalive에 의존하면, 뽑힌 cable이 slot 하나를 몇 분 동안 붙잡는다.
  • 아무 연결에서 온 write든 소유권 규칙 없이 받아들이는가. 저가 장비 대부분은 누가 보내든 0x06 write를 그냥 받는다.

그리고 gateway에서 다들 잊는 하나 — MBAP header의 Unit Identifier가 bridge가 뒤쪽 serial slave로 라우팅하는 방식이다. Ethernet client 10개가 깔끔하게 붙고도 결국 19200 baud RS-485 한 선에 몰릴 수 있다. TCP는 정상처럼 보이는데 serial 쪽은 timeout이 쌓인다. 여기서 계획하는 건 Ethernet socket이 아니라 two-wire bus 하나의 airtime다.

데이터에 소유자를 하나 준다

Operator가 실제로 보는 데이터라면 나는 authoritative poller 하나를 원한다. SCADA server가 PLC를 읽고, historian·화면·report는 그 데이터를 SCADA platform이나 OPC UA 계층을 통해 받는다 — PLC에 직접 socket을 열지 않는다. 잘 이해된 client 하나와 난장판의 차이다.

여러 system이 직접 polling해도 되지만, 아래가 전부 성립할 때만이다.

  • 장비가 합산 request rate를 bench가 아니라 부하 상태에서 버틴다는 게 증명됨.
  • 같은 holding register 값을 세 번씩 사 오지 않도록 read 범위를 조정함.
  • Write 권한은 정확히 하나의 system — operator command를 소유한 system — 에만 있음.
  • Maintenance tool은 troubleshooting 중일 때만 연결하고 끝나면 끊음. "다시 붙기 귀찮으니 켜 둘게"가 네 번째 slot이 사라지는 경로다.

Historian이 정말 직접 polling해야 한다면 느린 scan group에 둔다 — sub-second가 아니라 몇 초. Background report 수집기가 command feedback·alarm과 같은 socket 예산을 두고 경쟁할 이유는 없다.

Modbus Messaging on TCP/IP Implementation Guide(v1.0b, Modbus.org)는 client가 server에 TCP 연결 하나를 열어 두고 여러 transaction에 재사용하라고 — 요청마다 연결을 새로 열지 말라고 — 명시한다. Poll마다 socket을 열고 닫는 stack은 connection table을 태우고 TIME_WAIT socket을 사방에 남긴다. 그런 stack을 물려받으면 그것만으로도 connection limit처럼 보인다.

Redundancy는 조용히 부하를 두 배로 만든다

Redundant SCADA에서 request rate가 슬그머니 올라온다. Active-active는 두 node가 모든 장비를 항상 읽는다 — failover는 빠르지만 traffic이 두 배다. Warm standby는 traffic을 줄이지만 primary가 죽을 때 충분히 빨리 올라오는지 증명해야 한다. Cold standby는 가장 많이 아끼지만 하필 failover 도중에 connection·cache 버그를 드러내는 경향이 있다.

무엇을 고르든 실제 장비 수로 시험한다. PLC 한 대 bench redundancy test는 gateway 50대짜리 line에 대해 아무것도 말해 주지 않는다. 정작 중요한 실패 방식 — socket table이 꽉 차는 것 — 은 규모가 커야 나오기 때문이다.

Network 장애처럼 보이지만 아닐 때

Connection limit 문제는 불안정한 Ethernet로 위장하길 좋아한다. 알아채는 신호들:

  • 새 client가 붙는 순간 기존 HMI가 곧바로 bad quality로 간다.
  • Historian service를 끄면 그제야 engineering software가 잘 된다.
  • Gateway 앞쪽 몇 장비는 응답하고, 어느 지점부터는 전부 timeout이 난다.
  • Cable을 뽑으면 장비가 몇 분 동안 새 연결을 거절한다.
  • Packet capture에서 TCP handshake는 끝나는데 Modbus response가 전혀 없고 — 중요한 건 exception code도 없다. 요청이 application 처리까지 못 가서 code를 만들 수조차 없기 때문이다.

이런 게 보이면 scan rate나 retry를 올리지 마라. 그게 본능이고, 경쟁만 더 심해진다. 이미 여유가 없는 장비에 요청을 더 얹는 일이다.

인수 전 drill

실제 client로, 실제 운전 상태로, 사인하기 전에 돌린다.

  1. 계획된 모든 client와 source IP를 적는다.
  2. 운전 때와 같은 조합으로 전부 연결한다.
  3. PLC·gateway·firewall 중 볼 수 있는 곳에서 current connection count를 읽는다.
  4. Historian이나 report connector를 켜고 SCADA quality를 본다 — 무엇이 깜빡였나?
  5. Engineering tool을 열어도 HMI socket이 밀려나지 않는지 확인한다.
  6. Client cable을 뽑고 stale socket이 정리되는 시간을 잰다.
  7. Failover를 돌리고 늘어난 request rate를 측정한다.
  8. Write 권한이 없어야 할 경로에서 write를 시도해 막히는지 확인한다.

결과는 project 파일에 남긴다. 나중에 report connector를 붙이려는 사람은 원래 용량과 소유권 모델이 무엇이었는지 알아야 한다 — 모르면, 전화는 당신에게 온다.