Substation Logging System 적용기

스마트팩토리 PHM 분야에서 유니콘을 향해 달려가는 Onepredict 의 제품 중, 'Substation' 제품의 로깅 시스템을 구축하기 위한 고민과 흔적을 돌이켜보는 과정을 적어보려 합니다.



안정적인 대기업에서 스타트업으로 넘어와, 여러 우여곡절을 겪고 있지만, 몰아치는 업무를 끝내고 이렇게 글을 작성하며 뒤돌아보면 스타트업에 발을 내디딘 그때가 떠올라 다시금 마음을 다잡게 됩니다. 


제조업에서 일하면서 수많은 공장 설비 데이터를 가공하고 운영하는 경험은 좋았지만, 오래된 기술 스택 과 보수적이고 딱딱한 분위기에서 부품처럼 돌아가는 업무는 개발자보단 엔지니어가 되는 느낌이 강했습니다. 

CTO를 꿈꾸는 사람으로서, 마주하는 이슈를 풀어가기 위해 아키텍처와 컨셉을 고민하고 이런 노력이 실제 제품에 반영될 수 있는 환경에서 역량을 펼쳐보고 싶었으며, 이 고민은 스타트업인 원프레딕트와 함께한 계기가 되었습니다.


서브스테이션이란? 

가디원 서브스테이션은 유입식 변압기의 절연유 데이터(DGA: Dissolved Gas Anlaysis) 를 활용해서 설비의 현재와 미래의 상태를 진단하는 제품입니다. 

변압기 (Transformer) , 변전소 (Substation)


해당 시스템의 백엔드는 SaaS 기반의 'Python' Flask 시스템이며, 제가 투입될 당시엔 'GS 인증 1등급'을 획득한 시점으로 나름의 구조가 궁금했습니다.

객체지향의 특징을 활용한 디자인 패턴(Design Pattern) 이나,  'Clean Architecture' 책에서 강조하는 Interface 를 활용해 응집도는 높이고 결합 도는 낮추는, 일련의 세련된 '미(美)' 는 없지만,  심플한 MVC 구조의 API 서버로, 각 함수에 대한 Exception 처리 및 에러 코드와 메시지가 잘 정리되어 있어서 탄탄하고 짜임새 있게 구성된 시스템이었습니다.  


로깅 시스템의 도입

새로운 업무를 하는 와중에, 새로 오신 CTO 의 첫 과제?로는 Guardione 전 제품에 대한 로깅 과정이 미흡해 에러 발생에 대한 원인 분석이 제대로 되지 않으니, 로깅 시스템을 구축하라는 지령이 있었습니다.


개인적으로도 이 부분은 공감이 되어 애자일 스프린트 일정을 쳐내면서도, 짬 내서 구축을 했었고 백엔드 개발자 간 챕터 과정에서 구현한 방식에 대해서 공유했었습니다. 다만 명확한 피드백 없이 만약의 만약의 꼬리만 무는 회의로 피로감이 쌓인채 끝난 미팅은 정말로 최선의 로깅 시스템은 어떻게 구축하는 걸까 라는 의문으로 이어졌습니다.



처음 로깅 시스템을 구축할 당시에는, 삼성 반도체 분야에서 일했던 경험을 바탕으로 구축 했었습니다.


  • 1. SQL Trasaction 로그를 기록한다. 

  • 2. 모든 유저의 행동을 Tracking 한다.
  • 3. 서버에서 발생하는 로그를 기록한다. 
  • 4. 에러 발생 알람을 메일, 메시지 형태로 알린다. 
  • 5. Splunk 외부 툴을 활용해서 로그를 필터링 및 시각화 한다.


 

- SQL Trasaction 로그를 기록한다. 

기본적으로 MES (Manufacturing Execution System) 는 공장에서 발생하는 모든 이력을 수집하고 관리 추적하는 시스템으로 ERP와 비슷하게 하나의 거대한 DB에 여러 테이블과 복잡한 관계로 구성된 특징을 가지고 있습니다 .

그러다 보니 시스템 대부분의 로직이 SQL 에 녹아 있었고, SQL 로그만 제대로 확인해도 에러 대부분을 찾아낼 수 있었습니다.


Clean Architeture Diagram 

비즈니스 업무 규칙을 '엔티티' 에 넣어야 한다는 Clean Architeture 에 정면으로 맞서 'DB SQL' 에서 처리하는 MES.... 


Substation은 Python에서 사용하는 'SQLAlchemy' ORM 을 활용해서 DB Transaction 처리를 하게 되고 Python 'Logging' 라이브러리를 활용해 보기 좋은 포맷터를 지정 후 변환된 Raw SQL 로그와 서버 로그를, console 형태와 일자별 file 형태로 저장해 관리합니다. 비록 substation은 MES 와는 거리가 있지만, 여전히 유효한 로그라고 생각했습니다. 



