현대 오토에버 클라우드 스쿨

Kubernetes Service, AWS EC2

Gom3rye 2025. 7. 22. 17:33
728x90
반응형

Load Balancer

  • 로드 밸런싱을 해주는 쿠버네티스 서비스
    • 참고) Cluster IP도 로드 밸런싱을 해준다. (애플리케이션의 포트, 클러스터 ip의 포트)
      • 클러스터 내부에서 다른 파드에 접근하고자 할 때 사용한다.
      • 실제 IP보다는 서비스 이름으로 접근하는 것을 권장한다.(서비스 디스커버리로 이동 권장)
      • 실제 애플리케이션은 컨테이너, 파드는 배포 단위
        • IP가 파드 단위로 부여되니까 (도커랑은 다른 점, 도커는 컨테이너 단위로 ip부여된다.)
        • 파드의 IP는 고정이 아니니까 파드의 IP로는 통신할 수 없다. → 그러면 파드를 가리키는 고정된 IP가 필요한데 그 IP가 클러스터 IP이다. ⇒ 클러스터 IP는 Private IP이고 이걸 Flannel이 부여하는 것, 따라서 Flannel이 없으면 파드끼리 통신을 할 수 없다. (그래서 Flannel을 init 하고 깐 것)
        • 근데 그 클러스터 IP도 일반적으로는 고정되지만 서비스가 삭제되었다가 다시 생성되면 변경될 수 있다. → IP 주소 대신 서비스 이름(서비스 디스커버리)을 사용하여 통신해야 한다.
    • 참고) Node port : 외부에서 Node의 public ip를 통해서 pod에 접근하기 위해서 생성되는 것, Cluster IP가 자동으로 생성된다. (외부에서 통신이 가능하면 내부도 당연히 통신이 되야 하니까) (애플리케이션 포트, 클러스터 ip의 포트, 노드 포트)
    • 헬름으로 yaml파일 여러 개를 묶어서 배포할 수 있다.
    • 하나의 pod에 있는 앱들은 localhost로 서로 포트만 바꾸면 통신할 수 있다.
    • 참고) Ingress vs. LB
      • Ingress는 pod1, pod2를 갈 수 있고 (path 기반, host기반) 안에 놔도 되고 밖에 놔도 된다. (하나의 진입점으로 들어오는 요청을 Pod1, Pod2 등 다양한 서비스로 라우팅할 수 있다.)
      • Ingress = ALB (application load balancer)
      • LB는 밖에 놔서 pod1끼리에만 가서 부하 분산 (안에 놓을 거면 cluster ip 쓰면 되니까)
      • LB = NLB (network load balancer)
    • 순서 중요) cluster ip → node port → load balancer → ingress

  항목                                Ingress                                                                   LoadBalancer (서비스)

역할 HTTP(S) 트래픽을 라우팅 (L7, 애플리케이션 계층) 외부에서 내부로 트래픽 전달 (L4, 네트워크 계층)
라우팅 대상 여러 서비스(Pod1, Pod2, ...) 단일 서비스(Pod1)
부하 분산 대상 여러 서비스로 조건부 라우팅 가능 해당 서비스의 Pod들만 부하 분산
경로 기반 라우팅 가능 (예: /api, /web) 불가능
도메인 기반 라우팅 가능 (예: api.example.com) 불가능
공개 IP 일반적으로 Ingress Controller 앞에 LB가 있음 서비스에 직접 퍼블릭 IP 할당
비용 상대적으로 적음 (LB 1개로 여러 서비스 핸들링) 서비스마다 LB 비용 발생
  • Metal LB
    • public cloud의 managed kubernetes service가 아닌 곳에서 사용할 수 있는 load balancer
    • 사용 절차
      • MetalLB를 k8s에 설치 (Manifest 방식)
      • LoadBalancer 서비스에 할당할 IP 주소 범위 지정
      • tyep: LoadBalancer 서비스 생성 시 자동으로 외부 IP 부여
    • 설치: kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml
      • pod가 4개(speaker3개, controller1개)만들어지고 service, daemonset, deployment, replicaset 만들어진다.
    • 확인: kubectl get all -n metallb-system
    • MetalLB IP 대역 설정
      • YAML로 (or 명령어로 해도 된다.)
      # metallb-config.yaml
      apiVersion: metallb.io/v1beta1
      kind: IPAddressPool
      metadata:
       name: my-ip-pool
       namespace: metallb-system
      spec:
       addresses:
       - 192.168.56.240-192.168.56.250
      ---
      apiVersion: metallb.io/v1beta1
      kind: L2Advertisement
      metadata:
       name: l2adv
       namespace: metallb-system
      
    • 모드 및 서비스 대역 지정 확인: kubectl get l2advertisements -n metallb-system
    • LoadBalancer 리소스 파일 생성
    apiVersion: v1
    kind: Service
    metadata:
     name: sample-lb
    spec:
     type: LoadBalancer
     ports:
     - name: "http-port"
     protocol: "TCP"
     port: 8080
     targetPort: 80
     nodePort: 30082
     selector:
     app: sample-app
    
    • 서비스 생성 후 실행: kubectl apply -f sample-lb.yaml
    • 리소스 확인: kubectl get svc
    • External IP로 확인: curl 192.168.56.240:8080

