수업 중 발생한 에러 정리... 하지만 미리 밝히자면 내 노트북에서는 해결을 했으나, 다른 분의 노트북에서는 해결이 안 됐다. 그래서 정확이 무엇이 문제인지, 아주 확실하게 밝혀진 것은 아니지만 내 노트북에서 해결했던 방식으로 나름대로 결론을 내리고 정리를 해보려고 한다!

🔄️ Ingress가 무엇인지 먼저 알아보자!

클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트
출처: 쿠버네티스 공식 문서

외부에서 쿠버네티스 클러스터로 들어오는 요청을 관리해주는 객체 혹은 서비스라고 생각하면 될 것 같다.
👉🏻 외부에서 내부로 접근하는 요청들에 대해 어떻게 처리할 것인지 정의하는 규칙들의 모음

인그레스는 외부에서 서비스로 접속이 가능한 URL, 로드 밸런스 트래픽, SSL/TLS 종료 그리고 이름 기반의 가상 호스팅을 제공하도록 구성할 수 있다.

👉🏻 외부에서 쿠버네티스에서 실행 중인 Deployment와 Service에 접근하기 위한 Gateway와 같은 역할을 담당한다!

* 클러스터가 뭔데?!
👉🏻 클러스터는 노드를 기반으로 실행된다. 쿠버네티스에서 관리되는 컨테이너화 된 애플리케이션을 실행하는 노드의 집합이 클러스터이다.

🕹️ Ingress Controller

인그레스가 돌아가기 위해서는 인그레스 컨트롤러가 있어야 한다. 인그레스 리소스만 생성한다고 해서 되지 않는다!

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

위는 공식 문서에서 제공하는 최소한의 인그레스 리소스 예제이다.

  • apiVersion
  • kind
  • metadata
  • spec 필드

등이 명시 되어야 한다. 인그레스 오브젝트의 이름은 유효한 DNS 서브도메인 이름이 되어야 한다.

인그레스 컨트롤러는 자동으로 실행되는 것이 아니라, 상황에 맞게 적절한 컨트롤러를 선택해서 설치해야 하는데 쿠버네티스에서는 GCENGINX를 오픈소스로 제공하고 있다.

🖼️ 인그레스와 인그레스 컨트롤러에 대한 간단한 아키텍처


