티스토리 뷰
안녕하세요, CICD 학습을 위해 CloudNetaStudy 스터디 모임을 통해 진행한 내용을 정리하였습니다.
2주차는 [GitOps Cookbook] 5~6장(192p) - 헬름, Cloud Native CI/CD(Tekton, Github Action) 에 대해 중점적으로 학습합니다.
[개념]
| 개념 | 설명 |
| Helm | 쿠버네티스(Kubernetes)를 위한 패키지 관리자입니다. 커스터마이즈(Kustomize)와 유사하게 템플릿 기반 솔루션을 제공하지만, 패키지 관리자처럼 작동하여 버전 관리, 공유, 배포 가능한 아티팩트(artifact)를 생성합니다. |
| Chart | Helm의 핵심 개념으로, 공유 가능한 쿠버네티스 패키지입니다. 애플리케이션을 배포하는 데 필요한 모든 리소스 정의(Deployment, Service 등)와 구성 정보를 포함하며, 다른 차트에 대한 의존성 등 다양한 요소를 담을 수 있습니다. |
[실습환경] - kind(k8s)
Kind를 사용하여 로컬 환경에 쿠버네티스 클러스터를 구성
- 클러스터 이름: myk8s
- 쿠버네티스 버전: v1.32.8
- 추가 포트 매핑: 호스트 포트 30000과 30001을 컨테이너 포트에 매핑하여 외부에서 접근 가능하도록 설정(NodePort 등 서비스 테스트에 유용)
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
EOF
[Helm]
Creating a Helm Project
Helm Chart 디렉토리 레이아웃 생성
- pacman이라는 이름의 Helm Chart 디렉터리를 생성하고 기본 구성 파일들을 작성했습니다.
1) 디렉터리 레이아웃
pacman/
├── Chart.yaml # 차트 메타데이터 (이름, 버전 등)
├── templates/ # 쿠버네티스 리소스 Go 템플릿 파일
│ ├── deployment.yaml
│ └── service.yaml
└── values.yaml # 차트의 기본 설정값 (사용자 정의 가능)
| 파일 | 역할 |
템플릿 변수 사용 예
|
| Chart.yaml | 차트의 이름(pacman), 설명, 차트 버전(version: 0.1.0), 애플리케이션 버전(appVersion: "1.0.0") 등 메타데이터 정의. |
{{ .Chart.Name}}, {{ .Chart.AppVersion }}
|
| values.yaml | 배포 시 적용될 기본 설정값을 정의. 배포 시점에 --set 파라미터 등으로 재정의 가능. |
{{ .Values.replicaCount }}, {{ .Values.image.repository }}
|
| templates/ | Deployment, Service 등 쿠버네티스 리소스 정의 파일을 Go 템플릿 언어로 작성. Chart.yaml 및 values.yaml의 값을 가져와 동적으로 YAML을 생성. |
{{ .Values.image.pullPolicy }}, {{- toYaml .Values.securityContext }}
|
# 헬름 차트 디렉터리 레이아웃 생성
mkdir pacman
mkdir pacman/templates
cd pacman
# 루트 디렉터리에 차트 정의 파일 작성 : 버전, 이름 등 정보
cat << EOF > Chart.yaml
apiVersion: v2
name: pacman
description: A Helm chart for Pacman
type: application
version: 0.1.0 # 차트 버전, 차트 정의가 바뀌면 업데이트한다
appVersion: "1.0.0" # 애플리케이션 버전
EOF
# templates 디렉터리에 Go 템플릿 언어와 Sprig 라이브러리의 템플릿 함수를 사용해 정의한 배포 템플릿 파일 작성 : 애플리케이션 배포
## deployment.yaml 파일에서 템플릿화 : dp 이름, app 버전, replicas 수, 이미지/태그, 이미지 풀 정책, 보안 컨텍스트, 포트
cat << EOF > templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name}} # Chart.yaml 파일에 설정된 이름을 가져와 설정
labels:
app.kubernetes.io/name: {{ .Chart.Name}}
{{- if .Chart.AppVersion }} # Chart.yaml 파일에 appVersion 여부에 따라 버전을 설정
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} # appVersion 값을 가져와 지정하고 따움표 처리
{{- end }}
spec:
replicas: {{ .Values.replicaCount }} # replicaCount 속성을 넣을 자리 placeholder
selector:
matchLabels:
app.kubernetes.io/name: {{ .Chart.Name}}
template:
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name}}
spec:
containers:
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion}}" # 이미지 지정 placeholder, 이미지 태그가 있으면 넣고, 없으면 Chart.yaml에 값을 설정
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
{{- toYaml .Values.securityContext | nindent 14 }} # securityContext의 값을 YAML 객체로 지정하며 14칸 들여쓰기
name: {{ .Chart.Name}}
ports:
- containerPort: {{ .Values.image.containerPort }}
name: http
protocol: TCP
EOF
## service.yaml 파일에서 템플릿화 : service 이름, 컨테이너 포트
cat << EOF > templates/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
name: {{ .Chart.Name }}
spec:
ports:
- name: http
port: {{ .Values.image.containerPort }}
targetPort: {{ .Values.image.containerPort }}
selector:
app.kubernetes.io/name: {{ .Chart.Name }}
EOF
# 차트 기본값 default vales 이 담긴 파일 작성 : 애플리케이션 배포 시점에 다른 값으로 대체될 수 있는, 기본 설정을 담아두는 곳
cat << EOF > values.yaml
image: # image 절 정의
repository: quay.io/gitops-cookbook/pacman-kikd
tag: "1.0.0"
pullPolicy: Always
containerPort: 8080
replicaCount: 1
securityContext: {} # securityContext 속성의 값을 비운다
EOF
# 디렉터리 레이아웃 확인
tree
├── Chart.yaml # 차트를 설명하며, 차트 관련 메타데이터를 포함
├── templates # 차트 설치에 사용되는 모든 템플릿 파일
│ ├── deployment.yaml # 애플리케이션 배포에 사용되는 헬름 템플릿 파일들
│ └── service.yaml
└── values.yaml # 차트 기본값
헬름 차트를 로컬에서 YAML로 렌더링
- helm template . 명령을 사용하여 실제로 쿠버네티스에 배포되기 전에, 템플릿이 values.yaml의 기본값과 결합되어 어떤 YAML 파일로 변환되는지 확인
- Service와 Deployment YAML 파일 생성
- 예시:
- name: pacman은 {{ .Chart.Name}}에서 가져옴
- replicas: 1과 image: "quay.io/...:1.0.0" 등은 values.yaml의 기본값으로 채움
#
helm template .
---
# Source: pacman/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: pacman
name: pacman # Chart.yaml 파일에 설정된 이름을 가져와 설정
spec:
ports:
- name: http
port: 8080 # values.yaml 파일에서 가져옴
targetPort: 8080
selector:
app.kubernetes.io/name: pacman
---
# Source: pacman/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pacman # Chart.yaml 파일에 설정된 이름을 가져와 설정
labels:
app.kubernetes.io/name: pacman
app.kubernetes.io/version: "1.0.0" # Chart.yaml appVersion 값을 가져와 지정하고 따움표 처리
spec:
replicas: 1 # replicaCount 속성을 넣을 자리 placeholder
selector:
matchLabels:
app.kubernetes.io/name: pacman
template:
metadata:
labels:
app.kubernetes.io/name: pacman
spec:
containers:
- image: "quay.io/gitops-cookbook/pacman-kikd:1.0.0" # 두 속성의 내용이 하나로 연결
imagePullPolicy: Always
securityContext: # 보안 컨텍스트는 빈 값
{} # securityContext의 값을 YAML 객체로 지정하며 14칸 들여쓰기
name: pacman
ports:
- containerPort: 8080
name: http
protocol: TCP
# --set 파라미터를 사용하여 기본값을 재정의
helm template --set replicaCount=3 .
...
spec:
replicas: 3 # replicaCount 속성을 넣을 자리 placeholder
...
해당 차트를 kind 배포 및 helm 확인
- helm install pacman . 명령으로 Chart를 클러스터에 배포
- Helm은 릴리스 상태를 관리하기 위해 자동으로 Secret 리소스를 생성
- kubectl get secret 명령으로 sh.helm.release.v1.pacman.v1과 같은 Secret이 생성된 것을 확인
- 이 Secret은 릴리스 메타데이터를 저장하며, Helm이 차트의 상태를 복구하거나 롤백(rollback)할 때 사용
# 해당 차트 배포
helm install pacman .
helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
pacman default 1 2025-10-18 16:56:58.315737 +0900 KST deployed pacman-0.1.0 1.0.0
# 배포된 리소스 확인
kubectl get deploy,pod,svc,ep
kubectl get pod -o yaml | kubectl neat | yq # kubectl krew install neat
kubectl get pod -o json | grep securityContext -A1
#
helm history pacman
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Sat Oct 18 16:56:58 2025 deployed pacman-0.1.0 1.0.0 Install complete
# Helm 자체가 배포 릴리스 메타데이터를 저장하기 위해 자동으로 Sercet 리소스 생성 : Helm이 차트의 상태를 복구하거나 rollback 할 때 이 데이터를 이용
kubectl get secret
NAME TYPE DATA AGE
sh.helm.release.v1.pacman.v1 helm.sh/release.v1 1 5m17s

