** 가시다님이 진행하는 CI/CD Study 내용을 기반으로 정리 및 작성하였습니다.
1. 실습 환경 구성
- kind k8s 배포
(⎈|N/A:N/A) gaji:~$ kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
Cluster> kind: Cluster
> apiVersion: kind.x-k8s.io/v1alpha4
> nodes:
> - role: control-plane
abels:
> labels:
> ingress-ready: true
> - role: control-plane
abels:
> labels:
> ingress-ready: true
> extraPortMappings:
> - containerPort: 80
> hostPort: 80
> protocol: TCP
> - containerPort: 443
> hostPort: 443
otocol:> protocol: TCP
> - containerPort: 30000
> - containerPort: 443
> hostPort: 443
otocol:> protocol: TCP
> - containerPort: 30000
stPort:> hostPort: 30000
> - containerPort: 30001
stPort:> hostPort: 30000
> - containerPort: 30001
> hostPort: 30001
> - containerPort: 30002
> hostPort: 30002
> - containerPort: 30003
> hostPort: 30003
> EOF
Creating cluster "myk8s" ...
✓ Ensuring node image (kindest/node:v1.32.8) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-myk8s"
You can now use your cluster with:
kubectl cluster-info --context kind-myk8s
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
(⎈|kind-myk8s:N/A) gaji:~$ helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
"geek-cookbook" already exists with the same configuration, skipping
(⎈|kind-myk8s:N/A) gaji:~$ helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30001 --set env.TZ="Asia/Seoul" --namespace kube-system
NAME: kube-ops-view
LAST DEPLOYED: Thu Nov 13 22:21:06 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace kube-system -o jsonpath="{.spec.ports[0].nodePort}" services kube-ops-view)
export NODE_IP=$(kubectl get nodes --namespace kube-system -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
- ingress-nginx 배포
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
{
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"ingress-ready": "true",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "myk8s-control-plane",
"kubernetes.io/os": "linux",
"node-role.kubernetes.io/control-plane": ""
}
(⎈|kind-myk8s:N/A) gaji:~$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 72s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.96.232.176 <pending> 80:32660/TCP,443:31148/TCP 72s
NAME ENDPOINTS AGE
endpoints/ingress-nginx-controller 10.244.0.8:443,10.244.0.8:80 72s
(⎈|kind-myk8s:N/A) gaji:~$ kubectl describe -n ingress-nginx deployments/ingress-nginx-controller
Name: ingress-nginx-controller
Namespace: ingress-nginx
CreationTimestamp: Thu, 13 Nov 2025 22:23:36 +0900
Labels: app.kubernetes.io/component=controller
app.kubernetes.io/instance=ingress-nginx
app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/part-of=ingress-nginx
app.kubernetes.io/version=1.14.0
Annotations: deployment.kubernetes.io/revision: 1
Selector: app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 25% max surge
Pod Template:
Labels: app.kubernetes.io/component=controller
app.kubernetes.io/instance=ingress-nginx
app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/part-of=ingress-nginx
app.kubernetes.io/version=1.14.0
Service Account: ingress-nginx
Containers:
controller:
Image: registry.k8s.io/ingress-nginx/controller:v1.14.0@sha256:e4127065d0317bd11dc64c4dd38dcf7fb1c3d72e468110b4086e636dbaac943d
Ports: 80/TCP (http), 443/TCP (https), 8443/TCP (webhook)
Host Ports: 80/TCP (http), 443/TCP (https), 0/TCP (webhook)
SeccompProfile: RuntimeDefault
Args:
/nginx-ingress-controller
--election-id=ingress-nginx-leader
--controller-class=k8s.io/ingress-nginx
--ingress-class=nginx
--configmap=$(POD_NAMESPACE)/ingress-nginx-controller
--validating-webhook=:8443
--validating-webhook-certificate=/usr/local/certificates/cert
--validating-webhook-key=/usr/local/certificates/key
--watch-ingress-without-class=true
--publish-status-address=localhost
Requests:
cpu: 100m
memory: 90Mi
Liveness: http-get http://:10254/healthz delay=10s timeout=1s period=10s #success=1 #failure=5
Readiness: http-get http://:10254/healthz delay=10s timeout=1s period=10s #success=1 #failure=3
Environment:
POD_NAME: (v1:metadata.name)
POD_NAMESPACE: (v1:metadata.namespace)
LD_PRELOAD: /usr/local/lib/libmimalloc.so
Mounts:
/usr/local/certificates/ from webhook-cert (ro)
Volumes:
webhook-cert:
Type: Secret (a volume populated by a Secret)
SecretName: ingress-nginx-admission
Optional: false
Node-Selectors: kubernetes.io/os=linux
Tolerations: node-role.kubernetes.io/control-plane:NoSchedule
node-role.kubernetes.io/master:NoSchedule
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: ingress-nginx-controller-8676d56f78 (1/1 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 91s deployment-controller Scaled up replica set ingress-nginx-controller-8676d56f78 from 0 to 1
(⎈|kind-myk8s:N/A) gaji:~$ kubectl exec -it -n ingress-nginx deployments/ingress-nginx-controller -- /nginx-ingress-controller --help | grep ssl
--default-ssl-certificate string Secret containing a SSL certificate to be used by the default HTTPS server (catch-all).
--enable-ssl-chain-completion Autocomplete SSL certificate chains with missing intermediate CA certificates.
--enable-ssl-passthrough Enable SSL Passthrough.
--ssl-passthrough-proxy-port int Port to use internally for SSL Passthrough. (default 442)
kubectl get secret -n argocd
# SSL-Passthrough : The 'tls: true' option will expect that the 'argocd-server-tls' secret exists as Argo CD server loads TLS certificates from this place.
## certificate.enabled=true 는 cert-manager가 있을 때만 동작하는 것 같네요
## 이번 실습환경 구성에서는 argocd-server-tls 를 사전에 만들어서 알아서 참조하는 구조네요. (다른 시크릿 사용시 certificateSecret 사용)
cat <<EOF > argocd-values.yaml
global:
domain: argocd.example.com
server:
ingress:
enabled: true
ingressClassName: nginx
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
tls: true
EOF
# 설치 : Argo CD v3.1.9 , (참고) 책 버전 Argo CD v2.1 ~ v2.2
# https://github.com/argoproj/argo-helm/blob/main/charts/argo-cd/values.yaml
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 9.0.5 -f argocd-values.yaml --namespace argocd
NAME: argocd
LAST DEPLOYED: Thu Nov 13 22:43:31 2025
NAMESPACE: argocd
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
In order to access the server UI you have the following options:
1. kubectl port-forward service/argocd-server -n argocd 8080:443
and then open the browser on http://localhost:8080 and accept the certificate
2. enable ingress in the values file `server.ingress.enabled` and either
- Add the annotation for ssl passthrough: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress/#option-1-ssl-passthrough
- Set the `configs.params."server.insecure"` in the values file and terminate SSL at your ingress: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress/#option-2-multiple-ingress-objects-and-hosts
After reaching the UI the first time you can login with username: admin and the random password generated during the installation. You can find the password by running:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
(You should delete the initial secret afterwards as suggested by the Getting Started Guide: https://argo-cd.readthedocs.io/en/stable/getting_started/#4-login-using-the-cli)
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get all -n argocd
NAME READY STATUS RESTARTS AGE
pod/argocd-application-controller-0 1/1 Running 0 25s
pod/argocd-applicationset-controller-bbff79c6f-749j7 1/1 Running 0 25s
pod/argocd-dex-server-6877ddf4f8-srvw9 1/1 Running 0 25s
pod/argocd-notifications-controller-7b5658fc47-9mz8x 1/1 Running 0 25s
pod/argocd-redis-7d948674-stpw8 1/1 Running 0 25s
pod/argocd-redis-secret-init-pc25b 0/1 Completed 0 100s
pod/argocd-repo-server-7679dc55f5-96qs4 1/1 Running 0 25s
pod/argocd-server-7d769b6f48-tqbxt 1/1 Running 0 25s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/argocd-applicationset-controller ClusterIP 10.96.112.55 <none> 7000/TCP 25s
service/argocd-dex-server ClusterIP 10.96.102.46 <none> 5556/TCP,5557/TCP 25s
service/argocd-redis ClusterIP 10.96.46.91 <none> 6379/TCP 25s
service/argocd-repo-server ClusterIP 10.96.230.23 <none> 8081/TCP 25s
service/argocd-server ClusterIP 10.96.144.44 <none> 80/TCP,443/TCP 25s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/argocd-applicationset-controller 1/1 1 1 25s
deployment.apps/argocd-dex-server 1/1 1 1 25s
deployment.apps/argocd-notifications-controller 1/1 1 1 25s
deployment.apps/argocd-redis 1/1 1 1 25s
deployment.apps/argocd-repo-server 1/1 1 1 25s
deployment.apps/argocd-server 1/1 1 1 25s
NAME DESIRED CURRENT READY AGE
replicaset.apps/argocd-applicationset-controller-bbff79c6f 1 1 1 25s
replicaset.apps/argocd-dex-server-6877ddf4f8 1 1 1 25s
replicaset.apps/argocd-notifications-controller-7b5658fc47 1 1 1 25s
replicaset.apps/argocd-redis-7d948674 1 1 1 25s
replicaset.apps/argocd-repo-server-7679dc55f5 1 1 1 25s
replicaset.apps/argocd-server-7d769b6f48 1 1 1 25s
NAME READY AGE
statefulset.apps/argocd-application-controller 1/1 25s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/argocd-redis-secret-init Complete 1/1 75s 100s
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get pod,ingress,svc,ep,secret,cm -n argocd
NAME READY STATUS RESTARTS AGE
pod/argocd-application-controller-0 1/1 Running 0 47s
pod/argocd-applicationset-controller-bbff79c6f-749j7 1/1 Running 0 47s
pod/argocd-dex-server-6877ddf4f8-srvw9 1/1 Running 0 47s
pod/argocd-notifications-controller-7b5658fc47-9mz8x 1/1 Running 0 47s
pod/argocd-redis-7d948674-stpw8 1/1 Running 0 47s
pod/argocd-redis-secret-init-pc25b 0/1 Completed 0 2m2s
pod/argocd-repo-server-7679dc55f5-96qs4 1/1 Running 0 47s
pod/argocd-server-7d769b6f48-tqbxt 1/1 Running 0 47s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/argocd-server nginx argocd.example.com localhost 80, 443 47s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/argocd-applicationset-controller ClusterIP 10.96.112.55 <none> 7000/TCP 47s
service/argocd-dex-server ClusterIP 10.96.102.46 <none> 5556/TCP,5557/TCP 47s
service/argocd-redis ClusterIP 10.96.46.91 <none> 6379/TCP 47s
service/argocd-repo-server ClusterIP 10.96.230.23 <none> 8081/TCP 47s
service/argocd-server ClusterIP 10.96.144.44 <none> 80/TCP,443/TCP 47s
NAME ENDPOINTS AGE
endpoints/argocd-applicationset-controller 10.244.0.12:7000 47s
endpoints/argocd-dex-server 10.244.0.11:5557,10.244.0.11:5556 47s
endpoints/argocd-redis 10.244.0.13:6379 47s
endpoints/argocd-repo-server 10.244.0.14:8081 47s
endpoints/argocd-server 10.244.0.15:8080,10.244.0.15:8080 47s
NAME TYPE DATA AGE
secret/argocd-initial-admin-secret Opaque 1 45s
secret/argocd-notifications-secret Opaque 0 47s
secret/argocd-redis Opaque 1 50s
secret/argocd-secret Opaque
3 47s
secret/argocd-server-tls kubernetes.io/tls 2 8m39s
secret/sh.helm.release.v1.argocd.v1 helm.sh/release.v1 1 2m3s
NAME DATA AGE
configmap/argocd-cm 18 47s
configmap/argocd-cmd-params-cm 20 47s
configmap/argocd-gpg-keys-cm 0 47s
configmap/argocd-notifications-cm 1 47s
configmap/argocd-rbac-cm 4 47s
configmap/argocd-redis-health-configmap 2 47s
configmap/argocd-ssh-known-hosts-cm 1 47s
configmap/argocd-tls-certs-cm 0 47s
configmap/kube-root-ca.crt 1 8m51s
(⎈|kind-myk8s:N/A) gaji:~$ kubectl describe ingress -n argocd argocd-server
Name: argocd-server
Labels: app.kubernetes.io/component=server
app.kubernetes.io/instance=argocd
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=argocd-server
app.kubernetes.io/part-of=argocd
app.kubernetes.io/version=v3.1.9
helm.sh/chart=argo-cd-9.0.5
Namespace: argocd
Address: localhost
Ingress Class: nginx
Default backend: <default>
TLS:
argocd-server-tls terminates argocd.example.com
Rules:
Host Path Backends
---- ---- --------
argocd.example.com
/ argocd-server:443 (10.244.0.15:8080)
Annotations: meta.helm.sh/release-name: argocd
meta.helm.sh/release-namespace: argocd
nginx.ingress.kubernetes.io/force-ssl-redirect: true
nginx.ingress.kubernetes.io/ssl-passthrough: true
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 3m19s (x2 over 3m30s) nginx-ingress-controller Scheduled for sync
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get ingress -n argocd argocd-server -o yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
meta.helm.sh/release-name: argocd
meta.helm.sh/release-namespace: argocd
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
creationTimestamp: "2025-11-13T13:44:48Z"
generation: 1
labels:
app.kubernetes.io/component: server
app.kubernetes.io/instance: argocd
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: argocd-server
app.kubernetes.io/part-of: argocd
app.kubernetes.io/version: v3.1.9
helm.sh/chart: argo-cd-9.0.5
name: argocd-server
namespace: argocd
resourceVersion: "2990"
uid: 6c5c4356-7d66-48ad-b7e7-3c5acc8c47ed
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- backend:
service:
name: argocd-server
port:
number: 443 ### ArgoCD가 (HTTPS)443으로 받는다는 것.
path: /
pathType: Prefix
tls:
- hosts:
- argocd.example.com
secretName: argocd-server-tls
status:
loadBalancer:
ingress:
- hostname: localhost
- Argo CD 설치 + Ingress by Helm
(⎈|kind-myk8s:N/A) gaji:~$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
yout arg> -keyout argocd.example.com.key \
> -out argocd.example.com.crt \
> -subj "/CN=argocd.example.com/O=argocd"
.+..+.........+.+..............+.+..+.+......+...........+.........+.+.....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*......+......+.+............+..+.........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*....+.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
...+....+...........+....+.........+......+...+..+................+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...+......+..+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...+........+.......+............+........+...+...+.+.....+.+...+.................+....+...+........+...+.+.....+.+...+..+............+..........+......+.....+....+........+...+.+...+...........+.+......+.......................+.+......+...............+..+.+......+...............+...+........+............+.+......+...............+......+.....+...+....+..+.+..................+.........+.....+...+.......+......+......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-----
(⎈|kind-myk8s:N/A) gaji:~$ ls -l argocd.example.com.*
-rw-r--r-- 1 gaji gaji 1184 Nov 13 22:34 argocd.example.com.crt
-rw------- 1 gaji gaji 1704 Nov 13 22:34 argocd.example.com.key
(⎈|kind-myk8s:N/A) gaji:~$ openssl x509 -noout -text -in argocd.example.com.crt
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
12:b8:5a:60:14:68:d6:8f:cc:57:2a:df:52:86:50:bc:d7:98:e7:7c
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = argocd.example.com, O = argocd
Validity
Not Before: Nov 13 13:34:49 2025 GMT
Not After : Nov 13 13:34:49 2026 GMT
Subject: CN = argocd.example.com, O = argocd
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b2:0a:6a:2e:73:a0:97:9a:75:47:0d:51:ce:5b:
47:73:5b:27:2d:10:23:67:3d:4d:6f:39:a2:a8:35:
83:21
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
1C:26:4D:F5:A6:B9:09:52:96:D7:7D:7B:1D:C0:01:E4:19:67:1A:6F
X509v3 Authority Key Identifier:
1C:26:4D:F5:A6:B9:09:52:96:D7:7D:7B:1D:C0:01:E4:19:67:1A:6F
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
7f:5c:f0:76:2e:bc:61:04:8d:34:09:8b:d8:95:d2:c3:b7:c7:
8c:b7:31:2a:0e:d7:d4:b7:4a:17:0b:ed:a0:63:77:4e:48:fc:
- C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가

- 접속 확인
(⎈|kind-myk8s:N/A) gaji:~$ curl -vk https://argocd.example.com/
* Host argocd.example.com:443 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
* Trying 127.0.0.1:443...
* Connected to argocd.example.com (127.0.0.1) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
* subject: CN=argocd.example.com; O=argocd
* start date: Nov 13 13:34:49 2025 GMT
* expire date: Nov 13 13:34:49 2026 GMT
* issuer: CN=argocd.example.com; O=argocd
* SSL certificate verify result: self-signed certificate (18), continuing anyway.
* Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://argocd.example.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: argocd.example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: argocd.example.com
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 307
< date: Thu, 13 Nov 2025 14:05:44 GMT
< content-type: text/html; charset=utf-8
< content-length: 63
< location: https://argocd.example.com/
< strict-transport-security: max-age=31536000; includeSubDomains
<
<a href="https://argocd.example.com/">Temporary Redirect</a>.
* Connection #0 to host argocd.example.com left intact
(⎈|kind-myk8s:N/A) gaji:~$ kubectl -n ingress-nginx logs deploy/ingress-nginx-controller
-------------------------------------------------------------------------------
NGINX Ingress controller
Release: v1.14.0
Build: 52c0a83ac9bc72e9ce1b9fe4f2d6dcc8854516a8
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.27.1
-------------------------------------------------------------------------------
W1113 13:23:58.258599 11 client_config.go:667] Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.
I1113 13:23:58.258723 11 main.go:205] "Creating API client" host="https://10.96.0.1:443"
I1113 13:23:58.263112 11 main.go:248] "Running in Kubernetes cluster" major="1" minor="32" git="v1.32.8" state="clean" commit="2e83bc4bf31e88b7de81d5341939d5ce2460f46f" platform="linux/amd64"
I1113 13:23:58.407716 11 main.go:101] "SSL fake certificate created" file="/etc/ingress-controller/ssl/default-fake-certificate.pem"
I1113 13:23:58.414684 11 ssl.go:535] "loading tls certificate" path="/usr/local/certificates/cert" key="/usr/local/certificates/key"
I1113 13:23:58.421075 11 nginx.go:273] "Starting NGINX Ingress controller"
I1113 13:23:58.424178 11 event.go:377] Event(v1.ObjectReference{Kind:"ConfigMap", Namespace:"ingress-nginx", Name:"ingress-nginx-controller", UID:"d46b067d-ee07-4a52-ac20-7667fb2fe20e", APIVersion:"v1", ResourceVersion:"774", FieldPath:""}): type: 'Normal' reason: 'CREATE' ConfigMap ingress-nginx/ingress-nginx-controller
I1113 13:23:59.622451 11 nginx.go:319] "Starting NGINX process"
I1113 13:23:59.622561 11 leaderelection.go:257] attempting to acquire leader lease ingress-nginx/ingress-nginx-leader...
I1113 13:23:59.623040 11 nginx.go:339] "Starting validation webhook" address=":8443" certPath="/usr/local/certificates/cert" keyPath="/usr/local/certificates/key"
I1113 13:23:59.623355 11 controller.go:214] "Configuration changes detected, backend reload required"
I1113 13:23:59.628163 11 leaderelection.go:271] successfully acquired lease ingress-nginx/ingress-nginx-leader
I1113 13:23:59.628278 11 status.go:85] "New leader elected" identity="ingress-nginx-controller-8676d56f78-tgxfj"
I1113 13:23:59.651571 11 controller.go:228] "Backend successfully reloaded"
I1113 13:23:59.651672 11 controller.go:240] "Initial sync, sleeping for 1 second"
I1113 13:23:59.651729 11 event.go:377] Event(v1.ObjectReference{Kind:"Pod", Namespace:"ingress-nginx", Name:"ingress-nginx-controller-8676d56f78-tgxfj", UID:"0cba77a5-3d6e-4901-827f-329165654c30", APIVersion:"v1", ResourceVersion:"798", FieldPath:""}): type: 'Normal' reason: 'RELOAD' NGINX reload triggered due to a change in configuration
W1113 13:44:48.809813 11 controller.go:1232] Service "argocd/argocd-server" does not have any active Endpoint.
W1113 13:44:48.809970 11 controller.go:1455] Error getting SSL certificate "argocd/argocd-server-tls": local SSL certificate argocd/argocd-server-tls was not found. Using default certificate
I1113 13:44:48.813517 11 main.go:107] "successfully validated configuration, accepting" ingress="argocd/argocd-server"
I1113 13:44:48.826750 11 store.go:443] "Found valid IngressClass" ingress="argocd/argocd-server" ingressclass="nginx"
I1113 13:44:48.829368 11 backend_ssl.go:67] "Adding secret to local store" name="argocd/argocd-server-tls"
I1113 13:44:48.830275 11 event.go:377] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"argocd", Name:"argocd-server", UID:"6c5c4356-7d66-48ad-b7e7-3c5acc8c47ed", APIVersion:"networking.k8s.io/v1", ResourceVersion:"2854", FieldPath:""}): type: 'Normal' reason: 'Sync' Scheduled for sync
W1113 13:44:52.058358 11 controller.go:1232] Service "argocd/argocd-server" does not have any active Endpoint.
W1113 13:44:52.058405 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:44:52.058418 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
I1113 13:44:52.058907 11 controller.go:214] "Configuration changes detected, backend reload required"
I1113 13:44:52.110725 11 controller.go:228] "Backend successfully reloaded"
I1113 13:44:52.111003 11 event.go:377] Event(v1.ObjectReference{Kind:"Pod", Namespace:"ingress-nginx", Name:"ingress-nginx-controller-8676d56f78-tgxfj", UID:"0cba77a5-3d6e-4901-827f-329165654c30", APIVersion:"v1", ResourceVersion:"798", FieldPath:""}): type: 'Normal' reason: 'RELOAD' NGINX reload triggered due to a change in configuration
W1113 13:44:55.390971 11 controller.go:1232] Service "argocd/argocd-server" does not have any active Endpoint.
W1113 13:44:55.391040 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:44:55.391057 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
W1113 13:44:58.724663 11 controller.go:1232] Service "argocd/argocd-server" does not have any active Endpoint.
W1113 13:44:58.724712 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:44:58.724736 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
I1113 13:44:59.630874 11 status.go:311] "updating Ingress status" namespace="argocd" ingress="argocd-server" currentValue=null newValue=[{"hostname":"localhost"}]
I1113 13:44:59.639724 11 event.go:377] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"argocd", Name:"argocd-server", UID:"6c5c4356-7d66-48ad-b7e7-3c5acc8c47ed", APIVersion:"networking.k8s.io/v1", ResourceVersion:"2990", FieldPath:""}): type: 'Normal' reason: 'Sync' Scheduled for sync
W1113 13:45:02.057715 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:45:02.057751 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
W1113 13:45:05.390570 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:45:05.390615 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
W1113 13:45:08.724244 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:45:08.724275 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
172.18.0.1 - - [13/Nov/2025:14:05:44 +0000] "GET / HTTP/2.0" 307 63 "-" "curl/8.5.0" 33 0.006 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.006 307 2a15da9faeafe3ac63e85008b8c68482
(⎈|kind-myk8s:N/A) gaji:~$ kubectl -n argocd logs deploy/argocd-server
time="2025-11-13T13:44:49Z" level=info msg="maxprocs: Leaving GOMAXPROCS=12: CPU quota undefined"
time="2025-11-13T13:44:49Z" level=info msg="ArgoCD API Server is starting" built="2025-10-17T21:35:08Z" commit=8665140f96f6b238a20e578dba7f9aef91ddac51 namespace=argocd port=8080 version=v3.1.9+8665140
time="2025-11-13T13:44:49Z" level=info msg="Starting configmap/secret informers"
time="2025-11-13T13:44:50Z" level=info msg="Configmap/secret informer synced"
time="2025-11-13T13:44:50Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:44:50Z" level=info msg="Initialized server signature"
time="2025-11-13T13:44:50Z" level=info msg="Initialized admin password"
time="2025-11-13T13:44:50Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:44:50Z" level=warning msg="Unable to parse updated settings: server.secretkey is missing"
time="2025-11-13T13:44:50Z" level=info msg="Starting configmap/secret informers"
time="2025-11-13T13:44:50Z" level=info msg="secrets informer cancelled"
time="2025-11-13T13:44:50Z" level=info msg="configmap informer cancelled"
time="2025-11-13T13:44:50Z" level=info msg="Configmap/secret informer synced"
time="2025-11-13T13:44:50Z" level=info msg="Starting configmap/secret informers"
time="2025-11-13T13:44:50Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:44:50Z" level=info msg="configmap informer cancelled"
time="2025-11-13T13:44:50Z" level=info msg="Configmap/secret informer synced"
time="2025-11-13T13:44:50Z" level=info msg="secrets informer cancelled"
time="2025-11-13T13:44:50Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:44:50Z" level=info msg="invalidated cache for resource in namespace: argocd with the name: argocd-notifications-cm"
time="2025-11-13T13:44:50Z" level=info msg="invalidated cache for resource in namespace: argocd with the name: argocd-notifications-secret"
time="2025-11-13T13:44:50Z" level=info msg="argocd v3.1.9+8665140 serving on port 8080 (url: https://argocd.example.com, tls: true, namespace: argocd,
sso: false)"
time="2025-11-13T13:44:50Z" level=info msg="Enabled application namespace patterns: argocd"
time="2025-11-13T13:44:50Z" level=info msg="0xc000372540 subscribed to settings updates"
time="2025-11-13T13:44:50Z" level=info msg="Starting rbac config informer"
time="2025-11-13T13:44:50Z" level=info msg="RBAC ConfigMap 'argocd-rbac-cm' added"
time="2025-11-13T13:44:53Z" level=warning msg="Failed to resync revoked tokens. retrying again in 1 minute: dial tcp 10.96.46.91:6379: connect: connection refused"
time="2025-11-13T13:44:59Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:45:09Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:45:19Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:45:29Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:45:39Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:45:49Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:45:59Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:46:09Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:46:19Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:46:29Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:46:39Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:46:49Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:46:59Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:47:09Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:47:19Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:47:29Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:47:39Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:47:49Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:47:50Z" level=info msg="invalidated cache for resource in namespace: argocd with the name: argocd-notifications-cm"
time="2025-11-13T13:47:50Z" level=info msg="invalidated cache for resource in namespace: argocd with the name: argocd-notifications-secret"
time="2025-11-13T13:47:59Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:48:09Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:48:19Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:48:29Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:48:39Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-13T13:48:49Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
# 최초 접속 암호 확인
(⎈|kind-myk8s:N/A) gaji:~$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
r87################
- ingress-nginx-controller의 SSL Passthrough 활성화 설정 없이 HTTPS 접근 시 오류 발생
$ kubectl -n ingress-nginx logs deploy/ingress-nginx-controller
W1113 13:45:05.390615 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
W1113 13:45:08.724244 11 controller.go:1469] Unexpected error validating SSL certificate "argocd/argocd-server-tls" for server "argocd.example.com": x509: certificate relies on legacy Common Name field, use SANs instead
W1113 13:45:08.724275 11 controller.go:1470] Validating certificate against DNS names. This will be deprecated in a future version
172.18.0.1 - - [13/Nov/2025:14:05:44 +0000] "GET / HTTP/2.0" 307 63 "-" "curl/8.5.0" 33 0.006 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.006 307 2a15da9faeafe3ac63e85008b8c68482
172.18.0.1 - - [13/Nov/2025:14:08:25 +0000] "GET / HTTP/1.1" 308 164 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 459 0.000 [argocd-argocd-server-443] [] - - - - 386f096c56d04d1e26725bd7cd0a084f
172.18.0.1 - - [13/Nov/2025:14:08:57 +0000] "GET / HTTP/1.1" 308 164 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 454 0.000 [argocd-argocd-server-443] [] - - - - 14a8279469bd8d9e3d45e9e412e5b524
172.18.0.1 - - [13/Nov/2025:14:09:28 +0000] "GET / HTTP/1.1" 308 164 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0" 369 0.000 [argocd-argocd-server-443] [] - - - - 95123c816b1a7d5a021754a8a3bc022c
- SSL Passthrough flag 활성화 설정
KUBE_EDITOR="nano" kubectl edit -n ingress-nginx deployments/ingress-nginx-controller