nginx ingress 실습

클러스터 내부의 Ingress

# 클러스터 내부에 인그레스용 파드를 배포하는 인그레스 설치
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/cloud/deploy.yaml
## nginx ingress controller가 설치되면서 k8s object들이 배포됨(pod, service, delpoyment, replicaset, job.batch)

# 설치 확인
kubectl get all -n ingress-nginx

# Ingress 생성 및 확인- Deployment 생성(deploy-test.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service-test
spec:
  replicas: 3
  selector:
    matchLabels:
      app: service-test-pod
  template:
    metadata:
      labels:
        app: service-test-pod
    spec:
      containers:
        - name: simple-http
          image: python:2.7
          imagePullPolicy: IfNotPresent
          command: ["/bin/bash"]
          args:
            - -c
            - echo '<p>Hello from $(hostname)</p>' > index.html && python -m SimpleHTTPServer 8080
          ports:
            - name: http
              containerPort: 8080
              
# Service 생성(svc-test.yaml)
apiVersion: v1
kind: Service
metadata:
  name: service-test
spec:
  selector:
    app: service-test-pod 
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

# 서비스 잘 만들어졌는지 확인
kubectl get svc
## service-test   ClusterIP      10.109.235.107   <none>     80/TCP     39s

# Ingress 생성(ingress-test.yaml)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: "ingress.test.com"
      http:
        paths:
          - pathType: Prefix
            path: /test
            backend:
              service:
                name: service-test
                port:
                  number: 80
                  
# 디플로이먼트를 위한 서비스 생성
kubectl apply -f ingress-test.yaml

# 로컬 PC에 URL과 Ingress 서비스의 EXTERNAL-IP 연결
kubectl get svc -n ingress-nginx
## ingress-nginx-controller  LoadBalancer   10.104.241.51    192.168.56.241   80:31530/TCP,443:31238/TCP   47
## 여기서 EXTERNAL-IP에 해당하는 192.168.56.241 을 적어준다.
sudo vi /etc/hosts

192.168.56.241 ingress.test.com

# Ingress 확인
curl http://ingress.test.com/test
## <p>Hello from $(hostname)</p>

 

호스트 기반 NGINX Ingress Controller 라우팅 설정(ingress-test.yaml)

# Ingress Controller를 이용한 배포 (ingress-resource.yaml)
# backend1 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend1
  template:
    metadata:
      labels:
        app: backend1
    spec:
      containers:
        - name: backend1
          image: nginxdemos/nginx-hello:plain-text
          ports:
            - containerPort: 8080
---
# backend1 Service
apiVersion: v1
kind: Service
metadata:
  name: backend1-svc
spec:
  selector:
    app: backend1
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
      name: http
