DevOps

EKS 구축 테라폼 코드 설명

우제혁 2023. 12. 18. 16:03

EKS 구축 테라폼 코드 설명

코드 구성

eks-terraform-final
    ├── providers.tf
    ├── outputs.tf
    ├── variables.tf
    ├── workstation-external-ip.tf
    ├── eks-cluster.tf
    ├── eks-worker-nodes.tf
    ├── vpc.tf
    └── script
        ├── ingrass
        │   ├── iam_policy.json
        │   ├── v2_5_4_full.yaml
        │   └── v2_5_4_ingclass.yaml
        ├── service
        │   ├── flask-deployment.yaml
        │   ├── flask-ingress.yaml
        └── └── flask-service.yaml

providers.tf

  • providers.tf 전체 코드
더보기
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

terraform {
  required_version = ">= 0.12"
}

provider "aws" {
  region = var.aws_region
}

data "aws_availability_zones" "available" {}

# Not required: currently used in conjunction with using
# icanhazip.com to determine local workstation external IP
# to open EC2 Security Group access to the Kubernetes cluster.
# See workstation-external-ip.tf for additional information.
provider "http" {}

  • terraform: Terraform 설정 블록으로, Terraform 자체에 대한 구성을 지정
    • 최소한 Terraform 버전 0.12 요구
  • provider "aws": AWS 프로바이더를 정의.
    • variables.tf에서 정의된 var.aws_region으로 지역을 설정
  • data "aws_availability_zones" "available" {}: AWS의 사용 가능한 가용 영역을 가져오기 위한 데이터 소스를 정의
    • AWS에서 제공하는 데이터 소스로, 가용 영역 정보를 조회하여 사용할 수 있다.
  • provider "http" {}: HTTP 프로바이더를 정의.
    • workstation-external-ip.tf를 사용하여 외부 IP를 확인하고 EC2 보안 그룹에 대한 액세스를 열 때 사용.

outputs.tf

  • outputs.tf 전체 코드
더보기
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

#
# Outputs
#

locals {
  config_map_aws_auth = <<CONFIGMAPAWSAUTH


apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: ${aws_iam_role.demo-node.arn}
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes
CONFIGMAPAWSAUTH

  kubeconfig = <<KUBECONFIG


apiVersion: v1
clusters:
- cluster:
    server: ${aws_eks_cluster.demo.endpoint}
    certificate-authority-data: ${aws_eks_cluster.demo.certificate_authority[0].data}
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: aws
  name: aws
current-context: aws
kind: Config
preferences: {}
users:
- name: aws
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: aws-iam-authenticator
      args:
        - "token"
        - "-i"
        - "${var.cluster_name}"
KUBECONFIG
}

output "config_map_aws_auth" {
  value = local.config_map_aws_auth
}

output "kubeconfig" {
  value = local.kubeconfig
}

 

Terraform의 출력(Output)을 정의하는 부분으로, Terraform이 실행된 후 생성된 값들을 사용자에게 보고하는 역할을 한다.

terraform이 실행된후 위에 정의된 값들이 출력된다.

variables.tf

  • variables.tf 전체코드
더보기
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

variable "aws_region" {
  default = "us-east-2"
}

variable "cluster_name" {
  default = "eks-JaeHyuk"
  type    = string
}

Terraform 변수를 정의하는 부분으로, 사용자가 구성 파일에서 지정할 수 있다.

var.aws_region var.cluster_name 으로 default 값을 사용할 수 있다.

workstation-external-ip.tf

  • workstation-external-ip.tf 전체 코드
더보기
data "http" "workstation-external-ip" {
  url = "http://ipv4.icanhazip.com"
}

# Override with variable or hardcoded value if necessary
locals {
  workstation-external-cidr = "${chomp(data.http.workstation-external-ip.response_body)}/32"
}

"http://ipv4.icanhazip.com을 통해 외부 ip를 알아낼 수 있다.

locals.workstation-external-cidr 로 사용될 수 있으며 이는 vpc 설정에 이용된다.

