ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [AWS] EKS - 구축하기(스터디 1주차)
    Infra/Cloud 2023. 4. 24. 10:34

    최근 Gasida님 스터디에 합류하게 되었고 보통은 Notion에 정리하다가 오랜만에 블로그에 글을 작성하게 되었다!

     

    이번 포스팅에서는 AWS의 EKS(Elastic Kubernetes Service)를 구축해보려고 한다.

     

    참고로 EKS의 경우 유료 서비스이다. 때문에 사용 후, 과금이 두렵다면 반드시 삭제해야한다(정지가 읎다..ㅠ)

    개인적으로 terraform을 이용해서 EKS없이 구축을 해보았으나 kubernetes에 필요한 최소사양으로 인해 free tier의 인스턴스로는 한계가 있다(AWS를 사용하면서 과금을 피할수는 없음). 또한 최근 M2 Macbook Pro를 구매하여 기존의 로컬환경에 사용하던 가상머신 이슈도 조금 있었기 때문에 해결이 귀찮다면 공부할겸 클라우드를 사용하는 것을 추천한다
     

    + 추가적으로 AWS에서 제공하는 다양한 기능들이 있어 생각보다 절대적인 시간 투자가 많이 필요할 것으로 보여진다.

     

     


    EKS란?

    Kubernetes의 controlplane 또는 node를 설치, 운영 및 유지 관리가 필요없는 AWS에서 제공하는 관리형 서비스이다. 
    기존에 On-premise 환경에서 구축해서 사용해본 경험이 있다면 조금은 다른(?) 클러스터의 느낌을 받을 수 있다. 물론 CKA, CKAD, CKS 등 자격증 시험을 본적(최근 근황을 살짝 말하자면 전부 취득 완료했다^^)이 있다면 비슷한 환경을 경험해봤을 것같다. on-premise 환경에서 Kubernetes 구축시 Master node(Controlplane)에 직접 접속하여 중요 컴포넌트에 대한 관리가 가능하였으나 EKS의 경우 Controlplane 환경은 AWS에서 관리한다는 차이점이 있었다. 아키텍처에 대한 내용은 뒤에서 다시 설명하도록 하겠다.

     

     

     

    AWS 계정 만들기

    우선 AWS계정은 있다는 가정하에 IAM을 생성해보자. 사실 IAM 생성은 여러 블로그에 방법이 있으니 간략하게 생성해보겠다. 왼쪽 그림과같이 기본적으로 생성한 AWS 계정을 루트 사용자라고 부른다. 이를 이용하여 로그인 후 Security Credentials를 클릭한다. 


    * [참고]
    루트 사용자에 대해 MFA 멀티 팩터 인증을 해두는 것을 추천한다. 사실상 추천은 아니고 강력 권고하고싶다..ㅎㅎ)

     

     

     

    - User 생성

    User 생성 후 권한 설정 진행
    생성된 User의 Security Credentials에서 Access Keys 생성
    예시 aceess key는 참고로 캡처 후 삭제했다^^

    그림처럼 계정을 생성한 뒤, 권한을 설정(실습 편의상 Administrator Access 권한을 줬음)해주고 앞으로 사용할 Access Key도 생성해주자. 참고로 Secret Access Key는 생성 시에만 확인이 가능(까먹으면 재생성해야함)하므로 잘적어두거나 .csv file을 다운로드해서 별로로 잘보관해두자. 


    * [참고]:
    Access key같은 credential은 유출되지 않게 잘보관해야한다. 종종 개발자들이나 인프라 담당자들의 실수로 github나 온라인에 유출되어 악용되는 사례가 많기 때문이다.

     

     

     

    관리용 인스턴스 생성

     

    AWS CloudFormation란 무엇입니까? - AWS CloudFormation

    이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

    docs.aws.amazon.com

    생성이 완료되었다면 이번에는 kubectl을 진행할 관리자 인스턴스를 생성해보자!  Kubernetes를 공부할 때도 느낀 것이지만 공식홈페이지가 최고인듯 싶다 + 요즘에는 ChatGPT가 있기 때문에 적절하게 섞어서 사용하면 냐미다! 관리용 인스턴스는 CloudFormation을 이용하여 만들어보았다. 모든 리소스 생성시 region을 잘 확인하고 생성하자!

     

     

    aws cli를 이용하여 작업용 인스턴스 배포진행

    - aws cli 설치방법(Mac OS)

    https://awscli.amazonaws.com/AWSCLIV2.pkg
    sudo ln -s /folder/installed/aws-cli/aws /usr/local/bin/aws
    sudo ln -s /folder/installed/aws-cli/aws_completer /usr/local/bin/aws_completer
    which aws
    aws --version
     

    최신 버전의 AWS CLI 설치 또는 업데이트 - AWS Command Line Interface

    이전 버전에서 업데이트하는 경우 unzip 명령을 실행하면 기존 파일을 덮어쓸지 묻는 메시지가 표시됩니다. 스크립트 자동화와 같은 경우에 이러한 프롬프트를 건너뛰려면 unzip에 대한 -u 업데이

    docs.aws.amazon.com

    => aws cli설치는 나의 host pc에서 진행하였다. 이후 host pc를 통해 생성한 AWS의 인스턴스에서도 필요하기에 (eksctl 사용을 위한) 두 번정도 설치하고 설정하게 될 것이다.

     

     

    - aws cli를 이용하기 위한 credential 등록 (자격증명)

    aws configure
    aws sts get-caller-identity

    aws configure 명령을 통해 아까 위에서 만든 access key, secret acess key, region을 설정해주고 aws sts get-caller-identity를 통해 정상적으로 설정되었는지 확인하면된다.

     

     

     

    [참고] aws cli credentials file path

    cd ~/.aws
    cat config
    cat credentials

    => 위에서 설정한 aws credentials는 해당위치에 존재하게되므로 파일 관리를 잘하자... 미사용시 삭제해도 될 것 같다. 나는 계속 쓸거라 남겨두었다.

     

     

     

    - cloudformation을 이용한 관리용 인스턴스 배포(json, yaml 형식)

    cloudformation은 AWS의 서비스와 리소스를 프로비저닝하고 관리하기 위한 코드기반의 서비스이다. templete file을 사용할 경우 json 또는 yaml 형식을 사용한다(yaml file은 스터디에서 제공해주었음)

    aws cloudformation deploy --template-file ~/aws/myeks-1week.yaml \
         --stack-name myeks --parameter-overrides KeyName=k8s-ho SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

     

     

     

    - 나의 공인 ip 확인 웹페이지

     

    https://ipinfo.io/ip

     

    ipinfo.io

    => 배포전 필요한 key file은 aws gui의 key pair에서 create가 가능하다. 생성 시, .pem file을 다운로드 할 수 있는데 이것 역시 잘보관하도록 하자!! (나의 공인아이피를 알려주는 웹사이트를 스터디에서 줍줍했다. 자주 애용해야지~)

     

     

    - 배포 상태 확인

    instance list 확인
    cloudFormation에서 인스턴스 외에 template에 정의해놓은 리소스들이 생성된 것을 확인

    => 이제 배포를 하면된다! 나는 스터디에서 제공해준 template을 이용하여 진행을 해주었다. 굉장히 친절하게도 현재 나의 공인아이피로만 ingress할 수 있도록 명령어 옵션을 만들어두셨고(ip 변경시 Security Group inbound rule 수정해야하는게 귀찮아서 아래 만들어 두었다). 그외에 필요한 인자들도 작성해주셔서 나의 설정에 맞게 KeyName이나 template file path만 수정해주면 굉장히 손쉽게 구축이 가능했다. 위의 그림처럼 인스턴스뿐아니라 template에 정의한 리소스들이 생성된 것을 볼 수 있었다!

     

     

     

    [참고] aws-cli를 이용하여 관련 securityGroup에 접속가능 CIDR을 현재 공인 CIDR로 바꾸는 방법

    GROUP_ID=$(aws ec2 describe-security-groups | jq -r '.SecurityGroups[] | select(.GroupName | startswith("myeks")).GroupId')
    
    OLD_CIDR=$(aws ec2 describe-security-groups --group-id $GROUP_ID | jq -r '.SecurityGroups[].IpPermissions[].IpRanges[].CidrIp')
    
    NEW_CIDR=$(curl https://ipinfo.io/ip)/32
    
    RULE_ID=$(aws ec2 describe-security-group-rules --filters "Name=group-id,Values=$GROUP_ID" | jq -r '.SecurityGroupRules[] | select(.CidrIpv4 | startswith("'$OLD_CIDR'")).SecurityGroupRuleId')
    
    echo $OLD_CIDR
    echo $NEW_CIDR
    echo $GROUP_ID
    echo $RULE_ID
    
    aws ec2 modify-security-group-rules \
    		--group-id $GROUP_ID \
    --security-group-rules '[{"SecurityGroupRuleId":"'$RULE_ID'","SecurityGroupRule":{"IpProtocol":"-1","CidrIpv4":"'$NEW_CIDR'"}}]'

    => 위치를 옮겨서 IP가 바뀌었다면 SecurityGroup 설정 변경을 위해 실행해주면된다.

     

     

     

    해당 내용은 당연히 AWS GUI에서도 가능하다.

     

    AWS::EC2::Subnet - AWS CloudFormation

    Thanks for letting us know this page needs work. We're sorry we let you down. If you've got a moment, please tell us how we can make the documentation better.

    docs.aws.amazon.com

    오른쪽은 내가만든 Stack Designer 화면으로 만들어진 Stack Design을 통해 templete을 다운로드 할 수 있다.

    * 오른쪽 그림의 templete 틀, 아래 더보기 참고

    더보기
    AWSTemplateFormatVersion: 2010-09-09
    Metadata:
      'AWS::CloudFormation::Designer':
        31800183-9e61-4d03-b2b9-b766bd2af52d:
          size:
            width: 140
            height: 140
          position:
            x: 460
            'y': -100
          z: 0
          embeds:
            - e2e52c72-e09b-43d2-94d9-980a41c612de
        d22feda5-3575-4743-a5b1-4ca5691c7c29:
          size:
            width: 140
            height: 140
          position:
            x: 640
            'y': -100
          z: 0
          embeds: []
        26939aba-2352-4a7b-b903-db4c9725684a:
          size:
            width: 140
            height: 140
          position:
            x: 810
            'y': -100
          z: 0
          embeds: []
        d4c09419-864c-42d1-9ad2-9cb1fd79d272:
          size:
            width: 140
            height: 140
          position:
            x: 980
            'y': -100
          z: 0
          embeds: []
        aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc:
          size:
            width: 140
            height: 140
          position:
            x: 710
            'y': 200
          z: 0
          embeds:
            - 9002e717-6c12-415c-ad9e-ec4ed60302d7
        e2e52c72-e09b-43d2-94d9-980a41c612de:
          size:
            width: 60
            height: 60
          position:
            x: 500
            'y': -50
          z: 1
          parent: 31800183-9e61-4d03-b2b9-b766bd2af52d
          embeds: []
          iscontainedinside:
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
            - 31800183-9e61-4d03-b2b9-b766bd2af52d
        9002e717-6c12-415c-ad9e-ec4ed60302d7:
          size:
            width: 60
            height: 60
          position:
            x: 750
            'y': 240
          z: 1
          parent: aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
          embeds: []
          iscontainedinside:
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
            - aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
        84dd86c3-2c58-49df-aa99-549e933955dc:
          size:
            width: 140
            height: 140
          position:
            x: 460
            'y': 460
          z: 0
          embeds:
            - 44d63f19-b093-472a-b982-a2abe39fe9b0
        44d63f19-b093-472a-b982-a2abe39fe9b0:
          size:
            width: 60
            height: 60
          position:
            x: 500
            'y': 510
          z: 1
          parent: 84dd86c3-2c58-49df-aa99-549e933955dc
          embeds: []
          isassociatedwith:
            - 6bbc6f1c-c73a-4ee7-adbe-119590f184c5
          iscontainedinside:
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
            - 84dd86c3-2c58-49df-aa99-549e933955dc
        6bbc6f1c-c73a-4ee7-adbe-119590f184c5:
          size:
            width: 60
            height: 60
          position:
            x: 250
            'y': 300
          z: 0
          embeds: []
        2daff536-9af5-480f-8409-c8b96cff330b:
          size:
            width: 140
            height: 140
          position:
            x: 1000
            'y': 460
          z: 0
          embeds: []
    Resources:
      PublicSubnet1:
        Type: 'AWS::EC2::Subnet'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 31800183-9e61-4d03-b2b9-b766bd2af52d
      PublicSubnet2:
        Type: 'AWS::EC2::Subnet'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: d22feda5-3575-4743-a5b1-4ca5691c7c29
      PrivateSubnet1:
        Type: 'AWS::EC2::Subnet'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 26939aba-2352-4a7b-b903-db4c9725684a
      PrivateSubnet2:
        Type: 'AWS::EC2::Subnet'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: d4c09419-864c-42d1-9ad2-9cb1fd79d272
      EksVPC:
        Type: 'AWS::EC2::VPC'
        Properties: {}
        Metadata:
          'AWS::CloudFormation::Designer':
            id: aeff9dc9-4161-4fe5-bdf5-fa9fca8bcddc
      ManagerEC2:
        Type: 'AWS::EC2::Instance'
        Properties:
          NetworkInterfaces:
            - SubnetId: !Ref PublicSubnet1
              DeviceIndex: 0
              GroupSet:
                - !Ref EKSec2SG
        Metadata:
          'AWS::CloudFormation::Designer':
            id: e2e52c72-e09b-43d2-94d9-980a41c612de
      EKSec2SG:
        Type: 'AWS::EC2::SecurityGroup'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 9002e717-6c12-415c-ad9e-ec4ed60302d7
      PublicSubnetRouteTable:
        Type: 'AWS::EC2::RouteTable'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 84dd86c3-2c58-49df-aa99-549e933955dc
      PublicSubnetRoute:
        Type: 'AWS::EC2::Route'
        Properties:
          RouteTableId: !Ref PublicSubnetRouteTable
          DestiantionCidrBlock: 0.0.0.0/0
          GatewayId: !Ref InternetGateway
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 44d63f19-b093-472a-b982-a2abe39fe9b0
      InternetGateway:
        Type: 'AWS::EC2::InternetGateway'
        Properties: {}
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 6bbc6f1c-c73a-4ee7-adbe-119590f184c5
      PrivateRouteTable:
        Type: 'AWS::EC2::RouteTable'
        Properties:
          VpcId: !Ref EksVPC
        Metadata:
          'AWS::CloudFormation::Designer':
            id: 2daff536-9af5-480f-8409-c8b96cff330b

    => templete이 준비되어있다면 아까처럼 사용하면 된다. 공식페이지에는 sample로 제공해주는 templete이 있어 참고하면 이해하는데 도움이 된다. 이중 굉장히 편리했던건 Design 기능이다. stack을 create할때 Design 페이지에서 오른쪽 사진과 같이 원하는 리소스를 선택 후 연결하면 templete이 아주 손쉽게 만들어진다. 이후 필요한 세부내용은 공식페이지를 참고하여 작성하면된다.

     

    개인적으로는 직접 한번 template을 만들어보는 것을 추천한다. 누군가 만들어놓은 templete 또한 URL 입력이나 File Upload 후, 오른쪽사진처럼 한 눈에 볼 수도 있다.

     

    나의 경우 스터디에서 제공해준 templete을 이해하고 직접 만들어보았다. 기본적인 틀 templete은 Designer를 통해 만들고 세부적인 사항은 코드단에서 수정하는 방식이 편한 것 같다. templete은 json 또는 yaml 형식이지만 yaml이 가독성이 더 좋아보이긴 한다. 처음 접해서 그렇게 느낀건지 모르겠지만 실행 시, Parameter로 옵션을 통해 값을 받는 기능도 있었고 !Ref나 !Sub도 굉장히 좋은 기능이였다. 자주 애용해야겠다..ㅎㅎ 

     

     

     

     

    - 관리용 인스턴스에 접근

    ssh -i [Key file 명] ec2-user@[작업용 인스턴스 public ip]

    => 여기서 말하는 keyfile은 인스턴스 생성시에 사용된 keyfile로 aws gui에서 확인이 가능하다. 위에서 배포전 설명했던 .pem file을 사용하면 된다. 해당 ec2에서의 기본 user는 "ec2-user"이다. 인스턴스의 기본계정을 모를때 root로 시도하게되면 친절하게 로그로 알려준다. 참고로 ssh로 붙는 것이 가능한 이유는 template의 security group 설정에서 허용해놓았기 때문이다. 접속이 안되면 확인하고 열어주자! 또한 ssh -i로 key file 사용시, 600 권한을 적용하자. 권한이 너무 높은 경우 ssh -i 옵션 사용에 실패할 수 있다.

     

     


     

    [참고] mac 사용자들에게 추천!! Termius

    => Mac app store에서 간편하게 설치가 가능하다. aws의 elastic ip를 설정하여 사용할 것이 아니라면 인스턴스 재생성시, 접속을 위해 변경된 공인 ip를 다시 적용해줘야한다는 단점이 있지만 그래도 개인적으로는 편리하게 사용중이라 추천한다!(과금이 두렵지 않다면 Elastic IP를 등록해 귀찮음을 감소시킬 수 있다) key설정은 해당 파일 내용을 복사해서 붙여넣기하면 된다.

     


     

    eksctl, kubectl 설치

     

    eksctl 설치 또는 업데이트 - Amazon EKS

    GitTag 버전은 0.135.0 이상이어야 합니다. 그렇지 않은 경우 터미널 출력에서 설치 또는 업그레이드 오류가 있는지 확인하거나, 1단계의 주소를 https://github.com/weaveworks/eksctl/releases/download/v0.135.0/eksct

    docs.aws.amazon.com

     

    리눅스에 kubectl 설치 및 설정

    시작하기 전에 클러스터의 마이너(minor) 버전 차이 내에 있는 kubectl 버전을 사용해야 한다. 예를 들어, v1.27 클라이언트는 v1.26, v1.27, v1.28의 컨트롤 플레인과 연동될 수 있다. 호환되는 최신 버전

    kubernetes.io

    source <(kubectl completion bash)
    alias k=kubectl
    complete -F __start_kubectl k

    본격적으로 EKS를 구축해보자. 그러기 위해 eksctl를 설치해줄 것이다. 스터디에서 배포에 필요한 인자들을 파씽하여 변수로 적용되도록 제공을 해주셔서 편리하게 사용했다. 짱짱!! 심지어 eksctl, kubectl은 설치 및 자동완성도 되어있었다. 음.. 그래도 할줄아는게 중요하기 때문에 설치를 별도로 해보는 것을 추천한다. 가이드가 잘되어있어서 그렇게 어렵지 않다.

     

     

     

    => 당연히 aws credential은 설정되어있지 않으므로 이전에 설명한 것과 동일한 방법으로 직접 등록해주면된다. 이게 되어있어야 eksctl을 사용할 수 있다.

     

     

    EKS Cluster 생성

    [예시]
    eksctl create cluster --name myeks --region=ap-northeast-2 --nodegroup-name=mynodegroup --node-type=t3.medium --node-volume-size=30 \
    --zones=ap-northeast-2a,ap-northeast-2c --vpc-cidr=172.20.0.0/16 --dry-run | yh
    
    eksctl create cluster --name myeks --region=ap-northeast-2 --nodegroup-name=mynodegroup --node-type=t3.medium --node-volume-size=30 \
    --zones=ap-northeast-2a,ap-northeast-2c --vpc-cidr=172.20.0.0/16

    eks cluster 생성은 여러가지 방법이 있다. 대표적으로는 우리가 진행할 eksctl이 있으며 kubectl과 비슷하게 eksctl create cluster -f 옵션이 가능했다(kubectl apply 또는 create -f 와 비슷). 또한 kubernetes의 --dry-run=client -o yaml 옵션과 같은 --dry-run이 존재해서 어떤식의 template으로 생성이 되는지 실행전에 확인이 가능했다. 매번 eksctl 명령어 입력이 귀찮다면 yaml로 만들어 놓는 것도 좋을 것 같다.

     

     

     

    - EKS Cluster 배포진행

    eksctl create cluster -f file
    # 또는
    eksctl create cluster [원하는 옵션]
    
    ---
    
    [ 실습 예제 ]
    # 변수설정
    export CLUSTER_NAME=myeks
    export AWS_DEFAULT_REGION=ap-northeast-2
    export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].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 CLUSTER_NAME=$CLUSTER_NAME" >> /etc/profile
    echo "export AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" >> /etc/profile
    echo "export VPCID=$VPCID" >> /etc/profile
    echo "export PubSubnet1=$PubSubnet1" >> /etc/profile
    echo "export PubSubnet2=$PubSubnet2" >> /etc/profile
    
    # 변수확인
    echo $CLUSTER_NAME
    echo $AWS_DEFAULT_REGION
    echo $VPCID
    echo $PubSubnet1,$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.24 --ssh-access --external-dns-access --verbose 4

    => 참고로 cluster 이름을 지정하지 않을 시 자동으로 만들어준다. --dry-run으로 확인하고 진행하자. 배포를 진행하게되면 현재 접속중인 인스턴스를 관리 인스턴트로 EKS Cluster가 배포된다.

     

     

     

    - 배포시작

    eks cluster design

    배포가 시작되면 위와같은 화면을 확인할 수 있으며 직접 AWS GUI에서 myeks라는 eks cluster가 생성중인 것을 확인할 수도 있다. 실제로 해당 cluster에 필요한 resource들이 여기저기서 생성되고 있는 중이다.

     

    배포가 완료되면 ~/.kube/config를 kubeconfig로 설정했다는 로그를 볼 수 있다. 이는 kubenetes 환경에서 사용할 관리자의 credential이 등록되는 것이다. kubectl은 이를 이용하여 kube-apiserver 등에 요청을 보내며 필요시 관리자가 가져다 사용하기도 한다(예를 들어 kube-client Dev). kubernetes를 관리하고 제어하는데 사용되기 때문에 굉장히 중요한 파일이다. 완료되면 eks-cluster와 nodegroup이 생성된다.

     

    EKS cluster 배포는 대략 18 ~ 25분 정도가 소요된다. 또한 해당 EKS cluster 역시 cloudformation -> stack 선택 -> Template -> View in Designer -> cloudformation Designer를 통해 오른쪽 그림과 같이 한눈에 확인도 가능했다.

     

    Gasida님이 2주차 네트워크는 1주차를 이해하지 못하면 쉽지않을 것이라고 한 이유를 조금은 알거같았다. 

     

     

     

     

    - 배포 완료 후, kubectl을 통해 cluster info 확인

    해당 내용으로 확인되는 주소는 api의 public access 주소이다. 해당 내용은 public하게 접근이 가능하기 때문에 숨길 수 있도록 처리하는 것을 권고한다고 스터디에서 알려주셨다. 이 내용은 추후에 조치해보도록 하겠다.

     

     

     

     

    - kube-apiserver public access

    => curl 명령을 통해 접근을 시도하면 Api server는 User를 system:anonymous로 인지하며 인증에는 성공하였으나 인가에 실패한 것을 403 code를 통해 확인할 수 있었다. 즉, system:anonymous에 권한이 있었다면 정보가 유출되었을 것이다. 실제로 그런지 한번해보자!! + (추가적으로 예상이지만 apiserver내의 설정중 --anonymous-auth값이 true(default 값이며 미설정시, Authorization 옵션에 따라 default 값인 true로 적용)로 되어있을 것이다, false였다면 인증실패로 401 code 반환)

     

    => [궁금증] 그렇다면 EKS에서 kube-apiserver에 대한 설정이 보고싶거나 수정하려면 어떻게 해야하는지 궁금해졌다..On-premise와 다르게 그저 인증과 인가를 통해 API 요청방식으로만 진행하는건가..?? 흠..

     

     

     

    [참고] automount되는 token을 이용한 public 주소 요청

    kubectl run test --image=nginx
    kubectl exec -it test -- cat /run/secrets/kubernetes.io/serviceaccount/token
    # token 값 복사
    
    kubectl create test clusterrolebinding --clusterrole cluster-admin --serviceaccount default:default
    # default:default serviceaccount에 cluster-admin role binding

    => pod 생성시, 별다른 옵션을 지정하지 않으면 자동으로 automount되는 token이 있다. 그것은 default namespace의 default 계정이며 해당 계정에 cluster-admin을 binding시킨 후, token을 복사하여 외부에서 kube-apiserver의 public 주소로 요청을 보내보자. 권한에 맞게 정상적인 응답을 돌려주는 것을 확인할 수 있었다(인가 성공). 참고할 사항이지만 이러한 이유로 불필요시, token의 automount를 비활성하는 옵션을 적용하는 것을 권고한다. Pod Container를 트리거로 serviceaccount같은 계정 권한에 따라 중요 컴포넌트에 CRUD로 충분히 치명적인 악영향을 미칠 수 있기 때문이다.(마찬가지로 system:anonymous 계정도 권한에 따라 위의 동작 가능)

     

     

     

     

    - EKS API 접속시도(with. aws-cli)

    추출한 ca.crt

    # aws eks describe-cluster를 이용하면 kubernetes에서 사용하는 ca.crt 인증서를 추출할 수 있다.
    aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.certificateAuthority.data | base64 -d > ca.crt
    
    # 추출된 인증서로 API에 Request 진행
    curl --cacert ca.crt $(aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.endpoint)/version | jq

    => aws-cli의 describe-cluster를 이용하여 추출한 인증서 내용을 확인해보면 자체서명한 kubernetes ca.crt file이라는 것을 알 수 있었다. 인증서 추출이 귀찮다면 그냥 -k 옵션으로 진행하여 확인해도 무방하다. 

     

     

     

     

    - 현재 kubernetes 구축 시, 확인되는 리소스 및 컴포넌트들이다.

    => 일반적으로 사용하는 kubernetes와 다른점이 있다면 kube-system namespace에 aws-node라는 처음보는 리소스가 보인다. daemonset으로 배포된 것으로 확인되며 image를 확인해보니 amazon cni라고 되어있다. EKS cluster내에서 실행되는 Amazon VPC CNI 플러그인으로 Pod 네트워크와 Node 네트워크 연결을 관리하는 역할을 한다고 한다. On-premise kubernetes에서 사용했던 Calico 같은 plugin으로 생각하면 될 것 같다(그렇다는 것은.. AWS만의 통신을 위한 라우팅 정보 공유라던지..기술들이 또 다를 수도 있다는 것인가??..공부할게 정말 많다.. 공부하면서 느끼는 것이지만 어느정도 깊이까지를 알고 넘어가야하는지 판별하는 것이 생각보다 쉽지 않다. 알아두면 언젠가는 써먹을 일이 올거라는 생각이 들기 때문이다..)

     

     

     

    - 현재 실행중인 Node 정보 확인

    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

    => 해당 명령어는 현재 실행중인 모든 인스턴스 정보를 보여준다(EKS 관련 인스턴스만 나오는 것은 아니므로 입맛에 맞게 필터링해서 사용하면 된다)

     

     

     

    - SecurityGroups 확인 및 추가 + ssh 접속

    # securityGroups GroupId 확인
    aws ec2 describe-security-groups --query "SecurityGroups[*].[GroupId]" --output table
    
    # securityGroups중 nodegroup의 GroupId만 필터링
    NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text)
    
    
    # 관리용 Node(private: 192.168.1.100)에서 Worker node들에 통신이 가능하도록 securityGroup 추가(-1은 ALL을 의미)
    aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32

    => 관리용 node에서 worker node들로 통신할 수 있도록 securityGroup을 추가해두었다. 참고로 스터디에서는 이미 id_rsa.pub을 각 worker node의 authorized_keys에 등록해놓아서 패스워드 없이 접근이 가능했다

     

     

     

     

    - 소켓정보를 통해 통신 Peer Address와 스토리지 정보도 확인해보자.

    # socket 정보 확인
    ssh ec2-user@$192.168.1.81 sudo ss -tnp
    ssh ec2-user@$192.168.2.68 sudo ss -tnp
    
    
    # 스토리지 정보 확인
    ssh ec2-user@192.168.1.81 lsblk
    ssh ec2-user@192.168.1.81 df -hT --type xfs

    소켓 정보와 스토리지 정보

     

     

     

    - Kubernetes Cluster에서 동작하는 Container 정보

    ctr
    nerdctl
    crictl

    => 원하는걸 가져다쓰면 될 것 같다. 나의 경우 crictl을 사용하며 보통 트러블슈팅이나 kube-apiserver.yaml을 수정해서 kubectl로 확인이 어려운 상황이나 static pod들에 대한 상태 확인, Container image 생성 등에 사용하는 편이다.

     

     

     

    - EKS Cluster & 관리용 인스턴스 Clean up

    # Amazon EKS cluster 삭제 -> 관리용 instance에서 진행
    eksctl delete cluster --name $CLUSTER_NAME
    
    # 관리용 인스턴스 stack 삭제 -> 자신의 host에서 진행
    aws cloudformation delete-stack --stack-name myeks

     

     

     


    Worker node에 접속

    worker node에 접속해서 확인해보면 eni라는 인터페이스가 확인되며 Veth interface의 Pair로 보여진다. 맞는지 살짝(?)만 보고가자 .확인방법은 간단하다. 

     

     

    - Veth 여부 확인

    => Pod는 각각 network namespace를 격리하여 가지고 있으며 node에 연결되어있는 veth interface는 pod에 pair로 연결된다. 대충 요점만 말하자면 pod당 veth가 각각 존재한다는 것이다. 때문에 현재 test와 abc Pod는 192.168.1.58 node에 배포되어있으므로 해당 node의 veth 역시 2개가 생성된 것을 확인할 수 있었다. (네트워크 관련 부분은 2주차부터 진행한다고 하셨으니 일단 여기까지만 작성하였다)

     

     

     

    - ENI 특징

     

    Control Plane - EKS Best Practices Guides

    EKS Control Plane Amazon Elastic Kubernetes Service (EKS) is a managed Kubernetes service that makes it easy for you to run Kubernetes on AWS without needing to install, operate, and maintain your own Kubernetes control plane or worker nodes. It runs upstr

    aws.github.io

    Network Interface 목록화면
    EKS owned ENI

    EKS에 존재하는 ENI는 특이한 특징이 있는데 바로 EKS owned ENI로 EKS cluster를 구성하는 Worker node들은 관리자의 소유이지만 해당 Node와 연결된 ENI의 인스턴스는 AWS의 소유라는 것이다. 

     

     


     

    ECR ( Elastic Container Repository ) - public repo 설정 시 us-east-1을 사용해야함

    # Public ECR 인증 & docker login
    aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
    
    
    # 필요 변수 설정
    NICKNAME=k8s-ho
    
    
    # Public Repo 생성
    aws ecr-public create-repository --repository-name $NICKNAME/mario --region us-east-1
    
    
    # Public Repo URI 변수 설정
    REPOURI=$(aws ecr-public describe-repositories --region us-east-1 | jq -r .repositories[].repositoryUri)
    
    
    # image tag 설정
    docker pull pengbai/docker-supermario
    docker images
    docker tag pengbai/docker-supermario:latest $REPOURI:latest
    docker images
    
    
    # image push 진행
    docker push $REPOURI:latest
    
    
    # Public image 삭제
    aws ecr-public batch-delete-image \
          --repository-name $NICKNAME/mario \
          --image-ids imageTag=latest \
          --region us-east-1
    
    
    # Public Repo 삭제
    aws ecr-public delete-repository --repository-name $NICKNAME/mario --force --region us-east-1
    
    
    # docker logout
    docker logout

    배포 yaml file은 아래의 더보기 참고

    더보기
    cat <<EOF | kubectl create -f -
    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: $REPOURI
    ---
    apiVersion: v1
    kind: Service
    metadata:
       name: mario
    spec:
      selector:
        app: mario
      ports:
      - port: 80
        protocol: TCP
        targetPort: 8080
      type: LoadBalancer
      externalTrafficPolicy: Local
    EOF

    service object를 LoadBalancer type으로 배포
    추억의 Mario Bros가 나타났다!!

    => AWS는 Docker Hub와 같은 public, private container repository도 제공해준다. 이건 그냥 한번 만들어보면 될 것 같다. 보안이나 개발 관점에서도 image 관리는 굉장히 중요한데 추후에 클라우드 업무를 하게된다면 자주 애용할 것만 같은 느낌이다^^. 스터디에서 알려준 슈퍼마리오게임 image를 repo에 등록하였고 위의 절차대로 진행시, 게임을 즐길 수 있게 되었다^^. 나처럼 Kubernetes Service LoadBalancer로 배포시, Pending 상태 이후 External-IP가 생기더라도 조금 기다려야하는 것으로 보여진다.  그동안 SecurityGroup관련 설정도 확인해서 필요시 추가해주면 될 것 같다

     

    반응형

    댓글

Designed by Tistory.