본문으로 건너뛰기

3.1. OPENMARU COP 운영절차


OPENMARU COP Console 개요​

CLI(kubectl/helm)로 수행하는 대부분의 작업은 OPENMARU COP Console(웹 콘솔)에서도 동일하게 수행할 수 있습니다. 본 장의 각 절차에서는 CLI 명령과 함께 해당 작업에 대응하는 Console 화면을 함께 안내합니다.

Console 전체 레이아웃/사이드바

Console은 https://console.{sub_domain}.{domain}.{TLD} 주소로 접속하며, SSO 로그인(Keycloak 통합 인증) 또는 토큰 로그인(ServiceAccount 토큰) 두 가지 방식을 지원합니다.

Console 로그인 화면
Keycloak SSO 로그인 화면

로그인 후에는 사용자 권한(admins/users/viewers)에 따라 표시되는 메뉴와 수행 가능한 작업이 달라집니다. 여러 클러스터가 등록된 환경에서는 상단 헤더의 클러스터 선택 드롭다운으로 클러스터를 전환할 수 있고, 네임스페이스 드롭다운으로 작업 대상 네임스페이스를 전환할 수 있습니다. 로그아웃은 우측 상단 프로필 메뉴에서 수행합니다.


사용자 관리​

OPENMARU COP는 LLDAP(경량 사용자 디렉터리)과 Keycloak(SSO/OIDC)을 통해 사용자 계정을 통합 관리합니다.

LLDAP 접속 정보​

항목값
네임스페이스openmaru-sso
LDAP 포트3890
Web UI 포트17170
Web UI URLhttps://ldap.{sub_domain}.{domain}.{TLD}
Base DNdc={sub_domain},dc={domain},dc={TLD}

ℹ️ 권장: 사용자와 그룹은 COP Console 의 사용자 관리 메뉴에서 만드십시오. 콘솔로 만들면 권한 부여와 SSO 반영까지 한 번에 끝납니다. LLDAP 웹 UI 와 CLI(lldap-cli)는 콘솔을 쓸 수 없거나 스크립트로 자동화할 때만 씁니다.

방법 1) COP Console 로 사용자 만들기 (권장)​

사용자 관리 > 사용자 화면에서 [사용자 추가] 를 누릅니다. 이 버튼은 admins 그룹에 속한 사용자에게만 보입니다.

항목설명
아이디로그인 계정. 만든 뒤에는 바꿀 수 없습니다
이메일Kubernetes 사용자 이름으로 쓰이므로 만든 뒤 콘솔에서 바꿀 수 없습니다
표시 이름 · 성 · 이름목록과 상세에 표시됩니다
전화번호숫자와 하이픈(-)만 쓸 수 있습니다
그룹하나 이상 골라야 합니다. 기본값은 users 입니다
비밀번호8자 이상
COP Console 사용자 목록
COP Console 사용자 추가

그룹을 반드시 고르게 하는 이유는 그룹이 곧 권한이기 때문입니다. 그룹이 없는 계정은 로그인은 되지만 클러스터에서 아무 작업도 할 수 없습니다. 만든 사람은 계정을 줬다고 생각하고, 받은 사람은 빈 화면을 보게 됩니다.

그룹매핑되는 클러스터 권한
adminscluster-admin (전체 관리 권한)
userscop-cluster-users (편집 사용자)
viewerscop-cluster-viewers (조회 사용자)

만든 뒤에는 목록의 행을 눌러 상세 창에서 다음을 합니다.

  • [편집] — 표시 이름·성·이름·전화번호를 고칩니다
  • [그룹 매핑] — 소속 그룹을 바꿉니다
  • [비밀번호 변경] — 관리자가 비밀번호를 재설정합니다
  • 삭제 아이콘 — 계정을 지웁니다. 되돌릴 수 없으므로 확인 창이 먼저 열립니다

자기 자신, 마지막 관리자, 사용자 디렉터리를 관리하는 계정은 지울 수 없어 삭제 아이콘이 보이지 않습니다.

COP Console 사용자 상세

ℹ️ SSO 동기화가 필요 없습니다. 콘솔은 계정을 만들거나 소속을 바꾼 뒤 Keycloak 의 사용자 캐시를 스스로 비웁니다. 새 계정은 바로 로그인할 수 있고, 바꾼 권한은 그 사용자가 다음에 로그인할 때부터 적용됩니다. 반영이 늦어지는 경우에는 콘솔이 화면에 알립니다.

그룹 추가​

