본문 바로가기
devops/cicd

[CI/CD] 6주차 - Argo CD 3/3

by gnobaaaar 2025. 11. 23.

 

Cluster Management

OrbStack

https://orbstack.dev/

 

OrbStack · Fast, light, simple Docker & Linux

Say goodbye to slow, clunky containers and VMs. The fast, light, and easy way to run containers and Linux. Develop at lightspeed with our Docker Desktop alternative.

orbstack.dev

 

원활한 테스트를 위해 OrbStack을 설치해줍니다 (mac)

아래 실습에서는 도커 데스크탑 대신 OrbStack을 사용합니다.

 

네이티브 macOS 앱이며 Docker Desktop에 비해 CPU와 메모리 사용량이 적습니다.

아래는 공식 웹사이트의 Benchmark 입니다.

 

아래 실습간 로컬에서 컨테이너 ip로 직접 접근해야되는 부분이 있습니다.

OrbStack은 컨테이너 네트워크 대역 인터페이스를 macOS 시스템 네트워크에 직접 노출시켜줍니다.

 

kind 클러스터 생성 시 mac 로컬에서 접근 가능한 네트워크 인터페이스가 노출됩니다. (ex. bridge102)

 

따라서 포트포워딩 없이도 컨테이너 내부 서비스에 접근이 가능합니다.

그 외에도 https 기능, 쿠버네티스, 리눅스 등이 있습니다.

다만 Docker Desktop과 마찬가지로 개인용은 무료이나 상업용은 비용을 지불해야합니다.

 

 

 

테스트

실습을 위해 kind mgmt k8s와 ingress nginx, Argo CD를 배포합니다.

enable-ssl-passthrough 옵션을 통해 Argo CD가 tls 종료를 하도록합니다.

 

Argo CD tls 인증서 구성

https://argo-cd.readthedocs.io/en/stable/operator-manual/tls/#tls-certificates-used-by-argocd-serverhttps://argo-cd.readthedocs.io/en/stable/operator-manual/tls/

 

argocd-server는 기본적으로 네임스페이스 내 argocd-server-tls 라는 이름의 Secret tls.crt 와 tls.key로 사용합니다.

해당 방식은 공식문서에서도 권장하는 방식입니다.

  • Secret 이름: argocd-server-tls (네임스페이스: argocd)
  • 필드: tls.crt (인증서), tls.key (비밀키)
  • Secret 타입: kubernetes.io/tls 또는 일반 Opaque여도 OK
  • 자동 감지됨 (재시작 없이 적용됨)
kubectl create -n argocd secret tls argocd-server-tls \
  --cert=/path/to/cert.pem \
  --key=/path/to/key.pem

 

구버전으로는 argocd-server 였지만 지금은 사용되지 않습니다.

둘다 없으면 Argo CD가 자체 서명 인증서(Self-Signed) 를 생성해서 argocd-server secret에 저장합니다.

 

# kind k8s 배포
kind create cluster --name mgmt --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
EOF

# NGINX ingress 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
KUBE_EDITOR="nano" kubectl edit -n ingress-nginx deployments/ingress-nginx-controller
...
  - --enable-ssl-passthrough
혹은
kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
        - --enable-ssl-passthrough' | kubectl apply -f -


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

#
kubectl create ns argocd

# tls 시크릿 생성
kubectl -n argocd create secret tls argocd-server-tls \
  --cert=argocd.example.com.crt \
  --key=argocd.example.com.key

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

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 argocd.example.com

# 접속 확인
curl -vk https://argocd.example.com/

# 최초 접속 암호 확인
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
38L2ZmXne7jIaRSZ

ARGOPW=<최초 접속 암호>
ARGOPW=YYpjg453yyKi4HdK

# argocd 서버 cli 로그인
argocd login argocd.example.com --insecure --username admin --password $ARGOPW

# 확인
argocd cluster list
argocd proj list
argocd account list

# admin 계정 암호 변경 : qwe12345
argocd account update-password --current-password $ARGOPW --new-password qwe12345


# Argo CD 웹 접속 주소 확인 : admin 계정 / qwe12345
open "http://argocd.example.com"
open "https://argocd.example.com"

 

정상적으로 접근된 ArgoCD

 

 

 

dev/prd 배포

테스트를 위해 두 클러스터를 더 배포합니다.

kubectl config get-contexts
CURRENT   NAME        CLUSTER     AUTHINFO    NAMESPACE
*         kind-mgmt   kind-mgmt   kind-mgmt

# 도커 네트워크 확인 : mgmt 컨테이너 IP 확인
docker network ls
docker network inspect kind | jq


# kind k8s 배포
kind create cluster --name dev --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 31000
    hostPort: 31000
EOF

kind create cluster --name prd --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 32000
    hostPort: 32000
EOF

 

설치후 context를 변경해 보면서 잘 설치되었는지 확인합니다.

# 설치 후 확인
kubectl config get-contexts
CURRENT   NAME        CLUSTER     AUTHINFO    NAMESPACE
          kind-dev    kind-dev    kind-dev    
          kind-mgmt   kind-mgmt   kind-mgmt   
*         kind-prd    kind-prd    kind-prd  

# mgmt k8s 자격증명 변경
kubectl config use-context kind-mgmt
kubectl config get-contexts

#
kubectl get node -v=6 --context kind-mgmt
kubectl get node -v=6 --context kind-dev
kubectl get node -v=6 --context kind-prd
cat ~/.kube/config

kubectl get pod -A --context kind-mgmt
kubectl get pod -A --context kind-dev
kubectl get pod -A --context kind-prd

