Supervisor Series: Ep 3. Deploying a 3-Tier Application with VM Service and TKG

최신 애플리케이션은 다재다능하고 끊임없이 진화하고 있지만 설계 철학은 동일하게 유지되며, 워크로드마다 배포 유형에 따라 이점이 다릅니다. 일부 애플리케이션은 마이크로서비스 아키텍처에 더 적합하여 더 작은 컨테이너화된 단위로 나뉘는 반면, 다른 애플리케이션은 가상 머신으로 더 잘 수행될 수 있습니다. 슈퍼바이저의 진정한 힘은 동일한 플랫폼에서 다양한 워크로드를 배포, 실행 및 관리할 수 있다는 데서 비롯됩니다.

이번 에피소드에서는 VM Service와 Tanzu Kubernetes Grid를 사용해 3티어 애플리케이션을 배포하는 방법을 살펴보겠습니다.

애플리케이션의 데이터베이스 부분은 VM 서비스를 사용하여 수퍼바이저 네임스페이스에 가상 머신으로 배포됩니다.

애플리케이션의 백엔드 및 프론트엔드 부분은 쿠버네티스 포드로서 탄즈 쿠버네티스 그리드(TKG) 클러스터에 배포됩니다. 이전 에피소드에서 이미 애플리케이션 이미지를 새로 배포된 하버 리포지토리에 업로드했습니다.

수퍼바이저 네임스페이스 구성하기