사용자 관리 > 사용자 그룹 화면의 [그룹 추가] 로 기본 세 그룹 외의 그룹을 만들 수 있습니다. 이름은 영소문자·숫자·하이픈만 2~32자로 쓰고, 권한(편집 사용자 또는 조회 사용자)을 반드시 하나 골라야 합니다.

COP Console 사용자 그룹 목록
COP Console 그룹 추가

콘솔이 정하는 것은 Kubernetes 권한까지입니다. 새 그룹이 ArgoCD 와 Nexus 에서도 권한을 가지려면 설치 담당자가 따로 설정해야 합니다.

자세한 화면 사용법은 설치 가이드의 「COP Console 사용 가이드」 장을 참고하십시오.

방법 2) LLDAP 웹 UI로 사용자 만들기​

콘솔에 접속할 수 없을 때 씁니다.

  1. LLDAP Web UI(https://ldap.{sub_domain}.{domain}.{TLD})에 관리자 계정으로 로그인합니다.

  2. 왼쪽 Users 메뉴에서 Create a user 를 누르고 아래를 입력합니다.

    칸값
    User name계정 아이디 (예: jdoe)
    Email로그인에 쓰는 주소 (예: jdoe@example.com)
    Display name목록에 보이는 이름 (선택)
    Password초기 비밀번호
  3. 왼쪽 Groups 메뉴에서 넣을 그룹(admins · users · viewers 중 하나)을 엽니다. 그룹 상세 화면 아래의 드롭다운에서 방금 만든 사용자를 고르고 Add to group 을 누릅니다.

⚠️ 이 방법은 SSO 동기화가 따로 필요합니다. 아래 「Keycloak 동기화」를 함께 수행하십시오.

방법 3) lldap-cli로 사용자 만들기​

스크립트로 자동화할 때 씁니다.

# 사용자 생성
lldap-cli user add --username jdoe --email jdoe@example.com

# 비밀번호 설정
lldap-cli user set-password --username jdoe

# 그룹에 사용자 추가
lldap-cli group add-member --group users --username jdoe

# 사용자 목록 조회
lldap-cli user list

# 사용자 상세 조회
lldap-cli user show --username jdoe

⚠️ 이 방법도 SSO 동기화가 따로 필요합니다.

그룹 관리 (CLI)​

# 그룹 생성
lldap-cli group add --name developers

# 그룹에 사용자 추가/제거
lldap-cli group add-user --group developers --username jdoe
lldap-cli group remove-user --group developers --username jdoe

# 그룹 목록/상세 조회
lldap-cli group list
lldap-cli group show --name developers

Keycloak 동기화​

방법 1(콘솔)로 만들었다면 이 절차는 필요 없습니다. 콘솔이 캐시를 스스로 비웁니다.

방법 2·3처럼 LLDAP 을 직접 고친 경우에는 Keycloak 이 이미 읽어 둔 값을 그대로 쓰기 때문에 바꾼 내용이 바로 반영되지 않을 수 있습니다. Keycloak Admin Console (https://sso.{sub_domain}.{domain}.{TLD})에서 대상 Realm(openmaru)을 선택한 뒤 User federation > ldap 으로 들어가 우측 상단 Action 드롭다운에서 Sync all users 를 실행하십시오. 사용자가 최초 로그인을 시도하면 자동으로 동기화되기도 합니다.

Keycloak User federation - LDAP Provider 목록
Keycloak LDAP Provider Action - Sync all users

⚠️ 주의: LLDAP 을 직접 고친 뒤 즉시 로그인이 안 될 경우 Keycloak Full Sync 를 수동으로 실행하십시오. 콘솔로 만든 계정에서는 이 증상이 발생하지 않습니다.


사용자 권한 부여​

OPENMARU COP는 Kubernetes RBAC(Role-Based Access Control)과 OIDC 그룹 클레임을 연동하여 권한을 부여합니다.

그룹 - 역할 매핑​

LLDAP 그룹OIDC 그룹 클레임ClusterRole
adminsoidc:adminscluster-admin
usersoidc:userscop-cluster-users (커스텀)
viewersoidc:viewerscop-cluster-viewers (커스텀)

커스텀 ClusterRoleBinding 예시​

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-admins-binding
subjects:
- kind: Group
name: "oidc:admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

네임스페이스 단위 권한 부여(Role/RoleBinding)​

특정 사용자/그룹에게 특정 네임스페이스에 대한 제한된 권한만 부여하려면 Role과 RoleBinding을 사용합니다.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-developer
namespace: my-app
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-developer-binding
namespace: my-app
subjects:
- kind: Group
name: "oidc:developers"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: app-developer
apiGroup: rbac.authorization.k8s.io

권한 확인​

⚠️ 주의: OPENMARU COP는 OIDC 사용자명에 oidc: 접두사(--oidc-username-prefix=oidc:)를 붙이도록 설정되어 있습니다. --as 옵션에 접두사 없이 사용자명만 입력하면 존재하지 않는 사용자로 조회되어 결과가 부정확합니다.

# 특정 사용자의 특정 네임스페이스 내 전체 권한 목록 확인
kubectl auth can-i --list --as=oidc:jdoe@example.com -n my-app

# 특정 동작 수행 가능 여부 확인
kubectl auth can-i create deployments --as=oidc:jdoe@example.com -n my-app

# OIDC 그룹에 연결된 ClusterRoleBinding 확인
kubectl get clusterrolebindings | grep oidc

Console에서 확인​

좌측 사용자 관리 메뉴에서 서비스 계정, 역할(Role/ClusterRole), 역할 바인딩(RoleBinding/ClusterRoleBinding)을 GUI로 조회할 수 있습니다. 역할 상세 화면에서는 Resources/Verbs/API Groups 규칙을, 역할 바인딩 상세 화면에서는 연결된 Subject(사용자/그룹) 목록을 확인할 수 있습니다.

역할 목록
역할 바인딩 목록

프로젝트(네임스페이스) 생성​

OPENMARU COP에서 프로젝트는 Kubernetes Namespace로 구현됩니다.

방법 1) OPENMARU COP Console을 통한 생성​

  1. 좌측 메뉴에서 클러스터 > 네임스페이스로 이동합니다.
  2. Create 버튼을 클릭합니다.
  3. Name, Labels, Annotations를 입력하고 생성합니다.
