[24단계 실습으로 정복하는 쿠버네티스] 책으로 스터디를 진행하였다.
이전 포스팅에서 사용한 구성을 활용하여
이전 구성에 Ingress(ALB)를 추가로 구성해 내부 서비스를 외부로 노출시키는 과정을 진행해보았다.
Ingress란?
Kubernetes 클러스터 외부에서 Kubernetes 서비스를 접근할 수 있게 해주는 Kubernetes 개체이다.
Ingress 자체로는 트래픽 라우팅 규칙 모음이라고 할 수 있고, 해당 규칙대로 트래픽을 전송하기 위해서는 Ingress Controler가 필요하다.
Ingress Controller에는 대표적으로 NGINX Ingress Controller, AWS Load Balancer Controller가 있으며,
아래와 같은 역할을 수행할 수 있다.
- Kubernetes 내부 서비스에서 외부로 연결할 수 있는 URL을 제공
- POD 트래픽 부하를 분산
- HTTP 및 HTTPS(SSL/TLS) 통신을 제공
위에 언급된 기능외에 L7 Layer의 PATH별로 Routing을 지원하는 것 등 다양한 기능을 제공한다.
Ingress (ALB) 구성하기
※참고사항 : 이전 포스팅과 같이 KOPS를 활용해 K8S를 구성하였고 CNI는 AWS VPC CNI로 구성하였다. (--networking amazonvpc 부분)
kops create cluster --zones=${AvailabilityZone1},${AvailabilityZone2} --networking amazonvpc --cloud aws \
--master-size ${MasterNodeInstanceType} --node-size ${WorkerNodeInstanceType} --node-count=${WorkerNodeCount} \
--network-cidr ${VpcBlock} --ssh-public-key ~/.ssh/id_rsa.pub --kubernetes-version "${KubernetesVersion}" --dry-run \
--output yaml > kops.yaml
이전 구성에서 cluster에 아래의 구문을 추가하고 적용한다.
(ersia:N/A) [root@kops-ec2 ~]# kops edit cluster
-----
spec:
certManager: # HTTPS를 사용하기 위한 add-on 구문
enabled: true
##############################
awsLoadBalancerController: # AWS ALB Ingress 사용을 위한 add-on 구문 추가
enabled: true #
##############################
externalDns: # Route53의 도메인과 연동해 자동으로 도메인 연결을 위한 add-on 구문
provider: external-dns
-----
(ersia:N/A) [root@kops-ec2 ~]# kops update cluster --yes && echo && sleep 3 && kops rolling-update cluster
구문 추가만으로는 Ingress(ALB)가 생성되지 않고 추가로 배포하고 적용해줘야 한다.
먼저 IngressClass를 배포한다.
IngressClass는 하나의 Cluster에서 여러개의 Ingress Controller를 사용하기 위해 사용하는데, 이번 스터디에서는 하나의 Ingress Controller만 사용한다.
참고 : https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/multiple-ingress.md#multiple-ingress-controllers
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: aws-alb # ingress class의 이름을 지정한다.
spec:
controller: ingress.k8s.aws/alb
(ersia:N/A) [root@kops-ec2 aa]# kubectl apply -f ingress_class.yaml
ingressclass.networking.k8s.io/aws-alb created
(ersia:N/A) [root@kops-ec2 aa]# kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
aws-alb ingress.k8s.aws/alb <none> 100s
이어서 ingress를 배포한다. rules란이 없으면 생성되지 않으므로 ingress를 통해 노출할 서비스를 지정한다.
이번 과정에서는 2개의 서비스를 생성하고 path를 기반으로 트래픽을 전달하도록 구성한다.
지원하는 annotation은 아래의 링크에서 더 상세하게 확인 가능하다.
---
apiVersion: v1
kind: Namespace
metadata:
name: test # Namespace를 명령어로 생성하지 않고 yaml로 배포하기 위해 추가
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: test # ingress가 생성될 namespace
name: test-ingress # ingress 이름
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing # 로드밸런서가 Private에 위치할지 Public에 위치할지 정의
alb.ingress.kubernetes.io/target-type: ip #POD로 트래픽을 Routing하는 방법, instance모드와 IP모드가 있다
alb.ingress.kubernetes.io/group.name: product-catalog # 여러 ingress와Group으로 묶기위한 Group 이름
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS":443}]' # ALB가 Listening하는 Port
alb.ingress.kubernetes.io/ssl-redirect: '443' # SSLRedirect를 활성화하고Redirection할 SSL 포트를 지정
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:XXXXXXXXXXXX:certificate/~~~~~~~~
# AWS Certificate Manager에서 관리하는 인증서의 ARN
spec:
ingressClassName: aws-alb # 사전에 생성한 ingressClass 이름
rules: # ingress(ALB)를 통해 전달할 서비스와 path
- host: games.ersia.net # ExternalDNS를 사용해 Route53에서 자동으로 연결할 도메인을 지정한다
http: # 아래의 규칙들은 ALB의 Listener Rule로 생성된다
paths:
- path: /mario
pathType: Prefix
backend:
service:
name: mario
port:
number: 80
- path: /service-2048
pathType: Prefix
backend:
service:
name: service-2048
port:
number: 80
(ersia:N/A) [root@kops-ec2 aa]# kubectl apply -f ingress.yaml
namespace/test created
ingress.networking.k8s.io/test-ingress created
여기서 실제 AWS ALB가 생성된다.
rules란에서 작성한 규칙들이 아래와 같이 생성된다.
Ingress(ALB)가 정상적으로 생성되었으므로 연결할 서비스를 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: test
name: deployment-2048
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-2048
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: app-2048
spec:
containers:
- image: public.ecr.aws/l6m2t8p7/docker-2048:latest
imagePullPolicy: Always
name: app-2048
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: test
name: service-2048
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
type: NodePort
selector:
app.kubernetes.io/name: app-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mario
namespace: test
labels:
app: mario
spec:
replicas: 1
selector:
matchLabels:
app: mario
template:
metadata:
labels:
app: mario
spec:
containers:
- name: mario
image: pengbai/docker-supermario
---
apiVersion: v1
kind: Service
metadata:
name: mario
namespace: test
spec:
selector:
app: mario
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: NodePort
(ersia:test) [root@kops-ec2 ~]# kubectl apply -f service.yaml
deployment.apps/deployment-2048 created
service/service-2048 created
deployment.apps/mario created
service/mario created
※ 주의사항
위의 작업만 수행하고 접속을 수행하면 404 not found가 발생한다.
Kubernetes에서 원인을 찾지못해 꽤 오래 삽질하였는데 원인은 URL Path로 접속하면서
nginx나 apache-tomcat이 서비스하는 기본 경로가 아닌 path로 서비스할 디렉토리를 찾으면서 발생한 문제였다.
(ersia:test) [root@kops-ec2 ~]# kubectl exec -it deployment-2048-6bc9fd6bf5-snzgn -- cat /var/log/nginx/error.log
2023/03/18 16:37:22 [error] 8#8: *10 open() "/usr/share/nginx/html/service-2048" failed (2: No such file or directory), client: 172.30.63.75, server: , request: "GET /service-2048 HTTP/1.1", host: "games.ersia.net"
기존에는 별도의 URL Path없이 접속하다보니 문제가 없었는데, nginx의 설정파일을 확인해보니
사용하는 docker image의 기본 설정 디렉토리가 지정되어있었다.
(ersia:test) [root@kops-ec2 ~]# kubectl exec -it deployment-2048-6bc9fd6bf5-snzgn -- cat /etc/nginx/http.d/default.conf
# This is a default site configuration serving from /usr/share/nginx/html
server {
listen 80 default_server;
listen [::]:80 default_server;
# Everything is a 404
location / {
root /usr/share/nginx/html;
}
# You may need this to prevent return 404 recursion.
location = /404.html {
internal;
}
}
하지만 Path를 타고 접속하면서 기본 서비스 경로인 /usr/share/nginx/html 가 아닌 /usr/share/nginx/html/service-2048을 찾으면서 404에러가 발생했다.
해당 에러는 docker image를 수정해야 근본적으로 해결이 가능하나, 임시로 아래와 같이 서비스할 디렉토리를 찾도록 수정해주었다.
(ersia:test) [root@kops-ec2 ~]# kubectl exec -it deployment-2048-6bc9fd6bf5-snzgn -- ls /usr/share/nginx/html/
service-2048
(ersia:test) [root@kops-ec2 ~]# kubectl exec -it deployment-2048-6bc9fd6bf5-snzgn -- ls /usr/share/nginx/html/service-2048
CONTRIBUTING.md index.html meta
favicon.ico js style
위의 nginx, apache-tomcat의 디렉토리 경로 수정 후 ExternalDNS로 자동 연결된 host 도메인으로 접속을 확인한다.
ExternalDNS를 통해 host를 명시했으므로 여기서 정한 URL(여기선 games.ersia.net)을 통해서만 접속할 수 있다.
실습 정리 및 소감
# 생성한 서비스 및 ingress 정리
kubectl delete -f service.yaml
kubectl delete -f ingress.yaml
kubectl delete -f ingress-class.yaml
# kops로 생성한 cluster 정리
kops delete cluster --yes
# cloudformation 스택 제거
# 생성한 S3 버킷 삭제
# Route53에 남아있는 불필요 Record 삭제 (TXT Record)
단순 구성 외에 실제 패킷의 동작도 확인해봐야할 필요성이 있다는 생각이 들었다. (https://coffeewhale.com/packet-network1)
Calico와 AWS VPC CNI의 동작차이 및 AWS VPC CNI의 POD 개수 제한과 네트워크 통신을 조금 더 자세히 알아둬야 실제 AWS에서 K8S를 운영할 때 제대로 구성할 수 있겠다는 생각이 든다. (https://trans.yonghochoi.com/translations/aws_vpc_cni_increase_pods_per_node_limits.ko)
참고자료
- Kubernetes Networking Options : https://kops.sigs.k8s.io/networking/
- EKS WorkShop : https://catalog.workshops.aws/eks-immersionday/en-US/services-and-ingress/ingress
- CloudNet@ Blog의 Ingress 네트워크 : https://gasidaseo.notion.site/K8S-Ingress-b38c1cbed5f9481bafa4d5315999e66a
- CloudNet@ Blog의 VPC CNI 네트워크 자료 : https://gasidaseo.notion.site/AWS-EKS-VPC-CNI-1-POD-f89e3e5967b24f8c9aa5bfaab1a82ceb