쿠버네티스로 3계층 아키텍처 구성하기 ( Deployment 적용 )

HD.Y·2024년 2월 14일
0

한화시스템 BEYOND SW

목록 보기
52/58
post-thumbnail

구성 환경 🧐

  • 도커 실습 시 실시했던 3계층 ( 프론트엔드, 백엔드, DB ) 아키텍처를 쿠버네티스로 구현해보려 한다.

  • 각각의 서버의 Pod 는 Deployment의 ReCreate 방식을 사용하여 생성할 예정이다.

  • 쿠버네티스는 지난 글에서 설치한 상태에서 이어서 진행한다.


🐶 DB 서버 설정하기

1. PV ( Persistent Volume ) 를 생성한다.
 ➡ PV는 원래 스토리지 서버를 따로 두는데, 지금은 그럴 수 없기 때문에 worker01
   컴퓨터를 스토리지 서버라 생각하고 설정한다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: db-pv  // 원하는 이름으로 지정
spec:
  capacity:
    storage: 5G  // 컴퓨터 사양 고려 설정
  accessModes:
    - ReadWriteOnce
  local:
    path: /mysql-vol  // 원하는 경로로 지정
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - {key: kubernetes.io/hostname, operator: In, values: ['worker01']}

  ➡ 이때 지정한 /mysql-vol 디렉토리를 실제로 worker01 컴퓨터에서 생성해줘야 된다.


2. PVC ( Persistent Volume Claim ) 를 생성한다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-pvc   // 원하는 이름으로 지정
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5G  // 컴퓨터 사양 고려 설정

  • PV 와 PVC 가 정상적으로 생성됬다면, 아래 그림처럼 각각 설정한 이름으로 연결되어 있다는 것을 볼 수 있다.

3. DB 서버용 "ConfigMap" 을 생성한다.

  ➡ ConfigMap 은 환경변수들을 설정하기 위한 것이라고 보면 된다.

  ➡ 하지만, 암호화 기능이 없어서 비밀번호 등 민감한 변수가 그대로 노출되기 때문에,
    Secret을 실제론 사용한다고 한다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: db-config  // 원하는 이름으로 지정
data:
  MYSQL_ROOT_PASSWORD: qwer1234  // root 계정 원하는 패스워드 입력
  MYSQL_DATABASE: lonua  // 초기 생성 데이터베이스 지정 (백엔드 서버와 연동해주기 위해 설정)

4. DB 서버 Deployment 를 생성한다.
 ➡ Deployment에는 ReplicaSet 이 포함되어 있어서, 주로 사용한다고 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: db-deployment  // 원하는 이름으로 지정
spec:
  replicas: 1  // 초기에 생성할 Pod 개수 입력
  strategy:
    type: Recreate
  revisionHistoryLimit: 1 // 버전 업데이트 시 레플리카 셋을 지정한 개수 만큼 남겨놓도록 하는 옵션 설정
  selector:
    matchLabels:
      type: mysql  // 바로 아래의 "labels" 와 서비스 생성 시에도 동일하게 설정해줄 라벨
  template:
    metadata:
      labels:
        type: mysql  // 바로 위에서 지정한 라벨명과 동일하게 설정
    spec:
      nodeSelector:
        kubernetes.io/hostname: worker01  // 원래는 필요없으나, 스토리지 서버가 없어서 설정
      containers:
        - name: mysql
          image: mysql:latest // 도커 허브의 mysql 이미지
          envFrom:
            - configMapRef:
                name: db-config  // 위에서 생성한 ConfigMap 이름
          volumeMounts:
            - name: db-vol  // 원하는 이름으로 지정하나 아래와 동일하게 맞춰줘야함
              mountPath: /var/lib/mysql  // mysql에서 데이터를 다루는 경로
      terminationGracePeriodSeconds: 5
      volumes:
        - name: db-vol  // 원하는 이름으로 지정하나 위와 동일하게 맞춰줘야함
          persistentVolumeClaim:
            claimName: db-pvc // 2번에서 생성한 PVC 이름
  • 아래처럼 정상적으로 Deployment가 생성되었고,

  • 생성된 Pod의 bash 창으로 mysql 서버에 로그인해서 데이터베이스를 확인해보면 지정한 "lonua" 데이터베이스가 생성되어 있는 것을 확인할 수 있다.


5. DB 서버에 대한 서비스를 생성한다.
 ➡ 백엔드 서버에서 DB 서버로 접근하기 위해 서비스를 생성해준다.
 ➡ 이때, Pod 간의 통신이므로 서비스 유형은 기본값인 Cluster Ip로 구성한다.

