반응형

Tool 개발 로그 - 1-1 #Jenkins #서비스 #재시작 + @작업하는 #Tool 만들기 c# -배경 #시나리오

 

Tool 개발 로그 - 1-1 #Jenkins #서비스 #재시작 + @작업하는 #Tool 만들기 c# -배경 #시나리오

* 2020년 9월에 작성 된 Tool의 개발 로그입니다. 회사에서 사용하고 있는 Jenkins 가 간혹 이상 증상을 일으키는데, 그 중 하나는 Windows OS 를 사용하는 Slave-node 들이 모두 off-line 으로 변경 된다. - 참

yobine.tistory.com

* 2020년 9월에 작성 된 Tool의 개발 로그, 2번째 프로그램 작성 ServiceControlCore 설명이다. 

- Windows OS 내 Services (서비스) 툴에 등록 된 서비스 를 멈추고 시작하고, 상태를 확인할 때, 유용하게 사용할 수 있도록 제공 된 닷넷 클래스가 있다. 이름은 ServiceController 이며 이에 대한 상세 설명은 다음 링크에 기록되어 있다. -  https://learn.microsoft.com/ko-kr/dotnet/api/system.serviceprocess.servicecontroller?view=dotnet-plat-ext-7.0

 

ServiceController 클래스 (System.ServiceProcess)

Windows 서비스를 나타내며 이 클래스를 사용하면 실행 중이거나 중지된 서비스에 연결하거나 서비스를 조작하거나 서비스 관련 정보를 가져올 수 있습니다.

learn.microsoft.com