네임스페이스 목록
네임스페이스 상세

ℹ️ 생성된 리소스 간의 소유/연결 관계는 Console 맵 메뉴에서 시각적 그래프로 확인할 수 있습니다.

방법 2) CLI를 통한 생성​

kubectl create namespace my-app

리소스 쿼터 설정​

신규 프로젝트 생성 시 리소스 사용량 제한을 함께 설정하는 것을 권장합니다.

apiVersion: v1
kind: ResourceQuota
metadata:
name: my-app-quota
namespace: my-app
spec:
hard:
requests.cpu: "8"
requests.memory: 16Gi
limits.cpu: "16"
limits.memory: 32Gi
pods: "50"
services: "20"
secrets: "50"
configmaps: "50"
persistentvolumeclaims: "10"

CIS 보안 수준(Pod Security Standards) 지정​

네임스페이스 단위로 Pod 보안 수준을 지정할 수 있습니다.

apiVersion: v1
kind: Namespace
metadata:
name: my-app
labels:
pod-security.kubernetes.io/enforce: restricted
보안 수준설명
privileged제한 없음
baseline알려진 권한 상승 차단
restricted최고 수준의 보안 제약 적용(운영 환경 권장)

Jenkins를 통한 신규 애플리케이션 프로젝트 생성 (자동화)​

CI/CD 파이프라인과 함께 신규 애플리케이션을 배포하는 경우, Jenkins의 00-NEW-PROJECT Job을 통해 네임스페이스 생성부터 Deployment/Service/Ingress 생성, 후속 빌드/배포 Job 생성까지 자동화할 수 있습니다.

Job 파라미터설명
NAMESPACE생성할 네임스페이스명
DEPLOYMENT생성할 Deployment명
GIT_URL애플리케이션 소스 저장소 URL
BASE_IMAGE사용할 S2I 빌더 이미지
HOSTIngress에 연결할 도메인
NEXUS_URL아티팩트 저장소 URL

오토스케일링(HPA/CronHPA) 설정​

OPENMARU COP는 표준 Kubernetes HPA(Horizontal Pod Autoscaler)와 함께, 시간 예약 기반의 자체 오토스케일러인 CronHPA를 제공합니다.

표준 HPA 설정​

kubectl autoscale deployment my-app --cpu-percent=70 --min=2 --max=10 -n my-app

CronHPA 설정​

CronHPA는 6자리(초 단위 포함) 크론 표현식을 사용하며, 네임스페이스 openmaru-cronhpa에서 컨트롤러가 동작합니다.

apiVersion: autoscaling.openmaru.io/v1
kind: CronHPA
metadata:
name: my-app-cronhpa
namespace: my-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
jobs:
- name: "scale-up-business-hours"
schedule: "0 0 9 * * *"
targetSize: 5
- name: "scale-down-off-hours"
schedule: "0 0 20 * * *"
targetSize: 1
스펙 필드설명
scaleTargetRef스케일 대상 리소스(Deployment 등)
jobs[].schedule초 분 시 일 월 요일 순서의 6자리 크론 표현식
jobs[].targetSize지정 시각에 적용할 Replica 수
jobs[].runOnce1회성 실행 여부
excludeDates스케줄링을 제외할 날짜 목록

