안녕하세요 깍돌이입니다.

 

자동화 시스템 운영중 Network Traffic Automation System 에 관련된 내용을 하나 적으려고 합니다.

 

시스템 구조상 명령을 내리는 Command Server 와 명령을 받아 행동을 하는 (자동화 케이스의 기능 ) Agent Server 가 있습니다.

 

Agent는 ONE Source 로 되어있습니다.  기존에는 VM 1대 = Agent 1대 였습니다. 하지만 이렇게 사용하게되면 네트워크가 복잡함에 따라서 TC를 늘릴때 마다 VM이 늘어나는 일이 발생하였으며 이로 인해서 효율화를 요청 받았습니다.

 

그로 인해서 케이스를 복잡하게 만들게 되었고 기존에는 1개의 포트 ( 6500Port ) 를 사용하였다면 지금은 VM 1개에서 6500 7500 8500 ~ .. .등으로 사용하게 되어 훨씬 더 적은 VM 으로 많은 케이스를 사용하게 되었습니다.

 

해당 포트로 운영하다 발생한 이슈가 하나 있어 공유 드리려고 합니다. 

 

spawn tcpdump ENOENT

UDP의 트래픽을 캡처 및 검증 하기 위해서 Node.js에서 Child_process의 spawn 을 사용하고 있으며

해당 spawn 에서 tcpdump (OS 레벨) 에서 직접 사용하고 있습니다.

그리고 기존에 VM 1개에서 실행되던 Agent는 nohup형태로 실행되어야 하기 때문에 pm2를 사용하였는데요 ( private 한 환경이라 local pm2를 사용합니다. ) 

package.json 

자동화 테스트 실행시 외부에서 UDP를 발생하게 되는데 위의 spawn 을 통해서 아래와 같이 정상적으로 받아오고 UDP 패킷을 확인할수 있습니다.

하지만 6500 7500 8500 ... x500으로 포트를 나눠서 에이전트를 관리하게 될시

재부팅시 pm2의 오류로 인해서 spawn 시 ENOENT 오류가 발생합니다.

Agent가 있는 VM의 경우 해당 Agent가 죽지 않도록 crontab이 걸려있습니다.

retry.sh 의 경우 pm2 list했을시 6500 7500 8500 등의 서비스가 없을 경우 재시작 해주는 역할을 하는데 서버 재시작 혹은 재부팅 혹은 내서버이미지로 서버 생성하여 새로 생성시 등의 경우 ENOENT 이슈가 발생합니다.

 

일단 하나씩 테스트 해봅니다.  ( 모든 전제조건은 npm run delete로 stop and delete를 했다는 조건 입니다. ) 

 

1. 현재 root계정에서 npm run restart 

실패

2. npm run delete & npm run start 

성공  ( npm run delete 시 pm2 save  to synchronize 워닝 발생 ) 

3. crontab 에서 확인

npm run delete상태기 때문에 npm run start & npm run restart가 됩니다.

실패

4. crontab 확인 ( retry.sh 코드 수정 )

 

재 실행 확인

crotnab으로 동작이 되어야 하지만 동작이 되지 않습니다. 스택 오버 플로우를 찾아보다 보면

spawn 에서 shell:true로 넣으라는 이야기가있는데 그렇게 해볼 경우 exit code 127 이 발생하면서 권한 문제로 종료 되게 됩니다. ( 사실 ENOENT 오류 자체가  " 일부 디렉터리는 권한 문제로 인해 또는 존재하지 않기 때문에 액세스할 수 없습니다" 라는 뜻입니다. ) 

 

ENOENT보단 나은 에러 메시지 아닌가?  권한 문제로 생각하고 pm2 save를 이용해보려고 합니다.

 

5. retry.sh 코드 수정 

npm run save ( pm2 save --force를 추가 )

npm run start

npm run restart

실패 

 

pm2를 커맨드 라인에서 직접 실행할경우만 정상적으로 권한 문제가 발생하지않고 실행이 되는 현상이 발생합니다.

 

그럼 crontab 에서 실행이 문제라고 판단되고 crontab에서 테스트 한 내용입니다.

 

1. sudo crontab -e

루트 권한으로 실행하기 위해서 sudo 를 사용합니다. -> 실패 

 

직접 커맨드라인에서 실행한게 아니라면 restart를 커맨드라인에서 직접 해도 오류가 발생합니다.

 

 

결론

npm run save 부분을 sudo 로 실행해야 권한 이 꼬이지 않고 save가 됩니다. 

npm run save는 

 

pm2 save --force 입니다. ㅎㅎ

pm2에 구조에 대해서 이야기 를 할까 했지만 해결한 것 으로 만족 하겠습니다...

 

지금은 Docker 기반이 아니라 위와 같은 이슈가 발생하고 ( 서버의 지속적 운영을 위해서 ) 

Docker 로 애초에 쓰시고 계신 분들은 아마 이슈가 없을 거라고 생각 합니다. 

Docker에서는 Docker의 이슈가 있겠지.. 내년엔 다 Docker 로 바꿉니다. 

 

감사합니다.

 

 

