CLOUDWAVE

Abstraction : Container, MSA ( 25.01.08 )

갬짱 2025. 1. 15. 18:23

 

추상화( abstraction )

(1) 복잡한 것을 감추는것(Encapsulation) → 보다 효율적인 설계, 구축

(2) 필요한 기능만 오픈하는 것

(3) 인터페이스의 표준화 → 다른 기능, 모듈과 소통

(ex) 언어별 SDK library(개발자가 특정 플랫폼, 서비스, 또는 API와 상호작용할 수 있도록 도와주는 라이브러리 또는 도구 모음)를 전달하는 것보다 서비스 스펙으로 제공한 Restful API를 호출하는 것이 좋음 ( 언어별 통신을 모두 표준화 )

 

대표적인 추상화 예시

  • 네트워크의 추상화 : SDN(software defined network) ⇒ 모든 개체를 묶어서 원하는 네트워크 환경의 스위치로 만듬
  • 스토리지의 추상화 : SDS(software defined storage) ⇒ 하나의 디스크인것처럼 만들어줌
  • 서버 : VM( virtual machine )

=> 이는 모두 하드웨어를 추상화하는 것임

 

  • 결과적으로 이를 다루고 하드웨어-소프트웨어 사이에 존재하는 OS를 추상화하는 것이 핵심( 가상머신의 방식등.. )
  • OS는 공통적으로 리눅스를 쓰는 방식으로 발전( 커널공유 ) = 표준화 당했다!
  • 어떤 리눅스라도( any Linux ) 하나의 커널(OS core)을 공유( 배포판에서는 OS 관리 유틸리티만 상이함 ) → 컨테이너구동이 가능( 리눅스 컨테이너 )
  • 윈도우에서도 Linux 커널을 가볍게 가상화하여 제공하는 WSL2를 이용하여 리눅스 환경을 제공 → 리눅스 컨테이너실행가능 / Docker Desktop은 WSL2를 기반으로 리눅스 컨테이너 지원

 

Container

모든 추상화 대상들을 컨테이너에 담아서 추상화

what is 컨테이너? 커널은 공유하고 리소스(하드웨어: CPU, Memory, Disk, Process, Network)를 격리해주는 것!!

how to 컨테이너? ⇒ namespace기술 : 서로 영역을 침범하지 못하게 함

( * 리눅스 컨테이너를 가능하게 하는 기술 : namespace & cgroup )

 

 

 

Cloud native

: 클라우드 환경에서 애플리케이션을 설계, 구축, 실행하는 방식

  • 리눅스 운영체제를 바탕 ( 리눅스 컨테이너 )
  • Linux Foundation, CNCF의 다양한 기술지원 : 컨테이너 기술(Docker, Containerd) & 클라우드 오케스트레이션 도구(Kubernetes)
  • CloudNative 방식
    • 격리성 : 컨테이너, 마이크로서비스 아키텍처, 컨테이너 오케스트레이션
    • 자동화 : 배포의 자동화 → CI/CD 파이프라인, 인프라관리의 자동화 → Terraform, Ansible
    • 확장성 및 복원력 : 불변인프라( immutable )
    • 서비스 메시( 마이크로 서비스간의 통신을 관리하고 모니터링 )

 

 

하드웨어 관점의 추상화 (가상화) : VM( 가상머신 ) → 인프라 운영에서 용이함

소프트웨어 관점의 추상화 (컨테이너화) : Container( 격리된 환경에서 모든 것을 담음 ) → 서비스 개발, 관리에서 용이함

각 추상화의 목적이 다름

  • 쿠버네티스 : 수많은 컨테이너를 다루고 제어하고 관리하기 위한 기술↔ docker swam : 컨테이너를 대규모로 관리하기 위한 기술
  • ↔ docker compose : 서비스 제공목적, 내부개발용
  • service mesh : 쿠버네티스 위에서 네트워크까지 추상화( 요청까지 추상화 )

 

핵심 요약

  1. 클라우드: 인터넷을 통해 제공되는 IT 리소스(서버, 스토리지 등)를 활용하는 환경.
  2. 가상화/컨테이너화: 클라우드 환경에서 얻은 리소스를 분리된 독립적인 환경(가상 머신/컨테이너)에서 관리하는 방법.
  3. 도커: 컨테이너를 생성하고 관리하는 도구.
  4. 쿠버네티스: 컨테이너를 기반으로 서비스를 배포하고 관리하는 오케스트레이션 도구.

 

