docker swarm 네트워크를 사용하면 각각의 분산된 PC를 하나로 묶을 수 있고,
docker swarm의 ingress 네트워크를 사용하여 클러스터를 구축 가능하고,
이를 사용하여 각각 분산된 PC에 docker시스템을 관리 할 수 있다.
특히 ingress네트워크 상에서 docker 시스템 간 로드밸런싱이
가능하여 여러대의 PC에 손쉽게 Inferecne Docker를 구축이 가능하다.
그런데 모든 상황에서 이 로드밸런싱 기능을 사용할 수 있는 건 아니였다.
프로젝트 진행 중, 기존 https 방식의 REST호출을 사용할 때는 분산 로드 밸런싱이 됬지만,
GRPC 통신 방식을 사용하면, 로드 밸런싱 기능을 사용할 수 없었다.
GRPC 통신을 통해서 로드밸런싱을 하려면 NGINX 프록시 서버로 사용하여 로드밸런싱을 사용할 수 있음을 알았다.
이를 사용하기 위해서 docker swarm의 docker컴포즈 방식인 docker stack을 사용하여 구축할 수 있었다.
서버는 python yolov5 + grpc를 사용하여 서버구축을 하였다.
https://github.com/DuarteMRAlves/yolov5-grpc
해당 git을 참조하여 구축하였다.
GitHub - DuarteMRAlves/yolov5-grpc
Contribute to DuarteMRAlves/yolov5-grpc development by creating an account on GitHub.
github.com
import concurrent.futures as futures
import io
import grpc
import grpc_reflection.v1alpha.reflection as grpc_reflect
import logging
import torch
import PIL.Image
import logging
import yolov5_service_pb2 as yolov5_service
import yolov5_service_pb2_grpc as yolov5_service_grpc
import time
_SERVICE_NAME = 'YoloV5'
_MODEL_REPO = 'ultralytics/yolov5'
_MODEL_VERSION = 'custom'
_MODEL_PATH = './model/best_safety.pt'
_PORT = 8061
class YoloV5Service(yolov5_service_grpc.YoloV5Servicer):
def __init__(self):
# Model for file/URI/PIL/cv2/np inputs and NMS
self.__model = torch.hub.load(_MODEL_REPO, _MODEL_VERSION, path= _MODEL_PATH)
def detect(self, request, context):
"""
Receives a request to detect objects and
replies with all the detected objects in the image
Args:
request: Request with the bytes of the image to process
context: Context for the gRPC call
Returns:
The DetectedObjects protobuf message with the objects
detected in the image
"""
start = time.perf_counter()
img_bytes = request.data ## grpc input image data
img = PIL.Image.open(io.BytesIO(img_bytes))
# Fix for PIL Images need file name in model
img.filename = "file"
with torch.no_grad():
results = self.__model(img, size=640)
end = time.perf_counter()
infer_time = end - start
return self.__build_detected_objects(results, infer_time)
def __build_detected_objects(self, results, infer_time):
# Only one image in each prediction so we can access predictions with [0]
# Get normalized values with xyxyn
objects = (self.__build_detected_object(line, results.names, infer_time) for line in results.xyxyn[0])
return yolov5_service.DetectedObjects(objects=objects)
def __build_detected_object(self, obj, names, infer_time):
p1 = self.__build_point_from_2x1tensor(obj[:2])
p2 = self.__build_point_from_2x1tensor(obj[2:4])
conf = obj[-2]
class_idx = int(obj[-1])
class_name = names[class_idx]
return yolov5_service.DetectedObject(
class_name=class_name,
class_idx=class_idx,
p1=p1,
p2=p2,
conf=conf,
infer_time = infer_time
)
@staticmethod
def __build_point_from_2x1tensor(tensor):
return yolov5_service.Point(x=tensor[0], y=tensor[1])
def main():
"""
Runs the server and waits for its termination
"""
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
yolov5_service_grpc.add_YoloV5Servicer_to_server(
YoloV5Service(),
server
)
service_names = (
yolov5_service.DESCRIPTOR.services_by_name[_SERVICE_NAME].full_name,
grpc_reflect.SERVICE_NAME
)
grpc_reflect.enable_server_reflection(service_names, server)
target = f'[::]:{_PORT}'
server.add_insecure_port(target)
logging.info('Starting YoloV5 server at %s', target)
server.start()
server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig(
format='[ %(levelname)s ] %(asctime)s (%(module)s) %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.INFO)
main()
서버를 실행시키는 docker를 만들었다. (** docker swarm은 실행되는 프로세스가 없으면 container가 생성되지 않는다.)
공식 yolov5 Doceker 이미지에 추가적으로 설치하여 dockerfile로 서버를 실행시켜 주었다.
grpcio==1.35.0
grpcio-tools==1.35.0
protobuf==3.14.0
FROM yolov5_grpc_server:0.1
EXPOSE 8061
## protobuf
# CMD ["python3", "-m", "grpc_tools.protoc", "-I.", "--python_out=/usr/src/app", "--grpc_python_out=/usr/src/app", "yolov5_service.proto"]
CMD ["python3", "yolov5_service.py"]
그다음, nginx conf 파일을 정의하자
user nginx;
worker_processes auto;
worker_rlimit_nofile 10240;
events {}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $server_port';
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream grpcservers {
server yoloapp:8061; # 실제 gRPC 서버의 주소와 포트
}
server {
listen 1443 http2;
location /YoloV5/detect {
grpc_pass grpc://grpcservers; # SSL 없이 gRPC를 사용
}
}
}
이를 사용해서 docker 클러스터를 묶어 실행해 보자
version: '3'
services:
yoloapp:
image: yolov5_grpc_server:0.2
ports:
- 8061:8061
volumes:
- /mnt/storage2t/grpc_yolov5/app:/usr/src/app
deploy:
replicas: 2
environment :
- NVIDIA_VISIBLE_DEVICES=ALL
networks:
- balance
proxy:
image: nginx:latest
ports:
- 1443:1443
depends_on:
- yoloapp
deploy:
placement:
constraints: [node.role == manager]
volumes:
- /mnt/storage2t/grpc_yolov5/app/nginx/nginx.conf:/etc/nginx/nginx.conf
networks:
- balance
command: ["nginx", "-g", "daemon off;"]
networks:
balance:
driver: overlay
'파이썬 이것저것 > 파이썬 딥러닝 관련' 카테고리의 다른 글
[Python] Gpt4Free 여러 LLM모델을 무료로 써보기 (1) | 2024.02.25 |
---|---|
[Python] Yolo-World 실시간 비전언어 모델 (1) | 2024.02.06 |
yolo-nas 학습하기[Python] (3) | 2023.06.07 |
[TensorRT] ValueError: cannot reshape array of size 57603 into shape (360,360) - ValueError 해결 (0) | 2022.10.30 |
[python] Yolo v5 object detection 고추 병해 데이터 셋 학습해보기 (0) | 2022.08.02 |