Reusing Statements Between Templates
같은 코드 확인 및 재사용 가능 코드 블록 정의
- 템플릿 간 구문 재사용: _helpers.tpl 활용
- 여러 쿠버네티스 리소스 정의 파일(예: deployment.yaml, service.yaml)에서 반복되는 코드 블록을 중앙 집중화하여 관리하고 재사용하는 방법
# deployment.yaml, service.yaml 에 selector 필드가 동일
## deployment.yaml
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ .Chart.Name}}
template:
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name}}
## service.yaml
selector:
app.kubernetes.io/name: {{ .Chart.Name }}
## 이 필드를 업데이트하려면(selector 필드에 새 레이블 추가 등) 3곳을 똑같이 업데이트 해야함
# 템플릿 디렉터리에 _helpers.tpl 파일을 만들고 그 안에 재사용 가능한 템플릿 코드를 두어 재사용할 수 있게 기존 코드를 디렉터링하자
## _helpers.tpl 파일 작성
cat << EOF > templates/_helpers.tpl
{{- define "pacman.selectorLabels" -}} # stetement 이름을 정의
app.kubernetes.io/name: {{ .Chart.Name}} # 해당 stetement 가 하는 일을 정의
{{- end }}
EOF
## deployment.yaml 수정
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "pacman.selectorLabels" . | nindent 6 }} # pacman.selectorLabels를 호출한 결과를 6만큼 들여쓰기하여 주입
template:
metadata:
labels:
{{- include "pacman.selectorLabels" . | nindent 8 }} # pacman.selectorLabels를 호출한 결과를 8만큼 들여쓰기하여 주입
## service.yaml 수정
selector:
{{- include "pacman.selectorLabels" . | nindent 6 }}
# 변경된 차트를 로컬에서 YAML 렌더링 : _helpers.tpl 설정된 값으로 갱신 확인
helm template .
# _helpers.tpl 파일 수정 : 새 속성 추가
cat << EOF > templates/_helpers.tpl
{{- define "pacman.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name}}
app.kubernetes.io/version: {{ .Chart.AppVersion}}
{{- end }}
EOF
# 변경된 차트를 로컬에서 YAML 렌더링 : _helpers.tpl 설정된 값으로 갱신 확인
helm template .
Updating a Container Image in Helm
이미지 업그레이드/롤백, value override
- Helm의 upgrade 및 rollback 명령을 사용하여 애플리케이션 버전을 효율적으로 관리하는 방법
1) 이미지 업그레이드
- 초기 배포: helm install pacman .으로 릴리스를 배포합니다. (REVISION: 1, Image: 1.0.0)
- 버전 업데이트:
- values.yaml 파일의 image.tag를 "1.1.0"으로 변경
- Chart.yaml 파일의 appVersion을 "1.1.0"으로 갱신
- 업그레이드 실행: helm upgrade pacman . 명령을 실행하면, Helm이 기존 릴리스의 설정을 새 설정으로 업데이트하고 새로운 쿠버네티스 리소스를 배포
- 결과: REVISION 2가 생성되고, Deployment의 이미지가 1.1.0으로 변경됨
2) 이전 버전으로 롤백
- 롤백 실행: helm rollback pacman 1 명령을 실행하여 릴리스를 이전 버전인 REVISION 1 상태로 되돌림
- 결과: REVISION 3이 생성되고, Deployment의 이미지는 다시 1.0.0으로 변경됨
3) 값 재정의 (-f 사용)
- newvalues.yaml 파일에 image.tag: "1.2.0"만 정의
- helm template pacman -f newvalues.yaml .을 실행하면, 기본 values.yaml의 설정은 유지하면서 image.tag 값만 newvalues.yaml의 "1.2.0"으로 덮어씀(override). 이는 임시 테스트나 환경별 설정을 적용할 때 유용
# _helpers.tpl 파일 초기 설정으로 수정
cat << EOF > templates/_helpers.tpl
{{- define "pacman.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name}}
{{- end }}
EOF
# helm 배포
helm install pacman .
# 확인 : 리비전 번호, 이미지 정보 확인
helm history pacman
kubectl get deploy -owide
# values.yaml 에 이미지 태그 업데이트
cat << EOF > values.yaml
image:
repository: quay.io/gitops-cookbook/pacman-kikd
tag: "1.1.0"
pullPolicy: Always
containerPort: 8080
replicaCount: 1
securityContext: {}
EOF
# Chart.yaml 파일에 appVersion 필드 갱신
cat << EOF > Chart.yaml
apiVersion: v2
name: pacman
description: A Helm chart for Pacman
type: application
version: 0.1.0
appVersion: "1.1.0"
EOF
# 배포 업그레이드
helm upgrade pacman .
Release "pacman" has been upgraded. Happy Helming!
NAME: pacman
LAST DEPLOYED: Sat Oct 18 18:15:47 2025
NAMESPACE: default
STATUS: deployed
REVISION: 2 # 새 리비전 번호
TEST SUITE: None
# 확인
helm history pacman
kubectl get secret
kubectl get deploy,replicaset -owide
# 이전 버전으로 롤백
helm history pacman
helm rollback pacman 1 && kubectl get pod -w
# 확인
helm history pacman
kubectl get secret
kubectl get deploy,replicaset -owide
# values 새 파일 작성
cat << EOF > newvalues.yaml
image:
tag: "1.2.0"
EOF
# template 명령 실행 시 새 values 파일 함께 전달 : 결과적으로 values.yaml 기본값을 사용하지만, image.tag 값은 override 함
helm template pacman -f newvalues.yaml .
...
- image: "quay.io/gitops-cookbook/pacman-kikd:1.2.0"
...

Packaging and Distributing a Helm Chart
- 작성한 차트를 압축하고 공유 가능한 형태로 만드는 과정
# pacman 차트를 .tgz 파일로 패키징
helm package .
Successfully packaged chart and saved it to: .../pacman/pacman-0.1.0.tgz
gzcat pacman-0.1.0.tgz
# 해당 차트를 차트 저장소 repository 에 게시
# 차트 저장소는 차트 및 .tgz 차트에 대한 메타데이터 정보를 담은 index.html 파일이 있는 HTTP 서버
# 차트를 저장소에 게시하려면 index.html 파일을 새 메타데이터 정보로 업데이트하고 아티팩트를 업로드해야 한다.
## index.html 파일 생성
helm repo index .
cat index.yaml
apiVersion: v1
entries:
pacman:
- apiVersion: v2
appVersion: 1.1.0
created: "2025-10-18T18:33:41.240749+09:00"
description: A Helm chart for Pacman
digest: 1a68e0069016d96ab64344e2d4c2fde2b7368e410f93da90bf19f6ed8ca9495a
name: pacman
type: application
urls:
- pacman-0.1.0.tgz
version: 0.1.0
generated: "2025-10-18T18:33:41.239645+09:00"
Deploying a Chart from a Repository
# repo
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo list
helm search repo postgresql
helm search repo postgresql -o json | jq
# 배포
helm install my-db \
--set postgresql.postgresqlUsername=my-default,postgresql.postgresqlPassword=postgres,postgresql.postgresqlDatabase=mydb,postgresql.persistence.enabled=false \
bitnami/postgresql
# 확인
helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-db default 1 2025-10-19 11:53:08.860197 +0900 KST deployed postgresql-18.0.17 18.0.0
kubectl get sts,pod,svc,ep,secret
# 서드 파티 차트 사용 시, 기본값(default value)나 override 파라미터를 직접 확인 할 수 없고, helm show 로 확인 가능
helm show values bitnami/postgresql
# 실습 후 삭제
helm uninstall my-db
Deploying a Chart with a Dependency
- 하나의 애플리케이션 차트(예: music 서비스)가 데이터베이스와 같은 다른 차트(예: postgresql)에 의존할 때 이를 함께 배포하는 방법
1) 의존성 선언
- Chart.yaml 파일에 dependencies 섹션을 추가하여 의존성을 선언함
- name: postgresql: 의존하는 차트의 이름
- version: 18.0.17: 사용할 버전
- repository: "https://charts.bitnami.com/bitnami": 차트가 위치한 저장소 주소
2) 의존성 다운로드
- helm dependency update 명령을 실행하면, 선언된 postgresql 차트의 .tgz 파일이 외부 저장소에서 다운로드되어 charts/ 디렉터리에 저장됨
3) 메인 차트와 의존성 차트의 연동
- music 서비스의 deployment.yaml 파일은 env 설정을 통해 PostgreSQL 데이터베이스 연결 정보를 환경 변수로 받도록 템플릿화됨
- {{ .Values.postgresql.server | default (printf "%s-postgresql" ( .Release.Name )) | quote }} 와 같은 구문은 의존성 차트의 값을 상위 차트의 values.yaml에서 가져오거나, 그렇지 않으면 기본값을 사용하여 데이터베이스 서비스의 주소를 구성
- 배포: helm install music-db .를 실행하면 music 차트와 그 의존성인 postgresql 차트가 동시에 배트됨
4) 서드 파티 차트 확인
- 저장소 추가: helm repo add bitnami ...
- 값 확인: 서드 파티 차트의 기본 설정값과 사용 가능한 파라미터는 helm show values bitnami/postgresql 명령으로 확인할 수 있음
5) 트러블슈팅 예시 (TS)
- 배포 후 Pod가 Error 상태가 되고 로그에 "couldn't find key postgresql-password in Secret..." 오류가 발생
- 해결책: PostgreSQL 차트가 생성한 Secret 리소스 (music-db-postgresql)에 데이터베이스 비밀번호를 담은 postgresql-password 키/값 쌍을 Base64 인코딩된 형태로 추가하여 Secret을 수동으로 수정함으로써 애플리케이션이 DB에 접근할 수 있도록 문제를 해결
#
mkdir music
mkdir music/templates
cd music
#
cat << EOF > templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name}}
labels:
app.kubernetes.io/name: {{ .Chart.Name}}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ .Chart.Name}}
template:
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name}}
spec:
containers:
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion}}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: {{ .Chart.Name}}
ports:
- containerPort: {{ .Values.image.containerPort }}
name: http
protocol: TCP
env:
- name: QUARKUS_DATASOURCE_JDBC_URL
value: {{ .Values.postgresql.server | default (printf "%s-postgresql" ( .Release.Name )) | quote }}
- name: QUARKUS_DATASOURCE_USERNAME
value: {{ .Values.postgresql.postgresqlUsername | default (printf "postgres" ) | quote }}
- name: QUARKUS_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.postgresql.secretName | default (printf "%s-postgresql" ( .Release.Name )) | quote }}
key: {{ .Values.postgresql.secretKey }}
EOF
#
cat << EOF > templates/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
name: {{ .Chart.Name }}
spec:
ports:
- name: http
port: {{ .Values.image.containerPort }}
targetPort: {{ .Values.image.containerPort }}
selector:
app.kubernetes.io/name: {{ .Chart.Name }}
EOF
# psql 10.16.2 차트 책 버전 사용 시
cat << EOF > Chart.yaml
apiVersion: v2
name: music
description: A Helm chart for Music service
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: postgresql
version: 10.16.2
repository: "https://charts.bitnami.com/bitnami"
EOF
helm search repo postgresql
NAME CHART VERSION APP VERSION DESCRIPTION
bitnami/postgresql 18.0.17 18.0.0 PostgreSQL (Postgres) is an open source object-...
bitnami/postgresql-ha 16.3.2 17.6.0 This PostgreSQL cluster solution includes the P...
...
helm search repo bitnami/postgresql --versions
NAME CHART VERSION APP VERSION DESCRIPTION
bitnami/postgresql 18.0.17 18.0.0 PostgreSQL (Postgres) is an open source object-...
bitnami/postgresql 18.0.16 18.0.0 PostgreSQL (Postgres) is an open source object-...
...
bitnami/postgresql 11.6.2 14.3.0 PostgreSQL (Postgres) is an open source object-...
bitnami/postgresql-ha 16.3.2 17.6.0 This PostgreSQL cluster solution includes the P...
bitnami/postgresql-ha 16.3.1 17.6.0 This PostgreSQL cluster solution includes the P...
# 현재 최신 차트 버전 사용
cat << EOF > Chart.yaml
apiVersion: v2
name: music
description: A Helm chart for Music service
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: postgresql
version: 18.0.17 # book 10.16.2
repository: "https://charts.bitnami.com/bitnami"
EOF
#
cat << EOF > values.yaml
image:
repository: quay.io/gitops-cookbook/music
tag: "1.0.0"
pullPolicy: Always
containerPort: 8080
replicaCount: 1
postgresql:
server: jdbc:postgresql://music-db-postgresql:5432/mydb
postgresqlUsername: my-default
postgresqlPassword: postgres
postgresqlDatabase: mydb
secretName: music-db-postgresql
secretKey: postgresql-password
EOF
#
tree
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ └── service.yaml
└── values.yaml
# 의존성으로 선언된 차트를 다운로드하여 차트 디렉터리에 어장
helm dependency update
#
tree
├── Chart.lock
├── Chart.yaml
├── charts
│ └── postgresql-18.0.17.tgz
├── templates
│ ├── deployment.yaml
│ └── service.yaml
└── values.yaml
# 차트 배포
helm install music-db .
Warning Failed 2s (x8 over 86s) kubelet Error: couldn't find key postgresql-password in Secret default/music-db-postgresql
# 확인
kubectl get sts,pod,svc,ep,secret,pv,pvc
# TS 1 : secret 에 키/값 추가
kubectl edit secret music-db-postgresql
postgresql-password: cG9zdGdyZXMK
# TS 2 : 직접 해결해보자!
kubectl logs -l app.kubernetes.io/name=music -f
at org.postgresql.Driver.connect(Driver.java:265)
at io.agroal.pool.ConnectionFactory.createConnection(ConnectionFactory.java:210)
at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:513)
at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:494)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at io.agroal.pool.util.PriorityScheduledExecutor.beforeExecute(PriorityScheduledExecutor.java:75)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1126)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
# music service 에 port-forward 설정 후 호출하여 노래 목록 확인
kubectl port-forward service/music 8080:8080
curl -s http://localhost:8080/song
...
# 삭제
helm uninstall music-db
kubectl delete pvc --all
Triggering a Rolling Update Automatically
- ConfigMap 객체가 변경될 때 deployment rolling update 가 자동으로 시작되도록 구성
- Helm은 ConfigMap이나 Secret 객체의 내용이 변경될 때 Deployment의 롤링 업데이트를 자동으로 시작하도록 Pod 템플릿에 해당 객체의 콘텐츠 해시 값(Checksum)을 주입하는 방식으로 이 문제를 해결함
- configmap.yaml 파일의 전체 콘텐츠에 대해 SHA-256 해시 값을 계산함
- 이 해시 값을 Deployment의 Pod 템플릿 메타데이터 내 Annotation 필드에 설정함
- ConfigMap의 내용이 변경되면, 해시 값이 달라지고, 이 해시 값이 설정된 Pod 템플릿의 Annotation도 변경됨
- Pod 템플릿 정의가 변경되었으므로, Deployment는 자동으로 롤링 업데이트를 시작함
...
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ .Chart.Name}}
template:
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name}}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml"). | sha256sum }}
...
[Cloud Native CI/CD]
Install Tekton
- 쿠버네티스 클러스터에 텍톤 설치
- 텍톤 Tekton 소개 - Home , Docs , Blog , Github
- 지속적 통합 Continuous Integration 은 개발자가 만들 새 코드를 가져와서 빌드, 테스트, 실행하는 과정을 자동으로 처리하는 프로세스
- 클라우드 네이티브 CI는 이 프로세스에 클라우드 컴퓨팅과 클라우드 서비스가 결합한 모델
- Git을 통해 수행되는 작업에 기반하여 자동화를 지원하므로 GitOps 워크플로의 기본 구성 요소
- 텍톤 Tekton 은 쿠버네티스 기반 오픈 소스 클라우드 네이티브 CI/CD 시스템
1) 텍톤 파이프라인 (Tekton Pipelines) 설치
- 역할: CI/CD 워크플로의 핵심 엔진. 빌드, 테스트, 배포와 같은 작업을 정의하고 실행함
- 설치: kubectl apply -f [release.yaml 주소] 명령을 사용해 커스텀 리소스 정의(CRD)와 컨트롤러를 배포함
- 확인:
- kubectl get crd: 파이프라인 관련 CRD(예: tasks.tekton.dev)가 생성되었는지 확인함
- kubectl get pod -n tekton-pipelines: tekton-pipelines-controller와 tekton-pipelines-webhook 등의 핵심 파드들이 정상적으로 실행 중인지 (Running) 확인함
# Tekton dependency 파이프라인(pipeline) 설치 : 현재 v1.5.0
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
# Tekton dependency 파이프라인(pipeline) 설치 확인
kubectl get crd
kubectl get ns | grep tekton
kubectl get-all -n tekton-pipelines # kubectl krew install get-all
kubectl get all -n tekton-pipelines
kubectl get-all -n tekton-pipelines-resolvers
kubectl get all -n tekton-pipelines-resolvers
# 파드 확인
kubectl get pod -n tekton-pipelines

