[AWS-EKS] IAM 사용자 생성 및 CloudFormation을 통한 인프라 배포

Introduction

EKS를 배포하는 방법에는 3가지가 있다고 했다. (관리콘솔, eksctl, IaC)

여기서 우리는 관리콘솔eksctl을 통해서 배포하는 방법에 대해서 알아보도록 할 것이다.

본 글에서는 이러한 배포를 하기 위한 사전작업IAM 사용자 생성 및 CloudFormation을 통한 인프라 배포를 수행할 것이다.

사전 준비

✅ 필수 🔑 IAM 사용자 생성 및 액세스 키 생성 작업

1. IAM 사용자 생성

  • 루트 계정으로 로그인하여 링크에 클릭하여 IAM 사용자 페이지에 진입합니다.
  • 사용자 추가 버튼을 클릭합니다.
  • 사용자 이름은 admin으로 입력하고 [AWS Management Console에 대한 사용자 액세스 권한 제공]을 체크합니다.
  • 사용자에게 콘솔 액세스 권한 제공은 [IAM 사용자를 생성하고 싶음]을 선택합니다.
  • 콘솔 암호는 [사용자 지정 암호]를 선택하고 생성 기준에 맞춰 각자 암호를 지정합니다.
  • 사용자는 다음 로그인 시 새 암호를 생성해야 합니다.를 체크 해제하고 다음 버튼을 클릭합니다.
  • 권한 옵션은 [직접 정책 연결]을 선택하고 권한 정책에서 [AdministratorAccess]를 체크한 후 아래 다음 버튼을 클릭합니다.
  • 검토 및 생성 페이지에서 사용자 생성 버튼을 클릭합니다.
  • 암호 검색 페이지에서 .csv 파일 다운로드 버튼을 클릭하여 자신의 PC의 디렉터리에 저장합니다.
  • 사용자 목록으로 돌아가기 버튼을 클릭하여 IAM 사용자 생성을 마무리합니다.

2. IAM 사용자 액세스 키 생성

  • IAM 사용자 페이지에서 생성한 사용자 이름을 클릭합니다.
  • 보안 자격 증명 탭을 클릭하고 [액세스 키] 영역에서 액세스 키 만들기 버튼을 클릭합니다.
  • 액세스 키 모범 사례 및 대안 페이지에서 [Command Line Interface(CLI)]를 선택하고 아래 체크 박스를 체크한 후 다음 버튼을 클릭합니다.
  • 액세스 키 만들기 버튼을 클릭합니다.
  • 액세스 키 검색 페이지에서 .csv 파일 다운로드 버튼을 클릭하여 자신의 PC의 디렉터리에 저장합니다.
  • 완료 버튼을 클릭하여 IAM 사용자 액세스 키 생성을 마무리합니다.

Note: IAM 사용자로 관리 콘솔에 로그인 할때 계정 ID가 필요하니 잘 메모해 둡니다.


CloudFormation으로 기본 인프라 배포

먼저 CloudFormation을 통해서 기본적인 VPC 및 my-eks-host 라는 배스천 호스트를 만든다. 이 배스천 호스트를 통해서 우리는 EKS 환경을 만들고 접근하여 작업하게 된다.

AWS CloudFormation 생성 : CloudFormation

cnaeb_ch1_lab_1.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'
          #FromPort: '22'
          #ToPort: '22'
          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: 20
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
          #!/bin/bash
          hostnamectl --static set-hostname "${ClusterBaseName}-host"

          # Config convenience
          echo 'alias vi=vim' >> /etc/profile
          echo "sudo su -" >> /home/ec2-user/.bashrc

          # Change Timezone
          sed -i "s/UTC/Asia\/Seoul/g" /etc/sysconfig/clock
          ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

          # Install Packages
          cd /root
          yum -y install tree jq git htop lynx

          # Install kubectl & helm
          #curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.26.2/2023-03-17/bin/linux/amd64/kubectl
          curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.25.7/2023-03-17/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 --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_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
          sudo ./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 -LO https://github.com/kubernetes-sigs/krew/releases/download/v0.4.3/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  # ktop df-pv mtail tree

          # Install node-shell
          curl -LO https://github.com/kvaps/kubectl-node-shell/raw/master/kubectl-node_shell
          chmod +x ./kubectl-node_shell
          sudo mv ./kubectl-node_shell /usr/local/bin/kubectl-node_shell

          # 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


그대로 CloudFormation을 생성해주면 된다.

