본문 바로가기
프로그래밍/젠킨스(Jenkins)

Groovy 스크립트로 도커 환경 젠킨스(Jenkins) 초기 설정하기

by bantomak 2024. 12. 20.
반응형

들어가면서(Intro)

CI/CD를 위해서 많은 사람들이 젠킨스를 활용한다. 젠킨스는 유저에게 강력한 GUI를 제공하여 편하게 설정을 추가하고 제거하는 것이 강점이다. 하지만 한번 설정한 후에는 거의 변경되지 않기 때문에 실수로 젠킨스 설정이 초기화되거나 하면 해당 설정이 기억나지 않아서 매우 난감한 상황이 벌어진다. 그래서 이런 상황을 피하기 위해서 세팅 방법을 따로 문서로 저장해 두거나 jenkins_home을 따로 저장해 두는 것도 방법이지만 이는 좋은 방법은 아닐 것이다. 좋은 방법은 초기 설정을 스크립트로 작성해서 관리하면 언제든지 도커로 매번 동일한 설정으로 젠킨스를 재설치할 수 있으며 해당 스크립트로 형상 관리 툴에 저장해서 이력을 관리하기 쉬울 것이다.

설정을 위해 Groovy 스크립트를 사용하는 이유(Why Groovy Scripts)

젠킨스는 Java로 작성되었으며, Groovy는 내부적으로 JVM 기반 스크립팅 언어로 Java와의 통합성이 뛰어나다. 그리고 안타깝게도 젠킨스 초기 설정 스크립트는 Groovy 언어만을 지원한다. 이는 젠킨스의 내부 API와 상호작용할 수 있는 기본 언어로 안타깝게도 Groovy가 선택되었기 때문이다.

Dockerfile에서 Groovy 초기화 스크립트 복사

로컬에서 나에게 맞는 내용으로 수정한 스크립트가 컨테이너 실행시 추가될 수 있도록 Dockerfile에서 추가해주도록 하자.

FROM jenkins/jenkins:lts

USER root

# Groovy 스크립트 복사
COPY init.groovy.d/ /usr/share/jenkins/ref/init.groovy.d/

USER jenkins

프로젝트 생성 스크립트(Project Add Scripts)

젠킨스 메뉴 '새로운 Item' 메뉴로 추가 가능한 항목을 '프로젝트'라고 한다. 프로젝트는 젠킨스가 제공하는 가장 기본적인 유형이며 단순한 빌드, 테스트, 배포 작업을 설정하는데 적합하다.

import jenkins.model.*
import hudson.model.*

// Jenkins 인스턴스 가져오기
def instance = Jenkins.getInstance()

// 프로젝트 이름 지정
def projectName = "MyFreestyleProject"

// Freestyle Project 생성
def freestyleProject = new FreeStyleProject(instance, projectName)
instance.add(freestyleProject)
println "Freestyle Project '${projectName}' has been created."

// 프로젝트 저장
freestyleProject.save()
println "Freestyle Project '${projectName}' has been saved."

Subversion & Credential 실행 스크립트(Subversion & Credential Setting Scripts)

형상 관리 툴인 Subversion을 실행해서 저장소에서 최신의 데이터를 가져와서 여러가지 작업이 가능하다.

import jenkins.model.*
import hudson.model.*
import hudson.scm.*
import hudson.scm.subversion.*

import com.cloudbees.plugins.credentials.CredentialsScope
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
import com.cloudbees.plugins.credentials.domains.Domain
import org.jenkinsci.plugins.credentialsbinding.impl.*

// Subversion Credentials 추가
def svnUsername = ""   // Subversion 사용자 이름
def svnPassword = "" // Subversion 비밀번호
def credentialsId = "" // Jenkins에서 참조할 ID

def svnCredentials = new UsernamePasswordCredentialsImpl(
    CredentialsScope.GLOBAL,     // 범위
    credentialsId,               // ID
    "",                          // 설명
    svnUsername,                 // 사용자 이름
    svnPassword                  // 비밀번호
)