2) 텍톤 트리거 (Tekton Triggers) 설치
- 역할: 외부 이벤트(예: GitHub 푸시, 웹훅 호출)에 응답하여 파이프라인을 자동으로 시작(트리거)하는 역할을 함
- 설치: 두 개의 YAML 파일을 적용하여 트리거 컨트롤러와 인터셉터(중간 처리기)를 배포함
- 확인: k get deploy -n tekton-pipelines | grep triggers 명령을 통해 tekton-triggers-controller 등의 트리거 관련 Deployment가 생성되었는지 확인함
# Tekton Trigger 설치 : 현재 v0.33.0
kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml
# Tekton Trigger 설치 확인
kubectl get crd | grep triggers # trigger 관련 crd 확인
kubectl get all -n tekton-pipelines
# trigger 관련 deployment 설치 확인
k get deploy -n tekton-pipelines | grep triggers

3) 텍톤 대시보드 (Tekton Dashboard) 설치 및 접속
- 역할: 웹 인터페이스를 통해 파이프라인, 태스크, 실행 결과 등의 CI/CD 워크플로를 시각적으로 모니터링할 수 있게 해줌
# Tekton Dashboard 설치 : 현재 v0.62.0
kubectl apply -f https://storage.googleapis.com/tekton-releases/dashboard/latest/release.yaml
# Tekton Dashboard 설치 확인
kubectl get crd | grep dashboard # dashboard 관련 crd 확인
kubectl get all -n tekton-pipelines
# Dashboard 관련 deployment 설치 확인
kubectl get deploy -n tekton-pipelines
tekton-dashboard 1/1 1 1 81s
...
# service 정보 확인
kubectl get svc,ep -n tekton-pipelines tekton-dashboard
kubectl get svc -n tekton-pipelines tekton-dashboard -o yaml | kubectl neat | yq
# service 를 Nodeport 설정 : nodePort 30000
kubectl patch svc -n tekton-pipelines tekton-dashboard -p '{"spec":{"type":"NodePort","ports":[{"port":9097,"targetPort":9097,"nodePort":30000}]}}'
kubectl get svc,ep -n tekton-pipelines tekton-dashboard
# 텍톤 대시보드 접속
open http://localhost:30000 # macOS 경우
웹 브라우저 http://<Ubuntu IP>:30000 # Windows WSL2 Ubuntu 경우

