Dev./Kubernetes & Helm

Kubernetes: run application via nks

Ivan'show 2023. 9. 24.
728x90
반응형

lion-app K8S

lion-app 을 쿠바네티스로 구동시켜보자.

테스트로 사용하기 위해 기존에 사용하던 도커파일을 sqlite db 와 연결하게 끔 새롭게 만든다.

App

# test 를 진행하기위해 allowed host 에 와일드 카드 추가
ALLOWED_HOSTS = [
    "localhost",
    "*",
    "127.0.0.1",
    # LOCAL_IP,
]
FROM python:3.11-alpine
LABEL likelion.web.backendauthor="Ivan kim <xormrdlsrks2@gmail.com>"

ARG APP_HOME=/app

ENV PYTHONUNBUFFERED 1

ENV PYTHONDONTWRITEBYTECODE 1

WORKDIR ${APP_HOME}

COPY ./requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . ${APP_HOME}

COPY ./scripts/start /start
RUN sed -i 's/\\r$//g' /start
RUN chmod +x /start

CMD [ "/start" ]

이후 NCP 에 이미지 파일을 올리는데, MacOS 유저라 멀티 플랫폼 형식으로 올린다.

docker build -t likelion-cr-mh.kr.ncr.ntruss.com/lion-app:sqlite -f Dockerfile.test --platform linux/arm64 --platform linux/amd64 .
# docker build 옵션중 멀티 플랫폼을 위한 옵션
--platform linux/arm64 --platform linux/amd64
docker push likelion-cr-mh.kr.ncr.ntruss.com/lion-app:sqlite

도커 이미지를 만들고 sqlite 라는 태그를 달아 latest 와 구분지어 준다.

K8S

이후 k8s 부분으로 넘어와서 해당 이미지를 pull 받아 파드를 생성하는 파일을 만든다.

# lion-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: lion-app
  labels:
    app: lion-app
spec:
  imagePullSecrets:
  - name: regcred
  containers:
    - name: lion-app
      image: likelion-cr-mh.kr.ncr.ntruss.com/lion-app:sqlite
      ports: 
        - containerPort: 8000
k create -f lion-pod.yaml

파드의 로그를 살펴보면 환경변수가 지정되어 있지 않아 에러가 발생하는 것을 확인할 수 있다.

kimminhyeok@Ivans-Mac lion-app % k logs lion-app

160 static files copied to '/var/www/html/static'.
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 289, in ensure_connection
    self.connect()
  File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 270, in connect
    self.connection = self.get_new_connection(conn_params)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
...

File "/usr/local/lib/python3.11/site-packages/psycopg2/__init__.py", line 122, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.OperationalError: could not translate host name "db" to address: Name does not resolve

이를 해결하기 위해 env 설정을 추가한다.

# lion-pod-env.yaml
apiVersion: v1
kind: Pod
metadata:
  name: lion-app
  labels:
    app: lion-app
spec:
  imagePullSecrets:
  - name: regcred
  containers:
    - name: lion-app
      image: likelion-cr-mh.kr.ncr.ntruss.com/lion-app:sqlite
      env: 
        - name: DJANGO_SETTINGS_MODULE
          value: "lion_app.settings.test"
        - name: DJANGO_SECRET_KEY
          value: "DJANGO_SECRET_KEY"

      ports: 
        - containerPort: 8000
# 파드 생성 후
k logs lion-app
kimminhyeok@Ivans-Mac lion-app % k logs lion-app

160 static files copied to '/var/www/html/static'.
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, forumapp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying forumapp.0001_initial... OK
  Applying forumapp.0002_alter_post_topic... OK
  Applying forumapp.0003_topicgroupuser... OK
  Applying forumapp.0004_post_image_url_alter_topicgroupuser_topic... OK
  Applying forumapp.0005_alter_post_image_url... OK
  Applying sessions.0001_initial... OK
[2023-09-06 01:22:03 +0000] [18] [INFO] Starting gunicorn 21.2.0
[2023-09-06 01:22:03 +0000] [18] [INFO] Listening at: <http://0.0.0.0:8000> (18)
[2023-09-06 01:22:03 +0000] [18] [INFO] Using worker: sync
[2023-09-06 01:22:03 +0000] [21] [INFO] Booting worker with pid: 21
[2023-09-06 01:22:03 +0000] [23] [INFO] Booting worker with pid: 23
[2023-09-06 01:22:03 +0000] [25] [INFO] Booting worker with pid: 25
[2023-09-06 01:22:03 +0000] [27] [INFO] Booting worker with pid: 27
[2023-09-06 01:22:03 +0000] [29] [INFO] Booting worker with pid: 29
[2023-09-06 01:22:03 +0000] [31] [INFO] Booting worker with pid: 31
[2023-09-06 01:22:03 +0000] [33] [INFO] Booting worker with pid: 33
[2023-09-06 01:22:03 +0000] [35] [INFO] Booting worker with pid: 35
[2023-09-06 01:22:03 +0000] [37] [INFO] Booting worker with pid: 37

마이그레이션까지 잘 동작하는 것을 확인 할 수 있다.

파드를 생성했으니 svc 와 ingress 를 연결하여 외부에서도 접근할 수 있게 한다.

