DevOps

EKS에서 기존 서비스에 DB(mysql) 연결 방법(pod 간 통신)

우제혁 2023. 12. 18. 16:25

 

kubectl를 사용하기위한 명령어

--region 과 --name은 생성한 지역과 자신의 cluster로 맞춰줘야한다.
aws eks update-kubeconfig --region us-east-2   --name eks-JaeHyuk

apply 명령어

kubectl apply -f db-statefulset.yaml
kubectl apply -f db-storagelass.yaml
kubectl apply -f db-pv.yaml
kubectl apply -f db-service.yaml

delete 명령어

kubectl delete -f db-statefulset.yaml
kubectl delete -f db-storagelass.yaml
kubectl delete -f db-pv.yaml
kubectl delete -f db-service.yaml

pv 목록 조회

kubectl get pv

pv 상세 조회

kubectl describe pv [PV 이름]

모든 리소스 조회 명령어

kubectl get all -n default

pv,pvc 목록 조회 명령어

kubectl get pv,pvc -n default

StorageClass 생성(db-storagelass.yaml)

  • 동적으로 스토리지를 프로비져닝 하기 위해 사용되는 kubernetes resource 객체
  • 즉, Persistent Volume이 필요할 때 자동으로 생성되도록 설정하는 역할
  • • 일반적으로 시스템 운영자가 Storage Class를 생성하면, 이후 개발자가 pod를 생성할때 PersisentVolumeClaim 을 통해서 볼륨을 필요에 따라 생성할 수 있게 된다.
  • 파드가 스케줄링되기 전까지 PVC의 바인딩과 프로비저닝을 지연
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: my-storage
  namespace: default
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

PersistentVolume Claim 생성

  • Persistent Volume Claim은 볼륨을 pod에서 요청할때 볼륨을 요청한 만큼 할당해 주는 역할
  • StatefulSet을 이용하여 mysql을 이용하기 때문에 PVC를 바로 생성하지 않고, StatefulSet에서 volumeClaimTemplates 을 이용하여 생성
  • 일반 케이스에서는 직접 생성

PersistentVolume 생성(db-pv.yaml)

  • pv-statefulset-mysql-0라는 이름의 PV를 생성
  • PV는 2GB의 용량
  • 한 번에 한 개의 노드에서만 읽기/쓰기가 가능
  • PV가 해제되면 자동으로 삭제되며, 특정 노드에만 바인딩되도록 설정
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-statefulset-mysql-0
  namespace: default
spec:
  storageClassName: "my-storage"
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  hostPath:
    path: /tmp/k8s-pv 
    type: DirectoryOrCreate
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: "kubernetes.io/hostname"
          operator: "In"
          values:
          - GLW4XPMQ2X

StatefulSet 지정 (db-statefulset.yaml)

  • 'mysql'이라는 이름의 StatefulSet을 생성
  • S1개의 'mysql' 컨테이너를 가진 파드를 1개 생성
  • 파드는 3306 포트를 열고, '/var/lib/mysql'에 볼륨을 마운트
  • 각 파드는 'mysql-data'라는 이름의 PVC를 사용하며, 이 PVC는 500Mi의 스토리지를 요청
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: default
spec:
  replicas: 1
  serviceName: mysql
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mysql
        image: mariadb:latest
        ports:
          - name: tpc
            protocol: TCP
            containerPort: 3306
        env:
          - name: MYSQL_ROOT_PASSWORD
            value: password
        volumeMounts:
          - name: mysql-data
            mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: mysql-data
        namespace: storage
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 500Mi

Service 생성(db-service.yaml)

  • 각 노드의 3306 포트를 노출
  • 이 포트로 들어오는 트래픽을 app: mysql 레이블을 가진 파드의 3306 포트로 전달
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  type: NodePort
  ports:
    - port: 3306
      protocol: TCP
      targetPort: 3306
  selector:
    app: mysql

deployment 수정(flask-deployment.yaml)

  • 컨테이너의 환경 변수를 설정
  • MySQL 서버의 호스트 이름(mysql), 포트(3306), 사용자 이름(root), 비밀번호(password), 데이터베이스 이름(petclinic)을 설정
apiVersion: apps/v1
kind: Deployment
metadata:
  name: osint-flask
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: osint-flask
  template:
    metadata:
      labels:
        app: osint-flask
    spec:
      containers:
        - name: osint-flask
          image: 871065065486.dkr.ecr.us-east-2.amazonaws.com/jenkinsflask:17
          imagePullPolicy: Always
          ports:
            - containerPort: 5000
          env:
            - name: MYSQL_HOST
              value: 'mysql' # MySQL 서비스의 이름
            - name: MYSQL_PORT
              value: '3306' # MySQL 포트
            - name: MYSQL_USER
              value: 'root'
            - name: MYSQL_PASSWORD
              value: password
            - name: MYSQL_DB
              value: 'petclinic' # MySQL 데이터베이스 이름

 