apiVersion: v1
kind: Service
metadata:
  name: db-svc  // 원하는 이름으로 지정
spec:
  selector:
    type: mysql  // 위에서 Deployment 생성시 지정한 라벨명과 동일하게 적어준다
  ports:
    - port: 3306
      targetPort: 3306
  • 여기까지 하면, DB 서버 설정이 끝난다.

🐱 백엔드 서버 설정하기

1. 백엔드 서버용 "ConfigMap" 을 생성한다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: backend-config
data:
  APP_PASSWORD: 
  AWS_S3_ACCESS_KEY: 
  AWS_S3_SECRET_KEY: 
  BRAND_BUCKET: 
  CLIENT_ID:
  EXPIRED_TIME: 
  JWT_SECRET_KEY: 
  MAIL_SENDER: 
  MASTER: root
  MASTER_PW: qwer1234
  // 여기서 DB 주소는 Pod 간의 통신이므로 위에서 생성한 DB 서비스의 이름으로 설정해줄 수 있다.
  MASTER_URL: jdbc:mysql://db-svc:3306/lonua  
  PORTONE_KEY: 
  PORTONE_SECRETKEY: 
  PRODUCT_BUCKET: 
  PRODUCT_INTROD_BUCKET: 
  REGION:
  REVIEW_BUCKET: 
  SLAVE: root
  SLAVE_PW: qwer1234
  SLAVE_URL: jdbc:mysql://db-svc:3306/lonua

  ➡ 환경변수들을 적어줄때, 숫자로만 되어있는 값은 " " 를 해줘야된다. 예를 들어,
    EXPIRED_TIME 을 300000으로 설정하고 싶다면 "300000" 으로 적어야 오류가 안난다.


2. 배포할 백엔드 서버 이미지를 생성한다.

3. 생성한 이미지를 도커 허브에 푸쉬한다.

  ➡ 이미지를 생성하고 푸쉬하는 방법은 2가지 방법이 있을 것 같다.

    1) 컴퓨터 사양때문에 쿠버네티스와 도커 데스크탑을 동시에 운용을 못한다면,
     쿠버네티스 master 컴퓨터로 jar 파일을 옮기고, Dockerfile을 생성하여 명령어로
     이미지 생성 및 푸쉬하는 방법

    2) 도커 데스크탑을 이용하여 이미지 생성 및 푸쉬하는 방법

  ➡ 도커 데스크탑을 이용하면 간단하겠지만, 이미지 생성하는 것을 연습하는 차원에서
   1번 방식으로 해보겠다.

1) 먼저 Filezilla 프로그램을 이용하여 jar 파일을 master 리눅스 컴퓨터로 옮긴다.

2) 옮긴 경로에서 도커파일을 생성한다 : vi Dockerfile

FROM openjdk:11-jdk-slim-stretch
COPY ./lonua-0.0.1-SNAPSHOT.jar lonua-0.0.1-SNAPSHOT.jar
CMD ["java", "-jar", "/lonua-0.0.1-SNAPSHOT.jar"]

3) 이미지를 생성한다 : docker build --tag [도커허브 계정명]/[레포지토리명]:버전 .

4) 생성한 이미지를 도커허브에 푸쉬한다

  ➡ docker push [도커허브 계정명]/[레포지토리명]:버전

  ➡ 푸쉬하기 위해서는 docker login 을 해야지만 가능하다.

  ➡ 만약, 이메일 계정을 사용하는데 도커 로그인이 안된다면 도커 허브 웹사이트에서
    로그인 후, My Account 클릭 - Change Password로 비밀번호를 새로 설정해줘야
    가능하다.


4. 백엔드 서버 Deployment 를 생성한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-deployment
spec:
  replicas: 1
  strategy:
    type: Recreate
  revisionHistoryLimit: 1
  selector:
    matchLabels:
      type: backend
  template:
    metadata:
      labels:
        type: backend
    spec:
      containers:
      - name: backend
        image: [위에서 푸쉬한 이미지]
        envFrom:
        - configMapRef:
            name: backend-config
  • 정상적으로 실행이 되었고, DB의 배쉬창으로 조회 해보니 데이터도 잘 들어가있었다.

  • 한글이 지원이 안되다 보니 데이터가 ? 로 출력되긴 하는데, 프론트엔드와 연결했을때 잘 불러와지는지 설정을 마치고 테스트 해보겠다.