4) 텍톤 CLI (tkn) 설치
tkn은 텍톤 파이프라인을 실행, 관리, 확인하기 위한 명령줄 도구(CLI)입니다.
- 역할: 개발 및 운영 환경에서 텍톤 리소스를 쉽게 조작할 수 있도록 도와줌
- 설치: 사용 환경(macOS의 경우 brew, Linux의 경우 PPA를 통한 apt 설치)에 맞춰 CLI 도구를 설치함
- 확인: tkn version을 실행하여 CLI가 정상적으로 설치되었고, 클러스터에 설치된 파이프라인, 트리거, 대시보드의 버전 정보가 정확하게 연결되는지 확인
# macOS
brew install tektoncd-cli
tkn version
Client version: 0.42.0
Pipeline version: v1.5.0
Triggers version: v0.33.0
Dashboard version: v0.62.0
# Windows WSL - Ubuntu Linux 경우
sudo apt update;sudo apt install -y gnupg
sudo mkdir -p /etc/apt/keyrings/
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/tektoncd.gpg --keyserver keyserver.ubuntu.com --recv-keys 3EFE0E0A2F2F60AA
echo "deb [signed-by=/etc/apt/keyrings/tektoncd.gpg] http://ppa.launchpad.net/tektoncd/cli/ubuntu eoan main"|sudo tee /etc/apt/sources.list.d/tektoncd-ubuntu-cli.list
sudo apt update && sudo apt install -y tektoncd-cli
tkn version
...
Create a Hello World Task
- Tekton의 가장 기본적인 구성 요소인 Task를 이해하는 단계
- Task는 CI/CD 워크플로에서 수행되는 단일 작업 단위를 정의함
1) Task 정의 (hello)
- kubectl apply -f -를 통해 hello라는 이름의 Task를 정의함
- 이 Task는 하나의 step을 가짐
- Step 이름: echo
- 컨테이너 이미지: alpine (매우 가벼운 Linux 이미지)
- 스크립트: echo "Hello World" 명령을 실행함.
2) Task 실행 및 확인
- Task 실행: tkn task start --showlog hello 명령으로 Task를 실행
- Task가 실행되면 TaskRun이라는 객체가 생성됨 (예: hello-run-722sp).
- --showlog 덕분에 실행 결과를 즉시 터미널에서 볼 수 있음: [echo] Hello World.
- 실행 원리: TaskRun이 시작되면 쿠버네티스에는 임시 Pod가 생성됨. 이 Pod는 정의된 step-echo 컨테이너 외에도, Tekton이 내부적으로 워크플로를 준비하는 2개의 Init Containers (예: prepare, place-scripts)를 포함
- 로그 확인: kubectl logs -c <컨테이너 이름> 또는 tkn task logs hello 명령으로 각 컨테이너의 실행 로그를 확인
💡 핵심: Tekton Task는 쿠버네티스 Pod로 실행되며, Task의 각 step은 해당 Pod 내의 컨테이너로 실행됨
#
kubectl explain tasks.tekton.dev
GROUP: tekton.dev
KIND: Task
VERSION: v1
# task 생성
cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: hello
spec:
steps:
- name: echo # step 이름
image: alpine # step 수행 컨테이너 이미지
script: |
#!/bin/sh
echo "Hello World"
EOF
# 확인
tkn task list
kubectl get tasks
kubectl get tasks -o yaml | kubectl neat | yq
kubectl get pod