---
# backend2 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend2
  template:
    metadata:
      labels:
        app: backend2
    spec:
      containers:
        - name: backend2
          image: nginxdemos/nginx-hello:plain-text
          ports:
            - containerPort: 8080
---
# backend2 Service
apiVersion: v1
kind: Service
metadata:
  name: backend2-svc
spec:
  selector:
    app: backend2
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
      name: http
      
# 리소스 생성
kubectl apply -f ingress-resource.yaml
# 리소스 확인
kubectl get pod,service

# 호스트 기반 Ingress생성 (도메인을 가지고 라우팅 하는 것)
# ingress-test.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx  # ← 이 줄로 대체 (이제는 권장 방식)
  rules:
    - host: example1.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend1-svc
                port:
                  number: 80
    - host: example2.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend2-svc
                port:
                  number: 80

# 리소스 배포 후 확인
kubectl apply -f ingress-test.yaml
kubectl get ingress
## NAME            CLASS   HOSTS                       ADDRESS          PORTS   AGE
## ingress-nginx   nginx   example1.com,example2.com   192.168.56.241   80      24m

# 노드 포트 확인
kubectl get svc -n ingress-nginx
## NAME                       TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                      AGE
## ingress-nginx-controller   LoadBalancer   10.104.241.51    192.168.56.241   80:31530/TCP,443:31238/TCP   70m

# /etc/hosts 파일에 Ingress Controller의 EXTERNAL-IP와 도메인 연결
192.168.56.241 example1.com example2.com

# 접속 테스트
curl <http://example1.com>
curl <http://example2.com>
## 호스트에 따라서 서로 다른 백엔드가 불려진다.

Path 기반 NGINX Ingress Controller 라우팅 설정(ingress-test.yaml)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /backend1
        pathType: Prefix
        backend:
          service:
            name: backend1-svc
            port:
              number: 80
      - path: /backend2
        pathType: Prefix
        backend:
          service:
            name: backend2-svc
            port:
              number: 80
              
# 배포 후 확인
kubectl get ingress
## NAME            CLASS    HOSTS         ADDRESS          PORTS   AGE
## ingress-nginx      example.com   192.168.56.241   80      39m
              
# /etc/hosts 파일에 연결
192.168.56.241 example.com

# 확인 
curl <http://example.com/backend1>
curl <http://example.com/backend2>

EC2

애플리케이션 배포

  • django app 만들기
    • 가상 환경 생성: python -m venv ./myvenv
    • 가상 환경 활성화:
      • cd /app/myvenv/Scripts
      • activate
      • cd ../../
    • django 설치
      • django 라이브러리 설치: pip install django
      • django 프로젝트 생성: django-admin startproject ec2django
    • django 실행
      • 디렉토리 이동: cd ec2django
      • 서버 실행: python manage.py runserver
    • 애플리케이션을 모든 컴퓨터에서 실행할 수 있도록 설정을 수정
      • settings.py 파일의 ALLOWED_HOSTS 부분을 *로 수정
      • ALLOWED_HOSTS = ['*']
    • 의존성 라이브러리를 별도의 파일에 기록
      • pip freeze > requirements.txt (다른 곳에 옮겨주려면 이 작업을 해줘야 한다. 다른 곳에서 동일한 환경을 재현하기 위해서)
    • git hub에 push
    • EC2에서 코드를 가져와서 실행
      • sudo apt update
      • sudo apt inatll git
      • git clone URL
      • sudo apt upgrade python3
      • sudo pip install -r requirements.txt --break-system-packages
      • pip install django --break-system-packages
      • sudo python3 manage.py runserver 0.0.0.0.:80
    • 다른 컴퓨터의 브라우저에서 EC2의 http://EC2 의 Public IP
      • 접속이 안되면 EC2의 보안 그룹에서 80번 포트가 열려있는지 확인

도메인 연결 - Route53 서비스

  • 웹 애플리케이션의 경우 일반적으로 IP를 기반으로 통신하지 않고 도메인을 기반으로 통신한다.
  • IP 주소에는 HTTPS를 적용할 수 없으므로 HTTPS를 적용하고자 한다면 도메인은 필수이다.
  • Route53 이외에도 DNS 역할을 사는 서비스는 많다.