확인 및 문제 해결​

# CronHPA 리소스 조회
kubectl get cronhpa -A
kubectl describe cronhpa -n my-app my-app-cronhpa

# 컨트롤러 로그 확인
kubectl logs -n openmaru-cronhpa deployment/openmaru-cop-cronhpa

⚠️ 주의: 표준 HPA와 CronHPA를 동일 Deployment에 함께 적용할 경우 두 컨트롤러가 서로 다른 시점에 충돌할 수 있으므로, CronHPA의 targetSize는 HPA의 minReplicas를 조정하는 방식으로 연동하는 것을 권장합니다.

Console에서 확인​

워크로드 > HPA / CronHPA 메뉴에서 설정된 오토스케일러 목록과 현재 Replica 상태를 조회할 수 있습니다.

HPA 목록
CronHPA 목록

PVC(스토리지) 생성​

OPENMARU COP는 두 가지 StorageClass를 기본 제공합니다.

StorageClass접근 모드볼륨 확장용도
nfs-client (기본)RWO, RWX지원여러 Pod 간 공유가 필요한 데이터
local-pathRWO미지원단일 Pod 전용, 고성능이 필요한 데이터

PVC 생성 예시​

# NFS 기반 공유 볼륨(RWX)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-shared-data
namespace: my-app
spec:
accessModes: ["ReadWriteMany"]
storageClassName: nfs-client
resources:
requests:
storage: 10Gi
---
# Local Path 기반 전용 볼륨(RWO)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-local-data
namespace: my-app
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: local-path
resources:
requests:
storage: 20Gi

볼륨 확장​

kubectl patch pvc my-app-shared-data -n my-app \
-p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

ℹ️ 참고: local-path StorageClass는 볼륨 확장을 지원하지 않으므로, 확장이 예상되는 데이터는 nfs-client를 사용하십시오.

Console에서 확인​

스토리지 > 영구 볼륨 클레임(PVC) 메뉴에서 상태(Bound/Pending/Lost)를 색상으로 확인할 수 있습니다.

PVC 목록

애플리케이션 빌드/배포​

ℹ️ 참고: 소스코드 빌드는 S2I(Source-to-Image) CLI로 수행하며, 이를 Jenkins Job으로 자동화합니다. 상세 절차는 COP 빌드/배포 가이드도 함께 참고하십시오.

S2I(Source-to-Image) 빌드​

s2i build <소스_위치> <빌더_이미지> <결과_이미지>

# 예시: Maven 프로젝트를 Nexus Mirror를 사용하여 빌드(Tomcat9 + JDK17)
s2i build ${GIT_URL} registry.{sub_domain}.{domain}.{TLD}:8443/images/tomcat9-jdk17-ubi8-s2i-openmaru:9.0 \
registry.{sub_domain}.{domain}.{TLD}:8443/apps/my-app:1.0.0 \
-e MAVEN_MIRROR_URL=${NEXUS_URL} \
-e CPU_LIMIT=2000m \
-e MEMORY_LIMIT=1Gi

지원 빌더 이미지: OpenJDK 8/11/17/21, Tomcat9+JDK 8/11/17, Node.js 18/20/22, Python 3.9/3.11/3.12, PHP 8.2/8.3, Ruby 3.3, Nginx 1.26, HTTPD 2.4, Varnish 6, MariaDB/MySQL/PostgreSQL/Redis 등. 전체 목록은 4.2. 자주 사용하는 명령어 > Private Registry 관리 참고.

Jenkins Job / Bastion 스크립트를 통한 빌드-배포 자동화​

Bastion 서버의 애플리케이션 작업 디렉터리에서 제공되는 스크립트를 실행하면 S2I 빌드부터 배포까지 한 번에 처리할 수 있습니다. 동일한 동작이 Jenkins Job(<namespace>-<app>-build → <namespace>-<app>-deploy)으로도 자동화되어 있습니다.

# Bastion에서 직접 실행하는 경우
cd /data/workspaces/apps/<namespace>/<app>
git -C git pull origin main
./build-app.sh # S2I 빌드(소스 → 컨테이너 이미지)
./build-push.sh # Harbor Registry로 이미지 Push
./build-deploy.sh # Deployment 이미지 업데이트 및 롤아웃
# 실행 로그 예시
>> Image Tag : main-4375493
>> Start build : my-app in my-app...
[INFO] BUILD SUCCESS
>> Image push completed: registry.{sub_domain}.{domain}.{TLD}:8443/apps/my-app:main-4375493
>> Start deploy : my-app in my-app
deployment.apps/my-app image updated
>> Waiting for rollout to complete...
deployment "my-app" successfully rolled out

