Post

CI/CD 5์ฃผ์ฐจ ์ •๋ฆฌ

๐Ÿš€ ์‹ค์Šตํ™˜๊ฒฝ ์ค€๋น„

1. xt_multiport ์ปค๋„ ๋ชจ๋“ˆ ๋กœ๋“œ

1
2
3
4
5
sudo modprobe xt_multiport

lsmod | grep xt_multiport
xt_multiport           16384  1
x_tables               65536  14 xt_conntrack,xt_statistic,nft_compat,xt_multiport,xt_tcpudp,xt_addrtype,xt_nat,xt_comment,xt_set,ipt_REJECT,xt_nfacct,ip_tables,xt_MASQUERADE,xt_mark
  • iptables์šฉ xt_multiport ์ปค๋„ ๋ชจ๋“ˆ ๋กœ๋“œํ•จ

2. kind ๊ธฐ๋ฐ˜ Kubernetes ํด๋Ÿฌ์Šคํ„ฐ(myk8s) ์ƒ์„ฑ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  labels:
    ingress-ready: true
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
  - containerPort: 30000
    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

Not sure what to do next? ๐Ÿ˜…  Check out https://kind.sigs.k8s.io/docs/user/quick-start/

3. ์ปจํŠธ๋กค ํ”Œ๋ ˆ์ธ ๋…ธ๋“œ ๋ผ๋ฒจ ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
11
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": ""
}

4. NGINX Ingress Controller ๋งค๋‹ˆํŽ˜์ŠคํŠธ ์ ์šฉ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

5. Ingress NGINX Controller ๊ธฐ๋ณธ ์ƒํƒœ ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   0/1     1            0           4s

NAME                               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller   LoadBalancer   10.96.226.252   <pending>     80:30185/TCP,443:30620/TCP   4s

NAME                                 ENDPOINTS   AGE
endpoints/ingress-nginx-controller   <none>      4s
  • READY: 0/1 โ†’ ์ปจํŠธ๋กค๋Ÿฌ Pod๊ฐ€ ์•„์ง Available ์ƒํƒœ ์ „ํ™˜ ์ „ ์‹œ์ 

6. Ingress NGINX Controller Deployment ์ƒ์„ธ ์„ค์ • ์ ๊ฒ€

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
kubectl describe -n ingress-nginx deployments/ingress-nginx-controller

Name:                   ingress-nginx-controller
Namespace:              ingress-nginx
CreationTimestamp:      Sat, 15 Nov 2025 10:08:37 +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 | 0 available | 1 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    ReplicaSetUpdated
OldReplicaSets:  <none>
NewReplicaSet:   ingress-nginx-controller-8676d56f78 (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  12s   deployment-controller  Scaled up replica set ingress-nginx-controller-8676d56f78 from 0 to 1

๐Ÿ” Argo CD ์„ค์น˜ + Ingress by Helm

1. Argo CD์šฉ self-signed TLS ํ‚คยท์ธ์ฆ์„œ ์ƒ์„ฑ

1
2
3
4
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout argocd.example.com.key \
  -out argocd.example.com.crt \
  -subj "/CN=argocd.example.com/O=argocd"

2. TLS ํ‚คยท์ธ์ฆ์„œ ํŒŒ์ผ ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ํ™•์ธ

1
2
3
4
ls -l argocd.example.com.*

.rw-r--r-- 1.2k devshin 15 Nov 10:09 argocd.example.com.crt
.rw------- 1.7k devshin 15 Nov 10:09 argocd.example.com.key

3. ์ธ์ฆ์„œ ์ƒ์„ธ ์ •๋ณด ๊ฒ€์ฆ (openssl x509)

1
2
3
4
5
6
7
8
9
10
11
openssl x509 -noout -text -in argocd.example.com.crt

...
        Issuer: CN=argocd.example.com, O=argocd
        Validity
            Not Before: Nov 15 01:09:25 2025 GMT
            Not After : Nov 15 01:09:25 2026 GMT
        Subject: CN=argocd.example.com, O=argocd
        ...
            X509v3 Basic Constraints: critical
                CA:TRUE
  • Issuer์™€ Subject ๋‘˜ ๋‹ค CN=argocd.example.com, O=argocd โ†’ self-signed ์ธ์ฆ์„œ
  • X509v3 Basic Constraints: critical / CA:TRUE ๋กœ, ์ž์ฒด CA ์—ญํ• ๋„ ๊ฐ€๋Šฅํ•œ ์ธ์ฆ์„œ๋กœ ์ƒ์„ฑ๋จ

4. Argo CD ์ „์šฉ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ

1
2
3
kubectl create ns argocd

namespace/argocd created

5. TLS Secret(argocd-server-tls) ์ƒ์„ฑ

1
2
3
4
5
kubectl -n argocd create secret tls argocd-server-tls \
  --cert=argocd.example.com.crt \
  --key=argocd.example.com.key

secret/argocd-server-tls created

6. TLS Secret ์ƒ์„ฑ ์ƒํƒœ ํ™•์ธ

1
2
3
4
kubectl get secret -n argocd

NAME                TYPE                DATA   AGE
argocd-server-tls   kubernetes.io/tls   2      9s

7. Argo CD Helm Values ํŒŒ์ผ ์ž‘์„ฑ

1
2
3
4
5
6
7
8
9
10
11
12
13
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

8. Helm Chart๋กœ Argo CD(v3.1.9) ์„ค์น˜

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
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: Sat Nov 15 10:10:38 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)

9. Argo CD ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ ์ ๊ฒ€

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
kubectl get pod,ingress,svc,ep,secret,cm -n argocd

NAME                                                   READY   STATUS    RESTARTS   AGE
pod/argocd-application-controller-0                    1/1     Running   0          118s
pod/argocd-applicationset-controller-bbff79c6f-h4p69   1/1     Running   0          118s
pod/argocd-dex-server-6877ddf4f8-cg5ss                 1/1     Running   0          118s
pod/argocd-notifications-controller-7b5658fc47-4qq4j   1/1     Running   0          118s
pod/argocd-redis-7d948674-v7d7s                        1/1     Running   0          118s
pod/argocd-repo-server-7679dc55f5-gjqzw                1/1     Running   0          118s
pod/argocd-server-7d769b6f48-kznpf                     1/1     Running   0          118s

