1. EKS(Elastic Kubernetes Service)
🔠EKS란?
AWS에서 제공하는 서비스로 정확한 명칭은 Elastic Kubernetes Service이다. On-premise에서 Kubernetes를 동작하는것과 다르게 AWS에서 클러스터 관리, 보안 업데이트, 자동 확장, 로드 밸런싱을 자동으로 할 수 있어서 사용자가 애플리케이션에 집중하고 Kubernetes 클러스터의 운영 부담을 최소화할 수 있는 장점이 있다.
EKS Components
- Control Plane (마스터 노드): AWS Managed
- API Server: Kubernetes API 요청을 받아들이고 처리하는 중앙 제어 요소입니다.
- Controller Manager: 클러스터의 상태를 감시하고 조정하는 컨트롤러를 실행합니다.
- Scheduler: 새로운 파드가 어떤 워커 노드에서 실행될지 결정합니다.
- etcd: 클러스터의 상태와 구성을 저장하는 분산 데이터 스토어입니다.
- Worker Nodes (워커 노드): User Managed
- kubelet: 각 노드에서 실행되는 에이전트로, API 서버와 상호 작용하여 노드의 상태를 보고하고 파드를 실행합니다.
- kube-proxy: 네트워크 프록시로, 서비스 및 파드의 네트워크 트래픽을 로드 밸런싱하고 관리합니다.
- container runtime: 컨테이너를 실행하는 데 사용되는 런타임 환경입니다. Amazon EKS에서는 일반적으로
containerd
가 사용됩니다.
- Networking:
- Amazon VPC (Virtual Private Cloud): Amazon EKS 클러스터가 배포되는 가상 네트워크 환경입니다.
- VPC CNI (Container Networking Interface): Kubernetes 클러스터 내의 파드 간 통신 및 인터넷에 대한 네트워크 구성을 제공합니다.
- AWS Load Balancer Controller: 서비스를 외부에 노출하는 데 사용되는 로드 밸런서를 자동으로 구성하고 관리합니다.
- Storage:
- Amazon EBS (Elastic Block Store): Kubernetes 볼륨에 사용되는 영구 스토리지를 제공합니다.
- Amazon EFS (Elastic File System): 여러 파드에서 공유하는 공유 파일 시스템을 제공합니다.
- Authentication & Authorization:
- AWS IAM (Identity and Access Management): 클러스터 및 리소스에 대한 인증 및 권한 부여를 처리합니다.
- Kubernetes RBAC (Role-Based Access Control): Kubernetes 내의 리소스에 대한 엑세스를 관리합니다.
EKS Deploy
Host Server Deploy
배포 방법은 대표적으로 웹 콘솔, eksctl, IaC(CDK, CloudFormation, Terraform)등이 있으며 그 중에 템플릿을 이용한 CloudFormation 배포를 진행합니다.
myeks-1week.yaml
AWSTemplateFormatVersion: '2010-09-09'
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "<<<<< EKSCTL MY EC2 >>>>>"
Parameters:
- ClusterBaseName
- KeyName
- SgIngressSshCidr
- MyInstanceType
- LatestAmiId
- Label:
default: "<<<<< Region AZ >>>>>"
Parameters:
- TargetRegion
- AvailabilityZone1
- AvailabilityZone2
- Label:
default: "<<<<< VPC Subnet >>>>>"
Parameters:
- VpcBlock
- PublicSubnet1Block
- PublicSubnet2Block
- PrivateSubnet1Block
- PrivateSubnet2Block
Parameters:
ClusterBaseName:
Type: String
Default: myeks
AllowedPattern: "[a-zA-Z][-a-zA-Z0-9]*"
Description: must be a valid Allowed Pattern '[a-zA-Z][-a-zA-Z0-9]*'
ConstraintDescription: ClusterBaseName - must be a valid Allowed Pattern
KeyName:
Description: Name of an existing EC2 KeyPair to enable SSH access to the instances. Linked to AWS Parameter
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: must be the name of an existing EC2 KeyPair.
SgIngressSshCidr:
Description: The IP address range that can be used to communicate to the EC2 instances
Type: String
MinLength: '9'
MaxLength: '18'
Default: 0.0.0.0/0
AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
MyInstanceType:
Description: Enter t2.micro, t2.small, t2.medium, t3.micro, t3.small, t3.medium. Default is t2.micro.
Type: String
Default: t3.medium
AllowedValues:
- t2.micro
- t2.small
- t2.medium
- t3.micro
- t3.small
- t3.medium
LatestAmiId:
Description: (DO NOT CHANGE)
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
AllowedValues:
- /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
TargetRegion:
Type: String
Default: ap-northeast-2
AvailabilityZone1:
Type: String
Default: ap-northeast-2a
AvailabilityZone2:
Type: String
Default: ap-northeast-2c
VpcBlock:
Type: String
Default: 192.168.0.0/16
PublicSubnet1Block:
Type: String
Default: 192.168.1.0/24
PublicSubnet2Block:
Type: String
Default: 192.168.2.0/24
PrivateSubnet1Block:
Type: String
Default: 192.168.3.0/24
PrivateSubnet2Block:
Type: String
Default: 192.168.4.0/24
Resources:
# VPC
EksVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcBlock
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-VPC
# PublicSubnets
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone1
CidrBlock: !Ref PublicSubnet1Block
VpcId: !Ref EksVPC
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-PublicSubnet1
- Key: kubernetes.io/role/elb
Value: 1
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone2
CidrBlock: !Ref PublicSubnet2Block
VpcId: !Ref EksVPC
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-PublicSubnet2
- Key: kubernetes.io/role/elb
Value: 1
InternetGateway:
Type: AWS::EC2::InternetGateway
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref EksVPC
PublicSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref EksVPC
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-PublicSubnetRouteTable
PublicSubnetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicSubnetRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicSubnetRouteTable
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicSubnetRouteTable
# PrivateSubnets
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone1
CidrBlock: !Ref PrivateSubnet1Block
VpcId: !Ref EksVPC
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-PrivateSubnet1
- Key: kubernetes.io/role/internal-elb
Value: 1
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone2
CidrBlock: !Ref PrivateSubnet2Block
VpcId: !Ref EksVPC
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-PrivateSubnet2
- Key: kubernetes.io/role/internal-elb
Value: 1
PrivateSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref EksVPC
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-PrivateSubnetRouteTable
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateSubnetRouteTable
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateSubnetRouteTable
# EKSCTL-Host
EKSEC2SG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: eksctl-host Security Group
VpcId: !Ref EksVPC
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-HOST-SG
SecurityGroupIngress:
- IpProtocol: '-1'
CidrIp: !Ref SgIngressSshCidr
EKSEC2:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref MyInstanceType
ImageId: !Ref LatestAmiId
KeyName: !Ref KeyName
Tags:
- Key: Name
Value: !Sub ${ClusterBaseName}-host
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PublicSubnet1
GroupSet:
- !Ref EKSEC2SG
AssociatePublicIpAddress: true
PrivateIpAddress: 192.168.1.100
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeType: gp3
VolumeSize: 30
DeleteOnTermination: true
UserData:
Fn::Base64:
!Sub |
#!/bin/bash
hostnamectl --static set-hostname "${ClusterBaseName}-host"
# Config Root account
echo 'root:qwe123' | chpasswd
sed -i "s/^#PermitRootLogin yes/PermitRootLogin yes/g" /etc/ssh/sshd_config
sed -i "s/^PasswordAuthentication no/PasswordAuthentication yes/g" /etc/ssh/sshd_config
rm -rf /root/.ssh/authorized_keys
systemctl restart sshd
# Config convenience
echo 'alias vi=vim' >> /etc/profile
echo "sudo su -" >> /home/ec2-user/.bashrc
sed -i "s/UTC/Asia\/Seoul/g" /etc/sysconfig/clock
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# Install Packages
yum -y install tree jq git htop
# Install kubectl & helm
cd /root
curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.28.5/2024-01-04/bin/linux/amd64/kubectl
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
# Install eksctl
curl -sL "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_Linux_amd64.tar.gz" | tar xz -C /tmp
mv /tmp/eksctl /usr/local/bin
# Install aws cli v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip >/dev/null 2>&1
./aws/install
complete -C '/usr/local/bin/aws_completer' aws
echo 'export AWS_PAGER=""' >>/etc/profile
export AWS_DEFAULT_REGION=${AWS::Region}
echo "export AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" >> /etc/profile
# Install YAML Highlighter
wget https://github.com/andreazorzetto/yh/releases/download/v0.4.0/yh-linux-amd64.zip
unzip yh-linux-amd64.zip
mv yh /usr/local/bin/
# Install krew
curl -L https://github.com/kubernetes-sigs/krew/releases/download/v0.4.4/krew-linux_amd64.tar.gz -o /root/krew-linux_amd64.tar.gz
tar zxvf krew-linux_amd64.tar.gz
./krew-linux_amd64 install krew
export PATH="$PATH:/root/.krew/bin"
echo 'export PATH="$PATH:/root/.krew/bin"' >> /etc/profile
# Install kube-ps1
echo 'source <(kubectl completion bash)' >> /etc/profile
echo 'alias k=kubectl' >> /etc/profile
echo 'complete -F __start_kubectl k' >> /etc/profile
git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1
cat <<"EOT" >> /root/.bash_profile
source /root/kube-ps1/kube-ps1.sh
KUBE_PS1_SYMBOL_ENABLE=false
function get_cluster_short() {
echo "$1" | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT
# Install krew plugin
kubectl krew install ctx ns get-all neat # ktop df-pv mtail tree
# Install Docker
amazon-linux-extras install docker -y
systemctl start docker && systemctl enable docker
# CLUSTER_NAME
export CLUSTER_NAME=${ClusterBaseName}
echo "export CLUSTER_NAME=$CLUSTER_NAME" >> /etc/profile
# Create SSH Keypair
ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa
Outputs:
eksctlhost:
Value: !GetAtt EKSEC2.PublicIp
위의 템플릿으로 Cloud Formation 결과

- VPC 생성
- myeks-VPC
- 서브넷 생성
- myeks-PublicSubnet1 – 192.168.1.0/24 (ap-northeast-2a)
- myeks-PublicSubnet2 – 192.168.2.0/24 (ap-northeast-2c)
- myeks-PrivateSubnet3 – 192.168.3.0/24 (ap-northeast-2a)
- myeks-PrivateSubnet4 – 192.168.4.0/24 (ap-northeast-2c)
- 인터넷게이트웨이 생성
- 라우팅 테이블 생성
- [인터넷게이트웨이] – [myeks-PublicSubnet1 – 192.168.1.0/24 (ap-northeast-2a)]
- [인터넷게이트웨이] – [myeks-PublicSubnet2 – 192.168.2.0/24 (ap-northeast-2c)]
- EC2 생성
- instancetype : t3.medium
- AMI : /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
- IP address : 192.168.1.100
- EBS volume : gp3 / 30GB
- 생성 후 shell script 진행
- 호스트명 : myeks-host
- 패키지 설치 : tree, jq, git, htop, kubectl, helm, eksctl, aws_cli_v2, yh(yaml highlighter), krew, krew plugin, kube-ps1, docker
Worker Node Deploy
🔠Node
Kubernetes에서는 Docker, containerd, CRI-O 등과 같은 컨테이너 런타임을 사용하여 컨테이너를 실행합니다. 이 런타임은 kubelet에 의해 관리되며, Pod 내의 컨테이너를 생성하고 관리
eksctl deploy
# 자격 구성 설정 없이 확인
aws ec2 describe-instances
# IAM User 자격 구성 : 실습 편리를 위해 administrator 권한을 가진 IAM User 의 자격 증명 입력
aws configure
AWS Access Key ID [None]: AKIA5...
AWS Secret Access Key [None]: CVNa2...
Default region name [None]: ap-northeast-2
Default output format [None]: json
# 자격 구성 적용 확인 : 노드 IP 확인
aws ec2 describe-instances
# EKS 배포할 VPC 정보 확인
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId)
echo "export VPCID=$VPCID" >> /etc/profile
echo $VPCID
# EKS 배포할 VPC에 속한 Subnet 정보 확인
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --output json | jq
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --output yaml
## 퍼블릭 서브넷 ID 확인
aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet1" | jq
aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text
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
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

