수업 중 발생한 에러 정리... 하지만 미리 밝히자면 내 노트북에서는 해결을 했으나, 다른 분의 노트북에서는 해결이 안 됐다. 그래서 정확이 무엇이 문제인지, 아주 확실하게 밝혀진 것은 아니지만 내 노트북에서 해결했던 방식으로 나름대로 결론을 내리고 정리를 해보려고 한다!
클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트
출처: 쿠버네티스 공식 문서
외부에서 쿠버네티스 클러스터로 들어오는 요청을 관리해주는 객체 혹은 서비스라고 생각하면 될 것 같다.
👉🏻 외부에서 내부로 접근하는 요청들에 대해 어떻게 처리할 것인지 정의하는 규칙들의 모음
인그레스는 외부에서 서비스로 접속이 가능한 URL, 로드 밸런스 트래픽, SSL/TLS 종료 그리고 이름 기반의 가상 호스팅을 제공하도록 구성할 수 있다.
👉🏻 외부에서 쿠버네티스에서 실행 중인 Deployment와 Service에 접근하기 위한 Gateway와 같은 역할을 담당한다!
* 클러스터가 뭔데?!
👉🏻 클러스터는 노드를 기반으로 실행된다. 쿠버네티스에서 관리되는 컨테이너화 된 애플리케이션을 실행하는 노드의 집합이 클러스터이다.
인그레스가 돌아가기 위해서는 인그레스 컨트롤러가 있어야 한다. 인그레스 리소스만 생성한다고 해서 되지 않는다!
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 서브도메인 이름이 되어야 한다.
인그레스 컨트롤러는 자동으로 실행되는 것이 아니라, 상황에 맞게 적절한 컨트롤러를 선택해서 설치해야 하는데 쿠버네티스에서는 GCE와 NGINX를 오픈소스로 제공하고 있다.
(출처: https://kubetm.github.io/k8s/08-intermediate-controller/ingress/)
# 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 파일이다.
크게 두 부분으로 나눌 수 있다.
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 # 컨테이너가 사용할 포트
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 파일에 문제가 없다면! 두 파일을 배포해준다.
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파일을 수정해야 한다.
Kubernetes에서는 Ingress를 사용하기 위해 Ingress Controller를 설정해야 한다고 앞서 설명했다. 이를 활성화하는 방법은! 바로 명령어 한 줄이다.
minikube addons enable ingress
나는 미니큐브에서 실습을 하고 있기 때문에 파워쉘에 위 명령어를 입력했다.
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)
kubectl apply -f ingress.yaml
Ingress를 실행한다!
kubectl get ingress
IP 주소가 잘 설정되어 있는지 확인해본다.
/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 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
난 계속해서 이런 에러가 발생했다.
그런데 왜!! 왜 안돼!!!
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에게 맡겼다! (당당)
그러니까 여기까지도...? 문제가 없다는 얘기잖아...? 로그 상으로도 아무런 문제가 없었다.
로그에도 아무런 문제가 없자 AI의 제안... minikube tunnel
을 활성화해라!
minikube 환경에서 Ingress를 사용하려면 minikube tunnel
명령을 실행해야 한다고 한다. 이유는 크게 세 가지가 있다.
때문에 이 터널을 활성화 시키면 로컬 머신의 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포트로 설정이 되어있다. 하지만!!!!
가 있다.
따라서 :80
을 명시적으로 지정함으로써 정확한 포트로 요청이 전달될 수 있다. 때문에 윈도우 환경에서 Ingress를 테스트할 때는 항상 포트를 명시적으로 지정해주어야 한다.
혹은 hosts 파일에서 포트를 포함해서 설정하는 방법도 있다.
<Minikube IP>:80 helloworld-v1.example.com
이런 식으로 말이다!
와... 진짜 분명 논리적으로 이게 되어야 말이 되는데 왜 안되는거지? 난 이런 것들이 싫다... 명확하게 이유를 알고 싶은데!!! 꽤 오랜 시간을 투자했고, 그래도 그 결과를 얻어갈 수 있어서 뿌듯했다. 집에서도 해보려고 했는데 내 개꾸진 노트북에서는 뭔가 설치부터 잘 안된다 ^^... 나중에 프로젝트할 때 어떡하지?... 후... 암튼 그래서 집에서 실습 캡쳐를 할 수는 없지만 내일 학원가서 해보는거로 ㅎㅎ 컨디션 이슈로 쿠버네티스 수업이 하루하루 힘들어 죽겠지만... 어쩌겠어요 해야지... 파이팅해봅니다 ㅠㅠ 그래도 이거 찾아보면서 많이 정리되고 새로 알게된 것들이 많다. 알아가야 할 것들이 더 많지만... 그래도 재밌어서 다행~ 깔깔깔~
본 포스팅은 글로벌소프트웨어캠퍼스와 교보DTS가 함께 진행하는 챌린지입니다.