안녕하세요 오늘은 자동화 테스트 코드에 대한 이야기를 해보려고 합니다.

 

5개의 TC(Test Case)가 있습니다. 테스트 방식은 직접하는 매뉴얼 테스트도 있을 것이고 성능,부하 등등 

 

여러가지가 있지만 말씀드리고자 하는 부분은 단순 Unit Test 혹은 API 테스트 같은 부류들 처럼 코드단에서

 

이루어지는 부류를 말씀 드리고자 합니다.

 

예전에 제가 사용 하였던 2가지의 패턴 방식을 보여드리려고 합니다.  현재는 또 이러 저러한 구조를 통해서 변경 

 

하고 있습니다.

 

독립적 실행이 필요한가?

어떠한 프로그램에 따라서는 아닐수도 있고 맞을수도 있습니다. 저같은 경우는 JS 로 돌아가는 환경이 최근에 많았었는데요 JS의 경우는 .js파일을 읽어들이면서 코드라인이 실행됩니다. JIT(Just In Time) 라고도 하죠 그렇기 때문에 어떠한 현상이 발생되는 현상은 

 

함수 b로 인해 실행되지 못한 c함수 

 

위와 같은 문제가 발생하게 됩니다. TestCase를 3개를 만들었지만 2번째 케이스로 인해서 3번째 케이스가 돌지 못하는 현상이 발생하게 되는 것이지요

 

1차적으로 간단하게 수정할수 있습니다.

function a(){
	console.warn('Success');
}
function b(){
    try{
        var test = "";
        test.push('test'); // Error 
    }
    catch(err){
	    console.log(err);
    }
}
function c(){
	// not Execution 
	console.warn('function C Success');
}

a();
b();
c();

try catch 로 묶어 주게되면 

문제없이 catch 로 핸들링이 되며 다음코드로 자동으로 넘어가게 되겠죠 

 

하지만 실제로 업무에서 사용되는 케이스는 10단위도 아니고 100단위도 아니고 1000단위가 넘어가게됩니다.

 

코드 단에서 할수 있다면 최대한 많은 케이스를 확보 하여 커버리지를 올릴 수 있기 때문이죠 

 

1. TC(TestCase) 내용으로 함수 연결하여 실행하기

TC(TestCase)의 경우 유니크한 값을 가지기 마련입니다. 함수명을 해당 키 값으로 하여서 실행시키는 방법입니다.

var testRepo = [
  {tc:"a",contents:"a를 테스트합니다.",result:"block"},
  {tc:"b",contents:"b를 테스트합니다.",result:"block"},
  {tc:"c",contents:"c를 테스트합니다.",result:"block"},
]

var testFunctionRepo = {
  run:function(repo){
    var _this = this;
      repo.forEach(function(item,index){
        try{
          _this[item.tc]();
        }
        catch(error){
          console.error(error);
        }

      });
  },
  a:function(){
    console.warn('Success');
  },
  b:function(){
    var test = '';
    test.push('test'); // Error
  },
  c:function(){
    // not Execution 
    console.warn('function C Success');
  }
}
testFunctionRepo.run(testRepo);

결과 :

성공

 

tc에서 만들어진 유니크한 값을 통해서 함수를 작성해놓았습니다.

 

그후엔 run 이라는 함수를 하나 만들어서 해당 함수에서 tcRepo 를 직접 순환시에 나오는 유니크 값을 함수를 불러와서  실행을 하는 구조입니다. 그렇기 때문에 try catch 문은 하나만 있을수 있죠 

 

위에서는 테스트를 변경하진 않았는데 해당의 내용도 가능합니다. 

 

2. TC(TestCase) 내용에 함수가 들어있는 경우 

이번에는 Repo 안에 함수가 들어있는 경우입니다. 


var testRepo = [
  {tc:"a",contents:"a를 테스트합니다.",result:"block",run:function(){
		console.warn('Success');
  }},
  {tc:"b",contents:"b를 테스트합니다.",result:"block",run:function(){
		var test = '';
		test.push('test'); // Error
  }},
  {tc:"c",contents:"c를 테스트합니다.",result:"block",run:function(){
		// not Execution 
		console.warn('function C Success');
  }},
]
function run(repo){
	if(repo && repo.length===0){
		return ;
       }
	repo.forEach(function(item,index){
		try{
			item.run();			
        }
		catch(error){
			console.error(error);
        }
    });
}

run(testRepo);

 

위에 있는 경우와 조금 다른 케이스입니다.  Repo 안에 직접 run 이라는 함수를 직접 내장 하였는데요 

Repo 안에 전부 있기 때문에 run 을 오브젝트를 통해서 실행할 필요는 없어 보여 run 함수를 따로 작성 하였습니다.

이렇게 2가지를 알아보았는데 해당 방식을 놓고 보면 좋아 보이는거 같기도하고 하지만 

기본적인 방법입니다. 뭔가 더 정형화되거나  확장성에는 용이 하지 않죠 

 

여러명이 작업할때 좀 더 모듈단위 로 세분화가 되거나  하여야 할 것으로 보입니다. 이부분에 대해서는

 

다음포스팅에 다시 작성 하겠습니다.

+ Recent posts