# alias 설정
alias k8s1='kubectl --context kind-mgmt'
alias k8s2='kubectl --context kind-dev'
alias k8s3='kubectl --context kind-prd'

# 확인
k8s1 get node -owide
k8s2 get node -owide
k8s3 get node -owide

 

 

클러스터 등록

# 기본 cluster 확인
argocd cluster list
argocd cluster list -o json | jq

kubectl get secret -n argocd
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq

#
k8s2 get sa -n kube-system
k8s3 get sa -n kube-system

 

이제 Argo CD에 다른 k8s 클러스터를 등록합니다.

https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-management/

 

클러스터를 등록하기 전 ~/.kube/config 를 확인하면 클러스터 정보가 

127.0.0.1:49440 과 같이 포트포워딩된 포트로 잡혀있는 것을 확인할 수 있습니다.

 

Argo CLI는 로컬에서 수행되기 때문에 로컬과 mgmt (ArgoServer) 모두 바라볼수있는 IP대역으로 설정되어야합니다.

docker network 를 통해 클러스터의 IP 대역을 확인합니다.

# docker network inspect kind | grep -E 'Name|IPv4Address'
        "Name": "kind",
                "Name": "mgmt-control-plane",
                "IPv4Address": "192.168.97.2/24",
                "Name": "prd-control-plane",
                "IPv4Address": "192.168.97.4/24",
                "Name": "dev-control-plane",
                "IPv4Address": "192.168.97.3/24",

 

해당 대역은 컨테이너 대역이지만 처음 설치한 OrbStack은 로컬에 해당 대역으로 접근할 수 있는 인터페이스를 오픈시킵니다.

로컬에서 ping 을 시도하면 정상적으로 가는 것을 확인할 수 있습니다.

 

이제 vi ~/.kube/config 에서 kind-prd, kind-dev를 도커 네트워크에 설정된 대역으로 변경해줍니다.

각각 192.168.97.4:6443 192.168.97.3:6443 으로 변경합니다.

# dev/prd k8s 에 api server 주소 컨테이너 IP로 변경
cp ~/.kube/config ./kube-config.bak
vi ~/.kube/config
...
    server: https://192.168.97.3:6443
  name: kind-dev
  ...
    server: https://192.168.97.4:6443
  name: kind-prd
...

# 확인
kubectl get node -v=6 --context kind-dev
kubectl get node -v=6 --context kind-prd

 

ArgoCD에 다른 k8s 클러스터 등록

# dev k8s 등록
argocd cluster add kind-dev --name dev-k8s
WARNING: This will create a service account `argocd-manager` on the cluster referenced by context `kind-dev` with full cluster level privileges. Do you want to continue [y/N]? y
{"level":"info","msg":"ServiceAccount \"argocd-manager\" already exists in namespace \"kube-system\"","time":"2025-11-15T16:37:59+09:00"}
{"level":"info","msg":"ClusterRole \"argocd-manager-role\" updated","time":"2025-11-15T16:37:59+09:00"}
{"level":"info","msg":"ClusterRoleBinding \"argocd-manager-role-binding\" updated","time":"2025-11-15T16:37:59+09:00"}
{"level":"info","msg":"Using existing bearer token secret \"argocd-manager-long-lived-token\" for ServiceAccount \"argocd-manager\"","time":"2025-11-15T16:37:59+09:00"}
Cluster 'https://192.168.97.3:6443' added

#
k8s2 get sa -n kube-system argocd-manager
kubectl rolesum -n kube-system argocd-manager --context kind-dev
• [CRB] */argocd-manager-role-binding ⟶  [CR] */argocd-manager-role
  Resource  Name  Exclude  Verbs  G L W C U P D DC  
  *.*       [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔

 

dev 클러스터에는 argo-manager 라는 서비스 어카운트가 생성됩니다.

이 SA가 클러스터롤바인딩을 통해 *.* (모든 API 그룹의 모든 리소스) 에 대해 거의 모든 권한을 가진것을 확인가능합니다.

CRB = ClusterRoleBinding

CR = ClusterRole

 

# 클러스터 자격증명은 시크릿에 저장하고, 해당 시크릿에 반드 시 argocd.argoproj.io/secret-type=cluster 라벨 필요
kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=cluster
NAME                              TYPE     DATA   AGE
cluster-192.168.97.3-3897021443   Opaque   3      12m

 

 

mgmt 클러스터에서는 위 시크릿을 통해 외부 클러스터 접속 정보를 확인합니다.

 

# k9s 에서 data 내용 확인
k9s -> : secret argocd -> 아래 secret 에서 d (Describe) -> x (Toggle Decode) 로 확인

#
argocd cluster list -o json | jq
argocd cluster list 
SERVER                          NAME        VERSION  STATUS   MESSAGE                                                  PROJECT
https://192.168.97.3:6443       dev-k8s              Unknown  Cluster has no applications and is not being monitored.  
https://kubernetes.default.svc  in-cluster           Unknown  Cluster has no applications and is not being monitored. 

# prd k8s 등록
argocd cluster add kind-prd --name prd-k8s --yes
k8s3 get sa -n kube-system argocd-manager
kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=cluster
argocd cluster list
SERVER                          NAME        VERSION  STATUS   MESSAGE                                                  PROJECT
https://192.168.97.3:6443       dev-k8s              Unknown  Cluster has no applications and is not being monitored.  
https://192.168.97.4:6443       prd-k8s              Unknown  Cluster has no applications and is not being monitored.  
https://kubernetes.default.svc  in-cluster           Unknown  Cluster has no applications and is not being monitored.

 

ArgoCD에 정상적으로 연결된 것을 확인할 수 있습니다.

 

 

nginx helm chart 생성

Argo CD 배포 테스트를 위해 nginx helm chart 를 작성합니다.

개인 Github 레포에 업로드 하겠습니다.

https://github.com/gnobaaaar/nginx-chart

 

GitHub - gnobaaaar/nginx-chart: nginx helmchart for cicd study

nginx helmchart for cicd study. Contribute to gnobaaaar/nginx-chart development by creating an account on GitHub.

github.com

 

아래에서처럼 로컬 경로에 helm 파일을 생성해주겠습니다.

#
mkdir nginx-chart
cd nginx-chart

mkdir templates

cat > templates/configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}
data:
  index.html: |
{{ .Values.indexHtml | indent 4 }}
EOF