Jenkins에서 실행할 경우 Jenkins > <namespace>-<app>-build(S2I 빌드+Push) Job을 먼저 실행한 뒤 <namespace>-<app>-deploy Job으로 배포합니다. Job 체계는 아래 CI/CD 파이프라인을 통한 배포 절을 참고하십시오.

Console에서 확인​

배포 완료 후 워크로드 > Deployment 메뉴에서 롤아웃 상태와 리소스 사용량을 확인할 수 있습니다. 상세 화면에서 CPU/Memory 요청·제한 값을 직접 조정하거나(리소스 제한 편집), 로그·터미널을 분할 뷰로 함께 열어볼 수 있습니다.

Deployment 목록
Deployment 상세
컨테이너 리소스 제한 편집

Helm을 통한 배포​

helm repo add openmaru http://chartmuseum.{sub_domain}.{domain}.{TLD}:8181
helm repo update

helm install my-app openmaru/my-app-chart \
--create-namespace -n my-app \
-f values.yaml

helm upgrade my-app openmaru/my-app-chart -n my-app -f values.yaml
helm rollback my-app 1 -n my-app
helm list -A
helm status my-app -n my-app
helm history my-app -n my-app
helm uninstall my-app -n my-app

CI/CD 파이프라인을 통한 배포 (GitLab → Jenkins → Harbor/Nexus → ArgoCD)​

OPENMARU COP의 표준 CI/CD 파이프라인은 다음과 같은 Jenkins Job 번호 체계를 따릅니다.

Job역할
00-NEW-PROJECT신규 프로젝트/네임스페이스 생성
10-*-helm-installHelm Chart 최초 설치
20-*-buildS2I 빌드 및 이미지 Push
30-*-deploy이미지 배포
40-*-rollback이전 버전 롤백
50-*-blue-greenBlue-Green 배포 전환
60-*-hpa오토스케일링 설정
70-*-argocd-deployArgoCD GitOps 배포
99-*-helm-uninstall리소스 삭제

Blue-Green 배포 전환 예시:

kubectl patch service my-app -n my-app \
-p '{"spec":{"selector":{"deployment":"green"}}}'

ArgoCD를 통한 GitOps 배포 확인:

argocd app list --grpc-web
argocd app get my-app --grpc-web
argocd app sync my-app --grpc-web
argocd app history my-app --grpc-web

이미지 태그 업데이트(수동 배포)​

kubectl set image deployment/my-app my-app=registry.{sub_domain}.{domain}.{TLD}:8443/apps/my-app:v1.1 -n my-app
kubectl rollout status deployment/my-app -n my-app

보안 컨텍스트(CIS 프로파일 준수)​

운영 환경(CIS restricted 네임스페이스) 배포 시 다음 SecurityContext 항목을 반드시 준수해야 합니다. runAsUser 값은 S2I 빌더 이미지 종류에 따라 다르므로 임의로 고정하지 말고 아래 표를 따라야 합니다. 값이 실제 이미지 UID와 다르면 파일 쓰기 권한 오류 등으로 Pod가 기동에 실패할 수 있습니다.

S2I 이미지 계열runAsUser비고
Java/Tomcat(OpenJDK, Tomcat9)185SCL OpenJDK/Tomcat 표준 UID
기타(Node.js, Python, PHP, Ruby, Nginx, HTTPD)1001SCL(Software Collections) 표준 UID
# 예시: Java/Tomcat 기반 S2I 이미지
securityContext:
runAsNonRoot: true
runAsUser: 185 # Java/Tomcat 계열. 그 외 런타임은 1001
runAsGroup: 0 # root 그룹(S2I 이미지 표준)
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]

Service/NodePort 설정​

Service 타입사용 예
ClusterIP클러스터 내부 통신 전용(기본값)
NodePort클러스터 외부에서 노드 IP + 포트로 직접 접근
LoadBalancer외부 로드밸런서 연동(온프레미스 환경에서는 제한적으로 사용)
apiVersion: v1
kind: Service
metadata:
name: my-app-nodeport
namespace: my-app
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 8080
targetPort: 8080
nodePort: 30080

ℹ️ 참고: NodePort 사용 가능 범위는 30000-32767입니다. 운영 환경에서는 가급적 NodePort보다 Ingress를 사용하는 것을 권장합니다.