eks-cluster.tf

  • eks-cluster.tf 전체 코드
더보기
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

#
# EKS Cluster Resources
#  * IAM Role to allow EKS service to manage other AWS services
#  * EC2 Security Group to allow networking traffic with EKS cluster
#  * EKS Cluster
#

resource "aws_iam_role" "demo-cluster" {
  name = "terraform-eks-demo-cluster"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "eks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "demo-cluster-AmazonEKSClusterPolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
  role       = aws_iam_role.demo-cluster.name
}

resource "aws_iam_role_policy_attachment" "demo-cluster-AmazonEKSVPCResourceController" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
  role       = aws_iam_role.demo-cluster.name
}

resource "aws_iam_role_policy_attachment" "demo-cluster-AdministratorAccess" {
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"  # AdministratorAccess 정책 추가
  role       = aws_iam_role.demo-cluster.name
}

resource "aws_security_group" "demo-cluster" {
  name        = "terraform-eks-demo-cluster"
  description = "Cluster communication with worker nodes"
  vpc_id      = aws_vpc.demo.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "terraform-eks-demo"
  }
}

resource "aws_security_group_rule" "demo-cluster-ingress-workstation-https" {
  cidr_blocks       = [local.workstation-external-cidr]
  description       = "Allow workstation to communicate with the cluster API Server"
  from_port         = 443
  protocol          = "tcp"
  security_group_id = aws_security_group.demo-cluster.id
  to_port           = 443
  type              = "ingress"
}

resource "aws_eks_cluster" "demo" {
  name     = var.cluster_name
  role_arn = aws_iam_role.demo-cluster.arn

  vpc_config {
    security_group_ids = [aws_security_group.demo-cluster.id]
    subnet_ids         = aws_subnet.demo[*].id
  }

  depends_on = [
    aws_iam_role_policy_attachment.demo-cluster-AmazonEKSClusterPolicy,
    aws_iam_role_policy_attachment.demo-cluster-AmazonEKSVPCResourceController,
    aws_iam_role_policy_attachment.demo-cluster-AdministratorAccess,
  ]
}

클러스터 IAM 역할 생성

Amazon EKS 서비스가 다른 AWS 서비스를 관리할 수 있도록 하는 IAM 역할을 생성

  • "Effect": "Allow": 이 정책 스타먼트는 허용(Allow)을 의미.
  • "Principal": AssumeRole을 통해 맡을 수 있는 역할을 지정 여기서는 "Service": "eks.amazonaws.com"으로 설정하여 EKS 서비스가 이 역할을 맡을 수 있도록 한다.
  • "Action": "sts:AssumeRole": 이 역할을 맡을 때 수행할 수 있는 액션을 지정. 여기서는 다른 역할을 맡을 수 있는 권한을 갖도록 한다.

IAM 역할에 정책을 연결

앞서 생성한 IAM 역할에 정책을 연결

VPC (Virtual Private Cloud) 보안 그룹을 생성

  • EKS 클러스터와 워커 노드 간의 통신을 관리하는 VPC 보안 그룹을 생성.
  • egress 설정은 모든 트래픽을 허용.

보안 그룹 규칙을 생성

클러스터 API 서버와 워크스테이션 간의 통신을 허용하는 보안 그룹 규칙을 생성

  • cidr_blocks: 보안 그룹 규칙을 적용할 IP 대역을 지정.
    • workstation-external-ip.tf 파일에서 정의 된 local.workstation-external-cidr에 의해 워크스테이션의 외부 IP 대역으로 설정되어 있다.
  • description: 보안 그룹 규칙에 대한 설명을 지정
  • from_port, to_port: 허용할 포트 범위를 지정하여 443 포트만을 대상으로 하고 있다.
  • protocol: 규칙이 적용되는 프로토콜을 지정.
  • security_group_id: 보안 그룹 규칙이 적용될 보안 그룹의 ID를 지정
  • type: 규칙의 유형을 지정
    • "ingress" : 워크스테이션에서 클러스터로 들어오는 트래픽을 허용하는 규칙.