cat > templates/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
      - name: nginx
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        ports:
        - containerPort: 80
        volumeMounts:
        - name: index-html
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
      volumes:
      - name: index-html
        configMap:
          name: {{ .Release.Name }}
EOF

cat > templates/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
spec:
  selector:
    app: {{ .Release.Name }}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: {{ .Values.nodePort }}
  type: NodePort
EOF

cat > values.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>Nginx version 1.26.1</p>
  </body>
  </html>

image:
  repository: nginx
  tag: 1.26.1

replicaCount: 1

nodePort: 30000
EOF

cat > values-dev.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Dev - Kubernetes!</h1>
    <p>Nginx version 1.26.1</p>
  </body>
  </html>

image:
  repository: nginx
  tag: 1.26.1

replicaCount: 1

nodePort: 31000
EOF

cat > values-prd.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Prd - Kubernetes!</h1>
    <p>Nginx version 1.26.1</p>
  </body>
  </html>

image:
  repository: nginx
  tag: 1.26.1

replicaCount: 2

nodePort: 32000
EOF

cat > Chart.yaml <<EOF
apiVersion: v2
name: nginx-chart
description: A Helm chart for deploying Nginx with custom index.html
type: application
version: 1.0.0
appVersion: "1.26.1"
EOF

#
git add . && git commit -m "Add sample yaml" && git push -u origin main


# 헬름 렌더링 확인
helm template mgmt-nginx . -f values.yaml
helm template dev-nginx . -f values-dev.yaml
helm template prd-nginx . -f values-prd.yaml

 

 

helm 정의

Chart.yaml

Helm Chart 메타데이터 정의

name: nginx-chart
version: 1.0.0

 

values.yaml

환경별로 nginx의 동작을 다르게 설정하였습니다.

파일 nodePort index.html replicas
values.yaml 30000 “Hello, Kubernetes!” 1
values-dev.yaml 31000 “Hello, Dev - Kubernetes!” 1
values-prd.yaml 32000 “Hello, Prd - Kubernetes!” 2

 

templates/configmap.yaml

Helm 템플릿 기능을 사용해서 index.html 내용을 Configmap으로 저장합니다.

 

 

templates/deployment.yaml

volumeMounts:
  - name: index-html
    mountPath: /usr/share/nginx/html/index.html
    subPath: index.html

 

configmap 으로 저장된 index.html을 마운트해서 사용합니다.

 

templates/service.yaml

type: NodePort
nodePort: {{ .Values.nodePort }}

 

values.yaml 에서 설정된 노드포트로 외부에 접근가능하게 노출시킵니다.

 

 

ArgoCD에 nginx 배포

배포를 위해 변수값을 잡고 갑니다.

docker network inspect kind | grep -E 'Name|IPv4Address'

DEVK8SIP=192.168.97.3
PRDK8SIP=192.168.97.4
echo $DEVK8SIP $PRDK8SIP

 

# argocd app 배포
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: mgmt-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values.yaml
    path: nginx-chart
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: mgmt-nginx
    server: https://kubernetes.default.svc
EOF

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-dev.yaml
    path: nginx-chart
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: dev-nginx
    server: https://$DEVK8SIP:6443
EOF

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prd-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-prd.yaml
    path: nginx-chart
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: prd-nginx
    server: https://$PRDK8SIP:6443
EOF

#
argocd app list
kubectl get applications -n argocd dev-nginx
kubectl get applications -n argocd dev-nginx -o yaml
kubectl get applications -n argocd dev-nginx -o yaml | kubectl neat | yq
kubectl describe applications -n argocd dev-nginx
kubectl get applications -n argocd
NAME         SYNC STATUS   HEALTH STATUS
dev-nginx    Synced        Healthy
mgmt-nginx   Synced        Healthy
prd-nginx    Synced        Healthy

 

정상적으로 접근이 되는지 nodeport로 확인해봅니다.

# mgmt
kubectl get pod,svc,ep,cm -n mgmt-nginx
curl -s http://127.0.0.1:30000

# dev
kubectl get pod,svc,ep,cm -n dev-nginx --context kind-dev
curl -s http://127.0.0.1:31000

# prd
kubectl get pod,svc,ep,cm -n prd-nginx --context kind-prd
curl -s http://127.0.0.1:32000


# Argo CD App 삭제
kubectl delete applications -n argocd mgmt-nginx dev-nginx prd-nginx

 

 

 

ApplicationSet

따로 포스팅 정리합니다

 

 

 

 

OpenLDAP + KeyCloak + Argo CD + Jenkins

keycloak 배포

실습을 위해 keycloak을 파드로 배포해 보겠습니다.