(출처: https://kubetm.github.io/k8s/08-intermediate-controller/ingress/)

🤓 실습

Googel에서 제공하는 기본 데모 애플리케이션을 Kubernetes의 Ingress를 통해서 배포해보자! (실습 툴: Minikube)

✔️ 간단한 웹 애플리케이션 배포 파일

# helloworld-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld-v1
  template:
    metadata:
      labels:
        app: helloworld-v1
    spec:
      containers:
      - name: helloworld
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: helloworld-v1
spec:
  selector:
    app: helloworld-v1
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
# helloworld-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld-v2
  template:
    metadata:
      labels:
        app: helloworld-v2
    spec:
      containers:
      - name: helloworld
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: helloworld-v2
spec:
  selector:
    app: helloworld-v2
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

위는 구글에서 제공하는 간단한 웹 애플리케이션 이미지를 통해서 미니큐브를 통해 배포할 수 있는 yaml 파일이다.

크게 두 부분으로 나눌 수 있다.

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-v1    # Deployment의 이름
spec:
  replicas: 1           # Pod 복제본 수 (1개만 실행)
  selector:            # Pod을 선택하는 기준
    matchLabels:
      app: helloworld-v1  # 이 라벨을 가진 Pod을 관리
  template:           # Pod 템플릿 정의
    metadata:
      labels:
        app: helloworld-v1  # Pod에 부여될 라벨
    spec:
      containers:
      - name: helloworld    # 컨테이너 이름
        image: gcr.io/google-samples/hello-app:1.0  # 사용할 도커 이미지
        ports:
        - containerPort: 8080  # 컨테이너가 사용할 포트

Service

apiVersion: v1
kind: Service
metadata:
  name: helloworld-v1    # 서비스 이름
spec:
  selector:
    app: helloworld-v1   # 이 라벨을 가진 Pod들을 대상으로 함
  ports:
  - protocol: TCP       # 프로토콜 종류
    port: 80           # 서비스가 노출할 포트
    targetPort: 8080   # Pod의 대상 포트

정리

👉🏻 컨테이너는 8080 포트를 사용할 것이며, 서비스는 80포트로 외부에 노출할 것임
👉🏻 [외부 요청] → [Ingress] → [Service(80)] → [Pod(8080)] 순으로 전달

👉🏻 Deployment와 Pod는 app: helloworld-v1 | v2 라벨을 갖는다
👉🏻 Service: 같은 라벨을 가진 Pod를 선택한다
👉🏻 때문에 Service와 Pod가 연결된다

👉🏻 grc.io/google-samples/hello-app:1.0: 구글에서 제공하는 간단한 웹 애플리케이션
👉🏻 "Hello, World!" 메시지를 반환
✔️ v1과 v2는 같은 이미지를 사용하지만 독립적으로 동작함

애플리케이션을 통해서 Pod를 생성하고 실행한 후(Deployment),
Pod에 접근할 수 있는 네트워크 진입점을 제공하고(Service),
Ingress를 통해 외부에서 접근이 가능한 URL을 제공한다

kubectl apply -f helloworld-v1.yaml
kubectl apply -f helloworld-v2.yaml

yaml 파일에 문제가 없다면! 두 파일을 배포해준다.

✔️ Ingress 설정

apiVersion: networking.k8s.io/v1    # Kubernetes API 버전
kind: Ingress                       # 리소스 종류
metadata:
  name: helloworld-rules            # Ingress 리소스의 이름
spec:
  rules:                            # Ingress 라우팅 규칙들
  - host: helloworld-v1.example.com # 첫 번째 호스트 규칙
    http:
      paths:                        # URL 경로 규칙
      - path: /                     # 루트 경로 (/)
        pathType: Prefix            # 경로 매칭 타입
        backend:                    # 요청을 전달할 백엔드 서비스
          service:
            name: helloworld-v1     # 서비스 이름
            port:
              number: 80            # 서비스 포트

  - host: helloworld-v2.example.com # 두 번째 호스트 규칙
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: helloworld-v2
            port:
              number: 80

설정 파일을 자세하게 뜯어봤다. 주석에도 썼지만, Ingress는 규칙들의 모음이다!

정리

👉🏻 helloworld-v1.example.ocm → helloworld-v1 서비스로 라우팅
👉🏻 helloworld-v2.example.ocm → helloworld-v2 서비스로 라우팅

👉🏻 동작하는 방식

클라이언트 요청 → Ingress Controller →
   ├── helloworld-v1.example.com 요청 → helloworld-v1 서비스
   └── helloworld-v2.example.com 요청 → helloworld-v2 서비스
  • pathType: Prefix: URL 경로 매칭 방식
    - Prefix: 지정된 경로로 시작하는 모든 URL 매칭
    • Exact: 정확히 일치하는 URL만 매칭
  • backend: 요청을 전달할 대상 서비스 정의
  • port 서비스의 포트 번호

버전 주의

apiVersion과 문법 체계가 살짝 바뀌었다! 원래 실습 자료에서는 extensions/v1beta1이 명시되어 있었는데 잘 찾아보면 1.14 버전에서 deprecated 되어서 사용할 수 없다는 것을 알 수 있다. 때문에 networking.k8s.io/v1으로 수정을 해주어야 하며 아래 문법도 살짝 바뀌었기 때문에 공식 문서나 구글링을 통해서 잘 찾아본 후 문법에 맞게 yaml파일을 수정해야 한다.

✔️ Ingress addon 활성화 (Ingress Controller 설치)

Kubernetes에서는 Ingress를 사용하기 위해 Ingress Controller를 설정해야 한다고 앞서 설명했다. 이를 활성화하는 방법은! 바로 명령어 한 줄이다.

minikube addons enable ingress

나는 미니큐브에서 실습을 하고 있기 때문에 파워쉘에 위 명령어를 입력했다.

✔️ Nginx 인그레스 컨트롤러 실행 중인지 확인

kubectl get pods -n ingress-nginx

위 명령어를 통해서 인그레스 컨트롤러가 설치되었는지 확인할 수 있다.

만약 Ingress Controller가 클러스터에 설치되지 않았다면

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

위 명령어를 입력해보자! (NGINX Ingress Controller)

✔️ Ingress 실행

kubectl apply -f ingress.yaml

Ingress를 실행한다!

✔️ IP 주소 설정 확인

kubectl get ingress

IP 주소가 잘 설정되어 있는지 확인해본다.

✔️ Host 파일(/ect/hosts)에 address 추가

윈도우의 경우 C:\Windows\System32\drivers\etc\hosts 이 위치이다.

minikube ip

위 명령어를 통해서 미니큐브의 IP 주소를 알아낸 후 hosts 파일에

<Minikube IP> helloworld-v1.example.com
<Minikube IP> helloworld-v2.example.com

위의 내용을 추가해준다.

✔️ curl을 통한 호출

curl http://helloworld-v1.example.com

위의 모든 과정이 정상적으로 작동했다면... 제대로 떠야 하건만!

🚨 문제 발생! curl: 원격 서버에 연결할 수 없습니다.

curl : 원격 서버에 연결할 수 없습니다.
위치 줄:1 문자:1
+ curl http://helloworld-v1. example.com
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
   eption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

난 계속해서 이런 에러가 발생했다.

🥲 문제 해결 과정

  1. 컨트롤러도 제대로 설치되어 있었고
  2. DNS 설정도 잘 되어 있었고
  3. 백엔드의 Service와 Pod도 정상적으로 기동되고 있다는 것도 확인했다.
  4. Ingress의 리소스도 모두 잘 확인을 했다!

그런데 왜!! 왜 안돼!!!

📜 Ingress Controller 로그 확인

kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

위의 명령어를 통해서 로그를 확인해보았다.

PS C:\Minikube> kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx
I1105 03:28:02.088065       7 nginx.go:304] "Starting NGINX process"
I1105 03:28:02.088464       7 nginx.go:324] "Starting validation webhook" address=":8443" certPath="/usr/local/certificates/cert" keyPath="/usr/local/certificates/key"
I1105 03:28:02.088732       7 controller.go:190] "Configuration changes detected, backend reload required"
I1105 03:28:02.090392       7 status.go:84] "New leader elected" identity="ingress-nginx-controller-bc57996ff-8mmfd"
I1105 03:28:02.137154       7 controller.go:207] "Backend successfully reloaded"
I1105 03:28:02.137246       7 controller.go:218] "Initial sync, sleeping for 1 second"
I1105 03:28:02.137309       7 event.go:285] Event(v1.ObjectReference{Kind:"Pod", Namespace:"ingress-nginx", Name:"ingress-nginx-controller-6755d5f7b5-7khct", UID:"0a6e0fef-4778-47e4-827c-1c336a2c4367", APIVersion:"v1", ResourceVersion:"28450", FieldPath:""}): type: 'Normal' reason: 'RELOAD' NGINX reload triggered due to a change in configuration
I1105 03:28:12.532111       7 event.go:285] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"default", Name:"helloworld-rules", UID:"1d605c39-7ebd-4652-bf8b-2d3023defdab", APIVersion:"networking.k8s.io/v1", ResourceVersion:"28509", FieldPath:""}): type: 'Normal' reason: 'Sync' Scheduled for sync
I1105 03:28:58.516711       7 leaderelection.go:258] successfully acquired lease ingress-nginx/ingress-nginx-leader
I1105 03:28:58.516759       7 status.go:84] "New leader elected" identity="ingress-nginx-controller-6755d5f7b5-7khct"
W1104 06:55:32.367343       1 client_config.go:618] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
{"err":"secrets \"ingress-nginx-admission\" not found","level":"info","msg":"no secret found","source":"k8s/k8s.go:229","time":"2024-11-04T06:55:32Z"}
{"level":"info","msg":"creating new secret","source":"cmd/create.go:28","time":"2024-11-04T06:55:32Z"}
W1104 06:55:32.566426       1 client_config.go:618] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
{"level":"info","msg":"patching webhook configurations 'ingress-nginx-admission' mutating=false, validating=true, failurePolicy=Fail","source":"k8s/k8s.go:118","time":"2024-11-04T06:55:32Z"}
{"level":"info","msg":"Patched hook(s)","source":"k8s/k8s.go:138","time":"2024-11-04T06:55:32Z"}