마이크로 서비스 아키텍처(MSA)의 특징

마이크로 서비스 아키텍처 = Loosely coupled and High-cohesion service oriented architecture with bounded context

: Loosely Coupled, High-cohesion, bounded context

 

(1) Loosely Coupled ( 느슨한 연결 )

  • 인터페이스를 이용한 통신 ( 세부구현은 숨김 )
  • 비동기 통신 : 요청에 대한 응답과 관계없이 처리 ↔ 동기 : 요청에 대한 응답이 이루어지기까지 대기
  • 서비스별로 데이터베이스 분리 : 독립적인 저장소 → 하나의 데이터베이스에 오류가 발생해도 사용가능
  • 회로차단( Circuit Break ) : 서비스가 서비스를 호출 / 하나의 서비스에서 문제가 발생해도 접근, 호출하지 않으면 다른 서비스에 영향이 가지 않음( 전이되지 않음 ) domino effect를 방지

 

(2) High Cohesion ( 높은 응집력 )

  • 단일책임원칙(SRP : Single Responsibility Principle) : 변경은 작은단위(하나의 서비스, 코드베이스)에서 진행
    • 관련 디자인패턴 : Strategy pattern, Factory pattern, Adapter pattern
  • Domain-Driven Design : 업무를 작은 도메인으로 구별

* Loosely Coupled는 서비스간의 관계를 말하고 High Cohesion는 서비스 내부의 관계를 말한다.*

 

 

(3) Bounded Context(도메인 경계)

하나의 큰 서비스 내부에서 기능별, 서비스별로 쪼갬 = 경계가 있는 컨텍스트

Rest API를 이용하여 통신 : 유의미한 기능을 open, 상세분석 및 내부조작을 close

 

서비스 크기에 따른 아키텍처 비교

  • Monolith : 코드베이스 하나
  • Modular monolith : 코드베이스 하나, 기능별로 폴더 구분
  • Mini Service : 코드베이스가 서로다름
  • Micro Service : 가장 작게 구분

→ 실무에서는 micro service보다 mini service 정도가 더욱 효율적임, 적절함

→ 업무요건, 요청, 기능이 수시로 바뀌는 경우 + 첫시작인 경우 micro service가 더욱 비효율적임을 명심하라

→ 마이크로 서비스는 빠른 서비스 배포, 소비자에 민감한 애플리케이션에 적합

 

 

마이크로서비스의 핵심은 서비스 단위에서의 독립적인(개별적인) 빌드, 배포이다. ⇒ 소비자의 개별적인 요구사항, 기능을 빠르게 구현할 수 있음

  • 전체 빌드(Build) 가 아닌 서비스 단위의 빌드 및 배포
  • 테스트 코드 또는 TDD 필요
  • 컨테이너 환경에 매우 적합

 

실습환경 구축

docker compose를 이용하여 IDE 컨테이너 생성 → 이를 통해 로컬 쿠버네티스 접속

(1) IMAGE : rudalsss/myide

# <https://hub.docker.com/r/linuxserver/code-server>
FROM linuxserver/code-server:4.93.1

# ENV for Code-server (VSCode)
ENV TZ="Asia/Seoul"
ENV PUID=0
ENV PGID=0

# Make DIR for code-server
RUN mkdir /code && chown 1000:1000 /code

# Update & Install the packages
RUN apt-get update && apt-get -y upgrade

RUN apt-get install -y ca-certificates curl gnupg software-properties-common wget unzip apt-transport-https telnet net-tools vim iputils-ping openjdk-11-jdk python3 python3-pip gh

# Install Jupyter
RUN pip install --no-cache-dir --break-system-packages jupyter ipykernel

# ENV for JDK
ENV JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"
ENV PATH="${JAVA_HOME}/bin:${PATH}"

# Docker CLI
RUN apt-get update && install -m 0755 -d /etc/apt/keyrings  \\
    && curl -fsSL <https://download.docker.com/linux/ubuntu/gpg> -o /etc/apt/keyrings/docker.asc  \\
    && chmod a+r /etc/apt/keyrings/docker.asc

RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] <https://download.docker.com/linux/ubuntu> \\
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \\
      tee /etc/apt/sources.list.d/docker.list > /dev/null

