전통적인 컨테이너 방식 대신 Nix Shell을 사용하여 임시 환경을 생성하고 파괴하는 새로운 접근 방식을 탐구합니다. Nix Shell은 개발자가 필요한 도구를 포함한 환경을 쉽게 생성하고 사용 후 즉시 제거할 수 있는 효율적인 방법을 제공합니다. 이 접근 방식의 주요 장점은 다양한 운영 체제에서 일관된 개발 환경을 제공하면서도 필요할 때만 특정 도구를 사용할 수 있게 해준다는 점입니다.

  • 임시 환경의 필요성: 개발자들이 필요에 따라 환경을 쉽게 생성하고 제거할 수 있는 능력은 효율적인 작업 흐름을 위해 필수적입니다. 임시 환경은 특히 개발, 테스트 및 빌드 파이프라인에서 유용합니다.
  • 컨테이너의 한계: 컨테이너는 여러 환경에서 널리 사용되지만, 설정과 관리가 복잡할 수 있으며, 다양한 도구와 응용 프로그램의 설치 및 설정에 제한이 있을 수 있습니다.
  • Nix Shell의 소개: Nix Shell은 이러한 문제를 해결하기 위한 대안으로, 필요한 도구와 응용 프로그램을 포함한 커스텀 환경을 쉽게 생성할 수 있습니다. 이는 특히 여러 도구가 필요한 복잡한 프로젝트나 다양한 개발 요구 사항이 있는 팀에 유용합니다.
  • 사용 사례와 예시: Nix Shell을 사용하여 GitHub CLI, Kubernetes, 그리고 다양한 개발 도구를 포함한 환경을 신속하게 설정하는 과정을 보여줍니다. 이는 개발자가 복잡한 설치 과정 없이도 필요한 모든 도구에 즉시 접근할 수 있게 해줍니다.
  • 플랫폼 독립성: Nix Shell은 macOS, Windows, Linux 등 다양한 운영 체제에서 동일한 방식으로 작동합니다. 이는 개발자가 운영 체제의 차이에 구애받지 않고 일관된 환경을 유지할 수 있게 해줍니다.
  • 효율성과 생산성 향상: Nix Shell을 사용하면 개발자가 프로젝트에 필요한 도구를 빠르고 쉽게 준비할 수 있으며, 사용하지 않을 때는 쉽게 제거할 수 있습니다. 이는 개발자의 시간을 절약하고 전반적인 생산성을 향상시킵니다.

 

https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-shell

 

nix shell - Nix Reference Manual

Warning This program is experimental and its interface is subject to change. nix shell - run a shell in which the specified packages are available nix shell [option...] installables... Start a shell providing youtube-dl from the nixpkgs flake: # nix shell

nixos.org

 



 

Nix 설치하기

  1. Nix 설치: Nix는 Linux 및 macOS에서 사용할 수 있습니다. 터미널을 열고 아래 명령어를 실행하여 Nix를 설치합니다.
sh <(curl -L <https://nixos.org/nix/install>)
  1. 환경 설정: 설치 후에는 .bashrc, .zshrc, 또는 사용 중인 셸의 설정 파일에 Nix를 초기화하는 코드가 추가됩니다. 변경사항을 적용하려면 새 터미널 세션을 시작하거나 설정 파일을 소스로 지정합니다.
source ~/.bashrc  # 또는 해당하는 셸의 설정 파일

Nix 셸 사용하기

  1. 단일 패키지 실행: Nix 셸을 사용하여 임시 환경에서 단일 패키지를 실행할 수 있습니다. 예를 들어, hello 프로그램을 사용해 보려면 다음 명령어를 실행합니다.
nix-shell -p hello
  1. 여러 패키지 실행: 여러 패키지를 포함하는 환경을 생성하려면, 각 패키지 이름을 -p 옵션과 함께 나열합니다.
nix-shell -p nodejs python39
  1. shell.nix 파일 사용: 보다 복잡한 환경을 구성하려면 shell.nix 파일을 생성하고 필요한 패키지와 환경 설정을 정의할 수 있습니다. 예를 들어, Node.js와 Python 환경을 구성하려면 다음과 같은 내용의 shell.nix 파일을 작성합니다.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = [
    pkgs.nodejs
    pkgs.python39
  ];
}

해당 디렉토리에서 **nix-shell**을 실행하면 **shell.nix**에 정의된 환경이 생성됩니다.ㅌㅌ

'job' 카테고리의 다른 글

Kubernetes환경에서의 의존성에 대해서  (0) 2024.01.28
2024년도 devops 추천 도구  (0) 2024.01.11
git 그룹단위로 전체 clone 하기  (0) 2023.08.25
keycloak 을 broker로, google을 IdP로  (0) 2023.05.23
teleport란  (0) 2023.05.02

애플리케이션이 구동되려면 여러가지 서비스들이 복합적으로 실행되어야 한다.

App A가 있어야하고 이를 바라보는 App B, App B에 연결된 DB, DB에 연결된 DB User, 스키마가 있어야 한다. 이렇듯 애플리케이션이 구동하려면 여러가지 서비스간의 의존성을 고려해야 한다.

 

이러한 서비스간의 의존성은 과거에는 배포순서가 중요했다. 예를들어, 과거의 배포방식은 SSH로 서버에 직접 접속하거나 혹은 파이프라인 또는 스크립트로 배포를 했다. 즉, 정해진 스크립트(배포 순서)에 따라 서비스들이 배포됐기에 배포 순서가 중요했다.

 

그러나 k8s 환경에서는 배포 순서가 그다지 중요하지 않아졌다.

왜냐하면 k8s는 실패한 리소스의 배포를 계속 시도하므로 만약 잘못된 순서대로 배포된다해도 결국 애플리케이션은 정상적으로 동작하게 될 것이다.

 

하지만 과거의 관습(의존성을 배포순서로 가져가는것)을 k8s 환경에서 사용하는 경우가 많다.

우리가 중요하게 생각해야할 점은 k8s를 사용하는 현 시점에서 리소스의 종속성과 생성 순서에 대해 과도하게 걱정할 필요가 없다는 것이다.  오히려 시스템이 자연스럽게 일관성을 유지하도록 하고, 필요한 데이터나 정보에 기반한 종속성에 초점을 맞추는 것이 중요하다는 의미이다.

예를 들어, 애플리케이션이 데이터베이스에 접근하기 위해 필요한 접근정보가 준비되지 않았다면, 쿠버네티스는 자동으로 해당 애플리케이션의 생성을 지연시킨다.

 

즉, 리소스의 생성 순서보다는 해당 리소스가 제공하는 데이터의 가용성이 더 중요함을 의미한다.

 

 

 

 

 

출처 : https://www.youtube.com/watch?v=4-WpJ49MDG8

'job' 카테고리의 다른 글

Nix shell 소개  (0) 2024.02.18
2024년도 devops 추천 도구  (0) 2024.01.11
git 그룹단위로 전체 clone 하기  (0) 2023.08.25
keycloak 을 broker로, google을 IdP로  (0) 2023.05.23
teleport란  (0) 2023.05.02

kubewarden

Kubewarden은 쿠버네티스(Kubernetes) 클러스터의 보안과 정책 준수를 관리하기 위한 도구입니다.

Kubewarden을 사용하면, 클러스터에 어떤 파드(Pod, 쿠버네티스에서 애플리케이션을 실행하는 단위)가 생성되거나 업데이트될 때 적용되는 규칙이나 정책을 설정할 수 있습니다.

  • Kubewarden은 WebAssembly(WASM)와 쿠버네티스 admission controllers를 결합한 도구로, 다양한 언어로 작성된 정책을 컴파일하여 쿠버네티스에서 실행할 수 있게 해줍니다.
    • 쿠버네티스의 Admission Controllers
      • 쿠버네티스의 Admission Controllers는 쿠버네티스 API 서버로의 요청을 가로채서 검사하고, 수정하거나 거부하는 역할을 합니다.
      • 이들은 쿠버네티스 클러스터에서 리소스(파드, 서비스 등)의 생성, 업데이트, 삭제 등의 요청이 처리되기 전에 특정 규칙이나 정책을 적용합니다.
      • 예를 들어, 특정 파드가 너무 많은 CPU 자원을 요청하는 것을 막거나, 특정 네임스페이스에서만 리소스를 생성할 수 있도록 제한하는 등의 작업을 수행합니다.
      • Admission Controllers는 쿠버네티스의 보안과 정책 준수를 강화하는 데 중요한 역할을 합니다.
  • Kubewarden을 사용하면 정의된 정책을 적용하고, 이 정책들이 쿠버네티스 API로의 요청을 수락, 거부 또는 변경할 수 있습니다.

    Kubewarden의 정책 모듈은 컨테이너 이미지로 참조되며, 이는 정책을 이미지에 저장하고 있다는 것을 의미합니다. 예를 들어, 특정 이미지가 권한 있는 파드의 실행을 방지하는 정책을 가지고 있을 수 있습니다. 
    • 예시
apiVersion: policies.kubewarden.io/v1
kind: ClusterAdmissionPolicy
metadata:
  name: validate-pod-security-standards
spec:
  module: registry://ghcr.io/kubewarden/policies/validate-pod-security-standards:v1.0.0
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
    operations:
    - CREATE
    - UPDATE
  mutating: false
  settings: 
    requiredLabels: # 파드에 반드시 존재해야 하는 레이블들을 정의합니다.
      - "app.kubernetes.io/name"
      - "app.kubernetes.io/version"
    requiredAnnotations: # 파드에 반드시 존재해야 하는 어노테이션을 정의합니다.
      - "kubewarden.policy/owner"
    enforceRunAsNonRoot: true # 파드가 Non-root 사용자로 실행되어야 한다는 요구사항을 설정합니다.
    allowedCapabilities: [] # 파드에서 허용되는 추가적인 리눅스 기능(capabilities)을 비어 있는 배열로 설정하여 모든 추가 기능을 금지합니다.

 

이 설정에 따르면:

  • module: registry://ghcr.io/kubewarden/policies/validate-pod-security-standards:v1.0.0: Kubewarden 정책 모듈은 쿠버네티스 클러스터에서 파드의 보안 표준을 검증하는 데 사용됩니다. 이 모듈의 주요 목적과 기능은 다음과 같습니다:
  • requiredLabels: 모든 파드에는 **app.kubernetes.io/name**과 app.kubernetes.io/version 레이블이 있어야 합니다. 이는 파드가 어떤 애플리케이션에 속하는지와 애플리케이션의 버전을 명시하는 데 사용됩니다.
  • requiredAnnotations: 모든 파드에는 kubewarden.policy/owner 어노테이션이 있어야 합니다. 이는 정책의 소유자 또는 관리자를 지정하는 데 사용될 수 있습니다.
  • enforceRunAsNonRoot: 이 값이 **true**로 설정되면, 모든 파드는 루트가 아닌 사용자로 실행되어야 합니다. 이는 보안 관행에 따른 것입니다.
  • allowedCapabilities: 이 배열이 비어 있기 때문에, 파드에서 어떠한 추가 리눅스 기능도 허용되지 않습니다. 이는 파드가 더 높은 권한을 가지는 것을 방지합니다.

 

  • Kubewarden 정책은 Artifact Hub에서 찾을 수 있으며, 이곳은 쿠버네티스 관련 거의 모든 것을 찾을 수 있는 장소입니다.
    • artifact hub

 

  • 사용자는 Kubewarden을 사용하여 사용자 정의 정책 모듈을 개발하고 적용할 수 있습니다. 예를 들어, 특정 크기의 SQL 클레임만을 허용하는 사용자 정의 정책을 만들 수 있습니다.
    • 사용자 정의 정책 생성 방법
      1. 정책 요구 사항 정의: 먼저, 이 정책이 해결하려는 문제를 정의합니다. 예를 들어, 클러스터에서 너무 큰 SQL 데이터베이스가 생성되는 것을 방지하고자 할 수 있습니다. 이를 위해 'small', 'medium', 'large'와 같은 특정 크기만을 허용하고자 하는 요구 사항을 정의합니다.
      2. 정책 로직 개발: 다음으로, 이 요구 사항을 구현하는 로직을 개발합니다. 이 과정에서는 Kubewarden 정책을 구현할 수 있는 프로그래밍 언어(예: Rust, Go 등)를 사용하여, SQL 클레임의 크기를 검사하고, 허용된 크기에 맞지 않는 클레임을 거부하는 코드를 작성합니다.
      3. WASM으로 컴파일: 개발한 정책 로직을 WebAssembly(WASM)로 컴파일합니다. WASM은 다양한 환경에서 실행될 수 있는 저수준 바이너리 포맷입니다. Kubewarden은 WASM 형식의 정책을 실행합니다.
      4. 정책 모듈 배포: 컴파일된 정책 모듈을 컨테이너 이미지로 패키징하고, Docker 레지스트리(예: Docker Hub, GHCR 등)에 배포합니다.
      5. Kubewarden 정책 설정: Kubewarden 정책을 쿠버네티스 클러스터에 적용합니다. 이때 정책 모듈의 위치와 해당 정책이 적용될 리소스 및 조건을 지정하는 YAML 파일을 작성하고 적용합니다.
      apiVersion: policies.kubewarden.io/v1
      kind: ClusterAdmissionPolicy
      metadata:
        name: sql-size-restriction
      spec:
        module: registry://myregistry.io/kubewarden/sql-size-restriction:v1.0.0
        rules:
        - apiGroups: [""]
          apiVersions: ["v1"]
          resources: ["sqlclaims"]
          operations:
          - CREATE
          - UPDATE
        mutating: false
        settings:
          allowedSizes:
            - small
            - medium
            - large
      
      이 예시에서, 정책은 **sqlclaims**라는 커스텀 리소스에 적용되며, 생성 또는 업데이트 시 'small', 'medium', 'large'라는 크기 제한을 강제합니다. 이를 통해 쿠버네티스 클러스터 내에서 자원 사용을 효과적으로 관리하고, 과도한 리소스 사용을 방지할 수 있습니다.
    • Kubewarden을 사용하여 사용자 정의 정책 모듈을 개발하고 적용하는 예시로, "특정 크기의 SQL 클레임만을 허용하는 정책"을 들 수 있습니다. 이런 종류의 정책은 쿠버네티스 클러스터에서 SQL 데이터베이스 리소스의 크기를 제한하는 데 사용될 수 있습니다. 여기에는 몇 가지 주요 단계가 있습니다:
  • Kubewarden의 장점 중 하나는 거의 모든 언어(WASM으로 컴파일될 수 있어야 함)로 정책을 작성할 수 있다는 것이며, 이는 다른 정책 도구와 구별되는 주요 특징입니다.
  • Kubewarden의 단점으로는 새 정책을 추가할 때마다 정책 서버가 재시작되어야 한다는 점과 쿠버네티스 표준을 따르지 않아 이벤트가 발생하지 않는다는 점이 있습니다.
    • 이벤트 미발생 예시
      • 쿠버네티스 클러스터에서 '파드 메모리 제한' 정책이 위반되어 파드 생성이 거부되었다고 가정해봅시다.
      • 쿠버네티스 표준을 따르는 시스템에서는 이러한 거부 사건이 '이벤트'로 기록되고, 시스템 관리자나 다른 애플리케이션에서 이를 감지할 수 있습니다.
      • 하지만 Kubewarden은 이러한 이벤트를 생성하지 않기 때문에, 관리자나 다른 시스템이 이러한 중요한 정보를 즉시 알 수 없을 수 있습니다.
    • 쿠버네티스에서는 보통 중요한 변화나 상태 변경 시 '이벤트'를 발생시켜 사용자나 다른 시스템 요소에 알립니다.하지만, Kubewarden은 쿠버네티스 표준 이벤트 생성을 지원하지 않아 이러한 알림이 발생하지 않습니다.

'job > devops' 카테고리의 다른 글

openfunction  (0) 2023.12.01
git common flow(또는 gitlab flow) 브랜치 전략에 대한 설명  (0) 2023.06.13

https://www.youtube.com/watch?v=-qeoLfSGlFU&t=2s 를 참고하였다.

 

1.Service Catalogs - Port 

  • Backstage는 CNCF의 인기있는 프로젝트 중 하나로, UI를 제공하는 도구의 기본이 될 수 있지만, 최종 사용자에게 직접 사용되기에는 복잡하고 유지 관리가 어렵.
  • Port는 현재 사용 가능한 최고의 도구로 평가되며, Kubernetes와 친화적으로 만들어야 할 작업이 있지만, SaaS로만 사용 가능하고 오픈소스가 아니라는 단점이 있음에도 불구하고 추천

서비스 카탈로그(Service Catalog)는 조직 내에서 사용되는 서비스, 애플리케이션, 리소스들의 목록을 관리하고, 사용자가 이들에 접근하고 활용할 수 있도록 도와주는 도구나 시스템을 의미합니다. 이러한 카탈로그는 IT 서비스 관리(ITSM)의 중요한 부분이며, 개발자, IT 전문가, 그리고 다른 사용자들이 필요한 서비스를 쉽게 찾고, 이해하며, 사용할 수 있도록 합니다.

  1. Backstage:
    • 개발자가 서비스를 더 빠르고 효율적으로 찾고, 사용하며, 관리할 수 있도록 도와주는 통합 개발 환경(IDE)입니다.
    • 기업이나 조직의 서비스, 소프트웨어 컴포넌트, 인프라 등을 한 곳에서 관리할 수 있도록 합니다.
    • 사용자 정의가 가능하며, 다양한 플러그인과의 통합을 지원합니다.
  2. Port:
    • Backstage와 유사한 기능을 제공하지만, 사용자에게 더 친숙하고 쉬운 인터페이스를 제공하는 것을 목표로 합니다.
    • SaaS(서비스로서의 소프트웨어) 형태로 제공되며, 오픈소스가 아닌 것이 특징입니다.
    • Kubernetes와의 통합 및 호환성에 중점을 두고 있으며, 쿠버네티스 클러스터에서 실행되는 서비스 관리에 특화되어 있습니다.

 

