학습일지/K-Digital Traing

[KDT] AIaaS 마스터클래스 3주차 - 클라우드 어플리케이션 배포 실습

tierr 2025. 4. 10. 17:37

실습용 환경 설정

오늘은 3-tier(front,back,db) app을 클라우드에 배포하는 것이 목표이다. 오늘의 실습을 위해 강사님께서 카카오클라우드 클러스터를 만들어주셨다. 하나의 클러스터 안에서 리소스가 논리적으로 격리된 공간을 네임스페이스라고 부른다.  나는 그 클러스터 안에 나의 네임스페이스를 만들고 작업할 필요가 있다.

 

💡 쉽게 비유하면?

개념 비유
클러스터 아파트 단지 전체
네임스페이스 각 동(건물)
Pod/Service 등 각 세대의 집들

카카오클라우드에 만들어진 클러스터에 접속하고, 나만의 작업공간인 네임스페이스를 만들어보자.

 

1. kubeconfig-sfacspace.yaml 파일 바탕화면에 배치

더보기
# kubeconfig-sfacspace.yaml
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM2akNDQWRLZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJMU1EUXdPVEV4TVRneE5Gb1hEVE0xTURRd056RXhNak14TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS0VPCjZ1RmdULzU2VU1YSlR1cGsrcDNYL1A5ZHNjQjExTm4zdkd5b28xTmRuSXZHNkxtVzJGRWFIZi9BYk5Ya1FzcWkKbHorM2xWQnhDMHRjczVVc0xERXRhMEw5SzFRTzFqQ3VlZEJIS1lkVGFiWmkyMzlVRGNBZVNkazlsUGsvcm5CMQpIZjhqYUV6UEFlSEFuVkZHeXZETFM5RlgvcGJBK1I2ajhma0N2aWNJdHZUV3ppajY1VDVtK0FEWi91RWlyWi9SCkQ1emY2QWF6SXR6dGYxcHVVOHJVeFVPMmo5U2ZFekxrY2NmMW1GQ3I4M1dveGFpTEkzaFFRYXlzY2JKL3paemIKRkhGd1F5UFNwY0YyeW1DVExMd1J1cUZtdi8vSXNWT2RBZ2VnT1VMeTg0VHlqakk0bnIvMkFmOHdORlE5U0RiYQpma2RFNlg4d1lLMXltb1RPOVg4Q0F3RUFBYU5GTUVNd0RnWURWUjBQQVFIL0JBUURBZ0trTUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdIUVlEVlIwT0JCWUVGQlpvM2ZVMGtEQlYyYm9lTWw1WmpEdUs4RC9rTUEwR0NTcUcKU0liM0RRRUJDd1VBQTRJQkFRQnlZZHZiKzZHeTU2aDdXQ2ZWVEN2bXJsdTNiUCtSbU1NbXdESFc3anNVSCtHSgppSWJHeGVSZGdZOHVseHB4UU0rWlY4aW15cnhrMUN4UUJaVG83MlF1K3dGZWZPaU9meVBNN0IvU1k4SytqWmlRClJYYW9uVXAxZS95RkFXbDVqYi91WFBwejV2YTBLOWFVMVJ2RUdKa3Y3WlBSUWtudG1xL0ZSTUNxWU5kK0lEYjQKeDBzMWtqTU8wZWJnQWM3enF2czJFZmZUa2FQcnVoaUpSbm0rN3hPV2dUaDNNNjhQOWxsTG5UVGVCRTRhUnJ3dwpEczNUT3RUVFRQdVF2R0IrQlRSY0ZMNG11NkJ2cStDL0oxM1IyMW9URG84endzb3J0L0M4UlY0T1ZHdmpWcjF5CmptZTdlQ0g3MVpRV2xtL2dFbDVFTG0zbU05WkY3MTh1cXJMVXF1c3IKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    server: https://58d6c44b-be0b-4635-9e76-700a077a5f06-public.ke.kr-central-2.kakaocloud.com
  name: sfacspace
contexts:
- context:
    cluster: sfacspace
    user: sfacspace-admin
  name: sfacspace-admin@sfacspace
