[TensorRT] Custom Plugin 등록·배포 실패 진단 가이드
엔진은 호환되지만 custom plugin 때문에 열리지 않을 때 dependency와 name·version·namespace, 로드 순서를 확인하는 절차입니다.
앞선 TensorRT 엔진 호환성 배포 진단 초안은 plan 역직렬화 실패를 TensorRT 버전, GPU compute capability, OS·CPU 플랫폼으로 나누고 plugin library를 한 진단 분기로 남겼습니다. 그 글의 한계는 파일이 있는데도 왜 creator를 찾지 못하는지, 어떤 version과 namespace를 맞춰야 하는지, library를 언제 로드해야 하는지를 설명하지 않았다는 점입니다. base runtime과 GPU가 호환되는데도 custom layer가 포함된 engine만 실패한다면 지금 이 다음 단계가 필요합니다.
대상 환경과 실패 증상
Linux x86-64의 TensorRT 11.4.1 full runtime에서 자체 또는 vendor IPluginV3 shared library를 쓰는 배포를 가정합니다. 같은 plan이 빌드 서버에서는 열리지만 새 container에서는 getPluginCreator 실패, creator를 찾지 못했다는 로그, deserialize_cuda_engine()의 None 반환이 나타나는 상황입니다. Windows에서는 같은 절차를 DLL dependency 검사와 절대 DLL 경로로 바꿉니다.