이 클래스를 사용하여 서비스 상태를 가지고 오고, 멈춘 뒤, 필요한 작업을 진행 하고, 마지막에 서비스를 다시 시작한다. 해당 프로그램의 배경 시나리오 포스트( https://yobine.tistory.com/582 ) 에서 작성 된 프로그램의 전체 플로우와 동일하다. 

Main

이번 포스팅에서는 ServiceControlCore 클래스 내 있는 Method 들을 살펴 본다. 상단에 언급했듯 이 ServiceControlCore 클래스 에서는 닷넷에서 제공하는 ServiceController 클래스를 사용하여 Jenkins 서비스의 상태를 확인하고 변경한다.

프로그램에서 제일 처음에 호출 하는 작업이 GetServices() 메소드 이다. 

이 메소드는 Jenkins 서비스 상태를 확인하는 부분인데, 리턴 값은 bool 이며. 선언된 serviceName (Jenkins) 과 Services[] 로 받아온 리스트를 비교 하여, ServiceName 이 리스트에 있는지 우선 확인 하며, 서비스 이름이 Services 에 등록되어 있다면 현재 상태를 확인 하고, running 인 경우 true 를 반환한다. 

services 에 등록된 service 리스트
Services 리스트에서 검색 매치된 Jenkins
- GetService() 메소드 - 뭔가 많아 보이지만, result = true 로 바뀌는 부분만 중요하며, 나머지는 로그 관련 스트링 전달문들이다.

- 서비스가 언제나 running 인 상태여야 프로그램이 돌기 때문에, 이부분에 대해 수정이 필요할 것 같다. 

더보기

* 서비스 이름은 검색이 안되는 경우를 빼고는 해당 서비스의 상태가 stopped 인 경우에도 프로그램이 돌아야 한다. 이부분은 수정 되어야 할 사항이다. 아마 수정이 된다면, 다음 표의 내용 처럼 리턴 값을 받아 간단하게 수정이 될 수 있을 것 같다.

서비스 등록여부 서비스 상태 리턴 값
true (등록) running 11 : (true 1, running 1)
true (등록) stopped 10 : (true 1, stopped 0)
false (등록안됨) stopped 20 : (false 2, stopped 0)

 

다음은 ControlService() 메소드 다. 이 메소드에서 서비스를 실질적으로 멈추고, 시작한다. 우선 int flag 를 인자로 , 0은 서비스 멈춤 (Stop), 1은 서비스 시작 (Start) 으로 약속된 값을 전달 받는다. 실행 중인 Jenkins 서비스(serviceName )를 멈추고(Stop)  20초 timeout 시간을 걸어주는데, 서비스가 멈추는데 걸리는 시간이 대략 최대 20초 정도여서, 멈출 때까지 대기를 하지만, 대부분 20초 전에 멈추기 때문에 대기 시간으로써는 충분한 시간이다.

ControlService() Method

ControlService() 메소드 에서 flag 값에 따라 분기가 일어나는데, if/else 가 아닌 switch 문을 사용했다. 1과 0 만이 아닌 그외의 명령어 까지 전달을 생각으로 메소드를 작성했다. 내부적으로 시작 혹은 멈추는 명령어를 전달 후, 서비스 상태를 2초마다 확인하는데, 이 메소드 이름은 CheckServiceStatus(int flag) 이다. 이 메소드 는 ControlService(int flag) 메소드에서 전달 받은 동일한 인자 flag 를 받아 GetService() 메소드를 호출하여 상태를 확인 한다. 

CheckServiceStatus()

서비스를 Stop (0) 을 시켜야 하는데, GetService 의 리턴 값이 true 면, 다시 동일한 flag 값 (0) 을 가지고 재귀호출을 한다. 만약 GetSerivce 의 리턴값이 false 라면, break 가 걸리고, 서비스가 멈추었다는 로그를 남긴다. 

서비스를 Start(1) 하는 내용도 동일하다. 리턴값들이 반대이지만, 동일하게 작동한다. 

CMD 화면

해당 프로그램을 실행하면 위의 CMD 화면과 같이 화면에 로그들이 찍힌다. 처음에 Jenkins 서비스를 찾았고, 실행 중인 것이 확인 되어 재시작 프로세스가 시작되었다고 나오며, 서비스를 멈추게 한다. 서비스가 멈춘 후 삭제해야 할 폴더들을 각각 찾아서 삭제를 진행 하게 되며, 작업이 끝난 후에 다시 Jenkins 서비스를 실행 한다. 

-끝-

youp-han/Jenkins-Restart: it restarts the running Jenkins Service locally (github.com)

 

GitHub - youp-han/Jenkins-Restart: it restarts the running Jenkins Service locally

it restarts the running Jenkins Service locally. Contribute to youp-han/Jenkins-Restart development by creating an account on GitHub.

github.com

 

반응형
반응형

 * 2020년 9월에 작성 된 Tool의 개발 로그입니다.

회사에서 사용하고 있는 Jenkins 가 간혹 이상 증상을 일으키는데, 그 중 하나는 Windows OS 를 사용하는 Slave-node 들이 모두 off-line 으로 변경 된다.

- 참고로 Jenkins 를 Master-Slave node 로 구성하는 내용은 다른 블로그에 많이 적혀 있으므로 여기서는 생략하며 아래 링크에 간략한 구성도 포함되어 공유한다. 

- https://velog.io/@doontagi/Jenkins-Master-Slave-%EA%B5%AC%EC%84%B1 

 

Jenkins Master / Slave 구성

젠킨스는 주로 소프트웨어 통합 서비스를 위해 사용된다. 이외에도 주기적인 빌드, 배포가 필요한 Batch Job을 수행하는데 활용될 수 있다.이 과정에서 하나의 Jenkins 인스턴스에 부하가 생길 수 있

velog.io

다시 본론으로 돌아오면,

사용 중 인 Jenkins 버전 (2.13) 버그여서, Jenkins 를 상위 버전으로 업그레이드를 하게 되면 없어지는 현상이지만, 해당 Jenkins 에 등록되어 배포 중 인 프로젝트들도 많고 (20+), 설치 된 플러그인들 중, 해당 버전의 Jenkins 에서는 잘 사용 중이지만, 상위 버전에서는 지원이 끊겨, 업데이트를 시도 했다가, 여러 문제가 발생하여 다시 현재 버전의 Jenkins 로 롤백하게 되었고, 버그 발생도 감안하며 사용 중 이다.

이 후 2.13 버전의 Jenkins 서비스엔 새로운 개발 프로젝트 등록은 하지 않기로 결정이 났고, 사용 중인 프로젝트들만 요청 시 따로 분리하는 작업을 진행 했지만 원치 않는 부서들이 더 많아서, 오류 발생 연락이 왔을 때, 바로 조치를 취해 줘야 한다.

Windows OS 를 사용하는 Slave-Node 들이 모두 off-line 으로 변경되는 오류 발생 시 취해야 하는 방법은, 인터넷 검색을 통해 다음과 같이 정리 되었다. 

  1. safeRestart : https://jenkins.url.com/safeRestart 를 실행 해 준다. CentOS 등을 사용하는 Slave-agent 들이 돌고 있을 수도 있어서 해당 agent 들의 job 이 완료되면 Jenkins 서비스 재실행이 될 수 있도록 한다. (Windows OS 의 Slave-node 로 배포 하는 프로젝트 들은 강제로 끊어줘야 한다. - 배포 job 실행 후 slave node 가 off-line 이여서 slave node 가 on-line 이 되기 를 기다리며서 job 진행 중이 되므로, safeRestart가 불가능하다. 
  2. Jenkins 서비스가 멈춘 뒤, 재시작이 진행 되었을 때, 거의 100 이면 100, 모두 오류가 나면서 서비스 재시작 실패를 하는데, 주로 다음과 같은 오류가 난다. 빨간색으로 표기한 lastStable 이라는 폴더 외에도 lastStable 이라를 폴더들 관련 ioException 오류들이 표시됩니다.
더보기
hudson.util.HudsonFailedToLoad: org.jvnet.hudson.reactor.ReactorException: java.lang.Error: java.lang.reflect.InvocationTargetException
    at hudson.WebAppMain$3.run(WebAppMain.java:244)
Caused by: org.jvnet.hudson.reactor.ReactorException: java.lang.Error: java.lang.reflect.InvocationTargetException
.....
Caused by: java.lang.Error: java.lang.reflect.InvocationTargetException
.....
Caused by: java.lang.reflect.InvocationTargetException
.....
    ... 8 more
Caused by: java.io.IOException: Tried to treat 'D:\Jenkins\jobs\2. Project_Deploy\modules\etp$etp\lastStable' as a directory, but could not get a listing
    at com.github.mjdetullio.jenkins.plugins.multibranch.TemplateDrivenMultiBranchProject.getConfigFiles(TemplateDrivenMultiBranchProject.java:717)
    ... 23 more

3. 이 상태에서는 Jenkins 서비스가 재시작을 계속 실패한다. 이때, %Jenkins HOME%/jobs 폴더 내 프로젝트 구성 폴더들 안에 있는 lastStable, lastSuccessful 폴더들을 모두 검색하여 삭제해야 한다. 이후 Jenkins 서비스를 재시작을 하면 성공적으로 서비스가 재시작 및 실행 중인 걸 확인 할 수 있다.

한동안은 위의 1,2,3 번 스텝을 slave-node 가 off-line 되는 현상이 있을 때마다 수작업으로 진행 하였는데, 서버 접속 해서 cmd 열고, 서비스 stop 명령어 전달 하고 서비스가 멈추고 나면, 3번 스텝에 언급된 폴더들을 검색하여 삭제 하고, jenkins 서비스의 재시작 누른 후.. 실행을 확인 하게 되는데, 걸리는 시간이 서버 및 네트워크 상태에 따라 족히 15분-30분까지도 걸리는 작업이다. 

정규 배포 날에 해당 문제가 발생하면 이전 버전을 그냥 사용하면서 배포 가능한 시간을 기다릴 수 있지만, 오류패치 배포 등 긴급한 상황인 경우엔 15분도 너무 긴 시간이다. 그래서 클릭 한번으로 서비스 내리고, 폴더들 삭제 하고, 다시 서비스를 올리는 프로그램이 필요했고, 그래서 다음과 같이 만들게 되었다.

출처: https://www.csc.gov.sg/articles/how-to-build-good-software

결론적으로는 프로그램 실행 후 모든 작업이 완료되기까지 소요되는 시간은 평균적으로 2분에서 3분정도 이며 (대부분 미만의 시간이 걸림), 이후 서비스 재시작하기까지 2-3분정도 걸린다. 빠르게 오류 발생 시 대처할 수 있게 되었다.

자 이제부터는 프로그램 코드를 공유 한다. 프로그램 언어는 c# 이며, 사용된 닷넷 버전은 4.0 이다.

서버에서 정기적으로 해당 어플리케이션(.exe) 을 실행 하여 젠킨스 서비스를 재시작하여 불시의 오류를 대비하기 위해 만드는 프로그램이여서, 콘솔앱이며 진행사항을 콘솔 및 로그 파일에 기록한다. - 하지만 불필요하게 서비스를 재시작하기 때문에, 오류 발생 시에만 사용하기로 정하였음.

이제 만들 프로그램은, 기본적으로 app.config 에 들어 있는 위의 정보들을 사용하여, 순차적으로 실행된다. Class 가 2개가 있는데 하나는 ServiceControlCore 라고 이름은 거창하지만, Jenkins 라는 서비스이름 과 타임아웃 시간을 받아 생성되는 객체다. FolderControlCore 역시 이름만 거창할 뿐, 상위 폴더 이름과 삭제할 타겟 폴더 이름을 받아, 폴더 검색, 폴더 확인 및 폴더 삭제를 진행 한다. 

A.     설치된 서버 에서 Windows Services 에 등록되어 있는 특정 서비스 검색 (serviceNameJenkins ) 후 서비스가 실행 중인지 확인한다. bool 값을 리턴받아 다음 단계를 진행한다. 

B.     실행 중인 Jenkins 서비스(serviceName )를 멈춘다. 멈추고(Stop)  20초 timeout 시간을 걸어주는데, 서비스가 멈추는데 걸리는 시간이 대략 최대 20초 정도이다.

C. 프로그램 상단에 생성 시 객체에 지정 해 준 상위 폴더 (upperDirectoryName = %Jenkins HOME% ) 이름 아래에 있는 삭제할 타겟 폴더 (deleteFolderNameList ) 들을 하나 씩 전달하여 타겟 폴더 갯수 만큼 순차적으로 검색 후 삭제를 진행 한다. 

D. Jenkins 서비스 (serviceName=Jenkins)  를 시작한다.( 시작 후 최대 대기 시간 20초)

E.     Log 추가 (NLog 패키지 활용)

NLog 패키지 설정 및 활용에 대해서는 따로 설명은 생략 하므로 잘 작성 된 블로그를 공유한다.

- 참조: https://icodebroker.tistory.com/9402

 

[C#/NLOG] NLOG 사용하기

▶ NLog.config ​ ※ 상기 파일 속성을 아래와 같이 설정한다. 빌드 작업 : (없음) 출력 디렉터리에 복사 : 새 버전이면 복사 ▶ Program.cs using System; using NLog; namespace TestProject { /// /// 프로그램 /// class

icodebroker.tistory.com

더보기
  1. 프로그램 시작
  2. 서비스 확인
  3. 서비스 멈춤 여부
  4. 지운 폴더 리스트 및 개수
  5. 서비스 시작
  6. 그 외 오류

이로써 프로그램은 잘 만들어졌고, 이번 프로그램에서 작성 한 FolderControlCore 와 ServiceControlCore 에 대해서 다음에 좀더 자세히 살펴 보기로 한다.

-끝-

youp-han/Jenkins-Restart: it restarts the running Jenkins Service locally (github.com)

 

GitHub - youp-han/Jenkins-Restart: it restarts the running Jenkins Service locally

it restarts the running Jenkins Service locally. Contribute to youp-han/Jenkins-Restart development by creating an account on GitHub.

github.com

 

반응형
반응형

//예제 샘플 : https://gist.github.com/vlucas/2bd40f62d20c1d49237a109d491974eb?permalink_comment_id=2981938  

//샘플 변경 내용: const ENCRYPTION_KEY  대신 GetHashKey() 를 사용하여 OS 에 로그인 한 사용자 이름으로 Hash 코드 생성 후 필요한 길이 만큼만 사용하여 암호화/복호화에 사용

//////////////////////////////////////////////////////////////////////////////////////////////////
//  string -> encrypt
//  encrypted string -> decrypt
//////////////////////////////////////////////////////////////////////////////////////////////////

const crypto = require('crypto');
const os = require('os');

//(예제코드) const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 256 bits (32 characters)
 
const IV_LENGTH = 16; // For AES, this is always 16


module.exports = {

    //암호화
    encryptHandler : function(text)
    {
         let iv = crypto.randomBytes(IV_LENGTH);
        //(예제코드) let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY), iv);
         let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(GetHashKey()), iv);
         let encrypted = cipher.update(text);
         encrypted = Buffer.concat([encrypted, cipher.final()]);
         return iv.toString('hex') + ':' + encrypted.toString('hex');
    },

    //복호화
    decryptHandler : function(text)
    {
        let textParts = text.split(':');
        let iv = Buffer.from(textParts.shift(), 'hex');
        let encryptedText = Buffer.from(textParts.join(':'), 'hex');
        //(예제코드)  let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY), iv);
        let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(GetHashKey()), iv);
        let decrypted = decipher.update(encryptedText);      
        decrypted = Buffer.concat([decrypted, decipher.final()]);
        return decrypted.toString();
    }
 
}

//사용자 이름으로 Hash 값 발생
//발생 후 32 길이만 사용
function GetHashKey()
{    
    var result = crypto.createHash('sha256').update(os.userInfo().username).digest('hex');
    result = result.substring(0, 32);
    console.log("gethashkey -> hash = "+ result);
    return result;
}

 

반응형
반응형

Spring 웹싸이트 : www.spring.io   

 

Spring makes Java simple.

Level up your Java code and explore what Spring can do for you.

spring.io

  1. Goals
    1. Java POJO
    2. DI - loose coupling
    3. AOP
    4. Minimize boilerplate java code
  2. Core Containter: Bean Factory for creating beans
    1. Beans
    2. Core
    3. SpEL
    4. Context
  3. Infrastructure
    1. AOP (Ascpect Oriented Proggramming): Add Functionallity to objects - logging, security, transactions etc..
    2. Aspects
    3. Instrumentation
      1. Java agaents to remotely monitor your app with JMX (Java Management Extension)
    4. Messaging
  4. Data Access Later
    1. JDBC 
    2. ORM (hibernate): Object to Relational Mapping
    3. JMS (Java Message Service)
    4. Transactions: Makes heavy use of AOP behind the scenes
    5. OXM
  5. Web Layer (Spring MVC)
    1. Servlet
    2. WebSocket
    3. Web
    4. Portlet
  6. Test Layer (TDD - Test-Driven Development)
    1. Unit
    2. Integration
    3. Mock

 

반응형
반응형

[ref:] https://www.electronjs.org/docs/latest/api/browser-window#event-close

 

BrowserWindow | Electron

Create and control browser windows.

www.electronjs.org

const dialogPop = require('electron').remote.dialog;
// it starts with onbeforeunload with the boolean value true
let doNotCloseWindow = true;

window.onbeforeunload = (event) => {

    //if the boolean value is true, the window do not close
    if(doNotCloseWindow)
    {
        // equivalent to `return false` but not recommended
        // it will prevent the window to be closed
        event.returnValue = true; 
   
        //open popup
        const dialogOpts = {
            type: 'info',                
            title: 'close',
            message: 'r u sure?',
            buttons: ['OK', 'Cancel']
        };

        dialogPop.showMessageBox(dialogOpts).then((response) => {

            // if OK, response.resonse = 0, else 1
            if(response.response === 0){
                doNotCloseWindow = false;
                // it will call onbeforeunload again
                window.close();
            }      

        });
    }
    else
    {
        console.log('It should be closed')

        //the window closes no matter what.. so not sure if destroy() works
        window.destroy();
    }
}
반응형
반응형

하드 디스크가 2개 달린 PC 에 윈10 재설치 후, 부팅 드라이브는 C 로 잘 붙어 있는데, 다른 디스크는 D 였다가 부팅만 하면 안보이네요. 컴퓨터 관리-디스크 관리-드라이브 문자 및 경로지정-드라이브문자할당 으로 D 드라이브 문자를 선택 해 주는 방법을 반복하다 다음 해결책을 발견하여 공유 합니다.

1. 명령 프롬프트를 실행합니다.

2. "diskpart"를 입력하여 diskpart에 접속합니다.

3. "list volume"을 입력하여 현재 문제를 겪고 계신 드라이브의 volume 번호를 확인합니다.

4. 문제가 있는 volume의 번호를 x라고 했을 때, "select volume=x"를 입력하여 해당 volume을 선택합니다.

- 예를 들어, 문제를 겪고 계신 volume이 volume 2인 경우, "select volume=2"를 입력하시면 됩니다.

5. "attributes volume"을 입력하여 해당 volume의 "숨김" 혹은 "기본 드라이브 문자 없음" 속성값이 "예"인 속성을 확인합니다.

6-1. "숨김" 속성의 값이 "예"인 경우, "attributes volume clear hidden"을 입력합니다.

6-2. "기본 드라이브 문자 없음" 속성의 값이 "예"인 경우, "attributes volume clear nodefaultdriveletter"를 입력합니다.

6-3. 두 속성 모두 "예"인 경우, 6-1과 6-2를 모두 입력합니다.

7. "attributes volume"을 다시 입력하여 해당 volume의 "숨김" 혹은 "기본 드라이브 문자 없음" 속성의 값이 "아니요"로 변경되었는지 확인합니다.

8. 정상적으로 변경되었다면, "exit"를 입력하여 diskpart에서 빠져나오신 후, 명령 프롬프트를 종료하시고 재부팅하여 해당 드라이브의 드라이브 문자가 자동적으로 할당되는지 확인합니다.

 

반응형
반응형

반응형
반응형
반응형
반응형
반응형
반응형

stackoverflow.com/questions/43687058/how-do-i-include-nuget-packages-in-my-solution-for-net-core-projects

 

How do I include NuGet packages in my solution for .Net Core projects?

With classic .Net projects, if I added a reference to a NuGet package, it would get downloaded to a packages folder and I could check that into source control along with the rest of my code. This

stackoverflow.com

sln 파일이 있는 곳에 NuGet.Config 파일을 만들어 다음 내용을 입력 후, 저장하고, 솔루션을 다시 오픈 하면 된다.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <config>
    <add key="globalPackagesFolder" value=".\packages" />
  </config>
</configuration>

반응형

+ Recent posts