current-context: sfacspace-admin@sfacspace
kind: Config
preferences: {}
users:
    - name: sfacspace-admin
      user:
        exec:
          apiVersion: client.authentication.k8s.io/v1beta1
          args: null
          command: kic-iam-auth
          env:
          - name: "OS_AUTH_URL"
            value: "https://iam.kakaocloud.com/identity/v3"
          - name: "OS_AUTH_TYPE"
            value: "v3applicationcredential"
          - name: "OS_APPLICATION_CREDENTIAL_ID"
            value: "fd9f2ff1df4c449a99797dc7e873f770"
          - name: "OS_APPLICATION_CREDENTIAL_SECRET"
            value: "955cb9b203f07a03b28612b1e823349328ed8fdb8bcc4dcc22a16376d239c4da43aabf"
          - name: "OS_REGION_NAME"
            value: "kr-central-2"

 

2. 본인 환경에 맞는 카카오클라우드 인증 클라이언트(kic-iam-auth)를 다운로드 (가이드라인 URL 참조)

가이드라인: https://docs.kakaocloud.com/service/container-pack/k8se/how-to-guides/k8se-kubectl

 

kubectl 제어 설정 | 카카오클라우드

Kubernetes 커맨드 라인 도구인 kubectl의 설치 및 제어 방법을 설명합니다.

docs.kakaocloud.com

 

3. 다음 커맨드를 입력하여 kubectl 연결 및 네임스페이스 만들기

 

# 1. 명령 프롬프트(cmd)를 관리자 권한으로 실행합니다.
# 2. 바탕화면으로 이동합니다:
cd %USERPROFILE%\Desktop

# 3. 현재 경로 확인:
echo %CD%

# 4. 경로에 kic-iam-auth 추가:
set PATH=%PATH%;%CD%

# 5. kubeconfig 파일 경로 설정:
set KUBECONFIG=%CD%\kubeconfig-sfacspace.yaml

# 6. 설정이 제대로 적용되었는지 확인:
echo %PATH%
echo %KUBECONFIG%

# 7. kubectl 명령 테스트:
kubectl --kubeconfig=%KUBECONFIG% get nodes

# 8. alias로 저장해두기 (Windows PowerShell은 doskey 이용)
doskey k=kubectl --kubeconfig=%KUBECONFIG% get nodes
# alias로 kubectl 혹은 k로 간단하게 지정해서 `kubectl --kubeconfig=%KUBE_CONFIG`가 불러와지도록!

# 10. 본인의 작업 네임스페이스 만들기
kubectl create namespace ${본인 이니셜}
# ex) kubectl create namespace kdj

# 참고) namespace 목록 출력하는 법
# kubectl --kubeconfig=%KUBECONFIG% get namespaces

# 참고) 현재 컨텍스트 확인하는 법 (minikube인가 kakao cloud인가)
# kubectl config current-context

 

같은 클러스터 안에 작성된 네임스페이스들


실습 1: 3-Tier 아키텍처 애플리케이션 실습 가이드

0. 샘플 3-Tier 애플리케이션 구조

이 샘플 애플리케이션은 다음 세 가지 계층으로 구성됩니다:

  1. 프론트엔드 (React) - 사용자 인터페이스 제공
  2. 백엔드 (Express.js) - 비즈니스 로직 및 API 제공
  3. 데이터베이스 (MySQL) - 데이터 저장

1. 기본 파일 작성 및 로컬에서 구동 테스트

더보기

1-1. Docker Compose 실행

docker-compose up -d

1-2. 로그 확인

각 컨테이너의 로그를 확인하여 서비스가 제대로 시작되었는지 확인

# 모든 로그 확인
docker-compose logs -f

# 특정 서비스 로그 확인
docker-compose logs -f db
docker-compose logs -f backend
docker-compose logs -f frontend

1-3. 애플리케이션 접속

브라우저에서 다음 URL로 접속:

http://localhost
http:// localhost:3000/api/users
http:// localhost:3000/health

1-4. 데이터베이스 확인

MySQL 클라이언트를 사용하여 데이터베이스에 접속하고 초기 데이터가 제대로 생성되었는지 확인

docker exec -it 3tier-db mysql -u appuser -papppassword myapp -e "SELECT * FROM users;"

1-5. API 테스트(필요시)

로컬 웹에서 프론트를 통해 데이터베이스가 보이고 기능 이 작동하면 제대로 동작하는것임!

curl 또는 Postman을 사용하여 백엔드 API를 테스트합니다:

# 모든 사용자 조회
curl http://localhost:3000/api/users

# 새 사용자 추가 Windows
Set-Content -Encoding ascii body.json '{"username":"testuser","email":"test@example.com"}'
curl.exe -X POST http://localhost:3000/api/users -H "Content-Type: application/json" --data "@body.json"

# 새 사용자 추가 Mac
# curl -X POST http://localhost:3000/api/users \
#  -H "Content-Type: application/json" \
#  -d '{"username":"testuser","email":"test@example.com"}'

