[Home-K8S] #22 FluxCD 계층과 분리 / 다중 클러스터 리소스 공유와 설정 분리
FluxCD - yaml 앞서 fluxcd 를 이용해서 helm chart 를 구성했습니다. 그 외에 일반적인 yaml
사용중인 helm chart 에 새로운 Release 가 업데이트 되면, 해당 release의 change log 를 요약해서 알려주고 버튼으로 업데이트 가능하게 합니다.
이번 포스트는 LLM의 무료 api 를 사용할 수 있게 해준 Gemini 를 사용해서 Change Log 요약하고 Slack 에 메시지까지 보내게 하는 과정입니다.
Slack 의 버튼 event 를 처리하는 과정은 다음 포스트에서 작성하겠습니다.
편하게도 대부분의 Helm Chart를 배포하는 Artifact hub 는 rss 를 제공합니다.

이 rss 링크를 마다 확인하는 rss trigger node를 만들어 줍니다.
n8n의 rss trigger는 Poll Times 를 정할 수 있지만, 업데이트가 되면 실시간으로 작동합니다. 정해놓은 시간은 "해당 시간에는 무조건 확인을 한번 한다." 입니다.
저는 매일 2시에 확인하게 했습니다.

실행해보면 가장 최신 버전 차트의 내용이 나옵니다.
이제 현재 적용되어 있는 버전의 값을 가져와서 비교를 진행합니다.
git node 를 사용해서 HelmRelease yaml 파일을 가져오면 base64로 인코딩 되어 있습니다.
Code Node를 활용해서 파일을 디코딩하고 버전을 찾아서 비교해 줍니다.
const rssVersion = $('Nginx ingress').first().json.title
// GitHub node에서 받은 데이터
const githubData = $input.first();
// 1. base64 content 디코딩
const base64Content = githubData.json.content;
const decodedContent = Buffer.from(base64Content, 'base64').toString('utf-8');
// 현재 버전 찾기
const chartVersionPattern = /(chart:\s*\n\s+spec:\s*\n(?:\s+[^:]+:[^\n]*\n)*?\s+version:\s*)"([^"]+)"/;
const chartMatch = decodedContent.match(chartVersionPattern);
const nowVersion = chartMatch ? chartMatch[2] : null;
// 5. 결과 출력
return {
"rss version": rssVersion,
"now version": nowVersion,
"version diff": rssVersion == nowVersion
};이제 IF Node 로 업데이트가 필요하면 진행합니다.
새로 업그레이드 할 차트의 변경점을 가져옵니다. 보통 github 의 releases 에 change log 를 작성합니다. github api 를 이용해서 값을 가져옵니다.
https://api.github.com/repos/{account}/{repository}/releases/tags/{release version}
# ingress-nginx 4.13.1 예시
https://api.github.com/repos/kubernetes/ingress-nginx/releases/tags/helm-chart-4.13.1 nginx-ingress 의 change log 의 경우 nginx controller 의 버전 변경이 대다수 더라구요. 그래서 nginx controller 의 변경점도 가져옵니다.
https://api.github.com/repos/kubernetes/ingress-nginx/releases/tags/controller-{controller version}가져온 Change Log 값을 살펴보면 불필요한 값들이 많이 들어 있습니다.
url 링크라든가, escape 구문 등 여러가지가 있는데 코드로 정리하고 change log 를 합쳐 줍니다.
# Changelog\r\n\r\nThis file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org).\r\n\r\n### 4.13.1\r\n\r\n* Make: Add `helm-test` target. (#13659)\r\n* Update Ingress-Nginx version controller-v1.13.1\r\n\r\n**Full Changelog**: https://github.com/kubernetes/ingress-nginx/compare/helm-chart-4.13.0...helm-chart-4.13.1ingress-nginx changelog body
const content = $('Nginx ingress').first().json.content
const release = content.split(' ')[0];
const chartChangeLog = $('Nginx Chart Change').first().json.body
const controllerChangeLog = $input.first().json.body
const cleanChangeLog = (rawLog) => {
// 입력값이 없거나 문자열이 아니면 빈 문자열 반환
if (!rawLog || typeof rawLog !== 'string') {
return '';
}
const cleanedLines = rawLog
.split('\n') // 1. 로그를 줄 단위로 나눕니다.
.map(line => line.trim()) // 2. 각 줄의 앞뒤 공백을 제거합니다.
.filter(line => {
// 3. 아래 조건에 해당하는 불필요한 줄을 '제외'합니다.
if (line === '') return false; // 빈 줄 제외
if (line.startsWith('# Changelog')) return false; // '# Changelog' 제목 제외
if (line.startsWith('This file documents')) return false; // 설명 문단 제외
if (line.startsWith('**Full Changelog**')) return false; // Full Changelog 링크 제외
if (line.toLowerCase().includes('images:')) return false; // 'Images:' 포함 줄 제외
if (line.includes('@sha256:')) return false; // 이미지 해시값 포함 줄 제외
if (line.startsWith('### All changes:')) return false; // 'All changes' 소제목 제외
if (line.startsWith('### Dependency updates:')) return false; // 'Dependency updates' 소제목 제외
return true; // 위의 모든 조건에 해당하지 않으면 필요한 줄로 판단
})
.map(line => {
// 4. 남은 줄들에서 불필요한 부분을 한번 더 제거합니다.
// 예: " Docs: Fix links. (#13743)" -> "Docs: Fix links."
return line.replace(/\s+\(#\d+\)$/, '');
});
// 5. 정리된 줄들을 다시 하나의 텍스트로 합칩니다.
return cleanedLines.join('\n');
};
const cleanedChartLog = cleanChangeLog(chartChangeLog);
const cleanedControllerLog = cleanChangeLog(controllerChangeLog);
const combinedChangeLog = `--- Chart Changes ---\n${cleanedChartLog}\n\n--- Controller Changes ---\n${cleanedControllerLog}`;
return { output: {
"release": release,
"version": $('Nginx ingress').first().json.title,
"release date": $('Nginx ingress').first().json.pubDate,
"change log": combinedChangeLog
}}ingress-nginx 만 helm 을 사용하는 것은 아니기에 output 형식을 맞춰줘서 모듈로 사용하기 쉽게 만들어 줍니다.


button 같은 메시지는 Slack Block 으로 보내며, Block kit Builder을 사용해서 참고할 수 있습니다. (Bot 이 아닌 사용자는 기본적으로 보낼 수 없습니다. )
n8n 의 Slack Node 대신 Http Request를 사용한 이유는 Slack Node 에서 Block 을 사용하면 약간 에러가 있어서 Http Request 를 사용했습니다.
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "📋 {{ $('reguler output').item.json.release }} helm upgrade *{{ $('reguler output').item.json.version }}*"
}
},
{
"type": "actions",
"elements": [
{
"type": "timepicker",
"initial_time": "13:00",
"placeholder": {
"type": "plain_text",
"text": "Select time",
"emoji": true
},
"action_id": "settime"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "✅ Approve",
"emoji": true
},
"style": "primary",
"action_id": "approve",
"value": "{\"name\": \"{{ $('reguler output').item.json.release }}\", \"version\": \"{{ $('reguler output').item.json.version }}\"}"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "❌ Reject",
"emoji": true
},
"style": "danger",
"action_id": "reject",
"value": "{\"name\": \"{{ $('reguler output').item.json.release }}\", \"version\": \"{{ $('reguler output').item.json.version }}\"}"
}
]
}
]배포 시각을 설정할 부분과 의사결정할 버튼을 만들면 완료됩니다.
이제 다른 Helm Chart 들에 대한 값들도 추가해 주면 완성입니다.

Comments