| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- grafana
- EC2
- Trino
- JavaScript
- helm
- tcp
- docker
- java
- AWS
- kubernetes
- Python
- Operating System
- Vision
- PostgreSQL
- Packet
- ip
- zookeeper
- MAC address
- kubeadm
- OS
- Kafka
- aws s3
- CSV
- kubectl
- jvm
- log
- airflow
- CVAT
- Network
- Spring
- Today
- Total
JUST WRITE
[Talos] 도커 하나면 쿠버네티스 설치가 가능하다고?! 본문

Talos로 Kubernetes 설치
회사에서 솔루션을 개발하게 되었습니다.
개발하기 앞서 필요한 여러 가지 서비스를 설치해야 했습니다.
MySQL, Clickhouse, Minio와 같은 스토리지와 외부 서비스를 구축해야 하였습니다.
처음에는 docker-compose로 관리했지만 서비스가 많아지면서 관리가 너무 힘들었습니다.
Kubernetes를 도입하고 싶었지만 클라우드 k8s 도입 비용이 없었고 GPU 서버 1대만 있는 상황이었습니다.
그러던 중 Talos Linux를 발견했습니다.
What is Talos?

Talos는 Kubernetes 전용으로 설계된 Container 전용 OS입니다.
세가지 특징이 있습니다.
- Container 전용 OS - SSH도 없고 shell도 없습니다. 다만 talosctl를 통해 관리할 수 있습니다.
- 서버에 직접 파일 수정이 힘듭니다.
- Docker Container로 Talos를 실행할 수 있습니다.
GPU 서버가 1대밖에 없는 상황에서 Kubernetes 설치가 불가능해 보였지만
Talos를 통해 Container 형태로 Kubernetes 클러스터를 구축하였습니다.
talosctl를 통한 설치
Talos에서는 talosctl이라는 CLI Client를 제공합니다.
직접 Talos Container를 건드는 것보다는 talosctl를 통해 관리하는 것이 좋습니다.
설치 역시 talosctl로 설치가 가능합니다.
talosctl cluster create \
--name dev \
--config-patch @patch.yaml \
--workers=3 \
--kubernetes-version=v1.34.2 \
--exposed-ports 30080:30080/tcp \
--skip-k8s-node-readiness-check
위와 같은 명령어로 Talos를 통해 Kubernetes 클러스터 설치가 가능합니다.
각 플래그의 의미는 아래 표로 정리하였습니다.
| Flag | 설명 |
| --name dev | Kubernetes 클러스터 명칭 |
| --config-patch @patch.yaml | 추가 설정 파일 적용 |
| --workers=3 | Worker 노드 갯수 |
| --kubernetes-version=v1.34.2 | Kubernetes 버전 |
| --exposed-ports 30080:30080/tcp | Host에서 k8s 접근 포트 노출 |
| --skip-k8s-node-readiness-check | CNI 설치 전까지 NotReady가 정상이므로 대기 skip |
위에 보면 추가 설정 파일을 적용하고 readiness-check를 skip 하였습니다.
따로 설정을 한 이유는 해당 설정 없이 설치할 경우 CoreDNS가 Pending 상태가 되었습니다.
alidating CIDR and reserving IPs
generating PKI and tokens
creating state directory in "/home/***/.talos/clusters/llmops-dev"
creating network dev
creating controlplane nodes
creating worker nodes
waiting for API
bootstrapping cluster
waiting for etcd to be healthy: OK
waiting for etcd members to be consistent across nodes: OK
waiting for etcd members to be control plane nodes: OK
waiting for apid to be ready: OK
waiting for all nodes memory sizes: OK
waiting for all nodes disk sizes: OK
waiting for no diagnostics: OK
waiting for kubelet to be healthy: OK
waiting for all nodes to finish boot sequence: OK
waiting for all k8s nodes to report: OK
waiting for all control plane static pods to be running: OK
waiting for all control plane components to be ready: OK
merging kubeconfig into "/home/***/kube/config"
renamed cluster "dev" -> "dev-1"
renamed auth info "admin@dev" -> "admin@dev-1"
renamed context "admin@dev" -> "admin@dev-1"
PROVISIONER docker
NAME dev
NETWORK NAME dev
NETWORK CIDR 10.5.0.0/24
NETWORK GATEWAY 10.5.0.1
NETWORK MTU 1500
KUBERNETES ENDPOINT https://127.0.0.1:35531
NODES:
NAME TYPE IP CPU RAM DISK
/dev-controlplane-1 controlplane 10.5.0.2 2.00 2.1 GB -
/dev-worker-1 worker 10.5.0.3 2.00 2.1 GB -
/dev-worker-2 worker 10.5.0.4 2.00 2.1 GB -
/dev-worker-3 worker 10.5.0.5 2.00 2.1 GB -
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
dev-controlplane-1 NotReady control-plane 7m57s v1.34.2
dev-worker-1 NotReady <none> 7m57s v1.34.2
dev-worker-2 NotReady <none> 7m56s v1.34.2
dev-worker-3 NotReady <none> 7m56s v1.34.2
$ kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/coredns-6bb5667469-zmqmz 0/1 Pending 0 7m26s
pod/coredns-6bb5667469-zrqjb 0/1 Pending 0 7m26s
pod/kube-apiserver-llmops-dev-controlplane-1 1/1 Running 0 7m8s
pod/kube-controller-manager-llmops-dev-controlplane-1 1/1 Running 3 (7m56s ago) 7m8s
pod/kube-scheduler-llmops-dev-controlplane-1 1/1 Running 4 (7m42s ago) 7m8s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 7m33s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 0/2 2 0 7m33s
NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-6bb5667469 2 2 0 7m26
이유는 기본 설정에서 CNI 제대로 설치가 되지 않았기 때문입니다.
해당 이슈는 아래 Github 이슈에서도 확인할 수 있습니다.
[1.8.0] coredns does not start with talosctl cluster create · Issue #9419 · siderolabs/talos
Bug Report Description When creating a cluster with talosctl and docker, coredns does not start talosctl cluster create View logs talosctl cluster create validating CIDR and reserving IPs generatin...
github.com
별도로 CNI를 세팅하였습니다.
Cilium 설치
CNI는 Kubernetes내 Pod에 네트워크 인터페이스를 부여하는 것입니다.
Cilium을 helm으로 설치하였습니다.
helm repo add cilium https://helm.cilium.io/
helm pull cilium/cilium
tar zxvf cilium-1.18.4.tgz
values-overrides.yaml을 따로 작성해서 만들었습니다.
공식 talos 문서에도 Cilium CNI 설치에 관한 안내가 있습니다.
해당 문서를 참고해서 설치하였습니다.
Deploy Cilium CNI - Sidero Documentation
In this guide you will learn how to set up Cilium CNI on Talos.
docs.siderolabs.com
ipam:
mode: "kubernetes"
kubeProxyReplacement: "true"
securityContext:
capabilities:
ciliumAgent: ["CHOWN","KILL","NET_ADMIN","NET_RAW","IPC_LOCK","SYS_ADMIN","SYS_RESOURCE","DAC_OVERRIDE","FOWNER","SETGID","SETUID"]
cleanCiliumState: ["NET_ADMIN","SYS_ADMIN","SYS_RESOURCE"]
cgroup:
autoMount:
enabled: false
hostRoot: /sys/fs/cgroup
k8sServiceHost: "localhost"
k8sServicePort: 7445
이제 helm install를 통해 Cilium을 설치합니다.
$ helm install cilium -n kube-system -f values-override.yaml .
NAME: cilium
LAST DEPLOYED: Tue Nov 25 10:40:20 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble.
Your release version is 1.18.4.
For any further help, visit https://docs.cilium.io/en/v1.18/gettinghelp
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
dev-controlplane-1 Ready control-plane 12m v1.34.2
dev-worker-1 Ready <none> 12m v1.34.2
dev-worker-2 Ready <none> 12m v1.34.2
dev-worker-3 Ready <none> 12m v1.34.2
Cilium 설치 후 각 Kubernetes 각 Node가 정상적인 상태인 것을 확인할 수 있습니다.
그리고 CoreDNS도 Pending이 아닌 Running 상태인 것을 확인할 수 있습니다.
$ kubetctl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/cilium-25cfl 1/1 Running 0 82s
pod/cilium-2ktjs 1/1 Running 0 82s
pod/cilium-dvjlr 1/1 Running 0 82s
pod/cilium-envoy-6wmh5 1/1 Running 0 82s
pod/cilium-envoy-lg6lq 1/1 Running 0 82s
pod/cilium-envoy-p7gcz 1/1 Running 0 82s
pod/cilium-envoy-t6h9t 1/1 Running 0 82s
pod/cilium-operator-6b565f556c-5xdrr 1/1 Running 0 82s
pod/cilium-operator-6b565f556c-7fkcz 1/1 Running 0 82s
pod/cilium-xtxzw 1/1 Running 0 82s
pod/coredns-6bb5667469-zmqmz 1/1 Running 0 12m
pod/coredns-6bb5667469-zrqjb 1/1 Running 0 12m
pod/kube-apiserver-llmops-dev-controlplane-1 1/1 Running 0 11m
pod/kube-controller-manager-llmops-dev-controlplane-1 1/1 Running 3 (12m ago) 11m
pod/kube-scheduler-llmops-dev-controlplane-1 1/1 Running 4 (12m ago) 11m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cilium-envoy ClusterIP None <none> 9964/TCP 82s
service/hubble-peer ClusterIP 10.105.165.190 <none> 443/TCP 82s
service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 12m
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/cilium 4 4 4 4 4 kubernetes.io/os=linux 82s
daemonset.apps/cilium-envoy 4 4 4 4 4 kubernetes.io/os=linux 82s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/cilium-operator 2/2 2 2 82s
deployment.apps/coredns 2/2 2 2 12m
NAME DESIRED CURRENT READY AGE
replicaset.apps/cilium-operator-6b565f556c 2 2 2 82s
replicaset.apps/coredns-6bb5667469 2 2 2 12m
kubeconfig 설정
위에서 보면 kubectl로 Talos로 설치한 Kuberenetes에 접근이 가능합니다.
kubectl 사용을 위해 kubeconfig를 가져와야 합니다.
talosctl 명령어로 가져올 수 있습니다.
# kubeconfig 파일을 현재 디렉토리로 추출
$ talosctl kubeconfig --nodes 127.0.0.1 dev
# 추출된 파일 확인
$ ls -la
-rw------- 1 user user 2281 11월 25 10:31 dev
# 내용 확인 (실제로는 certificate-authority-data 등 민감정보 포함)
$ cat llmops-dev
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ***
server: https://127.0.0.1:35531
name: dev
...
기본적으로 talosctl cluster create 실행 시 ~/. kubeconfig/config에 자동으로 merge 됩니다.
context 이름은 자동으로 admin@{cluster_name}으로 생성됩니다.
Storageclass 설치
이제 Talos로 Kubernetes 설치를 완료하였습니다.
Talos는 기본적으로 Storageclass를 제공하지 않으므로 직접 설치해야 합니다.
이번 포스팅에서는 rancher의 local-strorage-provisioner로 설치하였습니다.
$ wget https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.32/deploy/local-path-storage.yaml
# version -> v0.0.32 (25/11/25 기준)
apiVersion: v1
kind: Namespace
metadata:
name: local-path-storage
labels:
pod-security.kubernetes.io/enforce: privileged #추가
pod-security.kubernetes.io/audit: privileged # 추가
pod-security.kubernetes.io/warn: privileged # 추가
...
...
...
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path
annotations:
storageclass.kubernetes.io/is-default-class: "true" # 추가
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
...
...
...
kind: ConfigMap
apiVersion: v1
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |-
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/var/lib/local-path-provisioner"] # 수정
}
]
}
setup: |-
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"
helperPod.yaml: |-
apiVersion: v1
kind: Pod
metadata:
name: helper-pod
spec:
priorityClassName: system-node-critical
tolerations:
- key: node.kubernetes.io/disk-pressure
operator: Exists
effect: NoSchedule
containers:
- name: helper-pod
image: busybox
imagePullPolicy: IfNotPresent
$ kubectl apply -f local-path-storage.yaml
namespace/local-path-storage created
serviceaccount/local-path-provisioner-service-account created
role.rbac.authorization.k8s.io/local-path-provisioner-role created
clusterrole.rbac.authorization.k8s.io/local-path-provisioner-role created
rolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
clusterrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
deployment.apps/local-path-provisioner created
storageclass.storage.k8s.io/local-path created
configmap/local-path-config created
$ kubectl get stroageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 48s
설치는 간단합니다.
버전을 참고해서 local-path-provisioner의 yaml 파일을 다운로드하여 설치하면 됩니다.
근데 Namespace에 Pod-security labels을 추가해줘야 합니다.
| Label | 역할 |
| pod-security.kubernetes.io/enforce: privileged | 이 수준을 벗어나는 Pod은 생성 자체가 거부됨. |
| pod-security.kubernetes.io/audit: privileged | 위반 시 감사 로그에 기록됨 |
| pod-security.kubernetes.io/warn: privileged | 위반시 경고메시지를 표시함 |
local-path-provisioner는 내부적으로 호스트 파일 시스템에 직접 접근해서 볼륨을 생성합니다.
이를 위해 hostPath 볼륨과 특권 권한이 필요하는데, 기본 보안 정책 하에서는 이것이 막힙니다.
세 가지 label을 모두 privileged로 설정하면, 해당 Namespace에서 특권 Pod을 자유롭게 실행할 수 있습니다.
Talos 공식문서에서도 local-path-provisioner 설치 내용을 확인할 수 있습니다.
Local Storage - Sidero Documentation
Using local storage for Kubernetes workloads.
docs.siderolabs.com
정리
Talos로 docker container형태로 Kubernetes를 설치하였습니다.
docker ps 명령어를 통해 Kubernetes Node가 Container형태로 구성된 것을 확인할 수 있습니다.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
a1b2c3d4e5f6 ghcr.io/siderolabs/talos:v1.7.6 "/init" 2 min ago Up 2 min dev-controlplane-1
b2c3d4e5f6a7 ghcr.io/siderolabs/talos:v1.7.6 "/init" 2 min ago Up 2 min dev-worker-1
c3d4e5f6a7b8 ghcr.io/siderolabs/talos:v1.7.6 "/init" 2 min ago Up 2 min dev-worker-2
d4e5f6a7b8c9 ghcr.io/siderolabs/talos:v1.7.6 "/init" 2 min ago Up 2 min dev-worker-3
서비스가 너무 많고 서버가 1대라면 docker-compse 말고 Talos를 고려해 보면 좋을 거 같습니다.
Talos 덕분에 솔루션 개발을 위한 서비스 관리나 배포가 쉬울 거 같습니다.
[참고자료]
What is Talos Linux? - Sidero Documentation
Talos Linux is the best OS for Kubernetes.
docs.siderolabs.com
GitHub - siderolabs/talos: Talos Linux is a modern Linux distribution built for Kubernetes.
Talos Linux is a modern Linux distribution built for Kubernetes. - siderolabs/talos
github.com
https://docs.cilium.io/en/stable/
GitHub - rancher/local-path-provisioner: Dynamically provisioning persistent local storage with Kubernetes
Dynamically provisioning persistent local storage with Kubernetes - rancher/local-path-provisioner
github.com
'Infra > Kubernetes' 카테고리의 다른 글
| Kubernetes에 GPU 노드 추가(2) - GPU Worker 노드 추가 (0) | 2024.06.05 |
|---|---|
| Kubernetes에 GPU 노드 추가(1) - GPU 노드 세팅 (0) | 2024.05.14 |
| 업그레이드해도 될까요? - Control Plane Upgrade (0) | 2024.04.25 |
| 여기만 사용해! - 특정 Namespace 전용 User 생성 (0) | 2024.02.15 |
| k8s 날 거부하지 마 - Certificate 만료 갱신 (0) | 2023.10.18 |