Profile picture

[k8s] 쿠버네티스로 Django 웹 서비스 배포하기

JaehyoJJAng2023년 06월 10일

▶︎ 개요

쿠버네티스로 간단한 Django 웹 서비스를 배포하는 과정을 기록.


▶︎ 프로젝트 구조

Django 개발 서버 디렉토리 구조

├── django
│   ├── Dockerfile
│   ├── manage.py
│   ├── myapp
│   │   ├── asgi.py
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   └── requirements.txt
├── docker-compose.yaml
└── nginx
    ├── conf
    │   └── default.conf
    └── Dockerfile

k8s 배포 서버 디렉토리 구조

├── create_secret.sh
├── django.yaml
├── ingress.yaml
├── nginx.yaml
├── postgres.yaml
└── README.md

▶︎ 사전 준비

  • 외부로 배포하기 위해 Nginx Ingress Controller & metalLB 설치 필요

postgresql DB 접속을 위해 django의 settings.py을 아래와 같이 수정

...

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('POSTGRES_DB','postgres'),
        'USER': os.getenv('POSTGRES_USER','postgres'),
        'PASSWORD': os.getenv('POSTGRES_PASSWORD','postgres'),
        'HOST': os.getenv('POSTGRES_HOST','postgres-service'),
        'PORT': int(os.getenv('POSTGRES_PORT',5432))
    }
}

...

▶︎ 프로젝트 생성

  • 해당 섹션의 하위 작업들은 모두 배포 서버(k8s)가 아닌 개발 서버에서 진행

Django 프로젝트 생성

# 가상환경 활성화
pyenv virtualenv 3.11.6 py3_11_6
pyenv activate py3_11_6

# Django 설치
pip install django

# 장고 프로젝트 생성
django-admin startproject myapp .

‣ 도커 이미지 생성 & 빌드

  • django와 nginx 이미지만 빌드하여 도커 허브에 배포
  • postgresql은 빌드된 이미지 없이 순정 이미지를 pull 받아서 띄울 것임.

• django

Dockerfile 작성

FROM python:3.11.6

ENV PYTHONDONTWRITEBYTECODE 1
WORKDIR /usr/src/app

COPY ./requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY . .

EXPOSE 8888

CMD ["gunicorn", "--bind", "0.0.0.0:8888", "myapp.wsgi:application"]

• nginx

Dockerfile 작성

FROM nginx:latest
COPY conf/default.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]

nginx/nginx.conf nginx 설정 파일 작성

upstream myweb {
    server django-service; # 장고 서비스명 지정
}

server {
    listen 80;
    server_name myweb;

    location / {
        proxy_pass http://myweb;
    }
}

• 이미지 빌드

django build & push

docker build --tag xxxx/k8s-django .

nginx build & push

docker build --tag xxxx/k8s-nginx .

▶︎ 서비스 배포

  • 해당 섹션의 하위 작업들은 쿠버네티스 마스터 노드에서 진행

‣ secret 생성

create_secret.sh

#!/usr/bin/bash

kubectl create secret generic postgres-secret \
	--from-literal=POSTGRES_DB=postgres \
	--from-literal=POSTGRES_USER=postgres \
	--from-literal=POSTGRES_PASSWORD=postgres \
	--from-literal=POSTGRES_HOST=postgres-service \
	--from-literal=POSTGRES_PORT=5432
# 실행권한 부여
chmod u+x create_secret.sh

# 스크립트 실행
bash create_secret.sh

‣ Django app 생성

django.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: django-deploy
spec:
  replicas: 3
  selector:
    matchLabels: 
      app: django
  template:
    metadata:
      labels:
        app: django
    spec:
      containers:
        - name: django
          image: yshrim12/k8s-django:latest
          ports:
            - containerPort: 8888
          env:
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_DB
            - name: POSTGRES_HOST
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_HOST
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PASSWORD
            - name: POSTGRES_PORT
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PORT
---
apiVersion: v1
kind: Service
metadata:
  name: django-service
spec:
  selector:
    app: django
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8888
  • 특이사항
    • env 속성에 생성한 secret의 키를 매핑하였음.
    • Django 이미지에서 EXPOSE된 포트가 8888 이므로 컨테이너의 targetPort 속성의 값을 8888로 지정하였음.
    • 서비스의 targetPort또한 8888로 지정.

‣ nginx app 생성

nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: yshrim12/k8s-nginx
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

‣ postgresql app 생성

postgres.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15.4
          env:
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_DB
            - name: POSTGRES_HOST
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_HOST
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PASSWORD
            - name: POSTGRES_PORT
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PORT
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
spec:
  selector:
    app: postgres
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432
  • 특이사항
    • env 속성에 생성한 secret의 키를 매핑하였음.

‣ ingress 생성

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - http:
        paths:
          - path: /django(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 80
  • 특이사항
    • /django 경로의 하위 경로까지 인식되도록 (/|$)(.*)를 추가로 작성하였음.

‣ 배포

kubectl apply -f .

외부 접속 테스트를 위해 nginx-ingress-controller의 EXTERNAL-IP 조회

kubectl get svc/nginx-ingress-controller-1715592941 -n custom-nginx

# output
NAME                                  TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                      AGE
nginx-ingress-controller-1715592941   LoadBalancer   10.96.31.137   192.168.219.40   80:30320/TCP,443:30629/TCP   27h

브라우저에 http://192.168.219.40/django 접속하여 정상적으로 접속 되는지 테스트
image
테스트 완료.


postgresql migrate 테스트

# 파드 조회 후 kubectl로 migrate 테스트
$ kubectl get pods -o name  | grep 'django' \
| xargs -I{} kubectl exec {} -- python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  No migrations to apply.
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  No migrations to apply.
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  No migrations to apply.

Loading script...