징그럽다... 그래서 로그 분석은 ai에게 맡겼다! (당당)

그러니까 여기까지도...? 문제가 없다는 얘기잖아...? 로그 상으로도 아무런 문제가 없었다.

🌟🚞 minikube tunnel

로그에도 아무런 문제가 없자 AI의 제안... minikube tunnel을 활성화해라!

minikube 환경에서 Ingress를 사용하려면 minikube tunnel 명령을 실행해야 한다고 한다. 이유는 크게 세 가지가 있다.

  1. LoadBalancer 타입 서비스 지원
    • 쿠버네티스 클러스터에서 로드밸런서 타입의 서비스를 실행하면 외부 IP 주소를 자동으로 할당 받아야 한다.
    • 하지만 미니큐브 환경에서는 클러스터 외부에서 접근할 수 있는 실제 IP 주소를 제공하지 않는다.
  2. Ingress 외부 IP 할당
    • 인그레스 리소스도 로드밸런서 타입의 서비스를 사용하여 외부 IP를 할당받는다.
    • 미니큐브 환경에서는 이 외부 IP를 자동으로 할당해주지 않는다. 따라서 명령어를 실행하여 로컬 머신의 IP를 인그레스에 연결해주어야 한다.
  3. 미니큐브 환경 특성
    • 미니큐브는 단일 노드 쿠버네티스 클러스터를 로컬에서 실행하는 도구이다.
    • 실제 클라우드 환경과는 다르게 외부 IP 주소를 자동으로 할당하지 않는다.