- 파일 수정 후 자동으로 재기동됨

- SSL Passthrough 활성화 후 pod가 새롭게 떴는데도 브라우저로 접근 시 오류

- 403 에러가 발생하는 이유와 해결 방법
- 요청 주체 127.0.0.1이 가리키는 대상 결과
| WSL2 curl | WSL2 내부 ingress-nginx | ✅ 정상 (200 OK) |
| Windows 브라우저 | Windows 자체 (빈 포트 or 다른 서비스) | ❌ 403 Access Denied |
- 즉, Windows의 127.0.0.1에는 ingress가 없는데, 브라우저는 Windows의 localhost로 요청을 보내니까 403 에러 발생
- WSL2는 “가상의 Linux PC”다.
- Windows와 WSL2는 127.0.0.1 이 다르다.
- kind ingress는 WSL2 안에서만 열려 있다.
- 브라우저는 Windows의 127.0.0.1 로 가니까 연결이 안 된다.
- 해결 방법 2가지
| 🔹 포트포워드 | WSL2 → Windows로 직접 포트 내보내기 → https://localhost:8080 |
| 🔹 WSL2 localhost 공유 켜기 | Windows와 WSL2가 같은 127.0.0.1 을 쓰도록 설정 (localhostForwarding=true) |
- 1번 포트포워드 방식으로 적용하여 정상 접속 확인됨
- 현재 8080으로 포트포워딩을 설정했는데 이후 keycloak 실습에서 8080 포트를 사용하므로 8000 포트 등 다른 포트로 맵핑하자!
(⎈|kind-myk8s:N/A) gaji:~$ kubectl port-forward svc/argocd-server -n argocd 8080:443
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
** 이후 실습에서 포트포워딩 포트를 8000으로 변경해서 사용 **