Console에서 확인​

네트워크 > 서비스 메뉴에서 Service 목록과 연결된 Endpoint(Pod) 상태를 조회할 수 있습니다.

서비스 목록

NetworkPolicy 설정​

기본적으로 모든 Pod 간 통신이 허용되어 있으므로, 운영 환경에서는 네임스페이스 단위로 NetworkPolicy를 적용하여 트래픽을 제한하는 것을 권장합니다.

# 기본적으로 모든 인바운드/아웃바운드 트래픽 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: my-app
spec:
podSelector: {}
policyTypes: ["Ingress", "Egress"]
---
# 동일 네임스페이스 내 통신만 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: my-app
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
---
# DNS(53번 포트) 통신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: my-app
spec:
podSelector: {}
egress:
- to: []
ports:
- protocol: UDP
port: 53

ℹ️ 참고: OPENMARU COP는 프로젝트/노드 단위 고정 Egress IP 기능을 기본 제공하지 않습니다. 특정 프로젝트의 외부 통신에 고정 출발지 IP가 필요한 경우, 별도의 Egress Gateway(NAT 전용 노드) 구성을 통해 아키텍처 수준에서 지원해야 하며, 도입이 필요할 경우 기술 지원팀과 협의하시기 바랍니다.

Console에서 확인​

네트워크 > 네트워크 정책 메뉴에서 적용된 NetworkPolicy의 from/to/ports 규칙을 시각적으로 확인할 수 있습니다.

네트워크 정책 목록

Helm Chart(템플릿) 수정​

OPENMARU COP는 Helm Chart를 애플리케이션 배포 템플릿으로 사용합니다.

# 새 Chart 스캐폴딩 생성
helm create my-app-chart

# Chart 문법 검증
helm lint my-app-chart

# Chart 패키징
helm package my-app-chart

# 사내 ChartMuseum(openmaru repo)에 업로드
helm cm-push my-app-chart-1.0.0.tgz openmaru

values.yaml을 수정한 뒤에는 helm upgrade로 반영합니다.

helm upgrade my-app openmaru/my-app-chart -n my-app -f values.yaml --dry-run
helm upgrade my-app openmaru/my-app-chart -n my-app -f values.yaml

Console에서 확인​

Helm 차트 > 릴리스 메뉴에서 설치된 Release 목록과 상세(Summary/Values/Manifests/Notes/Resources/History 탭)를 확인하고, 업그레이드/롤백/삭제를 GUI로 수행할 수 있습니다. 저장소 메뉴에서는 ChartMuseum 등 Helm Repository를 등록/관리합니다.

Helm 릴리스 목록

Worker/Infra 노드 증설​

  1. 신규 노드 호스트명 설정

    hostnamectl hostname cop-worker-3
  2. inventory.ini와 env.yaml에 신규 노드 정보 추가

  3. Bastion에서 신규 노드로 SSH 키 배포

    ./run-sshkeycopy.sh <신규_노드_IP>
  4. 신규 노드 기본 설정 적용

    ./run-playbook.sh openmaru-cop-system-setup.yaml --limit cop-worker-3
    ./run-playbook.sh openmaru-cop-system-dns.yaml --limit cop-worker-3
  5. (GPU 노드인 경우) GPU 드라이버 사전 설치

    ./120-gpu-preinstall.sh
    ./140-gpu-install.sh
  6. RKE2 설치 및 클러스터 조인

    ./run-playbook.sh openmaru-cop-rke2-install.yaml
    ./run-playbook.sh openmaru-cop-rke2-post-install.yaml --limit cop-worker-3
    ./run-playbook.sh openmaru-cop-rke2-private-registry.yaml --limit cop-worker-3
  7. (GPU 노드인 경우) GPU Operator 설치

    ./450-gpu-operator-install.sh

노드 제거 절차​

kubectl cordon cop-worker-3
kubectl drain cop-worker-3 --ignore-daemonsets --delete-emptydir-data

# 대상 노드에서 실행
systemctl stop rke2-agent
systemctl disable rke2-agent

# Bastion 또는 Master 노드에서 실행
kubectl delete node cop-worker-3

# 대상 노드에서 실행 (RKE2 완전 제거)
rke2-uninstall.sh

⚠️ 주의: 노드 제거 전 반드시 Cordon/Drain을 수행하여 해당 노드의 워크로드가 다른 정상 노드로 안전하게 이전(재스케줄링)되도록 해야 합니다.

Console에서 확인​