NAME                                      CLASS   HOSTS                ADDRESS     PORTS     AGE
ingress.networking.k8s.io/argocd-server   nginx   argocd.example.com   localhost   80, 443   118s

NAME                                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
service/argocd-applicationset-controller   ClusterIP   10.96.118.107   <none>        7000/TCP            119s
service/argocd-dex-server                  ClusterIP   10.96.141.129   <none>        5556/TCP,5557/TCP   119s
service/argocd-redis                       ClusterIP   10.96.8.99      <none>        6379/TCP            119s
service/argocd-repo-server                 ClusterIP   10.96.111.208   <none>        8081/TCP            119s
service/argocd-server                      ClusterIP   10.96.252.68    <none>        80/TCP,443/TCP      119s

NAME                                         ENDPOINTS                           AGE
endpoints/argocd-applicationset-controller   10.244.0.13:7000                    118s
endpoints/argocd-dex-server                  10.244.0.11:5557,10.244.0.11:5556   118s
endpoints/argocd-redis                       10.244.0.9:6379                     118s
endpoints/argocd-repo-server                 10.244.0.12:8081                    118s
endpoints/argocd-server                      10.244.0.15:8080,10.244.0.15:8080   119s

NAME                                  TYPE                 DATA   AGE
secret/argocd-initial-admin-secret    Opaque               1      117s
secret/argocd-notifications-secret    Opaque               0      119s
secret/argocd-redis                   Opaque               1      2m2s
secret/argocd-secret                  Opaque               3      119s
secret/argocd-server-tls              kubernetes.io/tls    2      2m50s
secret/sh.helm.release.v1.argocd.v1   helm.sh/release.v1   1      2m21s

NAME                                      DATA   AGE
configmap/argocd-cm                       18     119s
configmap/argocd-cmd-params-cm            20     119s
configmap/argocd-gpg-keys-cm              0      119s
configmap/argocd-notifications-cm         1      119s
configmap/argocd-rbac-cm                  4      119s
configmap/argocd-redis-health-configmap   2      119s
configmap/argocd-ssh-known-hosts-cm       1      119s
configmap/argocd-tls-certs-cm             0      119s
configmap/kube-root-ca.crt                1      2m54s

10. argocd-server Ingress ์„ค์ • ์ƒ์„ธ ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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    73s (x2 over 2m12s)  nginx-ingress-controller  Scheduled for sync
1
2
3
4
kubectl get ingress -n argocd argocd-server

NAME            CLASS   HOSTS                ADDRESS     PORTS     AGE
argocd-server   nginx   argocd.example.com   localhost   80, 443   2m29s

11. Ingress YAML ์ •๋ฆฌ ์ถœ๋ ฅ ๋ฐ TLS/Backend spec ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
kubectl get ingress -n argocd argocd-server -o yaml | kubectl neat |yq

...
      "nginx.ingress.kubernetes.io/force-ssl-redirect": "true",
      "nginx.ingress.kubernetes.io/ssl-passthrough": "true"
		...
    "name": "argocd-server",
    "namespace": "argocd"
  },
  "spec": {
    "ingressClassName": "nginx",
    "rules": [
      {
        "host": "argocd.example.com",
        "http": {
          "paths": [
            {
              "backend": {
                "service": {
                  "name": "argocd-server",
                  "port": {
                    "number": 443
                  }
                }
              },
              "path": "/",
              "pathType": "Prefix"
            }
          ]
        }
      }
    ],
    "tls": [
      {
        "hosts": [
          "argocd.example.com"
        ],
        "secretName": "argocd-server-tls"
      }
    ]
  }
}

12. /etc/hosts ์— ๋„๋ฉ”์ธ ๋งคํ•‘ ์„ค์ •

1
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts
1
2
3
cat /etc/hosts
...
127.0.0.1 argocd.example.com