// Credentials 저장
credentialsStore.addCredentials(Domain.global(), svnCredentials)
println("Subversion credentials added with ID '${credentialsId}'.")

// Jenkins 인스턴스 가져오기
def instance = Jenkins.getInstance()

def jobName = "job-name"
def job = instance.getItem(jobName)

if (job == null) {
    println("Creating new Freestyle project: ${jobName}")

    // Freestyle 프로젝트 생성
    job = new FreeStyleProject(instance, jobName)
    instance.add(job, jobName)

    // Subversion 설정
    def depthOption = "infinity"

    // SubversionSCM.ModuleLocation 객체 생성
    def moduleLocation = new SubversionSCM.ModuleLocation("http://svn.co.kr/svn", credentialsId, "./", depthOption, true, true)

    // SubversionSCM 객체 생성
    def svnSCM = new SubversionSCM(
        [moduleLocation], // ModuleLocation 리스트
        null, // workspaceUpdater
        null, // browser
        "",  // excludedRegions
        "",  // excludedUsers
        "",  // excludedRevprop
        "",  // excludedCommitMessages
        "",  // includedRegions
        false, // ignoreDirPropChanges
        false, // filterChangelog
        null,  // additionalCredentials
        true  // quietOperation
    )

    job.setScm(svnSCM)

    // 빌드 환경 설정: Use secret text(s) or file(s) - Username and Password (separated)
    def usernamePasswordBinding = new UsernamePasswordMultiBinding(
        "svn_username", // 사용자 이름이 바인딩될 환경 변수 이름
        "svn_password", // 비밀번호가 바인딩될 환경 변수 이름
        "credential_ID" // Jenkins Credentials ID
    )

    def buildWrapper = new SecretBuildWrapper([usernamePasswordBinding])
    job.getBuildWrappersList().add(buildWrapper)

    job.save()
    println("Freestyle project '${jobName}' created with Subversion checkout configuration.")
} else {
    println("Freestyle project '${jobName}' already exists.")
}
 

SubversionSCM.ModuleLocation (Jenkins Subversion Plug-in 1281.vc8837f91a_07a_ API)

All Implemented Interfaces: Describable , Serializable Enclosing class: SubversionSCM small structure to store local and remote (repository) location information of the repository. As a addition it holds the invalid field to make failure messages when doin

javadoc.jenkins.io

파워쉘 설정 및 실행 스크립트(PowerShell Execute Scripts)

import jenkins.model.*
import hudson.model.*
import hudson.plugins.powershell.PowerShell

def jobName = "powershell-job"
def job = instance.getItem(jobName)

if (job == null) {
    println("Creating new Freestyle project: ${jobName}")

    // Freestyle 프로젝트 생성
    job = new FreeStyleProject(instance, jobName)
    instance.add(job, jobName)

    // PowerShell Build Step 추가
    def powershellCommand = '''스크립트를 이곳에 추가'''.stripIndent()
    def powershellStep = new PowerShell(powershellCommand, true, true, 0)
    job.buildersList.add(powershellStep)

    job.save()
    println("Freestyle project '${jobName}' created with Subversion checkout configuration.")
} else {
    println("Freestyle project '${jobName}' already exists.")
}
 

PowerShell (Jenkins PowerShell plugin 2.2 API)

 

javadoc.jenkins.io

Publish Over SSH 설정 스크립트(Publish Over SSH Setting Scripts)

def jobName = "job-name"
def job = instance.getItem(jobName)