- 클러스터 이름 및 지역 설정
- cluster name : myeks
- region : ap-northeast-2
- 노드 그룹 설정
- nodegroup name : myeks-nodegroup
- node type : t3.medium
- volume size : 30GB
- VPC 및 서브넷 설정
- 각 public subnet에서 워커 노드 배포
- EKS 버전 설정
- v1.28
- SSH 및 외부 DNS 액세스 설정
- SSH 액세스
- 외부 DNS 액세스
- Verbose 모드 설정
- –verbose 4 플래그를 사용하여 자세한 정보출력
Pod Deploy
🔠pod
Kubernetes에서 가장 기본적인 배포 단위입니다. Pod은 Kubernetes 클러스터에서 실행되는 하나 이상의 컨테이너 그룹입니다. 일반적으로 한 Pod에는 주로 함께 동작하는 여러 컨테이너가 포함됩니다. 이러한 컨테이너는 네트워크와 스토리지 등을 공유하며 동일한 호스트에서 실행
Sample pod deploy
kubectl create deployment my-webs --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --replicas=3
- kubectl을 통해 Deployment 로 pod 3개 배포
- 강제 삭제 하면서 Kubernetes의 장점중 하나인 멱등성(Idempotence)을 확인


💡멱등성(idempotent) : 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질
Example
- HTTP 메서드:
- HTTP 메서드 중 PUT과 DELETE는 멱등성을 가집니다. 예를 들어, 같은 PUT 요청을 여러 번 실행하더라도 동일한 리소스가 생성되며, 같은 DELETE 요청을 여러 번 실행하더라도 동일한 리소스가 삭제됩니다.
- 수학적 연산:
- 예를 들어, 정수에 대한 절대값을 계산하는 연산은 멱등성을 가집니다. 어떤 정수에 대한 절대값을 여러 번 계산하더라도 결과는 항상 동일합니다.
- 시스템 설정 변경:
- 시스템 설정을 변경하는 작업 중에도 멱등성을 고려할 수 있습니다. 예를 들어, 설정 파일을 업데이트하는 작업이 있을 때, 동일한 설정을 여러 번 적용해도 시스템 상태는 동일하게 유지되어야 합니다.
docker-supermario pod deploy
mario.yaml
# 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
(gyuroot@myeks:default) [root@myeks-host ~]# kubectl apply -f mario.yaml
(gyuroot@myeks:default) [root@myeks-host ~]# k get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
mario 1/1 1 1 63m
(gyuroot@myeks:default) [root@myeks-host ~]# k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 9h
mario LoadBalancer 10.100.101.35 xxxxxxxxxxxxxxxxxxxxxxx.ap-northeast-2.elb.amazonaws.com 80:32385/TCP 63m
(gyuroot@myeks:default) [root@myeks-host ~]# k get ep
NAME ENDPOINTS AGE
mario 192.168.2.138:8080 63m