# lion-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: lion-svc-nodeport
spec:
  type: NodePort
  selector: # app 으로 레이블을 달아서 서비스가 해당 레이블 정보를 가진 파드를 관리할 수 있게 한다.
    app: lion-app
  ports:
      # 기본적으로 그리고 편의상 `targetPort` 는 `port` 필드와 동일한 값으로 설정된다.
    - port: 80
      targetPort: 8000
k creaet -f lion-svc-nodeport.yaml
k get svc
# result

kimminhyeok@Ivans-Mac lion_app % k get svc
NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
fortune-configmap   NodePort    10.104.173.25    <none>        80:30383/TCP   18h
kubernetes          ClusterIP   10.96.0.1        <none>        443/TCP        4d21h
lion-svc-nodeport   NodePort    10.102.165.204   <none>        80:32353/TCP   5m51

파드의 레이블과 서비스의 셀렉터의 정보값을 일치시켜 서비스가 파드를 관리할 수 있도록 한다.

k label po lion-app app=lion-app

연결이 잘 되었는지 확인하기 위해

파드로 접속한뒤 curl 을 서비스 주소로 날려본다.

해당 이미지는 알파인 이기 때문에 쉘로 접속 후 curl 을 설치해준다.

k exec -it lion-app -- sh
apk add curl
# result

kimminhyeok@Ivans-Mac lion-app % k exec -it lion-app -- sh
/app # apk add curl
fetch <https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz>
fetch <https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz>
(1/6) Installing brotli-libs (1.0.9-r14)
(2/6) Installing libunistring (1.1-r1)
(3/6) Installing libidn2 (2.3.4-r1)
(4/6) Installing nghttp2-libs (1.55.1-r0)
(5/6) Installing libcurl (8.2.1-r0)
(6/6) Installing curl (8.2.1-r0)
Executing busybox-1.36.1-r2.trigger
OK: 19 MiB in 44 packages
# curl with out code info
curl -I <http://10.244.0.248/api/docs>

# api/docs/ 는 다른게 필요없이 바로 생성되고 넘어가니까 200 을 반환 할 것이다.
# result
HTTP/1.1 200 OK
Server: gunicorn
Date: Wed, 06 Sep 2023 02:15:23 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Allow: GET, HEAD, OPTION
...
...
...

Django 서버에서 /health/ 요청시 특정 값을 리턴하는 미들웨어 생성하고 replicaset 을 이용해서 파드 생성 및 조회 검증

# lion_app/common/middleware.py
from django.http import JsonResponse

class HealthcheckMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.path == "/health/":
            return JsonResponse({"status": "ok"})

        return self.get_response(request)  # 그 다음 미들웨어로 보낸다...
docker build ...
docker push ...
# lion-rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: lion-app
  labels:
    app: lion-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lion-app
  template:
    metadata:
      labels:
        app: lion-app
    spec:
      imagePullSecrets:
      - name: regcred
      containers:
      - name: lion-app
        image: likelion-cr-mh.kr.ncr.ntruss.com/lion-app:sqlite
        imagePullPolicy: Always # 기존의 이미지를 쓰지않고 항상 가져오게 끔
        env: 
        - name: DJANGO_SETTINGS_MODULE
          value: lion_app.settings.test
        - name: DJANGO_SECRET_KEY
          value: "DJANGO_SECRET_KEY"

        ports:
          - containerPort: 8000
k create -f lion-rs.yaml
k exec -it ${pod_name} -- sh
apk add curl
curl <http://10.102.165.204/health/>

# result
/app # curl <http://10.102.165.204/health/>
{"status": "ok"}
k describe po ${pod_name}
# result
kimminhyeok@Ivans-Mac lion-app % k describe po lion-app-zblkm
Name:             lion-app-zblkm
Namespace:        default
Priority:         0
Service Account:  default
Node:             minikube/192.168.49.2
Start Time:       Wed, 06 Sep 2023 14:31:15 +0900
Labels:           app=lion-app
Annotations:      <none>
Status:           Running
IP:               10.244.1.18
IPs:
  IP:           10.244.1.18
Controlled By:  ReplicaSet/lion-app
Containers:
  lion-app:
    Container ID:   docker://24cb9f53e060ec56556a946057bc787bb20cbe5c8f1e361697847afd1a3d12e6
    Image:          likelion-cr-mh.kr.ncr.ntruss.com/lion-app:sqlite
    Image ID:       docker-pullable://likelion-cr-mh.kr.ncr.ntruss.com/lion-app@sha256:5dd01b56334854fedcf611a9903c61f73422d4f95ece42836f9013cabb16f9c0
    Port:           8000/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 06 Sep 2023 14:31:16 +0900
    Ready:          True
    Restart Count:  0
    Liveness:       http-get http://:8000/health/ delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:
      DJANGO_SETTINGS_MODULE:  lion_app.settings.test
      DJANGO_SECRET_KEY:       eykjzm7ro(ewbug#3r%s64xeygef6q_k!cb+ns_vexsza^9n--
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-hw88k (ro)
Conditions:
  Type              Status
  Initialized       True
728x90
반응형

'Dev. > Kubernetes & Helm' 카테고리의 다른 글

Kubernetes: nks 에서 서비스 띄우기  (4) 2023.09.26
Kubernetes: nks IAM 인증  (0) 2023.09.25
Kubernetes: ConfigMap  (0) 2023.09.18
Kubernetes: svc - pod 구조 이해  (0) 2023.09.17
Kubernetes: 서비스, 파드의 관리자  (0) 2023.09.16

댓글