하..이 개떡같은 티스토리...이미지 업로드하다 멈춰서 글 날리고 다시 작성하겠습느드..........
와 글 세번 날아가고 네번째 작성중인게 실화인가요..? 임시저장도 꼬박꼬박 했는데 임시저장에 제 글이 없는데요...?
티스토리 개발자들 반성합시다,,,이건 아니지않나요,,,이번에도 실패하면 velog로 갈아타서 편안한 마크다운라이프 즐기겠습니다
Jenkins 여러 Job 동시에 설정하기(configuration on multiple jobs), 여러 프로젝트에 파라미터 주입하기
엔터프라이즈 환경에서 젠킨스를 운영하다 보면 여러 잡(프로젝트)을 동시에 수정해야 할 일이 생깁니다. 아키텍쳐가 바뀌었다든가, 공통 인자가 추가되었다든가 하는 일들로요 ... 그런 일이 발생할 때마다 잡 하나하나에 들어가서 설정을 추가주는 건 정말 반복적이고 가치 없는 일입니다. 이 포스팅에서는 그걸 해결해 나가는 과정을 공유하려 합니다.
결론만 필요하시다면 결과물만 ← 이 단어를 검색하셔서 아래의 결론부로 직행하시기 바랍니다.
배포 로직 중 일부가 변경되어 운영중인 젠킨스 내 일부 잡들..
약 300개 정도의 잡들에 한 번에 파라미터 주입이 필요하게 되었습니다.
기존 배포 로직의 경우, 설정의 경우 Jenkinsfile의 Groovy 코드에서 Properties를 이용하여
일관되게 적용되도록 운영하고 있었습니다. Properties에 선언하여 적용할 경우, 설정을 해당 잡에서 변경하더라도
재배포 시 Jenkinsfile 내 로직이 수행되며 선언한 대로 엎어쳐(!) 지기 때문에 일관된 설정으로 운영하기 정말 편리합니다.
다만, 이것이 수시로 값이 변경되거나 해야 하는 Parameter 라면 이야기가 달라집니다.
말 그대로 선언한 형태로 정의되는 declarative 형태이므로 파라미미터를 그 안에 선언하면
파라미터 값을 변경하더라도 재배포(빌드)시 해당 값이 pipeline 안에 선언한 대로 원복되게 됩니다.
그러면 이제 드는 생각은,
- parameter를 받아서 동적으로 할당해 주면 안 되나? ⇒ 그 파라미터도 로직 돌 때 같이 날아간다니까요...?
- 변경/추가할 파라미터만 선언하면 안 되나? ⇒ 선언한 것만 남깁니다.
파라미터 a,b,c,d 있고 파이프라인 로직에서 b,c만 선언하면 a,d는 날아갑니다.
대충 이런 것들입니다.
그래서 고민을 좀 하다가, 기존의 설정값을 받아와서 거기에 append 하는 방식으로 코드를 변경하였습니다.
import groovy.json.*
import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
import org.jenkinsci.plugins.builduser.BuildUser
...
...
(이런저런 로직들)
...
node("노드명") {
if (이 부분은 조건분기인데 회사 로직이라 가렸습니당) {
// 프로젝트에 설정된 기존 파라미터 설정값을 가져와 existing 변수에 추가합니다.
existing = currentBuild.rawBuild.parent.properties
.findAll { it.value instanceof hudson.model.ParametersDefinitionProperty }
.collectMany { it.value.parameterDefinitions }
// INCREASE_VERSION 이라는 파라미터가 설정된 것이 없다면,
if (!env.INCREASE_VERSION) {
// choice parameter인 INCREASE_VERSION 을 existing에 추가한 값을
// jobParams에 저장합니다.
jobParams = [
choice(choices:['PATCH', 'MAJOR', 'MINOR'], name: 'INCREASE_VERSION')
] + existing
} else {
// INCREASE_PARAM 변수가 null이 아닌 경우에는 손대지 않습니다.
jobParams = existing
}
// properties 의 parameter 설정을 jobParams로 할당합니다.
properties([parameters(jobParams),
office365ConnectorWebhooks([msvariables]),
buildDiscarder(logRotator(daysToKeepStr: '370')),
compressBuildLog(),
disableConcurrentBuilds()])
} else {
// 위에 가려놓은 회사로직 분기 안 태우는 경우인데 마찬가지로 적용됩니다.
existing = currentBuild.rawBuild.parent.properties
.findAll { it.value instanceof hudson.model.ParametersDefinitionProperty }
.collectMany { it.value.parameterDefinitions }
if (!env.INCREASE_VERSION) {
jobParams = [
choice(choices:['PATCH', 'MAJOR', 'MINOR'], defaultValue: 'PATCH', name: 'INCREASE_VERSION')
] + existing
} else {
jobParams = existing
}
properties([parameters(jobParams),
office365ConnectorWebhooks([msvariables]),
disableConcurrentBuilds()])
}
println(params.INCREASE_VERSION)
}
위의 로직은 결국
- INCREASE_VERSION 변수가 있는가?
yes: 기존에 가지고 있던 파라미터값 그대로 properties 설정
(기존에 가지고 있는 파라미터 값들, 예를 들면 변경이 있었던 값들도 모두 반영됨)
no: 기존에 가지고 있던 파라미터 값에서 + choice parameter를 추가해 주고 + 이를 properties를 통해 반영
의 방식으로 동작합니다.
이렇게 적용하고, 부푼 마음으로 빌드를 돌려 보았습니다.
job configuration에 가 보니, 잘 추가된 parameter가 저를 반깁니다.
와! 작업 완료! 케이스 종결합니다
... 라는 생각을 할 때 젠킨스가 제 뒤통수를 세게 때렸습니다~~~!
여기서 발생한 문제는, Jenkins 잡 내에서 config 로 가 보면 설정은 반영되어 있지만
막상 잡을 실행하는 ui에 가서 build with parameters를 눌렀을 때에는 이 파라미터가 보이지 않는다는 것..^^
이 문제에 대해 찾아보니,
해당 파라미터를 추가하는 로직이 적용됨 → 새로운 설정이 생성만 되고 Jenkins UI단에서 이를 로드하지는 않음
→ 다시 빌드하면 그때 로드되면서 파라미터가 생성됨 + 로직이 다시 돌면서 파라미터가 없었네?
하고 파라미터를 추가해 줘서 파라미터가 두개 생성됨 이라는 버그 아닌 버그가 발생하는 것 같았습니다.
이 문제를 해결할 수 있는 방법은 ... 글쎄요 . . . . . . . . . .고질적인 문제라고 하는데 . . . . . .
눈 앞을 스쳐가는... 한땀한땀 장인정신으로 파라미터를 수정하는 내 모습 . . . .....
이건 진짜 아닌 것 같아서, 공통 pipeline에서 빼더라도 일괄로 기존 잡이라도
한번에 수정할 수 있는 방법을 찾아 헤매기 시작했습니다.
API 문서도 뒤져보고, 플러그인도 찾아보고...
기존에도 전체 잡을 하나씩 돌면서 프로젝트(잡)를 비활성화하거나 하는 잡은 있었는데,
이걸 파라미터에 적용한 케이스는 잘 없어서 찾는데 애를 먹었습니다.
그래서 나온 첫 번째 테스트 코드는 요거입니다.
import hudson.model.*
import com.cloudbees.*
injectParameter(Hudson.instance.items)
def injectParameter(items) {
for (item in items) {
if (item.class.canonicalName == 'com.cloudbees.hudson.plugins.folder.Folder') {
injectParameter(((com.cloudbees.hudson.plugins.folder.Folder) item).getItems())
} else {
if ( item.fullName.contains("작업을 수행할 프로젝트의 이름(contain으로 찾음) 일부") ) {
def paramDefProp = item.getProperty(ParametersDefinitionProperty.class);
if (paramDefProp != null) {
def parameters = new ArrayList<ParameterDefinition>();
parameters += paramDefProp.getParameterDefinitions();
parameters.add(new ChoiceParameterDefinition('version_selector', 'PATCH\nMINOR\nMAJOR','desc'));
item.addProperty(new ParametersDefinitionProperty(parameters));
item.save()
println(item.name)
}
}
}
}
}
오... 잘 돌겠는데?
잡은 success로 떨어지는데 안 돕니다. 하........... 로직도 다 맞는데 왜?
여기까지 온 이상 포기는 없습니다.
append가 아니라, 기존 값 리스트에 저장한 후 기존 값은 설정에서 싹 날려버리고 새로 선언해 주는 방식을 사용해 봅니다.
import hudson.model.*
import com.cloudbees.*
injectParameter(Hudson.instance.items)
def injectParameter(items) {
for (item in items) {
if (item.class.canonicalName == 'com.cloudbees.hudson.plugins.folder.Folder') {
injectParameter(((com.cloudbees.hudson.plugins.folder.Folder) item).getItems())
} else {
if ( item.fullName.contains("MAINTENANCE/PARAMTEST") ) {
// fullName.contains안에는 파라미터를 추가할 잡의 이름 일부 혹은 경로를 지정해 주시면 됩니다. 제 경우엔 폴더별로 적용했습니다
def paramDefProp = item.getProperty(ParametersDefinitionProperty.class);
if (paramDefProp != null) {
def parameters = new ArrayList<ParameterDefinition>();
parameters += paramDefProp.getParameterDefinitions();
parameters.add(new ChoiceParameterDefinition('version_selector', 'PATCH\nMINOR\nMAJOR',''));
item.removeProperty(ParametersDefinitionProperty.class);
item.addProperty(new ParametersDefinitionProperty(parameters));
item.save()
println(item.name)
}
}
}
}
}
와!
드디어 적용이 되기 시작했습니다!
이때의 기쁨이란...ㅠ 300개를 한 개씩 설정하지 않아도 된다니...
<<< 결과물만 필요하신 분들은 여기서부터 읽으시면 됩니다 >>>
이제 저 contains 부분을 변수로 설정했습니다.
import hudson.model.*
import com.cloudbees.*
injectParameter(Hudson.instance.items)
def injectParameter(items) {
for (item in items) {
if (item.class.canonicalName == 'com.cloudbees.hudson.plugins.folder.Folder') {
injectParameter(((com.cloudbees.hudson.plugins.folder.Folder) item).getItems())
} else {
if( item.fullName.contains("$target_folder") ) {
def paramDefProp = item.getProperty(ParametersDefinitionProperty.class);
if (paramDefProp != null) {
def parameters = new ArrayList<ParameterDefinition>();
parameters += paramDefProp.getParameterDefinitions();
parameters.add(new ChoiceParameterDefinition('version_selector', 'PATCH\nMINOR\nMAJOR',''));
item.removeProperty(ParametersDefinitionProperty.class);
item.addProperty(new ParametersDefinitionProperty(parameters));
item.save()
println(item.name)
}
}
}
}
}
이렇게 하면 $target_folder를 원하는 잡 이름 혹은 폴더이름 등을 넣고 돌리면,
조건에 맞는 잡에 version_selector라는 choice parameter가 주입됩니다.
로직 동작 방식은, 전체 잡 중 조건에 맞는 이름을 가진(혹은 조건에 맞는 폴더 하위에 있는) 잡들을 for문으로 돌면서
기존의 parameter 설정과 값을 arraylist에 담아서 넣고, choice parameter를 그 리스트에 추가합니다.
그 다음, 해당 잡으로부터 Parameters 설정을 다 날려버린 후
해당 잡의 파라미터 설정을 설정한 리스트 값으로 넣어 준 후, 설정합니다.... 라는 식으로 동작합니다.
적용한 모습은 이런 식으로 freestyle 잡에 추가하고, build with parameters로 타겟 설정해서 돌리시면
잘 동작하게 됩니다 😉
이건 비단 add parameter 뿐만 아니라 다양한 방식으로도 활용이 가능하니
mass config update가 필요하신 분들은 참고하시면 좋겠습니다 ~~
검색 키워드: jenkins mass configuration, jenkins configure multiple jobs, 젠킨스 설정 일괄 수정, 젠킨스 파라미터 일괄 추가
'DevOps' 카테고리의 다른 글
ARP Protocol은 어떻게 맥 어드레스를 쿼리하고 수신할까? (1) | 2021.08.16 |
---|---|
traceroute로 목적지까지 거치는 경로 확인하기 (0) | 2021.08.14 |
K8S(쿠버네티스) current context, namespace 한 번에 확인하기 (0) | 2021.07.11 |
Ubuntu 리눅스 - 서버에 연결된 connection(트래픽) 수 확인 (2) | 2021.07.04 |
[이럴땐이렇게] Kubernetes,K8s - 여러 컨텍스트 파일 동시에 사용하기 (2) | 2021.06.12 |