Amazon EKS 클러스터를 생성

  • name = var.cluster_name: 클러스터의 이름을 지정하는 속성.
    • 이 값은 variables.tf 파일에 의해 변수가 지정되어 있다.
  • role_arn = aws_iam_role.demo-cluster.arn: 클러스터를 관리하는 데 사용될 IAM 역할의 Amazon 리소스 이름(ARN)을 지정
    • 앞서 정의한 IAM 역할인 aws_iam_role.demo-cluster의 ARN이 여기에 사용.
  • vpc_config: VPC 구성에 대한 설정 블록.
    • security_group_ids = [aws_security_group.demo-cluster.id]: 클러스터에서 사용할 보안 그룹의 ID를 지정.
      • 이 보안 그룹은 클러스터와 워커 노드 간의 통신을 관리.
    • subnet_ids = aws_subnet.demo[*].id: 클러스터에서 사용할 서브넷의 ID를 지정.
      • 여러 서브넷을 사용하여 다양한 가용 영역에 분산된 워커 노드를 배치할 수 있다.
  • depends_on: 클러스터의 IAM 역할과 연결된 정책들이 모두 적용된 후에 클러스터를 생성하도록 의존성을 설정

 

eks-worker-nodes.tf

  • eks-worker-nodes.tf 전체코드
더보기
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

#
# EKS Worker Nodes Resources
#  * IAM role allowing Kubernetes actions to access other AWS services
#  * EKS Node Group to launch worker nodes
#

resource "aws_iam_role" "demo-node" {
  name = "terraform-eks-demo-node"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "demo-node-AmazonEKSWorkerNodePolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
  role       = aws_iam_role.demo-node.name
}

resource "aws_iam_role_policy_attachment" "demo-node-AmazonEKS_CNI_Policy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
  role       = aws_iam_role.demo-node.name
}

resource "aws_iam_role_policy_attachment" "demo-node-AmazonEC2ContainerRegistryReadOnly" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
  role       = aws_iam_role.demo-node.name
}

resource "aws_iam_role_policy_attachment" "demo-node-AdministratorAccess" {
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"  # AdministratorAccess 정책 추가
  role       = aws_iam_role.demo-node.name
}


resource "aws_eks_node_group" "demo" {
  cluster_name    = aws_eks_cluster.demo.name
  node_group_name = "JaeHyuk-node-group"
  node_role_arn   = aws_iam_role.demo-node.arn
  subnet_ids      = aws_subnet.demo[*].id
  instance_types = [ "t3.xlarge" ]

  scaling_config {
    desired_size = 4
    max_size     = 5
    min_size     = 1
  }

  depends_on = [
    aws_iam_role_policy_attachment.demo-node-AmazonEKSWorkerNodePolicy,
    aws_iam_role_policy_attachment.demo-node-AmazonEKS_CNI_Policy,
    aws_iam_role_policy_attachment.demo-node-AmazonEC2ContainerRegistryReadOnly,
    aws_iam_role_policy_attachment.demo-node-AdministratorAccess,
  ]
  
}

노드 IAM 역할 생성

  • 노드 그룹을 위한 IAM 역할을 정의
  • "Action": "sts:AssumeRole": 이 역할을 맡을 때 수행할 수 있는 액션을 지정. 여기서는 다른 역할을 맡을 수 있는 권한을 갖도록 한다.

IAM 역할에 정책을 연결

AmazonEKSWorkerNodePolicy는 노드가 EKS 클러스터에 가입할 수 있도록 허용하는 권한을 제공

Amazon EKS 노드 그룹을 정의

  • cluster_name: 노드 그룹이 속할 EKS 클러스터의 이름을 지정.
  • node_group_name: 노드 그룹의 이름을 지정.
  • node_role_arn: 노드 그룹이 사용할 IAM 역할의 Amazon 리소스 이름(ARN)을 지정.
  • subnet_ids: 노드가 배치될 서브넷의 ID를 지정.
    • vpc.tf의 설정을 참고 한다.
  • instance_types: 노드 인스턴스의 유형을 지정.