#
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
        - name: keycloak
          image: quay.io/keycloak/keycloak:26.4.0
          args: ["start-dev"]     # dev mode 실행
          env:
            - name: KEYCLOAK_ADMIN
              value: admin
            - name: KEYCLOAK_ADMIN_PASSWORD
              value: admin
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: keycloak
spec:
  selector:
    app: keycloak
  ports:
    - name: http
      port: 80
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: keycloak
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  ingressClassName: nginx
  rules:
    - host: keycloak.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 8080
EOF

 

디플로이먼트와 서비스, 그리고 접근하는 ingress를 배포합니다.

args: ["start-dev"] 를 통해 개발자 모드로 실행합니다.

 

ingress의 annotation은 두가지가 쓰였습니다.

nginx.ingress.kubernetes.io/rewrite-target: /

이 설정이 있으면, /keycloak/ 로 바꿔서 백엔드(Keycloak 컨테이너)로 전달합니다.

즉, Keycloak 컨테이너는 원래 경로 /에서 동작하므로, rewrite를 하지 않으면 /keycloak이라는 경로를 처리 못합니다.

 

nginx.ingress.kubernetes.io/ssl-redirect: "false"

HTTP 요청을 HTTPS로 리다이렉트 하지않습니다.

http 접속접근도 허용해 줍니다.

 

# 확인
kubectl get deploy,svc,ep keycloak
kubectl get ingress keycloak
NAME       CLASS   HOSTS                  ADDRESS     PORTS   AGE
keycloak   nginx   keycloak.example.com   localhost   80      91s

#
curl -s -H "Host: keycloak.example.com" http://127.0.0.1 -I
HTTP/1.1 302 Found
Location: http://keycloak.example.com/admin/

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 keycloak.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 keycloak.example.com

# keycloak 웹 접속 : admin / admin
curl -s http://keycloak.example.com -I
open "http://keycloak.example.com/admin"

 

# 확인
kubectl get deploy,svc,ep keycloak
kubectl get ingress keycloak
NAME       CLASS   HOSTS                  ADDRESS     PORTS   AGE
keycloak   nginx   keycloak.example.com   localhost   80      91s

#
curl -s -H "Host: keycloak.example.com" http://127.0.0.1 -I
HTTP/1.1 302 Found
Location: http://keycloak.example.com/admin/

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 keycloak.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 keycloak.example.com

# keycloak 웹 접속 : admin / admin
curl -s http://keycloak.example.com -I
open "http://keycloak.example.com/admin"

 

/etc/hosts에 도메인을 넣어주고 접근하면 정상적으로 포털에 접근 가능합니다.

 

 

deployment 에 설정했던 admin/admin 으로 접근합니다.

접속 했다면 realms와 users를 생성해줍니다.

  • realms 생성 : myrealm
  • users 생성 : alice - 암호 alice123

 

 

mgmt k8a in-cluster 내부 설정

mgmt k8a in-cluster 내부에서도 keycloak / argocd 도메인 호출 가능하게 설정해줍니다.

테스트를 위해 경량의 curl 파드를 배포합니다.

# 경량의 curl 테스트용 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl
  namespace: default
spec:
  containers:
  - name: curl
    image: curlimages/curl:latest
    command: ["sleep", "infinity"]
EOF

 

배포된 keycloak과 argocd 정보를 확인합니다.

#
kubectl get pod -l app=keycloak -owide
kubectl get pod -l app=keycloak -o jsonpath='{.items[0].status.podIP}'
KEYCLOAKIP=$(kubectl get pod -l app=keycloak -o jsonpath='{.items[0].status.podIP}')
echo $KEYCLOAKIP 

# 기본 통신 및 정보 확인 : ClusterIP 메모!
kubectl get svc keycloak
NAME       TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
keycloak   ClusterIP   10.96.58.66   <none>        80/TCP   25m

kubectl get svc -n argocd argocd-server
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
argocd-server   ClusterIP   10.96.156.196   <none>        80/TCP,443/TCP   134m

 

예시처럼 값을 확인해보겠습니다.

keycloak pod ip : 10.244.0.17

keycloak svc cluster ip : 10.96.82.111
argocd svc cluster ip : 10.96.193.83

 

ip나 서비스클러스터 주소를 사용한 파드간 통신은 가능하지만

도메인으로의 요청 접근은 불가합니다.

현재 도메인이 DNS에 없어서 추가가 필요합니다. 

kubectl exec -it curl -- ping -c 1 $KEYCLOAKIP
kubectl exec -it curl -- curl -s http://keycloak.default.svc.cluster.local/realms/myrealm/.well-known/openid-configuration | jq

# 왜 호출이 되지 않을까?
kubectl exec -it curl -- curl -s http://keycloak.example.com -I
kubectl exec -it curl -- nslookup -debug keycloak.example.com
kubectl exec -it curl -- nslookup keycloak.example.com

 

