Kubernetes에 대해 알아보자 2 - Ingress

제훈·2024년 10월 14일

DevOps

목록 보기
5/16

Ingress

이전에는 frontend, backend 를 vue 와 springboot를 활용해서

각각 dev, ser 컨테이너를 쿠버네티스를 활용해서 manifest 형태로 정의한 뒤 적용을 해뒀다.

그 상태에서 게이트 웨이와 비슷한 역할을 하는 Kuberenetes Ingress에 대해 알아볼 것이다.

Kubernetes Ingress : 클러스터 외부에서 내부 서비스로의 HTTP 및 HTTPS 트래픽을 관리하는 API 객체

로드밸런싱, SSL 종단, 이름 기반의 가상 호스팅 제공, 복잡한 라우팅 규칙을 정의하는 역할로 단일 IP의 단일 진입점 역할을 하고 URL 경로나 호스트 이름을 기반으로 적절한 서비스로 라우팅한다.

예전에 적용해봤던 게이트웨이와 비슷한 역할을 하는 것 같다.

그럼 Ingress 관련 파일을 작성해보자.


Ingress 용 파일

우선 Vue 프로젝트 파일을 선택한 뒤 터미널을 열자.

npm run build 입력 시 정적 리소스로 현재 vue 프로젝트에 관한 js, css 파일을 담는다.

  • dist 폴더에서 확인 가능하다.

그 다음 아래 코드를 보자.
전체적으로는 Docker.file을 수정하여 이후 docker image를 만들어 docker hub에 push까지 진행할 것이다.

우선 Docker.file을 수정해보자.

# FROM node:lts-alpine

# # curl 설치(client-url): 필수는 아님(postman 같은 것)
# RUN apk add --no-cache curl

# WORKDIR /app
# COPY . ./
# RUN npm install

# # 컨테이너가 시작될 때 실행할 명령어를 지정한다.
# # npm run dev: Vite 개발 서버를 실행한다.
# # --: npm 명령어의 옵션과 Vite 명령어의 옵션을 구분한다.
# # --host 0.0.0.0: Vite 서버를 모든 네트워크 인터페이스에서 접근 가능하도록 설정한다.
# #                 이는 Docker 컨테이너 외부에서 애플리케이션에 접근할 수 있게 한다.
# CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]

FROM node:lts-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# nginx 이미지를 사용하여 프로덕션 스테이지를 구성한다.
FROM nginx:stable-alpine AS production-stage
# 빌드 스테이지에서 생성된 정적 파일들을 Nginx의 기본 웹 서버 디렉토리로 복사한다.
# 이렇게 하면 Nginx가 Vue.js 애플리케이션의 빌드된 파일들을 서빙할 수 있게 된다.
COPY --from=build-stage /app/dist /usr/share/nginx/html

# 로컬 디렉토리의 nginx.conf 파일을 Nginx의 설정 디렉토리로 복사한다.
# 이 설정 파일은 Nginx의 동작을 커스터마이즈하는데 사용된다.
# 예를 들어, 라우팅 규칙, SSL 설정, 로깅 등을 정의할 수 있다.
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Vue 프로젝트에 있던 도커 파일을 수정한다.

그리고 위의 파일을 보면 nginx.conf 도 작성해줘야한다는 것을 볼 수 있는데 Kubernetes 클러스터에서 Nginx 컨테이너는 Pod의 일부로 실행되며 build 된 vue의 정적 파일을 배포하는 웹서버 역할을 하고 있다.

nginx.conf 파일도 vue 프로젝트에 추가

# Nginx 서버 설정 블록
server {
    # 80번 포트에서 HTTP 요청을 수신
    listen 80;
    # 서버 이름을 localhost로 설정
    server_name localhost;
    
    # 모든 요청 처리
    location / {
        # '/usr/share/nginx/html/' 디렉토리를 루트로 설정
        root /usr/share/nginx/html/;
        # 요청된 URI에 해당하는 파일을 찾고, 없으면 '/index.html'로 폴백
        try_files $uri $uri/ /index.html;
    }
}

그럼 기존에 했던 5173이 아닌, 80 포트에서 HTTP 요청을 수신하는 것을 알 수 있어서 새로운 dep, ser가 필요하다.

추가해준 뒤 변동 사항이 생겼으니 docker build -t jojehuni/nine_v_proj . 을 해줘야 한다. (근데 어차피 아래에서 App.vue를 수정할 것이라서 또 해줘야 한다.)

