처음 읽는 분들도 이해하기 쉽게 최대한 풀어서 설명했습니다.
EKS 는 Elastic Kubernetes Service 의 약자로 AWS 에서 제공하는 쿠버네티스 서비스이다.
EKS 를 조금 더 쉽게 풀어서 표현하면, AWS 에서 제공하는 여러 클라우드 서비스를 통해 쿠버네티스를 쉽고 간편하게 이용할 수 있는 유료 서비스라고 이해하면 된다.
쿠버네티스도 다른 프로그램과 마찬가지로 프로그램이기 때문에 AWS 나 GCP 같은 클라우드 서비스를 이용하지 않고 내 컴퓨터에 다운 받아서 사용할 수 있다.
쿠버네티스를 설치하고 사용할 수 있는 방법은 크게 3가지다.
1번, 2번 방법의 장단점은 아래와 같다.
쿠버네티스와 도커, 네트워크를 공부하고 이해하기 위해 로컬 호스트에 설치하는 것은 분명 도움이 된다.
쿠버네티스를 구성하는 기본적인 개념들(클러스터, 노드, 파드 등)을 이해해야 설치가 가능하기 때문이다.
뿐만 아니라, 쿠버네티스는 여러 컴퓨터를 하나의 컴퓨터처럼 사용하기 때문에 네트워크도 잘 이해해야 한다.
하지만 빠르게 실무에 적용해야 하거나, 처음 쿠버네티스를 접한다면 1번, 2번은 진입 장벽이 너무 높다.
쿠버네티스를 설치하고 설정하느라 시간이 굉장히 오래 걸린다.
게다가 오류가 발생하면 오류를 해결하느라 쿠버네티스를 본격적으로 써보기도 전에 지쳐버린다.
그래서 AWS 에서는 쿠버네티스를 누구나 쉽고 빠르게 사용할 수 있도록 EKS 라는 서비스를 제공하고 있다.
쿠버네티스를 사용하기 위한 여러 복잡한 설정을 AWS 에서 알아서 해준다.
편리함을 제공해주는 대신 돈을 지불해야 하지만, 소중한 시간을 단축시켜준다는 점에서 충분히 지불할 가치가 있다.
쿠버네티스는 컨테이너를 간편하게 관리하기 위한 도구이다.
컨테이너에 배포한 애플리케이션을 여러 컴퓨터 에 나누어서 배포하고 있다면, 매번 업데이트가 있을 때마다 각 컴퓨터에 접속해서 업데이트 한다는 것은 굉장히 번거로운 일이다.
그렇다면 여러 컴퓨터를 하나의 컴퓨터에서 관리할 수 있도록 한다면 얼마나 편할까?
마치 PC방의 카운터에 있는 컴퓨터로 매장 안에 있는 모든 컴퓨터를 조작하는 것을 생각하면 된다.
PC방 안에 있는 모든 컴퓨터는 하나의 네트워크로 묶여있기 때문에 카운터 컴퓨터가 매장의 모든 컴퓨터를 마음대로 조작하는 것이 가능하다.
쿠버네티스도 마찬가지로 여러 컴퓨터를 하나의 컴퓨터에서 관리할 수 있도록 네트워크를 하나로 묶어서 관리한다.
카운터 컴퓨터에 해당하는 컴퓨터를 컨트롤 플레인(Control Plane)라 부르고, 고객이 사용하는 컴퓨터를 노드(Node)라고 부른다.
컨트롤 플레인을 마스터(Master), 노드를 데이터 플레인(Data Plane)으로 부르기도 하는데, 이 글에서는 편의상 컨트롤 플레인과 노드로 표현한다.
컨트롤 플레인은 막강한 힘을 가져야 하기 때문에 복잡한 설정이 필요하다.
그래서 AWS 에서 컨트롤 플레인을 사용하기 위한 설정을 알아서 해주고 관리까지 해준다.
대신, 막강한 힘을 가졌기 때문에 AWS 를 이용하는 사용자가 컨트롤 플레인에 직접 접근할 수 없다고 이해하면 된다.
출처: On Amazon EKS and cluster add-ons [medium]
위의 그림에서 왼쪽에 위치한 EKS Control Plane 은 별도의 VPC 에 할당이 되어 있는데, 이 영역은 AWS 가 관리하는 영역이기 때문에 일반 사용자는 접근할 수 없는 것이다.
그림의 오른쪽에 해당하는 인스턴스(Instance)들이 사용자가 직접 사용할 수 있는 노드들이다.
사용자는 노드에 컨테이너를 배포하지만, 컨트롤 플레인을 통해 간편하게 무중단 배포도 하고, 컨테이너를 자유자재로 늘렸다가 줄일 수도 있는 것이다.
여기서부터는 학습한 내용을 정리하기 위해 개념이나 용어를 풀어서 설명하지 않고 생략합니다.
아래의 이미지와 같은 구조로 클러스터 환경에서 실습을 진행할 예정이다.
스터디에서 제공한 CloudFormation 템플릿을 이용해서 간편하게 구축했다.
여기서 작업용 EC2
는 EKS 를 생성하고 노드를 관리하기 위해서 사용한다.
작업용 EC2 없이 내 로컬 호스트에서 EKS 를 CLI 로 관리할 수 있는 eksctl
을 설치해서 컨트롤 플레인에 요청을 보낼 수도 있지만, 여기서는 편의상 작업용 EC2 를 하나 만들어서 진행했다.
작업용 EC2 에는 내 컴퓨터에서만 ssh 접속할 수 있도록 아래의 이미지처럼 공인IP/32
로 설정했다.
/32
의 의미를 알고 싶다면 ‘서브넷 마스크’를 찾아보면 된다.)2~3분 정도 지나면 아래와 같이 작업용 EC2 인스턴스 하나가 생성된다.
로컬 호스트의 터미널에서 키 페어를 이용해서 작업용 EC2 에 ssh 접속을 하면 아래의 이미지와 같이 표시된다.
작업용 EC2 인스턴스에서 나의 AWS 계정에 대해 자유롭게 EKS 클러스터를 설치하고 배포하기 위해 현재 접속한 PC 가 나의 AWS 계정을 통해 AWS 서비스에 접근한다는 것을 증명을 받아야 한다.
이를 가능하게 하는 것이 보안 자격 증명이다.
[우측 상단 계정] 클릭 - [보안 자격 증명] 클릭
[액세스 키 만들기] 버튼 클릭
[CLI] 선택 후 [다음] 클릭
2단계에서는 태그를 지정해주는데, 별도로 설정하지 않고 넘어갔다.
3단계에서는 발급한 액세스 키 정보를 확인할 수 있다.
비밀 액세스 키는 2번 다시 확인할 수 없으므로 반드시 별도로 저장한다.
작업용 EC2 에서 아래의 명령어를 실행한다.
aws configure
아래와 같이 위에서 발급 받은 액세스 키에 대한 정보를 입력하도록 요구한다.
AWS Access Key ID [None]: **<액세스 키>**
AWS Secret Access Key [None]: **<비밀 액세스 키>**
Default region name [None]: ***ap-northeast-2***
Default output format [None]: json
<액세스 키>
와 <비밀 액세스 키>
는 발급 받은 정보를 그대로 입력한다.
Default region name 은 현재 작업용 EC2 가 배포되어 있는 서울 리전의 영어 이름인 ap-northeast-2 로 지정했다.
정상적으로 입력하고 나서 아래의 명령어를 실행해서 AWS 계정에서 실행 중인 EC2 인스턴스의 정보를 확인할 수 있다.
aws ec2 describe-instances
아래의 명령어를 실행해서 EKS 를 배포할 VPC 가 정상적으로 표시되는 지 확인한다.
aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq
참고로 jq
는 JSON 데이터를 사람이 읽기 쉬운 형태로 출력해주는 프로그램이다. JSON Query 의 약자라고 생각하면 편하다.
VPC 의 ID 를 환경변수로 저장하기 위해 아래의 명령어를 실행한다.
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId)
echo "export VPCID=$VPCID" >> /etc/profile
# VPCID 정상 저장 확인
echo $VPCID
애플리케이션을 배포할 노드는 외부 네트워크와 통신할 수 있도록 퍼블릭 서브넷에 위치해야 한다.
아래의 명령어를 실행해서 퍼블릭 서브넷에 대한 정보를 환경 변수로 저장한다.
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
echo "export PubSubnet1=$PubSubnet1" >> /etc/profile
echo "export PubSubnet2=$PubSubnet2" >> /etc/profile
# 퍼블릭 서브넷 정상 저장 확인
echo $PubSubnet1
echo $PubSubnet2
먼저 아래의 명령어를 실행해서 EKS 배포에 필요한 환경 변수들이 정상적으로 저장되었는지 확인한다.
echo $AWS_DEFAULT_REGION
echo $CLUSTER_NAME
echo $VPCID
echo $PubSubnet1,$PubSubnet2
아래의 명령어를 실행해서 EKS 클러스터를 배포하기 전에 미리 정보를 확인한다.
--dry-run
옵션은 명령어를 실행하지 않고 명령어가 실행할 정보를 출력해준다.
eksctl create cluster \
--name $CLUSTER_NAME \
--region=$AWS_DEFAULT_REGION \
--nodegroup-name=$CLUSTER_NAME-nodegroup \
--node-type=t3.medium \
--node-volume-size=30 \
--vpc-public-subnets "$PubSubnet1,$PubSubnet2" \
--version 1.28 \
--ssh-access \
--external-dns-access \
**--dry-run** | yh
참고로 yh
는 yaml 파일을 사람이 읽기 좋은 형식으로 출력해주는 프로그램이다.
yaml syntax highlighter 라고 생각하면 된다.
아래의 명령어를 실행해서 EKS 클러스터를 배포한다. 배포에는 약 15분 정도가 소요된다.
eksctl create cluster \
--name $CLUSTER_NAME \
--region=$AWS_DEFAULT_REGION \
--nodegroup-name=$CLUSTER_NAME-nodegroup \
--node-type=t3.medium \
--node-volume-size=30 \
--vpc-public-subnets "$PubSubnet1,$PubSubnet2" \
--version 1.28 \
--ssh-access \
--external-dns-access \
--verbose 4
배포가 정상적으로 되었는 지 확인하기 위해 아래의 명령어들을 하나씩 실행하면서 확인해보자.
EKS 클러스터 정보 확인
아래의 명령어는 쿠버네티스 클러스터에 대한 기본 정보 표시한다.
kubectl cluster-info
#Kubernetes control plane is running at https://75EC6A8233D6C844A18D2E5169C5172C.sk1.ap-northeast-2.eks.amazonaws.com
#CoreDNS is running at https://75EC6A8233D6C844A18D2E5169C5172C.sk1.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
#To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
아래의 명령어는 EKS 클러스터 목록을 표시한다.
eksctl get cluster
#NAME REGION EKSCTL CREATED
#myeks ap-northeast-2 True
아래의 명령어는 EKS 클러스터의 엔드포인트를 출력한다.
aws eks describe-cluster --name $CLUSTER_NAME \
| jq -r .cluster.endpoint
#https://75EC6A8233D6C844A18D2E5169C5172C.sk1.ap-northeast-2.eks.amazonaws.com
dig 조회
아래의 명령어는 클러스터 API 서버 DNS 주소를 가져오고, 해당 주소를 사용해서 DNS 조회를 실행한다. 이를 통해 클러스터의 API 서버가 현재 사용 가능한지, 동작 중인지 확인할 수 있다.
```bash
APIDNS=$(aws eks describe-cluster --name $CLUSTER_NAME \
| jq -r .cluster.endpoint \
| cut -d '/' -f 3)
dig +short $APIDNS
#3.36.145.166
#52.79.70.15
```
EKS API 접속 시도
EKS 클러스터 API 서버에 쿠버네티스 클러스터 버전 정보를 출력한다.
curl -k -s \
$(**aws eks describe-cluster --name $CLUSTER_NAME \
| jq -r .cluster.endpoint**)/version \
| jq
#{
# "major": "1",
# "minor": "28+",
# "gitVersion": "v1.28.6-eks-508b6b3",
# "gitCommit": "25a726351cee8ee6facce01af4214605e089d5da",
# "gitTreeState": "clean",
# "buildDate": "2024-01-29T20:58:56Z",
# "goVersion": "go1.20.13",
# "compiler": "gc",
# "platform": "linux/amd64"
#}
EKS 노드 그룹 정보 확인
클러스터의 노드 그룹에 대한 세부 정보를 조회한다.
aws eks describe-nodegroup \
--cluster-name $CLUSTER_NAME \
--nodegroup-name $CLUSTER_NAME-nodegroup \
| jq
#{
# "nodegroup": {
# "nodegroupName": "myeks-nodegroup",
# "nodegroupArn": "arn:aws:eks:ap-northeast-2:265524074804:nodegroup/myeks/myeks-nodegroup/90c711dd-d34d-0535-36c1-b04bd2f96765",
# "clusterName": "myeks",
# "version": "1.28",
# "releaseVersion": "1.28.5-20240307",
# "createdAt": "2024-03-10T00:01:28.243000+09:00",
# "modifiedAt": "2024-03-10T00:22:04.199000+09:00",
# "status": "ACTIVE",
# ...
노드 정보 확인
# 노드가 사용하고 있는 EC2 인스턴스 정보 조회 (가용 영역, 인스턴스 종류, 컴퓨팅 성능)
# 여기서는 온디맨드 타입의 t3.medium 을 사용하고 있고, 가용 영역은 2a, 2c 에 걸쳐 실행되고 있다.
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
#NAME STATUS ROLES AGE VERSION INSTANCE-TYPE CAPACITYTYPE ZONE
#ip-192-168-1-229.ap-northeast-2.compute.internal Ready <none> 21m v1.28.5-eks-5e0fdde t3.medium ON_DEMAND ap-northeast-2a
#ip-192-168-2-86.ap-northeast-2.compute.internal Ready <none> 21m v1.28.5-eks-5e0fdde t3.medium ON_DEMAND ap-northeast-2c
인증 정보 확인
# 쿠버네티스 설정 파일(클러스터 접속 및 인증 정보) 출력
cat /root/.kube/config
# 현재 사용 중인 쿠버네티스 클러스터에 대한 설정 정보 출력
kubectl config view
# 쿠버네티스 컨텍스트(클러스터, 사용자, 네임스페이스 등) 출력
****kubectl ctx
# EKS 클러스터와 통신할 때 필요한 인증 토큰 생성
aws eks get-token \
--cluster-name $CLUSTER_NAME \
--region $AWS_DEFAULT_REGION
파드 정보 확인
kubectl get pod -n kube-system -o wide
kube-system
네임스페이스는 쿠버네티스 설치할 때 기본적으로 생성된다.
이 네임스페이스에는 기본적으로 apiserver, scheduler 파드도 함께 설치되지만, 이 파드는 AWS 가 관리하는 영역이기 때문에 사용자가 조회할 수 없다는 차이점이 있다.
작업용 EC2 에서 노드로 접속이 가능한 지 확인해보자.
노드들의 사설 IP 주소와 공인 IP 주소를 테이블 형식으로 조회
aws ec2 describe-instances \
--query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" \
--filters Name=instance-state-name,Values=running \
--output table
아래와 같이 출력된다.
------------------------------------------------------------------------------
| DescribeInstances |
+-----------------------------+----------------+-----------------+-----------+
| InstanceName | PrivateIPAdd | PublicIPAdd | Status |
+-----------------------------+----------------+-----------------+-----------+
| myeks-myeks-nodegroup-Node | 192.168.2.86 | 15.164.171.72 | running |
| myeks-host | 192.168.1.100 | 52.78.67.202 | running |
| myeks-myeks-nodegroup-Node | 192.168.1.229 | 13.124.213.100 | running |
+-----------------------------+----------------+-----------------+-----------+
사설 IP 를 환경 변수로 저장한다.
**N1=$(**kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
**N2=$**(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
****echo $N1, $N2
# 192.168.1.229, 192.168.2.86
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
노드 보안그룹 ID 를 환경 변수에 저장
aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text)
#sg-0ba295c06e988d049
echo $NGSGID
#sg-0ba295c06e988d049
echo "export NGSGID=$NGSGID" >> /etc/profile
노드 보안그룹에 eksctl-host 에서 노드에 접속할 수 있도록 인바운드 트래픽 허용 규칙 추가
--protocol '-1'
은 모든 프로토콜을 허용한다는 의미이다.
aws ec2 authorize-security-group-ingress \
--group-id $NGSGID \
--protocol '-1' \
--cidr **192.168.1.100/32**
ping 명령어로 노드에 접속 시도를 한다.
ping -c 2 $N1
#PING 192.168.1.229 (192.168.1.229) 56(84) bytes of data.
#64 bytes from 192.168.1.229: icmp_seq=1 ttl=255 time=0.688 ms
#64 bytes from 192.168.1.229: icmp_seq=2 ttl=255 time=0.343 ms
#
#--- 192.168.1.229 ping statistics ---
#2 packets transmitted, 2 received, 0% packet loss, time 1027ms
#rtt min/avg/max/mdev = 0.343/0.515/0.688/0.173 ms
ping -c 2 $N2
#PING 192.168.2.86 (192.168.2.86) 56(84) bytes of data.
#64 bytes from 192.168.2.86: icmp_seq=1 ttl=255 time=1.47 ms
#64 bytes from 192.168.2.86: icmp_seq=2 ttl=255 time=1.03 ms
#
#--- 192.168.2.86 ping statistics ---
#2 packets transmitted, 2 received, 0% packet loss, time 1001ms
#rtt min/avg/max/mdev = 1.034/1.255/1.476/0.221 ms
노드에 SSH 접속 시도
편의상 1번 노드만 확인한다.
# 1번 노드 사설 IP 주소 확인
echo $N1
# 1번 노드에 ssh 접속
ssh ec2-user@$N1
# 접속 성공 후 노드의 사설 IP 주소 확인
ifconfig eth0 | grep inet
#inet 192.168.1.229 netmask 255.255.255.0 broadcast 192.168.1.255
#inet6 fe80::bb:43ff:fe8c:8481 prefixlen 64 scopeid 0x20<link>
CLB 를 이용해서 간단한 애플리케이션을 배포해보자.
여기서는 마리오 게임을 배포하고자 한다.
배포에 사용할 쿠버네티스 yaml 파일은 아래와 같다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mario
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
spec:
selector:
app: mario
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
아래의 명령어를 사용해서 작업용 EC2 에 mario.yaml
로 저장한다.
cat << EOF > mario.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mario
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
spec:
selector:
app: mario
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
EOF
아래의 명령어를 실행해서 애플리케이션을 배포한다.
kubectl apply -f mario.yaml
#deployment.apps/mario created
#service/mario created
아래의 명령어를 실행해서 CLB 주소를 확인한다.
kubectl get svc mario \
-o jsonpath={.status.loadBalancer.ingress[0].hostname}
#abb03c7a52fe545faad922403fcd7744-966114085.ap-northeast-2.elb.amazonaws.com
위에서 출력한 주소를 브라우저에서 접속하면 아래의 이미지와 같이 마리오 게임이 정상적으로 배포된 것을 확인할 수 있다.
아래의 명령어를 실행해서 배포한 애플리케이션을 삭제한다.
kubectl delete -f mario.yaml
#deployment.apps "mario" deleted
#service "mario" deleted
작업용 EC2 에서 EKS 클러스터 삭제를 위해 아래의 명령어를 실행한다.
클러스터 삭제에는 약 10분 정도 소요된다.
eksctl delete cluster --name $CLUSTER_NAME
아래의 명령어를 실행해서 클러스터가 정상적으로 삭제됐는지 확인한다.
eksctl get cluster
AWS CloudFormation 스택을 삭제하기 위해 아래의 명령어를 실행한다.
aws cloudformation delete-stack --stack-name myeks
AWS EKS 를 이용한 플랫폼 엔지니어링 프로젝트를 기획하고 있는데, AWS EKS 가 어떻게 구성되어 있고, 어떻게 사용하는 지 위주로 살펴보았다.
아직 쿠버네티스에 대한 이해도가 부족해서 다른 분들처럼 깊은 이해는 못했지만, AWS 를 익히는 가장 좋은 방법은 무작정 따라해보는 것이라 생각했다.
쿠버네티스에 대한 이해도가 낮다보니 스터디에서 진행한 내용에 대한 이해를 못하고 있다는 걸 느꼈다.
그래서 이정훈 님께서 집필하신 <24단계 실습으로 정복하는 쿠버네티스> 책에 자세하게 설명이 나와있으니 참고해서 쿠버네티스에 대한 이해도를 높일 계획이다.
다른 프로젝트들을 동시에 같이 하고 있다보니 스터디에 많은 시간을 쓸 수 없던 것이 아쉽지만, 남은 기간동안 차근차근 배워가는 마음으로 스터디를 완주하고 싶다.
아직 많이 부족하지만 좋은 기회를 주신 가시다 님께 감사하다는 말씀을 전하고 싶다.