if (job == null) {
    println "Creating project '${jobName}'"

    job = new FreeStyleProject(instance, jobName)
    instance.add(job, jobName)

    def command = '''실행 커맨드 내용 추가'''.stripIndent()

    // **Step 2: Configure SSH Publisher for file transfer**
    def transferSet = new BapSshTransfer(
        "",                            // sourceFiles
        "",                            // excludes
        "",                            // remoteDirectory
        "",                            // removePrefix
        false,                         // remoteDirectorySDF
        false,                         // flatten
        command,            		   // execCommand
        120000,                        // execTimeout
        false,                         // usePty
        false,                         // keepFilePermissions
        false,                         // noDefaultExcludes
        false,                         // makeEmptyDirs
        "[, ]+"                        // patternSeparator
    )

    // SSH Publisher 설정
    def sshPublisher = new BapSshPublisher(
        "",                // Jenkins에 등록된 SSH 서버 이름
        true,              // verbose
        [transferSet],     // Transfer Set
        false,
        false,
        null,
        null,
        null
    )

    def sshPublisherPlugin = new BapSshPublisherPlugin(
        [sshPublisher],
        false,
        false,
        false,
        "",
        null
    )

    // 빌드 스텝에 추가
    job.publishersList.add(sshPublisherPlugin)

    job.save()
    println "Project '${jobName}' created successfully with Copy Artifacts and SSH Execution steps."
} else {
    println "Project '${jobName}' already exists."
}
 

BapSshTransfer (Publish Over SSH 383.v4eb_4c44da_2dd API)

java.lang.Object jenkins.plugins.publish_over.BPTransfer jenkins.plugins.publish_over_ssh.BapSshTransfer All Implemented Interfaces: Describable , Serializable See Also: Nested Class Summary Nested classes/interfaces inherited from class jenkins.plugins.p

javadoc.jenkins-ci.org

.NET 빌드해서 빌드 결과물 아티팩트로 보관하기(Dotnet Build and Build Archive to Artifact)

def jobName = "project-build"
def job = instance.getItem(jobName)

if (job == null) {
    println("Creating new Freestyle project: ${jobName}")

    job = new FreeStyleProject(instance, jobName)
    instance.add(job, jobName)

    def credentialsId = "credential-id" // Jenkins Credentials ID
    def depthOption = "infinity"

    // SubversionSCM.ModuleLocation 객체 생성
    def moduleLocation = new SubversionSCM.ModuleLocation("http://svn/svn/", credentialsId, ".", depthOption, true, true)

    // SubversionSCM 객체 생성
    def svnSCM = new SubversionSCM(
        [moduleLocation], // ModuleLocation 리스트
        null, // workspaceUpdater
        null, // browser
        "",  // excludedRegions
        "",  // excludedUsers
        "",  // excludedRevprop
        "",  // excludedCommitMessages
        "",  // includedRegions
        false, // ignoreDirPropChanges
        false, // filterChangelog
        null,  // additionalCredentials
        false  // quietOperation
    )

    // Check-out Strategy 설정: Emulate Clean Checkout
    svnSCM.setWorkspaceUpdater(new UpdateWithCleanUpdater())

    job.setScm(svnSCM)

    // .NET Publish Project 빌드 스텝 추가
    def command = '''dotnet publish ./ `
--configuration Release `
--framework net8.0 `
--runtime linux-x64 `
--self-contained false `
--output ./Bin `
-p:PublishSingleFile=true'''.stripIndent()
    def step = new PowerShell(command, true, true, 0)
    job.buildersList.add(step)

    // 빌드 후 조치: Archive the artifacts 설정 추가
    def artifactArchiver = new ArtifactArchiver("") // 경로 추가
    artifactArchiver.setExcludes("");
    artifactArchiver.setOnlyIfSuccessful(true) // 빌드 성공 시에만 아카이브 수행 (옵션)
    artifactArchiver.setDefaultExcludes(true)
    artifactArchiver.setCaseSensitive(true)
    artifactArchiver.setFollowSymlinks(false)
    
    // 빌드 후 조치에 추가
    job.getPublishersList().add(artifactArchiver)

    job.save()
    println("Freestyle project '${jobName}' created with Subversion checkout configuration.")
} else {
    println("Freestyle project '${jobName}' already exists.")
}
 

ArtifactArchiver (Jenkins core 2.490 API)