위의 명령을 터미널 창에서 해주면 npm num build 또한 해주는데
그 때는 vue 프로젝트를 각각 하나의 js 파일, css 파일로 uglyfy 형태로 바꿔서 프로젝트에 대한 정보를 백엔드에서 jar 파일 만들듯이 dist 폴더 속에 만들어준다.

그 내용도 COPY 명령어에 있으니 확인할 수 있을 것이다.


vue002dep.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vue002dep
spec:
  selector:
    matchLabels:
      app: vue002kube
  template:
    metadata:
      labels:
        app: vue002kube
    spec:
      containers:
      - name: vue-container
        image: jojehuni/nine_v_proj:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 80      # 5173이 아니라 80으로 바꿔야 한다.

vue002ser.yml

apiVersion: v1
kind: Service
metadata:
  name: vue002ser
spec:
  type: ClusterIP
  ports:
  - port: 8000
    targetPort: 80      # 5173이 아니라 80으로 바꿔야 한다.
  selector:
    app: vue002kube

Vue 프로젝트에 002 버전으로 만들었으니 Spring 프로젝트에도 필요할 것이다.

boot002dep.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: boot002dep
spec:
  selector:
    matchLabels:
      app: boot002kube
  # replicas: 3
  replicas: 1
  template:
    metadata:
      labels:
        app: boot002kube
    spec:
      containers:
      - name: boot-container
        image: jojehuni/nine_b_proj:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 7777

boot002ser.yml

apiVersion: v1
kind: Service
metadata:
  name: boot002ser
spec:
  type: ClusterIP
  ports:
  - port: 8001
    targetPort: 7777
  selector:
    app: boot002kube

ingress02.yml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sw-camp-ingress
  annotations:
    # 기본적으로 NGINX Ingress 컨트롤러는 모든 HTTP 트래픽을 HTTPS로 리다이렉트한다.
    # 이 설정을 "false"로 지정하면 HTTP 요청을 HTTPS로 자동 리다이렉트 하지 않는다.
    # 개발 환경이나 SSL/TLS가 필요 없는 상황에서 유용하다.
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    # 매칭된 경로에서 /boot 부분을 제거하고 나머지 부분만 백엔드 서비스로 전달한다.
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  # 사용할 Ingress 컨트롤러의 클래스를 지정. 여기서는 NGINX Ingress 컨트롤러를 사용하도록 설정
  ingressClassName: nginx  
  rules:
  - http:
      paths:
      - path: /()(.*)
        # rewriter와 관련된 사이트 주소: https://kubernetes.github.io/ingress-nginx/examples/rewrite/
        # ImplementationSpecific은 Ingress 컨트롤러의 구현에 따라 경로 매칭 방식이 결정된다.
        # NGINX Ingress Controller의 경우, 정규 표현식을 포함한 더 유연한 라우팅 규칙을 적용할 수 있다.
        # 이는 Prefix나 Exact보다 더 복잡한 경로 매칭을 가능하게 한다.
        # 예: /, /about, /users 등
        pathType: ImplementationSpecific
        backend:
          service:
            name: vue002ser
            port: 
              number: 8000
      - path: /boot(/|$)(.*)
        # 여기서도 ImplementationSpecific을 사용하여 /boot로 시작하는 모든 경로를 매칭한다.
        # (/|$)는 /boot 다음에 /가 오거나 문자열이 끝나는 경우를 모두 포함한다.
        # (.*)는 그 뒤의 모든 문자열을 캡처한다.
        # 예: /boot, /boot/, /boot/plus, /boot/users 등
        pathType: ImplementationSpecific
        backend:
          service:
            name: boot002ser
            port: 
              number: 8001

폴더 구조는 아래 이미지와 같다.


Ingress 설정 후 기존 코드와 달라져야 할 부분

ingress 설정 후에는 프론트의 워커노드를 통해서 접근하는 것이 아니라 클러스터 내부에서의 Vue 서비스 접근을 통해서 접근하게 된다.
따라서 CORS 처리를 하지 않아도 된다.