# 특정 사용자 조회
curl http://localhost:3000/api/users/1

# 사용자 삭제 Windows
curl.exe -X DELETE http://localhost:3000/api/users/1

# 사용자 삭제 Mac
# curl -X DELETE http://localhost:3000/api/users/1

1-6. 변경 사항 테스트

ex) 프론트엔드 코드를 수정한 경우 다시 빌드하는 방법

docker-compose up -d --build frontend

1-7. 정리

테스트가 완료되면 컨테이너를 중지하고 삭제

# 컨테이너 중지 (데이터 유지)
# docker-compose up -d 하면 추가했던 유저가 남아있음
docker-compose down

# 컨테이너 중지 및 볼륨 삭제 (모든 데이터 삭제)
# docker-compose up -d 하면 추가했던 유저가 남아있지 않음
docker-compose down -v

2. Docker 이미지 빌드 및 Docker Hub에 푸시

애플리케이션의 각 계층(frontend, backend, database)을 컨테이너화하고 Docker Hub에 Push

  • your-dockerhub-username : 자신의 Docker Hub 사용자명
# Docker Hub 로그인
docker login

# 프론트엔드 이미지 빌드 및 푸시
cd frontend
docker build -t ${your-dockerhub-username}/3tier-frontend:latest .
docker push ${your-dockerhub-username}/3tier-frontend:latest

# 백엔드 이미지 빌드 및 푸시
cd ../backend
docker build -t ${your-dockerhub-username}/3tier-backend:latest .
docker push ${your-dockerhub-username}/3tier-backend:latest

# 데이터베이스 이미지 빌드 및 푸시 (선택 사항)
cd ../database
docker build -t ${your-dockerhub-username}/3tier-database:latest .
docker push ${your-dockerhub-username}/3tier-database:latest

Docker Hub에 Pull되어 있는 것을 확인


3. 쿠버네티스 배포

3-1. 메니페스트 파일 작성

쿠버네티스 클러스터에 배포할 리소스들(Pod, Service, HPA 등)의 설정 파일(YAML)들을 작성하자.

이미지 이름(tierr/3tier-backend etc.) 등은 자신의 환경에 맞게 알아서 바꿔야 한다.

더보기
  • backend.yaml
# 5. 백엔드용 ConfigMap, Secret
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: backend-config
data:
  NODE_ENV: "production"
  PORT: "3000"
  DB_HOST: "mysql"
  DB_PORT: "3306"
  DB_NAME: "myapp"
  DB_USER: "appuser"
  LOG_LEVEL: "info"
---
apiVersion: v1
kind: Secret
metadata:
  name: backend-secret
type: Opaque
stringData:
  DB_PASSWORD: "apppassword"

# 6. 백엔드용 Deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: tierr/3tier-backend:latest
        ports:
        - containerPort: 3000
        envFrom:
        - configMapRef:
            name: backend-config
        - secretRef:
            name: backend-secret
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 256Mi
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 20

# 7. 백엔드용 Service
---
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  selector:
    app: backend
  ports:
  - port: 3000
    targetPort: 3000
  type: ClusterIP