💡 "t3.xlarge” 는 유로 인스턴스 유형임으로 과금에 주의해야한다.
  • scaling_config: 노드 그룹의 자동 크기 조정 구성을 지정.
  • depends_on: 클러스터의 IAM 역할과 연결된 정책들이 모두 적용된 후에 클러스터를 생성하도록 의존성을 설정

vpc.tf

  • vpc.tf 전체 코드
더보기
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

#
# VPC Resources
#  * VPC
#  * Subnets
#  * Internet Gateway
#  * Route Table
#

resource "aws_vpc" "demo" {
  cidr_block = "10.0.0.0/16"

  tags = tomap({
    "Name"                                      = "terraform-eks-demo-node",
    "kubernetes.io/cluster/${var.cluster_name}" = "shared",
    "kubernetes.io/role/elb" = "1"
  })
}

resource "aws_subnet" "demo" {
  count = 2

  availability_zone       = data.aws_availability_zones.available.names[count.index]
  cidr_block              = "10.0.${count.index}.0/24"
  map_public_ip_on_launch = true
  vpc_id                  = aws_vpc.demo.id

  tags = tomap({
    "Name"                                      = "terraform-eks-demo-node",
    "kubernetes.io/cluster/${var.cluster_name}" = "shared",
    "kubernetes.io/role/elb" = "1"
  })
}

resource "aws_internet_gateway" "demo" {
  vpc_id = aws_vpc.demo.id

  tags = {
    Name = "terraform-eks-demo"
  }
}

resource "aws_route_table" "demo" {
  vpc_id = aws_vpc.demo.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.demo.id
  }
}

resource "aws_route_table_association" "demo" {
  count = 2

  subnet_id      = aws_subnet.demo[count.index].id
  route_table_id = aws_route_table.demo.id
}

AWS VPC를 정의

  • cidr_block은 VPC의 IP 주소 범위를 나타낸다.
    • 여러 태그를 정의하여 리소스에 메타데이터를 추가합니다.

 

 

 

 

AWS 서브넷을 정의

  • count : 서브넷의 수를 지정 → 2 : 두 개의 서브넷을 생성
    • 각 서브넷은 특정 가용 영역에 배치.
  • cidr_block : 각 서브넷의 IP 주소 범위를 나타낸다.
  • map_public_ip_on_launch : 서브넷으로 시작된 인스턴스에 퍼블릭 IP 주소가 할당되어야 함을 나타내려면 true를 지정 (Default is false)
  • vpc_id : 클러스터와 연결된 vpc의 id( = The ID of the subnet)

AWS 인터넷 게이트웨이를 정의

VPC에 연결되어 인터넷과의 통신을 제공

AWS 라우트 테이블을 정의

  • aws_route_table: AWS 라우트 테이블을 정의.
    • 0.0.0.0/0 : 모든 트래픽을 인터넷 게이트웨이로 보내기 위한 기본 경로가 설정

서브넷과 라우트 테이블 간의 연결을 정의

  • aws_route_table_association: 서브넷과 라우트 테이블 간의 연결을 정의
    • 두 개의 서브넷이 각각의 라우트 테이블과 연결

 

 

Trouble Shooting

서브넷 관리 : https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/network_reqs.html

  • 서브넷에 로드 밸런서를 배포하려는 경우 다음과같은 서브넷 태그가 있어야한다.
    • 프라이빗 서브넷
      • kubernetes.io/role/internal-elb 1
    • 퍼블릭 서브넷
      • kubernetes.io/role/elb 1
  • 버전이 Kubernetes 이하인 1.18 클러스터가 생성되면 Amazon EKS에서 지정된 모든 서브넷에 다음 태그를 추가했어야했다.
    • kubernetes.io/cluster/my-cluster shared