그 외에도 이런 방식을 사용하는 이유

  1. 유연성: 전체 URL을 하드코딩하는 대신 다른 도메인이나 환경(개발, 스테이징, 프로덕션)에서 실행될 때 쉽게 적응
  2. 보안: 전체 URL을 노출하지 않음으로써, 서버의 실제 주소가 숨겨짐
  3. 리버스 프록시 호환성: 프록시(요청을 적절한 백엔드 서버로 라우팅) 뒤에서 실행될 때 유용
  4. Ingress 설정과의 호환성: K8S의 Ingress나 다른 유사한 시스템에서는 특정 경로(/boot)를 특정 서비스로 라우팅하도록 설정할 수 있음
  5. 환경 독립성: 프론트엔드 코드가 특정 백엔드 URL에 의존하지 않아 다양한 환경에서 동일한 코드를 사용할 수 있음

그래서 프론트엔드에서 백엔드의 워커노드로 요청하는 부분을 수정할 것이다.
즉, 이전에 했던 30001 포트가 아닌 절대 경로로 통신한다.

App.vue

<template>
  <div class="plus">
    <h1>덧셈 기능 만들기</h1>
    <label>num1: </label><input type="text" v-model="num1">&nbsp;
    <label>num2: </label><input type="text" v-model="num2">&nbsp;
    <button @click="sendPlus">더하기</button>
    <hr>
    <p>`{{ num1 }} + {{ num2 }} = {{  result }}`</p>
  </div>
</template>

<script setup>
  import { ref } from 'vue';

  const num1 = ref(0);
  const num2 = ref(0);
  const result = ref(0);

  const sendPlus = async() => {
    /* Ingress 적용 이전 30001번의 백엔드 워커노드와 통신 */
    // const response = await fetch(`http://localhost:30001/plus?num1=${num1.value}&num2=${num2.value}`);

    /* Ingress를 활용한 절대 경로로 통신 */
    const response = await fetch(`/boot/plus?num1=${num1.value}&num2=${num2.value}`);
    
    const data = await response.json();
    result.value = data.sum;
  }

</script>

<style scoped>
  div.plus {
    align-items: center;
  }
</style>

위에서 말한대로 docker build -t jojehuni/nine_v_proj . 한 번 더 하자. (이번에 한 번만 해도 되긴 한다.)


백엔드 프로젝트도 WebConfig 수정한 뒤 build를 다시 하자.
WebConfig

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
//                .allowedOrigins("http://localhost:5173")

				// Ingress 적용 이전 프론트 워커노드 포트에 대한 CORS 처리
//                .allowedOrigins("http://localhost:30000")
                
                // Ingress 적용 이후 CORS 불필요로 인한 경로 제거
                .allowedOrigins()
                .allowedMethods("GET", "POST", "PUT", "DELETE");
    }
}

해당 백엔드 프로젝트의 도커 파일이 존재하는 경로로 온 뒤 터미널을 열고

  • docker build -t jojehuni/nine_b_proj . 실행

이후

  • docker push jojehuni/nine_v_proj
  • docker push jojehuni/nine_b_proj
    2가지 실행

Ingress 활용

Nginx 를 위한 Ingress 컨트롤러 설치 및 적용 코드

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml

위의 코드를 백엔드 프로젝트, 프론트엔드 프로젝트가 둘다 있는 곳에서 터미널을 열어서 한다.

kubectl get ingressclass : 현재 클러스터에 설정된 Ingress 클래스 확인 명령어

다음은 Manifest 파일들이 있는 곳으로 가서 리소스를 배포한다.

PS C:\lecture\14_devops\chap02_k8s_manifests\section02> kubectl apply -f ingress002.yml

이제 나머지 4개 dev, ser도 해주면 된다.

kubectl apply -f vue002ser.yml
kubectl apply -f boot002ser.yml
kubectl apply -f vue002dep.yml
kubectl apply -f boot002dep.yml

적용한 뒤 kubectl get ingress 를 했을 때 변화도 확인 가능하다.

이제 브라우저에서도 확인이 가능하다.

localhost:80 port로 해뒀는데 그냥 localhost만 적어도 되는 이유는 기본 포트가 80이기 때문이다.

CORS도 필요 없어졌고, Ingress라는 단일 진입점이 생겨서 좋아진 것을 알 수 있다.
하지만 nginx에 대해 알고서 적용하는 것이 좋아서 단순하게만 생각하지는 말자.


이후에는 Jenkins를 통해서 도커 허브에 자동으로 업데이트되게끔도 해보자.

profile
백엔드 개발자 꿈나무

0개의 댓글