본문으로 건너뛰기

kro로 리소스 생성하기

이제 kro가 설치되었으므로, kro WebApplication ResourceGraphDefinition을 사용하여 Carts 컴포넌트를 배포하겠습니다. 먼저 재사용 가능한 WebApplication API를 정의하는 ResourceGraphDefinition 템플릿을 살펴보겠습니다:

전체 RGD 매니페스트 펼치기
~/environment/eks-workshop/modules/automation/controlplanes/kro/rgds/webapp-rgd.yaml
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
name: web-application
spec:
schema:
apiVersion: v1alpha1
kind: WebApplication
spec:
appName: string | required=true description="Web Application Name"
replicas: integer | default=1 minimum=1 maximum=100
image: string | default=nginx
port: integer | default=8080
healthcheck:
readinessPath: string | default="/actuator/health/readiness"
readinessPort: integer | default=8080
livenessPath: string | default="/actuator/health/liveness"
livenessPort: integer | default=8080

service:
enabled: boolean | default=true
iamRole: string | default=""

env: map[string]string | default={}

ingress:
enabled: boolean | default=false
path: string | default="/"
healthcheckPath: string | default="/health"
groupname: string | default="eks-workshop"

resources:
- id: serviceAccount
template:
apiVersion: v1
kind: ServiceAccount
metadata:
name: ${schema.spec.appName}
namespace: ${schema.spec.appName}

- id: configMap
template:
apiVersion: v1
kind: ConfigMap
metadata:
name: ${schema.spec.appName}
namespace: ${schema.spec.appName}
data: ${schema.spec.env}

- id: deployment
template:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${schema.spec.appName}
namespace: ${schema.spec.appName}
labels:
app.kubernetes.io/created-by: "eks-workshop"
app.kubernetes.io/type: app
spec:
replicas: ${schema.spec.replicas}
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: ${schema.spec.appName}
app.kubernetes.io/instance: ${schema.spec.appName}
app.kubernetes.io/component: service
template:
metadata:
annotations:
prometheus.io/path: /actuator/prometheus
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
labels:
app.kubernetes.io/name: ${schema.spec.appName}
app.kubernetes.io/instance: ${schema.spec.appName}
app.kubernetes.io/component: service
app.kubernetes.io/created-by: eks-workshop
spec:
serviceAccountName: ${schema.spec.appName}
securityContext:
fsGroup: 1000
containers:
- name: ${schema.spec.appName}
env:
- name: JAVA_OPTS
value: -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom
envFrom:
- configMapRef:
name: ${schema.spec.appName}
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
image: ${schema.spec.image}
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: ${schema.spec.port}
protocol: TCP
readinessProbe:
httpGet:
path: ${schema.spec.healthcheck.readinessPath}
port: ${schema.spec.healthcheck.readinessPort}
initialDelaySeconds: 15
periodSeconds: 3
livenessProbe:
httpGet:
path: ${schema.spec.healthcheck.livenessPath}
port: ${schema.spec.healthcheck.livenessPort}
initialDelaySeconds: 45
periodSeconds: 3
resources:
limits:
memory: 1Gi
requests:
cpu: 250m
memory: 1Gi
volumeMounts:
- mountPath: /tmp
name: tmp-volume
volumes:
- name: tmp-volume
emptyDir:
medium: Memory

- id: service
template:
apiVersion: v1
kind: Service
metadata:
name: ${schema.spec.appName}
namespace: ${schema.spec.appName}
labels:
app.kubernetes.io/created-by: eks-workshop
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: ${schema.spec.appName}
app.kubernetes.io/instance: ${schema.spec.appName}
app.kubernetes.io/component: service
includeWhen:
- ${schema.spec.service.enabled}

- id: ingress
template:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${schema.spec.appName}
namespace: ${schema.spec.appName}
labels:
app.kubernetes.io/created-by: eks-workshop
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/healthcheck-path: ${schema.spec.ingress.healthcheckPath}
alb.ingress.kubernetes.io/group.name: ${schema.spec.ingress.groupname}
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: ${schema.spec.ingress.path}
pathType: Prefix
backend:
service:
name: ${schema.spec.appName}
port:
number: 80
includeWhen:
- ${schema.spec.ingress.enabled}

이 ResourceGraphDefinition은 다음을 배포하는 복잡성을 추상화하는 커스텀 WebApplication API를 생성합니다:

  • ServiceAccount
  • ConfigMap
  • Deployment
  • Service
  • Ingress (선택 사항)

스키마는 다음과 같이 애플리케이션 이미지, 레플리카 수, 환경 변수, 헬스 체크 구성과 같은 주요 매개변수의 커스터마이징을 허용하면서 합리적인 기본값을 제공합니다:

    spec:
appName: string | required=true description="Web Application Name"
replicas: integer | default=1 minimum=1 maximum=100
image: string | default=nginx
port: integer | default=8080
healthcheck:
readinessPath: string | default="/actuator/health/readiness"
readinessPort: integer | default=8080
livenessPath: string | default="/actuator/health/liveness"
livenessPort: integer | default=8080