때문에 이 터널을 활성화 시키면 로컬 머신의 IP 주소가 Ingress의 외부 IP 역할을 하게 되어서, 외부 클라이언트가 Ingress에 접근할 수 있게 한다.

실제로 터널을 확인하기 전에 실제 IP 주소를 확인해보면 (kubectl get ingress helloworld-rules)

PS C:\Minikube> kubectl get ingress helloworld-rules
NAME               CLASS   HOSTS                                                 ADDRESS   PORTS   AGE
helloworld-rules   nginx   helloworld-v1.example.com,helloworld-v2.example.com             80      64m

위 명령어를 실행해보면 address가 뜨지 않는 것을 알 수 있다.

🌟🥸 포트를 명시적으로 지정

결국 승리했다.

# 이렇게 하면 실패할 수 있음
curl http://helloworld-v1.example.com

# 이렇게 하면 성공
curl http://helloworld-v1.example.com:80

뒤에 포트 번호를 명시적으로 지정해주니! 드디어 요청 성공이다.

그래서 왜!!! 왜 이런 일이 발생하냐!!!

🖥️ 이게 다~ 윈도우 때문인거 아시죠?

기본 포트를 인식하지 못하는 것이 문제였다.

minikube의 Ingress Controller는 80포트로 설정이 되어있다. 하지만!!!!

Windows의 PowerShell에서 curl을 실행할 때 기본 포트를 제대로 인식하지 못하는 경우

가 있다.

따라서 :80을 명시적으로 지정함으로써 정확한 포트로 요청이 전달될 수 있다. 때문에 윈도우 환경에서 Ingress를 테스트할 때는 항상 포트를 명시적으로 지정해주어야 한다.

혹은 hosts 파일에서 포트를 포함해서 설정하는 방법도 있다.

<Minikube IP>:80 helloworld-v1.example.com

이런 식으로 말이다!


느낀점

와... 진짜 분명 논리적으로 이게 되어야 말이 되는데 왜 안되는거지? 난 이런 것들이 싫다... 명확하게 이유를 알고 싶은데!!! 꽤 오랜 시간을 투자했고, 그래도 그 결과를 얻어갈 수 있어서 뿌듯했다. 집에서도 해보려고 했는데 내 개꾸진 노트북에서는 뭔가 설치부터 잘 안된다 ^^... 나중에 프로젝트할 때 어떡하지?... 후... 암튼 그래서 집에서 실습 캡쳐를 할 수는 없지만 내일 학원가서 해보는거로 ㅎㅎ 컨디션 이슈로 쿠버네티스 수업이 하루하루 힘들어 죽겠지만... 어쩌겠어요 해야지... 파이팅해봅니다 ㅠㅠ 그래도 이거 찾아보면서 많이 정리되고 새로 알게된 것들이 많다. 알아가야 할 것들이 더 많지만... 그래도 재밌어서 다행~ 깔깔깔~


본 포스팅은 글로벌소프트웨어캠퍼스와 교보DTS가 함께 진행하는 챌린지입니다.

profile
영차영차 😎

0개의 댓글