EKS 스터디 CloudNet@팀의 AEWS 2기에 작성된 자료를 베이스로 작성된 블로깅입니다.
Terraform
테라폼 소개 : 하시코프사에서 공개한 IaC 도구, Use infrastructure as code to provision and manage any infrastructure across your organization
자세한 테라폼 내용은 이 블로그 Terraform 카테고리에
mkdir learn-terraform
cd learn-terraform
touch main.tf
learn_terraform이라는 디렉토리를 만들고, main.tf파일을 생성합니다.
aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId, Name]' --output text
aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId]' --output text
AL2ID=`aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId]' --output text`
echo $AL2ID
Amazon Linux 2 최신 ami id 찾아서 변수에 저장해줍니다.
# [터미널1] EC2 생성 모니터링
export AWS_PAGER=""
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done
모니터링을 걸어줍니다.
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "$AL2ID"
instance_type = "t2.micro"
}
main.tf 파일을 수정합니다.
# 초기화
terraform init
ls -al
tree .terraform
Terraform init으로 초기화를 실행해줍니다.
# plan 확인
terraform plan
# apply 실행
terraform apply
Enter a value: yes 입력
Terraform plan으로 생길 리소스들에 대해서 확인해줍니다.
# ec2 생성 확인 : aws 웹 관리 콘솔에서도 확인 - 서울 리전 선택
export AWS_PAGER=""
aws ec2 describe-instances --output table
# 테라폼 정보 확인
terraform state list
terraform show
terraform show aws_instance.example
배포를 실행해줍니다. 모니터링 화면에 생성된 인스턴스에 대한 설명 확인할 수 있습니다.
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "$AL2ID"
instance_type = "t2.micro"
tags = {
Name = "aews-study"
}
}
EOT
수정합니다.
tag에 aews-study를 넣어줍니다.
terraform plan
terraform apply
Terraform plan으로 확인해보니 tags가 생길 예정이라고 알려줍니다.
어플라이를 하니
태그 값에서 aews-study가 추가된 것을 확인할 수 있었습니다.
콘솔에서도 해당 인스턴스 확인할 수 있었고, 태그값도 잘 들어가있는거 확인 가능했습니다.
클린업
# 리소스 삭제
terraform destroy
Enter a value: yes 입력
혹은
terraform destroy -auto-approve
VPC 배포
default VPC 대신 직접 VPC를 만들고, 해당 VPC내에 EC2 1대를 배포
# 신규 디렉터리 생성
mkdir my-vpc-ec2
cd my-vpc-ec2
디렉토리 생성해줍니다.
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "aews-study"
}
}
위 내용으로 Vpc.tf를 생성해줍니다.
terraform init && terraform plan && terraform apply -auto-approve
테라폼 이니셜라이징과, 플랜, 그리고 어플라이를 해줍니다.
만들어질 리소스를 확인하고, 생성되었다는 메세지를 확인하면, 확인해봅니다.
콘솔에서도 위에서 설정한 이름과 아이피 대역으로 vpc가 생성된것을 확인할 수 있습니다.
자세히 확인해보면 DNS호스트 이름은 비활성화 되고, DNS 확인은 활성화 되어있는것을 확인할 수 있습니다.
terraform state list
terraform state show aws_vpc.myvpc
위 명령어들로 생성된 Vpc에 대한 세부 정보를 확인할 수 있습니다.
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "aews-study"
}
}
DNS설정 부분을 둘 다 true로 설정해줍니다.
terraform plan && terraform apply -auto-approve
테라폼 플랜과 어플라이를 합니다.
바뀔 리소스들에 대해서 알려주는데 , enable_dns_hostnames = false -> true 로 활성화 될것이라고 알려줍니다.
콘솔에서 확인해보면 DNS 호스트 이름이 활성화로 바껴있는것을 확인할 수 있습니다.
확인해보면 VPC에 딸린 서브넷은 존재하지 않습니다.
vpc.tf 코드 내용 수정 : 서브넷 2개 생성 추가
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "aews-study"
}
}
resource "aws_subnet" "mysubnet1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "mysubnet2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "aews-subnet2"
}
}
output "aws_vpc_id" {
value = aws_vpc.myvpc.id
}
위 내용으로 vpc.tf을 수정해줍니다.
terraform plan && terraform apply -auto-approve
테라폼 플랜과 어플라이로 생성될 리소스들에 대해서 확인해줍니다.
마지막 부분을 확인하면, 서브넷 두개가 생성된것을 확인할 수 있습니다.
AWS 콘솔에서 확인해보면 이전에는 Vpc 리소스맵에서 확인할 수 없었던 서브넷 2개가 생성된것을 확인할 수 있습니다.
vpc.tf 코드 내용 수정 : IGW 인터넷 게이트웨이 추가
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "mysubnet1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "mysubnet2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "myigw" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "t101-igw"
}
}
output "aws_vpc_id" {
value = aws_vpc.myvpc.id
}
IGW를 추가하는 블럭을 선언한 뒤 저장해줍니다.
terraform plan && terraform apply -auto-approve
플랜 뒤 apply를 하도록 명령을 합니다.
igw가 생성이 되었고, 서브넷 한개에 대해 aews-study에서 t101-study가 바뀌었다고 합니다.
# 코드 가져오기
git clone https://github.com/gasida/aews-cicd.git
cd aews-cicd/4
# terraform 환경 변수 저장
export TF_VAR_KeyName=[각자 ssh keypair]
export TF_VAR_KeyName='kp-gasida'
echo $TF_VAR_KeyName
#
terraform init
terraform plan
# 10분 후 배포 완료
terraform apply -auto-approve
서브넷 이름이 그 직전 apply때와 비교했을때 t101-subnet 1,2로 통일되어 바뀐것을 확인할 수 있습니다.
해당 apply에 따른 igw 생성된 것 확인 할 수 있습니다.
vpc.tf 코드 내용 수정 : IGW 인터넷 게이트웨이로 전달하는 디폴트 라우팅 정보 추가
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "mysubnet1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "mysubnet2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "myigw" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "t101-igw"
}
}
resource "aws_route_table" "myrt" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "t101-rt"
}
}
resource "aws_route_table_association" "myrtassociation1" {
subnet_id = aws_subnet.mysubnet1.id
route_table_id = aws_route_table.myrt.id
}
resource "aws_route_table_association" "myrtassociation2" {
subnet_id = aws_subnet.mysubnet2.id
route_table_id = aws_route_table.myrt.id
}
resource "aws_route" "mydefaultroute" {
route_table_id = aws_route_table.myrt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.myigw.id
}
output "aws_vpc_id" {
value = aws_vpc.myvpc.id
}
위와같이 Igw 인터넷 게이트웨이로 전달하는 디폴트 라우팅 블록을 추가해줍니다.
어플라이 후 라우트 테이블이 생성되었고, 이 라우트 테이블에서 2가지 연결이 생성되었다는 로그를 확인가능합니다.
어플라이 후 VPC 리소스 맵 화면을 확인하면 서브넷 두개가 새로 생성된 라우트 테이블에 연결되었고, igw까지 연결이 된것을 확인할 수 있습니다.
terraform state list
생성될 리소스들에 대한 확인을 할 수 있습니다.
보안그룹/EC2 배포
sg.tf, ec2.tf 이 두 파일을 만들어서 진행합니다.
sg.tf 파일 생성 : 보안그룹 생성
resource "aws_security_group" "mysg" {
vpc_id = aws_vpc.myvpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "mysginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
resource "aws_security_group_rule" "mysgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.mysg.id
}
terraform plan && terraform apply -auto-approve
테라폼 플랜으로 리소스 확인후 어플라이를 해줍니다.
보안그룹 인바운드와 아웃바운드가 생겼다는 로그 확인 가능합니다.
이 Terraform apply로 생성된 보안그룹 확인 가능합니다.
인바운드와 아웃바운드 규칙도 확인 가능합니다.
ec2.tf 파일 생성 : EC2 생성
data "aws_ami" "my_amazonlinux2" {
most_recent = true
filter {
name = "owner-alias"
values = ["amazon"]
}
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-ebs"]
}
owners = ["amazon"]
}
resource "aws_instance" "myec2" {
depends_on = [
aws_internet_gateway.myigw
]
ami = data.aws_ami.my_amazonlinux2.id
associate_public_ip_address = true
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.mysg.id}"]
subnet_id = aws_subnet.mysubnet1.id
user_data = <<-EOF
#!/bin/bash
wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
mv busybox-x86_64 busybox
chmod +x busybox
echo "Web Server</h1>" > index.html
nohup ./busybox httpd -f -p 80 &
EOF
user_data_replace_on_change = true
tags = {
Name = "t101-myec2"
}
}
output "myec2_public_ip" {
value = aws_instance.myec2.public_ip
description = "The public IP of the Instance"
}
ec2.tf파일을 생성하고 위 내용을 넣어줍니다.
terraform plan && terraform apply -auto-approve
어플라이 후 i-0a72efa9d17c6e5f4라는 인스턴스 Id를 가진 ec2한대가 생성되었다는 로그 확인 할 수 있습니다.
AWS 콘솔에서 확인해보면 초기화중인 ec2한대 확인 가능합니다. 인스턴스 ID역시 일치합니다.
terraform output -raw myec2_public_ip
MYIP=$(terraform output -raw myec2_public_ip
while true; do curl --connect-timeout 1 http://$MYIP/ ; echo "------------------------------"; date; sleep 1; done
생성된 ec2에 curl 요청을 하면 잘 가는것 확인 할 수 있습니다.
클린업
terraform destroy -auto-approve
EKS
# 코드 가져오기
git clone https://github.com/gasida/aews-cicd.git
cd aews-cicd/4
# terraform 환경 변수 저장
export TF_VAR_KeyName='ecs'
echo $TF_VAR_KeyName
#
terraform init
terraform plan
# 10분 후 배포 완료
terraform apply -auto-approve
이렇게 쭉 생성이 된다는 로그가 나온 뒤 완료가 됩니다.
AWS 콘솔상에서도 클러스터가 생성된 것 확인가능합니다.
주요 생성 자원 : VPC, Subnet, IGW, NATGW, Routing Table, EKS, EKS SG인것을 확인할 수 있습니다.
➜ terraform state list
data.aws_caller_identity.current
data.aws_eks_cluster.cluster
data.aws_eks_cluster_auth.cluster
aws_iam_policy.external_dns_policy
aws_iam_role_policy_attachment.external_dns_policy_attach
aws_security_group.node_group_sg
kubernetes_service_account.aws_lb_controller
module.aws_load_balancer_controller_irsa_role.data.aws_caller_identity.current
module.aws_load_balancer_controller_irsa_role.data.aws_iam_policy_document.load_balancer_controller[0]
module.aws_load_balancer_controller_irsa_role.data.aws_iam_policy_document.this[0]
module.aws_load_balancer_controller_irsa_role.data.aws_partition.current
module.aws_load_balancer_controller_irsa_role.aws_iam_policy.load_balancer_controller[0]
module.aws_load_balancer_controller_irsa_role.aws_iam_role.this[0]
module.aws_load_balancer_controller_irsa_role.aws_iam_role_policy_attachment.load_balancer_controller[0]
module.eks.data.aws_caller_identity.current
module.eks.data.aws_eks_addon_version.this["coredns"]
module.eks.data.aws_eks_addon_version.this["kube-proxy"]
module.eks.data.aws_eks_addon_version.this["vpc-cni"]
module.eks.data.aws_iam_policy_document.assume_role_policy[0]
module.eks.data.aws_iam_session_context.current
module.eks.data.aws_partition.current
module.eks.data.tls_certificate.this[0]
module.eks.aws_ec2_tag.cluster_primary_security_group["Environment"]
module.eks.aws_ec2_tag.cluster_primary_security_group["Terraform"]
module.eks.aws_eks_access_entry.this["admin"]
module.eks.aws_eks_access_policy_association.this["admin_myeks"]
module.eks.aws_eks_addon.this["coredns"]
module.eks.aws_eks_addon.this["kube-proxy"]
module.eks.aws_eks_addon.this["vpc-cni"]
module.eks.aws_eks_cluster.this[0]
module.eks.aws_iam_openid_connect_provider.oidc_provider[0]
module.eks.aws_iam_policy.cluster_encryption[0]
module.eks.aws_iam_role.this[0]
module.eks.aws_iam_role_policy_attachment.cluster_encryption[0]
module.eks.aws_iam_role_policy_attachment.this["AmazonEKSClusterPolicy"]
module.eks.aws_iam_role_policy_attachment.this["AmazonEKSVPCResourceController"]
module.eks.aws_security_group.cluster[0]
module.eks.aws_security_group.node[0]
module.eks.aws_security_group_rule.cluster["ingress_nodes_443"]
module.eks.aws_security_group_rule.node["egress_all"]
module.eks.aws_security_group_rule.node["ingress_cluster_443"]
module.eks.aws_security_group_rule.node["ingress_cluster_4443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_6443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_8443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_9443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_kubelet"]
module.eks.aws_security_group_rule.node["ingress_nodes_ephemeral"]
module.eks.aws_security_group_rule.node["ingress_self_coredns_tcp"]
module.eks.aws_security_group_rule.node["ingress_self_coredns_udp"]
module.eks.time_sleep.this[0]
module.eks-external-dns.data.aws_region.current
module.vpc.aws_default_network_acl.this[0]
module.vpc.aws_default_route_table.default[0]
module.vpc.aws_default_security_group.this[0]
module.vpc.aws_eip.nat[0]
module.vpc.aws_internet_gateway.this[0]
module.vpc.aws_nat_gateway.this[0]
module.vpc.aws_route.private_nat_gateway[0]
module.vpc.aws_route.public_internet_gateway[0]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.public[0]
module.vpc.aws_route_table_association.private[0]
module.vpc.aws_route_table_association.private[1]
module.vpc.aws_route_table_association.private[2]
module.vpc.aws_route_table_association.public[0]
module.vpc.aws_route_table_association.public[1]
module.vpc.aws_route_table_association.public[2]
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.private[2]
module.vpc.aws_subnet.public[0]
module.vpc.aws_subnet.public[1]
module.vpc.aws_subnet.public[2]
module.vpc.aws_vpc.this[0]
module.eks.module.eks_managed_node_group["default"].data.aws_caller_identity.current
module.eks.module.eks_managed_node_group["default"].data.aws_iam_policy_document.assume_role_policy[0]
module.eks.module.eks_managed_node_group["default"].data.aws_partition.current
module.eks.module.eks_managed_node_group["default"].aws_eks_node_group.this[0]
module.eks.module.eks_managed_node_group["default"].aws_iam_role.this[0]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.additional["myeksExternalDNSPolicy"]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.this["AmazonEC2ContainerRegistryReadOnly"]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.this["AmazonEKSWorkerNodePolicy"]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.this["AmazonEKS_CNI_Policy"]
module.eks.module.eks_managed_node_group["default"].aws_launch_template.this[0]
module.eks.module.kms.data.aws_caller_identity.current[0]
module.eks.module.kms.data.aws_iam_policy_document.this[0]
module.eks.module.kms.data.aws_partition.current[0]
module.eks.module.kms.aws_kms_alias.this["cluster"]
module.eks.module.kms.aws_kms_key.this[0]
module.eks.module.eks_managed_node_group["default"].module.user_data.null_resource.validate_cluster_service_cidr
생성된 자원들도 확인 가능합니다.
노드그룹도 정상적으로 생성 되었고
# EKS 클러스터 인증 정보 업데이트
CLUSTER_NAME=myeks
aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME
kubectl config rename-context "arn:aws:eks:ap-northeast-2:$(aws sts get-caller-identity --query 'Account' --output text):cluster/$CLUSTER_NAME" "Aews-Labs"
#
kubectl get node -v=6
kubectl cluster-info
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get pod -A
#
# ExternalDNS
MyDomain=<자신의 도메인>
MyDomain=crowsnest.click
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnzHostedZoneId
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
kubectl annotate service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=kubeopsview.$MyDomain"
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=1.5"
# AWS LB Controller
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
--set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
# gp3 스토리지 클래스 생성
kubectl apply -f https://raw.githubusercontent.com/gasida/PKOS/main/aews/gp3-sc.yaml
위 정보로 EKS 배포 확인 및 기본 환경설정이 가능합니다.
코드 재사용 검증
cd ..
mkdir 5
cd 5
cp ../4/*.tf .
ls
terraform init
terraform apply -auto-approve -var=ClusterBaseName=myeks2 -var=KubernetesVersion="1.28"
EKS myeks2라는 이름으로 버전 1.28버전으로 배포합니다.
이렇게 위에서 terraform apply한 것 처럼 myeks2 1.28버전으로 생성된것을 확인 가능합니다.
# EKS 클러스터 인증 정보 가져오기
CLUSTER_NAME2=myeks2
aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME2 --kubeconfig ./myeks2config
# EKS 클러스터 정보 확인
kubectl --kubeconfig ./myeks2config get node
kubectl --kubeconfig ./myeks2config get pod -A
클린업
# 첫번째 클러스터 삭제
# CLB는 terraform으로 배포하지 않고 직접 수동으로 배포되었으니, 수동으로 삭제를 해주어야 함
helm uninstall kube-ops-view --namespace kube-system
# 클러스터 삭제
terraform destroy -auto-approve
# 두번째 클러스터 삭제
terraform destroy -auto-approve -var=ClusterBaseName=myeks2 -var=KubernetesVersion="1.28"
8주간의 스터디가 끝이 났네요... 정리한 내용들 다시 보고 계속 보면서 내용익힐 수 있도록 하겠습니다.
8주간 열심히 강의해주신 가시다님 감사하고, 같이 수고해주신 2기분들 고생하셨습니다.
댓글