2. Application Management (Kubernetes Manifests - Helm 대안. Timoni, CUE 랭귀지

  • Timoni는 CUE 기반으로 Helm과 유사하게 작동한다.

Helm과 차이점 : https://timoni.sh/comparison/

CUE 랭귀지 : https://timoni.sh/cue/introduction/

 

 

3. Pipelines (CI/CD) - Dagger

  • Dagger는 새로운 도구로, 어디서나 실행될 수 있는 파이프라인을 정의할 수 있으며, 다양한 언어로 정의 가능.

컨테이너 기반의 파이프라인 도구. 벤더 종속성 피함

 

4. Observability -Pixie, groundcover

  • Grafana Cloud가 전체 솔루션으로서 강조됨. Pixie는 혁신적인 접근 방식을 제공.

결국 옵저버빌리티에는 loki, prometheus, victoria metircs, jaeger, tempo, alertmanager, komodor등 여러가지 도구들을 조합해서 사용해야하는데 이런 여러가지 솔루션을 종합적으로 제공하는 Pixie나 groundcover를 추천한다.

pixie: https://px.dev/

groundcover: https://www.groundcover.com/

 

5. Databases - CMPG, Atlas Operator

    • 관리형 데이터베이스 서비스 사용을 권장, PostgreSQL에 특화된 Cloud Native PG (CMPG)와 Atlas Operator를 추천함 
  1. CMPG (Cloud Native PostgreSQL):
    • CMPG는 PostgreSQL 데이터베이스를 Kubernetes 환경에서 관리하고 운영하기 위한 솔루션입니다.
    • Kubernetes 네이티브 방식을 채택하여, 데이터베이스 관리를 Kubernetes 클러스터와 일관성 있게 통합합니다. 이는 Kubernetes의 자동화, 확장성, 복원력 등의 장점을 데이터베이스 관리에도 적용할 수 있게 합니다.
    • PostgreSQL에 특화되어 있어, 이 데이터베이스 시스템을 사용하는 조직에게 특히 유용합니다.
    • Kubernetes 환경에서의 운영을 간소화하고, 더 효율적인 관리 및 자동화 기능을 제공하는 것이 주된 장점입니다.
  2. Atlas Operator:
    • Atlas Operator는 MongoDB Atlas와의 통합을 위한 Kubernetes 오퍼레이터입니다.
    • MongoDB Atlas는 클라우드에서 MongoDB 데이터베이스를 관리하는 서비스로, 자동화된 백업, 확장성, 보안 기능을 제공합니다.
    • Atlas Operator를 사용하면 Kubernetes 환경 내에서 MongoDB Atlas 리소스를 보다 쉽게 관리하고, Kubernetes 애플리케이션과 MongoDB Atlas 사이의 통합을 간편하게 구성할 수 있습니다.
    • 이러한 통합은 Kubernetes 클러스터의 리소스 관리와 데이터베이스 서비스 관리를 하나의 플랫폼에서 할 수 있게 해, 개발 및 운영 효율성을 높입니다.

6. Infrastructure and Service Management - k8s

  • Ansible, Terraform, Pulumi 등이 언급되지만, 최종 선택은 Kubernetes 자체임. Kubernetes는 컨테이너 관리뿐만 아니라 다양한 리소스 관리를 위한 플랫폼으로 강조됨

7.  Security - KubeScape, Teller

  • KubeScape는 Kubernetes 클러스터의 보안을 전반적으로 평가하고 개선할 수 있는 능력을 제공합니다. 이는 클라우드 네이티브 환경의 복잡성을 고려할 때 매우 중요한 기능입니다. 자동화된 보안 평가는 클러스터의 보안 상태를 지속적으로 모니터링하고 개선하는 데 큰 도움이 됩니다.
  • Teller는 민감한 데이터의 관리와 보안을 강화하는 데 중점을 두고 있으며, 특히 개발과 운영 환경에서의 비밀 관리에 있어서 중요한 역할을 합니다. 코드 내에 민감한 데이터를 하드코딩하는 위험을 줄이고, 보안을 강화하는 동시에 개발자의 작업 편의성을 높여줍니다.
  1. KubeScape:
    • KubeScape는 Kubernetes 환경을 위한 보안 검사 도구입니다.
    • 이 도구는 Kubernetes 클러스터의 구성과 배포된 애플리케이션을 분석하여, 보안 취약점과 비효율적인 구성을 식별합니다.
    • KubeScape는 CNCF의 보안 벤치마크와 산업 표준에 따라 Kubernetes 환경을 평가합니다. 이를 통해 보안 위험을 줄이고, 클러스터의 보안 상태를 개선하는 데 도움을 줍니다.
    • 자동화된 보안 검사를 통해 개발 및 운영 과정에서의 보안 관리를 간소화하고 효율적으로 만듭니다.
  2. Teller:
    • Teller는 애플리케이션과 개발 환경에서 비밀번호, API 키, 인증서 등과 같은 민감한 데이터를 안전하게 관리하기 위한 도구입니다.
    • 이 도구는 다양한 비밀 관리 시스템과 통합되며, 이러한 민감한 데이터를 안전하게 가져오고, 관리할 수 있도록 합니다.
    • 개발자들이 코드 내에 민감한 정보를 하드코딩하지 않고도, 필요한 시점에 안전하게 접근할 수 있게 해줍니다.
    • CI/CD 파이프라인, 개발자의 로컬 환경, 서버 등 다양한 환경에서의 비밀 관리를 지원합니다.

8. Networking - Cilium, Gateway API

  • 서비스 메시 도구들과 함께 Cilium이 중요한 네트워킹 솔루션이다. Cilium은 Kubernetes 네트워킹의 표준이며, eBPF를 기반으로 확장성을 제공함.
  • Gateway API는 Kubernetes의 Ingress 규격을 대체할 것으로 예상됨

9. Miscellaneous - Charm

  • Charm은 단일 도구가 아니라 터미널 사용 경험을 개선하기 위한 다양한 도구와 라이브러리의 세트임. 터미널 사용자에게 유용할 것

https://charm.sh/

 

10. Miscellaneous - eBPF

  • eBPF는 2023년 중요한 기술로 강조되며, 여러 솔루션의 기반이 됨. eBPF를 직접 사용하지는 않겠지만, 많은 도구와 서비스가 eBPF를 기반으로 구축될 것

 

'job' 카테고리의 다른 글

Nix shell 소개  (0) 2024.02.18
Kubernetes환경에서의 의존성에 대해서  (0) 2024.01.28
git 그룹단위로 전체 clone 하기  (0) 2023.08.25
keycloak 을 broker로, google을 IdP로  (0) 2023.05.23
teleport란  (0) 2023.05.02

137은 종료 코드 137은 컨테이너가 시그널 9 (SIGKILL)로 종료이다.

 

즉 컨테이너 내부의 문제라기 보다는 서버의 문제이다.

 

서버 문제로 docker 137이 나는 이유는 뭐가 있을까.

 

서버 문제를 찾기 위해 dmesg를 해보면 OOM(out of memory) 확인된다.

OOM 외에도 컨테이너에 mem_limit 걸어뒀는데 이거보다 넘어가면 137 에러 뜬다.

OpenFunction은 자체 서버리스 컴퓨팅 플랫폼으로 쿠버네티스 기반이다.

서버리스란 개발자들이 애플리케이션을 만들 때 서버를 직접 구축하고 관리하는 대신에 클라우드 공급자(예: AWS, Azure, Google Cloud)가 제공하는 서버리스 서비스를 사용하는 것을 의미합니다. 이는 개발자들이 애플리케이션 코드에 집중할 수 있도록 도와주며, 서버 관리와 같은 인프라 작업을 간소화합니다.

그러나 이 방법을 선택하면 약간의 제한사항이 발생할 수 있습니다. 예를 들어, 클라우드 공급자가 제공하는 서비스의 방식과 기능을 따라야 하기 때문에 개발자가 특정한 제한 사항을 갖게 될 수 있습니다. 이것이 바로 "벤더 락인" 또는 "공급자 종속성"이라고 불리는 현상입니다.

간단히 말해서, 서버리스를 사용하면 편리하고 빠른 개발이 가능하지만, 클라우드 공급자가 정한 규칙과 제한사항을 따라야 하므로 개발자가 자유롭게 원하는 대로 모든 것을 조절하는 것이 어려울 수 있습니다. 따라서 이러한 제한을 고려하여 개발 방향을 결정해야 합니다.

OpenFunction은 서버리스 컴퓨팅 플랫폼으로, 사용자가 애플리케이션의 비즈니스 로직에 집중할 수 있게 해주는 것을 목표로 합니다. 즉, 사용자는 기반 인프라나 운영 체제, 하드웨어 등에 대해 걱정할 필요 없이 애플리케이션 개발에만 집중할 수 있습니다.

그러나 실제로 OpenFunction을 사용하려면, 사용자가 스스로 이 플랫폼을 설정하고 유지 관리해야 합니다. 이것은 Kubernetes 클러스터의 설정 및 유지 관리, OpenFunction이 통합하는 다양한 구성 요소(예: 빌드 도구, 실행 환경)의 유지 관리, 그리고 OpenFunction 자체의 유지 관리를 포함합니다.

간단히 말해서, OpenFunction은 개발자가 애플리케이션 개발에만 집중할 수 있게 도와주는 플랫폼이지만, 이 플랫폼 자체를 운영하고 유지 관리하기 위해서는 기술적인 지식과 추가적인 노력이 필요하다는 것입니다. 따라서 비전공자나 기술적 지식이 부족한 사람이 OpenFunction을 효과적으로 사용하려면, 기술적인 부분을 관리할 수 있는 전문가의 도움이 필요할 수 있습니다.

강점으로는 서버리스 컴퓨팅의 이점, 다양한 도구와의 통합

단점으로는 불친절한 문서화, 특정 기술에 대한 깊은 이해가 필요하다는 점, 빌더가 자주 유지되지 않는다는 점

실제 사용 사례: 함수 생성, HTTP 트리거, 데이터베이스 연동, 스케일링 테스트 등을 포함합니다.

 

로컬 소스 코드에서 함수를 빌드 방법

컨테이너 이미지로 소스 코드 패키징: 로컬에 있는 소스 코드를 컨테이너 이미지로 패키징하고, 이 이미지를 컨테이너 레지스트리에 푸시합니다.

Dockerfile 사용: 소스 코드가 'samples' 디렉토리에 있다고 가정할 때, 아래와 같은 Dockerfile을 사용하여 소스 코드 번들 이미지를 빌드할 수 있습니다

dockerfileCopy code
FROM scratch
WORKDIR /
COPY samples samples/

 

이미지 빌드 및 푸시: 다음 명령어를 사용하여 소스 코드 번들 이미지를 빌드하고 푸시합니다.

bashCopy code
docker build -t <your registry name>/sample-source-code:latest -f </path/to/the/dockerfile> .
docker push <your registry name>/sample-source-code:latest

 

 

Scratch 이미지 사용 권장: 소스 코드 번들 이미지를 빌드할 때는 비어 있는 scratch 이미지를 기본 이미지로 사용하는 것이 좋습니다. 비어 있지 않은 기본 이미지는 소스 코드 복사에 실패할 수 있습니다.

 

Function 정의 변경: Git 저장소 방식으로 **spec.build.srcRepo.url**을 정의하는 것과 달리, 로컬 소스 코드를 사용할 때는 spec.build.srcRepo.bundleContainer.image 필드를 정의해야 합니다.

yamlCopy code
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
spec:
  build:
    srcRepo:
      bundleContainer:
        image: openfunctiondev/sample-source-code:latest
      sourceSubPath: "/samples/functions/async/logs-handler-function/"

 

Cloud Native Buildpacks 사용: OpenFunction은 Cloud Native Buildpacks를 사용하여 Dockerfile 생성 없이 함수 이미지를 빌드하는 것을 지원합니다. 이는 Serverless Applications을 Dockerfile과 함께 빌드할 때도 사용될 수 있습니다​​.

 

Build 섹션 정의를 통한 함수 빌드: 사용자는 Git 저장소의 소스 코드나 로컬에 저장된 소스 코드에서 함수나 애플리케이션을 빌드할 수 있습니다. Function 정의에 빌드 섹션을 추가하면, 서빙 섹션도 정의되어 있다면 빌드가 완료되자마자 함수가 시작됩니다​​.

 

Pack CLI 사용: 디버그 목적이나 오프라인 환경에서는 로컬 소스 코드에서 직접 함수 이미지를 빌드하는 것이 필요할 수 있습니다. 이를 위해 Pack CLI를 사용할 수 있습니다. Pack는 Cloud Native Buildpacks 프로젝트에 의해 유지되며 빌드팩을 사용하여 애플리케이션을 빌드하는 기능을 제공합니다​​.

 

OpenFunction Builders: Cloud Native Buildpacks를 사용하여 함수 이미지를 빌드하려면 빌더 이미지가 필요합니다. OpenFunction 커뮤니티에서는 여러 인기 언어에 대한 빌더를 제공하고 있습니다​​.

git 리포지토리의 소스 코드에서 함수 빌드하기

아래와 같이 함수 정의에 빌드 섹션을 추가하기만 하면 함수 이미지를 빌드할 수 있습니다. 서빙 섹션이 함께 정의되어 있으면 빌드가 완료되는 즉시 함수가 실행됩니다.

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
spec:
  version: "v2.0.0"
  image: openfunctiondev/logs-async-handler:v1
  imageCredentials:
    name: push-secret
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "LogsHandler"
      FUNC_CLEAR_SOURCE: "true"
      ## Customize functions framework version, valid for functions-framework-go for now
      ## Usually you needn't to do so because the builder will ship with the latest functions-framework
      # FUNC_FRAMEWORK_VERSION: "v0.4.0"
      ## Use FUNC_GOPROXY to set the goproxy
      # FUNC_GOPROXY: "https://goproxy.cn"
    srcRepo:
      url: "https://github.com/OpenFunction/samples.git"
      sourceSubPath: "functions/async/logs-handler-function/"
      revision: "main"

 

 

 

이외에도 아래와 같은 방법으로 빌드가 가능하다.

  1. Pack CLI 사용: 디버그 목적이나 오프라인 환경에서는 로컬 소스 코드에서 직접 함수 이미지를 빌드하는 것이 필요할 수 있습니다. 이를 위해 Pack CLI를 사용할 수 있습니다. Pack는 Cloud Native Buildpacks 프로젝트에 의해 유지되며 빌드팩을 사용하여 애플리케이션을 빌드하는 기능을 제공합니다​​.
  2. OpenFunction Builders: Cloud Native Buildpacks를 사용하여 함수 이미지를 빌드하려면 빌더 이미지가 필요합니다. OpenFunction 커뮤니티에서는 여러 인기 언어에 대한 빌더를 제공하고 있습니다​​.

 

 

 

-참고 URL

https://openfunction.dev/docs/introduction/

[Introduction

Overview OpenFunction is a cloud-native open source FaaS (Function as a Service) platform aiming to let you focus on your business logic without having to maintain the underlying runtime environment and infrastructure. You can generate event-driven and dyn

openfunction.dev](https://openfunction.dev/docs/introduction/)

'job > devops' 카테고리의 다른 글

kubewarden  (0) 2024.01.20
git common flow(또는 gitlab flow) 브랜치 전략에 대한 설명  (0) 2023.06.13

1. Android OS

 

2. iOS

glab

'job' 카테고리의 다른 글

Kubernetes환경에서의 의존성에 대해서  (0) 2024.01.28
2024년도 devops 추천 도구  (0) 2024.01.11
keycloak 을 broker로, google을 IdP로  (0) 2023.05.23
teleport란  (0) 2023.05.02
keycloak - gitlab 연동1  (0) 2023.04.17

이 글의 목적 : git common flow(gitlab flow)를 제대로 사용할 수 있도록 공식 문서를 번역하고 이해하기

참고 URL : https://commonflow.org/spec/1.0.0-rc.5.html

 

 

Introduction

Common-Flow is an attempt to gather a sensible selection of the most common usage patterns of git into a single and concise specification. It is based on the original variant of GitHub Flow, while taking into account how a lot of open source projects most commonly use git.

In short, Common-Flow is essentially GitHub Flow with the addition of versioned releases, optional release branches, and without the requirement to deploy to production all the time.

 

Git에서 가장 흔히 사용되는 패턴들을 효과적으로 처리할수 있또록 Github flow에 기반하여 다른 브랜치 전략(git flow)의 장점을 모은 브랜치전략이다.

 

Summary

  • The "master" branch is the mainline branch with latest changes, and must not be broken.
  • Changes (features, bugfixes, etc.) are done on "change branches" created from the master branch.
  • Rebase change branches early and often.
  • When a change branch is stable and ready, it is merged back in to master.
  • A release is just a git tag who's name is the exact release version string (e.g. "2.11.4").
  • Release branches can be used to avoid change freezes on master. They are not required, instead they are available if you need them.

"메인 브랜치"는 최신 변경 사항이 있는 메인라인 브랜치이며 중단되면 안됨.

개발작업은 마스터 브랜치에서 생성된 change branch에서 진행.

 

 

 

 

Terminology

  • Master Branch - Must be named "master", must always have passing tests, and is not guaranteed to always work in production environments.
  • Change Branches - Any branch that introduces changes like a new feature, a bug fix, etc.
  • Source Branch - The branch that a change branch was created from. New changes in the source branch should be incorporated into the change branch via rebasing.
  • Merge Target - A branch that is the intended merge target for a change branch. Typically the merge target branch will be the same as the source branch.
  • Pull Request - A means of requesting that a change branch is merged in to its merge target, allowing others to review, discuss and approve the changes.
  • Release - May be considered safe to use in production environments. Is effectively just a git tag named after the version of the release.
  • Release Branches - Used both for short-term preparations of a release, and also for long-term maintenance of older version.

 

 

Git Common-Flow Specification (Common-Flow)

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

  1. TL;DR
    1. Do not break the master branch.
    2. A release is a git tag.

 

  1. The Master Branch
    1. A branch named "master" MUST exist and it MUST be referred to as the "master branch".
    2. The master branch MUST always be in a non-broken state with its test suite passing.
    3. The master branch IS NOT guaranteed to always work in production environments. Despite test suites passing it may at times contain unfinished work. Only releases may be considered safe for production use.
    4. The master branch SHOULD always be in a "as near as possibly ready for release/production" state to reduce any friction with creating a new release.

 

  1. Change Branches
    1. Each change (feature, bugfix, etc.) MUST be performed on separate branches that SHOULD be referred to as "change branches".
    2. All change branches MUST have descriptive names.
    3. It is RECOMMENDED that you commit often locally, and that you try and keep the commits reasonably structured to avoid a messy and confusing git history.
    4. You SHOULD regularly push your work to the same named branch on the remote server.
    5. You SHOULD create separate change branches for each distinctly different change. You SHOULD NOT include multiple unrelated changes into a single change branch.
    6. When a change branch is created, the branch that it is created from SHOULD be referred to as the "source branch". Each change branch also needs a designated "merge target" branch, typically this will be the same as the source branch.
    7. Change branches MUST be regularly updated with any changes from their source branch. This MUST be done by rebasing the change branch on top of the source branch.
    8. After updating a change branch from its source branch you MUST push the change branch to the remote server. Due to the nature of rebasing, you will be required to do a force push, and you MUST use the "--force-with-lease" git push option when doing so instead of the regular "--force".
    9. If there is a truly valid technical reason to not use rebase when updating change branches, then you can update change branches via merge instead of rebase. The decision to use merge MUST only be taken after all possible options to use rebase have been tried and failed. People not understanding how to use rebase is NOT a valid reason to use merge. If you do decide to use merge instead of rebase, you MUST NOT use a mixture of both methods, pick one and stick to it.

 

  1. Pull Requests
    1. To merge a change branch into its merge target, you MUST open a "pull request" (or equivalent).
    2. The purpose of a pull request is to allow others to review your changes and give feedback. You can then fix any issues, complaints, and more that might arise, and then let people review again.
    3. Before creating a pull request, it is RECOMMENDED that you consider the state of your change branch's commit history. If it is messy and confusing, it might be a good idea to rebase your branch with "git rebase -i" to present a cleaner and easier to follow commit history for your reviewers.
    4. A pull request MUST only be merged when the change branch is up-to-date with its source branch, the test suite is passing, and you and others are happy with the change. This is especially important if the merge target is the master branch.
    5. To get feedback, help, or generally just discuss a change branch with others, it is RECOMMENDED you create a pull request and discuss the changes with others there. This leaves a clear and visible history of how, when, and why the code looks and behaves the way it does.

 

  1. Versioning
    1. A "version string" is a typically mostly numeric string that identifies a specific version of a project. The version string itself MUST NOT have a "v" prefix, but the version string can be displayed with a "v" prefix to indicate it is a version that is being referred to.
    2. The source of truth for a project's version MUST be a git tag with a name based on the version string. This kind of tag MUST be referred to as a "release tag".
    3. It is OPTIONAL, but RECOMMENDED to also keep the version string hard-coded somewhere in the project code-base.
    4. If you hard-code the version string into the code-base, it is RECOMMENDED that you do so in a file called "VERSION" located in the root of the project. But be mindful of the conventions of your programming language and community when choosing if, where and how to hard-code the version string.
    5. If you are using a "VERSION" file in the root of the project, this file MUST only contain the exact version string, meaning it MUST NOT have a "v" prefix. For example "v2.11.4" is bad, and "2.11.4" is good.
    6. It is OPTIONAL, but RECOMMENDED that that the version string follows Semantic Versioning (http://semver.org/).

 

  1. Releases
    1. To create a new release, you MUST create a git tag named as the exact version string of the release. This kind of tag MUST be referred to as a "release tag".
    2. The release tag name can OPTIONALLY be prefixed with "v". For example the tag name can be either "2.11.4" or "v2.11.4". It is however RECOMMENDED that you do not use a "v" prefix. You MUST NOT use a mixture of "v" prefixed and non-prefixed tags. Pick one form and stick to it.
    3. If the version string is hard-coded into the code-base, you MUST create a "version bump" commit which changes the hard-coded version string of the project.
    4. When using version bump commits, the release tag MUST be placed on the version bump commit.
    5. If you are not using a release branch, then the release tag, and if relevant the version bump commit, MUST be created directly on the master branch.
    6. The version bump commit SHOULD have a commit message title of "Bump version to VERSION". For example, if the new version string is "2.11.4", the first line of the commit message SHOULD read: "Bump version to 2.11.4"
    7. It is RECOMMENDED that release tags are lightweight tags, but you can OPTIONALLY use annotated tags if you want to include changelog information in the release tag itself.
    8. If you use annotated release tags, the first line of the annotation SHOULD read "Release VERSION". For example for version "2.11.4" the first line of the tag annotation SHOULD read "Release 2.11.4". The second line MUST be blank, and the changelog MUST start on the third line.

 

  1. Short-Term Release Branches
    1. Any branch that has a name starting with "release-" SHOULD be referred to as a "release branch".
    2. Any release branch which has a name ending with a specific version string, MUST be referred to as a "short-term release branch".
    3. Use of short-term release branches are OPTIONAL, and intended to be used to create a specific versioned release.
    4. A short-term release branch is RECOMMENDED if there is a lengthy pre-release verification process to avoid a code freeze on the master branch.
    5. Short-term release branches MUST have a name of "release-VERSION". For example for version "2.11.4" the release branch name MUST be "release-2.11.4".
    6. When using a short-term release branch to create a release, the release tag and if used, version bump commit, MUST be placed directly on the short-term release branch itself.
    7. Only very minor changes should be performed on a short-term release branch directly. Any larger changes SHOULD be done in the master branch, and SHOULD be pulled into the release branch by rebasing it on top of the master branch the same way a change branch pulls in updates from its source branch.
    8. After a release tag has been created, the release branch MUST be merged back into its source branch and then deleted. Typically the source branch will be the master branch.

 

  1. Long-term Release Branches
    1. Any release branch which has a name ending with a non-specific version string, MUST be referred to as a "long-term release branch". For example "release-2.11" is a long-term release branch, while "release-2.11.4" is a short-term release branch.
    2. Use of long-term release branches are OPTIONAL, and intended for work on versions which are not currently part of the master branch. Typically this is useful when you need to create a new maintenance release for a older version.
    3. A long-term release branch MUST have a name with a non-specific version number. For example a long-term release branch for creating new 2.9.x releases MUST be named "release-2.9".
    4. Long-term release branches for maintenance releases of older versions MUST be created from the relevant release tag. For example if the master branch is on version 2.11.4 and there is a security fix for all 2.9.x releases, the latest of which is "2.9.7". Create a new branch called "release-2.9" from the "2.9.7" release tag. The security fix release will then end up being version "2.9.8".
    5. To create a new release from a long-term release branch, you MUST follow the same process as a release from the master branch, except the long-term release branch takes the place of the master branch.
    6. A long-term release branch should be treated with the same respect as the master branch. It is effectively the master branch for the release series in question. Meaning it MUST always be in a non-broken state, MUST NOT be force pushed to, etc.

 

  1. Bug Fixes & Rollback
    1. You MUST NOT under any circumstances force push to the master branch or to long-term release branches.
    2. If a change branch which has been merged into the master branch is found to have a bug in it, the bug fix work MUST be done as a new separate change branch and MUST follow the same workflow as any other change branch.
    3. If a change branch is wrongfully merged into master, or for any other reason the merge must be undone, you MUST undo the merge by reverting the merge commit itself. Effectively creating a new commit that reverses all the relevant changes.

 

  1. Git Best Practices
    1. All commit messages SHOULD follow the Commit Guidelines and format from the official git documentation: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
    2. You SHOULD never blindly commit all changes with "git commit -a". It is RECOMMENDED you use "git add -i" or "git add -p" to add individual changes to the staging area so you are fully aware of what you are committing.
    3. You SHOULD always use "--force-with-lease" when doing a force push. The regular "--force" option is dangerous and destructive. More information: https://developer.atlassian.com/blog/2015/04/force-with-lease/
    4. You SHOULD understand and be comfortable with rebasing: https://git-scm.com/book/en/v2/Git-Branching-Rebasing
    5. It is RECOMMENDED that you always do "git pull --rebase" instead of "git pull" to avoid unnecessary merge commits. You can make this the default behavior of "git pull" with "git config --global pull.rebase true".
    6. It is RECOMMENDED that all branches be merged using "git merge --no-ff". This makes sure the reference to the original branch is kept in the commits, allows one to revert a merge by reverting a single merge commit, and creates a merge commit to mark the integration of the branch with master.

 

FAQ

Why use Common-Flow instead of Git Flow, and how does it differ?

Common-Flow tries to be a lot less complicated than Git Flow by having fewer types of branches, and simpler rules. Normal day to day development doesn't really change much:

  • You create change branches instead of feature branches, without the need of a "feature/" or "change/" prefix in the branch name.
  • Change branches are typically created from and merged back into "master" instead of "develop".
  • Creating a release is done by simply creating a git tag, typically on the master branch.

 

 

In detail, the main differences between Git Flow and Common-Flow are:

  • There is no "develop" branch, there is only a "master" branch which contains the latest work. In Git Flow the master branch effectively ends up just being a pointer to the latest release, despite the fact that Git Flow includes release tags too. In Common-Flow you just look at the tags to find the latest release.
  • There are no "feature" or "hotfix" branches, there's only "change" branches. Any branch that is not master and introduces changes is a change branch. Change branches also don't have a enforced naming convention, they just have to have a "descriptive name". This makes things simpler and allows more flexibility.
  • Release branches are available, but optional. Instead of enforcing the use of release branches like Git Flow, Common-Flow only recommends the use of release branches when it makes things easier. If creating a new release by tagging "master" works for you, great, do that.

 

 

Why use Common-Flow instead of GitHub Flow, and how does it differ?

Common-Flow is essentially GitHub Flow with the addition of a "Release" concept that uses tags. It also attempts to define how certain common tasks are done, like updating change/feature branches from their source branches for example. This is to help end arguments about how such things are done.

If a deployment/release for you is just getting the latest code in the master branch out, without caring about bumping version numbers or anything, then GitHub Flow is a good fit for you, and you probably don't need the extras of Common-Flow.

However if your deployments/releases have specific version numbers, then Common-Flow gives you a simple set of rules of how to create and manage releases, on top of what GitHub Flow already does.

 

 

What does "descriptive name" mean for change branches?

It means what it sounds like. The name should be descriptive, as in by just reading the name of the branch you should understand what the branch's purpose is and what it does. Here's a few examples:

  • add-2fa-support
  • fix-login-issue
  • remove-sort-by-middle-name-functionality
  • update-font-awesome
  • change-search-behavior
  • improve-pagination-performance
  • tweak-footer-style

Notice how none of these have any prefixes like "feature/" or "hotfix/", they're not needed when branch names are properly descriptive. However there's nothing to say you can't use such prefixes if you want.

You can also add ticket numbers to the branch name if your team/org has that as part of it's process. But it is recommended that ticket numbers are added to the end of the branch name. The ticket number is essentially metadata, so put it at the end and out of the way of humans trying to read the descriptive name from left to right.

 

 

 

How do we release an emergency hotfix when the master branch is broken?

This should ideally never happen, however if it does you can do one of the following:

  • Review why the master branch is broken and revert the changes that caused the issues. Then apply the hotfix and release.
  • Or use a short-term release branch created from the latest release tag instead of the master branch. Apply the hotfix to the release branch, create a release tag on the release branch, and then merge it back into master.

In this situation, it is recommended you try to revert the offending changes that's preventing a new release from master. But if that proves to be a complicated task and you're short on time, a short-term release branch gives you a instant fix to the situation at hand, and let's you resolve the issues with the master branch when you have more time on your hands.

 

 

'job > devops' 카테고리의 다른 글

kubewarden  (0) 2024.01.20
openfunction  (0) 2023.12.01

이번주 주제는 EKS automation 이다.

 

우선 실습환경 배포

# YAML 파일 다운로드
curl -O <https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/**eks-oneclick6.yaml**>

# CloudFormation 스택 배포
예시) aws cloudformation deploy --template-file **eks-oneclick6.yaml** --stack-name **myeks** --parameter-overrides KeyName=**kp-gasida** SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=**AKIA5...** MyIamUserSecretAccessKey=**'CVNa2...'** ClusterBaseName=**myeks** --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name **myeks** --query 'Stacks[*].**Outputs[0]**.OutputValue' --output text

# 작업용 EC2 SSH 접속
ssh -i **~/.ssh/kp-gasida.pem** ec2-user@$(aws cloudformation describe-stacks --stack-name **myeks** --query 'Stacks[*].Outputs[0].OutputValue' --output text)
# default 네임스페이스 적용
kubectl ns default

# (옵션) context 이름 변경
NICK=<각자 자신의 닉네임>
NICK=gasida
kubectl ctx
kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io $NICK

# ExternalDNS
MyDomain=<자신의 도메인>
echo "export MyDomain=<자신의 도메인>" >> /etc/profile
*MyDomain=gasida.link*
*echo "export MyDomain=gasida.link" >> /etc/profile*
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 -

# 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

# 노드 IP 확인 및 PrivateIP 변수 지정
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3

# 노드 보안그룹 ID 확인
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values='*ng1*' --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.0/24

# 워커 노드 SSH 접속
for node in $N1 $N2 $N3; do ssh ec2-user@$node hostname; done
# 사용 리전의 인증서 ARN 확인
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

# repo 추가
helm repo add prometheus-community <https://prometheus-community.github.io/helm-charts>

# 파라미터 파일 생성
cat < monitor-values.yaml
**prometheus**:
  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

defaultRules:
  create: false
kubeControllerManager:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false
alertmanager:
  enabled: false
EOT

# 배포
kubectl create ns monitoring
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 45.27.2 \\
--set prometheus.prometheusSpec.scrapeInterval='15s' --set prometheus.prometheusSpec.evaluationInterval='15s' \\
-f monitor-values.yaml --namespace monitoring

# Metrics-server 배포
kubectl apply -f <https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml>

 

ACK(AWS Controller for K8S)

aws 서비스 리소스를 k8s에서 직접 정의하고 사용할수있다. 즉 쿠버네티스 안에서 aws의 리소스를 생성하고 관리할수있다.

다만 모든 aws 서비스를 지원하지는 않는다. 

  • Maintenance Phases 관리 단계 : PREVIEW (테스트 단계, 상용 서비스 비권장) , GENERAL AVAILABILITY (상용 서비스 권장), DEPRECATED, NOT SUPPORTED
  • GA 서비스 : ApiGatewayV2, CloudTrail, DynamoDB, EC2, ECR, EKS, IAM, KMS, Lambda, MemoryDB, RDS, S3, SageMaker…
  • Preview 서비스 : ACM, ElastiCache, EventBridge, MQ, Route 53, SNS, SQS…

 

S3를 쿠버네티스(with helm)에서 배포하려면 다음과 같이 진행한다.

# 서비스명 변수 지정
**export SERVICE=s3**

# helm 차트 다운로드
~~#aws ecr-public get-login-password --region us-east-1 | helm registry login --username AWS --password-stdin public.ecr.aws~~
export RELEASE_VERSION=$(curl -sL <https://api.github.com/repos/aws-controllers-k8s/$SERVICE-controller/releases/latest> | grep '"tag_name":' | cut -d'"' -f4 | cut -c 2-)
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
tar xzvf $SERVICE-chart-$RELEASE_VERSION.tgz

# helm chart 확인
tree ~/$SERVICE-chart

# ACK S3 Controller 설치
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=ap-northeast-2
helm install --create-namespace -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller --set aws.region="$AWS_REGION" ~/$SERVICE-chart

# 설치 확인
helm list --namespace $ACK_SYSTEM_NAMESPACE
kubectl -n ack-system get pods
kubectl get crd | grep $SERVICE
buckets.s3.services.k8s.aws                  2022-04-24T13:24:00Z

kubectl get all -n ack-system
kubectl get-all -n ack-system
kubectl describe sa -n ack-system ack-s3-controller

우선 위와같이 ACK S3 컨트롤러를 설치해주고...

위와같이 IRSA를 설정해준다.

 

이제 S3를 배포해보자.

# [터미널1] 모니터링
watch -d aws s3 ls

# S3 버킷 생성을 위한 설정 파일 생성
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
export BUCKET_NAME=my-ack-s3-bucket-$AWS_ACCOUNT_ID

read -r -d '' BUCKET_MANIFEST <<EOF
apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
  name: $BUCKET_NAME
spec:
  name: $BUCKET_NAME
EOF

echo "${BUCKET_MANIFEST}" > bucket.yaml
cat bucket.yaml | yh

# S3 버킷 생성
aws s3 ls
**kubectl create -f bucket.yaml**
*bucket.s3.services.k8s.aws/my-ack-s3-bucket-**<my account id>** created*

# S3 버킷 확인
aws s3 ls
**kubectl get buckets**
kubectl describe bucket/$BUCKET_NAME | head -6
Name:         my-ack-s3-bucket-**<my account id>**
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  s3.services.k8s.aws/v1alpha1
Kind:         Bucket

aws s3 ls | grep $BUCKET_NAME
2022-04-24 18:02:07 my-ack-s3-bucket-**<my account id>**

# S3 버킷 업데이트 : 태그 정보 입력
read -r -d '' BUCKET_MANIFEST <<EOF
apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
  name: $BUCKET_NAME
spec:
  name: $BUCKET_NAME
  **tagging:
    tagSet:
    - key: myTagKey
      value: myTagValue**
EOF

echo "${BUCKET_MANIFEST}" > bucket.yaml

# S3 버킷 설정 업데이트 실행 : 필요 주석 자동 업뎃 내용이니 무시해도됨!
**kubectl apply -f bucket.yaml**

# S3 버킷 업데이트 확인 
kubectl describe bucket/$BUCKET_NAME | grep Spec: -A5
Spec:
  Name:  my-ack-s3-bucket-**<my account id>**
  Tagging:
    Tag Set:
      Key:    myTagKey
      Value:  myTagValue

# S3 버킷 삭제
**kubectl delete -f bucket.yaml**

# verify the bucket no longer exists
kubectl get bucket/$BUCKET_NAME
aws s3 ls | grep $BUCKET_NAME

위와같이 손쉽게 S3가 배포된것을 확인할수있다.

 

이번엔 배포된 s3를 업데이트해보면..

 

위와같이 태깅이 추가된것을 확인할수있다.

 

EC2 배포는 다음과 같이 진행한다.(S3 와 거의 동일하다.)

# 서비스명 변수 지정 및 helm 차트 다운로드
**export SERVICE=ec2**
export RELEASE_VERSION=$(curl -sL <https://api.github.com/repos/aws-controllers-k8s/$SERVICE-controller/releases/latest> | grep '"tag_name":' | cut -d'"' -f4 | cut -c 2-)
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
tar xzvf $SERVICE-chart-$RELEASE_VERSION.tgz

# helm chart 확인
tree ~/$SERVICE-chart

# ACK EC2-Controller 설치
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=ap-northeast-2
helm install -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller --set aws.region="$AWS_REGION" ~/$SERVICE-chart

# 설치 확인
helm list --namespace $ACK_SYSTEM_NAMESPACE
kubectl -n $ACK_SYSTEM_NAMESPACE get pods -l "app.kubernetes.io/instance=ack-$SERVICE-controller"
**kubectl get crd | grep $SERVICE**
dhcpoptions.ec2.services.k8s.aws             2023-05-30T12:45:13Z
elasticipaddresses.ec2.services.k8s.aws      2023-05-30T12:45:13Z
instances.ec2.services.k8s.aws               2023-05-30T12:45:13Z
internetgateways.ec2.services.k8s.aws        2023-05-30T12:45:13Z
natgateways.ec2.services.k8s.aws             2023-05-30T12:45:13Z
routetables.ec2.services.k8s.aws             2023-05-30T12:45:13Z
securitygroups.ec2.services.k8s.aws          2023-05-30T12:45:13Z
subnets.ec2.services.k8s.aws                 2023-05-30T12:45:13Z
transitgateways.ec2.services.k8s.aws         2023-05-30T12:45:13Z
vpcendpoints.ec2.services.k8s.aws            2023-05-30T12:45:13Z
vpcs.ec2.services.k8s.aws                    2023-05-30T12:45:13Z

 

 

 

 

 Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
eksctl create **iamserviceaccount** \\
  --name **ack-**$SERVICE**-controller** \\
  --namespace $ACK_SYSTEM_NAMESPACE \\
  --cluster $CLUSTER_NAME \\
  --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`**AmazonEC2FullAccess**`].Arn' --output text) \\
  --**override-existing-serviceaccounts** --approve

# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get sa -n $ACK_SYSTEM_NAMESPACE
kubectl describe sa ack-$SERVICE-controller -n $ACK_SYSTEM_NAMESPACE

# Restart ACK service controller deployment using the following commands.
**kubectl -n $**ACK_SYSTEM_NAMESPACE **rollout restart deploy ack-$**SERVICE**-controller-$**SERVICE**-chart**

# IRSA 적용으로 Env, Volume 추가 확인
kubectl describe pod -n $ACK_SYSTEM_NAMESPACE -l k8s-app=$SERVICE-chart
...

 

 

VPC, Subnet 생성 삭제는 다음과같이 진행한다.

# [터미널1] 모니터링
while true; do aws ec2 describe-vpcs --query 'Vpcs[*].{VPCId:VpcId, CidrBlock:CidrBlock}' --output text; echo "-----"; sleep 1; done

# VPC 생성
cat <<EOF > vpc.yaml
apiVersion: **ec2.services.k8s.aws/v1alpha1**
kind: **VPC**
metadata:
  name: **vpc-tutorial-test**
spec:
  cidrBlocks: 
  - **10.0.0.0/16**
  enableDNSSupport: true
  enableDNSHostnames: true
EOF
 
**kubectl apply -f vpc.yaml**
*vpc.ec2.services.k8s.aws/vpc-tutorial-test created*

# VPC 생성 확인
kubectl get vpcs
kubectl describe vpcs
**aws ec2 describe-vpcs --query 'Vpcs[*].{VPCId:VpcId, CidrBlock:CidrBlock}' --output text**

# [터미널1] 모니터링
VPCID=$(kubectl get vpcs vpc-tutorial-test -o jsonpath={.status.vpcID})
while true; do aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --query 'Subnets[*].{SubnetId:SubnetId, CidrBlock:CidrBlock}' --output text; echo "-----"; sleep 1 ; done

# 서브넷 생성
VPCID=$(kubectl get vpcs vpc-tutorial-test -o jsonpath={.status.vpcID})

cat <<EOF > subnet.yaml
apiVersion: **ec2**.services.k8s.aws/v1alpha1
kind: **Subnet**
metadata:
  name: **subnet-tutorial-test**
spec:
  cidrBlock: **10.0.0.0/20**
  vpcID: $VPCID
EOF
**kubectl apply -f subnet.yaml**

# 서브넷 생성 확인
kubectl get subnets
kubectl describe subnets
**aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --query 'Subnets[*].{SubnetId:SubnetId, CidrBlock:CidrBlock}' --output text**

# 리소스 삭제
kubectl delete -f subnet.yaml && kubectl delete -f vpc.yaml

 

 

 

 

 

이번에는 VPC 워크플로우를 생성해보고자 한다.

 

cat <<EOF > vpc-workflow.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **VPC**
metadata:
  name: **tutorial-vpc**
spec:
  cidrBlocks: 
  - **10.0.0.0/16**
  enableDNSSupport: true
  enableDNSHostnames: true
  tags:
    - key: name
      value: vpc-tutorial
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **InternetGateway**
metadata:
  name: **tutorial-igw**
spec:
  **vpcRef**:
    from:
      name: **tutorial-vpc**
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **NATGateway**
metadata:
  name: **tutorial-natgateway1**
spec:
  **subnetRef**:
    from:
      name: tutorial-public-subnet1
  **allocationRef**:
    from:
      name: tutorial-eip1
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **ElasticIPAddress**
metadata:
  name: **tutorial-eip1**
spec:
  tags:
    - key: name
      value: eip-tutorial
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **RouteTable**
metadata:
  name: **tutorial-public-route-table**
spec:
  **vpcRef**:
    from:
      name: tutorial-vpc
  **routes**:
  - destinationCIDRBlock: 0.0.0.0/0
    **gatewayRef**:
      from:
        name: tutorial-igw
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **RouteTable**
metadata:
  name: **tutorial-private-route-table-az1**
spec:
  **vpcRef**:
    from:
      name: tutorial-vpc
  routes:
  - destinationCIDRBlock: 0.0.0.0/0
    **natGatewayRef**:
      from:
        name: tutorial-natgateway1
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **Subnet**
metadata:
  name: tutorial-**public**-subnet1
spec:
  availabilityZone: **ap-northeast-2a**
  cidrBlock: **10.0.0.0/20**
  mapPublicIPOnLaunch: true
  **vpcRef**:
    from:
      name: tutorial-vpc
  **routeTableRefs**:
  - from:
      name: tutorial-public-route-table
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **Subnet**
metadata:
  name: tutorial-**private**-subnet1
spec:
  availabilityZone: **ap-northeast-2a**
  cidrBlock: **10.0.128.0/20**
  **vpcRef**:
    from:
      name: tutorial-vpc
  **routeTableRefs**:
  - from:
      name: tutorial-private-route-table-az1
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **SecurityGroup**
metadata:
  name: tutorial-security-group
spec:
  description: "ack security group"
  name: tutorial-sg
  vpcRef:
     from:
       name: tutorial-vpc
  ingressRules:
    - ipProtocol: tcp
      fromPort: 22
      toPort: 22
      ipRanges:
        - cidrIP: "0.0.0.0/0"
          description: "ingress"
EOF

 

# VPC 환경 생성
**kubectl apply -f vpc-workflow.yaml**

# [터미널1] NATGW 생성 완료 후 tutorial-private-route-table-az1 라우팅 테이블 ID가 확인되고 그후 tutorial-private-subnet1 서브넷ID가 확인됨 > 5분 정도 시간 소요
**watch -d kubectl get routetables,subnet**

# VPC 환경 생성 확인
kubectl describe vpcs
kubectl describe internetgateways
kubectl describe routetables
kubectl describe natgateways
kubectl describe elasticipaddresses
kubectl describe securitygroups

# 배포 도중 2개의 서브넷 상태 정보 비교 해보자
**kubectl describe subnets
...
Status**:
  **Conditions**:
    Last Transition Time:  2023-06-04T02:15:25Z
    Message:               Reference resolution failed
    Reason:                the referenced resource is not synced yet. resource:RouteTable, namespace:default, name:tutorial-private-route-table-az1
    **Status:                Unknown**
    Type:                  ACK.ReferencesResolved
**...
Status**:
  Ack Resource Metadata:
    Arn:                       arn:aws:ec2:ap-northeast-2:911283464785:subnet/subnet-0f5ae09e5d680030a
    Owner Account ID:          911283464785
    Region:                    ap-northeast-2
  Available IP Address Count:  4091
  **Conditions**:
    Last Transition Time:           2023-06-04T02:14:45Z
    **Status:                         True**
    Type:                           ACK.ReferencesResolved
    Last Transition Time:           2023-06-04T02:14:45Z
    Message:                        Resource synced successfully
    Reason:
    **Status:                         True**
    Type:                           ACK.ResourceSynced
...

참고로 앞서 EC2, VPC등의 실습을 진행해야 VPC워크플로우 생성이 된다. CRD를 미리 생성해줘야 하기 때문이다.

또한 리소스 의존성으로 늦게 생성되는것들이 있어서 좀 기다려야 한다.

 

이제 퍼블릭 서브넷에 인스턴스를 생성해보자.

# public 서브넷 ID 확인
PUBSUB1=$(kubectl get subnets **tutorial-public-subnet1** -o jsonpath={.status.subnetID})
echo $PUBSUB1

# 보안그룹 ID 확인
TSG=$(kubectl get securitygroups **tutorial-security-group** -o jsonpath={.status.id})
echo $TSG

# Amazon Linux 2 최신 AMI ID 확인
AL2AMI=$(aws ec2 describe-images --owners **amazon** --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" --query 'Images[0].ImageId' --output text)
echo $AL2AMI

# 각자 자신의 SSH 키페어 이름 변수 지정
MYKEYPAIR=<각자 자신의 SSH 키페어 이름>
MYKEYPAIR=kp-gasida

**# 변수 확인 > 특히 서브넷 ID가 확인되었는지 꼭 확인하자!**
echo $PUBSUB1 , $TSG , $AL2AMI , $MYKEYPAIR

# [터미널1] 모니터링
while true; do 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; date ; sleep 1 ; done

# public 서브넷에 인스턴스 생성
cat <<EOF > tutorial-bastion-host.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **Instance**
metadata:
  name: **tutorial-bastion-host**
spec:
  imageID: $AL2AMI # AL2 AMI ID - ap-northeast-2
  instanceType: **t3.medium**
  subnetID: $PUBSUB1
  securityGroupIDs:
  - $TSG
  keyName: $MYKEYPAIR
  tags:
    - key: producer
      value: ack
EOF
**kubectl apply -f tutorial-bastion-host.yaml**

# 인스턴스 생성 확인
**kubectl** get instance
**kubectl** describe instance
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

위와같이 새로운 인스턴스 생성을 확인할수있다.

 

허나 아직 접속이 되지 않을것이다.

아래처럼 egress 규칙을 추가해야한다.

cat <<EOF > modify-sg.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: SecurityGroup
metadata:
  name: tutorial-security-group
spec:
  description: "ack security group"
  name: tutorial-sg
  vpcRef:
     from:
       name: tutorial-vpc
  ingressRules:
    - ipProtocol: tcp
      fromPort: 22
      toPort: 22
      ipRanges:
        - cidrIP: "0.0.0.0/0"
          description: "ingress"
  **egressRules:
    - ipProtocol: '-1'
      ipRanges:
        - cidrIP: "0.0.0.0/0"
          description: "egress"**
EOF
kubectl apply -f modify-sg.yaml

# 변경 확인 >> 보안그룹에 아웃바운드 규칙 확인
kubectl logs -n $ACK_SYSTEM_NAMESPACE -l k8s-app=ec2-chart -f

 

 

이번에는 프라이빗 서브넷에 인스턴스를 생성해보자.

# private 서브넷 ID 확인 >> NATGW 생성 완료 후 RT/SubnetID가 확인되어 다소 시간 필요함
PRISUB1=$(kubectl get subnets **tutorial-private-subnet1** -o jsonpath={.status.subnetID})
**echo $PRISUB1**

**# 변수 확인 > 특히 private 서브넷 ID가 확인되었는지 꼭 확인하자!**
echo $PRISUB1 , $TSG , $AL2AMI , $MYKEYPAIR

# private 서브넷에 인스턴스 생성
cat <<EOF > tutorial-instance-private.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: **Instance**
metadata:
  name: **tutorial-instance-private**
spec:
  imageID: $AL2AMI # AL2 AMI ID - ap-northeast-2
  instanceType: **t3.medium**
  subnetID: $PRISUB1
  securityGroupIDs:
  - $TSG
  keyName: $MYKEYPAIR
  tags:
    - key: producer
      value: ack
EOF
**kubectl apply -f tutorial-instance-private.yaml**

# 인스턴스 생성 확인
**kubectl** get instance
**kubectl** describe instance
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

위와같이 프라이빗 인스턴스도 생성된것이 확인된다.

 

RDS도 다음과같이 진행하면된다.

# 서비스명 변수 지정 및 helm 차트 다운로드
**export SERVICE=rds**
export RELEASE_VERSION=$(curl -sL <https://api.github.com/repos/aws-controllers-k8s/$SERVICE-controller/releases/latest> | grep '"tag_name":' | cut -d'"' -f4 | cut -c 2-)
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
tar xzvf $SERVICE-chart-$RELEASE_VERSION.tgz

# helm chart 확인
tree ~/$SERVICE-chart

# ACK EC2-Controller 설치
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=ap-northeast-2
helm install -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller --set aws.region="$AWS_REGION" ~/$SERVICE-chart

# 설치 확인
helm list --namespace $ACK_SYSTEM_NAMESPACE
kubectl -n $ACK_SYSTEM_NAMESPACE get pods -l "app.kubernetes.io/instance=ack-$SERVICE-controller"
**kubectl get crd | grep $SERVICE**
# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
eksctl create **iamserviceaccount** \\
  --name **ack-**$SERVICE**-controller** \\
  --namespace $ACK_SYSTEM_NAMESPACE \\
  --cluster $CLUSTER_NAME \\
  --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`**AmazonRDSFullAccess**`].Arn' --output text) \\
  --**override-existing-serviceaccounts** --approve

# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get sa -n $ACK_SYSTEM_NAMESPACE
kubectl describe sa ack-$SERVICE-controller -n $ACK_SYSTEM_NAMESPACE

# Restart ACK service controller deployment using the following commands.
**kubectl -n $**ACK_SYSTEM_NAMESPACE **rollout restart deploy ack-$**SERVICE**-controller-$**SERVICE**-chart**

# IRSA 적용으로 Env, Volume 추가 확인
kubectl describe pod -n $ACK_SYSTEM_NAMESPACE -l k8s-app=$SERVICE-chart
...
# DB 암호를 위한 secret 생성
RDS_INSTANCE_NAME="<your instance name>"
RDS_INSTANCE_PASSWORD="<your instance password>"
RDS_INSTANCE_NAME=**myrds**
RDS_INSTANCE_PASSWORD=**qwe12345**
kubectl create secret generic "${RDS_INSTANCE_NAME}-password" --from-literal=password="${RDS_INSTANCE_PASSWORD}"

# 확인
kubectl get secret $RDS_INSTANCE_NAME-password

# [터미널1] 모니터링
RDS_INSTANCE_NAME=myrds
watch -d "kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'"

# RDS 배포 생성 : 15분 이내 시간 소요 >> 보안그룹, 서브넷 등 필요한 옵션들은 추가해서 설정해보자!
cat <<EOF > rds-mariadb.yaml
apiVersion: rds.services.k8s.aws/v1alpha1
kind: DBInstance
metadata:
  name: "${RDS_INSTANCE_NAME}"
spec:
  allocatedStorage: 20
  dbInstanceClass: **db.t4g.micro**
  dbInstanceIdentifier: "${RDS_INSTANCE_NAME}"
  engine: **mariadb**
  engineVersion: "**10.6**"
  masterUsername: "**admin**"
  masterUserPassword:
    namespace: default
    name: "${RDS_INSTANCE_NAME}-password"
    key: password
EOF
kubectl apply -f rds-mariadb.yaml

# 생성 확인
kubectl get dbinstances  ${RDS_INSTANCE_NAME}
kubectl describe dbinstance "${RDS_INSTANCE_NAME}"
aws rds describe-db-instances --db-instance-identifier $RDS_INSTANCE_NAME | jq

kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'
  Db Instance Status:         **creating**

kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'
  Db Instance Status:         **backing-up**

kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'
  Db Instance Status:         **available**

# 생성 완료 대기 : for 지정 상태가 완료되면 정상 종료됨
****kubectl wait dbinstances ${RDS_INSTANCE_NAME} **--for=condition=ACK.ResourceSynced** --timeout=15m
*dbinstance.rds.services.k8s.aws/myrds condition met*

 

이번엔 Flux에 대해 스터디를 진행한다.

설치 방법은 다음과 같다.

# Flux CLI 설치
**curl -s <https://fluxcd.io/install.sh> | sudo bash**
. <(flux completion bash)

# 버전 확인
**flux --version**
flux version 2.0.0-rc.5

# 자신의 Github 토큰과 유저이름 변수 지정
export GITHUB_TOKEN=
export GITHUB_USER=
export GITHUB_TOKEN=ghp_###
export GITHUB_USER=gasida

# Bootstrap
## Creates a git repository **fleet-infra** on your GitHub account.
## Adds Flux component manifests to the repository.
## **Deploys** Flux Components to your Kubernetes Cluster.
## Configures Flux components to track the path /clusters/my-cluster/ in the repository.
**flux bootstrap github \\
  --owner=$GITHUB_USER \\
  --repository=fleet-infra \\
  --branch=main \\
  --path=./clusters/my-cluster \\
  --personal**

# 설치 확인
kubectl get pods -n flux-system
kubectl get-all -n flux-system
kubectl get crd | grep fluxc
**kubectl get gitrepository -n flux-system**
NAME          URL                                       AGE    READY   STATUS
flux-system   ssh://git@github.com/gasida/fleet-infra   4m6s   True    stored artifact for revision 'main@sha1:4172548433a9f4e089758c3512b0b24d289e9702'

위와같이 설치 진행

 

Gitops 도구 설치는 아래와같이 진행한다.

# gitops 도구 설치
curl --silent --location "<https://github.com/weaveworks/weave-gitops/releases/download/v0.24.0/gitops-$(uname)-$>(uname -m).tar.gz" | tar xz -C /tmp
sudo mv /tmp/gitops /usr/local/bin
gitops version

# flux 대시보드 설치
PASSWORD="password"
gitops create dashboard ww-gitops --password=$PASSWORD

# 확인
flux -n flux-system get helmrelease
kubectl -n flux-system get pod,svc

이후 ingress 를 설정해준다.

CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

# Ingress 설정
cat <<EOT > gitops-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitops-ingress
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/group.name: **study**
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
  - host: gitops.$MyDomain
    http:
      paths:
      - backend:
          service:
            name: ww-gitops-weave-gitops
            port:
              number: 9001
        path: /
        pathType: Prefix
EOT
kubectl apply -f gitops-ingress.yaml -n flux-system

# 배포 확인
kubectl get ingress -n flux-system

# GitOps 접속 정보 확인 >> 웹 접속 후 정보 확인
echo -e "GitOps Web <https://gitops.$MyDomain>"

헬로월드 예제 소스 코드이다.

# 소스 생성 : 유형 - git, helm, oci, bucket
# flux create source {소스 유형}
# 악분(최성욱)님이 준비한 repo로 git 소스 생성
GITURL="<https://github.com/sungwook-practice/fluxcd-test.git>"
**flux create source git nginx-example1 --url=$GITURL --branch=main --interval=30s**

# 소스 확인
flux get sources git
kubectl -n flux-system get gitrepositories

 

이후 flux 애플리케이션 생성은 다음과같이 진행한다.

# [터미널] 모니터링
watch -d kubectl get pod,svc nginx-example1

# flux 애플리케이션 생성 : nginx-example1
flux create **kustomization** **nginx-example1** --target-namespace=default --interval=1m --source=nginx-example1 --path="**./nginx**" --health-check-timeout=2m

# 확인
kubectl get pod,svc nginx-example1
kubectl get kustomizations -n flux-system
flux get kustomizations

 

 

 

 

 

 

설치 완료 및 확인

 

 

'job > eks' 카테고리의 다른 글

eks 6주차  (0) 2023.05.31
eks 5주차  (0) 2023.05.23
4주차 eks 스터디  (0) 2023.05.16
3주차 eks 스터디  (0) 2023.05.13
eks 2주차  (0) 2023.05.02

6주차 주제는 eks security이다.

 

사전준비는 아래와 같다.

# YAML 파일 다운로드
curl -O <https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/**eks-oneclick5.yaml**>

# CloudFormation 스택 배포
예시) aws cloudformation deploy --template-file **eks-oneclick5.yaml** --stack-name **myeks** --parameter-overrides KeyName=**kp-gasida** SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=**AKIA5...** MyIamUserSecretAccessKey=**'CVNa2...'** ClusterBaseName=**myeks** --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name **myeks** --query 'Stacks[*].**Outputs[0]**.OutputValue' --output text

# 작업용 EC2 SSH 접속
ssh -i **~/.ssh/kp-gasida.pem** ec2-user@$(aws cloudformation describe-stacks --stack-name **myeks** --query 'Stacks[*].Outputs[0].OutputValue' --output text)
  • 기본 설정
# default 네임스페이스 적용
kubectl ns default

# (옵션) context 이름 변경
NICK=<각자 자신의 닉네임>
NICK=gasida
kubectl ctx
kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io $NICK

# ExternalDNS
MyDomain=<자신의 도메인>
echo "export MyDomain=<자신의 도메인>" >> /etc/profile
*MyDomain=gasida.link*
*echo "export MyDomain=gasida.link" >> /etc/profile*
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 -

# 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

# 노드 IP 확인 및 PrivateIP 변수 지정
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3

# 노드 보안그룹 ID 확인
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values='*ng1*' --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.0/24

# 워커 노드 SSH 접속
for node in $N1 $N2 $N3; do ssh ec2-user@$node hostname; done
  • 프로메테우스 & 그라파나(admin / prom-operator) 설치 : 대시보드 추천 15757 17900 15172
# 사용 리전의 인증서 ARN 확인
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

# repo 추가
helm repo add prometheus-community <https://prometheus-community.github.io/helm-charts>

# 파라미터 파일 생성
cat < monitor-values.yaml
**prometheus**:
  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

defaultRules:
  create: false
kubeControllerManager:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false
alertmanager:
  enabled: false
EOT

# 배포
kubectl create ns monitoring
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 45.27.2 \\
--set prometheus.prometheusSpec.scrapeInterval='15s' --set prometheus.prometheusSpec.evaluationInterval='15s' \\
-f monitor-values.yaml --namespace monitoring

# Metrics-server 배포
kubectl apply -f <https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml>

 

EKS 인증 / 인가 설명에 앞서 AWS IAM과 K8S 인증 관련 지식이 있는 상태에서 실습이 시작된다.

 

EKS는 인증은 AWS IAM에서 진행하고 인가는 K8S RBAC을 사용한다.

RBAC 관련 롤을 확인하는것은 아래와 같다. 

# 설치
**kubectl krew install access-matrix rbac-tool rbac-view rolesum**

# Show an RBAC access matrix for server resources
**kubectl access-matrix** # Review access to cluster-scoped resources
kubectl access-matrix --namespace default # Review access to namespaced resources in 'default'

# RBAC Lookup by subject (user/group/serviceaccount) name
kubectl rbac-tool lookup
**kubectl rbac-tool lookup system:masters**
  SUBJECT        | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE
+----------------+--------------+-------------+-----------+---------------+
  system:masters | Group        | ClusterRole |           | cluster-admin

kubectl rbac-tool lookup system:nodes # eks:node-bootstrapper
kubectl rbac-tool lookup system:bootstrappers # eks:node-bootstrapper
**kubectl describe ClusterRole eks:node-bootstrapper**

# RBAC List Policy Rules For subject (user/group/serviceaccount) name
kubectl rbac-tool policy-rules
kubectl rbac-tool policy-rules -e '^system:.*'

# Generate ClusterRole with all available permissions from the target cluster
kubectl rbac-tool show

# Shows the subject for the current context with which one authenticates with the cluster
**kubectl rbac-tool whoami**
{Username: "kubernetes-admin",
 UID:      "aws-iam-authenticator:911283.....:AIDA5ILF2FJ......",
 Groups:   ["system:masters",
            "system:authenticated"],
 Extra:    {**accessKeyId**:  ["AKIA5ILF2FJ....."],
            arn:          ["arn:aws:iam::911283....:user/admin"],
            canonicalArn: ["arn:aws:iam::911283....:user/admin"],
            principalId:  ["AIDA5ILF2FJ....."],
            sessionName:  [""]}}

# Summarize RBAC roles for subjects : ServiceAccount(default), User, Group
kubectl rolesum -h
**kubectl rolesum aws-node -n kube-system**
kubectl rolesum -k User system:kube-proxy
**kubectl rolesum -k Group system:masters**

# [터미널1] A tool to visualize your RBAC permissions
**kubectl rbac-view**
INFO[0000] Getting K8s client
INFO[0000] serving RBAC View and <http://localhost:8800>

## 이후 해당 작업용PC 공인 IP:8800 웹 접속
echo -e "RBAC View Web <http://$>(curl -s ipinfo.io/ip):8800"

 

https://youtu.be/bksogA-WXv8

참고영상

 

kubectl로 AWS IAM 을 통해 위와같이 STS 토큰을 받아온다. 해당 토큰을 디코딩하면 GetCallerIdentity 나 버전 expiredate등이 포함돼있다.

 

 

해당 토큰을 EKS api에 가 받으면 Webhook token authenticator에 요청한다.

이후 AWS STS(AWS IAM)이 응답을 해주고(인증이 완료되면) User/Role에 대한 ARN을 반환한다.

 

이후 K8S에서 RBAC 인가를 처리한다. 

# Webhook api 리소스 확인 
**kubectl api-resources | grep Webhook**
mutatingwebhookconfigurations                  admissionregistration.k8s.io/v1        false        MutatingWebhookConfiguration
**validatingwebhookconfigurations**                admissionregistration.k8s.io/v1        false        ValidatingWebhookConfiguration

# validatingwebhookconfigurations 리소스 확인
**kubectl get validatingwebhookconfigurations**
NAME                                        WEBHOOKS   AGE
eks-aws-auth-configmap-validation-webhook   1          50m
vpc-resource-validating-webhook             2          50m
aws-load-balancer-webhook                   3          8m27s

**kubectl get validatingwebhookconfigurations eks-aws-auth-configmap-validation-webhook -o yaml | kubectl neat | yh**

# aws-auth 컨피그맵 확인
**kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh**
apiVersion: v1
kind: ConfigMap
metadata: 
  name: aws-auth
  namespace: kube-system
data: 
  **mapRoles**: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::91128.....:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1OS1WSTV0YB9X
      username: system:node:{{EC2PrivateDNSName}}
*#---<아래 생략(추정), ARN은 EKS를 설치한 IAM User , 여기 있었을경우 만약 실수로 삭제 시 복구가 가능했을까?---
  **mapUsers**: |
    - groups:
      - **system:masters**
      userarn: arn:aws:iam::111122223333:user/**admin**
      username: **kubernetes-admin***

# EKS 설치한 IAM User 정보 >> system:authenticated는 어떤 방식으로 추가가 되었는지 궁금???
**kubectl rbac-tool whoami**
{Username: "kubernetes-admin",
 UID:      "aws-iam-authenticator:9112834...:AIDA5ILF2FJIR2.....",
 Groups:   ["system:masters",
            "system:authenticated"],
...

# system:masters , system:authenticated 그룹의 정보 확인
kubectl rbac-tool lookup system:masters
kubectl rbac-tool lookup system:authenticated
kubectl rolesum -k Group system:masters
kubectl rolesum -k Group system:authenticated

# system:masters 그룹이 사용 가능한 클러스터 롤 확인 : cluster-admin
**kubectl describe clusterrolebindings.rbac.authorization.k8s.io cluster-admin**
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
Role:
  Kind:  ClusterRole
  Name:  **cluster-admin**
Subjects:
  Kind   Name            Namespace
  ----   ----            ---------
  Group  **system:masters**

# cluster-admin 의 PolicyRule 확인 : 모든 리소스  사용 가능!
**kubectl describe clusterrole cluster-admin**
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  **Resources**  **Non-Resource URLs**  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  ***.***        []                 []              [*****]
             [*****]                []              [*]

# system:authenticated 그룹이 사용 가능한 클러스터 롤 확인
kubectl describe ClusterRole **system:discovery**
kubectl describe ClusterRole **system:public-info-viewer**
kubectl describe ClusterRole **system:basic-user**
kubectl describe ClusterRole **eks:podsecuritypolicy:privileged**

 

위와같이 인증/인가 흐름을 알아보았다.

 

이번엔 실제 권한을 할당하는 실습을 진행해보겠다.

# testuser 사용자 생성
aws iam create-user --user-name **testuser**

# 사용자에게 프로그래밍 방식 액세스 권한 부여
aws iam create-access-key --user-name **testuser**
{
    "AccessKey": {
        "UserName": "testuser",
        "**AccessKeyId**": "AKIA5ILF2##",
        "Status": "Active",
        "**SecretAccessKey**": "TxhhwsU8##",
        "CreateDate": "2023-05-23T07:40:09+00:00"
    }
}
# testuser 사용자에 정책을 추가
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/**AdministratorAccess** --user-name **testuser**

# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::911283464785:user/admin"

# EC2 IP 확인 : myeks-bastion-EC2-2 PublicIPAdd 확인
**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**

위와같이 사용자 생성하고 키&정책 부여해준다.

# get-caller-identity 확인 >> 왜 안될까요?
**aws sts get-caller-identity --query Arn**

# testuser 자격증명 설정
**aws configure**
AWS Access Key ID [None]: *AKIA5ILF2F...*
AWS Secret Access Key [None]: *ePpXdhA3cP....*
Default region name [None]: ***ap-northeast-2***

# get-caller-identity 확인
**aws sts get-caller-identity --query Arn**
"arn:aws:iam::911283464785:user/**testuser**"

# kubectl 시도 >> testuser도 **AdministratorAccess** 권한을 가지고 있는데, 실패 이유는?
**kubectl get node -v6**
ls ~/.kube

그다음 위와같이 aws계정 연동을 진행해주고

# 방안1 : eksctl 사용 >> iamidentitymapping 실행 시 aws-auth 컨피그맵 작성해줌
# Creates a mapping from IAM role or user to Kubernetes user and groups
eksctl create iamidentitymapping --cluster $**CLUSTER_NAME** --username **testuser** --group **system:masters** --arn arn:aws:iam::$ACCOUNT_ID:user/**testuser**

# 확인
**kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh**
...

~~# 방안2 : 아래 edit로 mapUsers 내용 직접 추가!
**kubectl edit cm -n kube-system aws-auth**
---
apiVersion: v1
data: 
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::911283464785:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ
      username: system:node:{{EC2PrivateDNSName}}
  **mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::911283464785:user/testuser
      username: testuser**
...~~

# 확인 : 기존에 있는 **role**/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-YYYYY 는 어떤 역할/동작을 하는 걸까요?
**eksctl get iamidentitymapping --cluster $CLUSTER_NAME**
ARN											USERNAME				GROUPS					ACCOUNT
arn:aws:iam::911283464785:**role**/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ	system:node:{{EC2PrivateDNSName}}	system:bootstrappers,system:nodes	
arn:aws:iam::911283464785:**user**/testuser							testuser				system:masters

위와같이 새로 생성한 testuesr 계정에다가 EKS 관리자 권한을ㄹ 부여한다.

 

권한이 부여됨을 확인할 수 있다.

 

edit를 통해 mapUsers를 직접 수정해줄수도 있다. 

# 방안2 : 아래 edit로 mapUsers 내용 직접 수정 **system:authenticated**
~~~~**kubectl edit cm -n kube-system aws-auth**
...

# 확인
eksctl get iamidentitymapping --cluster $CLUSTER_NAME

 

Testuser를 삭제하는 방법은 다음과 같다.

# testuser IAM 맵핑 삭제
eksctl **delete** iamidentitymapping --cluster $CLUSTER_NAME --arn  arn:aws:iam::$ACCOUNT_ID:user/testuser

# Get IAM identity mapping(s)
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
kubectl get cm -n kube-system aws-auth -o yaml | yh

지금까지는 사용자/애플리케이션 > k8s 이용시에 대한 인증/인가에 대해 알아보았다.

 

지금부터는 k8s파드 > aws 서비스 이용시에 대해 알아보도록 한다. IRSA이다.

요점은 OIDC를 통해 인증/인가를 진행한다.

 

 

실습을 진행해보자.

# 파드1 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test1
spec:
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      args: ['s3', 'ls']
  restartPolicy: Never
  **automountServiceAccountToken: false**
EOF

# 확인
kubectl get pod
kubectl describe pod

# 로그 확인
kubectl logs eks-iam-test1

# 파드1 삭제
kubectl delete pod eks-iam-test1

automountServiceAccountToken: false

위와같이 자동 발급을 false 해놓으면 

 

 

위와같은 결과가 나온다.

 

두번째 실습이다.

 

# 파드2 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test2
spec:
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
EOF

# 확인
kubectl get pod
kubectl describe pod

# aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls

# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN

# jwt 혹은 아래 JWT 웹 사이트 이용
jwt decode $SA_TOKEN --json --iso8601
...

#헤더
{
  "alg": "RS256",
  "kid": "1a8fcaee12b3a8f191327b5e9b997487ae93baab"
}

# 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘
## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증
{
  "aud": [
    "https://kubernetes.default.svc"  # 해당 주소는 k8s api의 ClusterIP 서비스 주소 도메인명, kubectl get svc kubernetes
  ],
  "exp": 1716619848,
  "iat": 1685083848,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/F6A7523462E8E6CDADEE5D41DF2E71F6",
  "kubernetes.io": {
    "namespace": "default",
    "pod": {
      "name": "eks-iam-test2",
      "uid": "10dcccc8-a16c-4fc7-9663-13c9448e107a"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "acb6c60d-0c5f-4583-b83b-1b629b0bdd87"
    },
    "warnafter": 1685087455
  },
  "nbf": 1685083848,
  "sub": "system:serviceaccount:default:default"
}

# 파드2 삭제
kubectl delete pod eks-iam-test2

 

 

위에서 생성한 파드도 마찬가지로 권한이 없는 상태이다.

 

위의 토큰을 디코딩해보면 서비스 어카운트, 파드네임등의 정보가 담겨있다. iss(oidc 제공자 정보)도 확인된다.

 

마지막 실습이다.

# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
eksctl create **iamserviceaccount** \\
  --name **my-sa** \\
  --namespace **default** \\
  --cluster $CLUSTER_NAME \\
  --approve \\
  --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3ReadOnlyAccess`].Arn' --output text)

# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
# aws-load-balancer-controller IRSA는 어떤 동작을 수행할 것 인지 생각해보자!
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
**kubectl get sa**
**kubectl describe sa my-sa**
Name:                my-sa
Namespace:           default
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         **eks.amazonaws.com/role-arn: arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1MJUYW59O6QGH**
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

irsa를 통해 아마존S3 리드온리 정책을 만들어준다.

그리고 해당 어카운트를 사용하는 파드를 생성한다.

# 파드3번 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test3
spec:
  **serviceAccountName: my-sa**
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
EOF

# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml | kubectl neat | yh

**# 파드 생성 yaml에 없던 내용이 추가됨!!!!!**
# **Pod Identity Webhook**은 **mutating** webhook을 통해 아래 **Env 내용**과 **1개의 볼륨**을 추가함
kubectl get pod eks-iam-test3
**kubectl describe pod eks-iam-test3**
...
**Environment**:
      AWS_STS_REGIONAL_ENDPOINTS:   regional
      AWS_DEFAULT_REGION:           ap-northeast-2
      AWS_REGION:                   ap-northeast-2
      AWS_ROLE_ARN:                 arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN
      AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    Mounts:
      /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-69rh8 (ro)
...
**Volumes:**
  **aws-iam-token**:
    Type:                    **Projected** (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  86400
  kube-api-access-sn467:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
...

# 파드에서 aws cli 사용 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
**kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn**
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN/botocore-session-1685179271"

# 되는 것고 안되는 것은 왜그런가?
kubectl exec -it eks-iam-test3 -- **aws s3 ls**
kubectl exec -it eks-iam-test3 -- **aws ec2 describe-instances --region ap-northeast-2**
kubectl exec -it eks-iam-test3 -- **aws ec2 describe-vpcs --region ap-northeast-2**

위에서 보면 Env가 추가된것이 확인이 된다.

 

최종적으로 아까 계속 S3 조회 실패하던것이 이번에는 성공한것을 확인할수있다.

 

우리가 준 권한은 S3리드온리이기 때문에 ec2 리드등 다른 정보는 볼수없다.

 

 

 

'job > eks' 카테고리의 다른 글

7주차 eks 스터디  (0) 2023.06.08
eks 5주차  (0) 2023.05.23
4주차 eks 스터디  (0) 2023.05.16
3주차 eks 스터디  (0) 2023.05.13
eks 2주차  (0) 2023.05.02
diff -y eks-oneclick3.yaml eks-oneclick4.yaml

4주차 원클릭과 5주차 원클릭의 차이는

ebs-csi-driver 설치 안함

t3 설치

xRay true로 변경

 

언제나처럼 실습환경 설치

  • Amazon EKS (myeks) 윈클릭 배포 & 기본 설정
    • 기본 설정
    # default 네임스페이스 적용
    kubectl ns default
    
    # (옵션) context 이름 변경
    NICK=<각자 자신의 닉네임>
    NICK=gasida
    kubectl ctx
    kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io $NICK@myeks
    
    # ExternalDNS
    MyDomain=<자신의 도메인>
    echo "export MyDomain=<자신의 도메인>" >> /etc/profile
    *MyDomain=gasida.link*
    *echo "export MyDomain=gasida.link" >> /etc/profile*
    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
    
    # 노드 보안그룹 ID 확인
    NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values='*ng1*' --query "SecurityGroups[*].[GroupId]" --output text)
    aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32
    
    • 프로메테우스 & 그라파나(admin / prom-operator) 설치 : 대시보드 추천 15757 17900 15172
    # 사용 리전의 인증서 ARN 확인
    CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
    echo $CERT_ARN
    
    # repo 추가
    helm repo add prometheus-community <https://prometheus-community.github.io/helm-charts>
    
    # 파라미터 파일 생성
    cat < monitor-values.yaml
    **prometheus**:
      prometheusSpec:
        podMonitorSelectorNilUsesHelmValues: false
        serviceMonitorSelectorNilUsesHelmValues: false
        retention: 5d
        retentionSize: "10GiB"
    
      verticalPodAutoscaler:
        enabled: true
    
      ingress:
        enabled: true
        ingressClassName: alb
        hosts: 
          - prometheus.$MyDomain
        paths: 
          - /*
        annotations:
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/target-type: ip
          alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
          alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
          alb.ingress.kubernetes.io/success-codes: 200-399
          alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
          alb.ingress.kubernetes.io/group.name: study
          alb.ingress.kubernetes.io/ssl-redirect: '443'
    
    grafana:
      defaultDashboardsTimezone: Asia/Seoul
      adminPassword: prom-operator
    
      ingress:
        enabled: true
        ingressClassName: alb
        hosts: 
          - grafana.$MyDomain
        paths: 
          - /*
        annotations:
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/target-type: ip
          alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
          alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
          alb.ingress.kubernetes.io/success-codes: 200-399
          alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
          alb.ingress.kubernetes.io/group.name: study
          alb.ingress.kubernetes.io/ssl-redirect: '443'
    
    defaultRules:
      create: false
    kubeControllerManager:
      enabled: false
    kubeEtcd:
      enabled: false
    kubeScheduler:
      enabled: false
    alertmanager:
      enabled: false
    EOT
    
    # 배포
    kubectl create ns monitoring
    helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 45.27.2 \\
    --set prometheus.prometheusSpec.scrapeInterval='15s' --set prometheus.prometheusSpec.evaluationInterval='15s' \\
    -f monitor-values.yaml --namespace monitoring
    
    # Metrics-server 배포
    **kubectl apply -f <https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml**>
    
  • EKS Node Viewer 설치 : 노드 할당 가능 용량요청 request 리소스 표시, 실제 파드 리소스 사용량 X - 링크
    • It displays the scheduled pod resource requests vs the allocatable capacity on the node. It does not look at the actual pod resource usage.
    # go 설치
    yum install -y go
    
    # EKS Node Viewer 설치 : 현재 ec2 spec에서는 **설치에 다소 시간이 소요됨** = **2분 이상**
    go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest
    
    # bin 확인 및 사용 
    tree ~/go/bin
    **cd ~/go/bin**
    **./eks-node-viewer**
    3 nodes (875m/5790m) 15.1% cpu ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ $0.156/hour | $113.880/month
    20 pods (0 pending 20 running 20 bound)
    
    ip-192-168-3-196.ap-northeast-2.compute.internal cpu ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░  22% (7 pods) t3.medium/$0.0520 On-Demand
    ip-192-168-1-91.ap-northeast-2.compute.internal  cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  12% (6 pods) t3.medium/$0.0520 On-Demand
    ip-192-168-2-185.ap-northeast-2.compute.internal cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  12% (7 pods) t3.medium/$0.0520 On-Demand
    Press any key to quit
    
    **명령 샘플**
    # Standard usage
    **./eks-node-viewer**
    
    # Display both CPU and Memory Usage
    **./eks-node-viewer --resources cpu,memory**
    
    # Karenter nodes only
    **./eks-node-viewer --node-selector "karpenter.sh/provisioner-name"**
    
    # Display extra labels, i.e. AZ
    **./eks-node-viewer --extra-labels topology.kubernetes.io/zone**
    
    # Specify a particular AWS profile and region
    AWS_PROFILE=myprofile AWS_REGION=us-west-2
    
    **기본 옵션**
    # select only Karpenter managed nodes
    node-selector=karpenter.sh/provisioner-name
    
    # display both CPU and memory
    resources=cpu,memory
    

 

HPA, VPA, CA에 대한 설명
HPA - 스케일인/아웃, KEDA
VPA - 스케일업/다운(사용 잘 안함)
CA - 노드 확장/축소

 

HPA 실습 시작

# Run and expose php-apache server
curl -s -O <https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/php-apache.yaml>
cat php-apache.yaml | yh
**kubectl apply -f php-apache.yaml**

# 확인
kubectl exec -it deploy/php-apache -- **cat /var/www/html/index.php**
...

# 모니터링 : 터미널2개 사용
watch -d 'kubectl get hpa,pod;echo;kubectl top pod;echo;kubectl top node'
kubectl exec -it deploy/php-apache -- top

# 접속
PODIP=$(kubectl get pod -l run=php-apache -o jsonpath={.items[0].status.podIP})
curl -s $PODIP; echo

현재 노드 사용량은 위와같다.

 

# Create the HorizontalPodAutoscaler : requests.cpu=200m - [알고리즘](<https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details>)
# Since each pod requests **200 milli-cores** by kubectl run, this means an average CPU usage of **100 milli-cores**.
kubectl autoscale deployment php-apache **--cpu-percent=50** --min=1 --max=10
**kubectl describe hpa**
...
Metrics:                                               ( current / target )
  resource cpu on pods  (as a percentage of request):  0% (1m) / 50%
Min replicas:                                          1
Max replicas:                                          10
Deployment pods:                                       1 current / 1 desired
...

# HPA 설정 확인
kubectl krew install neat
kubectl get hpa php-apache -o yaml
**kubectl get hpa php-apache -o yaml | kubectl neat | yh**
spec: 
  minReplicas: 1               # [4] 또는 최소 1개까지 줄어들 수도 있습니다
  maxReplicas: 10              # [3] 포드를 최대 5개까지 늘립니다
  **scaleTargetRef**: 
    apiVersion: apps/v1
    kind: **Deployment**
    name: **php-apache**           # [1] php-apache 의 자원 사용량에서
  **metrics**: 
  - type: **Resource**
    resource: 
      name: **cpu**
      target: 
        type: **Utilization**
        **averageUtilization**: 50  # [2] CPU 활용률이 50% 이상인 경우

# 반복 접속 1 (파드1 IP로 접속) >> 증가 확인 후 중지
while true;do curl -s $PODIP; sleep 0.5; done

# 반복 접속 2 (서비스명 도메인으로 접속) >> 증가 확인(몇개까지 증가되는가? 그 이유는?) 후 중지 >> **중지 5분 후** 파드 갯수 감소 확인
# Run this in a separate terminal
# so that the load generation continues and you can carry on with the rest of the steps
kubectl run -i --tty load-generator --rm --image=**busybox**:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- ; done"

사용량 올라가는중

위와같이 파드가 오토스케일링된것을 확인할수있다.

 

이번엔 KEDA

HPA는 CPU, Memory 메트릭을 기반으로 스케일 인/아웃을 하는데 KEDA는 특정 이벤트를 기반으로 스케일여부를 결정한다.

지원 메트릭이 무지하게 많다.

 

  • KEDA with Helm : 특정 **이벤트(cron 등)**기반의 파드 오토 스케일링 - Chart Grafana Cron
    # KEDA 설치
    cat < keda-values.yaml
    metricsServer:
      useHostNetwork: true
    
    **prometheus**:
      metricServer:
        enabled: true
        port: 9022
        portName: metrics
        path: /metrics
        serviceMonitor:
          # Enables ServiceMonitor creation for the Prometheus Operator
          enabled: true
        podMonitor:
          # Enables PodMonitor creation for the Prometheus Operator
          enabled: true
      operator:
        enabled: true
        port: 8080
        serviceMonitor:
          # Enables ServiceMonitor creation for the Prometheus Operator
          enabled: true
        podMonitor:
          # Enables PodMonitor creation for the Prometheus Operator
          enabled: true
    
      webhooks:
        enabled: true
        port: 8080
        serviceMonitor:
          # Enables ServiceMonitor creation for the Prometheus webhooks
          enabled: true
    EOT
    
    kubectl create namespace **keda**
    helm repo add kedacore <https://kedacore.github.io/charts>
    helm install **keda** kedacore/keda --version 2.10.2 --namespace **keda -f** keda-values.yaml
    
    # KEDA 설치 확인
    kubectl **get-all** -n keda
    kubectl get **all** -n keda
    **kubectl get crd | grep keda**
    
    # keda 네임스페이스에 디플로이먼트 생성
    **kubectl apply -f php-apache.yaml -n keda
    kubectl get pod -n keda**
    
    # ScaledObject ****정책 생성 : cron
    cat < keda-cron.yaml
    apiVersion: keda.sh/v1alpha1
    kind: **ScaledObject**
    metadata:
      name: php-apache-cron-scaled
    spec:
      minReplicaCount: 0
      maxReplicaCount: 2
      pollingInterval: 30
      cooldownPeriod: 300
      **scaleTargetRef**:
        apiVersion: apps/v1
        kind: Deployment
        name: php-apache
      **triggers**:
      - type: **cron**
        metadata:
          timezone: Asia/Seoul
          **start**: 00,15,30,45 * * * *
          **end**: 05,20,35,50 * * * *
          **desiredReplicas**: "1"
    EOT
    **kubectl apply -f keda-cron.yaml -n keda**
    
    # 그라파나 대시보드 추가
    # 모니터링
    watch -d 'kubectl get ScaledObject,hpa,pod -n keda'
    kubectl get ScaledObject -w
    
    # 확인
    kubectl get ScaledObject,hpa,pod -n keda
    **kubectl get hpa -o jsonpath={.items[0].spec} -n keda | jq**
    ...
    "metrics": [
        {
          "**external**": {
            "metric": {
              "name": "s0-cron-Asia-Seoul-**00,15,30,45**xxxx-**05,20,35,50**xxxx",
              "selector": {
                "matchLabels": {
                  "scaledobject.keda.sh/name": "php-apache-cron-scaled"
                }
              }
            },
            "**target**": {
              "**averageValue**": "1",
              "type": "AverageValue"
            }
          },
          "type": "**External**"
        }
    
    # KEDA 및 deployment 등 삭제
    kubectl delete -f keda-cron.yaml -n keda && kubectl delete deploy php-apache -n keda && helm uninstall keda -n keda
    kubectl delete namespace keda
    

설치 완료, 30분에 생성되고 35분에 삭제될것이다.

29분의 상태

 

3

30분이되니 php-apache 파드가 생성된것을 확인할수있다.

 

CA 실습 시작

 

HPA는 파드확장, CA는 노드 확장개념이다. 

 

# 현재 autoscaling(ASG) 정보 확인
# aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='**클러스터이름**']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table
**aws autoscaling describe-auto-scaling-groups \\
    --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" \\
    --output table**
-----------------------------------------------------------------
|                   DescribeAutoScalingGroups                   |
+------------------------------------------------+----+----+----+
|  eks-ng1-44c41109-daa3-134c-df0e-0f28c823cb47  |  3 |  3 |  3 |
+------------------------------------------------+----+----+----+

# MaxSize 6개로 수정
**export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].AutoScalingGroupName" --output text)
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${ASG_NAME} --min-size 3 --desired-capacity 3 --max-size 6**

# 확인
**aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table**
-----------------------------------------------------------------
|                   DescribeAutoScalingGroups                   |
+------------------------------------------------+----+----+----+
|  eks-ng1-c2c41e26-6213-a429-9a58-02374389d5c3  |  3 |  6 |  3 |
+------------------------------------------------+----+----+----+

# 배포 : Deploy the Cluster Autoscaler (CA)
curl -s -O <https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml>
sed -i "s//$CLUSTER_NAME/g" cluster-autoscaler-autodiscover.yaml
kubectl apply -f cluster-autoscaler-autodiscover.yaml

# 확인
kubectl get pod -n kube-system | grep cluster-autoscaler
kubectl describe deployments.apps -n kube-system cluster-autoscaler

# (옵션) cluster-autoscaler 파드가 동작하는 워커 노드가 퇴출(evict) 되지 않게 설정
kubectl -n kube-system annotate deployment.apps/cluster-autoscaler cluster-autoscaler.kubernetes.io/safe-to-evict="false"

 

현재 ec2 3대임을 확인할수있다.

# 모니터링 
kubectl get nodes -w
while true; do kubectl get node; echo "------------------------------" ; date ; sleep 1; done
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------"; date; sleep 1; done

# Deploy a Sample App
# We will deploy an sample nginx application as a ReplicaSet of 1 Pod
cat <<EoF> nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-to-scaleout
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        service: nginx
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx-to-scaleout
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 512Mi
EoF

kubectl apply -f nginx.yaml
kubectl get deployment/nginx-to-scaleout

# Scale our ReplicaSet
# Let’s scale out the replicaset to 15
kubectl scale --replicas=15 deployment/nginx-to-scaleout && date

# 확인
kubectl get pods -l app=nginx -o wide --watch
kubectl -n kube-system logs -f deployment/cluster-autoscaler

# 노드 자동 증가 확인
kubectl get nodes
aws autoscaling describe-auto-scaling-groups \\
    --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='**myeks**']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" \\
    --output table

**./eks-node-viewer**
42 pods (0 pending 42 running 42 bound)
ip-192-168-3-196.ap-northeast-2.compute.internal cpu ███████████████████████████████████ 100% (10 pods) t3.medium/$0.0520 On-Demand
ip-192-168-1-91.ap-northeast-2.compute.internal  cpu ███████████████████████████████░░░░  89% (9 pods)  t3.medium/$0.0520 On-Demand
ip-192-168-2-185.ap-northeast-2.compute.internal cpu █████████████████████████████████░░  95% (11 pods) t3.medium/$0.0520 On-Demand
ip-192-168-2-87.ap-northeast-2.compute.internal  cpu █████████████████████████████░░░░░░  84% (6 pods)  t3.medium/$0.0520 On-Demand
ip-192-168-3-15.ap-northeast-2.compute.internal  cpu █████████████████████████████░░░░░░  84% (6 pods)  t3.medium/$0.0520 On-Demand

# 디플로이먼트 삭제
kubectl delete -f nginx.yaml && date

# **노드 갯수 축소** : 기본은 10분 후 scale down 됨, 물론 아래 flag 로 시간 수정 가능 >> 그러니 **디플로이먼트 삭제 후 10분 기다리고 나서 보자!**
# By default, cluster autoscaler will wait 10 minutes between scale down operations, 
# you can adjust this using the --scale-down-delay-after-add, --scale-down-delay-after-delete, 
# and --scale-down-delay-after-failure flag. 
# E.g. --scale-down-delay-after-add=5m to decrease the scale down delay to 5 minutes after a node has been added.

# 터미널1
watch -d kubectl get node

위와같이 배포를 진행하면

인스턴스가 생성된다. 즉 노드가 확장되는것이다.

 

이번엔 CPA

노드 수 증가에 비례하여 성능 처리가 필요한 애플리케이션(컨테이너/파드)를 수평으로 자동 확장하는 개념

 

#
helm repo add cluster-proportional-autoscaler <https://kubernetes-sigs.github.io/cluster-proportional-autoscaler>

# CPA규칙을 설정하고 helm차트를 릴리즈 필요
helm upgrade --install cluster-proportional-autoscaler cluster-proportional-autoscaler/cluster-proportional-autoscaler

# nginx 디플로이먼트 배포
cat < cpa-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        resources:
          limits:
            cpu: "100m"
            memory: "64Mi"
          requests:
            cpu: "100m"
            memory: "64Mi"
        ports:
        - containerPort: 80
EOT
kubectl apply -f cpa-nginx.yaml

# CPA 규칙 설정
cat < cpa-values.yaml
config:
  ladder:
    nodesToReplicas:
      - [1, 1]
      - [2, 2]
      - [3, 3]
      - [4, 3]
      - [5, 5]
options:
  namespace: default
  target: "deployment/nginx-deployment"
EOF

# 모니터링
**watch -d kubectl get pod**

# helm 업그레이드
helm upgrade --install cluster-proportional-autoscaler -f cpa-values.yaml cluster-proportional-autoscaler/cluster-proportional-autoscaler

# 노드 5개로 증가
export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].AutoScalingGroupName" --output text)
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${ASG_NAME} --min-size 5 --desired-capacity 5 --max-size 5
aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table

# 노드 4개로 축소
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${ASG_NAME} --min-size 4 --desired-capacity 4 --max-size 4
aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table

 

마지막으로 karpenter 이다.

 

karpenter는 CA보다 더 똑똑하게 작동한다.

 

  • Kubernetes 스케줄러가 예약할 수 없다고 표시한 파드들을 감시합니다.
  • 파드가 요청한 자원 요구사항, 노드 선택자, 어피니티, 허용 조건, 그리고 토폴로지 분산 제약조건을 평가합니다.
  • 파드 요구사항을 충족하는 노드프로비저닝합니다.
  • 파드를 새로운 노드에 예약합니다.
  • 더 이상 필요하지 않은 경우 노드제거합니다.

karpenter 실습은 새로 배포하여 실습한다.

# YAML 파일 다운로드
curl -O <https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/**karpenter-preconfig.yaml**>

# CloudFormation 스택 배포
예시) aws cloudformation deploy --template-file **karpenter-preconfig.yaml** --stack-name **myeks2** --parameter-overrides KeyName=**kp-gasida** SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=**AKIA5...** MyIamUserSecretAccessKey=**'CVNa2...'** ClusterBaseName=**myeks2** --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name **myeks2** --query 'Stacks[*].**Outputs[0]**.OutputValue' --output text

# 작업용 EC2 SSH 접속
ssh -i **~/.ssh/kp-gasida.pem** ec2-user@$(aws cloudformation describe-stacks --stack-name **myeks2** --query 'Stacks[*].Outputs[0].OutputValue' --output text)
# IP 주소 확인 : 172.30.0.0/16 VPC 대역에서 172.30.1.0/24 대역을 사용 중
ip -br -c addr

# EKS Node Viewer 설치 : 현재 ec2 spec에서는 **설치에 다소 시간이 소요됨** = **2분 이상**
go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest

# [터미널1] bin 확인 및 사용
tree ~/go/bin
cd ~/go/bin
**./eks-node-viewer -h**
./eks-node-viewer  # EKS 배포 완료 후 실행 하자
```bash
# 환경변수 정보 확인
export | egrep 'ACCOUNT|AWS_|CLUSTER' | egrep -v 'SECRET|KEY'

# 환경변수 설정
export KARPENTER_VERSION=v0.27.5
export TEMPOUT=$(mktemp)
**echo $KARPENTER_VERSION $CLUSTER_NAME $AWS_DEFAULT_REGION $AWS_ACCOUNT_ID $TEMPOUT**

# CloudFormation 스택으로 IAM Policy, Role, EC2 Instance Profile 생성 : **3분 정도 소요**
curl -fsSL https://karpenter.sh/"${KARPENTER_VERSION}"/getting-started/getting-started-with-karpenter/cloudformation.yaml  > $TEMPOUT \
&& aws cloudformation deploy \
  --stack-name "Karpenter-${CLUSTER_NAME}" \
  --template-file "${TEMPOUT}" \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides "ClusterName=${CLUSTER_NAME}"

# 클러스터 생성 : myeks2 EKS 클러스터 생성 **19분 정도** 소요
**eksctl create cluster** -f - <<EOF
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: ${CLUSTER_NAME}
  region: ${AWS_DEFAULT_REGION}
  version: "1.24"
  tags:
    karpenter.sh/discovery: ${CLUSTER_NAME}

iam:
  withOIDC: true
  serviceAccounts:
  - metadata:
      name: karpenter
      namespace: karpenter
    roleName: ${CLUSTER_NAME}-karpenter
    attachPolicyARNs:
    - arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}
    roleOnly: true

iamIdentityMappings:
- arn: "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}"
  username: system:node:{{EC2PrivateDNSName}}
  groups:
  - system:bootstrappers
  - system:nodes

managedNodeGroups:
- instanceType: m5.large
  amiFamily: AmazonLinux2
  name: ${CLUSTER_NAME}-ng
  desiredCapacity: 2
  minSize: 1
  maxSize: 10
  iam:
    withAddonPolicies:
      externalDNS: true

*## Optionally run on fargate
# fargateProfiles:
# - name: karpenter
#  selectors:
#  - namespace: karpenter*
EOF

**# eks 배포 확인**
eksctl get cluster
eksctl get nodegroup --cluster $CLUSTER_NAME
eksctl get **iamidentitymapping** --cluster $CLUSTER_NAME
eksctl get **iamserviceaccount** --cluster $CLUSTER_NAME
eksctl get addon --cluster $CLUSTER_NAME

# [터미널1] eks-node-viewer
cd ~/go/bin && ./eks-node-viewer

**# k8s 확인**
kubectl cluster-info
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get pod -n kube-system -owide
**kubectl describe cm -n kube-system aws-auth**
...
mapRoles:
----
- groups:
  - system:bootstrappers
  - system:**nodes**
  **rolearn**: arn:aws:iam::911283464785:role/**KarpenterNodeRole-myeks2**
  username: system:node:{{EC2PrivateDNSName}}
- groups:
  - system:bootstrappers
  - system:nodes
  rolearn: arn:aws:iam::911283464785:role/eksctl-myeks2-nodegroup-myeks2-ng-NodeInstanceRole-1KDXF4FLKKX1B
  username: system:node:{{EC2PrivateDNSName}}
...

# 카펜터 설치를 위한 환경 변수 설정 및 확인
export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output text)"
export KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"
echo $CLUSTER_ENDPOINT $KARPENTER_IAM_ROLE_ARN

# EC2 Spot Fleet 사용을 위한 service-linked-role 생성 확인 : 만들어있는것을 확인하는 거라 아래 에러 출력이 정상!
# If the role has already been successfully created, you will see:
# An error occurred (InvalidInput) when calling the CreateServiceLinkedRole operation: Service role name AWSServiceRoleForEC2Spot has been taken in this account, please try a different suffix.
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true

# docker logout : Logout of docker to perform an unauthenticated pull against the public ECR
docker logout public.ecr.aws

# karpenter 설치
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter --create-namespace \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
  --set settings.aws.clusterName=${CLUSTER_NAME} \
  --set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
  --set settings.aws.interruptionQueueName=${CLUSTER_NAME} \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --set controller.resources.limits.cpu=1 \
  --set controller.resources.limits.memory=1Gi \
  --wait

# 확인
kubectl get-all -n karpenter
kubectl get all -n karpenter
kubectl get cm -n karpenter karpenter-global-settings -o jsonpath={.data} | jq
kubectl get crd | grep karpenter
```

위와같이 설치를 진행해준다.

 

그다음 진행

#
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: **Provisioner**
metadata:
  name: default
**spec**:
  requirements:
    - key: karpenter.sh/capacity-type
      operator: In
      values: ["**spot**"]
  limits:
    resources:
      cpu: 1000
  providerRef:
    name: default
  **ttlSecondsAfterEmpty**: 30
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: **AWSNodeTemplate**
metadata:
  name: default
spec:
  subnetSelector:
    karpenter.sh/discovery: ${CLUSTER_NAME}
  securityGroupSelector:
    karpenter.sh/discovery: ${CLUSTER_NAME}
EOF

# 확인
kubectl get awsnodetemplates,provisioners

위와같이   Provisioner  설치를 진행한다.

# pause 파드 1개에 CPU 1개 최소 보장 할당
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 0
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            **requests:
              cpu: 1**
EOF
kubectl scale deployment inflate --replicas 5
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller

# 스팟 인스턴스 확인!
aws ec2 describe-spot-instance-requests --filters "Name=state,Values=active" --output table
kubectl get node -l karpenter.sh/capacity-type=spot -o jsonpath='{.items[0].metadata.labels}' | jq
**kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type,node.kubernetes.io/instance-type**
NAME                                                 STATUS   ROLES    AGE     VERSION                CAPACITYTYPE   CAPACITY-TYPE   INSTANCE-TYPE
ip-192-168-165-220.ap-northeast-2.compute.internal   Ready    <none>   2m37s   v1.24.13-eks-0a21954                  spot            c5a.2xlarge
ip-192-168-57-91.ap-northeast-2.compute.internal     Ready    <none>   13m     v1.24.13-eks-0a21954   ON_DEMAND                      m5.large
ip-192-168-75-253.ap-northeast-2.compute.internal    Ready    <none>   13m     v1.24.13-eks-0a21954   ON_DEMAND                      m5.large

 

실습을 진행하는 과정에서

 

위와같은 에러가 발생했다.

https://github.com/aws/karpenter/issues/2902

와 동일한 에러인지... 추후 확인이 필요하다.

 

 

#
kubectl delete provisioners default
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: **Provisioner**
metadata:
  name: default
spec:
  **consolidation:
    enabled: true**
  labels:
    type: karpenter
  limits:
    resources:
      cpu: 1000
      memory: 1000Gi
  providerRef:
    name: default
  requirements:
    - key: karpenter.sh/capacity-type
      operator: In
      values:
        - on-demand
    - key: node.kubernetes.io/instance-type
      operator: In
      values:
        - c5.large
        - m5.large
        - m5.xlarge
EOF

#
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 0
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: 1
EOF
kubectl scale deployment inflate --replicas 12
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller

# 인스턴스 확인
# This changes the total memory request for this deployment to around 12Gi, 
# which when adjusted to account for the roughly 600Mi reserved for the kubelet on each node means that this will fit on 2 instances of type m5.large:
kubectl get node -l type=karpenter
**kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type**
**kubectl get node --label-columns=node.kubernetes.io/instance-type,topology.kubernetes.io/zone**

# Next, scale the number of replicas back down to 5:
kubectl scale deployment inflate --replicas 5

# The output will show Karpenter identifying specific nodes to cordon, drain and then terminate:
**kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller**
2023-05-17T07:02:00.768Z	INFO	controller.deprovisioning	deprovisioning via **consolidation delete**, terminating 1 machines ip-192-168-14-81.ap-northeast-2.compute.internal/m5.xlarge/on-demand	{"commit": "d7e22b1-dirty"}
2023-05-17T07:02:00.803Z	INFO	controller.termination	**cordoned** node	{"commit": "d7e22b1-dirty", "node": "ip-192-168-14-81.ap-northeast-2.compute.internal"}
2023-05-17T07:02:01.320Z	INFO	controller.termination	**deleted** node	{"commit": "d7e22b1-dirty", "node": "ip-192-168-14-81.ap-northeast-2.compute.internal"}
2023-05-17T07:02:39.283Z	DEBUG	controller	**deleted** launch template	{"commit": "d7e22b1-dirty", "launch-template": "karpenter.k8s.aws/9547068762493117560"}

# Next, scale the number of replicas back down to 1
kubectl scale deployment inflate --replicas 1
**kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller**
2023-05-17T07:05:08.877Z	INFO	controller.deprovisioning	deprovisioning via consolidation delete, terminating 1 machines ip-192-168-145-253.ap-northeast-2.compute.internal/m5.xlarge/on-demand	{"commit": "d7e22b1-dirty"}
2023-05-17T07:05:08.914Z	INFO	controller.termination	cordoned node	{"commit": "d7e22b1-dirty", "node": "ip-192-168-145-253.ap-northeast-2.compute.internal"}
2023-05-17T07:05:09.316Z	INFO	controller.termination	deleted node	{"commit": "d7e22b1-dirty", "node": "ip-192-168-145-253.ap-northeast-2.compute.internal"}
2023-05-17T07:05:25.923Z	INFO	controller.deprovisioning	deprovisioning via consolidation replace, terminating 1 machines ip-192-168-48-2.ap-northeast-2.compute.internal/m5.xlarge/on-demand and replacing with on-demand machine from types m5.large, c5.large	{"commit": "d7e22b1-dirty"}
2023-05-17T07:05:25.940Z	INFO	controller.deprovisioning	launching machine with 1 pods requesting {"cpu":"1125m","pods":"4"} from types m5.large, c5.large	{"commit": "d7e22b1-dirty", "provisioner": "default"}
2023-05-17T07:05:26.341Z	DEBUG	controller.deprovisioning.cloudprovider	created launch template	{"commit": "d7e22b1-dirty", "provisioner": "default", "launch-template-name": "karpenter.k8s.aws/9547068762493117560", "launch-template-id": "lt-036151ea9df7d309f"}
2023-05-17T07:05:28.182Z	INFO	controller.deprovisioning.cloudprovider	launched instance	{"commit": "d7e22b1-dirty", "provisioner": "default", "id": "i-0eb3c8ff63724dc95", "hostname": "ip-192-168-144-98.ap-northeast-2.compute.internal", "instance-type": "c5.large", "zone": "ap-northeast-2b", "capacity-type": "on-demand", "capacity": {"cpu":"2","ephemeral-storage":"20Gi","memory":"3788Mi","pods":"29"}}
2023-05-17T07:06:12.307Z	INFO	controller.termination	cordoned node	{"commit": "d7e22b1-dirty", "node": "ip-192-168-48-2.ap-northeast-2.compute.internal"}
2023-05-17T07:06:12.856Z	INFO	controller.termination	deleted node	{"commit": "d7e22b1-dirty", "node": "ip-192-168-48-2.ap-northeast-2.compute.internal"}

# 인스턴스 확인
kubectl get node -l type=karpenter
kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type
kubectl get node --label-columns=node.kubernetes.io/instance-type,topology.kubernetes.io/zone

# 삭제
kubectl delete deployment inflate

 

마지막은 Consolidation 이다.

 

위와같이 새로 provisioners 새로 생성해주고

거의 실시간으로 확장이된다. !!! 굿!!!!

이번에는 위와같이 줄여보면 바로바로 줄어든다. !

 

'job > eks' 카테고리의 다른 글

7주차 eks 스터디  (0) 2023.06.08
eks 6주차  (0) 2023.05.31
4주차 eks 스터디  (0) 2023.05.16
3주차 eks 스터디  (0) 2023.05.13
eks 2주차  (0) 2023.05.02

실 서비스에 keycloak 사용하는법(keycloak을 브로커로 구성)

1. 인증되지 않은 사용자가 클라이언트 애플리케이션에서 보호된 리소스를 요청합니다.

2. 클라이언트 애플리케이션은 인증하기 위해 사용자를 Keycloak으로 리디렉션합니다.

3. Keycloak은 영역에 구성된 ID 공급자 목록이 있는 로그인 페이지를 표시합니다.

4. 사용자는 버튼 또는 링크를 클릭하여 ID 공급자 중 하나를 선택합니다.

5. Keycloak은 인증을 요청하는 대상 ID 공급자에게 인증 요청을 발급하고 사용자를 ID 공급자의 로그인 페이지로 리디렉션합니다. 관리자는 이미 관리 콘솔의 ID 공급자에 대한 연결 속성 및 기타 구성 옵션을 설정했습니다.

6. 사용자가 자격 증명을 제공하거나 ID 공급자에 인증하는 데 동의합니다.

7. ID 공급자가 인증에 성공하면 사용자는 인증 응답과 함께 Keycloak으로 다시 리디렉션됩니다. 일반적으로 이 응답에는 Keycloak에서 ID 공급자의 인증을 신뢰하고 사용자 정보를 검색하는 데 사용하는 보안 토큰이 포함됩니다.

8. Keycloak은 ID 공급자의 응답이 유효한지 확인합니다. 유효한 경우, 사용자가 아직 존재하지 않는 경우 Keycloak는 사용자를 가져와서 만듭니다. 토큰에 해당 정보가 포함되어 있지 않은 경우 Keycloak는 ID 공급자에게 추가 사용자 정보를 요청할 수 있습니다. 이 동작을 ID 페더레이션이라고 합니다. 사용자가 이미 존재하는 경우, Keycloak는 ID 공급자로부터 반환받은 ID를 기존 계정과 연결하도록 사용자에게 요청할 수 있습니다. 이 동작을 계정 연결이라고 합니다. Keycloak에서는 계정 연결을 구성하고 첫 로그인 플로우에서 계정 연결을 지정할 수 있습니다. 이 단계에서 Keycloak은 사용자를 인증하고 토큰을 발급하여 서비스 공급자의 요청된 리소스에 액세스할 수 있도록 합니다.
9. 사용자가 인증하면 Keycloak은 로컬 인증 중에 이전에 발급한 토큰을 전송하여 사용자를 서비스 공급자로 리디렉션합니다.
10. 서비스 공급자는 Keycloak으로부터 토큰을 받고 보호된 리소스에 대한 액세스를 허용합니다.

 

이 흐름의 변형이 가능합니다. 예를 들어, 클라이언트 애플리케이션에서 ID 공급자의 목록을 표시하지 않고 특정 공급자를 요청하거나, 사용자가 ID를 페더레이션하기 전에 추가 정보를 제공하도록 Keycloak을 설정할 수 있습니다.

인증 프로세스가 끝나면 Keycloak는 토큰을 클라이언트 애플리케이션에 발급합니다. 클라이언트 애플리케이션은 외부 ID 공급자와 분리되어 있으므로 클라이언트 애플리케이션의 프로토콜이나 사용자 신원을 검증하는 방법을 볼 수 없습니다. 공급자는 Keycloak에 대해서만 알면 됩니다.



구글 계정을통해 keycloak으로 연동한 saml로 실 서비스에 로그인하기.

 

'job' 카테고리의 다른 글

2024년도 devops 추천 도구  (0) 2024.01.11
git 그룹단위로 전체 clone 하기  (0) 2023.08.25
teleport란  (0) 2023.05.02
keycloak - gitlab 연동1  (0) 2023.04.17
Multi-Cluster Kubernetes Orchestration - karmada  (0) 2023.03.14

4주차는 EKS observability 이다.

 

시작.

 

로깅 대상은 컨트롤 플레인, node, 애플리케이션이 있다.

 

먼저 컨트롤 플레인 로깅이다.

현재 모든 로깅은 꺼져있는 상태

위와같이 enable 해주면

On 확인

 

# EC2 Instance가 NodeNotReady 상태인 로그 검색
fields @timestamp, @message
| filter @message like /**NodeNotReady**/
| sort @timestamp desc

# kube-apiserver-audit 로그에서 userAgent 정렬해서 아래 4개 필드 정보 검색
fields userAgent, requestURI, @timestamp, @message
| filter @logStream ~= "kube-apiserver-audit"
| stats count(userAgent) as count by userAgent
| sort count desc

#
fields @timestamp, @message
| filter @logStream ~= "**kube-scheduler**"
| sort @timestamp desc

#
fields @timestamp, @message
| filter @logStream ~= "**authenticator**"
| sort @timestamp desc

#
fields @timestamp, @message
| filter @logStream ~= "kube-controller-manager"
| sort @timestamp desc

log insights 에서 필터링하여 로그를 확인할수 있다.

 

위와같이 프로메테우스 메트릭 형태로도 볼수있다.

 

그외에도 아래 내용을 참고하여 컨트롤플래인단 로그를 확인할수 있다.

# How to monitor etcd database size? >> 아래 10.0.X.Y IP는 어디일까요? >> 아래 주소로 프로메테우스 메트릭 수집 endpoint 주소로 사용 가능한지???
**kubectl get --raw /metrics | grep "etcd_db_total_size_in_bytes"**
etcd_db_total_size_in_bytes{endpoint="<http://10.0.160.16:2379>"} 4.665344e+06
etcd_db_total_size_in_bytes{endpoint="<http://10.0.32.16:2379>"} 4.636672e+06
etcd_db_total_size_in_bytes{endpoint="<http://10.0.96.16:2379>"} 4.640768e+06

**kubectl get --raw=/metrics | grep apiserver_storage_objects |awk '$2>100' |sort -g -k 2**

# CW Logs Insights 쿼리
fields @timestamp, @message, @logStream
| filter @logStream like /**kube-apiserver-audit**/
| filter @message like /**mvcc: database space exceeded**/
| limit 10

# How do I identify what is consuming etcd database space?
**kubectl get --raw=/metrics | grep apiserver_storage_objects |awk '$2>100' |sort -g -k 2**
**kubectl get --raw=/metrics | grep apiserver_storage_objects |awk '$2>50' |sort -g -k 2**
apiserver_storage_objects{resource="clusterrolebindings.rbac.authorization.k8s.io"} 78
apiserver_storage_objects{resource="clusterroles.rbac.authorization.k8s.io"} 92

# CW Logs Insights 쿼리 : Request volume - Requests by User Agent:
fields userAgent, requestURI, @timestamp, @message
| filter @logStream like /**kube-apiserver-audit**/
| stats count(*) as count by userAgent
| sort count desc

# CW Logs Insights 쿼리 : Request volume - Requests by Universal Resource Identifier (URI)/Verb:
filter @logStream like /**kube-apiserver-audit**/
| stats count(*) as count by requestURI, verb, user.username
| sort count desc

# Object revision updates
fields requestURI
| filter @logStream like /**kube-apiserver-audit**/
| filter requestURI like /pods/
| filter verb like /patch/
| filter count > 8
| stats count(*) as count by requestURI, responseStatus.code
| filter responseStatus.code not like /500/
| sort count desc

#
fields @timestamp, userAgent, responseStatus.code, requestURI
| filter @logStream like /**kube-apiserver-audit**/
| filter requestURI like /pods/
| filter verb like /patch/
| filter requestURI like /name_of_the_pod_that_is_updating_fast/
| sort @timestamp

 

이번엔 컨테이너 로깅이다..

# NGINX 웹서버 **배포**
helm repo add bitnami <https://charts.bitnami.com/bitnami>

# 사용 리전의 인증서 ARN 확인
CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text)
echo $CERT_ARN

# 도메인 확인
echo $MyDomain

# 파라미터 파일 생성
cat < nginx-values.yaml
service:
    type: NodePort

ingress:
  enabled: true
  ingressClassName: alb
  hostname: nginx.$MyDomain
  path: /*
  annotations: 
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/success-codes: 200-399
    **alb.ingress.kubernetes.io/load-balancer-name: $CLUSTER_NAME-ingress-alb
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/ssl-redirect: '443'**
EOT
cat nginx-values.yaml | yh

# 배포
**helm install nginx bitnami/nginx --version 14.1.0 -f nginx-values.yaml**

# 확인
kubectl get ingress,deploy,svc,ep nginx
kubectl get targetgroupbindings # ALB TG 확인

# 접속 주소 확인 및 접속
echo -e "Nginx WebServer URL = "
curl -s 
kubectl logs deploy/nginx -f

# 반복 접속
while true; do curl -s  -I | head -n 1; date; sleep 1; done

# (참고) 삭제 시
helm uninstall nginx

 

위와같이 진행한다. 다만 ACM 은 인증서 따로 추가해줘야 한다.

CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text) echo $CERT_ARN
이부분

 

 

위와같이 배포 완료

 

컨테이너도 위와같이 로깅을 확인할수있다.

 

이번에는 외부에서 로그를 수집하도록 하는 방법이다.

# 설치
FluentBitHttpServer='On'
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
FluentBitReadFromTail='On'
**curl -s <https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml> | sed 's/{{cluster_name}}/'${CLUSTER_NAME}'/;s/{{region_name}}/'${AWS_DEFAULT_REGION}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl apply -f -**

# 설치 확인
kubectl get-all -n amazon-cloudwatch
kubectl get ds,pod,cm,sa -n amazon-cloudwatch
kubectl describe **clusterrole cloudwatch-agent-role fluent-bit-role**                          # 클러스터롤 확인
kubectl describe **clusterrolebindings cloudwatch-agent-role-binding fluent-bit-role-binding**  # 클러스터롤 바인딩 확인
kubectl -n amazon-cloudwatch logs -l name=cloudwatch-agent -f # 파드 로그 확인
kubectl -n amazon-cloudwatch logs -l k8s-app=fluent-bit -f    # 파드 로그 확인
for node in $N1 $N2 $N3; do echo ">>>>> $node <<<<<"; ssh ec2-user@$node sudo ss -tnlp | grep fluent-bit; echo; done

# cloudwatch-agent 설정 확인
**kubectl describe cm cwagentconfig -n amazon-cloudwatch**
{
  "agent": {
    "region": "ap-northeast-2"
  },
  "logs": {
    "metrics_collected": {
      "kubernetes": {
        "cluster_name": "myeks",
        "metrics_collection_interval": 60
      }
    },
    "force_flush_interval": 5
  }
}

# CW 파드가 수집하는 방법 : Volumes에 HostPath를 살펴보자! >> / 호스트 패스 공유??? 보안상 안전한가? 좀 더 범위를 좁힐수는 없을까요?
**kubectl describe -n amazon-cloudwatch ds cloudwatch-agent**
...
ssh ec2-user@$N1 sudo tree /dev/disk
...

# Fluent Bit Cluster Info 확인
**kubectl get cm -n amazon-cloudwatch fluent-bit-cluster-info -o yaml | yh**
apiVersion: v1
data:
  cluster.name: myeks
  http.port: "2020"
  http.server: "On"
  logs.region: ap-northeast-2
  read.head: "Off"
  read.tail: "On"
kind: ConfigMap
...

# Fluent Bit 로그 INPUT/FILTER/OUTPUT 설정 확인 - [링크](<https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-setup-logs-FluentBit.html#ContainerInsights-fluentbit-multiline>)
## 설정 부분 구성 : application-log.conf , dataplane-log.conf , fluent-bit.conf , host-log.conf , parsers.conf
**kubectl describe cm fluent-bit-config -n amazon-cloudwatch
...
application-log.conf**:
----
[**INPUT**]
    Name                tail
    Tag                 **application.***
    Exclude_Path        /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
    **Path                /var/log/containers/*.log**
    multiline.parser    docker, cri
    DB                  /var/fluent-bit/state/flb_container.db
    Mem_Buf_Limit       50MB
    Skip_Long_Lines     On
    Refresh_Interval    10
    Rotate_Wait         30
    storage.type        filesystem
    Read_from_Head      ${READ_FROM_HEAD}

[**FILTER**]
    Name                kubernetes
    Match               application.*
    Kube_URL            <https://kubernetes.default.svc:443>
    Kube_Tag_Prefix     application.var.log.containers.
    Merge_Log           On
    Merge_Log_Key       log_processed
    K8S-Logging.Parser  On
    K8S-Logging.Exclude Off
    Labels              Off
    Annotations         Off
    Use_Kubelet         On
    Kubelet_Port        10250
    Buffer_Size         0

[**OUTPUT**]
    Name                cloudwatch_logs
    Match               application.*
    region              ${AWS_REGION}
    **log_group_name      /aws/containerinsights/${CLUSTER_NAME}/application**
    log_stream_prefix   ${HOST_NAME}-
    auto_create_group   true
    extra_user_agent    container-insights
**...**

# Fluent Bit 파드가 수집하는 방법 : Volumes에 HostPath를 살펴보자!
**kubectl describe -n amazon-cloudwatch ds fluent-bit**
...
ssh ec2-user@$N1 sudo tree /var/log
...

# (참고) 삭제
curl -s <https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml> | sed 's/{{cluster_name}}/'${CLUSTER_NAME}'/;s/{{region_name}}/'${AWS_DEFAULT_REGION}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl delete -f -

우선 위와같이 설치를 진행한다.  

정상적으로 설치 완료

 

 

위와같이 fluent bit로 어떻게 가공 과정을 볼수있다.

 

 

콘솔에서도 로그 확인

Insights 항목에서도 여러 항목 모니터링 가능

 

이번에는 모니터링을하기위한 메트릭수집 도구들을 알아볼것이다.

 

 

# 배포
**kubectl apply -f <https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml**>

# 메트릭 서버 확인 : 메트릭은 15초 간격으로 cAdvisor를 통하여 가져옴
kubectl get pod -n kube-system -l k8s-app=metrics-server
kubectl api-resources | grep metrics
kubectl get apiservices |egrep '(AVAILABLE|metrics)'

# 노드 메트릭 확인
kubectl top node

# 파드 메트릭 확인
kubectl top pod -A
kubectl top pod -n kube-system --sort-by='cpu'
kubectl top pod -n kube-system --sort-by='memory'

메트릭서버 설치방법(만 알아본다.)

 

kwatch라는것도 있다. 이걸로 slack 웹훅을 걸어줄수있다.

 

# configmap 생성
cat < ~/kwatch-config.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: kwatch
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kwatch
  namespace: kwatch
data:
  config.yaml: |
    **alert**:
      **slack**:
        webhook: '**<https://hooks.slack.com/services/T03G23CRBNZ/sssss/sss**>'
        title: $NICK-EKS
        #text:
    **pvcMonitor**:
      enabled: true
      interval: 5
      threshold: 70
EOT
**kubectl apply -f kwatch-config.yaml**

# 배포
kubectl apply -f <https://raw.githubusercontent.com/abahmed/kwatch/v0.8.3/deploy/**deploy.yaml**>

botube라는 도구도 있다.

 

 

# repo 추가
helm repo add botkube <https://charts.botkube.io>
helm repo update

# 변수 지정
export ALLOW_KUBECTL=true
export ALLOW_HELM=true
export SLACK_CHANNEL_NAME=webhook3

#
cat < botkube-values.yaml
actions:
  'describe-created-resource': # kubectl describe
    enabled: true
  'show-logs-on-error': # kubectl logs
    enabled: true

executors:
  k8s-default-tools:
    botkube/helm:
      enabled: true
    botkube/kubectl:
      enabled: true
EOT

# 설치
helm install --version **v1.0.0** botkube --namespace botkube --create-namespace \\
--set communications.default-group.socketSlack.enabled=true \\
--set communications.default-group.socketSlack.channels.default.name=${SLACK_CHANNEL_NAME} \\
--set communications.default-group.socketSlack.appToken=${SLACK_API_APP_TOKEN} \\
--set communications.default-group.socketSlack.botToken=${SLACK_API_BOT_TOKEN} \\
--set settings.clusterName=${CLUSTER_NAME} \\
--set 'executors.k8s-default-tools.botkube/kubectl.enabled'=${ALLOW_KUBECTL} \\
--set 'executors.k8s-default-tools.botkube/helm.enabled'=${ALLOW_HELM} \\
-f **botkube-values.yaml** botkube/botkube

# 참고 : 삭제 시
helm uninstall botkube --namespace botkube
# 연결 상태, notifications 상태 확인
**@Botkube** ping
**@Botkube** status notifications

# 파드 정보 조회
**@Botkube** k get pod
**@Botkube** kc get pod --namespace kube-system
**@Botkube** kubectl get pod --namespace kube-system -o wide

# Actionable notifications
**@Botkube** kubectl

botkube의 장점은 슬랙과 연동하여 @Botkube로 슬랙의 지정채널에서 파드의 정보를 불러오는등 슬랙에서 클러스터 정보 확인이 가능한장점이 있다. 

 

자 이제 마지막으로 프로메테우스&그라파나이다.

 

프로메테우스 오퍼레이터 : 프로메테우스 및 프로메테우스 오퍼레이터를 이용하여 메트릭 수집과 알람 기능 실습
https://malwareanalysis.tistory.com/566

Thanos 타노드 : 프로메테우스 확장성과 고가용성 제공
https://hanhorang31.github.io/post/pkos2-4-monitoring/

 

 

프로메테우스란

  •  

제공 기능

  • a multi-dimensional data model with time series data(=TSDB, 시계열 데이터베이스) identified by metric name and key/value pairs
  • PromQL, a flexible query language to leverage this dimensionality
  • no reliance on distributed storage; single server nodes are autonomous
  • time series collection happens via a pull model over HTTP
  • pushing time series is supported via an intermediary gateway
  • targets are discovered via service discovery or static configuration
  • multiple modes of graphing and dashboarding support

설치 방법은 아래와 같다.

# 모니터링
kubectl create ns monitoring
watch kubectl get pod,pvc,svc,ingress -n monitoring

# 사용 리전의 인증서 ARN 확인
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

****# repo 추가
helm repo add prometheus-community <https://prometheus-community.github.io/helm-charts>

# 파라미터 파일 생성
cat < monitor-values.yaml
**prometheus**:
  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      **alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'**

**grafana**:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      **alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'**

defaultRules:
  create: false
**kubeControllerManager:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false**
alertmanager:
  enabled: false

# alertmanager:
#   ingress:
#     enabled: true
#     ingressClassName: alb
#     hosts: 
#       - alertmanager.$MyDomain
#     paths: 
#       - /*
#     annotations:
#       alb.ingress.kubernetes.io/scheme: internet-facing
#       alb.ingress.kubernetes.io/target-type: ip
#       alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
#       alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
#       alb.ingress.kubernetes.io/success-codes: 200-399
#       alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
#       alb.ingress.kubernetes.io/group.name: study
#       alb.ingress.kubernetes.io/ssl-redirect: '443'
EOT
cat monitor-values.yaml | yh

# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version **45.**27.2 \\
--**set** prometheus.prometheusSpec.scrapeInterval='15s' --**set** prometheus.prometheusSpec.evaluationInterval='15s' \\
-f **monitor-values.yaml** --namespace monitoring

# 확인
~~## alertmanager-0 : 사전에 정의한 정책 기반(예: 노드 다운, 파드 Pending 등)으로 시스템 경고 메시지를 생성 후 경보 채널(슬랙 등)로 전송~~
## grafana : 프로메테우스는 메트릭 정보를 저장하는 용도로 사용하며, 그라파나로 시각화 처리
## prometheus-0 : 모니터링 대상이 되는 파드는 ‘exporter’라는 별도의 사이드카 형식의 파드에서 모니터링 메트릭을 노출, pull 방식으로 가져와 내부의 시계열 데이터베이스에 저장
## node-exporter : 노드익스포터는 물리 노드에 대한 자원 사용량(네트워크, 스토리지 등 전체) 정보를 메트릭 형태로 변경하여 노출
## operator : 시스템 경고 메시지 정책(prometheus rule), 애플리케이션 모니터링 대상 추가 등의 작업을 편리하게 할수 있게 CRD 지원
## kube-state-metrics : 쿠버네티스의 클러스터의 상태(kube-state)를 메트릭으로 변환하는 파드
helm list -n monitoring
kubectl get pod,svc,ingress -n monitoring
kubectl get-all -n monitoring
**kubectl get prometheus,servicemonitors -n monitoring**
~~~~**kubectl get crd | grep monitoring**

 

위와같이 설치 완료

 

프로메테우스 메트릭 수집정보를 확인해보자.

노드의 정보를 가져오는 노드 익스포터라는 파드가 올라온것을 확인할수있다.

 

위 메트릭들을 수집한다.

 

프로메테우스가 수집할 타겟들

 

아까 봤떤 노드 익스포터

 

그라파나 확인

다른 사용자가 만들어둔 여러가지 대시보드들이 있다.

공식 대시보드 가져오기 - 링크 추천

  • [Kubernetes / Views / Global] Dashboard → New → Import → 15757 입력 후 Load ⇒ 데이터소스(Prometheus 선택) 후 Import 클릭
  • [1 Kubernetes All-in-one Cluster Monitoring KR] Dashboard → New → Import → 13770 or 17900 입력 후 Load ⇒ 데이터소스(Prometheus 선택) 후 Import 클릭
  • [Node Exporter Full] Dashboard → New → Import → 1860 입력 후 Load ⇒ 데이터소스(Prometheus 선택) 후 Import 클릭
  • kube-state-metrics-v2 가져와보자 : Dashboard ID copied! (13332) 클릭 - 링크
    • [kube-state-metrics-v2] Dashboard → New → Import → 13332 입력 후 Load ⇒ 데이터소스(Prometheus 선택) 후 Import 클릭
  • [Amazon EKS] **AWS CNI Metrics 16032 - 링크

 

 

위 항목에서 Dashboard 임폴트가 가능하다.

 

임폴트한 대시보드 화면

 

그럼 마지막으로 아까 배포한 Nginx 파드의 여러가지 정보를 모니터링해보자.

 

# 모니터링
watch -d kubectl get pod

# 파라미터 파일 생성 : 서비스 모니터 방식으로 nginx 모니터링 대상을 등록하고, export 는 9113 포트 사용, nginx 웹서버 노출은 AWS CLB 기본 사용
cat <<EOT > ~/nginx_metric-values.yaml
metrics:
  enabled: true

  service:
    port: 9113

  serviceMonitor:
    enabled: true
    namespace: monitoring
    interval: 10s
EOT

# 배포
helm **upgrade** nginx bitnami/nginx **--reuse-values** -f nginx_metric-values.yaml

# 확인
kubectl get pod,svc,ep
kubectl get servicemonitor -n monitoring nginx
kubectl get servicemonitor -n monitoring nginx -o json | jq

# 메트릭 확인 >> 프로메테우스에서 Target 확인
NGINXIP=$(kubectl get pod -l app.kubernetes.io/instance=nginx -o jsonpath={.items[0].status.podIP})
curl -s <http://$NGINXIP:9113/metrics> # nginx_connections_active Y 값 확인해보기
curl -s <http://$NGINXIP:9113/metrics> | grep ^nginx_connections_active

# nginx 파드내에 컨테이너 갯수 확인
kubectl get pod -l app.kubernetes.io/instance=nginx
kubectl describe pod -l app.kubernetes.io/instance=nginx

# 접속 주소 확인 및 접속
echo -e "Nginx WebServer URL = <https://nginx.$MyDomain>"
curl -s <https://nginx.$MyDomain>
kubectl logs deploy/nginx -f

# 반복 접속
while true; do curl -s <https://nginx.$MyDomain> -I | head -n 1; date; sleep 1; done

 

추가 참고.

 

kubecost - k8s 리소스별 비용 현황 가시화 도구

# 
cat < cost-values.yaml
global:
  grafana:
    enabled: true
    proxy: false

priority:
  enabled: false
networkPolicy:
  enabled: false
podSecurityPolicy:
  enabled: false

persistentVolume:
    storageClass: "gp3"

prometheus:
  kube-state-metrics:
    disabled: false
  nodeExporter:
    enabled: true

reporting:
  productAnalytics: true
EOT

**# kubecost chart 에 프로메테우스가 포함되어 있으니, 기존 프로메테우스-스택은 삭제하자 : node-export 포트 충돌 발생**
**helm uninstall -n monitoring kube-prometheus-stack**

# 배포
kubectl create ns kubecost
helm install kubecost oci://public.ecr.aws/kubecost/cost-analyzer --version **1.103.2** --namespace kubecost -f cost-values.yaml

# 배포 확인
kubectl get-all -n kubecost
kubectl get all -n kubecost

# kubecost-cost-analyzer 파드 IP변수 지정 및 접속 확인
CAIP=$(kubectl get pod -n kubecost -l app=cost-analyzer -o jsonpath={.items[0].status.podIP})
curl -s $CAIP:9090

# 외부에서 bastion EC2 접속하여 특정 파드 접속 방법 : socat(SOcket CAT) 활용 - [링크](<https://www.redhat.com/sysadmin/getting-started-socat>)
yum -y install socat
socat TCP-LISTEN:80,fork TCP:$CAIP:9090
웹 브라우저에서 bastion EC2 IP로 접속

설치 방법

 

위와같이 kubecost를 통해 리소스별 비용 확인이 가능하다.

 

 

 

오픈텔레메트리

 

https://opentelemetry.io/docs/what-is-opentelemetry/ 참고

 

 

 

'job > eks' 카테고리의 다른 글

eks 6주차  (0) 2023.05.31
eks 5주차  (0) 2023.05.23
3주차 eks 스터디  (0) 2023.05.13
eks 2주차  (0) 2023.05.02
eks 교육 1  (0) 2023.04.24

3주차 eks 스터디는 스토리지 및 노드 관리이다.

 

 

3주차 실습 환경 구성은 앞선 2주차와 마찬가지로 클라우드 포메이션을 통해 배포를 진행한다.

2주차

https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick.yaml

3주차

https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick2.yaml

 

2주차 3주차 각각 다운받아서 diff로 비교해보면 efs 쪽 설정이 추가된것을 확인할수있다.

  • Amazon EKS 윈클릭 배포 (EFS 생성 추가) & 기본 설정
    • 기본 설정 및 EFS 확인
    # default 네임스페이스 적용
    **kubectl ns default**
    
    # (옵션) context 이름 변경
    NICK=<각자 자신의 닉네임>
    **NICK=gasida**
    kubectl ctx
    kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io **$NICK@myeks**
    
    # EFS 확인 : AWS 관리콘솔 EFS 확인해보자
    mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport ***<자신의 EFS FS ID>***.efs.ap-northeast-2.amazonaws.com:/ **/mnt/myefs**
    **df -hT --type nfs4**
    mount | grep nfs4
    **echo "efs file test" > /mnt/myefs/memo.txt**
    cat /mnt/myefs/memo.txt
    **rm -f /mnt/myefs/memo.txt**
    
    # 스토리지클래스 및 CSI 노드 확인
    kubectl get sc
    kubectl get sc gp2 -o yaml | yh
    kubectl get csinodes
    
    # 노드 정보 확인
    kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
    ****eksctl get iamidentitymapping --cluster myeks
    ****
    # 노드 IP 확인 및 PrivateIP 변수 지정
    N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
    N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
    N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
    echo "export N1=$N1" >> /etc/profile
    echo "export N2=$N2" >> /etc/profile
    echo "export N3=$N3" >> /etc/profile
    echo $N1, $N2, $N3
    
    # 노드 보안그룹 ID 확인
    NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
    aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32
    
    # 워커 노드 SSH 접속
    ssh ec2-user@$N1 hostname
    ssh ec2-user@$N2 hostname
    ssh ec2-user@$N3 hostname
    
    # 노드에 툴 설치
    ssh ec2-user@$N1 sudo yum install links tree jq tcpdump sysstat -y
    ssh ec2-user@$N2 sudo yum install links tree jq tcpdump sysstat -y
    ssh ec2-user@$N3 sudo yum install links tree jq tcpdump sysstat -y
    
    • AWS LB/ExternalDNS, kube-ops-view 설치
    # 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
    
    # ExternalDNS
    MyDomain=<자신의 도메인>
    **MyDomain=gasida.link**
    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"
    
    • 설치 정보 확인
    # 이미지 정보 확인
    **kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\\n' | sort | uniq -c**
    
    # eksctl 설치/업데이트 addon 확인
    **eksctl get addon --cluster $CLUSTER_NAME**
    
    # IRSA 확인
    **eksctl get iamserviceaccount --cluster $CLUSTER_NAME**
    

 

 

2주차와 마찬가지(efs 및 2주차에서 실습 진행했떤 ALB등은 추가돼있음)로 위와같이 진행하면 3주차 실습 준비 완료

 

 

첫번째 사진처럼 파드안의 컨테이너 내 tmpFS가 있는경우 컨테이너가 종료되면 tmpFS내 데이터도 삭제된다.

마찬가지로 두번째 사진처럼 파드내에 볼륨이 있고 두개의 컨테이너가 해당 볼륨을 공유해서 사용하여도 파드가 종료되면 볼륨내 데이터도 삭제가 된다.

따라서 컨테이너, 파드의 종료여부와 상관없이 데이터를 보존하기 위해 PV/PVC가 필요하다.

앞서 PV/PVC를 이용하지 않는다면(즉 컨테이너 내에 임시의 파일시스템을 이용한다면)데이터가 보존되지 않는다고했는데 이를 실제로 확인해보면 아래와 같다.

10초간격으로 date를 찍는 busybox를 deployment로 배포하고 pod-out.txt 확인하면 10초에 한번씩 찍힌것을 볼수있다.

이후 busybox 파드를 종료후 다시 확인해보면 이전에 찍혔던 date(즉 기존 데이터)는 보존되지 않은것을 확인할수있다.

 

이번에는 local-path-provisioner 스트리지 클래스를 사용하여 데이터 보존성을 확인해보자.

위와같이 local-path-provisioner 사용하기 위해 localpth claim 을 생성해주고

위와같이 앞서 생성한 PVC를 마운트하여 파드를 실행하고 아까와같이 파드를 재시작하여 데이터 보존유무를 확인해보자.

위와같이 파드를 종료하여도 데이터가 없어지지않고 기존 데이터가 보존되는것을 확인할수있다.

실제 데이터는 node1에 저장돼있는것을 확인할수있다.

 

여기까지 eks 클러스터내의 파드를 이용한 볼륨 마운트를 알아보았는데 지금부터는 AWS EBS 마운트를 알아보도록 하자.

 

```bash
# 아래는 **aws-ebs-csi-driver** 전체 버전 정보와 기본 설치 버전(True) 정보 확인
**aws eks describe-addon-versions \\
    --addon-name aws-ebs-csi-driver \\
    --kubernetes-version 1.24 \\
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \\
    --output text**
v1.18.0-eksbuild.1
Tru
v1.17.0-eksbuild.1
False
...

# ISRA 설정 : AWS관리형 정책 AmazonEBSCSIDriverPolicy 사용
eksctl create **iamserviceaccount** \\
  --name **ebs-csi-controller-sa** \\
  --namespace kube-system \\
  --cluster ${CLUSTER_NAME} \\
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/**AmazonEBSCSIDriverPolicy** \\
  --approve \\
  --role-only \\
  --role-name **AmazonEKS_EBS_CSI_DriverRole**

# ISRA 확인
kubectl get sa -n kube-system ebs-csi-controller-sa -o yaml | head -5
**eksctl get iamserviceaccount --cluster myeks**
NAMESPACE	    NAME				            ROLE ARN
kube-system 	ebs-csi-controller-sa		**arn:aws:iam::911283464785:role/AmazonEKS_EBS_CSI_DriverRole**
...

# Amazon EBS CSI driver addon 추가
eksctl create **addon** --name aws-ebs-csi-driver --cluster ${CLUSTER_NAME} --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/**AmazonEKS_EBS_CSI_DriverRole** --force

# 확인
**eksctl get addon --cluster ${CLUSTER_NAME}**
kubectl get deploy,ds -l=app.kubernetes.io/name=aws-ebs-csi-driver -n kube-system
kubectl get pod -n kube-system -l 'app in (ebs-csi-controller,ebs-csi-node)'
kubectl get pod -n kube-system -l app.kubernetes.io/component=csi-driver

# ebs-csi-controller 파드에 6개 컨테이너 확인
**kubectl get pod -n kube-system -l app=ebs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo**
ebs-plugin csi-provisioner csi-attacher csi-snapshotter csi-resizer liveness-probe

# csinodes 확인
kubectl get csinodes

# gp3 스토리지 클래스 생성
kubectl get sc
cat <<EOT > gp3-sc.yaml
kind: **StorageClass**
apiVersion: storage.k8s.io/v1
metadata:
  name: gp3
**allowVolumeExpansion: true**
**provisioner: ebs.csi.aws.com**
volumeBindingMode: WaitForFirstConsumer
parameters:
  **type: gp3**
  allowAutoIOPSPerGBIncrease: 'true'
  encrypted: 'true'
  #fsType: ext4 # 기본값이 ext4 이며 xfs 등 변경 가능 >> 단 스냅샷 경우 ext4를 기본으로하여 동작하여 xfs 사용 시 문제가 될 수 있음 - 테스트해보자
EOT
**kubectl apply -f gp3-sc.yaml**
kubectl get sc
kubectl describe sc gp3 | grep Parameters
```

-

2주차때 했던것처럼 IRSA 설정해서 AWS EBS와 EKS 클러스터간에 통신이 가능하도록 해줘야 한다. 

ebs 테스트 준비 완료

 

이제 아까처럼 pv/pvc 로 마운트하여 테스트해보자.

기존

위와같이 pvc 생성하고 해당 pvc를 마운트하는 파드를 새로 생성하면 아래와같이 새로운 볼륨이 생성된것을 확인할수있다.

이와같이 파드에 EBS를 마운트하여 사용할수있다. 또한 기존 aws ebs와 마찬가지로 용량 증설도 가능하다.(반대로 축소는 불가능)

 

이번엔 볼륨 스냅샷에 대해 알아보자. 이또한 EKS의 개념이 아닌 aws내 ebs 의 스냅샷 기능을 이용하는것.

 

```bash
# (참고) EBS CSI Driver에 snapshots 기능 포함 될 것으로 보임
kubectl describe pod -n kube-system -l app=ebs-csi-controller

# Install Snapshot CRDs
curl -s -O <https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml>
curl -s -O <https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml>
curl -s -O <https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml>
kubectl apply -f snapshot.storage.k8s.io_volumesnapshots.yaml,snapshot.storage.k8s.io_volumesnapshotclasses.yaml,snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl get crd | grep snapshot
kubectl api-resources  | grep snapshot

# Install Common Snapshot Controller
curl -s -O <https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml>
curl -s -O <https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml>
kubectl apply -f rbac-snapshot-controller.yaml,setup-snapshot-controller.yaml
kubectl get deploy -n kube-system snapshot-controller
kubectl get pod -n kube-system -l app=snapshot-controller

# Install Snapshotclass
curl -s -O <https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml>
kubectl apply -f snapshotclass.yaml
kubectl get vsclass # 혹은 volumesnapshotclasses
```

 위와같이 준비 진행

 

위와같이 볼륨 스냅샷을 apply하면 아래와같이 스냅샷이 생성된것을 확인할수있다.

 

 

그럼 장애를 재연해서 스냅샷으로 복원해보도록 하자.

위와같이 실수록 삭제한 상황에서

우선 위와같이 datasource를 ebs볼륨 스냅샷을 가지고 pvc를 생성해주고

위와같이 스냅샷을 이용하여 생성한 pvc를 가지고 파드를 생성해주면 

위와같이 기존에 가지고 있떤 스냅샷을 가지고 ebs를 생성된것이 확인된다.

 

이번엔 efs에 대해 진행할것이다.

우선 efs의 구성 및 아키텍쳐는 다음과 같다.

(처음 efs 에 대해 공부할때 AWS에 있는 NFS로 이해했었다.)

 

# EFS 정보 확인 
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text

# IAM 정책 생성
curl -s -O <https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/docs/**iam-policy-example.json**>
aws iam create-policy --policy-name **AmazonEKS_EFS_CSI_Driver_Policy** --policy-document file://iam-policy-example.json

# ISRA 설정 : 고객관리형 정책 AmazonEKS_EFS_CSI_Driver_Policy 사용
eksctl create **iamserviceaccount** \\
  --name **efs-csi-controller-sa** \\
  --namespace kube-system \\
  --cluster ${CLUSTER_NAME} \\
  --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AmazonEKS_EFS_CSI_Driver_Policy \\
  --approve

****# ISRA 확인
kubectl get sa -n kube-system efs-csi-controller-sa -o yaml | head -5
****eksctl get iamserviceaccount --cluster myeks

# EFS Controller 설치
helm repo add aws-efs-csi-driver <https://kubernetes-sigs.github.io/aws-efs-csi-driver/>
helm repo update
helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \\
    --namespace kube-system \\
    --set image.repository=602401143452.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/eks/aws-efs-csi-driver \\
    --set controller.serviceAccount.create=false \\
    --set controller.serviceAccount.name=efs-csi-controller-sa

# 확인
helm list -n kube-system
kubectl get pod -n kube-system -l "app.kubernetes.io/name=aws-efs-csi-driver,app.kubernetes.io/instance=aws-efs-csi-driver"

LB나 EBS와 마찬가지로 IRSA 셋팅해준다.

이제 efs를 마운트한 파드를 배포해보자.

 

위와같이 EFS가 연결돼있는 PV/PVC를 생성해주고

 

위와같이 파드 2개를 생성해준다.

위와같이 2개의 파드와 맨 처음 해당 실습을 진행했을때 배스천호스트에 마운트해놨던 efs까지 확인할수있다.

 

즉 현재 방금 배포한 2개의 파드와 실습용ec2 인스턴스는 한개의 efs 볼륨을 공유해서 사용하고있는상황
(예전 온프레미스 환경에서 NFS는 실시간 동기화필요할때(업로드 디렉토리처럼) 많이 사용했었음)

 

'job > eks' 카테고리의 다른 글

eks 6주차  (0) 2023.05.31
eks 5주차  (0) 2023.05.23
4주차 eks 스터디  (0) 2023.05.16
eks 2주차  (0) 2023.05.02
eks 교육 1  (0) 2023.04.24

목표

EKS 네트워크의 이해
각각의 노드내 파드에서의 통신(내/외부) 흐름 이해

 

 

 

1. 실습 환경 배포

https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick.yaml 을 통해 실습환경구축

Stackname, keyName, Accesskey, secret key 입력

SgIngressSshCidr 은 본인 아이피 입력

나머지는 자동

 

ps.

비용아끼겠다고 t3말고 t2로 하면 1시간도 더 걸린다. 그러니 그냥 t3로 진행할것

 

관리용 ec2 접속

에러는 아직 클라우드 포메이션이 전부 배포가 되지 않았기 때문이다.

클라우드 포메이션으로 생성중

 

배포완료
/root/myeks.yml 파일에서 배포정보를 확인가능

 

# default 네임스페이스 적용 kubectl ns default



# 노드 정보 확인 kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone eksctl get iamidentitymapping --cluster myeks



# 노드 IP 확인 및 PrivateIP 변수 지정

N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})

N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})

N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})

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

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

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

echo $N1, $N2, $N3

# 노드 보안그룹 ID 확인 NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text) aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32



# 워커 노드 SSH 접속

ssh ec2-user@$N1 hostname

ssh ec2-user@$N2 hostname

ssh ec2-user@$N3 hostname

 

위 작업들을 순서대로 진행하면 N1, N2, N3(노드1,2,3)서버로 접속이 가능해진다.

default name space 적용
노드 정보 확인
노드별로 N1,2,3 에 아이피 지정
노드 보안그룹 ID 확인
설치된 addon들
AWS 웹 콘솔에서도 설치된 에드온 확인이 가능하다.
node1,2,3 의 ssh 접속 및 hostname 확인

여기까지 하면 2주차 실습 준비 완료

 

 

온프레미스 k8s에 가장 많이 사용되는 CNI인 Calico와 AWS EKS에 가장 많이 사용되는 AWS VPC CNI의 차이점은 다음과 같다.

- Calico의 경우 파드와 노드의 대역이 다르지만 AWS VPC CNI는 네트워크 통신 최적화를 위해 파드와 노드의 대역이 같다.

- Calico는 오버레이(인캡슐 디캡슐진행) 통신을 하고 AWS VPC CNI는 동일대역으로 직접 통신

AWS VPC CNI를 구축할때 주의할점은 네트워크 대역을 보통 24비트로 하는경우가 많을텐데(나만 그런가...) AWS VPC CNI의 경우 파드 네트워크와 노드 네트워크가 공유되기때문에 네트워크 대역을 여유롭게 22비트 정도로 주는게 좋다.

위와같이 노드1,3에는 eni~로 가상의 네트워크가 추가돼있는데 이것들이 각각 노드1,3에 추가돼있는 파드의 네트워크 정보이다.

 

t3.medium의 경우 ENI별로 6개의 아이피를 할당받을수있는데 아래와같이 node1,3은 ENI가 2개라 보조 프라이빗 IP까지 총 12개가 할당된것을 볼수있고 node2는 6개가 할당된것을 볼수있다.

위와같이 노드1,3에는 보조 프라이빗 ip주소가 생성돼있는걸 확인할수있는데 노드2에는 파드가 추가돼있지 않아서 기본적으로 할당되는 6개(1개는 프라이빗으로 실제 할당이됐고 나머지5개)만 할당돼있는걸 확인할 수 있다.

 

여기서 replicas 3을 줘서 각각의 노드에 파드를 1개씩 추가로 생성되도록 해보면 결과는 아래와 같다.

보는것처럼 각각의 노드에 ENI가 1개씩 추가된것을 확인할수있다.

그럼 실제 할당된 아이피 갯수는 몇개일까

보는것처럼 노드2는 6개의 아이피가 추가된것을 확인할수있다. 다만 노드1,3은 아까와 동일하다.

그렇다. 파드가 추가(ENI가 생성)된다고 해서 무조건 새로운 아이피가 할당되는게 아니다.

ENI 가 생성되는 프로세스는 다음과같다.

IP POOL에 내용이 없으면 세컨더리 ENI를 생성(방금 노드2번의 경우) 그게 아니라면 기존 IP POOL에서 아이피를 할당하는것이다.

이렇게 IP POOL이 부족하면 새로운 ENI를 생성하는건데 무제한으로 생성되지 않는다.

 

 

 

온프레미스에서는 iptables의 NAT 구성으로 트래픽을 포워딩 해주는데  eks는 하나의 네트워크 대역에 들어가니까 확실히 calico(온프레미스용 CNI)보다 네트워크 구성을 이해하기가 더 좋은거같다. 그렇기 때문에 문제가 발생했을경우 어떤구간에서 문제가 발생했는지 좀더 직관적(동일 네트워크이기에 원인 파악이 쉬움)으로 트러블 슈팅이 가능할것이다.

 

실제로 노드내 파드끼리, 통신을 해보자.

https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/cni-proposal.md

 

GitHub - aws/amazon-vpc-cni-k8s: Networking plugin repository for pod networking in Kubernetes using Elastic Network Interfaces

Networking plugin repository for pod networking in Kubernetes using Elastic Network Interfaces on AWS - GitHub - aws/amazon-vpc-cni-k8s: Networking plugin repository for pod networking in Kubernete...

github.com

위와같은 플로우로 진행이 되는데 실제로 실습을 해보고자 한다.

위와같이 파드2에서 ping 하여 tcpdump 해보면 pod1의 아이피로 통신하는것을 확인할수 있다.

 

이번엔 파드에서 외부로 나가는 통신을 확인해보자.

위와같이 파드1에서 google.com 으로 통신을 한 결과이다.

일단 ping이 정상적인거보면 통신은되는데... 172.217.31.132가 실제 구글 아이피는 아니다.

google의 아이피는 172.217.31.132가 아닌데 tcpdump의 결과는 172.217.31.132로 통신을 보내고있다.

그러면 172.217.31.132 에서 또 다른곳으로 NAT를 통해 실제 google로 통신이 되고 있는것이다.

다시 말하자면 파드의 실제 외부아이피(즉 외부 인터넷(구글)과 통신할 아이피)는 사설망 아이피(192나 172.로 시작하는게 아닌)가 아닌 외부 아이피로 통신을 해야한다. 

이건 iptables의 SNAT를 통해 통신이 된다.

iptables 설정은 위와같다.

위와같이 192.168(클러스터 내 파드들의 대역)은 AWS SNAT-CHAIN으로 통신을 하고 그 외 나머지는 192.168.1.190아이피 달고 가라. 그럼 라우팅 설정에 의해 외부(인터넷)으로 통신이 되는것이다.

 

위와같이 각 pod별로 아이피가 할당이되고 통신되는것을 알아보았는데. 파드에 할당되는 아이피는 설정한 대역내 랜덤으로 아이피가 할당된다. 그럼 실제 서비스를 하기위해 파드로의 통신은 어떻게 해야할까.

service 라는 리소스 오브젝트로 VIP를 만들어서 파드로 통신이 가능하다.

서비스 종류는 다음과 같다.

이중 NLB+AWS VPC CNI의 경우 앞단의 NLB에서 파드로 바로 통신이 가능하다. 이것이 가능한 이유는 node의 아이피대역과 파드의 아이피 대역이 같은 AWS VPC CNI의 특징때문이다. 

즉 노드내 iptables나 contrack와 같은 자원을 사용하지 않아 불필요한 자원이 사용되지 않고 통신구간이 줄어드는 장점이 있다.

다만 k8s와 AWS 로드밸런서(NLB)간에 어떠한 통신을하며 k8s 파드가 늘거나 혹은 줄어듬에 따라 NLB에서 대상타깃이 추가/제거가 필요하기 때문이다. 이는 k8s 클러스터내에 존재하는 load balancer controller 파드와 AWS의 서비스인 로드밸런서(NLB)간에 인증(OIDC)절차가 진행/완료되어 파드 IP등 지속적인 정보를 제공해주도록 해야한다.

인증 절차 진행을 위한 작업 IRSA 으로 진행한다.

더보기

IRSA는 AWS에서 제공하는 IAM Role을 Kubernetes Service Account와 연결하는 방법입니다. Kubernetes에서 Pod에서 사용하는 인증 방식 중 Service Account를 사용하는데, 이를 통해 Pod가 AWS 리소스에 접근할 수 있습니다. 하지만 이 때 Pod 내부에서 AWS SDK를 사용해 AWS API를 호출하면, 이 Pod는 AWS 인증 정보를 갖지 않아서 AWS API를 호출할 권한이 없습니다. 이 때 IRSA를 사용하면 Kubernetes Service Account를 IAM Role과 연결하여 Pod에서 AWS 리소스에 접근할 수 있게 됩니다. 이를 통해 보안성을 높일 수 있고, AWS IAM의 역할을 더욱 세분화하여 Pod에게 필요한 권한만 부여할 수 있습니다.

인증 방법은 아래와같다.

# OIDC 확인
aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text
aws iam list-open-id-connect-providers | jq

# IAM Policy (AWSLoadBalancerControllerIAMPolicy) 생성
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.7/docs/install/iam_policy.json
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json

# 혹시 이미 IAM 정책이 있지만 예전 정책일 경우 아래 처럼 최신 업데이트 할 것
# aws iam update-policy ~~~

# 생성된 IAM Policy Arn 확인
aws iam list-policies --scope Local
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --query 'Policy.Arn'

우선 OIDC 를 설정(클라우드 포메이션으로 자동 설정)하면 아래와 같이 EKS 클러스터에서 OIDC 가 설정돼있음을 확인할수있고

 

IAM 폴리시를 생성하면

 

위와같이 IAM 폴리시가 추가된것을 확인할수 있다.

여기까지하면 aws EKS에서는 OIDC를 통해 인증을 진행할 준비가 됐고 IAM 폴리시를 추가하여 EKS 클러스터에서 AWS 로드밸런서를 컨트롤할수있는 권한을 가져올 준비가 끝났다.

 

이제 IRSA를 통해 컨트롤이 가능하도록 구성하면된다.

방법은 아래와 같다.

# AWS Load Balancer Controller를 위한 ServiceAccount를 생성 >> 자동으로 매칭되는 IAM Role 을 CloudFormation 으로 생성됨!
# IAM 역할 생성. AWS Load Balancer Controller의 kube-system 네임스페이스에 aws-load-balancer-controller라는 Kubernetes 서비스 계정을 생성하고 IAM 역할의 이름으로 Kubernetes 서비스 계정에 주석을 답니다
eksctl create iamserviceaccount --cluster=$CLUSTER_NAME --namespace=kube-system --name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve

## IRSA 정보 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

## 서비스 어카운트 확인
kubectl get serviceaccounts -n kube-system aws-load-balancer-controller -o yaml | yh

eksctl을 통해 서비스 어카운트를 생성하고 이는 클라우드포메이션을 통해 추가로 배포된다.

IRSA 확인

이제 모든 준비가 끝났으니 로드밸런서 컨트롤러를 EKS 클러스터에 헬름차트를 통해 배포하여 사용해보자.

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

이후 로드밸런싱이 어떻게 구성되는지 확인하기 위해 아래와같이 테스트로 서비스와 파드를 배포하면

curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/echo-service-nlb.yaml
cat echo-service-nlb.yaml | yh
kubectl apply -f echo-service-nlb.yaml

아래와같이 enpoint가 두개(192.168.2.101, 192.168.3.75)로 로드밸런싱되도록 배포 된것을 확인할수 있다.

여기서 중요한부분은 앞서 말한것처럼 NLB에서 다이렉트로 파드의 아이피로 간다는것이다. 

이것이 가능한이유는 AWS VPC CNI의 장점인 노드아이피와 파드의 아이피가 동일한 대역이라 가능하기 때문이다.

 

실제 AWS 로드밸런서를 확인해보면

위와같이 eks 클러스터에서 AWS 로드밸런서 서비스가 생성된것을 확인할수있다.

이것이 가능한것은 앞서 클러스터에서는 OIDC, AWS에는 IAM 정책을 생성해줬고 이를 IRSA로 설정해줬기 때문이다.

 

실습 완료후 자원 삭제 

aws cloudformation delete-stack --stack-name eksctl-$CLUSTER_NAME-addon-iamserviceaccount-kube-system-aws-load-balancer-controller

 

'job > eks' 카테고리의 다른 글

eks 6주차  (0) 2023.05.31
eks 5주차  (0) 2023.05.23
4주차 eks 스터디  (0) 2023.05.16
3주차 eks 스터디  (0) 2023.05.13
eks 교육 1  (0) 2023.04.24

텔레포트란 ?
Teleport is an open-source tool that provides zero-trust access to servers and cloud applications using SSH, Kubernetes and HTTPS. It eliminates the complexity of setting up VPNs by providing a secure gateway to applications, servers and Kubernetes clusters. It was open-sourced by Gravitational Inc

 

 



시스템엔지니어나 IT담당자. 데브옵스엔지니어등 사내 인프라담당자, 관리자들은 여러 서비스에 접근이 필요하다.

1. 관리하고 있는 온프렘의 수십 수백대의 서버.

2. pgsql, mariadb등 수많은 디비들
3. k8s 
4. 각종 어플리케이션

5. aws등

위와같이 여러 서비스를 관리해야하기에 각각의 로그인 창구가 필요하다. 이를 하나의 창구로 통일시키면 다음과 같은 장점이 생긴다.

1. 편리함 - SSH를통한 서버로의 접속이나 클러스터, 데이터베이스, 리모트데스크톱, 웹 애플리케이션등에 액세스할때 각각의 로그인 창구가 아니라 Teleport 하나의 단일 솔루션으로 로그인 하니까.

2. 보안 향상
2-1) 세션 레코딩
2-2) 제로 트러스트

2-3) 감사 로그

 

 

 

1. 로그인 시스템

1-1) 계정별로 환경 분리 - 개발/운영 계정 별도로

1-2) 로그인 창구 최소화 - 대표계정 하나에서 로그인하고 이후 dev / prod 로 어쓔밍

1-3) 침해사고시 영향 최소화  - 각 계정별 접근권한 최소화

 

2. 서버 접근 - Teleport. 세션 레코딩 기능이 가장 매력적임. linux의 script 명령어가 훨씬 더 깔금하게 저장된다고 생각하면될듯

3. 인프라는 뭘로 ? - k8s

4. CI/CD 파이프라인 - 

5. 모니터링

6. APM

 

'job > public cloud' 카테고리의 다른 글

AWS스터디 - S3  (0) 2022.11.15
aws 스터디 - rds  (0) 2022.11.14
AWS스터디 - EC2, EBS, ELB, Route53  (0) 2022.11.14
AWS 스터디 - IAM  (0) 2022.11.14
aws SAA(AWS Certified Solutions Architect - Associate) 불합격 & 합격 후기  (6) 2020.05.12

EKS는 제대로 써본적이 없어서 이번에 한번 공부해봐야겠다 싶어 신청했는데 좋은 기회가 왔다. aws 워크샵을 토대로 여러 쟁쟁한 분들께서 참여&도움주시는 스터디다.
유튜브부터 각종 커뮤니티에서 활동하시는 정
말 현업에서 쟁쟁하신분들께서 함께 공유에 힘쓰는 모습이 멋있다는 생각이 든다.

특히 유튜브 악분일상님보고 너무 반가웠다. 개인적으로 공부법이 자기전에 그냥 틀어놓고 자는건데 악분일상님 유튜브 많이 틀어놓는데.. 혼자서 반가웠다.

여튼 알려주신 회사동료와 좋은 교육을 제공해주시는 모든 분들께 감사인사를 표해야겠다.

 

 

이 글의 목표
eks교육 1주차를 듣고... 내가 아는 IT 지인들중 쿠버네티스(EKS) 이름만 들어보고 뭔지 모르는 사람들이 이 글을 읽고 언제 어떻게 왜 써야 하는지 알게끔 하기.
최대한 간략하고 이해하기 쉽게 쓰기.

 

시작.

1. EKS란 

AWS에서 제공하는 쿠버네티스 서비스.
쿠버네티스란 컨테이너가 한두개면 상관없는데 너~무 많아지면서(컨테이너당 서비스 하나가 표준) 컨테이너 관리가 어려워졌다. 그래서 나온게 k8s.
k8s란 컨테이너 오케스트레이션 도구. 컨테이너 인프라를 더 똑똑하게 효율적으로 잘 관리해주는 도구.
ㄴ똑똑하고 효율적이다 : 자원을 모니터링해서 오토힐링, 오토스케일링등

 

컨테이너란 ? https://kk-7790.tistory.com/135 참고

 

컨테이너를 왜쓰지 ? 
1. 서비스 이식성 - 어떤 OS건 도커등 컨테이너 실행환경만 갖춰진 OS라면 바로 실행이 가능하다. 
2. 빠른 복구 - 서비스 이식성이 좋기 때문에 서버 다운시 다른 서버로 빠르게 복구가 가능하다.
ㄴbefore : 기존 서버와 동일한 아파치,php, mysql 설치하고 설정파일 복사해서 붙여넣고 등등...
ㄴafter : 컨테이너만 기존꺼랑 맞게 실행해주면 끝(주기적으로 변경되는 데이터(DB등)은 당연히 리스토어해야함)

3. 배포 및 관리 단순화 - 통일된 인프라(도커 컨테이너라는)가 되기 때문에 오퍼레이터 입장에서 상당히 편해짐
ㄴbefore : OS별로 설치,배포방법 다름...
ㄴafter : OS위에 도커만 설치하면 그 뒤부터는 도커위에 컨테이너 올려서 사용. 

이외에도 여러 장점이 있음.


요약하자면
1. 컨테이너의 장점이 워낙 뚜렷하기때문에 현재 IT 인프라는 컨테이너 기반이 표준이 됐다.

2. 근데 관리해야할 컨테이너가 너무 많아짐에따라 관리도구가 필요했고 그게 K8S다.

3. EKS는 aws에서 제공하는 k8s 서비스

 

2. EKS와 쿠버네티스의 차이

쿠버네티스 - 온프레미스에 직접 설치해서 사용해야함. 그래서 쿠버네티스에 필요한 구성요소들. 컨트롤플레인등을 직접 설치하고 관리해줘야한다.

EKS - 관리에 필요한 구성요소들을 제외(aws에서 관리해줌)하고 실제 서비스 운영에 필요한 요소(워커노드)만 관리하면 된다. 

컨트롤플레인은 물론 데이터플레인중 일부까지도 aws에서 관리

예를들어 l7 로드밸런싱에 엔진엑스 리버스프록시서버 1대, 실제 서비스 서버2대 총 3대가 있을경우

k8s - 3대 모두 직접 구성하고 관리해줘야함. 특히 리버스 프록시 서버 도구 설치부터 설정, 이후 관리(고가용성등)까지.

EKS - 실제 운영되는 서버 2대만 관리하면됨. 그 2대도 aws에서 구축(ec2)해준다. 관리만 하면됨.

 

꼭 k8s(eks)를 써야하나...?

꼭 그렇진 않다. 클러스터가 필요하지 않은 환경. 대표적으로 접속자가 일정하고 한대의 서버에서 충분히 커버가 가능하고 어느정도의 다운타임(업무시간에 30분정도 ?)이 허용되는 환경이라면 k8s를 굳이 사용할필요는 없다. 컨테이너 환경으로도 충분하다.

그럼에도 k8s를 구축하면 좋은점은 소유한 서버들의 리소스를 최대한 효율적으로 사용할 수 있다. 반대로 k8s를 도입함으로써의 쓰이는 인적자원도 있고... 잘 생각해보고 도입하면 된다. 도입할 여건(운영지식)만 된다면 실보단 득이 더 많다고 본다.

 

4. EKS 아키텍처

용어 설명

controlplane(master node) -  k8s 클러스터를 제어노드

dataplane(worker node) - 실제 데이터(파드,볼륨등)이 쌓이고 자원(cpu, 메모리)이 사용되는 노드

kubectl - 쿠버네티스 제어 도구. eks의 경우 컨트롤플레인 서버에 직접 접속이 안되니까 외부에 kubectl 설치해서 컨트롤플레인(마스터노드)의 api server로 명령을 전달한다.

Scheduler - 새로운 파드를 상황에 맞게 노드에 할당하는 역할

API server - 모든 컴포넌트들사이에서 api 서버 역할을 하며 통신해주는 역할

etcd - 데이터 저장소

controller manager - 클러스터 상태를 감시(오토힐링, 오토스케일링을위한)하고 작업을 수행하는 역할

kubelet - 워커노드의 상태 관리

kube-proxy - 네트워크 역할

 

아키텍처를 알아봤으니 어떻게 동작하는지도 알아보자. deployment로 파드를 배포했을때의 동작과정은 다음과같다.

1. 파드 생성 - 사용자가 kubectl run 명령어 타이핑 엔터

2. kubectl에서 컨트롤플레인(마스터노드)의 apiserver에 전달

3-1. apiserver가 etcd에 생성에 관한 정보(unscheduled pod)를 etcd에 저장

3-2. 동시에 apiserver가 etcd에 deployment에 포함된 replicas에 관한 정보를 etcd에 저장

4. scheduler는 unscheduled pod를 감지

5. 이후 scheduler가 어떤 워커노드에 배포할지 확인

6. 정해진 노드를 apiserver에 전달

7. api서버는 그 노드를 etcd에 또 저장

8. kubelet이 api서버에서 해당 노드에 파드를 배포

 

최대한 간략하게 적었고 자세한건.

https://blog.eunsukim.me/posts/understanding-how-kubernetes-works

참고

 

개인적으로 쿠버네티스, eks나 깃랩이나 여러 솔루션, 도구들을 관리하는 서버 관리자라면 해당 도구들의 아키텍쳐와 주요 컴포넌트들. 그 컴포넌트들이 어떤일을 왜 하는지정도는 알아야 한다고 생각한다.

허나 개발자들이 클라우드 환경에서 사용한다면 알면 좋겠지만 몰라도 괜찮다고는 생각한다.(그래야 우리같은 엔지니어가 밥벌이를 하니까..) 여튼 개발자분들은 다른 컴포넌트들은 몰라도 되는데 알아야할건 apiserver이다.

어떤 컴포넌트들이건 무조건 apiserver 와 통신을 한다. 심지어 외부에서 접근할때도 apiserver 서버와 통신을한다. 

온프렘에 설치한 k8s를 컨트롤하기 위한 kubeadmin 명령어대신 K8s.domain.co.kr/api 로도 통신이 가능하다.

 

5. EKS 구축방법

 

 

 

  • 기본 인프라 배포 - 링크 ← AWS CloudFormation 페이지로 연결되며, 파라미터 입력 후 스택 실행
  • CLI 를 이용하여 배포 (CLI를 사용하기 위해선 aws confiure 설정이 완료되어야 한다)
# yaml 파일 다운로드
curl -O <https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/**myeks-1week.yaml**>

# 배포
# aws cloudformation deploy --template-file ~/Downloads/myeks-1week.yaml --stack-name mykops --parameter-overrides KeyName= SgIngressSshCidr=/32 --region <리전>
예시) aws cloudformation deploy --template-file ~/Downloads/**myeks-1week.yaml** \\
     --stack-name **myeks** --parameter-overrides KeyName=**kp-gasida** SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 EC2 IP 출력
aws cloudformation describe-stacks --stack-name **myeks** --query 'Stacks[*].Outputs[*].OutputValue' --output text
예시) 3.35.137.31

# ec2 에 SSH 접속
예시) ssh -i  **ec2-user**@3.35.137.31
ssh -i **~/.ssh/kp-gasida.pem** ec2-user@$(aws cloudformation describe-stacks --stack-name **myeks** --query 'Stacks[*].Outputs[0].OutputValue' --output text)
  • IAM User 자격 증명 설정 및 VPC 확인 및 변수 지정
# 자격 구성 설정 없이 확인
**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 정보 확인
**aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq**
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq Vpcs[]
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq Vpcs[].VpcId
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" | jq -r .Vpcs[].VpcId
**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 | yh

****## 퍼블릭 서브넷 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
  • Amazon EKS 배포 실습
# 변수 확인
echo $AWS_DEFAULT_REGION
echo $CLUSTER_NAME
echo $VPCID
echo $PubSubnet1,$PubSubnet2

# 옵션 [터미널1] EC2 생성 모니터링
#while true; do 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 text ; echo "------------------------------" ; sleep 1; done
**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 클러스터 & 관리형노드그룹 배포 전 정보 확인
**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 --dry-run | yh**
...
**vpc**:
  autoAllocateIPv6: false
  cidr: 192.168.0.0/16
  clusterEndpoints:
    privateAccess: false
    publicAccess: true
  **id**: vpc-0505d154771a3dfdf
  manageSharedNodeSecurityGroupRules: true
  nat:
    gateway: Disable
  **subnets**:
    **public**:
      ap-northeast-2a:
        az: ap-northeast-2a
        cidr: 192.168.1.0/24
        id: subnet-0d98bee5a7c0dfcc6
      ap-northeast-2c:
        az: ap-northeast-2c
        cidr: 192.168.2.0/24
        id: subnet-09dc49de8d899aeb7
****
# eks 클러스터 & 관리형노드그룹 배포: 총 16분(13분+3분) 소요 
**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**
...
023-04-23 01:32:22 [▶]  setting current-context to admin@myeks.ap-northeast-2.eksctl.io
2023-04-23 01:32:22 [✔]  **saved kubeconfig as "/root/.kube/config"**
...

eks 클러스터 & 관리형노드그룹 배포 전

 

배포 완료

AWS CloudFormation 확인

 

AWS EKS 확인

 

Daemon Set pod를 통한 pod가 통신하는 ENI 확인

# 노드 IP 확인
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

# PrivateIP 변수 지정
N1=<PrivateIPAdd.myeks-myeks-nodegroup-Node1>
N2=<PrivateIPAdd.myeks-myeks-nodegroup-Node2>

# 노드 보안그룹 ID 확인
aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text)
echo $NGSGID

# 노드 보안그룹에 eksctl-host 에서 노드(파드)에 접속 가능하게 룰(Rule) 추가 설정
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32

# eksctl-host 에서 노드의IP나 coredns 파드IP로 ping 테스트
ping -c 2 $N1
ping -c 2 $N2

# kubelet, kube-proxy 통신 Peer Address 확인
ssh -i ~/.ssh/id_rsa ec2-user@$N1 sudo ss -tnp
ssh -i ~/.ssh/id_rsa ec2-user@$N2 sudo ss -tnp

# aws-node 데몬셋 파드 1곳에 bash 실행해두기 
kubectl exec daemonsets/aws-node -it -n kube-system -c aws-node -- bash

# 추가된 kubelet, kube-proxy 통신 Peer Address 확인
ssh -i ~/.ssh/id_rsa ec2-user@$N1 sudo ss -tnp
ssh -i ~/.ssh/id_rsa ec2-user@$N2 sudo ss -tnp

aws-node 데몬셋에 bash 실행 전

  • aws-node 데몬셋에 bash 실행 후
    • 실행 후 IP 확인을 해보면 추가된 연결정보를 확인할 수 있다.
      • 추가된 IP : 192.168.2.240

  • 추가된 연결된 정보(192.168.2.240)를 확인하기 위해 ENI를 확인
    • ENI가 추가로 생성되어있고 “소유자”와 “요청자”가 다르다.

  • 실습 완료 후 자원 삭제
# Amazon EKS 클러스터 삭제 (약 10분 소요)
eksctl delete cluster --name $CLUSTER_NAME

# 클러스터 삭제 완료 후 AWS CloudFormmation 스택 삭제
aws cloudformation delete-stack --stack-name myeks

'job > eks' 카테고리의 다른 글

eks 6주차  (0) 2023.05.31
eks 5주차  (0) 2023.05.23
4주차 eks 스터디  (0) 2023.05.16
3주차 eks 스터디  (0) 2023.05.13
eks 2주차  (0) 2023.05.02

gitlab 과 keycloak을 연동해서 인증시스템을 구축해볼것이다.

그전에 관련 용어 설명

keycloak - 오픈소스 IAM 솔루션

okta - 상용 IAM 솔루션

IAM(Identity and Access Management) - User ID, 암호를 통해 계정을 생성&관리하고 권한을 할당하여 접근제어(인증, 인가, 감사등)

즉 IAM이란 어떤 솔루션이나 프로토콜을 의믜하는게 아니고 보안행위 및 규칙등을 의미하고

IAM을 하기 위한 솔루션으로 keycloak 과 Okta등이 있는것.

더 자세히 들어가보면 IAM 솔루션들의 구성요소, 프로토콜이나 보안 정책등을 알아보자면

  1. SAML - 인증정보를 중앙 관리하여 한번의 디지털 서명(ID, PW)으로 설정돼있는 모든 리소스(SAML을 제공하는)에 접근 가능.
    로직은 다음과 같다.
    요청(사용자가 로그인 시도) > 검증(SAML과 IDP 연결) > 로그인(ID, Password 입력창 표시) > 토큰생성(사용자 정보 올바를시 SAML 토큰이 전송되어 서버에 로그인)

2. OAuth - 한번의 로그인(자격증명 성공)으로 얻은 토큰을 다른서비스에 로그인(인증)시 해당 토큰을 전달하는데 사용(ID,PW를 전달해주는게 아님). 단 조건은 사전에 IdP에서 사용자의 승인을 받아 리소스 서버에 토큰을 발행할수 있어야 함.

로직은 다음과 같다.

요청(사용자가 로그인 시도) > 선택(로그인 방법으로 타사 권한 인증 선택) > 로그인(인증 서버가 액세스 토큰 생성하여 리소스 서버에 전송) > 연결(리소스 서버가 토큰 확인후 접근 허용)

 

 

 

SAML과 OAuth의 유사점과 차이점은?

먼저 유사점은 SAML과 OAuth를 사용(한번의 로그인)으로 여러 서비스의 접근이 가능(SSO)해지는것.

예를들어 구글(IDP)계정을 가지고 깃랩도 로그인하고 페북도 로그인하고 플럼버도 로그인하고.. 이런것들이 SAML 혹은 OAuth를 가지고 SSO 기능을 구현했다라고 볼수있음.

차이점은 SAML=인증 / OAuth = 인가이다. SAML의 경우 구글(IDP)계정들의 아이디/패스워드들을 가지고 인증을 받아 다른 서비스에 로그인하는것.

OAuth는 구글에서 받은 토큰을 로그인 하려는 다른 서비스에 등록, 즉 권한을 허가 받아 다른서비스에 로그인하는것

로그인에 초점을 맞추면 둘중 하나만 사용하면 되고.

좀더 보안을 강화하려면 일단 로그인은 SAML(인증된자만)로 접근가능하도록 하고 그 서비스 로그인 이후의 하위서비스 접근은 OAuth(인가된자만)로 접근 가능하도록 처리하면 보안을 좀 더 강화할 수 있다

 

3. OIDC - 인증을 위해 OAuth에서 인증부분을 확장하여 구현된 프로토콜

 

OAuth에서 openid 스코프가 포함돼있으면 OIDC임. oauth 스코프에 openid가 있으면 oauth의 본래 목적인 액세스토큰(인가정보)외에 추가로 인증정보인 ID토큰(JWT)을 반환함.
##인증과 인가의 차이 : 인증 - 신원 검증 / 인가 - 권한 부여

즉 OIDC는 인가가 아닌 인증을 위해 OAuth에 인증이 확장된 개념이다.

앞의 내용을 다 이해했다면 OAuth와 OIDC를 각가 언제 사용해야 하는지 이해했을듯 하다.

 

그럼 둘다 인증이 목적인 SAML과 OIDC은 특징 및 장단점이 뭐고 각각 언제 쓰일까 ?

 

SAML - XML 기반, OIDC보다 상대적으로 느림, 메시지 크기가 큼, 20년전부터 널리 사용됐기에 스탠다드함. 따라서 기존 시스템과의 호환성이 높음. 기존 엔터프라이즈 환경에서 더 잘 어울려 왔음

 

OIDC - JSON 기반, 처리속도가 빠르고 사이즈가 작음. 최근(5~7년전)에 각광받고 있음. 여러 환경에서 사용됨.

장단점만을 본다면 OIDC가 더 좋아보이나 기존 엔터프라이즈 환경에서 SAML이 이미 많이 사용되고 있었기에 보안규정 및 지침등이 SAML에 맞게 표준화 돼 있었음. 따라서 아주 빡세게 보안규정을 지켜야한다면 OIDC보단 그에 맞는SAML을 채택하는게 맞고 그게 아니라면 OIDC가 더 유리하다고 판단됨

 

하여 아직까진 SAML을 많이 사용하나 OIDC도 많아지는 추세임.

 

그외 

SSO - 싱글사인온. 하나의 인증/인가로 해결하는것

IdP - identitiy provider. 즉 인증/인가정보를 제공하는곳.(구글, 카카오, 페이스북등)

JWT - Json웹 토큰 - 사용자 인증정보를 json 구조로 구성. OIDC가 JWT로 인증을 주고받음

 

karmada 란

  1. CNCF 프로젝트
  2. kubefed 단점 보완
  3. 클러스터간 리소스 공유&배포
  4. 워크로드 분산 가능
  5. 정책기반 배포 및 관리가 가능
  6. 클러스터간 고가용성 지원

아키텍쳐

  • 기본적인 통신 플로우는 k8s 통신과 동일하게
    api서버를 기반으로 통신
  • karmada의 컨트롤플레인이 각 클러스터의 API 서버와 통신하는 구조

 

리소스 배포 흐름

  1. PropagationPolicy 추가시 PolicyController가 Resource Binding 생성
  2. Resource Binding 시 Binding Controller가 Manifest 를 토대로 목적 클러스터에 Execution 생성
  3. Execution 생성시 Execution Controller가 목적 클러스터에 리소스를 배포

 

propagation policy란 
PropagationPolicy란 워크로드를 어떤 클러스터에 어떻게 분산할지 정의하는 규칙으로 멀티클러스터환경을 위해 진행되는 karmada의 핵심 개념

  • ClusterAffinity: ClusterName, Label, Field에 기반 스케쥴링
  • SpreadConstraint: 여러 클러스터에 고르게 분산
  • ReplicasScheduling: 워크로드복제본(동일 pod)을 여러 클러스터에 어떻게(Duplicated, Wdighted, Custom) 스케쥴링할지 결정
  • Toleration: 기존 k8s 스케쥴링기법을 그대로 사용

 

karmada 설치 방법

git clone https://github.com/karmada-io/karmada.git; karmada/hack/local-up-karmada.sh

karmada join CLUSTER_NAME --cluster-kubeconfig=/path/to/your/cluster-kubeconfig.yaml

 

 

karmada 를통해 리소스 배포 방법

placement:

    clusterAffinity:

      clusterNames:

        - member1

        - member2

를 통해서 배포할 클러스터를 정해준다 - clusterAffinity 방식 weighted 를 기준으로 분산하는데 target cluster 모두 가중치는 1로 줬기에 균등분산하여 nginx 애플리케이션을 member1,2 각각의 클러스터에 분산 배치하여 고가용성 및 부하분산 향상

pod - 컨테이너, 라벨, 노드스케쥴등으로 설정해서 컨테이너 실행, 파드에 메모리 사용량 미리 적어줄수있음. 안적어주면 문제생김 - 메모리 부족시 다른 파드의 메모리를 사용하여 애플리케이션이 불안정할거나 파드를 스케쥴링할때 마스터노드에서 얼마나 할당해야하는지 메모리 요구사항을 모르기때문에 파드가 다른 노드에 할당되지 않을수있음 마지막으로 자원 낭비

ㄴ라벨 - 키/밸류 형식의 라벨

ㄴ노드스케쥴 - 파드는 노드위에 올라가는데 노드를 직접 지정하거나 아니면 K8S 에서 자동으로 

리밋 - CPU- 낮춤, 메모리-파드 종료

 

 

서비스 - 파드는 재시작하면 아이피가 변경되기때문에 서비스 달아서 파드 연결해줘야함.

ㄴ클러스터아이피 - 외부에서는 접근안되고 클러스터 내부에서만 아이피로 접근 가능함.

ㄴ노드포트 - 클러스터아이피 기능 + 파드가 있는 노드에만 포트가 추가되는게 아니고 파드가 없는 노드까지. 모든 노드에 포트 할당함

ㄴ로드밸런서 -  앞단에서 로드밸런싱으로 파드에 접근되도록(NGINX)

 

 

볼륨 - 

EMPTY DIR - 컨테이너들끼리 데이터를 공유하기 위해 사용, 파드 생성시 같이 생성되고 지우면 같이 지워짐

HOST PATH - 경로 지정해서 사용되며 노드에 저장됨, 파드 재생성시 다른노드에 생성되면 당연히 볼륨 사용불가. 다만 노드별로 마운트해줌 사용은 가능

PV/PVC - pod > pvc > pv > volume 형식으로 연결

 

 

config map - 환경변수 (이미지 1개에 환경변수를 준다) / secret - 보안 환경변수 secret(base64)으로 설정(메모리에 저장됨)

 

namespace - 서비스&파드집합소, 네임스페이스끼리 공유안됨(pv등은 공유됨). 삭제하면 그안의 서비스도 전부 삭제됨

리소스쿼타 - 네임스페이스별로 리소스 쿼타 적용해서 더 올라가지 않도록 한다

리밋 레인지 - 네임스페이스별로 리소스 리밋을 적용해서 넘을경우 파드가 해당 네임스페이스에 들어가지 않도록 한다

 

 

컨트롤러 - 오토힐링, 오토스케일링, 소프트웨어업데이트, 잡등  오케스트레이션 기능들

 

리플리카셋 - 오토힐링,오토스케일링, 자원할당(파드할당) 기능 

 

탬플릿 - 탬플릿에 설정돼있는대로 오토힐링함, 이걸로 버전업그레이드 가능

replicas - 스케일인, 스케일아웃. 

셀렉터 - 파드 지정해서 실행

 

deployment - 파드 배포 방식

recreate - 기존 파드 삭제하고 새로운 파드 생성, 다운타임 발생

롤링 업데이트 - 기존 파드 그대로 두고 새로운 파드 생성후 기존 파드 삭제, 생성/삭제하는 시간동안 기존 파드로 접근되는 문제가 있음. 다운타임 없음.

블루/그린 - 리플리카스를 이용하여 새로운 파드 생성후 서비스를 새로운 파드로 보도록 라벨 변경. 다운타임없고 기존파드로 접근되지도 않음. 롤백도 쉬움. 가장 많이 사용

카나리 배포 - 새버전의 파드를 생성후 일부 트래픽을 새버전의 파드로 가도록하여 테스트.

 

데몬셋 - 모든 노드에 파드가 하나씩 생성. 프로메테우스처럼 모든 노드에 설치되야할 서비스들 설치할때 사용

잡/크론잡 - 임시로 사용할 파드들. 프로세스가 실행되지 않으면 파드 종료.(삭제되는게아님)

 

파드의 라이프사이클 - 파드나 컨테이너의 state 확인

 

서비스 - headless, endpoint, external name

ingress - 로드밸런싱이나 카나리 업그레이드 하기 위해

 

네트워크 - 파드네트워크와 서비스 네트워크, 실 서버의 네트워크를 다르게 줘야함.

 

developer commit
형상&버전관리 - gitlab

build / test / package - jenkins : gitlab에 있는 소스를 불러와서 build하고 test하고 package 한다. 단위test 통과 못하면 fail되어 개발자에게 알람. 단위테스트 통과하면 dev&prod서버에 배포 진행

정적테스트 및 분석 : 소나큐브

IaC, 빌드된 결과물을 운영환경에 배포 : ansible

운영환경 : 쿠버네티스

이런 일련의 과정을 CI/CD 파이프라인

 

젠킨스 설치는 도커 컴포즈로 젠킨스 플러그인 전체 설치

 

jenkins-item이란 : 작업(빌드,배포)최소단위

 

jenkins git plugin 설치

아이템별 git 레포 설정

jenkins maven plugin 설치

메이븐으로 컴파일 - clean compile package

 

jenkins > docker 방법

1) publish over SSH

 

S3란 스토리지 서비스

- 안전하고 가변적인 Object 저장공간을 제공 (ex 구글 클라우드 드라이브, 네이버 클라우드 드라이브)

- 편리한 UI 인터페이스를 통해 어디서나 쉽게 데이터를 저장하고 불러올 수 있음

- 파일 크기는 0KB부터 5TB까지 지원

- 저장공간 무제한

- Bucket 이라는 이름을 사용함(디렉토리와 유사)

- Bucket 은 보편적인 namespace를 사용 : 버킷 이름은 고유해야한다. 리전 상관없이(글로벌설정임)하나의 버킷은 모든 리전에서 고유해야함.

 

S3 object의 구성요소

- Key
- Value

- Version ID

- Metadata

- CORS(Cross Origin Resource Sharing) : 한 버킷의 파일을 다른 버킷에서 접근되도록 해주는 기능(리전 달라도 가능)

 

S3 Data Consistency Model

 - Read after Write Consistency(put) : 파일 업로드시 딜레이없이 바로 사용 가능
- Eventual Consistency(update, delete) : 버킷에 올라간 파일을 update&delete시 결과가 바로 나타나지 않는다. 다만 S3 내부에서는 적용돼있음?

 

S3 스토리지 타입

 - 일반 S3 : 가장 보편적으로 사용되는 스토리지 타입. 높은내구성과 가용성

 - S3 - IA(Infrequent Access) : 자주 접근되지는 않으나 빠른 접근이 요구되는 파일이 많을시 유용, 일반 S3에 비해 비용은 저렴하나 접근시 추가 비용 발생. 멀티 AZ를 통한 데이터 저장으로 가용성이 높음

 - S3 One Zone IA : 단일 AZ에 저장. 상대적으로 낮은 가용성. 데이터 접근시 S3-IA보다 20프로 비용 저렴

 - Glacier : 거의 접근하지 않을 데이터 저장시 유용. 매우 저렴한 비용. 데이터 접근시 대략 4-5시간 소요

 - Intelligent Tiering : 데이터 접근 주기가 불규칙할때 매우 유용, 2가지 티어 존재[Frequent Tier / Infrequent Tier]. 데이터 접근주기에 따라 두가지 티어중 하나로 선택됨. Frequent Tier가 비용이 더 비쌈. 최고의 비용 절감 효율을 누릴 수 있음

 

 

rds는 AWS의 RDBMS 서비스

 

RDS 백업의 종류

 

Automated Backups (자동 백업)

1. Retention period(Point in TIme) - 1~35일 안의 어떤 시간으로 돌아가게 할 수 있음

2. AB는 그날 생성된 스냅샷과 Transaction logs(TL)을 참고함

3. 디폴트로 AB기능이 설정되어 있으며 백업정보는 S3에 저장. RDS 용량 만큼만 S3 무료제공, 그 이상되면 과금

4. AB동안 약간의 I/O suspension이 존재할 수 있음 > Latency

 

DB snapshots (데이터베이스 스냅샷)

1. 주로 사용자에 의해 실행됨

2. 원본 RDS instance를 삭제해도 스냅샷은 존재함. 즉 스냅샷만으로도 RDS 인스턴스 복원가능

 

DB restore할 경우

새로운 객체가 생성됨. 

 

Multi AZ 

 - 원래 존재하는 RDS DB에 무언가 변화(write)가 생길 때 다른 AZ에 똑같은 복제본이 만들어짐

 - AWS에 의해 자동으로 관리가 이루어짐

 - 원본 RDS DB에 문제가 생길 시 자동으로 다른 AZ의 복제본이 사용됨

 - DR 용

 - Multi AZ는 성능 개선용이 아님. 성능 개선은 Read Replica를 써야함

 

Read Replicas(RR)

 - 읽기전용 복제본이 생성됨(mysql replcation이라 생각하면 됨)

 - read가 많으면 read replica 사용하면 됨( 스케일아웃가능)

 - 이건 DR용도가 아님

 - 최대 5개까지 RR 가능 - 다만 RR DB의 RR을 다시 생성가능(단 latency 발생) 이럼 무제한 ?

 - 각각의 RR은 자기만의 고유 endpoint 존재 - 아이피로 구분하지 않음

 

ElasticCache

 - 클라우드 내에 In-memory 캐싱디비

 - 데이터베이스에서 데이터를 읽어오는것이 아니라 캐시에서 빠른 속도로 데이터를 읽어옴

 - Read-Heavy 어플리케이션에서 상당한 Latency 감소 효과

 

엘라스틱캐시는 2개 타입으로 나뉨 Memcached / Redis

Memcached

 - Object 캐싱 시스템

 - Elastic cache는 Memcached의 프로토콜을 디폴트로 따름

 - 오토 스케일링 가능

 - 오픈소스

 - 단순한 캐싱 모델

 

Redis

 - Key-Value, set, list와 같은 형태의 데이터를 In memory 캐싱 모델

 - 오픈소스

 - Multi AZ 지원(DR 가능)

 - 리더보드처럼 데이터셋의 랭킹을 정렬하는 용도

 

 

---------실습------------

RDS 생성 - mysql 생성

1.버전 선택(디폴트)

2.탬플릿

프로덕션, 개발/테스트, 프리티어 > 선택

PS. 오로라는 프리티어 없음.

3.인스턴스 식별자 - AWS RDS 인스턴스 이름

4.마스터(root) 사용자 이름/ 암호 생성

5.DB 인스턴스 크기 > 디폴트 선택(프리티어니까)

6.스토리지 유형, 용량 > 디폴트

7.스토리지 자동조정이란 오토스케일링이고 디폴트는 활성화

8.가용성 및 내구성이란 멀티 AZ. 프리티어는 멀티 AZ 사용 못함

9.VPC 연결

10.추가 연결 구성. RDS인스턴스의 접근 허용여부. 퍼블릭 엑세스 디폴트 아니오.

11.VPC의 새로운 보안그룹 생성 해야함 ?

12. 가용영역 설정은 뭐하는거지 ? 자동설정 아니였나 ?

 

 

Read replicas 생성( 읽기전용 복제본 생성)

1.네트워크 및 보안

ㄴ완전히 다른 지역으로 복제본 생성 

ㄴ혹은 다른 서브넷 그룹

퍼블릭 액세스 (외부 공개 여부)

2.암호화 : 원본 데이터베이스를 암호화하지 않아도 RR의 암호화를 가능하게 해줌

3.모니터링 : RR의 로그 정보를 Cloud watch에 보낼지에 대한 설정

 

Multi AZ 

1.수정후 다중 AZ 배포 활성화(예)

 

 

EC2 요금 지불 방법

On-demand : 시간 단위로 가격이 고정되어 있음. 탄력 요금제라 생각하면 됨

 

Reserved : 한정된 EC2 용량 사용 가능, 1-3년동안 시간별로 할인 적용 받을 수 있음. 할당 요금제

 

Spot : 입찰 가격 적용, 가장 큰 할인률을 적용받으며 특히 인스턴스의 시작과 끝기간이 전혀 중요하지 않을때 매우 유용.경매방식. 실 서비스에는 쓰면 안됨

 

 

 

EBS란 - EC2에 부착되는 하드디스크. EC2를 사용하기 위해서는 EBS라는 디스크 볼륨이 필요

 - 저장공간이 생성되어지며 EC2 인스턴스에 부착된다.

 - 디스크 볼륨 위에 File System이 생선된다.

 - EBS는 특정 Aailability Zone에 생성된다.
AZ란 : 하나의 리전안에 여러개의 AZ가 존재하고 유사시 다른 AZ 사용가능.

 

EBS 볼륨타입

1.SSD군

ㄴGeneral Purpose SSD(GP2) : 최대 10K IOPS를 지원하며 1GB당 3IOPS속도. 보편적으로 사용됨
ㄴProvisioned IOPS SSD(IO1) : 극도의 I/O률을 요구(ex:매우 큰 DB관리)환경에서 주로 사용. 10K이상의 IOPS지원. 비싼만큼 속도 빠름.

 

2.Magnetic/HDD군

ㄴThrouhput Optimized HDD(ST1) : 빅데이터 웨어하우스, log 프로세싱에 주로 사용, Boot volume으로는 사용 불가능

ㄴCDD HDD (SC1) : 파일 서버와 같이 드문 Volume 접근시 주로 사용. Boot volume으로 사용 불가능하나 매우 저렴함

ㄴMagnetic (standard) : 디스크 1GB당 가장 쌈 . Boot volume 가능.

 

 

ELB란 Elastic Load Balancers
AWS의 로드밸런싱해줌.

Traffic의 흐름을 Unhealty instance를 healthy instance로 고가용성 제공해줌

 

ELB의 종류

ㄴALB : L7 로드밸런싱. http, https 로드밸런싱가능. 커스터마이징 라우팅 설정을통하여 특정 서버로 요청 보낼 수 있음
ㄴCLB : 거의 사용안하는 레거시 로드밸런싱. L4, L7 둘다 지원. 근데 사용 안함.(레거시)

ㄴNLB : L4 로드밸런싱. 매우 빠름. 극도의 퍼포먼스가 요구되는 TCP 트래픽을 처리. 

 

 

X-Forwarded-for 헤더 > 실제 아이피를 찍어주는것

 

 

 

Route53 : DNS 서비스

1. 도메인을 먼저 등록해주고

2. 해당 도메인들의 레코드 설정

3. 만약 도메인이 이미 있는거면 따로 등록해야 함.

 

 

-------

EC2 생성 실습

EC2는 리전 선택해야함

생성하는법 - 이건 또깡한테 듣는게 더 좋음

AWS-VPC&EC2 생성 메뉴얼

VPC(클라우드상에서 고객에게 지원되는 개인 사설 네트워크망)는 특별한경우(전혀 다른 용도의 AWS 네트워크 구성등)가 아니라면 리전별로 1개만 있으면 된다. 따라서 추가 EC2인스턴스 생성시 VPC 생성(1.AWS 네트워크 구성)은 건너뛰고 Public, Private, DB Netmask에 맞게끔 EC2를 할당

 

NATGW : 외부 > 내부 불가능, 내부 > 외부 가능
IGW : 외부 > 내부 가능, 내부 > 외부 가능

  1. AWS 네트워크 구성
    A)VPC 생성

    B)서브넷 - 서브넷은 보통 퍼블릭, 프라이빗, DB로 나눈다.
    [퍼블릭 : bastion, NATGW, ALB|NLB]
    [프라이빗 : EC2, WEB, WAS]
    [DB : DB서버]

    C)IGW 생성 후 VPC에 attach

    D)EIP 2개 생성(1개는 bastion, 1개는 NATGW용도)

    E)NATGW 생성 - EIP 선택, 서브넷 선택

    F)라우팅 테이블을 설정
    ㄴ퍼블릭은 IGW로 트래픽 나가도록 라우팅
    ㄴ프라이빗,DB는 NATGW로 트래픽 나가도록 라우팅
    F-1)서브넷연결을 통해 서브넷끼리 라우팅
    ps.
    VPC 생성시 디폴트로 생성되는 라우팅 테이블은 기본 라우팅 테이블로 설정 돼 있음. 이를 새로운 라우팅 테이블을 생성한 뒤 새로 생성한 라우팅 테이블을 기본 라우팅 테이블로 변경해야만 삭제가 가능

     
  2. EC2 생성

    A)bastion용 EC2 생성 - Keypair는 구글드라이브의 B/P 폴더에 보관
    B)bastion용 EC2에 EIP 연결
    C)service 용 EC2 생성
    D)앞서 생성한 ALB에 service EC2를 연결
    E)보안그룹에서 IP 허용
    F)접속 [root@proxy0 dhsa]# ssh -i Keypair-California-Bitmango.pem ec2-user@54.193.180.216


  3. standby용으로 생성된 인스턴스들은 사용하지 않을 때 꼭 인스턴스 중지(종료가 아님, 종료시 인스턴스 삭제됨)를 해야함




  1. 참고 내용
    base img별 ssh 계정
    ubuntu - ubuntu / centos - centos / windows - administrator / amazon linux - ec2-user
    EC2내 OS에 퍼블릭 키가 있고 그에 맞는 프라이빗 키가 발급됨 이거 잃어버리면 안됨




 

'job > public cloud' 카테고리의 다른 글

AWS스터디 - S3  (0) 2022.11.15
aws 스터디 - rds  (0) 2022.11.14
AWS 스터디 - IAM  (0) 2022.11.14
aws SAA(AWS Certified Solutions Architect - Associate) 불합격 & 합격 후기  (6) 2020.05.12
공부한거 대충 정리  (0) 2018.10.11

1. IAM - 유저를 관리하고 접근 레벨 및 권한에 대한 관리

1-1) Root 유저 하위의 일반 유저(A라 가정)를 생성하고 해당 유저에대한 접근키(Access key)와 비밀키(Secret Access Key)를 관리

1-2) 매우 세밀한 접근 권한 부여 기능(Granular Permission) - 회계 부서에 새로 입사한 유저 A에게 EC2의 회계 관련 서버에만 권한을 줘야함

1-3) 비밀번호를 일괄적으로 변경토록 강제 가능

1-4) 다중 인증 MFA(Multi-Factor Authentication)기능 제공

 

정책 - Json 형태의 document 로 돼 있음.  그룹 또는 역할에 추가 할 수 있음

역할 - 하나 혹은 다수의 정책을 추가

 

IAM은 regional하지 않고 global 설정임

 

IAM 정책 시뮬레이터 - IAM정책을 production에 빌드하기전 테스트

 

실습

사용자 생성

1. 엑세스 유형 - 둘다 선택도 가능
ㄴ프로그래밍 방식 엑세스 : 접근키&비밀키를 통해 접근하는 방식

ㄴAWS management console 엑세스 : 비밀번호 부여하는 방식

 

2. 그룹에 추가하거나 혹은 단독 유저로.

 

3. 액세스키와 비밀 액세스키를 확인 가능. 비밀 엑세스키의 경우 처음 한번만 확인 가능하니까 혹시 분실한다면 비밀 엑세스키 재 생성해야함.

 

그룹 생성

1. 그룹에 정책 추가 가능

2. 그룹에 유저 추가 가능

 

역할 - 다양한 정책을 부여할 수 있음. 정책과 비슷하나 유저에게 부여

 

정책

정책 생성에는 2가지 방법이 있다.

1. 원하는 서비스를 webUI로 선택하여 엑세스레벨을 부여할 수 있음
ㄴ리소스를 선택하는데 리소스란 다양한 기능&펑션들(백업, index등). 모든 리소스 선택

 

2. JSON 형식으로 엑세스 부여할 수 있음

 

IAM Policy Simulater 도 있음

 

구성

Service1.domain.co.kr 에 접속할 경우

Service1.domain.co.kr 도메인의 dns설정은 cname proxy.domain.co.kr(nginx proxy서버) 으로 해놓고

Service1.domain.co.kr > nginx proxy(nginx proxy서버) > server:service1 port(docker)

상황 : 여러 서비스에서 간헐적으로 500 Server internal Error 발생. 새로고침하면 정상화. 새로고침하면 정상화 되기에 서비스 이용에는 크게 문제가 없으나 API 이용시 500 error 발생으로 스크립트 오작동하는경우 발생.

 

사내 모니터링 툴인 icinga2(nagios기반)에서는 에러 발생시 1분뒤 다시한번 체크하도록 설정 돼 있기에 icinga2에서는 서비스 에러 체크가 안돼고 있었음.
다만 debug 모드에서는 500 error를 확인 할 수 있다. icinga2로그 확인시 같은시간에 여러 서비스에서 동시에 발새하는것으로 보아 어플리케이션 단일의 문제는 아닐것으로 보인다.

 

그럼 서비스들이 모여있는 web1, 또는 intra1 그리고 DB서버, proxy 정도의 문제로 의심해볼 수 있을듯 하다. 여기서 확인해볼 수 있는것은 최종 목적지인 web1, intra1 모든 서버에서 에러가 발생하고 있고 특히 xlsoft의 경우 디비 설정도 안돼있기에 디비 문제도 아님이 확인된다.

proxy0에 있는 munin, icinga2 는 에러가 없는것으로 보아 proxy1 서버에서 발생하는 문제라 추측하고 원인 파악할 예정

우선 접속자가 가장 적은 test.co.kr 을 모니터링 할꺼고 1.1.1.1(proxy1)과 통신을 전혀 하지 않는 standby1에서 tcpdump를 뜬다. 그래야 딱 test.co.kr 꺼만 tcpdump가 가능하니까

#!/bin/bash
function check_fnc(){
CMD=`docker exec -it compose.icinga2.test.co.kr /usr/lib/nagios/plugins/check_http -I 1.1.1.1 -S -u <https://www.test.co.kr/images/xlsoftlogo.png`>
CRI=`echo $CMD | awk '{print $2}'`
date > /etc/bin/date.txt
}

check_fnc
while [ $CRI != "CRITICAL:" ]
do
check_fnc
sleep 2
done

위 스크립트를 돌려놓기.

tcpdump 떠놓고 위 스크립트로 500 error 발생할 때까지 스크립트 돌려놓기

1) 11:24:11.764227 IP standby1.58272 > 1.1.1.1.https: Flags [S], seq 1285422234, win 29200, options [mss 1460,sackOK,TS val 2345513401 ecr 0,nop,wscale 9], length 0
2) 11:24:11.764640 IP 1.1.1.1.https > standby1.58272: Flags [S.], seq 3917766911, ack 1285422235, win 28960, options [mss 1460,sackOK,TS val 2362302169 ecr 2345513401,nop,wscale 9], length 0
3) 11:24:11.764706 IP standby1.58272 > 1.1.1.1.https: Flags [.], ack 1, win 58, options [nop,nop,TS val 2345513401 ecr 2362302169], length 0
4) 11:24:11.766695 IP standby1.58272 > 1.1.1.1.https: Flags [P.], seq 1:280, ack 1, win 58, options [nop,nop,TS val 2345513403 ecr 2362302169], length 279
5) 11:24:11.766881 IP 1.1.1.1.https > standby1.58272: Flags [.], ack 280, win 60, options [nop,nop,TS val 2362302208 ecr 2345513403], length 0
6) 11:24:11.769720 IP 1.1.1.1.https > standby1.58272: Flags [.], seq 1:5793, ack 280, win 60, options [nop,nop,TS val 2362302211 ecr 2345513403], length 5792
7) 11:24:11.769838 IP standby1.58272 > 1.1.1.1.https: Flags [.], ack 5793, win 80, options [nop,nop,TS val 2345513406 ecr 2362302211], length 0
8) 11:24:11.769977 IP 1.1.1.1.https > standby1.58272: Flags [P.], seq 5793:6028, ack 280, win 60, options [nop,nop,TS val 2362302211 ecr 2345513403], length 235
9) 11:24:11.770009 IP standby1.58272 > 1.1.1.1.https: Flags [.], ack 6028, win 86, options [nop,nop,TS val 2345513406 ecr 2362302211], length 0
1) 011:24:11.770843 IP standby1.58272 > 1.1.1.1.https: Flags [P.], seq 280:406, ack 6028, win 86, options [nop,nop,TS val 2345513407 ecr 2362302211], length 126
11) 11:24:11.800062 IP 1.1.1.1.https > standby1.58272: Flags [P.], seq 6028:6079, ack 406, win 60, options [nop,nop,TS val 2362302241 ecr 2345513407], length 51
12) 11:24:11.800284 IP standby1.58272 > 1.1.1.1.https: Flags [P.], seq 406:553, ack 6079, win 86, options [nop,nop,TS val 2345513437 ecr 2362302241], length 147
13) 11:24:11.818999 IP 1.1.1.1.https > standby1.58272: Flags [P.], seq 6079:6433, ack 553, win 62, options [nop,nop,TS val 2362302260 ecr 2345513437], length 354
14) 11:24:11.819120 IP 1.1.1.1.https > standby1.58272: Flags [FP.], seq 6433:6464, ack 553, win 62, options [nop,nop,TS val 2362302260 ecr 2345513437], length 31
15) 11:24:11.819208 IP standby1.58272 > 1.1.1.1.https: Flags [.], ack 6465, win 91, options [nop,nop,TS val 2345513456 ecr 2362302260], length 0
16) 11:24:11.819235 IP standby1.58272 > 581.1.1.1.https: Flags [F.], seq 553, ack 6465, win 91, options [nop,nop,TS val 2345513456 ecr 2362302260], length 0
17) 11:24:11.819447 IP 1.1.1.1.https > standby1.58272: Flags [.], ack 554, win 62, options [nop,nop,TS val 2362302261 ecr 2345513456], length 0

이게 500 에러일 때 tcpdump

1) 16:14:47.778543 IP standby1.42750 > 1.1.1.1.https: Flags [S], seq 387201813, win 29200, options [mss 1460,sackOK,TS val 2276549415 ecr 0,nop,wscale 9], length 0
2) 16:14:47.778893 IP 1.1.1.1.https > standby1.42750: Flags [S.], seq 1673702661, ack 387201814, win 28960, options [mss 1460,sackOK,TS val 2293338207 ecr 2276549415,nop,wscale 9], length 0
3) 16:14:47.778973 IP standby1.42750 > 1.1.1.1.https: Flags [.], ack 1, win 58, options [nop,nop,TS val 2276549415 ecr 2293338207], length 0
4) 16:14:47.781420 IP standby1.42750 > 1.1.1.1.https: Flags [P.], seq 1:280, ack 1, win 58, options [nop,nop,TS val 2276549418 ecr 2293338207], length 279
5) 16:14:47.781666 IP 1.1.1.1.https > standby1.42750: Flags [.], ack 280, win 59, options [nop,nop,TS val 2293338210 ecr 2276549418], length 0
6) 16:14:47.783826 IP 1.1.1.1.https > standby1.42750: Flags [P.], seq 1:6028, ack 280, win 59, options [nop,nop,TS val 2293338212 ecr 2276549418], length 6027
7) 16:14:47.783933 IP standby1.42750 > 1.1.1.1.https: Flags [.], ack 6028, win 81, options [nop,nop,TS val 2276549420 ecr 2293338212], length 0
8) 16:14:47.785036 IP standby1.42750 > 1.1.1.1.https: Flags [P.], seq 280:406, ack 6028, win 81, options [nop,nop,TS val 2276549421 ecr 2293338212], length 126
9) 16:14:47.785568 IP 1.1.1.1.https > standby1.42750: Flags [P.], seq 6028:6079, ack 406, win 59, options [nop,nop,TS val 2293338214 ecr 2276549421], length 51
10) 16:14:47.785670 IP standby1.42750 > 1.1.1.1.https: Flags [P.], seq 406:553, ack 6079, win 81, options [nop,nop,TS val 2276549422 ecr 2293338214], length 147
11) 16:14:47.788991 IP 1.1.1.1.https > standby1.42750: Flags [P.], seq 6079:10659, ack 553, win 61, options [nop,nop,TS val 2293338217 ecr 2276549422], length 4580
12) 16:14:47.789028 IP 1.1.1.1.https > standby1.42750: Flags [FP.], seq 10659:10690, ack 553, win 61, options [nop,nop,TS val 2293338217 ecr 2276549422], length 31
13) 16:14:47.789149 IP standby1.42750 > 1.1.1.1.https: Flags [.], ack 10659, win 99, options [nop,nop,TS val 2276549426 ecr 2293338217], length 0
14) 16:14:47.789189 IP standby1.42750 > 1.1.1.1.https: Flags [F.], seq 553, ack 10691, win 99, options [nop,nop,TS val 2276549426 ecr 2293338217], length 0
15) 16:14:47.789349 IP 1.1.1.1.https > standby1.42750: Flags [.], ack 554, win 61, options [nop,nop,TS val 2293338218 ecr 2276549426], length 0

이건 정상일 때 tcpdump

  1. SYN-SENT - standby1에서 proxy1로 Syn을 보냄 : 내가(standby1) 너(proxy1)한테 접속 해도 돼 ?
  2. SYN-RECEIVE - proxy1에서 syandby1한테 Syn에 대한 응답(Ack) : 너(standby1)한테 Syn 잘 받았어 (ACK) 나 준비완료, 들어와도돼 (Syn.), tcpdump에서는 Ack를 .으로 표현
  3. ESTABLISHED - standby1에서 proxy1로 Ack 보냄 : 나(standby1) 랑 너(proxy1) 연결됐어 1~3까지가 3WAY 핸드쉐이크
  4. SSL 핸드쉐이크 부분 - https://run-it.tistory.com/29 이거나 https://lxxyeon.tistory.com/176 이거를 4~9 까지 요거 참고
  5. SSL 핸드쉐이크 부분 - https://run-it.tistory.com/29 이거나 https://lxxyeon.tistory.com/176 이거를4~9 까지 요거 참고
  6. SSL 핸드쉐이크 부분 - https://run-it.tistory.com/29 이거나 https://lxxyeon.tistory.com/176 이거를4~9 까지 요거 참고
  7. SSL 핸드쉐이크 부분 - https://run-it.tistory.com/29 이거나 https://lxxyeon.tistory.com/176 이거를4~9 까지 요거 참고
  8. SSL 핸드쉐이크 부분 - https://run-it.tistory.com/29 이거나 https://lxxyeon.tistory.com/176 이거를4~9 까지 요거 참고
  9. SSL 핸드쉐이크 부분 - https://run-it.tistory.com/29 이거나 https://lxxyeon.tistory.com/176 이거를4~9 까지 요거 참고
  10. SSL 암호화된 데이터 전송 https OK
  11. SSL 암호화된 데이터 전송 https OK
  12. FIN_WAIT1 - proxy1에서 standby1한테 접속 끊자는(FIN) 패킷 발송 : 나(proxy1)는 너(standby1)한테 볼일 다 봤어. 이제 끊자(FIN)
  13. CLOSE_WAIT - standby1이 proxy1한테 Fin에 대한 응답(Ack) : 그럴래 ? 잠깐만~ 너가 볼일 다본 프로세스 종료준비좀 할께. 기다려봐
  14. LAST_ACK - standby1이 proxy1한테 FIN 패킷 발송 : 나(standby1)는 프로세스 종료할 준비 됐어 ~ 너 준비 됐어 ?
  15. TIME_WAIT - proxy1이 standby1한테 종료에 대한 응답 .(ACK) 발송 : 응. 나 끊었어

정상 / 비정상과 딱히 차이 없음.

비정상일때 패킷2개가 더 있긴한데 이거는 SSL 통신할 때 인증서 확인을 한패킷에서 했냐 한번 나눴냐 차이. 즉 패킷이 끊기는건 없다. 따라서 timeout은 아니라는 말.
ㄴ간헐적 장애는 거의 대부분이 timeout문제(경험)임. 네트워크랑 앤드포인트나 애플리케이션단 타임아웃이 서로 달라서 발생하는등. 근데 이 문제는 타임아웃 문제 아님. 그럼 백프로 nginx proxy서버문제가 맞다고 보여진다.

nginx proxy 에러로그를 다시한번 살펴보면..

에러 발생한 시간에 bind(0.0.0.0) failed (98: Address already in use) while connecting to upstream, 이게 찍혀있다. 포트 중복되는 문제로 많이 보던 에러라 보고서 누가 포트 잘못 올렸나 하고 넘어갔떤건데... 이제보니 포트가 안찍혀있다.
그리고 생각해보면 포트 중복문제면 nginx 재시작등 서비스 올라올 때 포트 중복되면 찍혀야 하는건데 이건 라이브중에 찍힌 에러.. 이 에러가 문제가 맞는것으로 보인다.

혹시나 icinga2에서 에러 발생한 시간에 맞춰보니 전부 그시간에 위 에러가 찍혀있다. 물론 icinga2는 체크를 실시간으로 하는게 아니니까 백프로 일치하진 않지만 nginx에 찍힌 에러 로그안에 아이싱가 로그 시간을 포함하고 있다.

bind(0.0.0.0) failed (98: Address already in use) while connecting to upstream 이처럼 포트가 안찍히는 경우는 해당 서버가 클라이언트입장에서 서버로 로그인 할 때 클라이언트 포트가 이미 사용중이라 해당 에러가 발생할 수 있다. > nginx proxy가 실서비스(도커의 :28080)

 

[root@proxy1 ~]# cat /proc/sys/net/ipv4/ip_local_port_range

1024 65535

[root@proxy1 nginx]# netstat -an |grep TIME_WAIT | grep -v 1.1.1.1:443 | grep -v 1.1.1.1:80 | awk '{print $4}'| awk -F ":" '{print $2}' | sort -n | head

1024

1025

1026

1027

1030

[root@proxy1 nginx]# netstat -an |grep TIME_WAIT | grep -v 1.1.1.1:443 | grep -v 1.1.1.1:80 | awk '{print $4}'| awk -F ":" '{print $2}' | sort -n | tail

65524

65525

65528

65529

65535

[root@proxy1 nginx]# netstat -an |grep TIME_WAIT | grep -v 1.1.1.1:443 | grep -v 1.1.1.1:80 | wc -l 44562

실제로 클라이언트 포트를 1024부터 뛰엄뛰엄 65535까지 상당히 많이 쓰고 있지만 이론상으로 현재 문제는 발생하면 안된다.(6만개도 안넘었으니까)

[root@proxy1 log]# cat /home/var/proxy1.datawave.co.kr/logs/error.log | grep 'Address already in use' | awk '{print $2}' | awk -F ":" '{print $1":"$2}' | sort | uniq -c | sort -rn | head -n 100

10743 05:29

10472 05:28

10092 05:43

10070 06:29

8481 05:51

8263 05:49

8252 05:48 중략

6081 05:26

6065 05:08

새벽 5~6시에 집중돼있음

netstat -an 카운트 새벽에 계속 돌렸을 때 몇개까지 차는지 보면 이게 원인인지 아닌지 확인 할 수 있음

만약 클라이언트 포트 고갈 문제가 맞다면

log3의 8080(fastlog)와 통신을 가장 많이하는데 이거 1회성 연결로 끊지말고 1시간에 한번씩 연결해서 작업하던가 하는 식으로 바꾸는게 바람짐학

서버에서 해줄 수 잇는 작업은 리싸이클 커널 파라미터 조정해야 할 듯 한데(그 외 커널 파라미터는 최적화 돼 있는 상태임)

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=nkyle9361&logNo=220060056557

근데 위 설정 해주면 fin 안받고 세션 끊고 바로 배정하는거라 킵얼라이브처럼 세션 재활용할 때 500 에러 난다고 하니 위 설정은 하면 안될 듯 - 위 본문에도 있듯이 Client쪽이 방화벽 또는 NAT 환경일 때<< 프록시 서버라 문제 날 수 있을것같음.

 

 

'job > linux' 카테고리의 다른 글

docker Exited (137)  (0) 2024.01.09
ci/cd 파이프라인  (0) 2022.11.16
du -sch --exclude  (0) 2022.07.11
systemd: Created slice libcontainer_number_systemd_test_default.slice.  (0) 2022.07.11
gitlab - elasticsearch intergration  (0) 2021.02.16

아마존 - AWS, 구글 - GCP, 마소 - Azure 와 같이 이미 구축되어진 클라우드 시스템을 이용하는것이 퍼블릭 클라우드라면관리자가 직접 프라이빗 클라우드 시스템을 구축할 수 있게끔 해주는 오픈소스 프로젝트가 openstack이다. 
(국내에 Openstack으로 클라우드 서비스를 제공하는 업체도 있다고 한다.)

 

 

 

 

기존 사내 업무 환경이 기존에는 구글 엑셀을 통해 업무를 많이 봤는데 최근에 rdbms를 통한 업무가 상당히 많아졌다.기존에도 rdbms를 쓰긴 했지만(사내 디비 환경은 도커 컨테이너 기반의 master+slave 환경으로 서비스를 하고 있었다.) 웹+디비등의 환경에서만 쓰였는데 최근에는 대부분의 임직원이 디비를 활용하여 업무를 하고 있다.

어쨌든 업무환경이 디비를 통한 업무가 증가하면서 기존에는 원활하게 돌아가던 DB(mariadb)가 원인불명의 다운이 발생한다. 아마 사용량이 많아지면서 잘못된 사용?이 있었거나.. 여튼 원인을 파악해야 하기에 모니터링 툴을 찾아봤고 pmm 이 나왔다.

 

pmm 이란 페르소나 모니터링 엔드 메니지먼트로 여러 오픈소스 디비의 모니터링을 지원(오라클은 지원X)하고 있다. 클라우드 디비들까지.

pmm은 클라이언트-서버로 구성 돼 있다. 클라이언트에서 수집된 데이터들을 서버로 보내면 서버에서 웹UI를 통한 대시보드, 그래프등으로 표시된다.

 

 

PMM 서버는 쿼리 분석을 위한 [QAN API, QAN web app] / DB저장 및 시각화하기 위한[그라파나, 프로메테우스, 클릭하우스]로 이루어져 있고 이것들로 유저는 PMM 서버의 엔진엑스/pgsql을 통해 웹 UI제공/설정저장이 가능하다.

PMM 클라이언트는  pmm-admin 과 pmm-agent, db-expoter로 이루어져있고 클라이언트의 데이터를 수집하고 서버로 전송하기위한 과정들을 할 수 있게끔 한다.

 

 

즉 쉽게 설명하자면

pmm 모니터링을 하기 위해서는 3가지가 필요하다.

pmm 서버 / pmm 클라이언트 / 수집대상 데이터베이스

pmm 서버는 당연히 아무곳에나 해도 되고 pmm 클라이언트도 아무곳에나 해도된다. 꼭 수집대상 데이터베이스에 안해도 된다. 

따라서 나같은 경우는 pmm서버/클라이언트를 한곳에 설치해줬다. 클라이언트라길래 꼭 수집대상 데이터베이스가 있는 서버내에 설치해야하나 헷갈렸어서 적어준다. 물론 데이터베이스 있는 서버에 설치해주면 서버의 리소스까지도 모니터링이 되는 이점은 있다.

 

 

설치 진행

 

 

현재 인프라 운영환경이 전부 도커 컨테이너 기반이기 떄문에 설치도 도커(도커 컴포즈)를 통해 진행할꺼고 순서는 다음과 같다.

1. 모니터링 호스트 서버에 PMM-server를 도커컴포즈로 설치하고
2. PMM-client가 디비에서 데이터 수집을 할 수 있도록 디비 계정 생성 및 추가 설정 진행 해주고
3. 디비 컨테이너가 있는 도커 컴포즈에 PMM-client를 추가하고

4. 

 

 

 

1. 모니터링 호스트 서버에 PMM-server 설치

version: '2'

services:
  pmm-data:
    image: percona/pmm-server:latest
    container_name: pmm-data
    volumes:
      - ./prometheus/data:/opt/prometheus/data
      - ./consul-data:/opt/consul-data
      - ./mysql:/var/lib/mysql
      - ./grafana:/var/lib/grafana
    entrypoint: /bin/true

  pmm-server:
    image: percona/pmm-server:latest
    container_name: pmm-server
    ports:
      - '11180:80'
    restart: always
    environment:
      - SERVER_USER=admin
      - SERVER_PASSWORD=pmmadmin
      - METRICS_RETENTION=720h
      - METRICS_MEMORY=4194304
      - METRICS_RESOLUTION=1s
      - QUERIES_RETENTION=30
    volumes_from:
      - pmm-data

docker-compose up -d 하고 웹으로 접속하면 초기 아이디/패스워드는 admin/admin 인것같다. 컴포즈에서 설정하는건 적용이 안되는것으로 보임 기본 아이디 패스워드는 admin/admin 이다. 패스워드는 로그인 한 뒤 패스워드 변경해야 하고 변경된 상태로 컨테이너 스탑 한 뒤 컨테이너 이미지를 새로 만들어야 한다.(각종 설정정보는 위에 기술한것처럼 pmm-server 내 pgsql에 저장 되는데 pgsql은 컴포즈 설정상 바인딩마운트를 하지 않기 때문에 도커 컴포즈 재시작하면 설정 다 날라간다. 그래서 초기 설정한 뒤 이미지 새로 떠놓던가 아니면 pgsql도 바인딩 마운트 추가하던가 해야함)

 

2. 수집할 데이터가 있는 디비에 pmm-client가 접근할 수 있도록 계정 생성 및 데이터 권한 추가

MariaDB [(none)]> create user 'pmm'@'192.168.226.%' identified by '패스워드';
Query OK, 0 rows affected (0.001 sec)
MariaDB [(none)]> grant select, process, replication client, reload on *.* to 'pmm'@'192.168.226.%';
Query OK, 0 rows affected (0.001 sec)

3. pmm-client가 디비서버에서 쿼리데이터를 수집 할 때 슬로우쿼리/퍼포먼스스키마 둘중 하나에서 수집하는데 뭘로할지 정해야함.

아래 설명 보는것처럼 슬로우쿼리는 디테일한 정보(즉 더 많은 정보)를 볼 수 있지만 시스템 성능에 영향을 줄 수 있고 로그 용량 관리등을 해줘야 한다. 반면 퍼포먼스 스키마에서 가져올 경우 디테일하지 않다는 단점만 있다. 일단 퍼포먼스 스키마로 설정해주고 내가 필요한 데이터(즉 현재 다운되는 원인을 파악 할 수 있는 데이터)가 없다면 슬로우 쿼리로그로 바꾸는게 맞을것같은데...

그러나 현재 디비 구성이 퍼포먼스 스키마는off이고 슬로우 쿼리가 on이다. 
우선 퍼포먼스 스키마/슬로우 쿼리에 대해 좀 더 알아볼 필요가 있겠다. 슬로우쿼리는 우리가 지정한 타임 이상 걸린 쿼리에 대한 정보를 남기는것이고 퍼포먼스스키마에 대한 설명:http://cloudrain21.com/everything-of-mysql-performance-schema 

 

MySQL 성능분석도구 이야기(PERFORMANCE_SCHEMA) - Rain.i

All about IT tech, especially database, cloud, linux, clustering.

cloudrain21.com

말 그대로 성능에 대한 정보가 담긴 디비(스키마)라고 생각하면 됨.

 

정리하자면 
슬로우쿼리 : 내가 설정해둔 시간보다 많이 걸리는 쿼리들 로깅 됨
퍼포먼스 스키마 : 여러 수집 가능한 정보들이 있는데 항목별로 on/off하여 수집 가능
앞서 PMM에서 알려준(슬로우쿼리가 좀 더 디테일한 장점이 있고 반대로 퍼포먼스스키마는 정보가 많지 않다는 단점이 있다는) 것과 반대로 디비 운영 관점에서 보자면 슬로우쿼리(슬로우쿼리에 쌓이는 데이터들은 단순히 어떤 쿼리가 느렸는지 정도만 파악 가능)보다 퍼포먼스 스키마가 더 많은(더 디테일한)정보를 볼 수 있다고 이해가 된다. 

근데 왜 PMM에서는 슬로우쿼리가 좀 더 디테일하다고 설명하는걸까 ? 슬로우쿼리 안에 데이터로 뭐가 분석이 되나...? 이부분 이해가 안가는데... 단순히 슬로우 쿼리는 쿼리 전체가 다 찍히니... 그래서 디테일 하다는건가 ? 일단 설치해보고 슬로우쿼리/퍼포먼스스키마 각각 수집되는 항목들 직접 보고 비교해봐야겠다.

 

아래와 같이 performance_chema를 on등을 설정해주고 mysql을 재시작한다.
performance_schema=ON
performance-schema-instrument='statement/%=ON'
performance-schema-consumer-statements-digest=ON
innodb_monitor_enable=all
#query_response_time_stats=ON   ###이건 mysql에 플러그인 추가로 설치해줘야 한다. 
userstat=ON

퍼포먼스 스키마 실행 확인 완료

퍼포먼스 스키마DB에 데이터가 쌓이고 있음도 확인

 

4. pmm-client install

https://docs.percona.com/percona-monitoring-and-management/details/commands/pmm-agent.html 참고하였다.

 

우선 아래의 내용으로 docker-compose.yaml 생성
version: '2'
services:
  pmm-client:
    image: percona/pmm-client:2
    hostname: pmm-client-myhost
    container_name: pmm-client
    restart: always
    ports:
      - "42000:42000"
      - "42001:42001"
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
    volumes:
      - ./pmm-agent.yaml:/etc/pmm-agent.yaml
      - pmm-client-data:/srv
    environment:
      - PMM_AGENT_CONFIG_FILE=/etc/pmm-agent.yaml
      - PMM_AGENT_SERVER_USERNAME=admin
      - PMM_AGENT_SERVER_PASSWORD=admin
      - PMM_AGENT_SERVER_ADDRESS=X.X.X.X:443
      - PMM_AGENT_SERVER_INSECURE_TLS=true
    entrypoint: pmm-agent setup
volumes:
  pmm-client-data:

 

 

      - PMM_AGENT_SERVER_ADDRESS=X.X.X.X:443
    hostname: pmm-client-myhost
위 두개를 수정해준다

 

그리고 콘피그 파일 touch pmm-agent.yaml && chmod 0666 pmm-agent.yaml 수정 될 수 있또록 생성/권한 수정 해주고

docker-comose up(설정파일 생성을 위해 임시로 실행하는것임)해주면 아래와 같이 pmm-agent.yaml이라는 설정파일이 생성된다.

여기서 중요한점 두가지
첫번째는 해당 설정파일에는 인증정보가 있으니 외부로 노출하면 안되고 두번째는  docker-compose.yaml 에서 수정했던
    hostname: pmm-client-myhost
위 부분을 각 사용자에 맞게 수정을 해줘야 그에 맞춰서 pmm setup 명령어를 통해 agent.yaml이 생성된다. 만약 hostname을 기존에 쓰던 네임일 경우 agent 설정파일이 생성되지 않는다.
agent.yaml 생성 됐으면 마지막으로 다시한번 도커 컴포즈 야믈파일에서     entrypoint: pmm-agent setup 이부분 주석하고 다시한번 마지막으로 docker-compose up -d 으로 실행해주면 컨테이너가 잘 실행 될것이다.pmm-agent.yaml 파일에 데이터가 씌어져 있어야 한다.



 

여기서 발생한 에러 

 

위와같이 pmm.mycompanydomain.co.kr로 연결이 안됨.

원인 : pmm-client가 pmm-server로 연결 할 때 pmm.mycompanydomain.co.kr 주소로 연결하는데 
회사 구성은 도메인 연결시 nginx.proxy 구성이 돼 있기에 pmm.mycompanydomain.co.kr:443 은 nginx proxy 로 intra.mycompany.co.kr:11443으로 포워딩 해주고 있는데
https://forums.percona.com/t/pmm-agent-can-not-connect-to-pmm-server-when-using-reverse-proxy/8435/10
이 과정에서 문제가 있었던것 같고 그냥 intra.마이콤파니닷컴 명시해주었다.

 

마지막으로 pmm.url.com 대쉬보드로 접속해서 

아까 pmm 계정 생성했던(즉 모니터링 할 디비)서버 정보 입력해주고 몇분 기다려보면 아래와같이 데이터가 수집된다.

 

 

모니터링 하는 방법은

 

그래프 쌓아서 보다보면 이건 쫌 이상한데 ? 싶은 튀거나 이가 빠지는 그런 그래프가 있을꺼다. 딱봐도 수상해보이는 그래프들이 있을꺼니까 

 

위와 같이 해당 그래프의 panel json을 들어가보면 

 

각 그래프들의 설명을 자세하게 적어줬다. 심지어 URL까지.

 

 

+ Recent posts