5. 백엔드 서버 서비스를 생성한다. ( DB와 마찬가지로 Cluster IP 로 생성 )

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  selector:
    type: backend
  ports:
  - port: 8080
    targetPort: 8080
  • Cluster IP 로 설정하는 사유는, 프론트엔드 서버에서 요청을 하면, 리버스 프록시 설정으로
    백엔드 서버 Pod 로 요청을 보내줘 Pod간의 통신이 되기 때문이다.

  • 여기까지 하면, 백엔드 서버의 설정이 끝난다.


🐹 프론트엔드 서버 설정하기

1. Vue 프로젝트에서 배포하기 위해 빌드 : npm run build

 ➡ 이때, 백엔드 서버를 호출하는 주소는
http://[아래에서 생성된 로드밸런서 서비스의 엔드포인트 IP]:80/api 로 설정하였다.

 ➡ 이부분은 순서를 고려해봐야될 것 같다. 여기서 설정되는 프론트서버의 IP는
   아래에서 서비스를 생성할때 알 수 있다. 하지만, Deployment의 경우 일단 만들어
   놓고, 나중에 백엔드 호출 주소를 수정 후 이미지를 새로 생성해서, 리소스 편집으로
   이미지 버전만 바꿔주면 적용되긴 한다.


2. 생성된 dist 폴더안의 파일들을 FileZilla를 이용하여 미리 생성해둔 /frontend 경로로 옮겨준다.

3. nginx 설정파일을 생성한다 : vi default.conf

server {
  listen       80;
  server_name  localhost;
  #access_log  /var/log/nginx/host.access.log  main;
  location /api {
      rewrite ^/api(.*)$ $1 break;
      proxy_pass http://backend-svc:8080;  // 위에서 생성한 백엔드 서버 서비스 이름
      proxy_set_header Host $http_host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
  }
  location / {
      alias   /usr/share/nginx/html/;
      try_files $uri $uri/ /index.html;
  }
  #error_page  404              /404.html;
  # redirect server error pages to the static page /50x.html
  #
  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
      root   /usr/share/nginx/html;
  }
  # proxy the PHP scripts to Apache listening on 127.0.0.1:80
  #
  #location ~ \.php$ {
  #    proxy_pass   http://127.0.0.1;
  #}
  # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  #
  #location ~ \.php$ {
  #    root           html;
  #    fastcgi_pass   127.0.0.1:9000;
  #    fastcgi_index  index.php;
  #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
  #    include        fastcgi_params;
  #}
  # deny access to .htaccess files, if Apache's document root
  # concurs with nginx's one
  #
  #location ~ /\.ht {
  #    deny  all;
  #}
}

4. 도커파일을 생성한다 : vi Dockerfile

 FROM nginx:latest
 ADD ./css /usr/share/nginx/html/css
 ADD ./fonts /usr/share/nginx/html/fonts
 ADD ./img /usr/share/nginx/html/img
 ADD ./js /usr/share/nginx/html/js
 ADD ./styles.css /usr/share/nginx/html/styles.css
 ADD ./logo.png /usr/share/nginx/html/logo.png
 RUN rm -rf /usr/share/nginx/html/index.html
 ADD ./index.html /usr/share/nginx/html/index.html
 RUN rm -rf /etc/nginx/conf.d/default.conf
 ADD ./default.conf /etc/nginx/conf.d/default.conf
 CMD ["nginx", "-g", "daemon off;"]

5. 이미지를 생성한다 : docker build --tag [도커허브 계정명]/[레포지토리명]:버전 .

6. 생성한 이미지를 도커 허브에 푸쉬한다.
  ➡ docker push [도커허브 계정명]/[레포지토리명]:버전


7. 프론트엔드 서버 Deployment 생성한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
spec:
  replicas: 1
  strategy:
    type: Recreate
  revisionHistoryLimit: 1
  selector:
    matchLabels:
      type: frontend
  template:
    metadata:
      labels:
        type: frontend
    spec:
      containers:
      - name: frontend
        image: [위에서 푸쉬한 이미지]