# 신규 터미널 : 파드 상태 모니터링
kubectl get pod -w
# tkn CLI로 task 시작
tkn task start --showlog hello
TaskRun started: hello-run-722sp
Waiting for logs to be available...
[echo] Hello World
# 파드 내, '2개의 init 컨테이너, 1개의 컨테이너' 확인
kubectl describe pod -l tekton.dev/task=hello
...
Init Containers:
prepare:
Container ID: containerd://f3555c826468ae6b6888f3406e42fe8cfa155c9f405941691d8aa9b4d4f3b40d
Image: ghcr.io/tektoncd/pipeline/entrypoint-bff0a22da108bc2f16c818c97641a296:v0.70.0@sha256:763d4cd4e362d381b46a5474d3d358e7731d7c13e22ebf632ef530b857521a48
...
place-scripts:
Container ID: containerd://959bd6f8d513781ab7c3294b8be1ac92a9765897b5e4b007c94a21e37e49f100
Image: cgr.dev/chainguard/busybox@sha256:19f02276bf8dbdd62f069b922f10c65262cc34b710eea26ff928129a736be791
...
Containers:
step-echo:
Container ID: containerd://98bef2bfee42c890df0bc0347fecfdebd52613ffda308a8248b359f4507fafc5
Image: alpine
Image ID: docker.io/library/alpine@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
...
# 로그 확인
kubectl logs -l tekton.dev/task=hello -c prepare
kubectl logs -l tekton.dev/task=hello -c place-scripts
kubectl logs -l tekton.dev/task=hello -c step-echo
Hello World
혹은
kubectl stern -l tekton.dev/task=hello # kubectl krew install stern
#
tkn task logs hello
tkn task describe hello
Name: hello
Namespace: default
🦶 Steps
∙ echo
🗂 Taskruns
NAME STARTED DURATION STATUS
hello-run-722sp 6 minutes ago 19s Succeeded
#
tkn taskrun logs
tkn taskrun list
NAME STARTED DURATION STATUS
hello-run-722sp 7 minutes ago 19s Succeeded
# 다음 실습을 위해 taskrun 삭제
kubectl delete taskruns --all
Create a Task to Compile and Package an App from Git
- 여러 Task를 연결하여 파이프라인(Pipeline)을 만드는 단계
- Pipeline은 Task들의 실행 순서와 의존성을 정의하는 워크플로
1) PipelineRun 정의와 실패
- Task 이름: clone-read
- Task 정의: fetch-source라는 Task를 실행하도록 정의했지만, taskRef: name: git-clone을 참조
- PipelineRun 실행: kubectl create -f -로 PipelineRun을 실행하면 Failed(CouldntGetTask) 오류가 발생
- 원인: git-clone Task가 클러스터에 정의되어 있지 않았기 때문
2) Tkn Hub를 이용한 Task 설치 및 재실행
- Task 설치: tkn hub install task git-clone 명령으로 Tekton Hub에서 공식 git-clone Task를 클러스터에 설치
- 재실행: PipelineRun을 다시 실행하면 Succeeded
- 작업 공간 (Workspace): PipelineRun은 shared-data라는 volumeClaimTemplate을 정의하여 PVC (Persistent Volume Claim)를 생성합니다. 이 PVC는 fetch-source Task가 코드를 다운로드하고 다른 Task가 접근할 수 있는 공유 저장소 역할을 함
- 실행 로그: 로그에서 Successfully cloned https://github.com/tektoncd/website 메시지를 통해 코드가 PVC에 성공적으로 다운로드되었음을 확인
# 파이프라인 파일 작성
cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: clone-read
spec:
description: |
This pipeline clones a git repo, then echoes the README file to the stout.
params: # 매개변수 repo-url
- name: repo-url
type: string
description: The git repo URL to clone from.
workspaces: # 다운로드할 코드를 저장할 공유 볼륨인 작업 공간을 추가
- name: shared-data
description: |
This workspace contains the cloned repo files, so they can be read by the
next task.
tasks: # task 정의
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-data
params:
- name: url
value: \$(params.repo-url)
EOF
# 확인
tkn pipeline list
tkn pipeline describe
kubectl get pipeline
kubectl get pipeline -o yaml | kubectl neat | yq
kubectl get pod