- 모든 유저의 행동을 Tracking 한다.

삼성 반도체 분야는 임직원의 공항 검색대 같은 출입 과정부터, 보안에 정말 민감하게 작용하는 곳이었습니다. 

매년 삼성 SDS에서 진행하는 보안점검에서 (보안점검이라 읽고 해킹이라 말한다..) 무난하게 넘어가기 위해서는 사용자의 모든 Request call 에 대한 추적이 바탕이 되어야 했습니다. 


API 필수 요청 정보뿐만 아니라 시스템 ,도메인에 필요한 여러 부가적인 정보를 함께 저장하여 사용자에 대한 로그를 관리하고 있었음에도, (파라미터 변조) 해킹을 당한 경험은... 지금 생각해도 끔찍했지만,  이 로그는 Exception에 대한 원인을 추적하는 데 좋은 지표가 될 거라 생각했습니다. 

어떻게 해킹하는지도 안알려주고,,, 해킹 공부부터, 보안 로직 심기까지 숱한 노력이 ㅜ..      

Link. 해킹 당해본적 있니?



부디 저희 시스템을 해커로부터 무사하게 하시옵소서..



- 서버에서 발생하는 로그를 기록한다. 

이 부분은 4대 MES 중 '생산 실적 집계'에 해당하는 시스템의 특화된 부분이기도 합니다. 여러 시스템에서 받아온 데이터를 가지고 최종적으로 설비별, 웨이퍼별 생산실적 집계를 내기 위해 금융 분야에서 자주 사용하는, pro*c 파일을 Linux의 Crontab 에 등록해 배치를 돌려서 가공합니다. 

그러다 보면 꼭, 심심치 않게 pro*c  에서 에러가 발생해 데이터 집계의 오류가 발생하는데, 어느 시스템에서 받아올 파일에 데이터가 없거나, 새로운 공정의 추가로 데이터 포맷이 달라 에러가 발생하거나, 배치가 평상시보다 오래 걸려서, 이어서 진행할 배치가 돌지 못했든가 하는 여러 이슈로 인해서, 서버 내부적으로 발생하는 이벤트에 대한 로그를 남기고 있었습니다.



개인적으로 저는 위에 두가지  '모든 유저의 행동을 Tracking 한다', '서버에서 발생하는 로그를 기록한다'  를 처리하기 위해서 시스템 내부 DB에 user_log 외 server_log 테이블을 만들어 관리하도록 구현했습니다.


user_log


user_log는 어떤 유저가, 어떤 API에, 무슨 파라미터로 언제 보냈는지에 대한 Input을 기록하고 있습니다. 


server_log

400: Exception 관련 서버 로그

500: Exception 관련 서버 로그

9XX: 서버 보안 관련 로그

8XX: 서버 스케줄링 관련 로그

server_log는 code에 따라서 큰 범주로 나누고, 해당 로그가 발생한 파일명, 함수명, 구체적인 로그(에러) 내용, 시간을 기록하여 user_log 의 Input 에 대한 Output을 기록하게끔 구현했습니다. 


예시로 server_log 의 9XX code 로 관리하는 보안로직은,  user_log 테이블에서 '특정 유저'가,  '특정 API' 를 분당 'n번' 실행 시, 해당 유저를 Block 하고 server_log 에 남기는 로직으로 구성되어 있습니다. 


Substation 은 각 설비에 License 를 발급받아 사용되는 구조로서, 사용자가 임의의 License 를 계속해서 체크하는 API 를 날리거나, Excel 를 여러번 업로드 하는등 악성 유저에 대한 차단 로직을 가지고 있습니다. 



또한 Exception에 대한 로그를 남기는 과정은, 모든 함수에 대해 Exception 구문을 만들고, Exception 발생 시, 재귀적으로 각 함수에 대한 에러 로그가 server_log에 남게 구현했습니다. 

 



이렇게 구현을 한 이유는, Clean Code를 지향하는 개발자라면, 하나의 함수 안에 많은 양의 코드를 작성하지 않고 기능적 단위로 함수를 분할해서 함수를 구현하는데, 이렇게 되면 많은 Depth의 함수를 띄게 됩니다. 

개인적으로 Exception이 발생했을때 함수마다 Exception을 걸지 않으면, 최상단에서 실행한 function_A 에서 보게 되는 Exception 에러 문구는 제한적이게 되며,  어느 함수명에서 어떤 Exception Argument로 발생했는지 알아채기 어려우므로, 각 함수 마다 같은 작업을 걸어주었고, GS 인증을 받았던 기존 소스에도 비슷한 구조로 되어 있었습니다. 