- 인증서 확인

- ArgoCD CLI 사용
(⎈|kind-myk8s:N/A) gaji:~$ argocd login argocd.example.com --insecure --grpc-web
Username: admin
Password:
'admin:login' logged in successfully
Context 'argocd.example.com' updated
(⎈|kind-myk8s:N/A) gaji:~$ argocd account list
NAME ENABLED CAPABILITIES
admin true login
(⎈|kind-myk8s:N/A) gaji:~$ argocd proj list
NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES DESTINATION-SERVICE-ACCOUNTS
default *,* * */* <none> <none> disabled <none>
(⎈|kind-myk8s:N/A) gaji:~$ argocd repo list
TYPE NAME REPO INSECURE OCI LFS CREDS STATUS MESSAGE PROJECT
(⎈|kind-myk8s:N/A) gaji:~$ argocd cluster list
SERVER NAME VERSION STATUS MESSAGE PROJECT
https://kubernetes.default.svc in-cluster Unknown Cluster has no applications and is not being monitored.
(⎈|kind-myk8s:N/A) gaji:~$ argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET
2. 접근 제어
2.1. 선언적 사용자
- 관리자용 admin 계정은 초기 구성에서만 사용하고 이후에는 로컬 사용자 계정으로 전환하거나 SSO 통합을 구성하는 것을 권장 → 즉, 관리자 비활성화
- 일상적인 작업을 수행할 최소 권한의 로컬 사용자 생성 (alice 사용자를 생성하고 CLI, web UI 허용)
$ KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm

- 생성 확인


- 신규 사용자(alice)의 초기 비밀번호는 관리자 계정(admin)과 동일
- alice 사용자의 비밀번호를 변경하자.
argocd account update-password \
--account alice \
--current-password qwe12345 \
--new-password alice12345
Password updated
- 변경된 내용이 secret에도 반영됨
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq

- alice의 토큰 확인

- admin과 alice의 권한 확인을 위해 애플리케이션 배포 및 각 계정에서 권한 확인
# guestbook helm 차트 애플리케이션 생성
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
helm:
valueFiles:
- values.yaml
path: helm-guestbook
repoURL: https://github.com/argoproj/argocd-example-apps
targetRevision: HEAD
syncPolicy:
automated:
enabled: true
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
destination:
namespace: guestbook
server: https://kubernetes.default.svc
EOF
- admin 계정에서 애플리케이션과 클러스터 정보 확인 가능


- alice 계정에서 애플리케이션과 클러스터 정보 확인 가능


→ 계정이 생성되면 기본적으로 권한이 없기 때문에 애플리케이션과 클러스터 정보 조회가 안 됨.
- RBAC
- RBAC 기능은 Argo CD 리소스에 대한 액세스를 제한할 수 있도록 한다.
- Argo CD에는 자체 사용자 관리 시스템이 없으며 기본 사용자(admin) 하나만 존재한다.
- admin사용자는 슈퍼유저이며 시스템에 대한 무제한 액세스 권한을 갖는다.
- RBAC 구성을 정의할 수 있는 두 가지 주요 구성 요소
- 글로벌 RBAC 구성 맵( argo-rbac-cm.yaml 참조 )
- AppProject 의 역할
- 기본 내장 역할 : Argo CD에는 두 가지 사전 정의된 역할이 있지만 RBAC 구성을 사용하면 역할과 그룹을 정의할 수 있다.
- role:readonly : 모든 리소스에 대한 읽기 전용 액세스
- role:admin : 모든 리소스에 대한 무제한 액세스
- 사용자가 Argo CD에서 인증되면 policy.default 에 지정된 역할이 부여된다.
- rbac 설정
$ kubectl get cm -n argocd argocd-rbac-cm -o jsonpath='{.data}' | jq
{
"policy.csv": "",
"policy.default": "",
"policy.matchMode": "glob",
"scopes": "[groups]"
}

- alice 사용자로 애플리케이션, 클러스터 재확인


RBAC을 통해 사용자 인증 후 readonly 역할을 부여 받아서 위의 정보를 확인할 수 있다.
- 현재 readonly 권한만 있으므로 delete 수행 불가

- alice 사용자의 CLI 접속 시 권한 확인
(⎈|kind-myk8s:N/A) gaji:~$ argocd login argocd.example.com --insecure --grpc-web
Username: alice
Password:
'alice:login' logged in successfully
Context 'argocd.example.com' updated
(⎈|kind-myk8s:N/A) gaji:~$ argocd cluster list
SERVER NAME VERSION STATUS MESSAGE PROJECT
https://kubernetes.default.svc in-cluster 1.32 Successful
(⎈|kind-myk8s:N/A) gaji:~$ argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET
argocd/guestbook https://kubernetes.default.svc guestbook default Synced Healthy Auto-Prune <none> https://github.com/argoproj/argocd-example-apps helm-guestbook HEAD

- admin 계정 비활성화
KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm



→ 더 이상 admin으로는 접근 불가
2.2. 서비스 어카운트
- 서비스 어카운트 소개
- 서비스 어카운트는 CI/CD 파이프라인과 같은 자동화를 시스템에 인증하는 데 사용하는 계정이다.
- 사용자를 비활성화하거나 권한을 제어하면 파이프라인이 실패할 수 있기 때문에 사용자와 연결돼서는 안 된다.
- 서비스 어카운트는 엄격하게 권한을 제한해야 하고 파이프라인에서 수행하는 것 이상의 권한이 있으면 안 된다.
- 반면 실제 사용자는 더 다양한 리소스에 접근할 수 있어야 한다.
- Argo CD에서 서비스 어카운트를 생성하는 방법은 두 가지가 있다.
- 첫 번째는 login 기능은 제거하고 api 키만을 사용하는 로컬 사용자이고, 둘째는 프로젝트 역할을 사용하고 그 역할에 토큰을 할당하는 것이다.
- 로컬 서비스 어카운트 RBAC로 구성
- 사용자를 하나 생성한다. (해당 사용자로는 UI나 CLI 접근은 X / API키로만 접근 → 즉, 시스템에서 접근한다는 것)
- 이 경우 사용자는 ui나 cli 에 대한 암호가 없고 api 키를 생성한 후에만 접근이 가능하다.
- 특정 애플리케이션에 대한 동기화를 시작할 수 있는 권한과 같은 특정 권한을 부여할 것이다.

→ gitops-ci 사용자는 api key만 사용 가능하게 설정
- gitops-ci 사용자에게는 apiKey만 허용된 상태 확인

- 계정 토큰 생성 시도 - 실패
# 계정 토큰 생성 시도
(⎈|kind-myk8s:N/A) gaji:~$ argocd account generate-token -a gitops-ci
{"level":"fatal","msg":"rpc error: code = PermissionDenied desc = permission denied: accounts, update, gitops-ci, sub: alice, iat: 2025-11-15T02:42:17Z","time":"2025-11-15T11:44:45+09:00"}
→ 현재 alice 사용자로 cli 명령어를 수행 중인데 alice는 토큰 생성 권한이 없어서 실패한다.
- alice 사용자에게 계정 업데이트 권한을 할당 : user-update 새 역할을 생성하고 사용자에게 이 역할을 할당
$ KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-rbac-cm

- 토큰 동작 확인
# 계정 토큰 생성 시도
~$ argocd account generate-token -a gitops-ci
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJnaXRvcHMtY2k6YXBpS2V5IiwibmJmIjoxNzYzMTc1MzI4LCJpYXQiOjE3NjMxNzUzMjgsImp0aSI6IjFmYjQ0NWU0LTNmNDYtNGI1Yi04MTJlLWVkNTcyZjY0YjE3MSJ9.9lYlKKMVS1NwzvDHHm_W7RlGOfGuo9_kFc7s472GlI8
# 토큰 작동 확인
~$ argocd account get-user-info --auth-token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJnaXRvcHMtY2k6YXBpS2V5IiwibmJmIjoxNzYzMTc1MzI4LCJpYXQiOjE3NjMxNzUzMjgsImp0aSI6IjFmYjQ0NWU0LTNmNDYtNGI1Yi04MTJlLWVkNTcyZjY0YjE3MSJ9.9lYlKKMVS1NwzvDHHm_W7RlGOfGuo9_kFc7s472GlI8
Logged In: true
Username: gitops-ci
Issuer: argocd
Groups:
→ alice 사용자에게 RBAC으로 토큰 생성 권한을 부여한 후 시도해 봤을 때 성공적으로 토큰 값을 받을 수 있다.
2. 프로젝트 역할과 토큰 TOKEN
- 프로젝트 역할은 서비스 어카운트에서 사용할 수 있는 두 번째 옵션이다.
- 애플리케이션 프로젝트는 역할을 통해 애플리케이션 정의에 일부 제약 조건을 적용하는 방식이다.
- 상태를 가져오는 리포지터리, 대상 클러스터나 배포할 수 있는 네임스페이스를 지정할 수 있고, 설치할 수 있는 리소스 유형을 필터링할 수도 있다.
- 예를 들어 이 프로젝트를 사용하는 애플리케이션은 시크릿을 배포할 수 없도록 할 수 있다.
- 이 외에도 프로젝트 역할을 생성하고, 할 수 있는 작업의 종류를 제한하고 역할에 토큰을 할당할 수도 있다.
- Argo CD가 설치되면 default 라는 기본 프로젝트가 제공되는데, 기본 프로젝트는 애플리케이션에 대한 제한 사항이 설정돼 있지 않다.
$ kubectl get appprojects.argoproj.io -n argocd default -o yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
creationTimestamp: "2025-11-13T13:44:50Z"
generation: 1
name: default
namespace: argocd
resourceVersion: "2958"
uid: bd39a8da-56e4-43dd-99b6-e972e17eef14
spec:
clusterResourceWhitelist:
- group: '*'
kind: '*'
destinations:
- namespace: '*'
server: '*'
sourceRepos:
- '*'
status: {}
- 프로젝트를 토큰과 함께 사용하는 방법을 확인하기 위해 새 프로젝트를 만들고 기존 argocd 애플리케이션에 사용해 볼 것이다.
- 일단 새 프로젝트를 만들면 프로젝트에 사용할 역할을 생성하고 역할에 권한을 할당하고 토큰을 생성해야 한다.
- AppProject 신규 생성
- 'roles'에 세부 설정을 통해 sample-apps 라는 프로젝트 내에 애플리케이션에 대한 get, sync 권한만 부여
- 'namespace'에 Argo CD가 배포하는 destination의 namespace가 'test'가 아닐 경우 오류 발생
- 'sourceRepos'도 명시된 레포만 사용
#
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: sample-apps
namespace: argocd
spec:
roles:
- name: read-sync
description: read and sync privileges
policies:
- p, proj:sample-apps:read-sync, applications, get, sample-apps/*, allow
- p, proj:sample-apps:read-sync, applications, sync, sample-apps/*, allow
clusterResourceWhitelist:
- group: '*'
kind: '*'
description: Project to configure argocd self-manage application
destinations:
- namespace: test
server: https://kubernetes.default.svc
sourceRepos:
- https://github.com/argoproj/argocd-example-apps.git
EOF
#
kubectl get appproject -n argocd
NAME AGE
default 92m
sample-apps 4m6s



- 해당 프로젝트에 app 배포
# 애플리케이션 생성
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: pre-post-sync
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: sample-apps
source:
path: pre-post-sync
repoURL: https://github.com/argoproj/argocd-example-apps
targetRevision: master
destination:
namespace: test
server: https://kubernetes.default.svc
syncPolicy:
automated:
enabled: false
syncOptions:
- CreateNamespace=true
EOF
#
argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET
argocd/pre-post-sync https://kubernetes.default.svc sync-test sample-apps OutOfSync Missing Manual <none> https://github.com/argoproj/argocd-example-apps pre-post-sync master
# 동기화 실행 시 실패!
argocd app sync argocd/pre-post-sync
{"level":"fatal","msg":"rpc error: code = PermissionDenied desc = permission denied: applications, sync, sample-apps/pre-post-sync, sub: alice, iat: 2025-11-09T05:41:55Z","time":"2025-11-09T16:12:09+09:00"}



→ 현재 alice 사용자는 sync 권한이 없으므로 수행이 안 된다.
** alice 사용자의 권한 확인 **

- 읽기 및 동기화 역할에 대한 토큰을 통해 수행해보자
KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-rbac-cm

# 역할에 대한 토큰 생성
(⎈|kind-myk8s:N/A) gaji:~$ argocd proj role create-token sample-apps read-sync
Create token succeeded for proj:sample-apps:read-sync.
ID: 655c187f-37dc-488b-a998-c912c4501213
Issued At: 2025-11-15T12:20:42+09:00
Expires At: Never
Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJwcm9qOnNhbXBsZS1hcHBzOnJlYWQtc3luYyIsIm5iZiI6MTc2MzE3Njg5OC1jOTEyYzQ1MDEyMTMifQ.U7EOvdzF8u2pSJSPOh0YCbqMPqkD5uAeAh_IM3iIGcU
# 해당 토큰으로 애플리케이션을 수동으로 동기화할 수 있다.
(⎈|kind-myk8s:N/A) gaji:~$ TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJwcm9qOnNhbXBsZS1hcHBzOnJlYWQtc3ljMTg3Zi0zN2RjLTQ4OGItYTk5OC1jOTEyYzQ1MDEyMTMifQ.U7EOvdzF8u2pSJSPOh0YCbqMPqkD5uAeAh_IM3iIGcU
(⎈|kind-myk8s:N/A) gaji:~$ argocd app sync argocd/pre-post-sync --auth-token $TOKEN
TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSA
2025-11-15T12:22:34+09:00 Service test pre-post-sync-kustomize-guestbook-ui OutOfSync Missing
2025-11-15T12:22:34+09:00 apps Deployment test pre-post-sync-kustomize-guestbook-ui OutOfSync Missing
2025-11-15T12:22:34+09:00 Namespace test Running Synced namespace/test created
2025-11-15T12:22:34+09:00 batch Job test pre-post-sync-before Progressing
2025-11-15T12:22:36+09:00 batch Job test pre-post-sync-before Running Synced PreSync job.batch/pre-post-syn
2025-11-15T12:22:52+09:00 Service test pre-post-sync-kustomize-guestbook-ui Synced Healthy
re-post-sync-after created
2025-11-15T12:23:09+09:00 batch Job test pre-post-sync-after Succeeded Synced PostSync Reached expected number of succeeded pods
URL: https://argocd.example.com/applications/argocd/pre-post-sync
Source:
- Repo: https://github.com/argoproj/argocd-example-apps
Target: master
Path: pre-post-sync
SyncWindow: Sync Allowed
Sync Policy: Manual
Sync Status: Synced to master (0d521c6)
Health Status: Healthy
Operation: Sync
Sync Revision: 0d521c6e049889134f3122eb32d7ed342f43ca0d
Phase: Succeeded
Start: 2025-11-15 12:22:34 +0900 KST
Finished: 2025-11-15 12:23:09 +0900 KST
Duration: 35s
Message: successfully synced (no more tasks)
GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
Namespace test Running Synced namespace/test created
batch Job test pre-post-sync-before Succeeded PreSync Reached expected number of succeeded pod pods
Service test pre-post-sync-kustomize-guestbook-ui Synced Healthy service/pre-post-sync-kustomize-guestbostbook-ui created
apps Deployment test pre-post-sync-kustomize-guestbook-ui Synced Healthy deployment.apps/pre-post-sync-kustomizedsmize-guestbook-ui created ok-ui created
batch Job test pre-post-sync-after Succeeded PostSync Reached expected number of succeeded po-guestbook-ui cd pods



2.3. SSO
- 소개
- SSO를 사용하면 마스터 로그인을 할 수 있으며, 이를 기반으로 다른 독립적인 애플리케이션에 대한 권한을 부여받을 수 있다.
- 예를 들어 argocd.mycompany.com 에 접근하려는 경우, argocd.mycompany.com은 외부 공급자를 신뢰해 서용자의 신원을 확인한다.
- 또한 argocd.mycompany.com에 대한 접근 유형은 외부 마스터 시스템에서 사용자의 소속 그룹 또는 계정 설정에 따라 제어할 수 있다.
keyClak
- 소개
- 애플리케이션에 초점을 맞춘 오픈 소스 ID 및 접근(권한) 관리 도구
- 애플리케이션에 초점을 맞춘 오픈 소스 ID 및 접근(권한) 관리 도구
- keyCloak Clients
- Keycloak을 IDP(Identity Provider)로 사용하여 로그인/토큰 발급을 받는 서비스 또는 애플리케이션
- 즉, Keycloak에게 "로그인/토큰 발급"을 요청하는 애플리케이션
- Keycloak Clients 종류
- 웹 서비스
- 백엔드 API 서버
- SPA 프론트엔드 (React, Vue 등)
- CLI
- ArgoCD 같은 DevOps 툴
- Grafana, Kubernetes Dashboard
- Argo CD에서 Keycloak을 사용할 때 동작 흐름
- ArgoCD가 Keycloak에게 로그인 요청
- Keycloak이 사용자 인증 후 토큰을 ArgoCD에게 전달
- ArgoCD는 토큰을 검증하고 UI 접근 허용
- Keycloak Realm
- 인증/인가를 완전히 분리해서 관리하는 하나의 독립된 보안 공간(Security domain)
- realm은 다른 realm과 서로 완전히 독립적이며, 각 realm은 자체 설정과 애플리케이션 및 사용자를 갖습니다.
- 하나의 realm에 포함된 내용
Users 사용자 계정 목록 Groups 사용자 그룹 Roles 역할(Role) Clients OAuth/OIDC 로그인을 사용하는 애플리케이션들 Identity Providers 외부 로그인(Google, GitHub 등) Authentication Flow 로그인 방식 설정(MFA 등) Themes 로그인 페이지 테마 - 서비스 realm 예시)
- realm = argo
- realm = company
- realm = development
-> 이런 식으로 만들어서 ArgoCD / Grafana / Jenkins / 쿠버 인증 등 앱마다 별도 realm을 사용하는 것이 일반적입니다.
- keycloak 배포 및 기본 설정
# 도커에서 컨테이너로 Keycloak 실행
# 기본 관리자 계정이 제공되지 않아, 환경 변수로 초기 관리자 계정 생성
docker run -d -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin --net host --name dev-keycloak quay.io/keycloak/keycloak:22.0.0 start-dev
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
de9f00b3188e quay.io/keycloak/keycloak:22.0.0 "/opt/keycloak/bin/k…" 51 seconds ago Up 51 seconds
dev-keycloak
9b3ad60e87e8 kindest/node:v1.32.8 "/usr/local/bin/entr…" 39 hours ago Up 39 hours 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0..0.0.0:30000-30003->30000-30003/tcp, 127.0.0.1:38175->6443/tcp myk8s-control-plane
- 웹 페이지 접속 (localhost:8080/admin)
- 관리자 웹 로그인 : admin / admin

- bob 유저 생성 : 암호 bob123

- keycloak 에 argo cd 를 위한 client 생성



(이후 진행에서 오류로 인해 수정하였다. 수정한 내용은 아래에서 참고하자!)
- Clients 확인


→ ArgoCD 클라이언트와 keycloak 간에 동일한 시크릿 값을 알고 있어야 flow가 진행됨 (보안을 강화한 것!)
- Configuring ArgoCD OIDC
- 클라이언트 시크릿 설정

→ArgoCD가 인가를 요청할 때 이 시크릿 값을 알고 있어야하기 때문에 아르고 시크릿에 추가
- Now we can configure the config map and add the oidc configuration to enable our keycloak authentication.
- 사용자가 처음에 ArgoCD에 로그인할 때 keycloak 에 인증을 해달라고 보내야하는데 그 내용에 대한 설정
# Windows WSL2 Ubuntu의 경우 ifconfig에서 eth0의 ip를 사용하면 된다.
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.21.96.204 netmask 255.255.240.0 broadcast 172.21.111.255
inet6 fe80::215:5dff:feae:f2bc prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:ae:f2:bc txqueuelen 1000 (Ethernet)
RX packets 774544 bytes 1126977274 (1.1 GB)
RX errors 0 dropped 11 overruns 0 frame 0
TX packets 135526 bytes 10839830 (10.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

→ localhost를 확인한 ip로 변경해서 접속해도 정상 접근됨!
KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm

→ Argo CD의 클라이언트를 하는 사용자도 시크릿 값을 알고 있고, 중간에서 인가를 처리해주는 keycloak도 알고 있으니까 문제 없이 통신 가능
- argocd 서버 재시작
kubectl rollout restart deploy argocd-server -n argocd

- keycloak 인증을 통한 로그인 (오류)
- 웹 브라우저에서 ArgoCD를 접속할 때, https://localhost:8000 포트포워딩 주소로 접속하는 상태에서 Keycloak으로 인증 시도 시 아래와 같은 오류 발생


- 해결 방법 (2가지)
- ① Keycloak에 Client 정보 수정(argocd)
- (기존) https://argocd.example.com -----> (변경) https://localhost:8000으로 모두 변경

- ② argocd configmap 내용 수정
- 1) redirectURL: https://localhost:8000/auth/callback 내용 새로 추가
- 2) url: https://localhost:8000 로 수정
# argocd configmap에 아래 내용 추가 및 수정 필요
# KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm
# 1) redirectURL: https://localhost:8000/auth/callback 추가
# 2) url: https://localhost:8000 로 수정
apiVersion: v1
data:
accounts.alice: apiKey,login
accounts.gitops-ci: apiKey
admin.enabled: "true"
application.instanceLabelKey: argocd.argoproj.io/instance
application.sync.impersonation.enabled: "false"
exec.enabled: "false"
oidc.config: |
name: Keycloak
issuer: http://172.21.96.204:8080/realms/master
clientID: argocd
clientSecret: EbCai8nQweIm9RdRGqv49lHPE7Sa54IP
requestedScopes: ["openid", "profile", "email"]
redirectURL: https://localhost:8000/auth/callback # Argo CD로 포트포워딩해서 들어갈 때 주소 추가
resource.customizations.ignoreResourceUpdates.ConfigMap: |
jqPathExpressions:
# Ignore the cluster-autoscaler status
- '.metadata.annotations."cluster-autoscaler.kubernetes.io/last-updated"'
# Ignore the annotation of the legacy Leases election
(중간 생략)
server.rbac.log.enforce.enable: "false"
statusbadge.enabled: "false"
timeout.hard.reconciliation: 0s
timeout.reconciliation: 180s
url: https://localhost:8000 # 기존에 https://argocd.example.com -> https://localhost:8000 로 수정
# 파일 내용 수정 후 kubectl get pods -n argocd 명령어로 확인 했을 때
# argocd-server-########### 라는 pod가 새롭게 뜨지 않는 경우 아래 명령어 수행
kubectl rollout restart deployment argocd-server -n argocd
# argocd-server-########### pod 상태 재확인
kubectl get pods -n argocd
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 89m
argocd-applicationset-controller-bbff79c6f-txcfm 1/1 Running 0 90m
argocd-dex-server-6877ddf4f8-wvk8x 1/1 Running 0 90m
argocd-notifications-controller-7b5658fc47-7kxjw 1/1 Running 0 90m
argocd-redis-7d948674-nsb7m 1/1 Running 0 90m
argocd-repo-server-7679dc55f5-4qqv9 1/1 Running 0 90m
argocd-server-7f59f9cd9b-2wvd8 1/1 Running 0 26s # 새로 떴음
- 웹 브라우저로 접속 확인 - 정상 접속됨!


- bob 사용자로 로그인


- 신규 사용자 생성 (ID : tom / PW : tom123)


→ tom 사용자로 접속 가능
→ 이제 Keycloak에서 계정 관리를 할 수 있다.
3. Argo Rollouts
- Argo Rollouts 소개
- Argo Rollouts는 Kubernetes 컨트롤러이자 CRD 세트로, Kubernetes에 블루-그린, 카나리아, 카나리아 분석, 실험, 점진적 전달 기능 등의 고급 배포 기능을 제공한다.
- Argo Rollouts는 (선택 사항) 인그레스 컨트롤러 및 서비스 메시와 통합되어 트래픽 셰이핑 기능을 활용하여 업데이트 중에 트래픽을 점진적으로 새 버전으로 전환
- 또한, Rollouts는 다양한 제공업체의 지표를 쿼리하고 해석하여 주요 KPI를 확인하고 업데이트 중에 자동 프로모션 또는 롤백을 실행할 수 있다.
- 네이티브 쿠버네티스 deployment 객체는 RollingUpdate 업데이트 제약 사항
- 롤아웃 속도에 대한 제어가 거의 없음.
- 새 버전으로의 트래픽 흐름을 제어할 수 없음.
- 준비 프로브는 심층 검사, 스트레스 검사 또는 일회성 검사에는 적합하지 않음.
- 업데이트를 확인하기 위해 외부 메트릭을 쿼리할 수 있는 기능 없음.
- 진행을 중지할 수 있지만 업데이트를 자동으로 중단하고 롤백할 수 없음.
- Argo Rollouts 동작
- 배포 객체 와 마찬가지로 Argo Rollouts 컨트롤러는 ReplicaSets 의 생성, 확장 및 삭제를 관리한다.
- 이러한 ReplicaSets는 spec.template배포 객체와 동일한 Pod 템플릿을 사용하는 Rollout 리소스 내부의 필드에 의해 정의된다.
- 이 변경되면 spec.templateArgo Rollouts 컨트롤러에 새 ReplicaSet이 도입됨을 알리는 신호가 전달된다. 컨트롤러는 필드 내에 설정된 전략을 사용하여 기존 ReplicaSet에서 새 ReplicaSet으로의 롤아웃 진행 방식을 결정한다. 새 ReplicaSet이 확장되고 (선택적으로 Analysis를 spec.strategy 통과하면 ) 컨트롤러는 해당 ReplicaSet을 "안정적"으로 표시한다.
- 안정적인 ReplicaSet에서 새로운 ReplicaSet으로 전환하는 동안 다른 변경 사항이 발생하면 spec.template(즉, 롤아웃 도중 애플리케이션 버전을 변경하는 경우), 이전에 새로 생성된 ReplicaSet의 크기가 축소되고 컨트롤러는 업데이트된 필드를 반영하는 ReplicasSet을 진행하려고 시도한다. spec.template. 각 전략의 동작에 대한 자세한 내용은 사양 섹션 참조
- 아키텍처

- Argo Rollouts 설치 및 Sample 테스트
$ helm install argo-rollouts argo/argo-rollouts --version 2.40.5 -f argorollouts-values.yaml --namespace argo-rollouts
Error: INSTALLATION FAILED: Get "https://github.com/argoproj/argo-helm/releases/download/argo-rollouts-2.40.5/argo-rollouts-2.40.5.tgz": dial tcp: lookup github.com on 172.21.96.1:53: read udp 172.21.96.204:59178->172.21.96.1:53: i/o timeout
- Argo Rollouts 설치 시 발생하는 DNS 오류 해결
- WSL2의 DNS(172.21.96.1)가 github.com을 못 찾고 있어 helm이 chart .tgz 파일을 다운로드 불가
- WSL2는 재부팅할 때마다 DNS를 덮어쓰는데 이게 주 원인
# /etc/resolv.conf 자동 생성 기능 끄기
sudo bash -c 'echo "[network]" >> /etc/wsl.conf'
sudo bash -c 'echo "generateResolvConf = false" >> /etc/wsl.conf'
# 기존 resolv.conf 삭제 후 새로 생성
sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
sudo bash -c 'echo "nameserver 1.1.1.1" >> /etc/resolv.conf'
# WSL 재시작 (PowerShell에서 수행)
wsl --shutdown
# 다시 ubuntu 실행 후 설치
(⎈|kind-myk8s:N/A) gaji:~$ helm install argo-rollouts argo/argo-rollouts --version 2.40.5 -f argorollouts-values.yaml --namespace argo-rollouts
NAME: argo-rollouts
LAST DEPLOYED: Sat Nov 15 20:01:47 2025
NAMESPACE: argo-rollouts
STATUS: deployed
REVISION: 1
TEST SUITE: None
# 확인
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get all -n argo-rollouts
NAME READY STATUS RESTARTS AGE
pod/argo-rollouts-658dd58fc8-6q8dx 1/1 Running 0 3m16s
pod/argo-rollouts-658dd58fc8-l8gw4 1/1 Running 0 3m16s
pod/argo-rollouts-dashboard-6bc9fff6fc-4vnm4 1/1 Running 0 3m16s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/argo-rollouts-dashboard NodePort 10.96.216.219 <none> 3100:30003/TCP 3m21s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/argo-rollouts 2/2 2 2 3m21s
deployment.apps/argo-rollouts-dashboard 1/1 1 1 3m21s
NAME DESIRED CURRENT READY AGE
replicaset.apps/argo-rollouts-658dd58fc8 2 2 2 3m17s
replicaset.apps/argo-rollouts-dashboard-6bc9fff6fc 1 1 1 3m17s
(⎈|kind-myk8s:N/A) gaji:~$ kubectl get crds
NAME CREATED AT
analysisruns.argoproj.io 2025-11-15T11:01:48Z
analysistemplates.argoproj.io 2025-11-15T11:01:48Z
applications.argoproj.io 2025-11-13T13:44:48Z
applicationsets.argoproj.io 2025-11-13T13:44:48Z
appprojects.argoproj.io 2025-11-13T13:44:48Z
clusteranalysistemplates.argoproj.io 2025-11-15T11:01:48Z
experiments.argoproj.io 2025-11-15T11:01:48Z
rollouts.argoproj.io 2025-11-15T11:01:48Z
# Argo rollouts 대시보드 접속 주소 확인
echo "http://127.0.0.1:30003"
open "http://127.0.0.1:30003"

- ArgoCD extension은 ArgoCD 기본 기능을 확장
#
cat <<EOF > argocd-values.yaml
global:
domain: argocd.example.com
certificate:
enabled: true
server:
ingress:
enabled: true
ingressClassName: nginx
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
tls: true
extensions:
enabled: true
extensionList:
- name: rollout-extension
env:
- name: EXTENSION_URL
value: https://github.com/argoproj-labs/rollout-extension/releases/download/v0.3.7/extension.tar
EOF
helm upgrade -i argocd argo/argo-cd --version 9.0.5 -f argocd-values.yaml --namespace argocd
# initContainer 환경변수 EXTENSION_URL에 설치할 extension을 명시하면 됩니다.
# mainContainer는 initContainer가 설치한 extension을 가져오기 위해 volumeMount로 가져옵니다.
k describe deploy -n argocd argocd-server
...
Init Containers:
rollout-extension:
Image: quay.io/argoprojlabs/argocd-extension-installer:v0.0.8
Port: <none>
Host Port: <none>
SeccompProfile: RuntimeDefault
Environment:
EXTENSION_URL: https://github.com/argoproj-labs/rollout-extension/releases/download/v0.3.7/extension.tar
Mounts:
/tmp from tmp (rw)
/tmp/extensions/ from extensions (rw)
...
Volumes:
extensions:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>

- Argo CD에 깃허브 레포지토리 추가


- GitHub에 yaml 파일 업로드
# Run the following command to deploy the initial Rollout and Service:
mkdir argo-rollouts && cd argo-rollouts
wget https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
wget https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/service.yaml
git add . && git commit -m "Add sample yaml" && git push -u origin main

- Deploying a Rollout (애플리케이션 생성)


- 위에서 생성한 애플리케이션을 sync 하고 확인해보자.



(⎈|kind-myk8s:N/A) gaji:~/my-sample-app/my-sample-app/argo-rollouts$ kubectl get rollout -n test
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
rollouts-demo 5 5 5 5 3m55s
(⎈|kind-myk8s:N/A) gaji:~/my-sample-app/my-sample-app/argo-rollouts$ kubectl describe rollout -n test
Name: rollouts-demo
Namespace: test
Labels: <none>
Annotations: argocd.argoproj.io/tracking-id: rollout-test:argoproj.io/Rollout:test/rollouts-demo
rollout.argoproj.io/revision: 1
API Version: argoproj.io/v1alpha1
Kind: Rollout
Metadata:
Creation Timestamp: 2025-11-15T11:50:34Z
Generation: 1
Resource Version: 55505
UID: 4ce406c9-2ed7-49fb-8527-66bcfc138ca4
Spec:
Replicas: 5
Revision History Limit: 2
Selector:
Match Labels:
App: rollouts-demo
Strategy:
Canary:
Steps:
Set Weight: 20
Pause:
Set Weight: 40
Pause:
Duration: 10
Set Weight: 60
Pause:
Duration: 10
Set Weight: 80
Pause:
Duration: 10
Template:
Metadata:
Labels:
App: rollouts-demo
Spec:
Containers:
Image: argoproj/rollouts-demo:blue
Name: rollouts-demo
Ports:
Container Port: 8080
Name: http
Protocol: TCP
Resources:
Requests:
Cpu: 5m
Memory: 32Mi
Status:
HPA Replicas: 5
Available Replicas: 5
Blue Green:
Canary:
Conditions:
Last Transition Time: 2025-11-15T11:50:35Z
Last Update Time: 2025-11-15T11:50:35Z
Message: RolloutCompleted
Reason: RolloutCompleted
Status: True
Type: Completed
Last Transition Time: 2025-11-15T11:50:49Z
Last Update Time: 2025-11-15T11:50:49Z
Message: Rollout is healthy
Reason: RolloutHealthy
Status: True
Type: Healthy
Last Transition Time: 2025-11-15T11:50:35Z
Last Update Time: 2025-11-15T11:50:49Z
Message: ReplicaSet "rollouts-demo-687d76d795" has successfully progressed.
Reason: NewReplicaSetAvailable
Status: True
Type: Progressing
Last Transition Time: 2025-11-15T11:50:49Z
Last Update Time: 2025-11-15T11:50:49Z
Message: Rollout has minimum availability
Reason: AvailableReason
Status: True
Type: Available
Current Pod Hash: 687d76d795
Current Step Hash: f64cdc9d
Current Step Index: 8
Observed Generation: 1
Phase: Healthy
Ready Replicas: 5
Replicas: 5
Selector: app=rollouts-demo
Stable RS: 687d76d795
Updated Replicas: 5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal RolloutAddedToInformer 3m58s rollouts-controller Rollout resource added to informer: test/rollouts-demo
Normal RolloutUpdated 3m57s rollouts-controller Rollout updated to revision 1
Normal NewReplicaSetCreated 3m57s rollouts-controller Created ReplicaSet rollouts-demo-687d76d795 (revision 1)
Normal RolloutNotCompleted 3m57s rollouts-controller Rollout not completed, started update to revision 1 (687d76d795)
Normal ScalingReplicaSet 3m57s rollouts-controller Scaled up ReplicaSet rollouts-demo-687d76d795 (revision 1) from 0 to 5
Normal RolloutCompleted 3m57s rollouts-controller Rollout completed update to revision 1 (687d76d795): Initial deploy
(⎈|kind-myk8s:N/A) gaji:~/my-sample-app/my-sample-app/argo-rollouts$ kubectl get pod -l app=rollouts-demo -n test
NAME READY STATUS RESTARTS AGE
rollouts-demo-687d76d795-26ngm 1/1 Running 0 4m12s
rollouts-demo-687d76d795-7r88r 1/1 Running 0 4m12s
rollouts-demo-687d76d795-crwnh 1/1 Running 0 4m12s
rollouts-demo-687d76d795-rpxmt 1/1 Running 0 4m12s
rollouts-demo-687d76d795-zpkxl 1/1 Running 0 4m12s
(⎈|kind-myk8s:N/A) gaji:~/my-sample-app/my-sample-app/argo-rollouts$ kubectl get svc,ep rollouts-demo -n test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/rollouts-demo ClusterIP 10.96.12.247 <none> 80/TCP 4m18s
NAME ENDPOINTS AGE
endpoints/rollouts-demo 10.244.0.21:8080,10.244.0.22:8080,10.244.0.23:8080 + 2 more... 4m18s
(⎈|kind-myk8s:N/A) gaji:~/my-sample-app/my-sample-app/argo-rollouts$ kubectl get rollouts rollouts-demo -n test -o json | grep rollouts-demo
"argocd.argoproj.io/tracking-id": "rollout-test:argoproj.io/Rollout:test/rollouts-demo",
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"argoproj.io/v1alpha1\",\"kind\":\"Rollout\",\"metadata\":{\"annotations\":{\"argocd.argoproj.io/tracking-id\":\"rollout-test:argoproj.io/Rollout:test/rollouts-demo\"},\"name\":\"rollouts-demo\",\"namespace\":\"test\"},\"spec\":{\"replicas\":5,\"revisionHistoryLimit\":2,\"selector\":{\"matchLabels\":{\"app\":\"rollouts-demo\"}},\"strategy\":{\"canary\":{\"steps\":[{\"setWeight\":20},{\"pause\":{}},{\"setWeight\":40},{\"pause\":{\"duration\":10}},{\"setWeight\":60},{\"pause\":{\"duration\":10}},{\"setWeight\":80},{\"pause\":{\"duration\":10}}]}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"rollouts-demo\"}},\"spec\":{\"containers\":[{\"image\":\"argoproj/rollouts-demo:blue\",\"name\":\"rollouts-demo\",\"ports\":[{\"containerPort\":8080,\"name\":\"http\",\"protocol\":\"TCP\"}],\"resources\":{\"requests\":{\"cpu\":\"5m\",\"memory\":\"32Mi\"}}}]}}}}\n",
"name": "rollouts-demo",
"app": "rollouts-demo"
"app": "rollouts-demo"
"image": "argoproj/rollouts-demo:blue", # old 버전을 blue라고 생각하자
"name": "rollouts-demo",
"message": "ReplicaSet \"rollouts-demo-687d76d795\" has successfully progressed.",
"selector": "app=rollouts-demo",
- Updating a Rollout (blue → yellow 버전으로 변경)
kubectl edit rollouts rollouts-demo -n test



→ old 버전 5개에서 1개가 신규 버전으로 뜨고 난 후 삭제됨.
→ 배포된 애플리케이션 에러 등을 확인한 후 마저 배포할 경우, 우측 상단에 'Promote'를 눌러서 계속 진행하면 된다.



→ 5개의 pod 모두 신규 버전으로 rollout 되었다.
'STUDY - CICD' 카테고리의 다른 글
| 7주차 - HashiCorp Vault (0) | 2025.11.29 |
|---|---|
| 6주차 - Argo CD 3/3 (1) | 2025.11.20 |
| 4주차 - Argo CD 1/3 (0) | 2025.11.08 |
| 3주차 - Jenkins + ArgoCD (0) | 2025.11.01 |
| 2주차 - Helm, Tekton (0) | 2025.10.25 |