# 파이프라인 실행 : 파이프라인을 인스턴스화하고 실제 값 설정
cat << EOF | kubectl create -f -
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: clone-read-run-
spec:
pipelineRef:
name: clone-read
taskRunTemplate:
podTemplate:
securityContext:
fsGroup: 65532
workspaces: # 작업 공간 인스턴스화, PVC 생성
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
params: # 저장소 URL 매개변수 값 설정
- name: repo-url
value: https://github.com/tektoncd/website
EOF
#
kubectl get pipelineruns -o yaml | kubectl neat | yq
kubectl get pipelineruns
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
clone-read-run-lvkzq False CouldntGetTask 2m41s 2m41s
tkn pipelinerun list
NAME STARTED DURATION STATUS
clone-read-run-lvkzq 3 minutes ago 0s Failed(CouldntGetTask)
tkn pipelinerun logs clone-read-run-lvkzq -f
Pipeline default/clone-read can't be Run; it contains Tasks that don't exist: Couldn't retrieve Task "git-clone": tasks.tekton.dev "git-clone" not found
#
kubectl get tasks
NAME AGE
hello 13m

# 파이프라인에서 git clone 작업을 사용하려면 먼저 클러스터에 설치 필요 : tacket hub 에서 가져오기
tkn hub install task git-clone
WARN: This version has been deprecated
Task git-clone(0.9) installed in default namespace
# 추가된 task 확인
kubectl get tasks
kubectl get tasks git-clone -o yaml | kubectl neat | yq
# 파이프라인 재실행
cat << EOF | kubectl create -f -
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: clone-read-run-
spec:
pipelineRef:
name: clone-read
taskRunTemplate:
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
params:
- name: repo-url
value: https://github.com/tektoncd/website
EOF
#
tkn pipelinerun list
NAME STARTED DURATION STATUS
clone-read-run-t9dfz 1 minute ago 17s Succeeded
clone-read-run-lvkzq 7 minutes ago 0s Failed(CouldntGetTask)
tkn pipelinerun logs clone-read-run-t9dfz -f
...
[fetch-source : clone] + git config --global --add safe.directory /workspace/output
[fetch-source : clone] + /ko-app/git-init '-url=https://github.com/tektoncd/website' '-revision=' '-refspec=' '-path=/workspace/output/' '-sslVerify=true' '-submodules=true' '-depth=1' '-sparseCheckoutDirectories='
[fetch-source : clone] {"level":"info","ts":1760857533.8664637,"caller":"git/git.go:176","msg":"Successfully cloned https://github.com/tektoncd/website @ e6d8959b05b8bbd4aa798b28153b25c0f8766dc7 (grafted, HEAD) in path /workspace/output/"}
[fetch-source : clone] {"level":"info","ts":1760857533.875513,"caller":"git/git.go:215","msg":"Successfully initialized and updated submodules in path /workspace/output/"}
...
# pv,pvc 확인
kubectl get pod,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/clone-read-run-t9dfz-fetch-source-pod 0/1 Completed 0 2m24s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-7e5f5abc-801b-4de0-b38e-fc21bfe844c7 1Gi RWO Delete Bound default/pvc-035aa34a19 standard <unset> 2m21s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/pvc-035aa34a19 Bound pvc-7e5f5abc-801b-4de0-b38e-fc21bfe844c7 1Gi RWO standard <unset> 2m24s
# 실습 완료 후 삭제
kubectl delete pipelineruns.tekton.dev --all

Create a Task to Compile and Package an App from Private Git
- Git-Clone Task가 SSH를 사용하여 Private 저장소에 접근하도록 인증 정보를 제공하는 고급 단계
1) 인증 정보 준비 및 Secret 생성
- SSH 키 인코딩: 로컬 PC의 SSH 개인 키(~/.ssh/id_ed25519)와 Git 서버의 공개 키(known_hosts)를 Base64로 인코딩함
- Secret 생성: 인코딩된 정보를 담아 git-credentials라는 Secret을 쿠버네티스에 생성함
2) ServiceAccount를 이용한 자격 증명 주입
- ServiceAccount 생성: build-bot이라는 ServiceAccount를 생성하고, 여기에 git-credentials Secret을 연결합니다.
- 파이프라인 수정: my-clone-read 파이프라인에 git-credentials라는 Workspace를 추가하고, 이를 fetch-source Task의 ssh-directory에 연결하도록 정의
3) PipelineRun 실행
- PipelineRun 실행 시 serviceAccountName: build-bot을 지정하고, git-credentials Secret을 Workspace로 매핑하여 Task에 전달함
- 결과: Task Pod는 build-bot ServiceAccount의 권한으로 git-credentials Secret에 접근하여 SSH 인증을 수행, Private Repository의 코드를 성공적으로 복제
[사전 준비 : github에 private repo 생성 후 sample app 코드 push]