RUN apt-get update && apt-get install -y docker-ce docker-ce-cli

# Install AWS CLI from `amazon/aws-cli` image
COPY --from=amazon/aws-cli /usr/local/aws-cli/v2 /usr/local/aws-cli/v2
RUN ln -s /usr/local/aws-cli/v2/current/bin/aws /usr/local/bin/aws
RUN ln -s /usr/local/aws-cli/v2/current/bin/aws_completer /usr/local/bin/aws_completer

# Terraform
RUN wget -O- <https://apt.releases.hashicorp.com/gpg> | gpg --dearmor | tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null  \\
    && gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint  \\
    && echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] <https://apt.releases.hashicorp.com> $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list

# RUN apt update && apt-get install -y terraform=1.9.7
# RUN apt update && apt-get install -y terraform
RUN apt update && apt-get install -y terraform

# Kubectl
RUN curl -fsSL <https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key> | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg  \\
    && echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] <https://pkgs.k8s.io/core:/stable:/v1.29/deb/> /' | tee /etc/apt/sources.list.d/kubernetes.list

RUN apt-get update && apt-get install -y kubectl

# Kubectx & Kubens
RUN git clone <https://github.com/ahmetb/kubectx> /opt/kubectx  \\
    && ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx  \\
    && ln -s /opt/kubectx/kubens /usr/local/bin/kubens
docker build -t rudalsss/myide .
docker login —user rudalsss
docker push rudalsss/myide

 

이미지 빌드해서 Dockerhub에 올려둔다.

 

Docker CLI, AWS CLI, Kubectl, Kubectx & Kubens, Terraform이 설치된 리눅스 code-server( VSCode환경 )

 

기존에는 시스템에서 필요한 util들을 직접 설치 ⇒ docker의 혁신 : 설치없이 선언으로 설치효과, binary를 받아와서 자동으로 만들어줌

 

[ 선언설치의 효과 ]

  • 이식성 : 다른 환경에서도 사용가능
  • git ops : 선언은 단순문자(코드) → 관리 및 수정 유지가 용이함
  • 자동화 : 선언만 해두면 git 엔진에서 설치를 자동적으로 수행
  • 추상화

[ Quiz ]

도커파일에서 명령어 1줄은 1개의 이미지 레이어이다. → TRUE

&&으로 연결시키면 모두 1줄이 된다.

레이어마다 고유한 일정길이의 hash값을 가지고 식별됨( 해시알고리즘 : input값이 조금이라도 다르면 상이한 값이 나옴 → 변경을 쉽게 파악할 수 있음 )

 

캐시의 이용 : 이미지 변경 및 수정시 고정된 부분은 이전의 레이어를 재사용하고 변경된 부분만 반영 → 네트워크 트래픽을 감소시키는 효과!

 

컨테이너 내부의 특정 파일에 접근하여 수정 및 생성이 가능하다 → TRUE

그러나 컨테이너가 종료되면 그 내용은 휘발됨

 

컨테이너는 union file system, overlay filesystem을 사용한다.

  • union filesystem : 파일 위에 새로운 파일이 올라가면 위에 있는 파일이 우선한다.
  • 컨테이너를 띄우면(이미지의 컨테이너화) 기존의 이미지의 read only 레이어(immutable) 외에 read write 레이어( container layer )가 생성됨 ( 오버레이 파일시스템 으로 r/w레이어가 우선함 )
  • r/w레이어는 호스트의 디스크( var/lib/container )에 존재함 → 호스트의 리소스를 같이 먹기 때문에 위험함

 

(2) Container : ide

version: "3.8"
name: "aws-cicd-practice"
services:
  # jenkins:
  #   image: jenkins/jenkins:lts
  #   container_name: jenkins
  #   user: root
  #   ports:
  #     - "8081:8080"     # Jenkins UI 포트
  #     - "50000:50000"   # 에이전트 연결 포트
  #   volumes:
  #     - jenkins-home:/var/jenkins_home  # Jenkins 데이터를 영구적으로 저장
  #     - /var/run/docker.sock:/var/run/docker.sock  # Docker를 Jenkins에서 사용 가능하게 설정
  #   environment:
  #     - JAVA_OPTS=-Djenkins.install.runSetupWizard=false  # 초기 설정 마법사 비활성화 옵션
    
  server:
    image: dangtong76/myide
    container_name: "ide"
    networks:
      - kind_network
    environment:
      FILE__PASSWORD: /run/secrets/code-server-password
    env_file:
      - .env
    working_dir: /code
    ports:
      - "8444:8443"
      - "5500:5500"
    secrets:
      - code-server-password
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - cloud-cicd-src:/code/cloud-cicd-src
      - vscode:/config