All Implemented Interfaces: ExtensionPoint, Describable , BuildStep, SimpleBuildStep Copies the artifacts into an archive directory. Author: Kohsuke Kawaguchi Nested Class Summary Nested Classes Field Summary Fields Constructor Summary Constructors Method

javadoc.jenkins.io

다른 프로젝트에서 아티팩트 복사 허용하기(CopyArtifact Permission to Another Project)

job = new FreeStyleProject(instance, jobName1)
instance.add(job, jobName1)

// 아티팩트를 복사할 수 있는 프로젝트 이름 추가
def permissionProperty = new CopyArtifactPermissionProperty("target-project")
// 대상 프로젝트에 권한 속성 추가
job.addProperty(permissionProperty)
 

CopyArtifactPermissionProperty (Copy Artifact Plugin 757.v05365583a_455 API)

All Implemented Interfaces: ExtensionPoint, Describable >, ReconfigurableDescribable >, BuildStep public class CopyArtifactPermissionProperty extends JobProperty > Job Property to define projects that can copy artifacts of this project. Nested Class Summar

javadoc.jenkins.io

다른 프로젝트에서 아티팩트 복사하기(Copy Artifact From Another Project Scripts)

def jobName = "job-name"
def job = instance.getItem(jobName)

if (job == null) {
    println "Creating project '${jobName}'"

    job = new FreeStyleProject(instance, jobName)
    instance.add(job, jobName)

    // 빌드 환경 세팅
    def preBuildCleanup = new PreBuildCleanup(null, false, "", "", false)
    job.getBuildWrappersList().add(preBuildCleanup)

    // **Step 1: Copy artifacts from another project**
    def copyArtifactStep = new CopyArtifact("project-name")

    // build selector 세팅
    def buildselector = new StatusBuildSelector()
    buildselector.setStable(false)

    copyArtifactStep.setSelector(buildselector)

    // 빌드 스텝에 추가
    job.buildersList.add(copyArtifactStep)

    job.save()
    println "Project '${jobName}' created successfully with Copy Artifacts and SSH Execution steps."
} else {
    println "Project '${jobName}' already exists."
}
 

CopyArtifact (Copy Artifact Plugin 757.v05365583a_455 API)

All Implemented Interfaces: ExtensionPoint, Describable , BuildStep, SimpleBuildStep Build step to copy artifacts from another project. Author: Alan Harder Nested Class Summary Nested Classes Field Summary Constructor Summary Constructors Method Summary Co

javadoc.jenkins.io

반복적으로 빌드 유발 추가(Add Build Trigger)

import jenkins.model.*
import hudson.model.*
import hudson.triggers.*

// Jenkins 인스턴스 가져오기
def instance = Jenkins.getInstance()

// 프로젝트 이름
def projectName = "MyFreestyleProject"

// 기존 프로젝트 가져오기 또는 새로 생성
def project = instance.getItem(projectName)
if (project == null) {
    project = new FreeStyleProject(instance, projectName)
    instance.add(project)
    println "Freestyle Project '${projectName}' has been created."
} else {
    println "Freestyle Project '${projectName}' already exists."
}

// Build periodically 트리거 설정
def timerTrigger = new TimerTrigger("H/15 * * * *") // 매 15분마다 빌드
timerTrigger.start(project, true) // 트리거 초기화
project.addTrigger(timerTrigger)

// 프로젝트 저장
project.save()
println "Freestyle Project '${projectName}' has been saved with periodic build schedule."
 

TimerTrigger (Jenkins core 2.490 API)

All Implemented Interfaces: ExtensionPoint, Describable > Trigger that runs a job periodically. Author: Kohsuke Kawaguchi Nested Class Summary Nested Classes Field Summary Constructor Summary Constructors Method Summary All MethodsInstance MethodsConcrete

javadoc.jenkins.io

참고하면 좋은 사이트

 

Jenkins Plugins Javadoc

 

javadoc.jenkins-ci.org

댓글