13. curl ๋กœ HTTPS ์ ‘์† ๋ฐ TLS ํ•ธ๋“œ์…ฐ์ดํฌ/๋ฆฌ๋””๋ ‰์…˜ ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust: peer verification disabled
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* 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 / X25519MLKEM768 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*   subject: CN=argocd.example.com; O=argocd
*   start date: Nov 15 01:09:25 2025 GMT
*   expire date: Nov 15 01:09:25 2026 GMT
*   issuer: CN=argocd.example.com; O=argocd
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* SSL certificate OpenSSL verify result: self-signed certificate (18)
*  SSL certificate verification failed, continuing anyway!
* Established connection to argocd.example.com (127.0.0.1 port 443) from 127.0.0.1 port 56882 
* 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.17.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: argocd.example.com
> User-Agent: curl/8.17.0
> Accept: */*
> 
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 307 
< date: Sat, 15 Nov 2025 01:19:03 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:443 left intact

14. Ingress NGINX ์ปจํŠธ๋กค๋Ÿฌ ๋กœ๊ทธ๋กœ Argo CD ์š”์ฒญ ํ๋ฆ„ ํ™•์ธ

1
2
3
4
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller

...
172.18.0.1 - - [15/Nov/2025:01:19:03 +0000] "GET / HTTP/2.0" 307 63 "-" "curl/8.17.0" 33 0.001 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 bc2372edab531a25b00d5e8e9b55bc36
  • Ingress๊ฐ€ argocd-argocd-server-443 ๋ฐฑ์—”๋“œ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋„˜๊ธฐ๊ณ , ์ตœ์ข…์ ์œผ๋กœ Argo CD ์„œ๋ฒ„ Pod (10.244.0.15:8080)๊นŒ์ง€ ๋ผ์šฐํŒ…๋˜๊ณ  ์žˆ์Œ์„ ํ™•์ธ

15. Argo CD ์„œ๋ฒ„ ๋กœ๊ทธ์—์„œ TLS Secret ๋กœ๋”ฉ ํ™•์ธ

1
2
3
4
5
6
kubectl -n argocd logs deploy/argocd-server

...
time="2025-11-15T01:20:02Z" level=info msg="Loading TLS configuration from secret argocd/argocd-server-tls"
time="2025-11-15T01:20:03Z" level=info msg="invalidated cache for resource in namespace: argocd with the name: argocd-notifications-cm"
time="2025-11-15T01:20:03Z" level=info msg="invalidated cache for resource in namespace: argocd with the name: argocd-notifications-secret"

16. ์ดˆ๊ธฐ admin ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ ์กฐํšŒ

1
2
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
uikd9ka0nmfTTcmV

17. ๋ธŒ๋ผ์šฐ์ €๋กœ Argo CD UI ์ ‘์† ์‹œ๋„ ๋ฐ ๋™์ž‘ ํ™•์ธ

1
http://argocd.example.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller

172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/1.1" 308 164 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 338 0.000 [argocd-argocd-server-443] [] - - - - 44eb99d55dce95412869019493c5b074
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 3a96052abfc7451ecb06aeb95f4075e4
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 72be6e4174ab0fbe86366faf21a6916e
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 137c63efef77d9f6c11429a9fbc10593
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.001 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 b8f42975e9f718a00fb0b497291d935d
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 20fa026fe954afe7756977ccb5d973c9
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 15f6af309ff20d89650573cd3240039b
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 51588e07b31e78e14ccfa5fd85354764
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 7000eed8384feb8d52188c25fc32e623
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 6d78d3a9c5a11fb93eb8b1d80e2eeb59
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 0c671ca13070b605fe689052b8278be7
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 215cf83536bb7b697111fa5bdc233cf4
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.001 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 a319cd1ddfffa8403d76b870aac5b355
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.001 307 92edf9f438425a11767dfc6ac06023c7
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 1bb6c1268d4d63efc8d7cf978746b8bd
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 8ac0291d43e0dba665d422bef78a3531
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.001 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 24793ccc9f08837d4f4d46cfb2302325
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 14a3a471899e1ea07a77d1548dbed123
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 f1187b04a1eb41b878342479267c9445
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 e6bb18b1b9fbac5a042e6abc7d93fbfe
172.18.0.1 - - [15/Nov/2025:02:01:23 +0000] "GET / HTTP/2.0" 307 63 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0" 17 0.000 [argocd-argocd-server-443] [] 10.244.0.15:8080 63 0.000 307 24d9dd37dd24e42dc29e05dbc5e4d9fb
  • GET / HTTP/1.1 โ†’ 308, ์ดํ›„ ๋‹ค์ˆ˜์˜ GET / HTTP/2.0 โ†’ 307 ์‘๋‹ต ๋กœ๊ทธ๊ฐ€ ์—ฐ์† ์ถœ๋ ฅ
  • ๋ชจ๋‘ backend: [argocd-argocd-server-443] โ†’ 10.244.0.15:8080 ์œผ๋กœ ํฌ์›Œ๋”ฉ๋˜๋Š” 307 ๋ฆฌ๋””๋ ‰์…˜

18. Ingress Controller์— SSL Passthrough ๊ธฐ๋Šฅ ํ™œ์„ฑํ™”

1
2
3
kubectl edit -n ingress-nginx deployments/ingress-nginx-controller

- --enable-ssl-passthrough ์ถ”๊ฐ€

19. Argo CD CLI๋กœ ์„œ๋ฒ„ ๋กœ๊ทธ์ธ (admin ๊ณ„์ •)

1
2
3
4
5
6
argocd login argocd.example.com --insecure
Username: admin
Password: uikd9ka0nmfTTcmV

'admin:login' logged in successfully
Context 'argocd.example.com' updated
1
2
3
4
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.  
1
2
3
4
argocd account list

NAME   ENABLED  CAPABILITIES
admin  true     login

โ›” ์ ‘๊ทผ ์ œ์–ด

1. ํŒจ๋“œ์›Œ๋“œ ๋ณ€๊ฒฝ

1
2
3
4
5
6
7
argocd account update-password
*** Enter password of currently logged in user (admin): uikd9ka0nmfTTcmV
*** Enter new password for user admin: qwe12345
*** Confirm new password for user admin: qwe12345

Password updated
Context 'argocd.example.com' updated

2. argocd-cm ConfigMap ๊ธฐ๋ณธ ์„ค์ • ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
kubectl get cm -n argocd argocd-cm -o yaml

apiVersion: v1
data:
  admin.enabled: "true"
  application.instanceLabelKey: argocd.argoproj.io/instance
  application.sync.impersonation.enabled: "false"
  exec.enabled: "false"
  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
      - '.metadata.annotations."control-plane.alpha.kubernetes.io/leader"'
  resource.customizations.ignoreResourceUpdates.Endpoints: |
    jsonPointers:
      - /metadata
      - /subsets
  resource.customizations.ignoreResourceUpdates.all: |
    jsonPointers:
      - /status
  resource.customizations.ignoreResourceUpdates.apps_ReplicaSet: |
    jqPathExpressions:
      - '.metadata.annotations."deployment.kubernetes.io/desired-replicas"'
      - '.metadata.annotations."deployment.kubernetes.io/max-replicas"'
      - '.metadata.annotations."rollout.argoproj.io/desired-replicas"'
  resource.customizations.ignoreResourceUpdates.argoproj.io_Application: |
    jqPathExpressions:
      - '.metadata.annotations."notified.notifications.argoproj.io"'
      - '.metadata.annotations."argocd.argoproj.io/refresh"'
      - '.metadata.annotations."argocd.argoproj.io/hydrate"'
      - '.operation'
  resource.customizations.ignoreResourceUpdates.argoproj.io_Rollout: |
    jqPathExpressions:
      - '.metadata.annotations."notified.notifications.argoproj.io"'
  resource.customizations.ignoreResourceUpdates.autoscaling_HorizontalPodAutoscaler: |
    jqPathExpressions:
      - '.metadata.annotations."autoscaling.alpha.kubernetes.io/behavior"'
      - '.metadata.annotations."autoscaling.alpha.kubernetes.io/conditions"'
      - '.metadata.annotations."autoscaling.alpha.kubernetes.io/metrics"'
      - '.metadata.annotations."autoscaling.alpha.kubernetes.io/current-metrics"'
  resource.customizations.ignoreResourceUpdates.discovery.k8s.io_EndpointSlice: |
    jsonPointers:
      - /metadata
      - /endpoints
      - /ports
  resource.exclusions: |
    ### Network resources created by the Kubernetes control plane and excluded to reduce the number of watched events and UI clutter
    - apiGroups:
      - ''
      - discovery.k8s.io
      kinds:
      - Endpoints
      - EndpointSlice
    ### Internal Kubernetes resources excluded reduce the number of watched events
    - apiGroups:
      - coordination.k8s.io
      kinds:
      - Lease
    ### Internal Kubernetes Authz/Authn resources excluded reduce the number of watched events
    - apiGroups:
      - authentication.k8s.io
      - authorization.k8s.io
      kinds:
      - SelfSubjectReview
      - TokenReview
      - LocalSubjectAccessReview
      - SelfSubjectAccessReview
      - SelfSubjectRulesReview
      - SubjectAccessReview
    ### Intermediate Certificate Request excluded reduce the number of watched events
    - apiGroups:
      - certificates.k8s.io
      kinds:
      - CertificateSigningRequest
    - apiGroups:
      - cert-manager.io
      kinds:
      - CertificateRequest
    ### Cilium internal resources excluded reduce the number of watched events and UI Clutter
    - apiGroups:
      - cilium.io
      kinds:
      - CiliumIdentity
      - CiliumEndpoint
      - CiliumEndpointSlice
    ### Kyverno intermediate and reporting resources excluded reduce the number of watched events and improve performance
    - apiGroups:
      - kyverno.io
      - reports.kyverno.io
      - wgpolicyk8s.io
      kinds:
      - PolicyReport
      - ClusterPolicyReport
      - EphemeralReport
      - ClusterEphemeralReport
      - AdmissionReport
      - ClusterAdmissionReport
      - BackgroundScanReport
      - ClusterBackgroundScanReport
      - UpdateRequest
  server.rbac.log.enforce.enable: "false"
  statusbadge.enabled: "false"
  timeout.hard.reconciliation: 0s
  timeout.reconciliation: 180s
  url: https://argocd.example.com
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: argocd
    meta.helm.sh/release-namespace: argocd
  creationTimestamp: "2025-11-15T01:11:01Z"
  labels:
    app.kubernetes.io/component: server
    app.kubernetes.io/instance: argocd
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
    app.kubernetes.io/version: v3.1.9
    helm.sh/chart: argo-cd-9.0.5
  name: argocd-cm
  namespace: argocd
  resourceVersion: "822"
  uid: a5429648-110c-4b8e-bee5-09939f3b23b6

3. ๋กœ์ปฌ ์‚ฌ์šฉ์ž alice ๊ณ„์ • ์ •์˜

1
2
3
4
kubectl edit cm -n argocd argocd-cm

accounts.alice: apiKey, login # ์ถ”๊ฐ€
configmap/argocd-cm edited

4. Argo CD ๊ณ„์ • ๋ชฉ๋ก์—์„œ alice ์ƒ์„ฑ ์—ฌ๋ถ€ ํ™•์ธ

1
2
3
4
5
argocd account list

NAME   ENABLED  CAPABILITIES
admin  true     login
alice  true     apiKey, login

5. alice ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •

1
2
3
4
5
6
argocd account update-password \
  --account alice \
  --current-password qwe12345 \
  --new-password alice12345

Password updated

6. argocd-secret ์—์„œ ๊ณ„์ • ์ •๋ณด(bcrypt ํ•ด์‹œ/ํ† ํฐ) ํ™•์ธ

1
2
3
4
5
6
7
8
9
10
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq

{
  "accounts.alice.password": "JDJhJDEwJGRWU2RZTkY4MjlHVXRaZ2FIckdrNy53Umt6Q1Ztb3QwL2tqTi81UHNyZENIcmNLSEVYZFlT",
  "accounts.alice.passwordMtime": "MjAyNS0xMS0xNVQwMzo0NzoxNVo=",
  "accounts.alice.tokens": "bnVsbA==",
  "admin.password": "JDJhJDEwJERaQXRMRUJWV1pydmc2WmI0OEhzUGVZSVRNdVVWbFo0ODRQc0tRay9jSmRQYXFwT1drS0l1",
  "admin.passwordMtime": "MjAyNS0xMS0xNVQwMzozODoxMlo=",
  "server.secretkey": "bVdDajNmSlU0YlVtT2o1YmdUZkNSVzJIZ0VSaUcxUmVJRU5QUy9sQzVSST0="
}

7. alice ํ† ํฐ ์ƒํƒœ ํ™•์ธ (์•„์ง ํ† ํฐ ์—†์Œ)

1
2
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data.accounts\.alice\.tokens}' | base64 -d ; echo
null

8. ์ƒ˜ํ”Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ Guestbook ๋ฐฐํฌ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

Warning: metadata.finalizers: "resources-finalizer.argocd.argoproj.io": prefer a domain-qualified finalizer name including a path (/) to avoid accidental conflicts with other finalizer writers
application.argoproj.io/guestbook created

9. alice ๊ณ„์ •์œผ๋กœ UI ๋กœ๊ทธ์ธ ํ›„ ๊ถŒํ•œ ์ œํ•œ ํ™•์ธ (RBAC ๊ธฐ๋ณธ๊ฐ’)

  • admin ๋กœ๊ทธ์•„์›ƒ ํ›„, UI ์—์„œ alice / alice12345 ๋กœ ๋กœ๊ทธ์ธ
  • Application ๋ชฉ๋ก/Cluster ์ •๋ณด๊ฐ€ ๋ณด์ด์ง€ ์•Š์Œ


๐Ÿ‘ค ๊ถŒํ•œ ๋ถ€์—ฌ RBAC

1. Argo CD RBAC ์„ค์ • ์ƒํƒœ ํ™•์ธ (์ดˆ๊ธฐ๊ฐ’ ์ ๊ฒ€)

1
2
3
4
5
6
7
8
kubectl get cm -n argocd argocd-rbac-cm -o jsonpath='{.data}' | jq

{
  "policy.csv": "", # ์ •์ฑ… ๋น„์–ด ์žˆ์Œ
  "policy.default": "", # ๊ธฐ๋ณธ ์ •์ฑ… ๋ฏธ์„ค์ •
  "policy.matchMode": "glob",
  "scopes": "[groups]"
}

2. argocd-rbac-cm ํŽธ์ง‘์œผ๋กœ alice ๊ถŒํ•œ ์ •์ฑ… ์ •์˜

1
2
3
kubectl edit cm -n argocd argocd-rbac-cm

configmap/argocd-rbac-cm edited

3. alice UI ๊ถŒํ•œ ๊ฒ€์ฆ โ€“ Application / Cluster ์กฐํšŒ ๊ฐ€๋Šฅ, ์‚ญ์ œ ๋ถˆ๊ฐ€

(1) Applications ํ™”๋ฉด: guestbook ์•ฑ์ด ๋ชฉ๋ก์— ํ‘œ์‹œ๋จ

(2) Clusters ํ™”๋ฉด: in-cluster ํด๋Ÿฌ์Šคํ„ฐ ์ •๋ณด ์กฐํšŒ ๊ฐ€๋Šฅ

(3) RBAC ์ •์ฑ… ์ƒ delete ๊ถŒํ•œ์ด ์—†์–ด ์‚ญ์ œ๋Š” ๋ถˆ๊ฐ€

4. alice ๊ณ„์ •์œผ๋กœ CLI ๋กœ๊ทธ์ธ ๋ฐ ๊ถŒํ•œ ํ™•์ธ (cluster/app)

1
2
3
4
5
argocd login argocd.example.com --insecure --username alice
Password: alice12345

'alice:login' logged in successfully
Context 'argocd.example.com' updated
1
2
3
4
argocd cluster list

SERVER                          NAME        VERSION  STATUS      MESSAGE  PROJECT
https://kubernetes.default.svc  in-cluster  1.32     Successful           
1
2
3
4
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

5. admin ๊ณ„์ • ๋น„ํ™œ์„ฑํ™” ์„ค์ •

1
2
3
4
kubectl edit cm -n argocd argocd-cm

 admin.enabled: "false"
configmap/argocd-cm edited

1
2
3
4
5
argocd account list

NAME   ENABLED  CAPABILITIES
admin  false    login
alice  true     apiKey, login

๐Ÿงช ๋กœ์ปฌ ์„œ๋น„์Šค ์–ด์นด์šดํŠธ RBAC

1. ๋กœ์ปฌ ์„œ๋น„์Šค ์–ด์นด์šดํŠธ์šฉ gitops-ci ๊ณ„์ • ์ƒ์„ฑ

1
2
3
4
kubectl edit cm -n argocd argocd-cm

accounts.gitops-ci: apiKey
configmap/argocd-cm edited

1
2
3
4
5
6
argocd account list

NAME       ENABLED  CAPABILITIES
admin      false    login
alice      true     apiKey, login
gitops-ci  true     apiKey

2. alice ๊ณ„์ •์œผ๋กœ gitops-ci ํ† ํฐ ์ƒ์„ฑ ์‹œ๋„ (๊ถŒํ•œ ๋ถ€์กฑ ์˜ค๋ฅ˜)

1
2
3
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-15T04:00:47Z","time":"2025-11-15T13:09:41+09:00"}

3. user-update ์—ญํ•  ์ƒ์„ฑ ๋ฐ alice์— ๊ณ„์ • ๊ด€๋ฆฌ ๊ถŒํ•œ ๋ถ€์—ฌ

1
2
3
4
5
6
7
8
kubectl edit cm -n argocd argocd-rbac-cm

  policy.csv: |
    p, role:user-update, accounts, update, *, allow
    p, role:user-update, accounts, get, *, allow
    g, alice, role:user-update
    
configmap/argocd-rbac-cm edited

4. gitops-ci ๊ณ„์ • ํ† ํฐ ์ƒ์„ฑ ๋ฐ JWT ํš๋“

1
2
3
argocd account generate-token -a gitops-ci

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJnaXRvcHMtY2k6YXBpS2V5IiwibmJmIjoxNzYzMTgwMDg5LCJpYXQiOjE3NjMxODAwODksImp0aSI6IjJjYWY4NWU5LWViNTQtNDg3ZC1iZjZmLWNkYjk4ZjAzODc1NSJ9.YvM1Xo5sQ941vTdJ-8cU9e_u35z5loCV_Cvwxqkd__g

5. gitops-ci ํ† ํฐ ๋™์ž‘ ๊ฒ€์ฆ

1
2
3
4
5
6
argocd account get-user-info --auth-token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJnaXRvcHMtY2k6YXBpS2V5IiwibmJmIjoxNzYzMTgwMDg5LCJpYXQiOjE3NjMxODAwODksImp0aSI6IjJjYWY4NWU5LWViNTQtNDg3ZC1iZjZmLWNkYjk4ZjAzODc1NSJ9.YvM1Xo5sQ941vTdJ-8cU9e_u35z5loCV_Cvwxqkd__g

Logged In: true
Username: gitops-ci
Issuer: argocd
Groups: 

๐Ÿงฉ ํ”„๋กœ์ ํŠธ ์—ญํ• ๊ณผ ํ† ํฐ TOKEN

1. sample-apps AppProject ์ƒ์„ฑ

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
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

appproject.argoproj.io/sample-apps created
1
2
3
4
5
kubectl get appproject -n argocd

NAME          AGE
default       3h8m
sample-apps   16s

2. sample-apps ํ”„๋กœ์ ํŠธ์— pre-post-sync ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ

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
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

Warning: metadata.finalizers: "resources-finalizer.argocd.argoproj.io": prefer a domain-qualified finalizer name including a path (/) to avoid accidental conflicts with other finalizer writers
application.argoproj.io/pre-post-sync created

1
2
3
4
5
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
argocd/pre-post-sync  https://kubernetes.default.svc  test       sample-apps  OutOfSync  Missing  Manual      <none>      https://github.com/argoproj/argocd-example-apps  pre-post-sync   master

3. alice ๊ณ„์ •์œผ๋กœ pre-post-sync Sync ์‹œ๋„ โ†’ ๊ถŒํ•œ ๋ถ€์กฑ์œผ๋กœ ์‹คํŒจ

1
2
3
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-15T04:00:47Z","time":"2025-11-15T13:24:55+09:00"}

4. ํ”„๋กœ์ ํŠธ ์—ญํ•  ํ† ํฐ ๋ฐœ๊ธ‰์„ ์œ„ํ•ด alice์— projects ์—…๋ฐ์ดํŠธ ๊ถŒํ•œ ๋ถ€์—ฌ

1
2
3
4
kubectl edit cm -n argocd argocd-rbac-cm

p, role:user-update, projects, update, sample-apps, allow
configmap/argocd-rbac-cm edited

5. sample-apps read-sync ์—ญํ• ์šฉ ํ”„๋กœ์ ํŠธ ํ† ํฐ ์ƒ์„ฑ

1
2
3
4
5
6
7
argocd proj role create-token sample-apps read-sync

Create token succeeded for proj:sample-apps:read-sync.
  ID: 29542016-a082-46c8-bc3e-4887a96ed15a
  Issued At: 2025-11-15T13:28:10+09:00
  Expires At: Never
  Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJwcm9qOnNhbXBsZS1hcHBzOnJlYWQtc3luYyIsIm5iZiI6MTc2MzE4MDg5MCwiaWF0IjoxNzYzMTgwODkwLCJqdGkiOiIyOTU0MjAxNi1hMDgyLTQ2YzgtYmMzZS00ODg3YTk2ZWQxNWEifQ.ESFPIoHR3iYE10zYvrGDb8-yRt4_6iwPsRXy3Dg_Mic

6. alice ๊ณ„์ •์€ ์—ฌ์ „ํžˆ ์ง์ ‘ sync ๋ถˆ๊ฐ€ (์‚ฌ์šฉ์ž RBAC์™€ ๋ณ„๊ฐœ)

1
2
3
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-15T04:00:47Z","time":"2025-11-15T13:28:52+09:00"}

7. read-sync ์—ญํ•  ํ† ํฐ์œผ๋กœ pre-post-sync ์ˆ˜๋™ ๋™๊ธฐํ™” ์„ฑ๊ณต

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJwcm9qOnNhbXBsZS1hcHBzOnJlYWQtc3luYyIsIm5iZiI6MTc2MzE4MDg5MCwiaWF0IjoxNzYzMTgwODkwLCJqdGkiOiIyOTU0MjAxNi1hMDgyLTQ2YzgtYmMzZS00ODg3YTk2ZWQxNWEifQ.ESFPIoHR3iYE10zYvrGDb8-yRt4_6iwPsRXy3Dg_Mic
argocd app sync argocd/pre-post-sync --auth-token $TOKEN

TIMESTAMP                  GROUP        KIND   NAMESPACE                  NAME                    STATUS    HEALTH        HOOK  MESSAGE
2025-11-15T13:30:16+09:00            Service        test  pre-post-sync-kustomize-guestbook-ui  OutOfSync  Missing              
2025-11-15T13:30:16+09:00   apps  Deployment        test  pre-post-sync-kustomize-guestbook-ui  OutOfSync  Missing              
2025-11-15T13:30:17+09:00          Namespace                              test   Running   Synced              namespace/test created
2025-11-15T13:30:17+09:00  batch         Job        test  pre-post-sync-before            Progressing              
2025-11-15T13:30:19+09:00  batch         Job        test  pre-post-sync-before   Running   Synced     PreSync  job.batch/pre-post-sync-before created
2025-11-15T13:30:35+09:00            Service        test  pre-post-sync-kustomize-guestbook-ui    Synced  Healthy                  
2025-11-15T13:30:35+09:00   apps  Deployment        test  pre-post-sync-kustomize-guestbook-ui    Synced  Progressing              
2025-11-15T13:30:36+09:00   apps  Deployment        test  pre-post-sync-kustomize-guestbook-ui    Synced  Healthy              
2025-11-15T13:30:37+09:00  batch         Job        test  pre-post-sync-before                  Succeeded   Synced     PreSync  Reached expected number of succeeded pods
2025-11-15T13:30:37+09:00            Service        test  pre-post-sync-kustomize-guestbook-ui    Synced   Healthy              service/pre-post-sync-kustomize-guestbook-ui created
2025-11-15T13:30:37+09:00   apps  Deployment        test  pre-post-sync-kustomize-guestbook-ui    Synced   Healthy              deployment.apps/pre-post-sync-kustomize-guestbook-ui created
2025-11-15T13:30:37+09:00  batch         Job        test   pre-post-sync-after   Running   Synced    PostSync  job.batch/pre-post-sync-after created
2025-11-15T13:30:52+09:00  batch         Job        test   pre-post-sync-after  Succeeded   Synced    PostSync  Reached expected number of succeeded pods

Name:               argocd/pre-post-sync
Project:            sample-apps
Server:             https://kubernetes.default.svc
Namespace:          test
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 13:30:17 +0900 KST
Finished:           2025-11-15 13:30:52 +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 pods
       Service     test       pre-post-sync-kustomize-guestbook-ui  Synced     Healthy            service/pre-post-sync-kustomize-guestbook-ui created
apps   Deployment  test       pre-post-sync-kustomize-guestbook-ui  Synced     Healthy            deployment.apps/pre-post-sync-kustomize-guestbook-ui created
batch  Job         test       pre-post-sync-after                   Succeeded           PostSync  Reached expected number of succeeded pods


๐Ÿณ keycloak ๋ฐฐํฌ ๋ฐ ๊ธฐ๋ณธ ์„ค์ •

1. Argo CD admin ๊ณ„์ • ์žฌํ™œ์„ฑํ™” ๋ฐ ๋กœ๊ทธ์ธ

1
2
3
4
kubectl edit cm -n argocd argocd-cm

  admin.enabled: "true"
configmap/argocd-cm edited
1
2
3
4
argocd login argocd.example.com --username admin --password qwe12345 --insecure

'admin:login' logged in successfully
Context 'argocd.example.com' updated
1
2
3
4
5
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
argocd/pre-post-sync  https://kubernetes.default.svc  test       sample-apps  Synced  Healthy  Manual      <none>      https://github.com/argoproj/argocd-example-apps  pre-post-sync   master

2. Docker๋กœ Keycloak ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ (start-dev ๋ชจ๋“œ)

1
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
1
2
3
4
5
docker ps

CONTAINER ID   IMAGE                              COMMAND                  CREATED          STATUS          PORTS                                                                                                       NAMES
a7451ea9ebdf   quay.io/keycloak/keycloak:22.0.0   "/opt/keycloak/bin/kโ€ฆ"   22 seconds ago   Up 22 seconds                                                                                                               dev-keycloak
87eae0036123   kindest/node:v1.32.8               "/usr/local/bin/entrโ€ฆ"   4 hours ago      Up 4 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:38967->6443/tcp   myk8s-control-plane

3. Keycloak Admin ์ฝ˜์†” ์ ‘์† ๋ฐ ์ดˆ๊ธฐ ๋กœ๊ทธ์ธ

1
2
http://localhost:8080/admin
admin ์›น ์ฝ˜์†” ์ ‘์† : admin / admin

4. Keycloak์— bob ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •

1
2
username: bob
password: bob123


๐Ÿ”‘ Keycloak์— Argo CD์šฉ OIDC Client ์ƒ์„ฑ

1. Keycloak์—์„œ Argo CD ์—ฐ๋™์šฉ ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ

(1) General Settings

1
2
client ID : argocd
name : argocd client

(2) Capability Config

1
Client authentication : ON

(3) Login Settings

1
2
3
4
5
Root URL : https://argocd.example.com/
Home URL : /applications
Valid redirect URIs : https://argocd.example.com/auth/callback
Valid post logout redirect URIs : https://argocd.example.com/applications
Web origins : +

2. Argo CD Client Secret ํ™•์ธ ๋ฐ ๋ฉ”๋ชจ

1
2
Credentials ํƒญ์—์„œ ํด๋ผ์ด์–ธํŠธ ์‹œํฌ๋ฆฟ ํ™•์ธ
t6dmKRr76nGQDNfcwYAsVSZFXLpeM6hc


๐ŸŒ Configuring ArgoCD OIDC

1. Argo CD OIDC ํด๋ผ์ด์–ธํŠธ ์‹œํฌ๋ฆฟ ์ €์žฅ

1
2
kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "t6dmKRr76nGQDNfcwYAsVSZFXLpeM6hc" }}'
secret/argocd-secret patched
1
2
3
4
5
6
7
8
9
10
11
12
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq

{
  "accounts.alice.password": "JDJhJDEwJGRWU2RZTkY4MjlHVXRaZ2FIckdrNy53Umt6Q1Ztb3QwL2tqTi81UHNyZENIcmNLSEVYZFlT",
  "accounts.alice.passwordMtime": "MjAyNS0xMS0xNVQwMzo0NzoxNVo=",
  "accounts.alice.tokens": "bnVsbA==",
  "accounts.gitops-ci.tokens": "W3siaWQiOiIyY2FmODVlOS1lYjU0LTQ4N2QtYmY2Zi1jZGI5OGYwMzg3NTUiLCJpYXQiOjE3NjMxODAwODl9XQ==",
  "admin.password": "JDJhJDEwJERaQXRMRUJWV1pydmc2WmI0OEhzUGVZSVRNdVVWbFo0ODRQc0tRay9jSmRQYXFwT1drS0l1",
  "admin.passwordMtime": "MjAyNS0xMS0xNVQwMzozODoxMlo=",
  "oidc.keycloak.clientSecret": "dDZkbUtScjc2bkdRRE5mY3dZQXNWU1pGWExwZU02aGM=",
  "server.secretkey": "bVdDajNmSlU0YlVtT2o1YmdUZkNSVzJIZ0VSaUcxUmVJRU5QUy9sQzVSST0="
}
  • oidc.keycloak.clientSecret ํ•ญ๋ชฉ์ด base64 ์ธ์ฝ”๋”ฉ๋œ ๊ฐ’์œผ๋กœ ์ถ”๊ฐ€๋œ ๊ฒƒ ํ™•์ธ๋จ

2. Keycloak Issuer URL ๊ตฌ์„ฑ์„ ์œ„ํ•œ ํ˜ธ์ŠคํŠธ IP ํ™•์ธ

1
2
3
ifconfig | grep 192.

        inet 192.168.219.107  netmask 255.255.255.0  broadcast 192.168.219.255
  • Keycloak์€ host ๋„คํŠธ์›Œํฌ๋กœ ๋„์›Œ์ ธ ์žˆ์œผ๋ฏ€๋กœ, ํ˜ธ์ŠคํŠธ์˜ IP๋ฅผ ๊ทธ๋Œ€๋กœ issuer์— ์‚ฌ์šฉํ•ด์•ผ ํ•จ

3. Argo CD OIDC ์„ค์ •(oidc.config) ์ถ”๊ฐ€

1
2
3
4
5
6
7
8
9
10
11
kubectl edit cm -n argocd argocd-cm

data:
  oidc.config: |
    name: Keycloak
    issuer: http://192.168.219.107:8080/realms/master
    clientID: argocd
    clientSecret: t6dmKRr76nGQDNfcwYAsVSZFXLpeM6hc
    requestedScopes: ["openid", "profile", "email"]
    
configmap/argocd-cm edited    

4. Argo CD ์„œ๋ฒ„ ๋กค๋ง ์žฌ์‹œ์ž‘์œผ๋กœ OIDC ์„ค์ • ๋ฐ˜์˜

1
2
3
kubectl rollout restart deploy argocd-server -n argocd

deployment.apps/argocd-server restarted

(1) Argo CD ๋กœ๊ทธ์ธ ํ™”๋ฉด์— Keycloak ๋ฒ„ํŠผ ํ™•์ธ

(2) ์„ค์ •ํ•œ Issuer/Client ์ •๋ณด ๊ธฐ๋ฐ˜์œผ๋กœ OIDC ํ”Œ๋กœ์šฐ ๋™์ž‘

5. Keycloak ์‚ฌ์šฉ์ž bob ์œผ๋กœ Argo CD SSO ๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ

  • Argo CD ์ ‘์† ์„ฑ๊ณต

6. ์ƒˆ๋กœ์šด Keycloak ์‚ฌ์šฉ์ž tom ์ƒ์„ฑ ๋ฐ SSO ๋กœ๊ทธ์ธ ๊ฒ€์ฆ

(1) Keycloak Admin ์ฝ˜์†”์—์„œ ์‚ฌ์šฉ์ž tom ์ƒ์„ฑ

(2) bob ์„ธ์…˜์„ Keycloak์—์„œ sign out

(3) tom์œผ๋กœ ๋กœ๊ทธ์ธ์‹œ๋„ โ†’ Argo CD ์ ‘์† ์„ฑ๊ณต

argo๊ฐ€ ๊ณ„์ •๊ด€๋ฆฌ๋ฅผ ์•ˆ ํ•ด๋„ ๋˜๊ณ , code(ํ† ํฐ/์ •์ฑ…)๊ฐ€ ๊ถŒํ•œ์ด๋‹ค


โœจ Argo Rollout

1. Argo Rollouts ์„ค์น˜ ์ค€๋น„

1
2
3
kubectl create ns argo-rollouts

namespace/argo-rollouts created
1
2
3
4
5
6
7
cat <<EOT > argorollouts-values.yaml
dashboard:
  enabled: true
  service:
    type: NodePort
    nodePort: 30003
EOT

2. Argo Rollouts Helm ์„ค์น˜ ๋ฐ ๋ฆฌ์†Œ์Šค/CRD ํ™•์ธ

1
2
3
4
5
6
7
8
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 15:11:43 2025
NAMESPACE: argo-rollouts
STATUS: deployed
REVISION: 1
TEST SUITE: None
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl get all -n argo-rollouts

NAME                                           READY   STATUS    RESTARTS   AGE
pod/argo-rollouts-658dd58fc8-x4kcq             1/1     Running   0          46s
pod/argo-rollouts-658dd58fc8-x7bxd             1/1     Running   0          46s
pod/argo-rollouts-dashboard-6bc9fff6fc-j87hr   1/1     Running   0          46s

NAME                              TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/argo-rollouts-dashboard   NodePort   10.96.112.188   <none>        3100:30003/TCP   46s

NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/argo-rollouts             2/2     2            2           46s
deployment.apps/argo-rollouts-dashboard   1/1     1            1           46s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/argo-rollouts-658dd58fc8             2         2         2       46s
replicaset.apps/argo-rollouts-dashboard-6bc9fff6fc   1         1         1       46s
1
2
3
4
5
6
7
8
9
10
11
kubectl get crds

NAME                                   CREATED AT
analysisruns.argoproj.io               2025-11-15T06:11:44Z
analysistemplates.argoproj.io          2025-11-15T06:11:44Z
applications.argoproj.io               2025-11-15T01:11:01Z
applicationsets.argoproj.io            2025-11-15T01:11:01Z
appprojects.argoproj.io                2025-11-15T01:11:01Z
clusteranalysistemplates.argoproj.io   2025-11-15T06:11:44Z
experiments.argoproj.io                2025-11-15T06:11:44Z
rollouts.argoproj.io                   2025-11-15T06:11:44Z

3. Argo Rollouts ๋Œ€์‹œ๋ณด๋“œ ์ ‘์†

1
http://127.0.0.1:30003

  • ์„ค์น˜ ์งํ›„์—๋Š” Rollout ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ฌด ๋ฆฌ์†Œ์Šค๋„ ๋ณด์ด์ง€ ์•Š๋Š” ์ƒํƒœ๋กœ ํ‘œ์‹œ๋จ

4. Argo CD์— Rollout Extension ์—ฐ๋™ (UI ํ†ตํ•ฉ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
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
helm upgrade -i argocd argo/argo-cd --version 9.0.5 -f argocd-values.yaml --namespace argocd

Release "argocd" has been upgraded. Happy Helming!
NAME: argocd
LAST DEPLOYED: Sat Nov 15 15:18:15 2025
NAMESPACE: argocd
STATUS: deployed
REVISION: 2
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubectl 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)
...      

5. GitHub my-sample-app ๋ ˆํฌ ์ค€๋น„ ๋ฐ Rollout ๋งค๋‹ˆํŽ˜์ŠคํŠธ ์ปค๋ฐ‹

(1) GitHub Personal Access Token ์ƒ์„ฑ

(2) my-sample-app ๋ ˆํฌ ์ƒ์„ฑ

(3) ๋กœ์ปฌ์—์„œ ์ƒ˜ํ”Œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๋‹ค์šด๋กœ๋“œ

1
2
3
4
mkdir my-sample-app && cd my-sample-app
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

(4) Git ์ดˆ๊ธฐํ™” ๋ฐ ํ‘ธ์‹œ

1
2
3
4
5
6
7
cd ..
git init
git add .
git commit -m "Add sample yaml"
git branch -M main
git remote add origin https://github.com/Shinminjin/my-sample-app.git
git push -u origin main

6. Argo CD์—์„œ rollout-test ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ์„ฑ ๋ฐ Sync

(1) GIT connection ์ƒ์„ฑ

(2) ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ์„ฑ

1
2
3
4
5
6
7
8
9
App name: rollout-test
Project: default
Sync policy: Manual
Set deletion finalizer: ์ฒดํฌ
Auto-create namespace: ์ฒดํฌ
Repo URL: https://github.com/Shinminjin/my-sample-app (์‹ค์Šต ๊ธฐ์ค€ URL)
Path: argo-rollouts
Destination: https://kubernetes.default.svc
Namespace: test

(3) ์ƒ์„ฑ ํ›„, SYNC ์‹คํ–‰

7. ๊ธฐ๋ณธ Rollout ์ƒํƒœ ํ™•์ธ โ€“ blue ๋ฒ„์ „ ๋ฐฐํฌ

1
2
3
4
kubectl get rollout -n test

NAME            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
rollouts-demo   5         5         5            5           3m33s
1
2
3
4
5
kubectl get rollouts rollouts-demo -n test -o json | grep rollouts-demo

...
                        "image": "argoproj/rollouts-demo:blue",
                        "name": "rollouts-demo",
  • ์ดˆ๊ธฐ ์ƒํƒœ๋Š” argoproj/rollouts-demo:blue ์ด๋ฏธ์ง€๋กœ 5๊ฐœ์˜ Pod๊ฐ€ ๋ชจ๋‘ ์„œ๋น„์Šค ์ค‘์ธ ์ƒํƒœ

8. Rollout ์—…๋ฐ์ดํŠธ โ€“ yellow ์ด๋ฏธ์ง€๋กœ ์ ์ง„ ๋ฐฐํฌ

1
2
3
4
5
6
kubectl edit rollouts rollouts-demo -n test

...
     - image: argoproj/rollouts-demo:yellow
...     
rollout.argoproj.io/rollouts-demo edited

  • old 4 / new 1 ๊ตฌ์กฐ๋กœ ํŠธ๋ž˜ํ”ฝ ์ผ๋ถ€๋งŒ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์ „ํ™˜๋œ ๋ชจ์Šต ํ™•์ธ

9. ์ƒˆ ๋ฒ„์ „ Promote โ€“ Canary์—์„œ ์ „์ฒด ์ „ํ™˜ ์™„๋ฃŒ

This post is licensed under CC BY 4.0 by the author.