여기서부터는 이미 올라간 서비스의 내부 코드를 변경하는 모습이다. 

 

flask 수정(login_module.py)

  • import os 추가
    • MySQL 데이터베이스에 연결하기 위한 정보를 환경 변수에서 가져오는데 사용
  • os 모듈을 사용하여 환경 변수를 읽어와 MySQL 데이터베이스에 접속하기 위한 정보를 설정
from flask import Flask, session, render_template, redirect, request, url_for, Blueprint
import pymysql
import os

mysql_host = os.environ.get('MYSQL_HOST', 'mysql')
mysql_port= int(os.environ.get('MYSQL_PORT', 3306))
mysql_user =  os.environ.get('MYSQL_USER', 'root')
mysql_password = os.environ.get('MYSQL_PASSWORD', 'password')
mysql_db = os.environ.get('MYSQL_DB', 'petclinic')

login_module = Blueprint("login_module", __name__)

@login_module.route("/login", methods=['GET', 'POST'])
def login_result():
    if request.method == 'POST':
        error = None

        db = pymysql.connect(host=mysql_host,port=mysql_port, user=mysql_user, password=mysql_password, db=mysql_db, charset='utf8')

        id = request.form['id']
        pw = request.form['pw']

        cursor = db.cursor()

        sql = "SELECT id FROM login_table WHERE id = %s AND pw = %s"
        value = (id, pw)

        cursor.execute(sql, value)

        data = cursor.fetchone()
        db.commit()
        db.close()

        if data:
            session['login_user'] = data[0]
            return render_template("index.html", user_id=data[0])
        else:
            error = 'invalid input data detected !'
            return render_template("error.html", error=error)

    return render_template("login.html")

 

 

(register_module.py)

  • import os 추가
    • MySQL 데이터베이스에 연결하기 위한 정보를 환경 변수에서 가져오는데 사용
  • os 모듈을 사용하여 환경 변수를 읽어와 MySQL 데이터베이스에 접속하기 위한 정보를 설정
from flask import Flask, session, render_template, redirect, request, url_for, Blueprint
import pymysql
import os

mysql_host = os.environ.get('MYSQL_HOST', 'mysql')
mysql_port= int(os.environ.get('MYSQL_PORT', 3306))
mysql_user =  os.environ.get('MYSQL_USER', 'root')
mysql_password = os.environ.get('MYSQL_PASSWORD', 'password')
mysql_db = os.environ.get('MYSQL_DB', 'petclinic')

register_module = Blueprint("register_module", __name__)

@register_module.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

@register_module.route("/register", methods=['GET', 'POST'])
def register_result():
    if request.method == 'POST':
        db = pymysql.connect(host=mysql_host,port=mysql_port, user=mysql_user, password=mysql_password, db=mysql_db, charset='utf8')

        id = request.form['id']
        pw = request.form['pw']

        cursor = db.cursor()

        cursor.execute("SELECT * FROM login_table WHERE id = %s", (id,))
        data = cursor.fetchone()

        if data is None:
            cursor.execute("INSERT INTO login_table (id, pw) VALUES (%s, %s)", (id, pw))
            db.commit()
            db.close()
            return render_template("register_success.html")
        else:
            return render_template("register_fail.html")

    return render_template("register.html")

MySQL, POD 연동 방식

  1. db-storageclass.yaml:StorageClass는 PersistentVolume의 동적 프로비저닝을 위한 설정을 제공 즉, 필요에 따라 자동으로 PersistentVolume을 생성
  2. db-pv.yaml: MySQL 데이터베이스가 데이터를 영구적으로 저장하기 위한 공간을 제공
  3. db-statefulset.yaml: StatefulSet은 네트워크 식별자와 저장소를 유지하면서 Pod를 실행하는데 사용, 이 설정을 통해 MySQL 데이터베이스가 실행
  4. db-service.yaml: Service는 MySQL 데이터베이스에 접근하는 데 사용, Flask 애플리케이션의 Pod는 이 Service를 통해 데이터베이스와 통신
  5. flask-deployment.yaml: Flask 애플리케이션의 Pod를 생성, 이 Pod는 login_module.py와 register_module.py에 정의된 로직을 실행
  6. login_module.py 및 register_module.py: MySQL 데이터베이스와 연결하여 사용자의 로그인 및 회원 가입 요청을 처리

모든 yaml 파일을 apply 시키고 모든 리소스를 조회

파드가 정상적으로 작동되는 것을 확인할 수 있었다.

'mysql' 서비스는 각 노드의 31278 포트를 통해 클러스터 외부에서 접근할 수 있다.

StatefulSet 역시 정상적으로 작동 중인 것을 확인


DB 정상 동작 확인

table 생성 확인

table 조회 명령어

SHOW TABLES;

table안의 값

SELECT * FROM login_table;

회원 가입 기능 동작 확인

id=kim, pw=123 지정

DB확인

정상 로그인 확인