service:
enabled: boolean | default=true
iamRole: string | default=""

env: map[string]string | default={}

ingress:
enabled: boolean | default=false
path: string | default="/"
healthcheckPath: string | default="/health"
groupname: string | default="eks-workshop"
정보

스키마가 기본값과 타입 정의를 사용하여 기본 Kubernetes 복잡성을 숨기는 개발자 친화적인 API를 생성하는 방법을 주목하세요.

이 WebApplication ResourceGraphDefinition을 사용하여 인메모리 데이터베이스를 사용하는 Carts 컴포넌트의 인스턴스를 생성하겠습니다. 이를 위해 먼저 기존 carts 배포를 정리하겠습니다:

~$kubectl delete all --all -n carts
pod "carts-68d496fff8-9lcpc" deleted
pod "carts-dynamodb-995f7768c-wtsbr" deleted
service "carts" deleted
service "carts-dynamodb" deleted
deployment.apps "carts" deleted
deployment.apps "carts-dynamodb" deleted

다음으로 WebApplication API를 등록하기 위해 ResourceGraphDefinition을 적용합니다:

~$kubectl apply -f ~/environment/eks-workshop/modules/automation/controlplanes/kro/rgds/webapp-rgd.yaml
resourcegraphdefinition.kro.run/web-application created

이것은 WebApplication API를 등록합니다. kro는 RGD 스키마를 기반으로 Custom Resource Definition (CRD)을 자동으로 생성합니다. CRD를 확인해보세요:

~$kubectl get crd webapplications.kro.run
NAME                       CREATED AT
webapplications.kro.run    2024-01-15T10:30:00Z

이제 WebApplication API를 사용하여 Carts 컴포넌트의 인스턴스를 생성할 carts.yaml 파일을 살펴보겠습니다:

~/environment/eks-workshop/modules/automation/controlplanes/kro/app/carts.yaml
apiVersion: kro.run/v1alpha1
kind: WebApplication
metadata:
name: carts
namespace: carts
spec:
# Basic types
appName: carts
replicas: 1
image: "public.ecr.aws/aws-containers/retail-store-sample-cart:1.2.1"
port: 8080
env:
RETAIL_CART_PERSISTENCE_PROVIDER: "in-memory"
RETAIL_CART_PERSISTENCE_DYNAMODB_TABLE_NAME: "Items"
RETAIL_CART_PERSISTENCE_DYNAMODB_CREATE_TABLE: "false"

service:
enabled: true
A

RGD에서 생성한 커스텀 WebApplication API를 사용합니다

B

carts 네임스페이스에 carts라는 이름의 리소스를 생성합니다

C

리소스 이름 지정을 위한 애플리케이션 이름을 지정합니다

D

단일 레플리카를 설정합니다

E

소매점 카트 서비스 컨테이너 이미지를 사용합니다

F

8080 포트에서 애플리케이션을 노출합니다

G

인메모리 지속성 모드를 위한 환경 변수를 구성합니다

H

Kubernetes Service 리소스를 활성화합니다

애플리케이션을 배포해보겠습니다:

~$kubectl apply -f ~/environment/eks-workshop/modules/automation/controlplanes/kro/app/carts.yaml
webapplication.kro.run/carts created

kro는 이 커스텀 리소스를 처리하고 모든 기본 Kubernetes 리소스를 생성합니다. 커스텀 리소스가 생성되었는지 확인해보겠습니다:

~$kubectl get webapplication -n carts
NAME    STATE         SYNCED   AGE
carts   IN_PROGRESS   False    16s

이제 인스턴스가 "synced" 상태에 도달할 때까지 기다릴 수 있습니다:

~$kubectl wait -o yaml webapplication/carts -n carts \
--for=condition=InstanceSynced=True --timeout=120s

다음으로, RGD의 컴포넌트가 실행 중인지 확인합니다:

~$kubectl get all -n carts
NAME                         READY   STATUS    RESTARTS   AGE
pod/carts-7d58cfb7c9-xyz12   1/1     Running   0          30s
 
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/carts   ClusterIP   172.20.123.45   <none>        80/TCP    30s
 
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/carts   1/1     1            1           30s
 
NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/carts-7d58cfb7c9   1         1         1       30s

kro는 Carts 컴포넌트에 필요한 모든 Kubernetes 리소스의 배포를 단일 단위로 성공적으로 오케스트레이션했습니다. kro를 사용함으로써, 일반적으로 여러 YAML 파일을 적용해야 하는 작업을 단일 선언적 API 호출로 변환했습니다. 이것은 복잡한 리소스 오케스트레이션을 단순화하는 kro의 강력함을 보여줍니다.

다음 섹션에서는 carts에서 현재 사용 중인 인메모리 데이터베이스를 Amazon DynamoDB 테이블로 교체하겠습니다.