# 해결해보자 : coredns 에 hosts 플러그인 활용
KUBE_EDITOR="nano" kubectl edit cm -n kube-system coredns
.:53 {
       ...
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        hosts {
           <CLUSTER IP> keycloak.example.com
           <CLUSTER IP> argocd.example.com
           fallthrough
        }
        reload # cm 설정 변경 시 자동으로 reload 적용됨
...

# cm 설정 변경 시 자동으로 reload 적용 로그 확인
kubectl logs -n kube-system -l k8s-app=kube-dns --timestamps

# 도메인 질의 시, ClusterIP로 변경 확인 : 파드 내에 dns 쿼리 캐시는 없는 걸까요???
kubectl exec -it curl -- nslookup -debug keycloak.example.com
kubectl exec -it curl -- nslookup keycloak.example.com

 

위 예제처럼 CoreDNS에 hosts를 직접 넣어주겠습니다.

 

reload 옵션으로 인해 자동으로 적용됩니다 (파드 재기동 불필요)

이제 도메인질의를 하면 ClusterIP로 질의하는 것을 확인할 수 있습니다.

 

 

 

keycloak, ArgoCD 설정

keycloak 설정

keycloak에 Argo CD 를 위한 client 생성을 진행하겠습니다.

  • client id : argocd
  • name : argocd client

 

 

  • 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 : +

 

 

생성된 client 에서 → Credentials : 메모 해둡니다.

ex. bVYzSVpPM25tSG9acjNCQkMzN1VwZHJNU01rRjlVbXQ

 

ArgoCD OIDC 구성

클라이언트 시크릿 설정

#
kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "<REPLACE_WITH_CLIENT_SECRET>" }}'
kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt" }}'

# 확인
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq
...
  "oidc.keycloak.clientSecret": "bVYzSVpPM25tSG9acjNCQkMzN1VwZHJNU01rRjlVbXQ=",

 

 

# 설정 추가
kubectl patch cm argocd-cm -n argocd --type merge -p '
data:
  oidc.config: |
    name: Keycloak
    issuer: http://keycloak.example.com/realms/myrealm
    clientID: argocd
    clientSecret: mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt
    requestedScopes: ["openid", "profile", "email"]
'

# 확인
kubectl get cm -n argocd argocd-cm -o yaml | grep oidc.config: -A5

  oidc.config: |
    name: Keycloak
    issuer: http://keycloak.example.com/realms/myrealm
    clientID: argocd
    clientSecret: mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt
    requestedScopes: ["openid", "profile", "email"]

 

설정되었다면 argocd를 재기동 해줍니다.

kubectl rollout restart deploy argocd-server -n argocd

 

ArgoCD 포털을 접근해서 로그아웃해주고 keycloak을 통해 로그인해줍니다.

 

로그인 과정에서, auth? 에서 페이로드 확인 : scopeopenid profile email 범위 허용 확인할 수 있습니다.

따라서 처음 접근하려고 한다면 email 기입이 필요합니다.

 

모든 입력이 완료되면 정상적으로 포털에 keycloak을 통해 접근할 수 있습니다.

 

 

jenkins 직접 파드로 배포

젠킨스에도 keyclaok을 연동해보겠습니다.

#
kubectl create ns jenkins
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pvc
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  namespace: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      securityContext:
        fsGroup: 1000
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts
          ports:
            - name: http
              containerPort: 8080
            - name: agent
              containerPort: 50000
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
      volumes:
        - name: jenkins-home
          persistentVolumeClaim:
            claimName: jenkins-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins-svc
  namespace: jenkins
spec:
  type: ClusterIP
  selector:
    app: jenkins
  ports:
    - port: 8080
      targetPort: http
      protocol: TCP
      name: http
    - port: 50000
      targetPort: agent
      protocol: TCP
      name: agent
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins-ingress
  namespace: jenkins
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
spec:
  ingressClassName: nginx
  rules:
    - host: jenkins.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: jenkins-svc
                port:
                  number: 8080
EOF

# 확인
kubectl get deploy,svc,ep,pvc -n jenkins
kubectl get ingress -n jenkins jenkins-ingress
NAME              CLASS   HOSTS                 ADDRESS     PORTS   AGE
jenkins-ingress   nginx   jenkins.example.com   localhost   80      60s

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 jenkins.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 jenkins.example.com


# 초기 암호 확인
kubectl exec -it -n jenkins deploy/jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
7521bc306a4049a6a0fd75391ae4b235

# 웹 접속 : 기본 설정 진행
curl -s http://jenkins.example.com -I
open "http://jenkins.example.com"

 

초기 패스워드로 접근합니다.

 

플러그인은 추천되는걸로 누르고 설치를 진행합니다.

  • 관리자 계정 설정 : admin / qwe123
  • in-cluster 호출을 위한 도메인 설정

젠킨스 포털에서 계정을 변경하고 나면 coreDNS에 host 를 추가해줍니다.

# 기본 통신 및 정보 확인 : ClusterIP 메모!
kubectl get svc -n jenkins
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)              AGE
jenkins-svc   ClusterIP   10.96.165.34   <none>        8080/TCP,50000/TCP   5m18s

kubectl get svc -n jenkins
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
jenkins-svc   ClusterIP   10.96.241.201   <none>        8080/TCP,50000/TCP   62m