8. 프론트엔드 서버 서비스를 생성한다. ( 운영을 위한 것으로 LoadBalencer 로 생성 )
  ➡ 개발 및 테스트 목적으로 서비스를 생성할때는 NodePort 유형으로 서비스를
    생성했었는데, 이제는 운영을 위한 목적이라 생각하고 LoadBalencer 유형으로
    생성해보겠다.

  • LoadBalencer 설정을 위한 사전 설치 : 무료로 사용 가능한 MetalLB 이용
     ➡ 설치 가이드 바로가기 / 가이드에 나와있는 순서대로 진행하면 된다.

    1) 현재 모드 확인

    kubectl get configmap kube-proxy -n kube-system -o yaml | \
    grep strictARP

     ➡ strictARP: false 라고 출력됨


    2) 아래를 실행하여 true로 변경

    kubectl get configmap kube-proxy -n kube-system -o yaml | \
    sed -e "s/strictARP: false/strictARP: true/" | \
    kubectl apply -f - -n kube-system


     ➡ 정상적으로 수행되면 아래와 같이 출력된다.

    Warning: resource configmaps/kube-proxy is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
    configmap/kube-proxy configured


    3) MetalLB 설치

    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/namespace.yaml

    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/metallb.yaml

     ➡ 정상설치 확인 : kubectl get pod -n metallb-system 실행 시 전부 "Running" 상태


     ➡ 만약, 시간이 지나도 계속 아래 사진과 같이 출력된다면

    • 아래 명령어 실행
      kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

4) 설정파일 생성 : vi metallb_config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 77.77.77.50-77.77.77.90  // master와 worker01/02 에서 쓰는 IP 대역 피해서 지정

5) 설정한 내용 적용 : kubectl apply -f metallb_config.yaml

  • 여기까지하면 MetalLB 설치가 끝나고, 이제 LoadBalancer 유형을 적용한 서비스를 생성할 수 있다.

  • 프론트엔드 서버 서비스 생성
apiVersion: v1
kind: Service
metadata:
 name: frontend-svc
spec:
 selector:
   type: frontend
 ports:
  - port: 80
    targetPort: 80
 type: LoadBalancer

 ➡ 로드밸런서로 서비스를 생성하면 아래처럼 외부 엔드포인트가 설정되어있다.

 ➡ 그럼 해당 엔드포인트로 접속해보면, 프론트엔드 서버가 정상적으로 접속이 될 것이고
   아래처럼 DB에서 상품 데이터를 잘 불러오고 있는 것을 확인할 수 있다.


  • 만약 여기서 서버에 트래픽이 몰려서 서버를 늘려줘야 된다면, 디플로이먼트의
    "스케일만 증가" 시켜주면 알아서 Pod를 늘려줘서 편하게 서버를 관리할 수 있다.

🐮 Metrics Server 설치하기

  • 마지막으로, CPU 와 메모리 사용량에 따라 자동으로 서버를 늘려줄 수 있도록 설정하는 내용이다.

  • 일단, 지금은 아래처럼 CPU 와 메모리가 아무것도 표시되어 있지 않은 상태다.

  • 이제 Metrics Server를 설치해보겠다.

    kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.3/components.yaml


  • 설치가 됬다면, 대시보드에서 kube-system 네임스페이스로 들어가서 파드를 보면 metrice-server 파드가 있을 것이다. ( 디플로이먼트도 생겨있을 것이다. )

  • 그러면 이제 디플로이먼트의 metrics-server 로 들어가서 리소스 편집을 클릭 후
    174번째 줄 아래에 이 내용을 추가한다 : - '--kubelet-insecure-tls'

  • 정상적으로 설정이 됬다면, 잠시 기다리다 보면 CPU 와 메모리 사용량이 아래 처럼
    보이게 될 것이다.

  • 오늘은 Deployment 의 여러가지 배포 방식 중 ReCreate 를 이용한 방식을 실습해봤다. 하지만 ReCreate 방식은 버전 수정 또는 업그레이드 시 기존 Pod가 삭제되고, 새로 생성되는 동안 서비스가 잠시 중단되는 문제가 있다.

  • 따라서, 이것을 해결하여 무중단 배포를 하기 위한 실습이 이어질 예정이다.


실습 진행 간 발생한 오류 내용 정리

  • 디플로이먼트 생성 시 아래와 같은 오류가 뜨면서, Pod가 2개를 설정했다면 1개밖에 생성이 안됬는데, 그럴때는 문제가 되는 worker 컴퓨터를 재부팅하니 해결되었다.

Failed to create pod sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "35d064b6feedd643f61af4fb2ae7ff7582b003bb32bf16ffbe7265eb80fc98bf" network for pod "deployment-1-56b547b87f-x8tc6": networkPlugin cni failed to set up pod "deployment-1-56b547b87f-x8tc6_kube-public" network: error getting ClusterInformation: connection is unauthorized: Unauthorized, failed to clean up sandbox container "35d064b6feedd643f61af4fb2ae7ff7582b003bb32bf16ffbe7265eb80fc98bf" network for pod "deployment-1-56b547b87f-x8tc6": networkPlugin cni failed to teardown pod "deployment-1-56b547b87f-x8tc6_kube-public" network: error getting ClusterInformation: connection

profile
Backend Developer

0개의 댓글