배포하기 전에 사전에 렌더링을 진행 할 수 있다. 실제 Chart.yaml, values.yaml 파일을 통해 주입된 값을 가지고 완상된 템플릿을 추출해준다.
helm template .
---
# Source: pacman/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: pacman
name: pacman
spec:
ports:
- name: http
port: 8080
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 # Chart.yaml 파일에 appVersion 여부에 따라 버전을 설정
app.kubernetes.io/version: "1.0.0" # 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" # 이미지 지정 placeholder, 이미지 태그가 있으면 넣고, 없으면 Chart.yaml에 값을 설정
imagePullPolicy: Always
securityContext:
{} # securityContext의 값을 YAML 객체로 지정하며 14칸 들여쓰기
name: pacman
ports:
- containerPort: 8080
name: http
protocol: TCP
# values.yaml 을 수정하지 않고 cli 단에서 바로 수정할 수도 있다.
# set 이 우선순위가 더 높기에 set으로 지정하면 values를 덮어쓴다.
helm template --set replicaCount=3 .
...
spec:
replicas: 3 # replicaCount 속성을 넣을 자리 placeholder
helm chart 배포 하기
# 설치
helm install pacman .
# 확인
helm list
---
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
pacman default 1 2025-10-20 11:22:13.929231 +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 Mon Oct 20 11:22:13 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 73s
# secret 값 조회
k describe secrets sh.helm.release.v1.pacman.v1
---
Name: sh.helm.release.v1.pacman.v1
Namespace: default
Labels: modifiedAt=1760926934
name=pacman
owner=helm
status=deployed
version=1
Annotations: <none>
Type: helm.sh/release.v1
Data
====
release: 2760 bytes
업그레이드 및 메타 데이터 조회
# replica 2로 업그레이드, --reuse-values: 기존 values 재 사용, --set: replicaCount 값 덮어쓰기
helm upgrade pacman --reuse-values --set replicaCount=2 .
kubectl get pod
NAME READY STATUS RESTARTS AGE
pacman-576769bb86-mn9tt 0/1 ImagePullBackOff 0 3m39s
pacman-576769bb86-mwf2q 0/1 ErrImagePull 0 3s
# 메타데이터 변경사항 조회
kubectl get secret
NAME TYPE DATA AGE
sh.helm.release.v1.pacman.v1 helm.sh/release.v1 1 11m
sh.helm.release.v1.pacman.v2 helm.sh/release.v1 1 20s
# helm 배포 정보 확인
helm get all pacman # 모든 정보
helm get values pacman # values 적용 정보
helm get manifest pacman # 실제 적용된 manifest
helm get notes pacman # chart nodes
# 삭제 후 secret 확인
helm uninstall pacman
kubectl get secret
템플릿 재사용
helm의 목적은 템플릿을 재사용 하여 유지보수를 높이는 것이다. 일회성으로 매번 생성한다면 생산성이 크게 떨어지기 때문이다.
템플릿 함수를 정의하는 파일명으로는_helpers.tpl을 사용하는 것이 일반적이지만, 사실_로 시작하기만 하면 된다.
_ 로 시작하는 팔일은 쿠버네티스 매니패스트 파일로 취급받지 않는다.
_helpers.tpl 파일이란?
Helm 차트의 templates/ 디렉터리 내에서 파일명이 언더바로 시작하는 파일들은 일반 매니페스트로 바로 렌더링되지 않고, 헬름 템플릿 엔진 내부에서 헬퍼 또는 부분 템플릿 용도로 사용
사용하는 이유
코드 중복제거: 리소스 생성로직이 반복된다면 _helpers.tpl에 정의해두고 각 템플릿에서는 호출만해서 사용
템플릿 유지보수성 향상: _helpers.tpl 한 곳만 변경하면 모든 곳 동일하게 변경
복잡한 계산/포맷 캡슐화
템플릿 가독성 향상
# 시나리오
# 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
## 파일 수정. 반드시 nindent를 고려해야 한다. nindent 는 white space이다.
### 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만큼 들여쓰기하여 주입
##3 service.yaml 수정
selector:
{{- include "pacman.selectorLabels" . | nindent 6 }} # pacman.selectorLabels를 호출한 결과를 6만큼 들여쓰기하여 주입
# 변경된 차트를 로컬에서 YAML 렌더링 : _helpers.tpl 설정된 값으로 갱신 확인
helm template .
---
# Source: pacman/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
# stetement 이름을 정의
app.kubernetes.io/name: pacman # 해당 stetement 가 하는 일을 정의
# _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 .
helm 컨테이너 이미지 변경
배포 파일에서 컨테이너 이미지를 갱신하고 실행 중인 인스턴스를 업그레이드 할 수 있다
배포된 helm chart를 upgrade 만으로 새로운 revision을 만들어서 배포하게 된다. 버전 관리는 Chart에서 appVersion 또는 이미지 tag로 관리할 수 있다.
# _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
---
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Mon Oct 20 11:43:34 2025 deployed pacman-0.1.0 1.0.0 Install complete
# 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: Mon Oct 20 11:44:16 2025
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
# 확인
helm history pacman
---
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Mon Oct 20 11:43:34 2025 superseded pacman-0.1.0 1.0.0 Install complete
2 Mon Oct 20 11:44:16 2025 deployed pacman-0.1.0 1.1.0 Upgrade complete
kubectl get secret
kubectl get deploy,replicaset -owide
롤백
# 이전 버전으로 롤백
helm history pacman
helm rollback pacman 1 && kubectl get pod -w
# 확인, 롤백한 버전이 revision 3이 되었다.
helm history pacman
---
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Mon Oct 20 11:43:34 2025 superseded pacman-0.1.0 1.0.0 Install complete
2 Mon Oct 20 11:44:16 2025 superseded pacman-0.1.0 1.1.0 Upgrade complete
3 Mon Oct 20 11:46:44 2025 deployed pacman-0.1.0 1.0.0 Rollback to 1
kubectl get secret
kubectl get deploy,replicaset -owide
새로운 values 파일 override. 새로운 values를 기존 values에 덮어 씌울 수 있다. 이 부분은 멀티 환경에서 매우 유용할 것 같다.
# 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"
...
helm chart 패키징과 배포
다른 유저가 helm chart를 재사용 할 수 있도록 패키징하여 배포할 수 있다.
helm package명령어를 사용하여 패키징한다.
# 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-20T11:51:20.865546+09:00"
description: A Helm chart for Pacman
digest: 95d1fb6e020038e41139eafc95b171d026d484b2776262e0bd7a98df1864ef87
name: pacman
type: application
urls:
- pacman-0.1.0.tgz
version: 0.1.0
generated: "2025-10-20T11:51:20.865074+09:00"
# 디렉터리 구조
tree .
---
.
├── Chart.yaml
├── index.yaml
├── pacman-0.1.0.tgz
├── templates
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ └── service.yaml
└── values.yaml
저장소로 부터 helm chart 배포
내가 차트를 만들지 않고 다른 유저가 만들어 놓은 차트를 저장소로 부터 가져와서 배포할 수 있다.
# repo 추가
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo list
---
NAME URL
bitnami https://charts.bitnami.com/bitnami
# repo로 부터 차트 검색
helm search repo postgresql
helm search repo postgresql -o json | jq
---
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...
# repo로 부터 차트를 배포하기
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-20 13:47:54.1578 +0900 KST deployed postgresql-18.0.17 18.0.0
# 서드 파티 차트 사용 시, 기본값(default value)나 override 파라미터를 직접 확인 할 수 없고, helm show 로 확인 가능
helm show values bitnami/postgresql
# 실습 후 삭제
helm uninstall my-db
의존성과 함께 차트 배포
어떤 차트가 다른 차트에 의존한다는 사실을 선언할 수 있고 Chart.yaml 에서dependencies섹션에 명시할 수 있다.
PostgreSQL 데이터베이스에 저장된 노래 목록을 반환하는 Java 서비스를 가지고 실습을 진행해본다.
mkdir -p music/templates && cd music
# deployment 파일
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
# 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
# 의존성 명시: 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...
# 의존성 명시: psql 버전 명시 의 경우, 현재 18.0.17 이 최신 버전이므로 해당 버전 사용
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
repository: "https://charts.bitnami.com/bitnami"
EOF
# values 파일 작성
cat << EOF > values.yaml
image:
repository: quay.io/gitops-cookbook/music
tag: latest
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
---
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading postgresql from repo https://charts.bitnami.com/bitnami
Pulled: registry-1.docker.io/bitnamicharts/postgresql:18.0.17
Digest: sha256:84b63af46f41ac35e3cbcf098e8cf124211c250807cfed43f7983c39c6e30b72
Deleting outdated charts
# update 이후 tree
.
├── Chart.lock
├── Chart.yaml
├── charts
│ └── postgresql-18.0.17.tgz
├── templates
│ ├── deployment.yaml
│ └── service.yaml
└── values.yaml
3 directories, 6 files
# Chart.lock
# 의존성 업데이트 이후 lock 파일도 생성되며 의존성 정보가 기입되어 있다.
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 18.0.17
digest: sha256:4af693b17381c8b2e34298ce6eaf6e63d831bdfd0b2b88ca37b1abac4f5d556e
generated: "2025-10-20T13:58:57.261927+09:00"
# 차트 배포
helm install music-db .
# 확인
kubectl get sts,pod,svc,ep,secret,pv,pvc
# TS 1 : secret 에 키/값 추가
kubectl edit secret music-db-postgresql
postgresql-password: cG9zdGdyZXMK
# 에러가 발생하는데 뭔가 비번이 잘못됬는지 에러가 난다. 설정도 이상한게 없는데 뭔지는 모르겠다.
# database 접근하기
echo "M011bEMzNWpWMw==" | base64 -d
3MulC35jV3
kubectl exec -it music-db-postgresql-0 -- psql -U postgres -c "\du"
Password for user postgres: 3MulC35jV3
List of roles
Role name | Attributes
-----------+------------------------------------------------------------
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
(책에는 정확한 정보가 안나와있다)
helm chart 롤링업데이트
sha256sum 템플릿 함수를 활용해서 Deployment 리소스의 Pod 템플릿에 변경값(체크섬)을 주입한다.
Kustomize 사용 시: ConfigMapGenerator를 통해 ConfigMap이 변경되면 메타데이터 이름에 해시값을 붙이고, Deployment가 그 해시 이름을 참조하도록 구성.
Helm 사용 시: 각 템플릿 파일(예: configmap.yaml)에 대해 체크섬을 계산해 Deployment annotation에 삽입하면, ConfigMap 내용이 바뀔 때마다 Pod 템플릿(metadata)이 바뀌고 결과적으로 롤링 업데이트가 트리거된다.
참고: ‘Secret, ConfigMap 변경 시 자동으로 Deployments, StatefulSets 등에 롤아웃’을 지원하는 도구로 Stakater Reloader 가 있다.
tekton에서 task는 작업 수행에 필요한 로직을 순차적으로 실행하는 일련의 step 들로 정의 된다.
모든 task는 pod로 실행되며, 각 step은 자체 컨테이너에서 실행된다.
# 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
---
NAME DESCRIPTION AGE
hello 21 seconds ago
kubectl get tasks
---
NAME AGE
hello 2m32s
kubectl get pod
---
# task가 실행되지는 않아 pipeline pod 들만 있다.
task 실행하기
# 신규 터미널 : 파드 상태 모니터링, task pod가 실행되며 2개의 init container가 실행된것을 볼 수 있다.
kubectl get pod -w
---
NAME READY STATUS RESTARTS AGE
hello-run-xbxpr-pod 0/1 Pending 0 0s
hello-run-xbxpr-pod 0/1 Pending 0 0s
# 2개의 init container
hello-run-xbxpr-pod 0/1 Init:0/2 0 0s
hello-run-xbxpr-pod 0/1 Init:1/2 0 7s
hello-run-xbxpr-pod 0/1 PodInitializing 0 11s
# 실제 step 실행
hello-run-xbxpr-pod 1/1 Running 0 16s
hello-run-xbxpr-pod 1/1 Running 0 16s
hello-run-xbxpr-pod 0/1 Completed 0 18s
hello-run-xbxpr-pod 0/1 Completed 0 19s
# tkn CLI로 task 시작
tkn task start --showlog hello
# init container 로그 확인
# initial과 관련된 로그들이 보인다.
kubectl logs -l tekton.dev/task=hello -c prepare
2025/10/21 15:21:38 Entrypoint initialization
kubectl logs -l tekton.dev/task=hello -c place-scripts
2025/10/21 15:21:42 Decoded script /tekton/scripts/script-0-mcmkj
# step이 실행된 container의 로그 확인
kubectl logs -l tekton.dev/task=hello -c step-echo
---
Hello World
# kubectl 뿐만 아니라 tekton cli로도 확인할 수 있다.
tkn task logs hello
tkn task describe hello
# task 삭제
kubectl delete taskruns --all
Create a Task to Compile and Package an App from Git
텍톤을 사용하여 Git 저장소에 보관된 앱 코드를 컴파일하고 패키징하는 작업을 자동화하는 방법에 대해서 다뤄 본다.
추후 파이프라인을 만들 때 쓸 수 있도록 input과 output이 잘 규정된 Task를 만들어 본다.
Step1: Tekton Pipelines를 사용하여 git에서 소스 코드를 복제 : Git 저장소에서 소스 코드를 복제하는 작업
Task에 속한 각 단계 step 와 Task 는 텍톤 워크스페이스workspace라 불리는 파일 시스템을 공유할 수 있다.
이 공유 파일 시스템은 PVC로 만들어진 파일 시스템이거나 ConfigMap 혹은 emptyDir 불륨을 사용한다.
Task 는param인자를 받을 수 있으며, 그 인자를 통해 실제 작업 내용을 동적으로 결정할 수 있다.
시나리오
git repo를 input으로 주입받아 정의된 clone을 하는 파이프라인을 구성한다.
# 파이프라인 파일 작성
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
---
NAME AGE LAST RUN STARTED DURATION STATUS
clone-read 26 seconds ago --- --- --- ---
tkn pipeline describe
---
Name: clone-read
Namespace: default
Description: This pipeline clones a git repo, then echoes the README file to the stout.
⚓ Params
NAME TYPE DESCRIPTION DEFAULT VALUE
∙ repo-url string The git repo URL to... ---
📂 Workspaces
NAME DESCRIPTION OPTIONAL
∙ shared-data This workspace cont... false
🗒 Tasks
NAME TASKREF RUNAFTER TIMEOUT PARAMS
∙ fetch-source git-clone --- url: string
# 딱히 별도의 pipeline이 생성된 것 같지는 않다.
kubectl get pod -A
---
tekton-pipelines-resolvers tekton-pipelines-remote-resolvers-86f56b6664-prkbs 1/1 Running 2 (14h ago) 23h
tekton-pipelines tekton-dashboard-7d4499b584-tdfqk 1/1 Running 2 (14h ago) 23h
tekton-pipelines tekton-events-controller-99665746c-8r44r 1/1 Running 2 (14h ago) 23h
tekton-pipelines tekton-pipelines-controller-7595d6585d-zsphc 1/1 Running 2 (14h ago) 23h
tekton-pipelines tekton-pipelines-webhook-5967d74cc4-hwvl8 1/1 Running 2 (14h ago) 23h
tekton-pipelines tekton-triggers-controller-74fccfc888-nbvpx 1/1 Running 2 (14h ago) 23h
tekton-pipelines tekton-triggers-core-interceptors-7b8dcb59fb-769m7 1/1 Running 1 (14h ago) 23h
tekton-pipelines tekton-triggers-webhook-5465cc8d5b-8h6qr 1/1 Running 2 (14h ago) 23h
ssh 인증보다는 token 으로 인증한다. 실제 라이브 환경에서 ssh 인증을 잘 사용하지 않는다.
Step1: 샘플 앱 생성 및 Git 초기화
# 작업 폴더 생성
mkdir my-sample-app
cd my-sample-app
# 샘플 파일 만들기 (Node.js 예시)
echo 'console.log("Hello GitHub!");' > app.js
echo "# sample app" > readme.md
# 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"
Step2: GitHub remote 연결 및 push
# origin remote 등록:
git remote add origin https://github.com/<your-username>/my-sample-app.git
# 메인 브랜치 이름을 main으로 변경 (GitHub 기본 브랜치와 맞춤):
git branch -M main
# Push!
git push -u origin main
---
Username for 'https://github.com': <your-username>
Password for 'https://<your-username>@github.com': <2번 토큰>
Step3: git credential 생성
# git credential 생성
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: git-credentials
annotations:
tekton.dev/git-0: https://github.com/<your-username>/my-sample-app.git # 나의 git 주소
type: kubernetes.io/basic-auth
stringData:
username: <your-username>
password: <your-token>
EOF
# 확인
kubectl get secret
NAME TYPE DATA AGE
git-credentials kubernetes.io/basic-auth 2 19s
# 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 8s
Step4: pipeline 실행
# 파이프라인 파일 작성
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 git token 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
NAME AGE LAST RUN STARTED DURATION STATUS
my-clone-read 3 seconds ago --- --- --- ---
tkn pipeline describe
kubectl get pipeline
NAME AGE
my-clone-read 29s
# yaml로 상세확인
kubectl get pipeline -o yaml | kubectl neat | yq
# task를 시작하지 않으므로 아무런 pod 없음
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
# 파이프라인 실행
# params에 꼭 자신의 github주소를 지정하고 token 으로 credential을 생성했기에 https 주소로 입력한다.
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: https://github.com/<your-username>/my-sample-app.git # 사용자 github 주소로 지정하기!
EOF
# 결과 확인 : 2개의 step 으로 각기 2개의 파드가 실행됨을 확인
kubectl get pod,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/affinity-assistant-365a7c8dcd-0 1/1 Running 0 6s
pod/clone-read-run-sn5pc-fetch-source-pod 0/1 PodInitializing 0 6s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-9754b648-fbf1-46e3-b13b-bbc2b5b43fb1 1Gi RWO Delete Bound default/pvc-d4206cb781 standard <unset> 4s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/pvc-d4206cb781 Bound pvc-9754b648-fbf1-46e3-b13b-bbc2b5b43fb1 1Gi RWO standard <unset> 6s
# task 를 실행하는 파드에 service acount 정보 확인
kubectl describe pod | grep 'Service Account'
---
Service Account: default
Service Account: build-bot
Service Account: build-bot
Service Account: build-bot
Service Account: build-bot
Service Account: build-bot
Service Account: default
Service Account: music-db-postgresql
실행 결과
Containerize an Application Using a Tekton Task and Buildah
텍톤 Task를 사용하여 git를 통하여 소스코드를 가지고 이미지를 빌드하고 container registry에 pus하는 파이프라인을 구성하는 실습이다.
텍톤의 확장 가능한 모델 덕분에 이전에서 사용한 Task를 재사용하는 것이 가능하다.
이전 단계 step 의 결과물을 가져와 컨테이너 이미지를 만드는 새로운 단계를 그림같이 추가하는 방식이다
Build Push app
동작
Git 저장소에서 소스 코드를 복제하는 작업을 만듭니다.
복제된 코드를 사용하여 Docker 이미지를 빌드kaniko하고 레지스트리에 푸시하는 두 번째 작업을 만듭니다.
사전 준비 : 비공개 컨테이너 이미지 저장소 인증 정보(토큰 등)
# task 설치 : https://hub.tekton.dev/tekton/task/kaniko
tkn hub install task kaniko
kubectl get tasks
---
NAME AGE
git-clone 41m
hello 67m
kaniko 55s
show-readme 13m
# Docker 자격 증명으로 Secret을 적용
## macOS 경우
-------------------------------------------------
# 아래 명령어를 실행하면 username과 secret 정보를 얻을 수 있다.
echo "https://index.docker.io/v1/" | docker-credential-osxkeychain get | jq
---
{
"ServerURL": "https://index.docker.io/v1/",
"Username": "이 값과",
"Secret": "요값을 사용함,dckr_pat_Cs5gwal97c6sjsHhe_Z34dAt4co"
}
echo -n "<Username>:<Secret>" | base64
AXDFGHXXCFGFGF==
# ~/.docker/config.json 대신 임시 파일 dsh.txt 작성
cat > dsh.txt <<'EOF'
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "AXDFGHXXCFGFGF=="
}
}
}
EOF
# 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
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-sa
namespace: default
secrets:
- name: docker-credentials
# 파이프라인 파일 작성
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/hanship0915/docsy:1.0.0 # 각자 자신의 저장소
EOF
# 결과 확인
kubectl get pod,pv,pvc
---
NAME READY STATUS RESTARTS AGE
pod/affinity-assistant-02e684853d-0 1/1 Running 0 7s
pod/clone-build-push-run-nvxwq-fetch-source-pod 1/1 Running 0 7s
pod/clone-read-run-qfvq8-fetch-source-pod 0/1 Completed 0 11m
pod/clone-read-run-qfvq8-show-readme-pod 0/1 Completed 0 11m
pod/clone-read-run-sn5pc-fetch-source-pod 0/1 Error 0 22m
pod/clone-read-run-tq2z4-fetch-source-pod 0/1 Completed 0 16m
pod/clone-read-run-tq2z4-show-readme-pod 0/1 Error 0 15m
pod/clone-read-run-zbnhr-fetch-source-pod 0/1 Completed 0 18m
pod/clone-read-run-zbnhr-show-readme-pod 0/1 Error 0 18m
tkn pipelinerun list
NAME STARTED DURATION STATUS
clone-build-push-run-nvxwq 1 minute ago 1m8s Succeeded
tkn pipelinerun logs clone-build-push-run-nvxwq -f
# 로그 확인
kubectl stern clone-build-push-run-5fn7x-build-push-pod
[fetch-source : clone] + '[' false '=' true ]
[fetch-source : clone] + '[' false '=' true ]
[fetch-source : clone] + '[' false '=' true ]
[fetch-source : clone] + CHECKOUT_DIR=/workspace/output/
[fetch-source : clone] + '[' true '=' true ]
[fetch-source : clone] + cleandir
[fetch-source : clone] + '[' -d /workspace/output/ ]
[fetch-source : clone] + rm -rf '/workspace/output//*'
[fetch-source : clone] + rm -rf '/workspace/output//.[!.]*'
[fetch-source : clone] + rm -rf '/workspace/output//..?*'
[fetch-source : clone] + test -z
[fetch-source : clone] + test -z
[fetch-source : clone] + test -z
[fetch-source : clone] + git config --global --add safe.directory /workspace/output
[fetch-source : clone] + /ko-app/git-init '-url=https://github.com/gasida/docsy-example.git' '-revision=' '-refspec=' '-path=/workspace/output/' '-sslVerify=true' '-submodules=true' '-depth=1' '-sparseCheckoutDirectories='
[fetch-source : clone] {"level":"info","ts":1761064488.694023,"caller":"git/git.go:176","msg":"Successfully cloned https://github.com/gasida/docsy-example.git @ 36776dc210006efaa6b487fe5cc772d466436cc6 (grafted, HEAD) in path /workspace/output/"}
[fetch-source : clone] {"level":"info","ts":1761064488.7060614,"caller":"git/git.go:215","msg":"Successfully initialized and updated submodules in path /workspace/output/"}
[fetch-source : clone] + cd /workspace/output/
[fetch-source : clone] + git rev-parse HEAD
[fetch-source : clone] + RESULT_SHA=36776dc210006efaa6b487fe5cc772d466436cc6
[fetch-source : clone] + EXIT_CODE=0
[fetch-source : clone] + '[' 0 '!=' 0 ]
[fetch-source : clone] + git log -1 '--pretty=%ct'
[fetch-source : clone] + RESULT_COMMITTER_DATE=1760966900
[fetch-source : clone] + printf '%s' 1760966900
[fetch-source : clone] + printf '%s' 36776dc210006efaa6b487fe5cc772d466436cc6
[fetch-source : clone] + printf '%s' https://github.com/gasida/docsy-example.git
[build-push : build-and-push] 2025/10/21 16:35:07 ERROR failed to get CPU variant os=linux error="getCPUVariant for OS linux: not implemented"
[build-push : build-and-push] INFO[0001] Retrieving image manifest floryn90/hugo:ext-alpine
[build-push : build-and-push] INFO[0001] Retrieving image floryn90/hugo:ext-alpine from registry index.docker.io
[build-push : build-and-push] INFO[0003] Built cross stage deps: map[]
[build-push : build-and-push] INFO[0003] Retrieving image manifest floryn90/hugo:ext-alpine
[build-push : build-and-push] INFO[0003] Returning cached image manifest
[build-push : build-and-push] INFO[0003] Executing 0 build triggers
[build-push : build-and-push] INFO[0003] Unpacking rootfs as cmd RUN apk add git && git config --global --add safe.directory /src requires it.
[build-push : build-and-push] INFO[0020] USER root
[build-push : build-and-push] INFO[0020] cmd: USER
[build-push : build-and-push] INFO[0020] RUN apk add git && git config --global --add safe.directory /src
[build-push : build-and-push] INFO[0020] Taking snapshot of full filesystem...
[build-push : build-and-push] INFO[0022] cmd: /bin/sh
[build-push : build-and-push] INFO[0022] args: [-c apk add git && git config --global --add safe.directory /src]
[build-push : build-and-push] INFO[0022] util.Lookup returned: &{Uid:0 Gid:0 Username:root Name:root HomeDir:/root}
[build-push : build-and-push] INFO[0022] performing slow lookup of group ids for root
[build-push : build-and-push] INFO[0022] Running: [/bin/sh -c apk add git && git config --global --add safe.directory /src]
[build-push : build-and-push] fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/main/aarch64/APKINDEX.tar.gz
[build-push : build-and-push] fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/community/aarch64/APKINDEX.tar.gz
[build-push : build-and-push] OK: 88 MiB in 68 packages
[build-push : build-and-push] INFO[0023] Taking snapshot of full filesystem...
[build-push : build-and-push] INFO[0023] Pushing image to docker.io/hanship0915/docsy:1.0.0
[build-push : build-and-push] INFO[0031] Pushed image to 1 destinations
[build-push : write-url] docker.io/<username>/docsy:1.0.0
# 다음 실습을 위해 삭제
kubectl delete taskruns,pipelineruns.tekton.dev --all