가장 먼저 해야 할 일은 슈퍼바이저 클러스터에 새 네임스페이스를 생성하는 것입니다.

  • Workload Management로 이동하여 NamespacesNew Namespace를 클릭합니다.
  • Supervisor-1을 선택하고 이름 필드에 namespace-1을 입력합니다.
  • 이 네임스페이스에서 생성된 오브젝트가 사용할 데이터스토어를 지정하기 위해 k8s-storage-policy 스토리지 정책을 할당합니다.
  • VM 서비스를 구성하려면 best-effort-small VM 클래스를 연결하고 Ubuntu VM 이미지가 포함된 콘텐츠 라이브러리를 구성합니다.
    참고: 이전에 생성한 사용자 지정 Ubuntu 22.04 Jammy Jellyfish 이미지를 사용합니다. 이 블로그 게시물(https://core.vmware.com/blog/vsphere-8-vmservice-bring-your-own-image-part-2)에서 이미지를 직접 만드는 방법에 대해 자세히 알아볼 수 있습니다.

새로운 기능 중 하나는 수퍼바이저 활성화 프로세스 중에 자동으로 Kubernetes Grid Service Content Library를 생성하며, 이 콘텐츠 라이브러리는 새 네임스페이스와 자동으로 연결되고 기본적으로 필요할 때 콘텐츠를 다운로드하도록 설정되어 있습니다. 콘텐츠 라이브러리를 변경하려면 Workload Management -> Supervisor -> Configure -> General -> Tanzu Kubernetes Grid Service -> Content Library에서 변경할 수 있습니다.

VM 서비스를 사용하여 MySQL DB VM 배포하기

이제 네임스페이스가 생겼으므로 데이터베이스 VM의 배포를 진행할 수 있습니다. 이 배포에는 두 개의 파일을 사용합니다:

  • cloud-config.yaml
  • mysql-vm.yaml

먼저 cloud-config.yaml을 살펴보겠습니다. 이 파일에는 사용자 정보, 데이터베이스 정보, 배포 중에 VM에서 실행해야 하는 명령 등 MySQL 데이터베이스에 대한 구성 세부 정보가 포함되어 있습니다. 이 파일을 통해 MySQL 서버가 설치 및 구성됩니다.

cloud-config.yaml:

#cloud-config
ssh_pwauth: true

groups:
  - admingroup: [root,sys]

users:
  - name: dev
    gecos: Dev S. Ops
    lock_passwd: false
    passwd: $6$n/zJuy.x/O0oRKHp$sRK0wNmKkTRX26poRTVPIsXiz4u9SvVR2euzNV7ZXR9DTD.L3XgH0TgZZyxiGE1Mw.B6D8YcqCrLpwDCoRnBQ.
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: sudo, users, admin
    shell: /bin/bash

write_files:
  - content: |
       ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
    append: true
    path: /alteruser.txt

  - content: |
       CREATE USER 'devops'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
       CREATE DATABASE demo;
       GRANT ALL PRIVILEGES ON demo.* TO 'devops'@'%';

       CREATE TABLE demo.user (
         id INTEGER PRIMARY KEY AUTO_INCREMENT,
         username varchar(255) NOT NULL,
         password varchar(255) NOT NULL,
         UNIQUE (username)
       );


       CREATE TABLE demo.entry (
         id INTEGER PRIMARY KEY AUTO_INCREMENT,
         author_id INTEGER NOT NULL,
         created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
         title varchar(255) NOT NULL,
         body varchar(5400) NOT NULL,
         FOREIGN KEY (author_id) REFERENCES user (id)
       );

       INSERT INTO demo.user (username,password) VALUES('Jeremy','devops123');
       INSERT INTO demo.entry (author_id,title,body) VALUES(1,"Welcome to the request page, this is the first entry","This entry is owned by Jeremy and can only be modified by him. You can create your own post by registering and logging in!");

    append: true
    path: /init.sql

runcmd:
  - sudo apt update
  - sudo apt -y install mysql-server
  - sudo systemctl start mysql.service
  - sudo mysql < /alteruser.txt
  - mysql -u root -ppassword < /init.sql
  - sudo sed -i '0,/bind-address/s//#bind-address/' /etc/mysql/mysql.conf.d/mysqld.cnf
  - sudo systemctl restart mysql.service

두 번째 파일인 mysql-vm.yaml은 DB VM의 원하는 상태를 정의하는 가상 머신 YAML 매니페스트입니다. 이 파일에서는 이름, VM을 배포할 네임스페이스, 사용할 이미지 및 기타 구성 세부 정보를 정의합니다. 전송 방법으로 CloudInit을 사용합니다.

ConfigMap 섹션에는 base64로 인코딩된 cloud-config.yaml 파일의 버전이 포함됩니다.

또한 필요한 포트에 DB VM을 노출하기 위해 로드 밸런서를 배포합니다.

mysql-vm.yaml:

apiVersion: vmoperator.vmware.com/v1alpha1
kind: VirtualMachine
metadata:
  labels:
    vm.name: db-vm
    role: db
  name: mysql-db
  namespace: namespace-1
spec:
  imageName: ubuntu-22.04
  className: best-effort-small
  powerState: poweredOn
  storageClass: k8s-storage-policy
  networkInterfaces:
  - networkName:
    networkType: nsx-t
  vmMetadata:
      configMapName: mysql-db-cm
      transport: CloudInit
---
apiVersion: v1
kind: ConfigMap
metadata:
    name: mysql-db-cm
    namespace: namespace-1
data:
  user-data: >-
    CiNjbG91ZC1jb25maWcKCnNzaF9wd2F1dGg6IHRydWUKCmdyb3VwczoKICAtIGFkbWluZ3JvdXA6IFtyb290LHN5c10KCnVzZXJzOgogIC0gbmFtZTogZGV2CiAgICBnZWNvczogRGV2IFMuIE9wcwogICAgbG9ja19wYXNzd2Q6IGZhbHNlCiAgICBwYXNzd2Q6ICQ2JG4vekp1eS54L08wb1JLSHAkc1JLMHdObUtrVFJYMjZwb1JUVlBJc1hpejR1OVN2VlIyZXV6TlY3WlhSOURURC5MM1hnSDBUZ1paeXhpR0UxTXcuQjZEOFljcUNyTHB3RENvUm5CUS4gCiAgICBzdWRvOiBBTEw9KEFMTCkgTk9QQVNTV0Q6QUxMCiAgICBncm91cHM6IHN1ZG8sIHVzZXJzLCBhZG1pbgogICAgc2hlbGw6IC9iaW4vYmFzaAoKd3JpdGVfZmlsZXM6CiAgLSBjb250ZW50OiB8CiAgICAgICBBTFRFUiBVU0VSICdyb290J0AnbG9jYWxob3N0JyBJREVOVElGSUVEIFdJVEggbXlzcWxfbmF0aXZlX3Bhc3N3b3JkIEJZICdwYXNzd29yZCc7CiAgICBhcHBlbmQ6IHRydWUKICAgIHBhdGg6IC9hbHRlcnVzZXIudHh0CgogIC0gY29udGVudDogfAogICAgICAgQ1JFQVRFIFVTRVIgJ2Rldm9wcydAJyUnIElERU5USUZJRUQgV0lUSCBteXNxbF9uYXRpdmVfcGFzc3dvcmQgQlkgJ3Bhc3N3b3JkJzsKICAgICAgIENSRUFURSBEQVRBQkFTRSBkZW1vOwogICAgICAgR1JBTlQgQUxMIFBSSVZJTEVHRVMgT04gZGVtby4qIFRPICdkZXZvcHMnQCclJzsKCiAgICAgICBDUkVBVEUgVEFCTEUgZGVtby51c2VyICgKICAgICAgICAgaWQgSU5URUdFUiBQUklNQVJZIEtFWSBBVVRPX0lOQ1JFTUVOVCwKICAgICAgICAgdXNlcm5hbWUgdmFyY2hhcigyNTUpIE5PVCBOVUxMLAogICAgICAgICBwYXNzd29yZCB2YXJjaGFyKDI1NSkgTk9UIE5VTEwsCiAgICAgICAgIFVOSVFVRSAodXNlcm5hbWUpCiAgICAgICApOwoKCiAgICAgICBDUkVBVEUgVEFCTEUgZGVtby5lbnRyeSAoCiAgICAgICAgIGlkIElOVEVHRVIgUFJJTUFSWSBLRVkgQVVUT19JTkNSRU1FTlQsCiAgICAgICAgIGF1dGhvcl9pZCBJTlRFR0VSIE5PVCBOVUxMLAogICAgICAgICBjcmVhdGVkIFRJTUVTVEFNUCBOT1QgTlVMTCBERUZBVUxUIENVUlJFTlRfVElNRVNUQU1QLAogICAgICAgICB0aXRsZSB2YXJjaGFyKDI1NSkgTk9UIE5VTEwsCiAgICAgICAgIGJvZHkgdmFyY2hhcig1NDAwKSBOT1QgTlVMTCwKICAgICAgICAgRk9SRUlHTiBLRVkgKGF1dGhvcl9pZCkgUkVGRVJFTkNFUyB1c2VyIChpZCkKICAgICAgICk7CgogICAgICAgSU5TRVJUIElOVE8gZGVtby51c2VyICh1c2VybmFtZSxwYXNzd29yZCkgVkFMVUVTKCdKZXJlbXknLCdkZXZvcHMxMjMnKTsKICAgICAgIElOU0VSVCBJTlRPIGRlbW8uZW50cnkgKGF1dGhvcl9pZCx0aXRsZSxib2R5KSBWQUxVRVMoMSwiV2VsY29tZSB0byB0aGUgcmVxdWVzdCBwYWdlLCB0aGlzIGlzIHRoZSBmaXJzdCBlbnRyeSIsIlRoaXMgZW50cnkgaXMgb3duZWQgYnkgSmVyZW15IGFuZCBjYW4gb25seSBiZSBtb2RpZmllZCBieSBoaW0uIFlvdSBjYW4gY3JlYXRlIHlvdXIgb3duIHBvc3QgYnkgcmVnaXN0ZXJpbmcgYW5kIGxvZ2dpbmcgaW4hIik7CgogICAgYXBwZW5kOiB0cnVlCiAgICBwYXRoOiAvaW5pdC5zcWwKCnJ1bmNtZDoKICAtIHN1ZG8gYXB0IHVwZGF0ZQogIC0gc3VkbyBhcHQgLXkgaW5zdGFsbCBteXNxbC1zZXJ2ZXIKICAtIHN1ZG8gc3lzdGVtY3RsIHN0YXJ0IG15c3FsLnNlcnZpY2UKICAtIHN1ZG8gbXlzcWwgPCAvYWx0ZXJ1c2VyLnR4dAogIC0gbXlzcWwgLXUgcm9vdCAtcHBhc3N3b3JkIDwgL2luaXQuc3FsCiAgLSBzdWRvIHNlZCAtaSAnMCwvYmluZC1hZGRyZXNzL3MvLyNiaW5kLWFkZHJlc3MvJyAvZXRjL215c3FsL215c3FsLmNvbmYuZC9teXNxbGQuY25mCiAgLSBzdWRvIHN5c3RlbWN0bCByZXN0YXJ0IG15c3FsLnNlcnZpY2UK
---
apiVersion: vmoperator.vmware.com/v1alpha1
kind: VirtualMachineService
metadata:
  name: mysql-db
  namespace: namespace-1
spec:
  ports:
  - name: ssh
    port: 22
    protocol: TCP
    targetPort: 22
  - name: mysql
    port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    vm.name: db-vm
  type: LoadBalancer

원하는 명령줄에서 다음 명령을 실행하여 DB VM을 배포합니다.

수퍼바이저에 로그인한다:

  • kubectl vsphere login –server=192.168.30.34 –vsphere-username administrator@vsphere.local

컨텍스트를 namespace-1로 설정한다:

  • kubectl config use-context namespace-1


mysql-vm.yaml을 배포한다:

  • kubectl apply -f mysql-vm.yaml -n namespace-1

vm과 서비스가 배포되고 구성되었는지 확인한다:

  • kubectl get vm
  • kubectl get service

백엔드 배포를 준비하기 위해, mysql-lb의 외부 IP를 가져와서 base64로 인코딩한다:

echo -n “192.168.30.39” | base64 -w 0

Tanzu Kubernetes Grid (TKG) 클러스터 배포하기

백엔드 및 프론트엔드 파드는 TKG 클러스터에 배포됩니다. tkg-cc-1.yaml 파일에서 원하는 클러스터의 상태를 정의합니다. 데모 목적상, 하나의 컨트롤 플레인과 두 개의 워커 노드가 있는 TKG 클러스터를 배포하겠습니다. 다음 클러스터 정의는 vSphere 8에 도입된 새로운 정의인 ClusterClass를 사용합니다. 이름, 네임스페이스, 네트워크 및 스토리지 구성, 배포할 버전을 정의합니다. 또한 어노테이션을 사용하여 사용하려는 OS 유형을 지정할 수 있으며, 우분투 또는 Photon OS 중에서 선택할 수 있습니다.

tkg-cc-1.yaml:

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: tkg-cc-1
  namespace: namespace-1
spec:
  clusterNetwork:
    services:
      cidrBlocks: ["192.168.192.0/18"]
    pods:
      cidrBlocks: ["192.168.128.0/18"]
    serviceDomain: "managedcluster.local"
  topology:
    class: tanzukubernetescluster
    version: v1.23.8---vmware.2-tkg.2-zshippable
    controlPlane:
      metadata:
        annotations:
          run.tanzu.vmware.com/resolve-os-image: os-name=ubuntu
      replicas: 1
    workers:
      # node pools
      machineDeployments:
        - class: node-pool
          name: node-pool-1
            # failureDomain: zone1
          replicas: 2
    variables:
      - name: vmClass
        value: best-effort-small
      # default storageclass for control plane and node pool
      - name: storageClass
        value: k8s-storage-policy

수퍼바이저에 로그인한 상태에서 다음 명령어를 사용하여 TKG 클러스터를 배포한다:

  • kubectl apply -f tkg-cc-1.yaml -n namespace-1

클러스터가 배포되면 로그인한다:

  • kubectl vsphere login –server=192.168.30.34 –tanzu-kubernetes-cluster-name tkg-cc-1 –tanzu-kubernetes-cluster-namespace namespace-1 –vsphere-username administrator@vsphere.local

컨텍스트를 tkg-cc-1로 설정한다:

  • kubectl config use-context tkg-cc-1

표준 보안 정책을 적용한다:

  • kubectl apply -f pod-security-policy.yaml

tkg 클러스터 내에 app-ns라는 네임스페이스를 생성한다:

  • kubectl create ns app-ns

프라이빗 하버 이미지 레지스트리에서 이미지를 가져올 수 있도록 도커 시크릿을 생성한다:

  • kubectl create secret docker-registry docker-hub-creds –docker-server=harbor.vmw.lab –docker-username=admin –docker-password=Harbor12345 -n app-ns

TKG 클러스터에 백엔드 애플리케이션 배포하기

이제 백엔드 애플리케이션을 배포할 준비가 되었습니다. backend-app.yaml 매니페스트는 세 개의 섹션으로 구성되어 있습니다. 먼저, 앞서 추가한 DB LB의 인코딩된 IP를 포함하여 데이터베이스 연결에 대한 정보를 담고 있는 시크릿을 생성합니다. 다음 섹션에서는 복제본 수, 이미지 정보 및 일부 구성 변수를 포함하여 배포를 정의합니다. 마지막으로 포트 5000에 백엔드를 노출하기 위한 로드 밸런서 서비스를 정의합니다.

backend-app.yaml:

apiVersion: v1
kind: Secret
metadata:  
  name: backend-app-secret
type: Opaque
data:
  mysql_user: ZGV2b3Bz
  db_passwd: cGFzc3dvcmQ=
  #mysql_host: <BASE64_ENCODED_IP_FOR_MYSQL_VM>
  mysql_host: MTkyLjE2OC4zMC4zOQ==
  db_name: ZGVtbw==

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-app-deployment
  labels:
    app: backend-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend-app
  template:
    metadata:
      labels:
        app: backend-app
    spec:
      containers:
        - name: backend-app
          image: harbor.vmw.lab/3ta/backend:latest
          ports:
            - containerPort: 5000
          env:
          - name: MYSQL_HOST
            valueFrom:
              secretKeyRef:
                name: backend-app-secret
                key: mysql_host
                optional: false
          - name: MYSQL_USER
            valueFrom:
              secretKeyRef:
                name: backend-app-secret
                key: mysql_user
                optional: false 
          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: backend-app-secret
                key: db_passwd
                optional: false 
          - name: DB_NAME
            valueFrom:
              secretKeyRef:
                name: backend-app-secret
                key: db_name
                optional: false 
      imagePullSecrets:
      - name: docker-hub-creds
---

apiVersion: v1
kind: Service
metadata:
  name: backend-app-service
spec:
  selector:
    app: backend-app
  ports:
    - name: web-app-port
      protocol: TCP
      port: 5000
      targetPort: 5000
  type: LoadBalancer

tkg-cc-1 클러스터에 로그인한 상태에서 app-ns 네임스페이스에 backend-app.yaml 매니페스트를 적용한다:

  • kubectl apply -f backend-app.yaml -n app-ns

백엔드 파드가 app-ns에서 실행 중인지 확인한다:

  • kubectl get pods -n app-ns

backend-app-service의 외부 IP를 가져온다:

  • kubectl get service -n app-ns

데이터베이스와의 통신을 검증하기 위해 포트 5000에서 올바른 응답이 수신되는지 확인한다:

  • curl -X GET 192.168.30.37:5000/api/index

이제 이 IP와 포트를 가져와서 베이스 64 인코딩한 다음 프론트엔드 매니페스트에 추가합니다:

  • echo -n “192.168.30.38:5000” | base64 -w0

TKG 클러스터에 프론트엔드 애플리케이션 배포하기

마지막으로 배포할 것은 프론트엔드 애플리케이션으로, 프론트엔드-app.yaml 매니페스트에 정의되어 있습니다. 여기에서도 백엔드와의 통신을 허용하기 위한 시크릿, 복제본 수와 이미지 정보를 포함한 배포, 포트 5000에서 프론트엔드를 노출하기 위한 로드 밸런서 서비스를 정의하고 있습니다.

frontend-app.yaml:

apiVersion: v1
kind: Secret
metadata:  
  name: frontend-app-secret
type: Opaque
data:
  #api_url: <BASE64_ENCODED_IP:PORT_FOR_backend-app-service>
  api_url: MTkyLjE2OC4zMC4zNzo1MDAw 

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-app-deployment
  labels:
    app: frontend-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend-app
  template:
    metadata:
      labels:
        app: frontend-app
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - backend-app
            topologyKey: kubernetes.io/hostname
      containers:
        - name: frontend-app
          image: harbor.vmw.lab/3ta/frontend:latest
          ports:
            - containerPort: 5000
          env:
          - name: API_URL
            valueFrom:
              secretKeyRef:
                name: frontend-app-secret
                key: api_url
                optional: false 
      imagePullSecrets:
      - name: docker-hub-creds
---

apiVersion: v1
kind: Service
metadata:
  name: frontend-app-service
spec:
  selector:
    app: frontend-app
  ports:
    - name: web-app-port
      protocol: TCP
      port: 5000
      targetPort: 5000
  type: LoadBalancer

tkg-cc-1 클러스터에 로그인한 상태에서 app-ns 네임스페이스에 frontend-app.yaml 매니페스트를 적용한다:

  • kubectl apply -f frontend-app.yaml -n app-ns

프론트엔드 파드가 app-ns에서 실행 중인지 확인한다:

  • kubectl get pods -n app-ns

frontend-app-service의 외부 IP를 가져온다:

  • kubectl get service -n app-ns

애플리케이션 테스트

애플리케이션이 성공적으로 배포되었는지 테스트하려면 프론트엔드-앱-서비스의 외부 IP를 가져와 포트 5000의 브라우저에서 열면 됩니다.

데이터베이스와의 연결을 확인하려면 새 사용자를 등록하고 로그인합니다. 사용자 정보가 데이터베이스에 저장됩니다. 요청을 클릭하고 새 요청을 추가하여 데이터베이스에 쓸 수 있는지 확인할 수도 있습니다.

이 에피소드가 실제로 작동하는 모습 보기

출처 : https://core.vmware.com/resource/supervisor-series-ep-3-deploying-3-tier-application-vm-service-and-tkg

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

You May Also Like