networks:
  kind_network:
    name: kind
    external: true

volumes:
  cloud-cicd-src:
    external: true
    name: cloud-cicd-src
  vscode:
    external: true
    name: vscode
  # jenkins-home:
  #   external: true
  #   name: jenkins-home
secrets:
  code-server-password:
    file: password.txt

컨테이너의 휘발성으로 인해 호스트의 디렉토리와 바인드하여 그 내용을 저장한다. ⇒ volume

IDE 컨테이너에서는 소스코드 및 extension 설치를 위한 영역을 볼륨에 저장

docker volume create --opt device="C:\\Users\\KHP\\Documents\\cloudwave\\k8s-practice\\cloud-cicd\\makeENV\\src" --opt o=bind --opt type=none cloud-cicd-src
docker volume create --opt device="C:\\Users\\KHP\\Documents\\cloudwave\\k8s-practice\\cloud-cicd\\makeENV\\vscode" --opt o=bind --opt type=none vscode

도커볼륨을 cloud-cicd-src, vscode라는 alias로 구축해둔다 ( 논리적인 외부볼륨 = external ) ⇒ 각각 윈도우 환경에서 ./src, ./vscode에 바인딩됨

 

볼륨 연결시 컨테이너 레이어, 이미지 레이어에 별도인 독립적인 영역이 사용되는 것

 

(3) Cluster : cwave-cluster

[ 클러스터 정의 yaml 작성 ]

**kind: Cluster**
apiVersion: kind.x-k8s.io/v1alpha4
name: cwave-cluster
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  image: kindest/node:v1.29.4
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
- role: worker
  image: kindest/node:v1.29.4
- role: worker
  image: kindest/node:v1.29.4
networking:
  serviceSubnet: "10.120.0.0/16"
  podSubnet: "10.110.0.0/16"
  • kind: Cluster → 클러스터 리소스를 생성하는 것을 선언
  • 쿠버네티스 버전
    • 쿠버네티스 : k8s
    • 쿠버네티스 경량화 버전 : k3s
    ⇒ api 버전에 맞는 제작이 가능한(하위호환) k8s와 달리 k3s는 버전간 호환지원하지 않음
  • kindest/node:v1.29.4 : Kind (Kubernetes in Docker)에서 사용되는 기본 노드 이미지
  • 가상의 서비스 컨테이너(nodes) → 역할(role)로 구분 ( contol-plane, worker )
    • contorl node : 쿠버네티스의 머리에 해당 → 조종 및 제어
    • worker node : 어플리케이션이 수행되는 노드

 

[ kind 설치 ]

kind (Kubernetes IN Docker) : Docker 컨테이너 위에서 실행되는 경량 쿠버네티스 클러스터를 생성하기 위한 도구

  • Kubeadm: 대규모, 프로덕션용 클러스터를 만들 때 사용.
  • Kind: 로컬 환경에서 간단한 클러스터를 만들 때 사용.

 

먼저 chocolatey설치

chocolatey : 윈도우 운영 체제의 패키지 관리 시스템으로 소프트웨어 설치, 업그레이드, 구성, 삭제 등을 명령어 기반으로 자동화

choco install kind

 

kind 설치

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('<https://community.chocolatey.org/install.ps1>'))

 

이때 kind 실행파일은 올라가지 않도록 .gitignore에 추가

kind-windows-amd64
kind-windows-amd64.exe

 

+) 파일로 다운받는 경우

사용자 환경변수 및 시스템 환경변수의 Path C:\\Users\\KHP\\Documents\\cloudwave\\k8s-practice\\cloud-cicd\\makeENV→ 다운로드 실행파일이 위치한 해당 경로를 추가함

kind 명령어를 어디에서나 즉각적으로 실행가능

 

[ kind를 이용한 클러스터 생성 ]

kind create cluster —config 3-node-k8s.yaml

kind를 이용 + 요청양식을 yaml로 작성하면 이에 맞게 제작

 