# 8. 백엔드용 HPA
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: backend-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: backend
  minReplicas: 2
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  • database.yaml
# DB 배포 (StatefulSet)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: tierr/3tier-database:latest
        ports:
        - containerPort: 3306
          name: mysql
        envFrom:
        - secretRef:
            name: mysql-secret
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        readinessProbe:
          exec:
            command: ["mysqladmin", "ping", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "csi-cinder-sc-delete"  # Explicitly specify the storage class
      resources:
        requests:
          storage: 1Gi
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
stringData:
  MYSQL_ROOT_PASSWORD: rootpassword
  MYSQL_DATABASE: myapp
  MYSQL_USER: appuser
  MYSQL_PASSWORD: apppassword
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306
  clusterIP: None  # Headless Service
  • frontend.yaml
# 9.프론트엔드용 Deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: tierr/3tier-frontend:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 300m
            memory: 256Mi
        readinessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 15
          periodSeconds: 20
# 10. 프론트엔드용 Service
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: 'true'
    loadbalancer.ke.kakaocloud.com/load-balancer-type: 'ALB'
spec:
  selector:
    app: frontend
  ports:
    - name: http
      port: 80
      targetPort: 80
  type: LoadBalancer
---
# 11. 프론트엔드용 HPA
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: frontend
  minReplicas: 2
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

3-2. 쿠버네티스에 배포

쿠버네티스에 배포하고 카카오클라우드 콘솔에서 퍼블릭 IP를 할당한 뒤, 접속해서 테스트해보기

  • your-namespace : 클러스터에 생성한 자신의 네임스페이스
# 데이터베이스 배포
kubectl apply -f k8s-manifests/database.yaml -n ${your-namespace}

# 데이터베이스가 준비되었는지 체크
kubectl get pods -n ${your-namespace} -l app=mysql -w

# 백엔드 배포
kubectl apply -f k8s-manifests/backend.yaml -n ${your-namespace}

# 프론트엔드 배포
kubectl apply -f k8s-manifests/frontend.yaml -n ${your-namespace}

3-3. 데이터베이스 연결 테스트

# Mac
# kubectl run mysql-client --rm -it --image=mysql:8.0 --restart=Never -n your-namespace -- \
#  mysql -h mysql -u appuser -papppassword -e "SELECT * FROM myapp.users;"

# Windows
# 데이터베이스 확인을 위해 1회성 MySQL 클라이언트 Pod 생성
kubectl run mysql-client --rm -it --image=mysql:8.0 --restart=Never -n ${your-namespace} -- mysql -h mysql -u appuser -papppassword -e "SELECT * FROM myapp.users;"

 

3-4. 포트 포워딩을 통한 로컬 접속

브라우저에서 http://localhost:8080으로 접속합니다.

  • your-namespace : 클러스터에 생성한 자신의 네임스페이스
kubectl port-forward svc/frontend 8080:80 -n ${your-namespace}

# 예제
kubectl port-forward svc/backend 3000:3000 &
kubectl port-forward svc/frontend 8080:80 &
kubectl port-forward svc/mysql 3306:3306 &

포트 포워딩
http://localhost:8080 접속 테스트

 


실습2: Kakao Cloud Container Registry의 이미지 저장소(Repository) 이용하기 

1. Repository에 이미지 저장하기

우측 메뉴에서 [Container Pack > Container Registry] 탭에서 '리포지토리 생성' 버튼을 눌러서 리포지토리를 생성 가능하다.

Container Registry > 리포지토리 생성

 

외부에서 레포지토리에 접속하려면 IAM 액세스 키가 필요하다.

[우측 상단  프로필 클릭 > 자격 증명]  을 누르면 계정 설정 페이지로 올 수 있다. 여기서 IAM 액세스 키를 생성할 수 있다.

자격 증명 페이지

 

[IAM 액세스 키 생성] 버튼을 눌러서 키를 생성할 수 있다.

단, 만들고 나면 IAM 액세스 키 Id와 Password를 알려주는데, 어디다 기록해두고 잘 보관해야 하므로 주의해야 한다.

IAM 액세스 키 생성

 

■ 이미지 build & tag
docker build -t ${image-name}:${tag} .
docker tag ${image-name}:${tag} ${project-name}.kr-central-2.kcr.dev/${repository-name}/${image-name}:${tag}

■ 로그인 (Windos CMD)
docker login ${project-name}.kr-central-2.kcr.dev ^
--username ${IAM-Access-Key-ID} ^
--password ${IAM-Access-Key-PW}

■ 이미지 push
docker push ${project-name}.kr-central-2.kcr.dev/${repository-name}/${image-name}:${tag}

 

Container Registry 에서 테스트용으로 만든 리포지토리(tutorial-yji)에 들어가보면, 내가 push한 이미지들을 확인할 수 있다.

리포지토리에 저장된 이미지

 

2. Repository에 저장된 이미지 사용하기

로컬 환경에서 테스트할때는 실제 코드 파일을 빌드해서 실행시켰다.하지만 이번에는 레포지터리에 있는 이미지를 pull 받아서 실행시킬 것이기 때문에 docker-compose.yml 파일을 이미지를 이용하게끔 수정해야 한다.

docker-compose.yaml 파일 수정

 

이제는 이미지를 이용하기 때문에 새로운 폴더에 docker-compose.yml 파일만 있어도 컨테이너를 실행할 수 있다.

# 테스트를 위해 기존 이미지 삭제 (Docker Desktop 에서 직접 삭제해도 된다)
docker rmi 이미지이름:태그

# KC Repository로부터 이미지 Pull
docker pull ${project-name}.kr-central-2.kcr.dev/${repository-name}/{image-name}:${tag}

# docker compose 실행
docker-compose up -d

본 후기는 [카카오엔터프라이즈x스나이퍼팩토리] 카카오클라우드로 배우는 AIaaS 마스터 클래스 (B-log) 리뷰로 작성 되었습니다.