# 작업 폴더 생성
mkdir my-sample-app
cd my-sample-app
# 샘플 파일 만들기 (Node.js 예시)
echo 'console.log("Hello GitHub!");' > app.js
# Git 초기화
git init
# Git 사용자 설정 (처음이라면)
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
# 파일 추가 및 커밋
git add .
git commit -m "Initial commit - sample app"
Github remote 연결 및 Push
# origin remote 등록:
git remote add origin https://github.com/<your-username>/my-sample-app.git
git remote add origin https://github.com/hayleyshim/my-sample-app.git
# 메인 브랜치 이름을 main으로 변경 (GitHub 기본 브랜치와 맞춤):
git branch -M main
# Push!
git push -u origin main
Username for 'https://github.com': <your-username> hayleyshim
Password for 'https://hayleyshim@github.com': <2번 토큰>

SSH 키로 인증 설정
# ssh 키 생성
ssh-keygen -t ed25519 -C "yhshim17@gmail.com"
# 키 보기
ls -l ~/.ssh | grep ed25519
#
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# 복사해두기
cat ~/.ssh/id_ed25519.pub
-> GitHub → Settings → SSH and GPG keys → New SSH key → 붙여넣기
SSH 연결 방식 사용 테스트
#
ssh -i ~/.ssh/id_ed25519 -T git@github.com
Hi gasida! You've successfully authenticated, but GitHub does not provide shell access.
#
git remote set-url origin git@github.com:<your-username>/my-sample-app.git
git remote set-url origin git@github.com:hayleyshim/my-sample-app.git
#
echo 'cicd study' > readme.md
git add .
git commit -m "add readme.md file"
git push -u origin main

Tekton Pipelines를 사용하여 git에서 소스 코드를 복제 : 공유 작업 공간에서 소스 코드를 읽는 두 번째 작업
# Git 인증용 SSH 사설키를 base64 인코딩
SSHPK=$(cat ~/.ssh/id_ed25519 | base64 -w0)
# Git 인증서버 known_hosts 값을 base64 인코딩
cat ~/.ssh/known_hosts | grep github
SSHKH=$(ssh-keyscan github.com | grep ecdsa-sha2-nistp256 | base64 -w0)
echo $SSHKH
#
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: git-credentials
data:
id_rsa: $SSHPK
known_hosts: $SSHKH
EOF
#
kubectl get secret
# ServiceAccount 에 Secret 속성 지정
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-bot
secrets:
- name: git-credentials
EOF
# 확인
kubectl get sa
NAME SECRETS AGE
build-bot 1 4s