kind get clusters # 생성한 클러스터 조회

kubectl get no # 생성한 노드 조회

 

kubectl : 쿠버네티스 클러스터를 관리하기 위한 CLI(Command-Line Interface) 도구, kind설치시 자동으로 설치됨

 

네트워크 조회 → kind라는 이름의 도커 네트워크가 자동적으로 생성됨

kind는 Docker 컨테이너 기반으로 클러스터를 생성 → 클러스터별로 새로운 Docker 네트워크가 생성

 

 

[ IDE 컨테이너 네트워크 재설정 ]

docker-compose.yaml( IDE컨테이너를 담고 있는 aws-cicd-practice 프로젝트를 정의 )에서 네트워크 설정을 다시 진행( kind네트워크와 묶음 ) 후에 docker compose up -d를 진행(업데이트) ⇒ kind라는 이름의 external 네트워크를 서비스에 적용( 클러스터의 노드들과 같은 네트워크 대역을 사용가능 )

...
services:
  server:
    image: rudalsss/myide
    container_name: "ide"
    networks:
      - kind_network
		....
		
networks:
  kind_network:
    name: kind
    external: true

 

[ 컨테이너의 구축상황 ]

  • aws-cicd-practice compose
    IDE container ⇒ 2개의 파일을 물고 있음( /src, /config )
  • cwave 클러스터( k8s )
    • control plane 1개
    • worker node 2개

 

이 컨테이너들(4개)은 모두 kind network( 172.19.0.0/16 )로 묶여있음 → 상호통신가능

 

docker inspect network kind

k8s를 만들면서 kind network가 자동으로 생성되었고 이를 IDE에 연결함

IDE내부에 kubectl를 깔아서 → 클러스터의 contorl plane과 통신이 가능함

 

[ kind ]

  • 쿠버네티스 클러스터(cwave-cluster)를 구축하면서 이에 접근제어할 수 있는 tool인 kubectl을 자동적으로 설치함
  • 호스트(윈도우)에서 생성한 클러스터 → C:\Users\KHP\.kube의 config에 접속정보 & 인증서를 저장 ( 홈디렉토리/.서비스의 config파일에 접속정보가 저장됨 )
    이렇게 host의 kube에는 인증정보 및 기록이 남아있지만 IDE 컨테이너 내에는 해당 기록이 없음 → 윈도우에서 인증정보를 받아와서 넣어주어야(복붙) IDE컨테이너에서도 접속이 가능함
    kind get kubeconfig --name cwave-cluster | CLIP

    표준출력이 클립보드로 복사된다.
    IDE에서 기존에 바인드된 파일 /config( 볼륨 vscode에 바인딩 )를 가시적으로 생성하고 여기에 하위경로 .kube/config 를 생성 & config내용을 복붙하여 넣어줌

이때 cluster의 서버주소는 local host의 포트가 아니라 control plane의 ip와 외부에 연결된 포트 6443으로 접속 ( https://172.19.0.4:6443 ) control plane의 API서버 주소로 바꾸는 작업임

  • IDE에서도 정상적으로 쿠버네티스 클러스터에 접속이 가능해짐

 

[ Kubectl 기본 사용법 ]

  • kubectl 리소스 : 리소스를 제어하기 위한 문법
    • kubectl get all : 모든 리소스에 대한 조회
    • kubectl get po, svc, ns
  • kubectl config get-contexts
    • context : API서버정보, 인증서를 기반으로 context를 생성 가르키는 지시자 → 전환가능( switch를 전제함 ) 클러스터 여러개 생성가능, 클러스터의 숫자만큼 정해짐
    • .kube/config하에는 cluster와 context 정보가 존재함
      • cluster : 여러 개의 노드들로 구성된 Kubernetes 환경
      • API server : master node에 접속할 수 있는 API server의 정보 → 바꿔서 접속가능
      • context : 클러스터, 사용자, 네임스페이스 등과 관련된 환경 설정을 하나로 묶은 것
      • current-context : 현재 활성화된 context
      • user : Kubernetes 클러스터에 접근할 때 사용할 사용자 정보 → 토큰, 인증서 등을 포함
      • 인증서 : ID/PASSWORD가 아닌 인증서 기반으로 접속
        • 나의 개인키(개인인증서)
        • CA 인증서