도메인과 EC2 연결

  • Route53에서 구입한 도메인을 클릭
  • 레코드 생성 버튼 클릭
  • 레코드 이름(도메인) 설정하고 A레코드를 선택하고 EC2의 public ip 주소를 입력한다.

Elastic Load Balancing

ELB

  • AWS가 제공하는 Load Balancer
  • Load Balancer는 집중되는 접속을 여러 대나 네트워크에 분배하는 장비
  • 한 대에 집중되는 부하를 분산시키기 때문에 부하 분산 장치라고도 한다.
  • ELB 종류
    • ALB
      • HTTP 및 HTTPS에 가장 적합한 Load Balancer로 OSI 모형의 애플리케이션 계층에서 동작한다. 요청하는 명령어를 보고 판단하기 때문에 대상의 URL 디렉토리 단위로 분배하는 것이 가능하며 인스턴스와 LoadBalancer 사이의 통신은 암호화가 가능하다.
      • 분배 대상으로 정적 IP 주소를 설정하고 그 IP를 가진 호스트 기기로 전송할 수 없다.
      • HTTP와 HTTPS를 지원한다.
    • NLB
      • OSI 계층 중 4계층에서 동작하는데 패킷이라고 불리는 단편 데이터밖에 볼 수 없기 때문에 ALB만큼 상세하게 분배할 수 없지만 분배 대상의 정적 IP 주소를 설정할 수 있고 서버에 접속한 클라이언트의 IP 주소를 그대로 서버에 전송하도록 설정할 수 있다.
      • TCP, TLS 등을 지원한다.
    • CLB
      • 오래된 유형의 Load Balancer
      • 지원하는 프로토콜이 많다.
      • 앞으로 구축하는 시스템에는 사용하지 않는 것을 권장한다.
      • TCP, SSL/TLS, HTTP, HTTPS 등을 지원
  • 요금 체계
    • 시간 당 요금과 LCU(Load Balancer 용량 단위) 요금의 합계로 계산한다.
    • 시간 당 단가는 1시간 당 0.0225 달러 정도이고 LCU 요금은 1시간당 0.005 달러이다.
  • 에러가 발생하는 경우
    • 최대 접속 시간 제한이라는 설정이 있는데 디폴트는 60초로 지정되어 있으며 Load Balancer가 60초 동안 아무런 데이터도 전달 받지 못할 경우 연결을 자동으로 종료하고 타임 아웃 에러를 생성한다.
    • 대표적인 에러가 애플리케이션 또는 데이터 서버에서 특정 시간 내에 응답을 받지 못할 경우 생기는 Load Balancer Error, 504 Error로 서버 레이어나 데이터베이스 레이어에서 발생하기 때문에 상대적으로 쉽게 해결할 수 있다.
    • 현재 돌아가는 애플리케이션의 규모가 크다면 최대 접속 시간 제한을 변경해서 Load Balancer가 기다려줄 수 있는 시간을 조금이라도 늘려서 해결하거나 직접 애플리케이션을 수정해서 서버로 전송되는 데이터의 양을 조절하거나 응답 시간을 최적화 시켜야 한다.
  • Load Balancer 연결
    • EC2 하단에 로드 밸런서 > Application Load Balancer 생성
    • EC2 하단에 대상 그룹 > 대상 그룹 생성
    • 다시 Load Balancer에 가서 리스너 및 라우딩] 프로토콜 http, 포트 80, 만든 대상 그룹 선택

HTTPS 적용

SSL/TLS

  • HTTP를 HTTPS로 바꿔주는 인증서
  • HTTP가 아닌 HTTPS로 통신할 수 있게 만들 수 있다.

인증서 발급

  • AWS Certificate Manager 서비스에 접속
  • 인증서 요청 클릭
  • 도메인을 작성
  • 인증서가 만들어지면서 검증 대기 중 일텐데 이 때 Route53에서 레코드 생성 버튼을 눌러서 레코드를 생성한다.
728x90
반응형