1. engine과 plugin bundle의 신원을 먼저 고정합니다
실패한 서버에서 바로 LD_LIBRARY_PATH를 늘리기 전에 plan, plugin, runtime이 배포 manifest와 같은지 확인합니다. --getPlanVersionOnly는 plan을 역직렬화하지 않고 생성 TensorRT 버전을 읽으므로 plugin 문제와 runtime version 문제를 먼저 분리할 수 있습니다.
python3 -c "import tensorrt as trt; print(trt.__version__)"
trtexec --loadEngine=model.plan --getPlanVersionOnly
sha256sum model.plan /opt/tensorrt-plugins/libmyplugin.so
file /opt/tensorrt-plugins/libmyplugin.so기대 관찰값: runtime과 plan 생성 버전, engine·plugin SHA-256, CPU architecture가 배포 manifest와 일치합니다. plan 생성 버전 자체가 지원 범위 밖이면 plugin을 디버깅하기 전에 대상 runtime에서 재빌드하거나 version-compatible 배포 계약을 다시 확인합니다.
shared library가 실제로 로드 가능한지 검사합니다
ldd -r /opt/tensorrt-plugins/libmyplugin.so
readelf -d /opt/tensorrt-plugins/libmyplugin.so | grep -E 'NEEDED|RPATH|RUNPATH'
ldconfig -p | grep -E 'libnvinfer|libcudart'진단 분기: not found 또는 unresolved symbol이 있으면 registry 문제로 넘어가지 않습니다. runtime image에 정확한 dependency를 넣거나 plugin의 RUNPATH를 수정한 새 bundle을 빌드합니다. 서비스 전체에 임의의 전역 library path를 추가해 우연히 다른 버전이 먼저 잡히게 하지 않습니다.
2. name·version·namespace 세 값을 registry에서 확인합니다
TensorRT는 engine 직렬화 때 plugin creator의 name, version, namespace를 저장합니다. 역직렬화 때 registry에서 세 값이 모두 같은 creator를 찾습니다. 파일명이나 plugin class 이름이 비슷해도 세 값 중 하나가 다르면 다른 plugin입니다. 운영 bundle에는 이 triple과 plugin commit을 반드시 기록합니다.
import os
from pathlib import Path
import tensorrt as trt
plugin_path = str(Path(os.environ["TRT_PLUGIN_PATH"]).resolve())
expected = (
os.environ["TRT_PLUGIN_NAME"],
os.environ["TRT_PLUGIN_VERSION"],
os.environ.get("TRT_PLUGIN_NAMESPACE", ""),
)
logger = trt.Logger(trt.Logger.VERBOSE)
registry = trt.get_plugin_registry()
before = {(c.name, c.plugin_version, c.plugin_namespace) for c in registry.all_creators}
handle = registry.load_library(plugin_path)
after = {(c.name, c.plugin_version, c.plugin_namespace) for c in registry.all_creators}
print("new_creators=", sorted(after - before))
print("expected=", expected)
if handle is None:
raise RuntimeError("plugin load failed or a creator identity was already registered")
creator = registry.get_creator(*expected)
if creator is None:
raise RuntimeError("name/version/namespace mismatch")
print("creator_match=", creator.name, creator.plugin_version, creator.plugin_namespace)실행 예: TRT_PLUGIN_PATH=/opt/tensorrt-plugins/libmyplugin.so TRT_PLUGIN_NAME=MyPlugin TRT_PLUGIN_VERSION=3 TRT_PLUGIN_NAMESPACE=com.example python3 inspect_plugin.py처럼 manifest 값을 환경변수로 줍니다.
주의: TensorRT 11에서는 plugin_creator_list가 deprecated이므로 all_creators를 사용합니다. load_library()는 library 안의 creator 하나라도 이미 같은 identity로 등록되어 있으면 실패할 수 있으므로, 배포 smoke test는 plugin을 미리 import하지 않은 새 process에서 실행합니다. static macro는 기본 namespace만 사용하므로 사내·vendor plugin family에는 unique namespace를 주는 dynamic registration이 충돌을 줄입니다.
3. plugin을 먼저 로드하고 그다음 engine을 엽니다
application에서 plan을 먼저 읽고 실패한 뒤 plugin을 로드하는 순서는 늦습니다. runtime이 역직렬화를 시작하기 전에 creator가 registry에 있어야 합니다. 먼저 trtexec로 application code를 배제합니다.
trtexec \
--loadEngine=model.plan \
--dynamicPlugins=/opt/tensorrt-plugins/libmyplugin.so \
--verbose \
--skipInference기대 관찰값: library가 로드되고 creator lookup과 engine deserialization이 통과합니다. 여기서도 실패하면 Python wrapper나 binding 주소 문제가 아니라 bundle·registry·직렬화 계약 문제입니다. 성공하면 실제 shape와 golden input을 넣어 별도의 inference smoke test로 넘어갑니다.
application의 수명 순서를 고정합니다
import os
from pathlib import Path
import tensorrt as trt
logger = trt.Logger(trt.Logger.INFO)
registry = trt.get_plugin_registry()
plugin_handle = registry.load_library(str(Path(os.environ["TRT_PLUGIN_PATH"]).resolve()))
if plugin_handle is None:
raise RuntimeError("plugin library was not registered")
runtime = trt.Runtime(logger)
plan = Path("model.plan").read_bytes()
engine = runtime.deserialize_cuda_engine(plan)
if engine is None:
raise RuntimeError("engine deserialization failed after plugin registration")
# plugin_handle, runtime, engine을 서비스 수명 동안 보관한다.
# 이 다음에 context 생성, 실제 enqueue, golden output 검증을 수행한다.plugin handle을 즉시 deregister하거나 library를 unload하지 않습니다. 관련 engine·context가 살아 있는 동안 plugin code와 registry resource의 수명을 유지합니다. engine load 성공은 배포 성공이 아니므로 실제 입력으로 output finite check, golden tolerance, target shape, 첫 enqueue와 steady-state p95를 검증합니다.
4. external plugin과 engine 내 직렬화를 의도적으로 선택합니다
TensorRT 11.4.1의 --dynamicPlugins는 build 또는 load 시 library를 동적으로 로드합니다. build 때 --setPluginsToSerialize까지 주면 plugin library를 plan에 포함합니다. 둘은 배포 계약이 다릅니다.
# 외부 library를 runtime bundle로 함께 배포
trtexec --onnx=model.onnx \
--dynamicPlugins=/opt/plugins/libmyplugin.so \
--saveEngine=model.plan --skipInference
# library를 engine에 직렬화하는 별도 계약
trtexec --onnx=model.onnx \
--dynamicPlugins=/opt/plugins/libmyplugin.so \
--setPluginsToSerialize=/opt/plugins/libmyplugin.so \
--saveEngine=model-with-plugin.plan --skipInference| 관찰 | 해석 | 다음 조치 |
|---|---|---|
| ldd -r에서 dependency 누락 | OS loader 단계 실패 | image·RUNPATH·패키지 복구 |
| library load, creator 없음 | identity 또는 등록 방식 불일치 | name·version·namespace와 init 경로 비교 |
| creator 있음, engine은 None | serialized field·plugin ABI·runtime 문제 | 정확한 plugin commit으로 재빌드 |
| engine load, 첫 enqueue 실패 | format·shape·kernel runtime 문제 | 실제 shape와 CUDA error 진단 |
plugin을 plan 안에 넣으면 단일 artifact가 되지만 plan 자체가 native executable code를 포함하는 신뢰 경계가 더 커집니다. 외부 방식과 포함 방식을 한 release에서 섞지 말고, 어느 쪽이든 engine과 plugin의 출처·해시를 검증합니다. public upload나 신뢰하지 않는 공급자의 plan·plugin은 역직렬화하지 않습니다.
배포 manifest에 최소 계약을 남깁니다
{
"engine_sha256": "...",
"tensorrt": "11.4.1",
"plugin_file": "libmyplugin.so",
"plugin_sha256": "...",
"plugin_name": "MyPlugin",
"plugin_version": "3",
"plugin_namespace": "com.example",
"plugin_commit": "...",
"packaging": "external"
}engine만 버전 관리하고 plugin identity를 기록하지 않으면 장애 시 어느 library로 재현해야 하는지 알 수 없습니다. bundle promotion은 engine·plugin·manifest를 한 단위로 수행합니다.
이 초안의 검증 경계
NVIDIA TensorRT 11.4.1의 custom layer, advanced plugin, Plugin Registry, runtime, trtexec, migration, security 문서를 대조했습니다. Python 예시는 구문 검사 대상으로 구성했습니다. 이 Windows 작성 환경에는 실제 Linux plugin library, TensorRT plan, target GPU가 연결되어 있지 않아 library load, creator triple, engine deserialization, enqueue, 출력 parity를 실행하지 않았습니다. 대상 container에서 같은 bundle로 smoke test해야 합니다.
롤백과 배포 기준
- 기존 production engine·plugin·container digest를 기준선으로 보존하고, plugin library와 engine을 따로 교체하지 않습니다.
ldd -r, registry creator triple, engine load 중 하나라도 실패하면 새 bundle promotion을 중단합니다.- creator identity를 바꿨다면 기존 plan을 억지로 열지 않고 새 identity로 engine을 다시 빌드합니다.
- engine load가 성공해도 golden output tolerance, finite output, target shape, p95가 기준선을 통과하지 않으면 이전 bundle로 되돌립니다.
- 중복 creator 충돌을 registration 순서로 숨기지 말고 unique namespace와 새 process smoke test로 제거합니다.
- 출처나 해시를 검증할 수 없는 plan 또는 plugin은 진단 편의를 위해서도 로드하지 않습니다.
공식 출처
- NVIDIA TensorRT 11.4.1: Adding Custom Layers Using the C++ API
- NVIDIA TensorRT 11.4.1: Advanced Plugin Topics
- NVIDIA IPluginRegistry Python API: all_creators, get_creator, load_library
- NVIDIA Runtime Python API: deserialize_cuda_engine과 local registry
- NVIDIA TensorRT Benchmarking: dynamicPlugins와 setPluginsToSerialize
- NVIDIA TensorRT 11 Python Migration: IPluginV3와 IPluginCreatorV3One
- NVIDIA TensorRT Security Considerations: engine과 plugin trust boundary
- NVIDIA TensorRT 11.4.1 Release Notes