# 해결해보자 : coredns 에 hosts 플러그인 활용
KUBE_EDITOR="nano" kubectl edit cm -n kube-system coredns
.:53 {
       ...
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        hosts {
           <CLUSTER IP> keycloak.example.com
           <CLUSTER IP> argocd.example.com
           <CLUSTER IP> jenkins.example.com
           fallthrough
        }
        reload # cm 설정 변경 시 자동으로 reload 적용됨
...

reload를 확인가능합니다.

 

 

keycloak 에 jenkins 를 위한 client 생성

  • client id : jenkins
  • name : jenkins client
  • Client authentication : Check
  • Authentication flow : Standard flow
  • Root URL : http://jenkins.example.com/
  • Home URL : http://jenkins.example.com/
  • Valid redirect URIs : http://jenkins.example.com/securityRealm/finishLogin
  • Valid post logout redirect URIs : http://jenkins.example.com
  • Web origins : +

 

Jenkins OIDC 플러그인 설치

keycloak을 통한 OIDC 사용을 위해 Jenkins 포털에서 플러그인 설치를 진행합니다.

 

 

    • 플러그인 설치 : OpenID Connect Authentication
    • Manage Jenkins → Security : Security Realm 설정 → 하단 Save
      • Login with Openid Connect
      • Client id : jenkins
      • Client secret : <keycloak 에서 jenkins client 에서 credentials>
      • Configuration mode : Discovery via Well-know configuration endpoint
        •  http://keycloak.example.com/realms/myrealm/.well-known/openid-configuration
        • Override scopes : openid email profile
      • Logout from OpenID Provider : Check
        • 젠킨스 로그아웃시 keycloak 도 함께 로그아웃됩니다 (SSO 세션)
      • Security configuration
        • Disable ssl verification : Check
          • Keycloak이 HTTP 또는 자체 서명된 SSL 인증서를 사용할 때 필요
        •  

 

 

새 창에서 jenkins 도메인으로 접근을 시도해봅니다.

302 redirect 되어 keycloak으로 이동하는 것을 확인할 수 있습니다.

 

 

 

 

 

 

 

LDAP

Lightweight Directory Access Protocol

사용자·그룹·권한 정보를 계층적으로 보관하는 주소록/조직도

 

디렉터리 서비스에 접근하기 위한 표준 프로토콜입니다.

트리 구조로 저장된 조직 내 사용자, 그룹, 권한 등의 정보를 저장하고 조회하는 시스템입니다.

dc=example,dc=org     # Base DN(Root DN)
 ├── ou=people
 │    ├── uid=alice
 │    └── uid=bob
 └── ou=groups
      ├── cn=devs
      └── cn=admins

 

각 노드는 DN(Distinguished Name)으로 전체 경로를 식별됩니다.

DN은 하나의 엔트리(ex. alice)를 고유하게 식별하는 전체경로입니다.

RDN은 DN을 구성하는 한단계 (한 조각)을 의미합니다.

  • 전체 경로의 단계는 여러 개의 RDN(Relative Distinguished Name)으로 구성
    • 예시) 사용자 alice
      • DN: uid=alice (사용자 식별자) ,ou=people (조직단위) ,dc=example,dc=org (도메인 컴포넌트)
      • DN에 할당된 “uid=alice”, “ou=people”, “dc=example,dc=org”은 각 엔트리의 RDN
# 파일시스템과 비유한다면
DN: uid=alice,ou=people,dc=example,dc=org
==> /org/example/people/alice  ← 이런 느낌

 

  • LDAP 서버 = 회사의 인사/보안부
    • LDAP 서버도 마찬가지로, 사용자·그룹·권한 정보를 디렉터리라는 구조로 보관하는 중앙 관리소입니다.
    • 회사에는 모든 직원의 정보(이름, 부서, 직급, 이메일 등)를 한 곳에 모아 관리하는 인사부가 있죠.
  • 디렉터리 구조 = 회사 조직도
    • LDAP도 “dc=company, ou=부서, cn=사용자” 같은 계층적 주소로 정보를 저장합니다.
    • 회사에 “본사–부서–팀–직원”으로 이어지는 조직도가 있는 것처럼,
  • 클라이언트(애플리케이션) = 직원 신분 확인 창구
    • 인사부가 “맞다/아니다” 또는 “이 사람의 권한은 이런 것”을 알려주죠.
    • 누군가 회사 시스템에 로그인하면, 시스템이 인사부(=LDAP 서버)에 “이 사람 우리 직원 맞나요?” 하고 묻습니다.
  • 인증(Authentication) = 신분증 검사
    • 시스템이 LDAP 서버에 아이디와 비밀번호를 보내 확인받습니다.
  • 권한 부여(Authorization) = 출입증/권한 확인
    • LDAP 서버가 “이 사람은 개발팀” “이 사람은 관리자”처럼 그룹 정보나 역할을 반환하면, 시스템이 그 정보에 따라 접근 권한을 부여합니다.

 

  • LDAP 디렉터리에서 표현하는 정보 구조 2가지
    • Entry : 디렉토리에서 정보를 표현하는 기본 단위이다. Entry는 다수의 attribute로 구성됩니다.
    • Atribute : Entry의 각 타입을 저장하는 공간으로 1개의 Attribute에 하나, 또는 다수의 값을 담을 수 있습니다.
  • 도구 : Windows AD, OpenLDAP - Home 등등

 

OpenLDAP 작업전 아래 삭업을 수행합니다.

사전 작업 : Keycloak 에 alice sign out 및 alice 사용자 제거

 

 

 

OpenLDAP

OpenLDAP 서버 배포

#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: openldap
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: openldap
  namespace: openldap
spec:
  replicas: 1
  selector:
    matchLabels:
      app: openldap
  template:
    metadata:
      labels:
        app: openldap
    spec:
      containers:
        - name: openldap
          image: osixia/openldap:1.5.0
          ports:
            - containerPort: 389
              name: ldap
            - containerPort: 636
              name: ldaps
          env:
            - name: LDAP_ORGANISATION    # 기관명, LDAP 기본 정보 생성 시 사용
              value: "Example Org"
            - name: LDAP_DOMAIN          # LDAP 기본 Base DN 을 자동 생성
              value: "example.org"
            - name: LDAP_ADMIN_PASSWORD  # LDAP 관리자 패스워드
              value: "admin"
            - name: LDAP_CONFIG_PASSWORD
              value: "admin"
        - name: phpldapadmin
          image: osixia/phpldapadmin:0.9.0
          ports:
            - containerPort: 80
              name: phpldapadmin
          env:
            - name: PHPLDAPADMIN_HTTPS
              value: "false"
            - name: PHPLDAPADMIN_LDAP_HOSTS
              value: "openldap"   # LDAP hostname inside cluster
