반응형

다음 스크립트가 동작하려면 별도로 sshpass를 install해야함.

젠킨스서버, 대상 클라이언트 서버 모두 설치되어있어야함.

pipeline {
    agent any
    environment {
        REMOTE_HOST = "[클라이언트주소]"
        REMOTE_USER = "root"
        REMOTE_PASS = "[비밀번호]"
        GIT_REPO = "http://[깃리포지토리주소].git"
    }
    stages {
        stage('Deploy to Rocky Linux via SSH') {
            steps {
                sh """
                sshpass -p '$REMOTE_PASS' ssh -o StrictHostKeyChecking=no $REMOTE_USER@$REMOTE_HOST <<'EOF'
                set -e
                cd [패키지경로]
                
                # Git 업데이트
                git pull

                # Docker 빌드 및 배포
                docker build -t [패키지명]:latest .

                # 기존 컨테이너 중지 및 삭제
                docker stop [패키지명] || true
                docker rm [패키지명] || true
                
                cd /root
                nohup bash ./run_docker.sh > /dev/null 2>&1 &

                """
            }
        }
    }
}
728x90
반응형

회사에서 젠킨스를 이용해서 자동 배포환경을 구축하는 과정에서 젠킨스에서 도커의 -it 옵션때문에 쉘스립트를 이용해서 도커를 실행하도록 작업하였습니다.
(tty에러가 나는데 그건 도커 bash는 interactive하게 클라이언트와 통신을 해야하기 때문에 발생한다고 한다.)

핵심은 -dit 옵션이고 -d옵션이 데몬을 의미하며 백그라운드로 실행되게 하는 옵션이다.

저렇게하면 도커를 실행해도 도커 내부 bash로 진입하지 않는다.

그리고 docker exec를 이용해서 도커 내부 bash에 명령어를 실행하도록 작업하여 도커 컨테이너 내부에 DB와 웹서버를 실행하도록 스크립트를 작성하였다.

#!/bin/bash

docker stop [DOCKER_NAME] 2>/dev/null
docker rm [DOCKER_NAME] 2>/dev/null

docker run -dit -p 8081:8081 -p 27017:27017 --name ds_ml -v mongodb:/data/db [DOCKER_NAME]:latest

docker exec [DOCKER_NAME] bash -c "source ~/.bashrc && conda activate myenv && mongod --bind_ip 0.0.0.0 --port 27017 --dbpath /data/db &"

docker exec [DOCKER_NAME] bash -c "source ~/.bashrc && conda activate myenv && uvicorn main:app --host 0.0.0.0 --port 8081 > /var/log/uvicorn.log 2>&1"

 

728x90
반응형

ForkJoinPool은 병렬 처리를 최적화하기 위해 Java 7에서 도입된 고급 멀티스레딩 도구로, 큰 작업을 작은 작업으로 분할(Fork)하고 여러 스레드에서 동시에 처리하여 그 결과를 합친(Join) 후 반환하는 방식으로 동작합니다. 주로 대규모 작업을 병렬로 처리할 때 성능을 최적화하는 데 사용됩니다.

1️⃣ 기본 개념

  • Fork: 큰 작업을 작은 작업으로 나누어 병렬 처리.
  • Join: 병렬로 처리된 작은 작업들을 다시 합쳐 최종 결과 생성.

ForkJoinPool은 분할-정복 알고리즘(divide and conquer)을 사용하여 대규모 작업을 효율적으로 처리합니다. 이를 통해 병렬성을 극대화하고, 여러 코어를 활용하여 성능을 향상시킬 수 있습니다.

2️⃣ ForkJoinPool 사용 이유

✅ 병렬 작업을 쉽게 처리

여러 개의 작은 작업들을 여러 스레드에서 동시에 처리할 수 있게 해줍니다. 일반적인 스레드 풀을 사용할 때보다 효율적으로 자원을 관리할 수 있습니다.

✅ 분할-정복 패턴

큰 작업을 여러 개의 작은 작업으로 나누어 동시에 처리하고, 그 결과를 합칩니다. 예: 병렬 계산, 대규모 데이터 처리 등.

3️⃣ ForkJoinPool 구조

ForkJoinPool은 기본적으로 두 가지 작업을 처리하는 방식으로 설계되었습니다:

  1. Work Stealing: 각 스레드는 자신의 작업 큐를 가집니다. 만약 스레드가 자신의 작업을 다 끝내면, 다른 스레드의 큐에서 작업을 빼와서 처리할 수 있습니다. 이 방법은 로드 밸런싱을 자연스럽게 제공합니다.
  2. RecursiveTask (결과를 반환하는 작업): 반환값이 있는 작업을 할 때 사용됩니다. 예: ForkJoinTask에서 fork()로 작업을 분할하고 join()으로 결과를 합칩니다.
  3. RecursiveAction (결과를 반환하지 않는 작업): 반환값이 없는 작업을 할 때 사용됩니다. 예: 데이터 변환, 파일 쓰기 등.