⚠ 주의 사항

  • IAM 계정으로 로그인한 후 액세스 키를 발급받은 상태에서 진행한다.
  • SSH 접속을 위한 EC2-KeyPair가 발급되어 있어야 한다.

이 때, 미리 발급받아 두었던 EC2 key 페어가 필요하다. 키페어가 없다면 EC2-KeyPair에서 생성해 주도록 하자.

이과정이 완료되면 다음은 부분들이** 전부 한번에 배포가 완료**된 것을 확인할 수 있다.

배스천 호스트(EC2) 접속

이제 관리를 위해서 생성해둔 EC2 인스턴스에 접속해보자. 내 로컬 컴퓨터 CMD를 열고, 각자의 SSH 키에 해당하는 접속을 하면된다.

  • ssh 명령어 확인

  • 접속 성공

✅ 설치 확인

접속이 성공적으로 완료되었다면, 이제 제대로 설치가 완료되었는지 확인해 보자.

Ctrl + V 명령이 동작하지 않는다면, 마우스 오른쪽을 클릭하면 된다. 복사할 때도 쭉 드래그하고 마우스 오른쪽을 클릭하면 된다.

사용자 확인

whoami

whoami를 입력하여 root 사용자임을 확인합니다.

Note: root 사용자로 전환하도록 미리 설정해 두었으며, 접속 타이밍에 따라 ec2-user 사용자라면 sudo su -를 입력하여 root 사용자로 전환합니다.

기본 설치 도구 확인

// kubectl 버전 확인
kubectl version --client=true -o yaml | yh

// eksctl 버전 확인
eksctl version

// awscli 버전 확인
aws --version

// 도커 정보 확인
docker info | yh

awscli 사용을 위한 IAM 자격 증명

// awscli로 인스턴스 정보 확인 (IAM 자격 증명 X)
aws ec2 describe-instances | jq

// IAM 사용자 자격 구성
aws configure

// awscli로 인스턴스 정보 다시 확인 (IAM 자격 증명 O)
aws ec2 describe-instances | jq

IAM 사용자의 액세스 키 생성할 때 저장한 xxxx_accesskeys.csv 파일을 열어 값을 참조합니다. csv로 액세스키 비밀번호를 저장하지 않았다면, 키를 다시 생성하면 됩니다.

aws configure를 입력하여 Access Key ID, Secret Access Key, Region 코드를 입력합니다.

IAM 자격 증명이 이루어지면 awscli 도구로 인스턴스 정보를 다시 확인합니다.

EKS 배포할 VPC 정보 확인

// CLUSTER_NAME 변수 확인
echo $CLUSTER_NAME

// EKS를 배포할 myeks-VPC 정보 확인
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq

// EKS를 배포할 myeks-VPC ID 값만 확인
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId

EKS 배포할 VPC ID 변수 저장

// VPCID 라는 환경변수로 myeks-VPC ID 값을 저장
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId)

// VPCID를 전역 변수로 선언
echo "export VPCID=$VPCID" >> /etc/profile

// VPCID 변수 호출
echo $VPCID

EKS 배포할 VPC의 서브넷 정보 확인

// EKS를 배포할 VPC의 전체 서브넷 정보 확인
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --output json | jq

// EKS를 배포할 VPC의 퍼블릭 서브넷 정보 확인
aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet1" | jq

aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet2" | jq

// EKS를 배포할 VPC의 퍼블릭 서브넷 ID 값만 확인
aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text

aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text

EKS 배포할 퍼블릭 서브넷 ID 변수 저장

// 변수에 퍼블릭 서브넷 ID 값을 저장
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)

// 퍼블릭 서브넷 ID를 전역 변수로 선언
echo "export PubSubnet1=$PubSubnet1" >> /etc/profile

echo "export PubSubnet2=$PubSubnet2" >> /etc/profile

// VPCID 변수 호출
echo $PubSubnet1

echo $PubSubnet2

변수 호출 (종합)

echo $AWS_DEFAULT_REGION

echo $CLUSTER_NAME

echo $VPCID

echo $PubSubnet1,$PubSubnet2

이를 통해서 생성된 VPC 환경 및 설치된 CLI 도구들이 잘 생성된 것을 확인했다. 또한, 추후 사용할 환경 변수에도 잘 넣어주는 작업까지 완료했다.

이제 다음 과정으로 본격적으로 EKS를 배포해보도록 하자.🤩


Reference code📎 | CloudNet@와 함께하는 Amazon EKS 기본 강의


© 2023 Lee. All rights reserved.