피드백... 

처음 백엔드 챕터 과정에서 공유했었던 Substation 로깅 시스템에는 위 3가지를 구현해 이야기했었고, 이에 대한 피드백으로 크게 3가지가 있었습니다. 


1. 개발에서는 되는데 운영에서는 안되는 경우 커버 가능한가?

방대한 레거시 프로그램을 유지보수 해온 사람이라면, 상상하기 꺼림칙한 일이란 것을 공감할 것 같습니다.

C#, WPF Desktop 프로그램을 운영하면서, 개발에서와 달리 운영에서 Client, Server 간 통신이 비정상적으로 종료되는 케이스가 있었습니다.

원인을 찾기까지 2달을 소모해 찾아낸(감동의 눈물ㅜ) 결과로는, Client 가 .exe 프로그램을 실행하기 전에,  중국 사업장에 접속하기 위한 원격 프로그램을 실행하게 되면 발생하는 현상이었고,  원격 프로그램의 알 수없는 보안 로직이 Client와 Server 간 SOAP RPC 통신 방해로 나타나는 현상이 있었습니다.

이런 경험은 서브스테이션에서도, 코드 로직과 무관하게 발생할 수 있는 오류에 대해서 어떻게 대처 하지에 대한 고민으로 이어지다가, 문득 정말 이런 케이스가 발생할 것인가에 대한 의문으로 이어졌습니다.


  • "Docker 환경에서 실행되는 서브스테이션은,  OS 버전부터 시작해, ARM, AMD 같은 아키텍처, 라이브러리를 모두 동일한 환경에서 실행되는 구조로 개발, 운영 서버를 각각 두어 운영되는 On-Premise 환경과 본질적으로 다르지 않나? "


  • "Desktop과 달리 Web에서는 외부 간섭이 없기 때문에 도메인이 다르거나, SSL 인증서가 없어서 발생하는 CORS 이슈가 대부분이지 않나? "


위 2가지 질문에 대한 고민으로 해당 케이스는 Docker 기반 환경에서는 커버가 가능하다고 생각했습니다.





2. 저렇게 구축하면 정말 에러에 대한 원인 파악이 가능한가?

복잡한 로직으로 구성된 서버 오류를 확인하기 위해 개발자가 트레이스 하는 주된 방식은 똑같이 재현하는 것으로 생각합니다.

이를 위해서는 시스템 운영상의 Input 과 에러 Output 을 확실하게 아는 게 필요했고, 이러한 정보가 갖춰지게 된다면 당시의 시나리오를 재현할 수 있어 오류에 대한 원인을 파악하고 수정할 수 있습니다. 


다만 Input과 Output을 확실히 알아도, 동일한 시나리오 결과를 낼 수 없는 예외 케이스가 있는데,  바로 DatabaseTime과 같은 '외부 종속' 요인들이 함께할 상황에 해당합니다.


지금은 마음 한편으로만 남아있는... Java ,Spring 기반의 'GuardiOne' 제품을 만들던 당시에 이런 고민이 많았습니다.

TDD(Test-Driven-Development)에 진심이었던 분과 테스트 코드를 작성하면서, 이러한 외부 종속요인 처리에 고민을 많이 했었고, 이러한 부분은, 가능한 모든 경우의 수를 고려해 심도 있게 테스트 코드를 작성하면, 시나리오 재현 과정에서, 해당 영역으로 발생할 에러를 많이 걸러낼 수 있다고 생각했습니다.


GuardiOne 해체 당시.. 모습



3. 에러 케이스 외에도, 미래 에러 발생 요소가 될 만한 녀석의 로깅이 가능한가? 
사실 개인적으로 이부분에 대한 피드백은 이해가 잘되지 않았습니다.

제가 이해하기에 CTO 님의 의견은, 프레임워크에서 제공해주는 logger 기능을 활용해서, 미래 에러가 발생할 수 있는 녀석에 대해 적재적소에 Info, Error 레벨 형태의 로깅을 하도록 기대하신 것 같으나, 일선에서 개발 로직을 건드려야 하는 개발자로서는, 적용하기에는 조금 부족한 두루뭉술한 가이드였습니다.


개인적으로 삼성 여러 시스템에서도 이러한 logger 를 개발 목적외 운영환경의 로그로서 제대로 활용하는 시스템을 보지 못했고,

하드 코딩을 통해 개발하게 될 경우, 시간이 지나 유지보수가 되지 않는 것을 수없이 봐온 저로선 차라리 TDD 구축하는 리소스를 더 투입하는 것에 한 표를 주고 싶었습니다.