클러스터 > 노드 메뉴에서 노드 목록/상세(사양, 할당 리소스, Conditions)를 조회할 수 있으며, Cordon/Uncordon과 Drain을 GUI 버튼으로 수행할 수 있습니다. Node Shell 기능을 사용하면 디버그용 특권 Pod를 통해 브라우저에서 노드에 직접 터미널로 접속할 수 있습니다.

노드 목록
노드 상세

Private Registry 관리​

OPENMARU COP는 컨테이너 이미지 저장소로 Harbor를, 빌드 아티팩트 저장소로 Nexus를 사용합니다.

Harbor 로그인 및 이미지 Push/Pull​

docker login registry.{sub_domain}.{domain}.{TLD}:8443 -u admin -p ${HARBOR_PASSWORD}

docker tag my-app:v1.0 registry.{sub_domain}.{domain}.{TLD}:8443/apps/my-app:v1.0
docker push registry.{sub_domain}.{domain}.{TLD}:8443/apps/my-app:v1.0
docker pull registry.{sub_domain}.{domain}.{TLD}:8443/apps/my-app:v1.0

Harbor 프로젝트 구조​

프로젝트공개 여부용도
libraryPublic공용 베이스 이미지
imagesPublicS2I 빌더 이미지
appsPrivate고객사 애플리케이션 이미지

ImagePullSecret 생성​

kubectl create secret docker-registry harbor-secret \
--docker-server=registry.{sub_domain}.{domain}.{TLD}:8443 \
--docker-username=admin \
--docker-password=${HARBOR_PASSWORD} \
-n my-app

이미지 취약점 스캔​

Harbor는 내장된 Trivy 스캐너로 Push 시 자동 취약점 스캔을 지원합니다. 프로젝트 설정에서 Automatically scan on push, **Vulnerability severity threshold(Critical)**를 지정할 수 있습니다.

가비지 컬렉션​

Harbor 관리 콘솔의 Administration > Garbage Collection에서 미사용(Untagged) 이미지의 정기 삭제 스케줄(예: 매일)을 설정할 수 있습니다.

ℹ️ 참고: OPENMARU COP Console의 도구 메뉴에서 Harbor를 비롯한 GitLab/Jenkins/ArgoCD/Nexus 등 통합 DevOps 도구로 바로 이동할 수 있습니다.


Worker 노드 Graceful 종료​

계획된 점검, OS 패치 등으로 특정 Worker 노드를 안전하게 종료해야 할 때는 다음 3단계 절차를 따릅니다.

단계명령효과
1. Cordonkubectl cordon <node-name>신규 Pod 스케줄링 차단(기존 Pod는 유지)
2. Drainkubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data기존 Pod를 다른 노드로 안전하게 재배치
3. Uncordon(작업 완료 후)kubectl uncordon <node-name>스케줄링 재개
# 1단계: 스케줄링 차단
kubectl cordon cop-worker-2

# 2단계: 워크로드 안전 이전(최대 300초 대기, 필요 시 --force)
kubectl drain cop-worker-2 --ignore-daemonsets --delete-emptydir-data --timeout=300s

# 점검 작업 수행 (OS 패치, 재부팅 등)

# 3단계: 점검 완료 후 스케줄링 재개
kubectl uncordon cop-worker-2
시나리오권장 절차
OS 패치 적용Cordon → Drain → 패치 → 재시작 → Uncordon
긴급 점검Cordon → Drain → 점검 → Uncordon
하드웨어 교체Cordon → Drain → 노드 제거(Worker/Infra 노드 증설 참고)
부하 분산 재조정Cordon(신규 스케줄링만 차단)

여러 노드를 순차적으로 점검해야 하는 경우, Ansible 자동화 플레이북을 사용하면 Cordon/Drain을 포함한 롤링 재시작을 일괄 수행할 수 있습니다.

./run-playbook.sh openmaru-cop-rke2-rolling-restart.yaml \
-e "target_group=workers" \
-e "cordon_nodes=true" \
-e "drain_nodes=true"

GPU 자원 운영​

GPU 노드가 있는 환경에서 GPU 할당 상태를 확인하고, 여러 워크로드가 한 장을 나누어 쓰도록 운영하는 절차입니다.

GPU 할당 방식​

COP 1.0.3부터 DRA(Dynamic Resource Allocation) 가 기본 방식입니다. 워크로드가 nvidia.com/gpu 확장 리소스를 직접 요청하는 대신 ResourceClaim 으로 장치를 요청합니다.

방식요청광고 주체프로젝트 간 공유
DRA (기본)ResourceClaimDRA 드라이버가능
타임슬라이싱nvidia.com/gpudevice-plugin노드 단위로 동작