---
apiVersion: v1
kind: Service
metadata:
  name: openldap
  namespace: openldap
spec:
  selector:
    app: openldap
  ports:
    - name: phpldapadmin
      port: 80
      targetPort: 80
      nodePort: 30000
    - name: ldap
      port: 389
      targetPort: 389
    - name: ldaps
      port: 636
      targetPort: 636
  type: NodePort
EOF

#
kubectl get deploy,pod,svc,ep -n openldap

# 기본 LDAP 정보 : 아래 Bind DN과 PW로 로그인
## Base DN: dc=example,dc=org
## Bind DN: cn=admin,dc=example,dc=org
## Password: admin
open http://127.0.0.1:30000

# phpLDAPadmin 로그인 
kubectl stern -n openldap openldap-54857b746c-ch9g4

 

아날로그 감성 좋다

 

OpenLDAP 구성

OpenLDAP을 쿠버네티스 환경에서 배포하고 사용자·그룹 엔트리를 구성하며, 인증/검색 테스트까지 진행해보겠습니다.

# 파드 내부로 접근
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
----------------------------------------------------
# slapd 프로세스가 실행 중임을 확인 (LDAP 서버 데몬)
pstree -aplpst
run,1 -u /container/tool/run
  └─slapd,433 -h ldap://openldap-54857b746c-ch9g4:389 ldaps://openldap-54857b746c-ch9g4:636 ldapi:/// -u openldap -g openldap -d 256
      ├─{slapd},436
      └─{slapd},437
      
# LDAP 관리자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapsearch -x -H ldap://localhost:389 -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin

# 실습 사용 최종 트리 구조
dc=example,dc=org
├── ou=people
│   ├── uid=alice
│   │   ├── cn: Alice
│   │   ├── sn: Kim
│   │   ├── uid: alice
│   │   └── mail: alice@example.org
│   └── uid=bob
│       ├── cn: Bob
│       ├── sn: Lee
│       ├── uid: bob
│       └── mail: bob@example.org
└── ou=groups
    ├── cn=devs
    │   └── member: uid=bob,ou=people,dc=example,dc=org
    └── cn=admins
        └── member: uid=alice,ou=people,dc=example,dc=org

# ldapadd로 ou 추가 (organizationalUnit)
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: ou=people,dc=example,dc=org
objectClass: organizationalUnit
ou: people

dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
ou: groups
EOF
adding new entry "ou=people,dc=example,dc=org"
adding new entry "ou=groups,dc=example,dc=org"

# ldapadd로 users 추가 (inetOrgPerson) : alice , bob
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: uid=alice,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
cn: Alice
sn: Kim
uid: alice
mail: alice@example.org
userPassword: alice123

dn: uid=bob,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
cn: Bob
sn: Lee
uid: bob
mail: bob@example.org
userPassword: bob123
EOF
adding new entry "uid=alice,ou=people,dc=example,dc=org"
adding new entry "uid=bob,ou=people,dc=example,dc=org"

# ldapadd로 groups 추가 (groupOfNames) : devs, admins
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: cn=devs,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: devs
member: uid=bob,ou=people,dc=example,dc=org

dn: cn=admins,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: admins
member: uid=alice,ou=people,dc=example,dc=org
EOF
adding new entry "cn=devs,ou=groups,dc=example,dc=org"
adding new entry "cn=admins,ou=groups,dc=example,dc=org"

# ldapsearch 검색 : ou
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "dc=example,dc=org" "(objectClass=organizationalUnit)" ou

# ldapsearch 검색 : 사용자
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "ou=people,dc=example,dc=org" "(uid=*)" uid cn mail
  
# ldapsearch 검색 : 그룹/멤버 확인
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "ou=groups,dc=example,dc=org" "(objectClass=groupOfNames)" cn member
  

# LDAP 사용자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapwhoami -x -D "uid=alice,ou=people,dc=example,dc=org" -w alice123
dn:uid=alice,ou=people,dc=example,dc=org

----------------------------------------------------

 

하나씩 나눠서 정리해보겠습니다.

 

LDAP 관리자 인증테스트

ldapsearch -x -H ldap://localhost:389 -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin
  • 관리자 계정으로 LDAP에 인증(bind) 후 전체 트리 검색 시도
  • 정상 연결되면 LDAP 루트 엔트리(dc=example,dc=org) 이하가 출력됨
    • -b어디서부터 검색할지 (Base DN)
    • -D로그인 계정
    • -w비밀번호

 

목표 LDAP 구조는 아래와 같습니다.

dc=example,dc=org
├── ou=people       ← 사용자
│   ├── uid=alice
│   └── uid=bob
└── ou=groups       ← 그룹
    ├── cn=devs
    └── cn=admins

 

 

조직 단위(ou) 추가

ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin <<EOF
...
EOF
  • ou=people, ou=groups 두 조직 단위를 추가 (objectClass: organizationalUnit)
  • 조직 구조의 컨테이너 생성

 

 

사용자 추가 (alice, bob)