4️⃣ ForkJoinPool 클래스

ForkJoinPool은 java.util.concurrent 패키지의 클래스이며, 스레드를 동적으로 관리하고 작업을 분할하여 효율적으로 실행합니다. 기본적으로 fork와 join 작업을 관리하는 작업 큐를 사용합니다.

기본 사용 방법:


import java.util.concurrent.*;

public class ForkJoinExample {
    public static void main(String[] args) {
        // ForkJoinPool 생성
        ForkJoinPool pool = new ForkJoinPool();

        // 작업 제출
        RecursiveTask task = new MyRecursiveTask(10);
        Integer result = pool.invoke(task);  // 작업 실행 및 결과 대기

        // 결과 출력
        System.out.println("Result: " + result);
    }
}

class MyRecursiveTask extends RecursiveTask {
    private int n;

    public MyRecursiveTask(int n) {
        this.n = n;
    }

    @Override
    protected Integer compute() {
        if (n <= 1) {
            return 1;
        }

        MyRecursiveTask task1 = new MyRecursiveTask(n - 1);
        task1.fork();  // task1을 병렬로 실행

        return n * task1.join();  // 결과를 합쳐서 반환
    }
}

설명:

  • ForkJoinPool pool = new ForkJoinPool(); - ForkJoinPool 인스턴스를 생성하여, 작업을 이 풀에 제출할 수 있습니다.
  • RecursiveTask task = new MyRecursiveTask(10); - RecursiveTask를 상속한 작업을 정의. 여기서는 1부터 10까지의 팩토리얼을 계산하는 예시입니다.
  • task.fork(); - fork() 메서드를 사용하여 작업을 병렬로 실행합니다.
  • task.join(); - join() 메서드를 사용하여 작업의 결과를 기다리며 받습니다.

5️⃣ ForkJoinPool 주요 메서드

  • fork(): 작업을 비동기적으로 실행시킵니다. 작업이 즉시 시작되며, 현재 스레드는 계속 실행됩니다.
  • join(): 작업이 완료될 때까지 결과를 기다립니다. fork()로 실행된 작업의 결과를 기다리며 받습니다.
  • invoke(): fork()와 join()을 결합한 메서드입니다. 작업을 제출하고, 결과가 반환될 때까지 기다립니다.

6️⃣ ForkJoinPool 장점

✅ 성능 최적화

CPU 코어를 효율적으로 활용하여 병렬 처리가 가능합니다. 작은 작업을 분할하고 동시에 처리하여 성능 향상.

✅ 동적 스레드 관리

동적 스레드 할당 및 작업 큐 관리가 효율적으로 이루어집니다. 필요시 다른 스레드가 대기 중인 작업을 처리할 수 있습니다.

✅ 스케일링 (Scale-out)

ForkJoinPool은 스레드 풀의 크기를 동적으로 조정하면서 여러 작업을 처리할 수 있기 때문에, 대규모 데이터 처리 및 병렬 작업에서 강력한 성능을 발휘합니다.

7️⃣ ForkJoinPool 사용 예시

팩토리얼 계산 예시 (재귀적으로 분할)


import java.util.concurrent.*;

public class ForkJoinExample {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        FactorialTask task = new FactorialTask(10);
        Integer result = pool.invoke(task);
        System.out.println("Factorial of 10 is: " + result);
    }
}

class FactorialTask extends RecursiveTask {
    private final int n;

    FactorialTask(int n) {
        this.n = n;
    }

    @Override
    protected Integer compute() {
        if (n == 1) {
            return 1;
        } else {
            FactorialTask subTask = new FactorialTask(n - 1);
            subTask.fork();  // 재귀적으로 작업 분할
            return n * subTask.join();  // 하위 작업의 결과를 합산
        }
    }
}

8️⃣ 결론

ForkJoinPool은 병렬 처리에 최적화된 Java 클래스입니다. 작은 작업들을 병렬로 처리하여 성능을 극대화합니다. 특히 대규모 데이터 처리나 분할-정복 알고리즘을 사용할 때 매우 유용하며, 동적 스레드 관리와 작업 큐 최적화 덕분에 효율적으로 멀티스레딩 작업을 처리할 수 있습니다.

728x90

+ Recent posts