주의: 두 방식을 함께 쓰지 마십시오. 같은 GPU를 두 경로가 각각 배정하면 서로의 점유를 알지 못해 실제 사용량이 물리 용량을 넘어섭니다.

현재 상태 확인​

# 노드별 물리 GPU 목록 — 드라이버가 발행합니다
kubectl get resourceslices

# 각 장치의 상세 (공유 가능 여부·용량)
kubectl get resourceslices -o custom-columns=\
NODE:.spec.nodeName,DRIVER:.spec.driver,\
SHARED:.spec.devices[*].allowMultipleAllocations

# 어느 워크로드가 GPU를 잡고 있는지
kubectl get resourceclaims -A

참고: 노드 화면의 GPU 개수와 resourceslices 의 장치 수는 다를 수 있습니다. 타임슬라이싱을 켜면 노드에는 물리 장수 × replicas 가 표시되지만 resourceslices 에는 물리 장치 수만 나옵니다. 실제 장비 수를 확인할 때는 후자를 봅니다.

GPU 사용량 확인​

# GPU를 사용 중인 Pod 안에서 확인합니다
kubectl exec -n <네임스페이스> <pod> -- \
nvidia-smi --query-gpu=name,memory.total,memory.used,memory.free --format=csv

주의: GPU 메모리는 스케줄러가 계산만 할 뿐 물리적으로 나뉘지 않습니다. 한 워크로드가 메모리를 모두 사용하면 같은 GPU를 쓰는 다른 워크로드가 종료됩니다. 함께 실행할 워크로드의 메모리 합계가 물리 용량을 넘지 않도록 관리해야 합니다.

여러 워크로드가 GPU 한 장을 나누어 쓰기​

ResourceClaim을 미리 만들고 여러 Pod가 그 이름을 참조하는 방식입니다.

apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: shared-gpu
namespace: <네임스페이스>
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.nvidia.com
allocationMode: ExactCount
count: 1

Pod 쪽에서는 이 클레임을 이름으로 참조합니다.

spec:
resourceClaims:
- name: gpu
resourceClaimName: shared-gpu # 템플릿이 아니라 이름 참조
containers:
- name: app
resources:
claims:
- name: gpu

주의: resourceClaimTemplateName 을 쓰면 Pod마다 클레임이 새로 생겨 장치를 각각 점유합니다. GPU가 한 장인 노드에서는 두 번째 Pod가 Pending 이 됩니다. 공유가 목적이면 반드시 이름 참조를 사용합니다.

클레임이 어느 Pod에 배정됐는지는 다음으로 확인합니다.

kubectl get resourceclaim -n <네임스페이스> shared-gpu \
-o jsonpath='{range .status.reservedFor[*]}{.name}{"\n"}{end}'

프로젝트 간 GPU 공유​

장치 분할 설정이 켜져 있으면 서로 다른 프로젝트(네임스페이스)의 워크로드도 같은 GPU를 사용할 수 있습니다. ResourceClaim은 프로젝트 단위 오브젝트라 각각 따로 만들지만, 같은 물리 장치에 배정됩니다.

# 장치가 다중 할당을 허용하는지 — True 여야 합니다
kubectl get resourceslices \
-o jsonpath='{range .items[*]}{.spec.nodeName}{" "}{.spec.devices[*].allowMultipleAllocations}{"\n"}{end}'

# 값이 비어 있으면 클러스터 기능 게이트를 확인합니다 (1 이면 켜진 상태)
kubectl get --raw /metrics | grep DRAConsumableCapacity

GPU 노드 점검​

GPU 노드도 Worker 노드 Graceful 종료와 같은 절차를 따릅니다. 다만 다음을 먼저 확인합니다.

확인이유
진행 중인 GPU 작업학습·추론 작업은 중간에 끊기면 처음부터 다시 해야 할 수 있습니다
다른 GPU 노드의 여유Drain 후 재배치될 자리가 있어야 합니다. 없으면 Pending 으로 남습니다
# 해당 노드에서 GPU를 쓰는 Pod 확인
kubectl get pods -A --field-selector spec.nodeName=<노드> -o json \
| jq -r '.items[] | select(.spec.resourceClaims != null) | "\(.metadata.namespace)/\(.metadata.name)"'

GPU 드라이버 버전 상향​

드라이버는 이미 설치되어 있으면 건너뜁니다. 버전만 올려서는 노드가 갱신되지 않으므로 제거 후 재설치해야 합니다. 절차는 설치 가이드의 GPU 환경 설정 장을 참조하십시오.