cn 전체 이름 (common name)
sn 성 (surname)
uid 고유 사용자 ID
mail 이메일 주소
userPassword 로그인 비밀번호 (LDAP bind에 사용됨)

 

 

그룹 추가 (devs, admins)

  • cn=devs, cn=admins 그룹 생성
  • member 속성은 DN 경로로 지정
member: uid=alice,ou=people,dc=example,dc=org

 

 

 

KeyCloak에서 LDAP 설정 실습

실습해보면서 조금씩 이해해보겠습니다.

Keycloak은 User Federation으로 LDAP과 연동하여 사용자 정보를 동기화합니다.

 

admin Console

[Realm] myrealm → User Federation → Add LDAP providers

 

 

더보기

Add LDAP Provider Sections

  • General options - LDAP 연결 기본 정보
    • UI display name : Admin Console에서 표시될 LDAP 이름.
    • Vendor : LDAP 서버 유형 선택 (Active Directory, RHDS 등).
  • Connection and authentication settings - LDAP 접속 방법과 인증 방법 정의
    • Connection URL : ldap:// 또는 ldaps:// 주소 (예: ldap://10.0.0.10:389)
    • Enable StartTLS : 일반 포트(389)에서 TLS로 보안 연결(636)할 때 사용.
      • Test connection : 연결 사전 검증.
    • Bind type : 인증 방식(simple, none).
    • Bind DN / Bind credentials : LDAP에 접근할 계정과 비밀번호.
      • Test authentication : 계정 인증을 사전 검증.
  • LDAP searching and updating - 사용자를 찾고 업데이트하는 방식
    • Edit mode : 데이터 수정 권한.
      • READ_ONLY : LDAP 데이터는 읽기 전용.
      • WRITABLE : Keycloak 변경 내용이 LDAP에 반영됨.
      • UNSYNCED : Keycloak 로컬 데이터로만 사용.
    • Users DN : 사용자 검색의 루트 DN(Distinguished Name - 식별 이름) (예: OU=Users,DC=example,DC=com)
    • Username LDAP attribute : 로그인 시 식별할 속성
    • Search scope : 검색 범위(One Level (하위 1단계), Subtree (하위 전체)).
  • Synchronization settings - LDAP 사용자 데이터를 Keycloak으로 동기화하는 주기를 정의
    • Import users : LDAP 사용자 정보를 Keycloak DB에 임포트할지 여부.
    • Sync Registrations : Keycloak에서 새로 생성한 사용자를 LDAP에 동기화할지 여부.
    • Periodic full sync / Periodic changed users sync : 자동 동기화 주기 설정.
  • Kerberos integration - Kerberos 통합에 대한 옵션
  • Cache settings - LDAP 요청 결과 캐싱 정책 설정
  • Advanced settings - LDAP 고급 기능
    • Enable the LDAPv3 password modify extended operation : LDAP 비밀번호 변경 확장 기능 활성화.
    • Validate password policy : Keycloak의 비밀번호 정책을 LDAP 사용자에게도 적용.
    • Trust email : LDAP에서 가져온 이메일을 신뢰할지 여부.

 

 

필드값 최소 설정

  • General
    • UI display name: ldap
      • admin 콘솔에 표시될 이름
    • Vendor: Other
  • Connection and authentication
    • Connection URL: ldap://openldap.openldap.svc:389 ⇒ Test connection
      • LDAP 서버 주소 입력
    • Bind DN: (= Login DN) cn=admin,dc=example,dc=org
      • 로그인 계정정보
    • Bind Credential: admin ⇒ Test authentication
  • LDAP searching and updating
    • Edit mode: WRITABLE
      • keycloak에서 LDAP 수정가능여부
    • Users DN: ou=people,dc=example,dc=org
      • 사용자들이 저장된 위치
    • Username LDAP attribute: uid
    • RDN LDAP attribute: uid
    • UUID LDAP attribute: entryUUID
    • User Object Classes: inetOrgPerson
    • Search scope: Subtree (OU 하위 모두 탐색)
  • Synchronization settings
    • Import Users: On (LDAP → KeyCloak : Sync OK)
      • LDAP -> keycloak 동기화 여부
    • Sync Registrations: Off (KeyCloak → LDAP : Sync OK)
  • Save

 

 

[KeyCloak] LDAP Mapper 확인

admin Console

User Federation → LDAP Provider 선택 → Mappers: user에 대한 Mappers가 기본 존재

 

 

 

[KeyCloak] LDAP -> KeyCloak 동기화

admin Console

User Federation → LDAP Provider 선택 → Settings → Action : Sync all users ⇒ 실패 시, 다시 한번 더 시도!

  • 처음 LDAP 연동 후 사용자 전부 불러올 때 : LDAP의 전체 사용자를 Keycloak DB에 동기화(기존 데이터 덮어씀)

 

admin Console

  • Users → Default search → * : user 바로 보이지 않고, 해당 user 나 혹은 * 검색 시 출력

 

 

접근테스트

ArgoCD에 bob으로 접근테스트 해보겠습니다.

 

keycloak의 session 에서도 확인이 가능합니다.

 

 

 

 

 

 

 

 

 

 

'devops > cicd' 카테고리의 다른 글

[CI/CD] 7주차 - Vault (2)  (1) 2025.12.03
[CI/CD] 7주차 - Vault (1)  (0) 2025.11.29
keycloak - Oauth - OIDC  (0) 2025.11.16
[CI/CD] 5주차 - Argo CD 2/3  (0) 2025.11.16
[CI/CD] 4주차 - Argo CD 1/3 (1)  (0) 2025.11.09