노드에 배포된 컨테이너 정보 확인
ctr
- Containerd 컨테이너 런타임의 CLI 도구
- ctr을 사용하면 컨테이너의 생성, 실행, 중지 및 관리와 같은 다양한 작업을 수행가능
- 주로 Containerd의 API를 활용하여 더 낮은 수준의 컨테이너 관리를 원할 때 사용
# 컨테이너 실행
ctr run --rm --tty docker.io/library/alpine:latest echo "Hello, World!"
# 컨테이너 목록 조회
ctr containers list
# 이미지 목록 조회
ctr images list
# Containerd에서 직접 컨테이너 생성
ctr container create --snapshotter=overlayfs alpine-container
nerdctl
- Docker CLI와 유사한 명령어를 제공하면서도 Docker Daemon을 필요로 하지 않고, 대신에 Containerd를 직접 조작하여 컨테이너를 실행
- nerdctl은 Docker와 호환되는 환경에서 Docker CLI 대신에 사용가능
- 주로 Docker Daemon이 실행 중이지 않은 환경이거나 독립적인 컨테이너 관리를 원할 때 사용
# 컨테이너 실행
nerdctl run --rm docker.io/library/alpine:latest echo "Hello, World!"
# 컨테이너 목록 조회
nerdctl ps
# 이미지 목록 조회
nerdctl images
crictl
- Kubernetes와 같은 CRI를 구현하는 컨테이너 런타임과 상호 작용하기 위한 CLI 도구
- Kubernetes가 컨테이너 런타임과 통신할 때 사용
- crictl을 사용하면 Kubernetes 클러스터 내의 컨테이너 런타임에 대한 정보를 조회하고, 컨테이너를 생성하고 관리하는 등의 작업을 수행
- 주로 Kubernetes 클러스터에서의 컨테이너 관리 및 디버깅에 사용
# 컨테이너 목록 조회
crictl ps
# 이미지 목록 조회
crictl images
# 컨테이너 생성
crictl runp <sandbox-id> echo "Hello, World!"