# 파이프라인 파일 작성
cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: my-clone-read
spec:
description: |
This pipeline clones a git repo, then echoes the README file to the stout.
params: # 매개변수 repo-url
- name: repo-url
type: string
description: The git repo URL to clone from.
workspaces: # 다운로드할 코드를 저장할 공유 볼륨인 작업 공간을 추가
- name: shared-data
description: |
This workspace contains the cloned repo files, so they can be read by the
next task.
- name: git-credentials
description: My ssh credentials
tasks: # task 정의
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-data
- name: ssh-directory
workspace: git-credentials
params:
- name: url
value: \$(params.repo-url)
- name: show-readme # add task
runAfter: ["fetch-source"]
taskRef:
name: show-readme
workspaces:
- name: source
workspace: shared-data
EOF
# 확인
tkn pipeline list
tkn pipeline describe
kubectl get pipeline
kubectl get pipeline -o yaml | kubectl neat | yq
kubectl get pod
# show-readme task
cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: show-readme
spec:
description: Read and display README file.
workspaces:
- name: source
steps:
- name: read
image: alpine:latest
script: |
#!/usr/bin/env sh
cat \$(workspaces.source.path)/readme.md
EOF
# 파이프라인 실행
cat << EOF | kubectl create -f -
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: clone-read-run-
spec:
pipelineRef:
name: my-clone-read
taskRunTemplate:
serviceAccountName: build-bot
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: git-credentials
secret:
secretName: git-credentials
params:
- name: repo-url
value: git@github.com:gasida/my-sample-app.git # 제가 사용하는 것 or 자신의 private repo 지정
EOF
# 결과 확인 : 2개의 step 으로 각기 2개의 파드가 실행됨을 확인
kubectl get pod,pv,pvc
# task 를 실행하는 파드에 service acount 정보 확인
kubectl describe pod | grep 'Service Account'
Service Account: build-bot
Service Account: build-bot
Containerize an Application Using a Tekton Task and Buildah
- 가장 복잡한 단계로, 코드를 다운로드한 후 Docker 이미지를 빌드하여 Registry에 푸시하는 CI의 핵심 기능을 구현
1) Kaniko Task 설치 및 Docker 인증 설정
- Kaniko Task 설치: tkn hub install task kaniko 명령으로 Kaniko (Container 이미지를 빌드하는 도구) Task를 설치함
- Docker 인증: Docker Registry (예: Docker Hub)에 이미지를 푸시하기 위한 인증 정보(Username:Password)를 Base64 인코딩하여 config.json 형태의 Secret docker-credentials에 저장
- ServiceAccount 연결: build-sa ServiceAccount를 생성하고 이 Secret을 연결
2) Pipeline: clone-build-push 정의
| Task 이름 | TaskRef | 역할 |
| fetch-source | git-clone | Git에서 소스 코드 다운로드 |
| build-push | kaniko | 다운로드된 코드를 바탕으로 Dockerfile을 읽어 이미지를 빌드하고 Registry에 푸시 |
- 두 Task 모두 shared-data Workspace를 통해 소스 코드를 공유
- build-push Task는 docker-credentials Workspace를 통해 인증 정보 Secret을 전달받아 Registry에 로그인
3) PipelineRun 실행 및 결과
- PipelineRun 실행: repo-url과 image-reference (docker.io/gasida/docsy:1.0.0) 매개변수를 지정하고 serviceAccountName: build-sa를 사용하여 실행
- 로그 확인: Kaniko Task의 로그를 통해 다음의 과정을 확인.
- step-build-and-push 컨테이너 시작.
- Dockerfile을 읽고 이미지를 빌드(RUN apk add git ...).
- INFO[0028] Pushed image to 1 destinations 메시지를 통해 Docker Hub에 이미지가 성공적으로 푸시되었음을 확인
# task 설치 : https://hub.tekton.dev/tekton/task/kaniko
tkn hub install task kaniko
kubectl get tasks
kubectl get tasks kaniko -o yaml | k neat | yq
# Docker 자격 증명으로 Secret을 적용
## Windows WSL Ubuntu 경우
-------------------------------------------------
cat ~/.docker/config.json
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "ZZZZZZZZ==" # 해당 값 echo -n "username:password" | base64
}
}
}
#
cat ~/.docker/config.json | base64 -w0
DSH=$(cat ~/.docker/config.json | base64 -w0)
echo $DSH
-------------------------------------------------
## macOS 경우
-------------------------------------------------
echo "https://index.docker.io/v1/" | docker-credential-osxkeychain get | jq
{
"ServerURL": "https://index.docker.io/v1/",
"Username": "gasida", -> 요값과
"Secret": "****" -> 요값을 가지고 auth 생성
}
echo -n "gasida:****" | base64
AXDFGHXXCFGFGF
# ~/.docker/config.json 대신 임시 파일 dsh.txt 작성
vi dsh.txt
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "AXDFGHXXCFGFGF"
}
}
}
# dsh.txt 파일 내용을 다시 base64 적용
DSH=$(cat dsh.txt | base64 -w0)
echo $DSH
-------------------------------------------------
# 여기서 부터는 공통 적용 내용
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: docker-credentials
data:
config.json: $DSH
EOF
# ServiceAccount 생성 및 Secert 연결
kubectl create sa build-sa
kubectl patch sa build-sa -p '{"secrets": [{"name": "docker-credentials"}]}'
kubectl get sa build-sa -o yaml | kubectl neat | yq
# 파이프라인 파일 작성
cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: clone-build-push
spec:
description: |
This pipeline clones a git repo, builds a Docker image with Kaniko and pushes it to a registry
params:
- name: repo-url
type: string
- name: image-reference
type: string
workspaces:
- name: shared-data
- name: docker-credentials
tasks:
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-data
params:
- name: url
value: \$(params.repo-url)
- name: build-push
runAfter: ["fetch-source"]
taskRef:
name: kaniko
workspaces:
- name: source
workspace: shared-data
- name: dockerconfig
workspace: docker-credentials
params:
- name: IMAGE
value: \$(params.image-reference)
EOF
# 파이프라인 실행
cat << EOF | kubectl create -f -
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: clone-build-push-run-
spec:
pipelineRef:
name: clone-build-push
taskRunTemplate:
serviceAccountName: build-sa
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: docker-credentials
secret:
secretName: docker-credentials
params:
- name: repo-url
value: https://github.com/gasida/docsy-example.git # 유형욱님이 제보해주신 대로 Dockerfile 에 USER root 추가해두었습니다
- name: image-reference
value: docker.io/gasida/docsy:1.0.0 # 각자 자신의 저장소
EOF
# 결과 확인
kubectl get pod,pv,pvc
tkn pipelinerun logs clone-build-push-run-4kgjr -f
# 로그 확인
kubectl stern clone-build-push-run-5fn7x-build-push-pod
+ clone-build-push-run-5fn7x-build-push-pod › working-dir-initializer
+ clone-build-push-run-5fn7x-build-push-pod › place-scripts
+ clone-build-push-run-5fn7x-build-push-pod › step-build-and-push
+ clone-build-push-run-5fn7x-build-push-pod › prepare
+ clone-build-push-run-5fn7x-build-push-pod › step-write-url
clone-build-push-run-5fn7x-build-push-pod prepare 2025/10/20 13:29:16 Entrypoint initialization
clone-build-push-run-5fn7x-build-push-pod step-build-and-push 2025/10/20 13:29:18 ERROR failed to get CPU variant os=linux error="getCPUVariant for OS linux: not implemented"
- clone-build-push-run-5fn7x-build-push-pod › working-dir-initializer
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0001] Retrieving image manifest floryn90/hugo:ext-alpine
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0001] Retrieving image floryn90/hugo:ext-alpine from registry index.docker.io
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0004] Built cross stage deps: map[]
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0004] Retrieving image manifest floryn90/hugo:ext-alpine
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0004] Returning cached image manifest
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0004] Executing 0 build triggers
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0004] Unpacking rootfs as cmd RUN apk add git && git config --global --add safe.directory /src requires it.
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0011] USER root
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0011] cmd: USER
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0011] RUN apk add git && git config --global --add safe.directory /src
clone-build-push-run-5fn7x-build-push-pod place-scripts 2025/10/20 13:29:16 Decoded script /tekton/scripts/script-1-tdjmw
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0011] Taking snapshot of full filesystem...
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] cmd: /bin/sh
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] args: [-c apk add git && git config --global --add safe.directory /src]
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] util.Lookup returned: &{Uid:0 Gid:0 Username:root Name:root HomeDir:/root}
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] performing slow lookup of group ids for root
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] Running: [/bin/sh -c apk add git && git config --global --add safe.directory /src]
clone-build-push-run-5fn7x-build-push-pod step-build-and-push fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/main/aarch64/APKINDEX.tar.gz
clone-build-push-run-5fn7x-build-push-pod step-build-and-push fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/community/aarch64/APKINDEX.tar.gz
clone-build-push-run-5fn7x-build-push-pod step-build-and-push OK: 88 MiB in 68 packages
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] Taking snapshot of full filesystem...
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0014] Pushing image to docker.io/gasida/docsy:1.0.0
clone-build-push-run-5fn7x-build-push-pod step-build-and-push INFO[0028] Pushed image to 1 destinations
- clone-build-push-run-5fn7x-build-push-pod › step-build-and-push
- clone-build-push-run-5fn7x-build-push-pod › place-scripts
- clone-build-push-run-5fn7x-build-push-pod › prepare
clone-build-push-run-5fn7x-build-push-pod step-write-url docker.io/gasida/docsy:1.0.0
- clone-build-push-run-5fn7x-build-push-pod › step-write-url
결과적으로, Tekton을 사용하여 Git에서 코드를 가져와 이미지를 빌드하고 Docker Registry에 푸시하는 완전한 CI 워크플로를 성공적으로 구현
'IT > Devops' 카테고리의 다른 글
| [cicd] 3주차 - Jenkins + ArgoCD (0) | 2025.11.02 |
|---|---|
| [cicd] 1주차 - Image Build (0) | 2025.10.19 |
| [aws] AI-driven DevOps(feat.gemini,chatgpt) (0) | 2025.09.28 |
| [aws] CICD 환경 구축(CodePipeline, CodeBuild, CodeDeploy) (1) | 2025.08.31 |
| [devops] DevOps Engineering (0) | 2023.10.28 |
- Total
- Today
- Yesterday
- S3
- NFT
- VPN
- k8s calico
- OS
- cni
- terraform
- security
- GCP
- 혼공단
- AI
- autoscaling
- NW
- 혼공챌린지
- IaC
- GKE
- 파이썬
- CICD
- AWS
- cloud
- k8s cni
- 도서
- k8s
- PYTHON
- gcp serverless
- 혼공파
- handson
- operator
- EKS
- SDWAN
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 |