회의를 마친 후, CTO 님의 임베디드 개발 당시 경험담을 듣고 난 후, 한때 임베디드에 진심이었던 저도 어떤 의도로 얘기하신지 이해가 됐지만, 아직은 TDD 역량을 강화하는 게 최선이라고 생각합니다.

Link [한컴MDS] 펌웨어 프로젝트 제작기






미팅이 끝나고 받은 피드백에 대해서, 나름대로 최고의 선택과 결정을 위해 고민 후, 발생한 에러에 대해 알람을 주는 기능도 구현했습니다. 


- 에러 발생 알람을 메일, 메시지 형태로 알린다.

삼성에서는 에러가 발생한 Exception 문구와 여러 부가 정보를 주기적으로 체크해 Knox 메일 형태로 메일을 보내거나, 문자 메시지로 전송되게끔 구현이 되어있었습니다.

원프레딕트는 회사 메신저로 Teams를 사용하고 있었고 마침 MS에서 제공하는 Teams bot 기능이 있어서, 해당 라이브러리를 활용해 에러가 발생할 때 알려주는 구조를 만들었습니다.

알람 메시지로는, 위에서 얘기한 유저의 요청 콜 에 대한 Input과, Output을 조합해서 보이도록 해, 시나리오 재현과정을 바로 확인할 수 있게 구현했습니다.






- 외부 툴을 활용해서 로그를 필터링 및 시각화한다.

CTO 님의 목표로는 로그 시스템을 구축과 더불어, 외부에서 전체 GuardiOne 제품에 대해 로그 관리를 하고 싶어 하셨고 필수 요소처럼 말씀하셨습니다. 당시에는 CTO 님이 말하는 키바나 툴에 대해서 잘 모르기에 이걸 이용하면 에러 원인을 찾을 수 있나? 하는 Question Mark가 존재했었지만, 나중에 따로 찾아본 바로는 삼성에서 사용 했었던 Splunk 와 유사한 데이터 분석 도구임을 알게 되었습니다.


Kibana Grafana Splunk




Kibana는 Elasticsearch 클러스터에서 생성된 로그 데이터를 시각화하는 데 도움이 되는 데이터 시각화 도구입니다.


Kibana는 Elasticsearch 및 Logstash와 함께 ELK 스택을 구성합니다. 따라서 Kibana는 Elasticsearch와 긴밀하게 통합됩니다

Grafana는 데이터 시각화에 적합한 오픈 소스 도구입니다. 주로 InfluxDB, Graphite 및 Elasticsearch에서 사용됩니다.


Grafana를 사용하면 사용자가 처음부터 자신만의 플러그인을 만들고 이를 사용하여 데이터 소스와의 통합을 수행할 수 있습니다. 강력한 경고 시스템이 있으며 사용자가 그래프에 주석을 달 수 있습니다.

Splunk는 방대한 양의 머신 생성 데이터를 수집 및 관리하고 그 안에서 특정 정보를 검색하는 데 사용할 수 있는 BI 플랫폼입니다.


Splunk는 모든 유형의 데이터를 수집하고 검색 결과가 특정 조건을 충족할 때 알림을 받도록 경고를 구성하는 데 도움을 줄 수 있습니다. 데이터 내에서 빠른 검색을 위해 데이터를 인덱싱합니다.

https://www.theairtips.com/post/kibana-vs-grafana-vs-splunk-vs-knowi


운영업무를 함에 있어서, 이러한 툴이 있다면 물론 좋지만 이러한 도구를 도입함으로서, 처음 미팅 주제였던 에러 발생에 대한 원인을 찾을 수 있나에 대해서는 개인적으로 부정적인 의견입니다.

방대한 기능을 제공해주는 Splunk 를 활용해 운영 업무를 했지만, 결과적으로 log 파일을 가져다 필터를 걸어 시각화해주는 도구로, 실제로는 SQL 로그를 확인하거나 CPU, Memory 상태를 측정하는 용도로만 사용을 했었습니다.


한정된 개발 인력 속에서, 전체 제품의 로그를 관리하는 키바나 툴을 도입하면 해당 로그 관리 시스템은 누가 관리하지? 하는 걱정도 있지만 서브스테이션에서는 AWS 에서 CPU, Memory 모니터링을 제공해주기에 나중에 여유가 된다면 오픈소스 기반의 그라파나를 도입해볼 생각입니다.



마치며

개발에 있어, 언제나 완벽한 방법은 없지만, 주어진 환경 내에서, 고객의 니즈를 충족하며 최선의 선택을 해야하는 개발자의 길고 깊은 고민은, 언제나 함께가는 숙명이 아닐까 합니다.