728x90
반응형

[목표]

현재 팀원들이 개발을 진행중인 여러 개의 Application들을 Main branch에 push를 하게 되면 자동으로 쿠버네티스 서버에 배포가 되게 하도록 해야합니다.

[Tool]

  • Gitea : Git은 Gitlab보다 가벼운 Gitea(깃티)라는 소프트웨어 패키지를 사용하고 있습니다.
  • Jenkins : 빌드된 파일들을 Dockerfile을 이용해서 이미지로 만드는 등의 Pipeline CI/CD 작업 툴로 Jenkins를 사용합니다.
  • ArgoCD : ArgoCD는 쿠버네티스 배포를 도와주는 도구입니다.
  • Kubernetes : Microservice를 구현할 수 있게 컨테이너화 된 애플리케이션을 관리합니다.
  • Helm : Kubernetes 패키지 관리를 위하여 사용합니다.

[전체 과정]

Gitea와 Jenkins 연동

1. Jenkins에 Gitea관련 설정 추가

Jenkins에 Gitea를 사용할 수 있는 Plugin을 설치해야 합니다.
https://plugins.jenkins.io/gitea-checks/
https://plugins.jenkins.io/gitea/

2. Gitea 연결 (Jenkins 관리 > 시스템 설정)

  • Name : 사용할 이름을 정해주면 됩니다. gitea로 설정하였습니다.
  • Server URL : Gitea의 URL을 입력해야 합니다. (Gitea 설정이 정상적으로 되었다면 아래에 조그만 글씨로 Gitea Version 정보가 나타나게 됩니다.)
    • Credentials 설정 (Jenkins관리 > Credentials > System > Global credentials)에서 Add Credentials를 클릭합니다.
    • Username with password를 통해 설정합니다. (root / cloud1234)
      • (이미 설정하였기에 This ID is already in use 가 생기고, Unique한 값으로 설정해줍니다)
      • Credentials는 구축된 Gitea에 접근하기 위한 인증입니다. Jenkins는 기본적으로 Credentials Plugin을 제공하며, 이를 통해 인증 정보를 관리하고 사용합니다.Manage hooks : Credentials를 지정해주어야 합니다.

3. Jenkins Webhook 설정

  • Multibranch Pipeline Project로 생성해야 합니다.

(Gitea 연결은 다른 Freestyle Project 또는 Pipeline으로는 연결이 불가능합니다.)

 

    • Multibranch Pipeline을 통해 아래와 같이 설정해줍니다.
      • Gitea Server는 gitea 서버를 설정해줍니다.
      • gitea 서버를 설정하면 아래 Repository 목록이 나올 것입니다. 원하는 Repository를 선택하면 됩니다.

  • Build Configuration
    • Mode는 Jenkinsfile로 설정합니다. 이후에 Webhook은 설정한 Repository에 있는 Jenkinsfile을 실행하게 됩니다
  • Scan Multibranch Pipeline Triggers
    • Scan by webhook에서 Trigger token은 이후에 Gitea에서 Webhook 설정을 할 때 header에 담을 token과 동일하게 설정해야 하기 때문에 기억한 뒤 Gitea Webhook 설정에 사용해야합니다.
    • 4insure-msa-app으로 설정해두었습니다.

4. Gitea Webhook 설정

    • Gitea에서 Repository의 설정 > Webhooks 에서 Webhook을 추가하면 됩니다.
      (Setting이 보이지 않는다면 해당 Repository에 권한이 없기 때문입니다)
    • Target URL
      • http://{Jenkins URL}/multibranch-webhook-trigger/invoke?token={토큰 값}
      • 토큰 값은 위에 Jenkins Webhook 설정 때 만들었던 token입니다.

5. Jenkinsfile 작성

  • Jenkinsfile을 해당 Repository의 main branch에 commit & push해주면 Webhook이 동작하고 Jenkins Pipeline이 동작합니다.

 

Jenkins Pipeline 작성

1. Maven 등록 (Jenkins 관리 > Tools)

  • Jenkins가 실행되면 Jenkins에게 Build Tool를 주어야 Build 할 수 있습니다.
  • Maven를 빌드 툴로 설정하였기 때문에 아래와 같이 설치합니다.

2. Jenkins WorkFlow

import java.text.SimpleDateFormat
def dateFormat = new SimpleDateFormat("yyMMdd_HHmmss")
def TAG = dateFormat.format(new Date())
pipeline {
    agent any
    tools {
        maven "M3"
    }
    environment {
        REGISTRY_HOST = 'msa.harbor.com'
        REGISTRY_PROJECT = 'conn-test'
        GIT_OPS_REPOSITORY = 'msa-gitops'
        TAG = 'latest'
        APP = '4insure-msa-api-user'
        APP_PATH = 'UserApi'
        NAMESPACE = 'portal'
        PROFILE = 'msa' //'prod'
        ENV = 'msa'
    }
    stages {
        stage('echo test') {
            steps {
                echo "4insure-msa-api-user app"
            }
        }
        stage('Maven Build') {
            steps {
                 sh 'ls -al' // Run Maven build
                 sh 'mvn -f pom.xml clean install' // Run Maven build
            }
        }
        stage('Docker Build & Push') {
            steps {
                sh "docker login ${REGISTRY_HOST} -u admin -p cloud1234"
                sh "docker build -t ${REGISTRY_HOST}/${REGISTRY_PROJECT}/${APP}:${TAG} ."
                sh "docker push ${REGISTRY_HOST}/${REGISTRY_PROJECT}/${APP}:${TAG}"
            }
        }
        stage('Clean dummy images') {
            steps {
                sh """docker image prune --all --force --filter label=app=${APP} --filter label=io.kubernetes.pod.namespace=jenkins"""
            }
        }
        stage('Remote api updating gitops job') {
            steps {
                sh """curl -X GET -u \"admin:cloud1234\" 'http://172.10.40.243:30070/job/msa-gitops/buildWithParameters?token=MSA_GITOPS_TOKEN&TARGET=${APP}&TAG=${TAG}&ENV=${ENV}'"""
            }
        }
    }   // End of stages
} // End of pipeline

Source Build

  • Source Repository에서 Maven을 이용해 해당 애플리케이션을 Build 합니다

Docker Build & Push

  • Build된 것을 해당 Repository의 Dockerfile을 이용해서 Image를 만듭니다.
  • 만들어진 이미지를 Harbor에 Push합니다.
FROM neduekwunife/openjdk8-jre-alpine-with-fontconfig
LABEL app="4insure-msa-api-user"
ADD ./target/UserApi-3.0.jar /
WORKDIR /
ENV SPRING_PROFILES_ACTIVE=msa
ENTRYPOINT ["java", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "-Dfile.encoding=UTF8", "-Duser.timezone=Asia/Seoul", "-jar", "UserApi-3.0.jar"]

 

Clean Docker Image

  • 필요없는 docker image를 삭제합니다.
  • namespace를 제대로 설정해야 정확한 image를 타겟팅하여 삭제할 수 있습니다.

Update GitOps

  • 새롭게 만들어진 Image를 배포하기 위해 Jenkins의 GitOps Job를 호출해서 실행해야 합니다.
  • Jenkins의 원격 빌드를 이용하여 빌드를 유발합니다.

3. Update GitOps

    • 새로운 Item 생성을 통해 Job을 생성합니다. 이번에는 Pipeline을 선택합니다.
    • 매개변수가 있기 때문에 매개변수를 등록하여 사용할 수 있도록 합니다.
      • Jenkins의 매개변수 설정 중 Trim the string 을 해주어야 합니다.
    • 소스코드 관리
      • Gitea에 GitOps를 위한 새로운 Repository를 생성합니다. 이름은 msa-gitops로 하였습니다
      • 소스코드 관리 탭에서 Repository URL과 Credentials를 설정해줍니다

  • 빌드 유발
    • 빌드를 원격으로 유발을 클릭하고 Authentication Token을 등록해줍니다.
    • 해당 Token값은 위에서 마지막 Stage인 Update GitOps를 작성할 때 token={TOKEN_NAME}의 TOKEN_NAME과 동일하여야 합니다.

Shell Script 내용

  • 위에서 설정한 파라미터들이 shell에서 사용됩니다.
  • Shell은 Helm을 사용하여 Kubernetes에 배포하는 과정을 자동화하는 것을 목적으로 합니다.
  • Image tag는 현재 시간값으로 재설정됩니다. 해당 내용은 위에서 작성한 Jenkins Workflow에서 확인할 수 있습니다.
    • def TAG = dateFormat.format(new Date())
  • Image tag를 바꾼 뒤, GitOps를 하기위한 Repository에 commit, push를 진행합니다.
#!/bin/bash
MODULES=$TARGET
APP_LIST="4insure-msa-api-portal 4insure-msa-api-user 4insure-msa-app"
echo "Deploy target application: "$TARGET
echo "TAG: "$TAG
chmod 777 manifests/$TARGET.yaml
helm template -n test $TARGET charts/$TARGET -f charts/$TARGET/values.yaml \
     --set image.tag=${TAG} > manifests/$TARGET.yaml
echo "TAG changed: "$TAG
# git add/commit/push
git add .
git commit -m "updating manifest file completed."
git status
git push origin HEAD:main

4. ArgoCD

ArgoCD는 GitOps Repository를 바라보고 있습니다.

해당 GitOps Job에 의해 변화된 이미지가 Repository에 push 되었으므로 ArgoCD가 이를 감지하여 Sync를 맞추어 쿠버네티스에 새로운 이미지를 사용하여 자동으로 배포합니다.

 

Helm 설정

Helm Chart

Helm은 쿠버네티스 애플리케이션을 관리하기 위한 패키지 매니저입니다.
npm 패키지 매니저의 package.json이 있듯이 Helm에는 Chart를 통해서 쿠버네티스의 리소스를 설치하는데 필요한 모든 정보를 포함하고 있습니다.
helm chart에는 templates폴더, values.yaml파일이 포함되어 있는데, 차트의 설정 값이 있는 values.yaml파일을 통하여 templates파일들을 렌더링하여 쿠버네티스의 리소스 파일을 생성합니다.
values.yaml파일의 설정값을 사용자가 재정의하여 커스터마이징을 할 수 있습니다.

Gitea Helm Chart Install

# gitea 공식 Helm Repo Add 
helm repo add gitea-charts <https://dl.gitea.com/charts/> 
# Offline Mode 
helm fetch gitea-charts/gitea 
# install 
helm install -n gitea gitea . -f values.yaml
helm repo add {사용자 임의 정의 Repo 이름} {추가할 Helm repository URL}
helm fetch {다운로드 받을 Chart} # 다운로드 받은 파일은 .tgz확장자를 가진 압축파일

Gitea의 공식 Repository를 Helm에 추가하여 Helm을 통해 Gitea 차트를 설치하거나 업데이트할 수 있도록 합니다.

Directory 구성

  • charts
    • templates
      • 쿠버네티스 리소스 yaml 파일들이 위치합니다.
        (Deployment.yaml, Service.yaml을 Values.yaml의 변수를 통해서 작성합니다.)
  • manifests
    • 변경된 yaml 파일이 저장됩니다.
    • ArgoCD는 manifests에 변환된 yaml파일을 감지하여 Sync를 맞추어 자동으로 배포합니다.
  • scripts
    • GitOps Job에서 ArgoCD를 동작시키기 위하여 Helm의 Image를 변경하기 위해 실행되는 script입니다
  • Chart.yaml
    • chart에 대한 메타데이터가 저장되어있습니다.
728x90
반응형
728x90
반응형

Worker Node를 잠시 내루거나, 업데이트가 필요할 때

Node 내부의 Software를 업그레이드 해야하거나, 혹은 문제가 생겼을 경우에 해당 Node를 안전하게 내리고 다시 올려야 합니다.

그럼 문제가 되는 부분은 해당 Node에서 실행되고 있던 Pod들입니다. 만약 아무도 모르는 어떠한 오류로 인하여 Worker Node-1이 내려간다고 가정하였을 때, 사용자 입장에서 해당 Pod에 접속하다가 오류를 만나게 될 것입니다.

위의 그림에서처럼 만약 사용자가 파란색 Pod에 접속하여 서비스를 이용하고 있었을때, Worker Node-1번이 이유모를 오류로 내려갔다고 가정해봅시다.

그럼 사용자는 LoadBalancer와 같은 경로를 통해 Worker Node-2번에 배포되어 있는 파란색 Pod로 접속하면 다시 서비스가 가능합니다.

이렇게 ReplicaSet이 여러 개이고, 여러 Node에 흩어져 있는 경우는 문제가 되지 않습니다.

문제가 되는 경우는 아래와 같습니다.

  1. 사용자가 초록색 Pod에 접속하다가 Worker Node-1번이 내려간 경우
  2. 사용자가 빨간색 Pod에 접속하다가 Worker Node-3번이 내려간 경우

이런 경우에는 사용자는 기존에 이용하던 서비스를 이용할 수 없게 됩니다.

쿠버네티스는 어떻게 하고 있을까?

쿠버네티스는 Worker-Node가 다시 올라오길 기다립니다.

내려갔었던 Node가 바로 다시 돌아오면 사실 문제가 없습니다. 근데 이 '바로' 는 과연 몇 초, 몇 분일까요?

이건 kube-controller-manager 가 정할 수 있으며 default는 5분입니다.

해당 내용은 아래와 같은 명령어로 수정할 수 있습니다.

즉, node가 다운될때마다 master node는 5분동안 기다립니다. 해당 시간을 Pod Eviction Timeout이라고 합니다.

만약 Pod Eviction Timeout이 지난 후에 node가 돌아오면 node는 공백이 된 상태로 돌아오게 됩니다. (안에 아무 Pod가 없는 상태)

이렇게 되면 문제가 될 수 있기 때문에 명령어를 통해 미리 방지할 수 있게 도와줍니다.

drain , uncordon , cordon

drain - worker node에 있는 Pod들을 안전하게 다른 Node들로 이동

drain 명령어는 worker node에 있는 Pod들을 안전하게 다른 Node들로 이동시켜줍니다.

만약 node-1이 문제가 생길 것 같다던가 모든 Pod들을 옮기고 update를 진행해야 하는 경우에 아래와 같은 명령어를 사용합니다

그래서 모든 작업이 다른 Node로 이동할 수 있게 합니다.

즉, drain 명령어를 실행하면 해당 node-1에서 pod 들이 정상적으로 종료되고, 다른 Node에서 Node-1에서 실행되던 Pod들이 재현되게 됩니다ㅏ.

restriction을 끝내지 않는 이상 위의 명령어를 입력하게 되면 node-1에서는 scheduling에서 제외되어서 pod 일정을 잡을 수 없게 됩니다.

그럼 다시 node-1이 정상적으로 돌아오더라도 node-1은 scheduling에서 제외됩니다.

다시 node-1이 일을 하게 하려면 uncordon 이라는 명령어를 사용하여야 합니다.

uncordon - Worker Node Restriction 해제

위의 명령어를 사용하면 node-1이 다시 Pod Schedule이 가능하게 됩니다.

물론 그렇다고 해서 이동한 Pod들이 다시 돌아오는 것은 아닙니다. 다른 Node에서 Pod는 그대로 실행되고, 이후에 Pod가 삭제되거나 새로 생성될 때 Schedule 될 수 있습니다.

그럼 만약 Node-1에 있던 Pod들을 이동시키지 않고, 해당 Node-1이 더이상 Schedule하지 않도록 하려면 cordon 명령어를 사용합니다.

cordon - 기존 node에서 pod를 종료하거나 이동시키지 않고 싶을 때

cordon은 drain과 달리 기존 node에서 pod를 종료하거나 이동시키지 않습니다.

단순히 해당 node에 새 pod가 scheduling되지 않도록 하는 명령어 입니다.

728x90
반응형
728x90
반응형

이런 에러가 계속해서 발생했다. 원래 tab을 누르면 자동으로 커맨드를 추천하거나 찾아야 되는데 일일이 입력하기가 너무 불편했다.

bash를 사용하고 있다면 bash-completion이 없어서 생기는 문제이다.

mac이라면 brew install bash-completion
linux라면 brew install bash-completion

그런데 이렇게 하면 리눅스 bash 자동완성이 다 해결되지는 않는다. kubectl 명령의 경우 해결되지 않는다.

리눅스에서 bash 자동 완성으을 사용하고 있는지 확인하려면 다음 커맨드를 입력해본다.

type _init_completion

이렇게 입력했을때 bash 내용이 주르륵 나오면 사용하고 있는 것이고 type 명령어를 찾을 수 없다 등의 내용이 나오면 사용하고 있지 않고 위의 오류가 계속해서 생길 수 있다.

다음 순서대로 진행하면 실행할 수 있다.
apt-get install bash-completion
exec bash
이렇게 하고 type _init_completion 입력

-> 만약 실패한다면
vi ~/.bashrc 로 들어가서 마지막 줄에 다음 내용 입력 source /usr/share/bash-completion/bash_completion

그다음 다시 type _init_completion 입력 -> 아래와 같이 나온다면 성공

728x90
반응형
728x90
반응형

Pod : 쿠버네티스의 가장 작은 배포 단위

쿠버네티스는 컨테이너를 직접 관리하지 않고 pod이라는 걸로 감싸서 관리한다.

Pod이 쿠버네티스에서 가장 작은 배포 단위이고, 컨테이너를 배포하는 것이 아니라 'Pod를 배포한다'고 한다.

Pod의 특징

  • 전체 클러스터에서 고유한 IP를 부여받는다.

여러 개의 컨테이너가 하나의 Pod에 속할 수 있다.

  • 첫번째의 경우 Container와 log를 수집하는 컨테이너, 이렇게 2개가 떠있을 수 있고, 호스트에 있는 폴더를 공유할 수 있다.
  • 두번째의 경우 캐시를 같이 띄워서 포트를 공유할 수 있다.

ReplicaSet

  • 여러 개의 Pod을 관리
  • 새로운 Pod는 template을 참고하여 생성
  • 신규 Pod를 생성하거나 기존 Pod를 제거하여 원하는 수의 Replicas를 유지

Deployment

  • 배포 버전을 관리
  • ReplicaSet을 무중단 배포하기 위해서 사용할 수 있다.
  • ReplicaSet을 둘러쌈

쿠버네티스는 다양한 방식을 제공한다.

  • Demonset : 모든 노드에 꼭 하나씩만 떠 있기를 원하는 Pod을 만들고 싶을 때 사용
    • 로그를 수집하거나 모니터링을 하는 것에 적합
  • StatefulSet
    • 순서대로 Pod을 실행하고 싶거나 같은 볼륨을 계속 재활용하고 싶을 때
  • Job
    • 한번 실행하고 죽는 종류의 Pod을 만들 수 있다.

쿠버네티스 네트워크

쿠버네티스는 네트워크도 별도의 오브젝트로 관리한다.

Cluster IP

서비스 중에서도 ClusterIP 라는 것은 Pod들(여기서는 3개)를 로드밸런서 하는 별도의 서비스이다.

ClusterIP로 요청을 보내면 자동으로 저 3개 Pod 중에 하나로 요청이 가게 되는 것이다.

이렇게 하는 이유는 Deployment나 ReplicaSet을 보면 Pod이 업데이트 될 때, 그 IP를 유지한 상태로 업데이트 되는 것이 아니라 그냥 Pod이 죽고 새로운 Pod이 뜨는 개념이기 때문에 IP가 언제든지 사라졌다가 바뀌거나 하는 것이 자연스럽다.

그래서 요청을 보낼 때, Pod으로 바로 보내는 것이 아니라 Service라는 것을 먼저 만들고 Service의 IP는 고정이니까 거기에 요청을 보내면 뒤에 Pod은 늘던 줄던 Ip가 바뀌든 상관없이 정상적으로 원하는 Pod에 요청을 보낼 수 있기 때문에 ClusterIP 서비스를 붙이게 된다.

내부적으로는 이렇게 통신을 하게 된다.

만약 웹 서버에서 Redis로 통신을 하고 싶다면 내부 DNS에서 Redis라는 도메인이 생긴다.

그러면 Redis로 요청을 보내면 ClusterIP로 요청을 보내고, 그 요청이 다시 안쪽에 있는 Pod으로 요청이 가게 된다.

웹에서 mysql로 요청을 보내면 1차적으로 ClusterIP로 요청이 간 다음에 그 요청이 다시 mysql의 pod으로 요청이 간다.

그런데 ClusterIP는 내부에서만 통신할 수 있다. (외부 브라우저로는 접근할 수 없다.)

그래서 외부에서 접근하기 위해서 NodePort라는 개념이 생긴다.

Node에 Port가 생기고, 거기로 접속을 하면 그 요청이 다시 ClusterIP로 가고 그 요청이 다시 또 Pod로 가는 그런 구조로 되어 있다.

모든 노드에 동일한 포트로 되어있다.

웹브라우저에서 요청 → NodePort → ClusterIP → 내부에 있는 Pod들


만약에 Node가 2개가 있고, NodePort를 생성하게 되면 1번 Node에도 생기고 2번 Node에도 생기게 된다. 그래서 1번 Node로 요청을 보내도 원했던 ClusterIP로 요청이 가고, 2번 Node에도 요청을 보내도 원했던 ClusterIP로 요청이 가게 된다.

예를 들어 Node가 10개면 10개 중에 아무데나 보내도 내부에 있는 네트워크를 잘 찾아가는 그런 기능을 가지고 있다.

문제는 만약 내가 1번 노드를 도메인으로 연결을 해놓았는데 그런데 갑자기 1번 노드가 죽을 수가 있다. (서버 자체가 사라질 수가 있다. )

그럼 2번 노드로 붙어야 되는데 설정을 1번 노드로 해놓았기 때문에 순간적으로 접속이 안되게 된다.

그래서 이것을 방지하기 위해서 앞에 로드 밸런서를 별도로 둔다.

[흐름]

사용자는 LoadBalancer로 요청을 보내고, 그 요청이 다시 NodePort로 가고, NodePort가 다시 ClusterIP로 가고, ClusterIP가 다시 Pod로 가는 구조로 되어 있다.

Ingress

위의 그림처럼 전부 다 NodePort를 만들거나 전부 다 LoadBalancer를 만들면 자원이 낭비된다.

Ingress 하나를 만들고, Ingress 내부에서 Service를 분기할 수 있다.

이전에 설명한 네트워크는 IP Port로만 접속을 하는 것이라면 Ingress는 도메인 이름, 그리고 도메인 뒤에 붙는 path에 따라서 내부에 있는 ClusterIP 연결을 할 수 있다.

예를 들어서, example.com, kluster.com/blog, kluster.com/news 이런 3개의 요청이 들어왔을 때 이 세개를 전부 다 NodePort를 만들거나 전부 다 LoadBalancer를 만들면 그 자원이 낭비가 된다.

그 대신에 Ingress 하나만 만들고 그 Ingress가 내부적으로 서비스를 분기할 수 있다.

Ingress는 뭔가 새로 만든다기보다는 기존에 있었던 Nginx, HAProxy, ALB 이런 것들을 재활용해서 쿠버네티스에 맞게 포장을 해서 사용을 한다.

일반적으로 Pod만 띄우는 경우는 잘 없다. Deployment를 생성을 하고, Deployment가 ReplicaSet을 자동으로 생성하고, ReplicaSet이 자동으로 Pod를 생성하는 이런 구조를 갖는다.

그리고 이것을 외부로 노출시키기 위해서 Service(ClusterIP)를 하나 붙인다.

그리고 서비스를 붙인 다음에 Ingress를 붙인다.

Ingress를 붙이면 NodePort랑 LoadBalancer가 자동으로 따라온다.

그래서 저걸 붙인 다음에 실제 클라이언트는 이제 저 도메인으로 접속을 하게 되면은 저 LoadBalancer를 거쳐서 NodePort를 거쳐서 ClusterIP를 거쳐서 Pod로 연결되는 것이 일반적인 방법이다.

그 외 기본 오브젝트

Volume - Storage(EBS, NFS, …)

Namespace - 논리적인 리소스 구분

  • 내부적으로 리소스를 네임스페이스로 구분할 수 있다.

ConfigMap / Secret - 설정

ServiceAccount - 권한 계정

Role / ClusterRole - 권한 설정 (get, list, watch, create …)

 

API 호출

그럼 쿠버네티스는 실제로 어떻게 API를 호출할까?

Pod이라는 오브젝트를 띄우고 싶다, 생성하고 싶다면 yaml로 사용해서 생성한다.

apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  containers:
  - name: busybox
    image: busybox:1.25

명세서 작성을 하면 컨테이너가 동작을 하게 된다.

명세서를 API Server가 보고 etcd에 저장을 하고, 각 Controller들이 동작하게 된다.

API 호출한다 == 원하는 상태(desired state)를 다양한 오브젝트(object)로 정의(spec)하고 API 서버에 yaml형식으로 전달하는 것

728x90
반응형
728x90
반응형

서버 관리 == 서버의 상태관리

서버가 갑자기 죽거나, 다운되면 문제가 생긴다. 이런 문제가 생겼을 때 매번 서버에 들어가서 다시 키고 재설정해주려면 서버 관리자는 낮밤이 없어진다.. 그리고 해당 서버를 누구에게 인수인계한다고 했을 때, 모두 설명하기에 너무 어렵다.

그래서 처음에는 해당 내용을 문서로 관리하였다. 그러나 문서는 한계가 있다. 업데이트도 잘 안되고, 관리도 어렵다.

그 뒤에 나온것이 서버 관리를 코드로 하는 방법이였다. 이 때 CHEF, ansible, puppet과 같은 도구들이 나온다. 그런데 이런 도구들 사용법도 난이도가 너무 높아서 다른 방법을 고안해야 했다. 이때 고민한 점이 '어떻게 한 개의 서버에서 여러 개의 버전들을 잘 돌릴 수 있을 것인가의 문제였다.'

가상 머신을 사용하면 해결된다. 그런데 가상머신 또한 지금의 클라우드 환경에 맞지 않고 특정 벤더에(VirtualBox, VMWare)에 의존성이 생긴다. 이때 나온것이 도커와 컨테이너이다.

도커, 컨테이너

도커와 컨테이너는 가상머신과 비교하여 컨테이너 생성이 쉽고 효율적이다.

https://jobdong7757.tistory.com/192

 

도커, 도커 엔진, 컨테이너, 가상 서버, 도커 데몬

도커 컨테이너를 실행하거나 컨테이너 이미지를 만들고 배포하는 플랫폼 도커는 응용 프로그램을 실행 환경 단위로 패키지화해서 컨테이너 이미지를 만듬 컨테이너 이미지는 컨테이너 레지스

jobdong7757.tistory.com

쿠버네티스 등장배경 - '컨테이너'만으로 서비스 실행의 한계점

  1. 컨테이너 관리
    컨테이너 안에 배포되어서 애플리케이션이 실행되는 환경일 때, 각각의 컨테이너를 관리하려면 직접 하나씩 들어가야 한다.

만약 컨테이너가 몇백개, 몇천개가 된다면 절대 불가능한 일이다. 또한 만약 컨테이너들을 새로운 버전으로 업데이트하거나 롤아웃, 롤백해야할 경우가 생긴다면 컨테이너가 많을수록 수행하기가 어렵다.

  1. 서비스 검색

proxy 기능을 하는 nginx 서버 하나와 web 서버를 하나 운영하고 있다고 가정해보자.
그런데 갑자기 web 서버가 하나 늘게 되었다. 그럼 사용자는 새로 로드밸런서를 설치해서 proxy가 로드밸런서를 바라보게 할 수는 있는데, 내부 서비스 간에 통신이 많아지는 경우에는 어려워진다.

컨테이너 오케스트레이션

그래서 복잡한 컨테이너 환경을 효과적으로 관리하기 위한 도구, 기술이 나오게 되었다.

결국 서버 관리자가 하는 일들을 대신해주는 프로그램인 것이다.

컨테이너 오케스트레이션의 특징

1. Cluster

  • 클러스터 안에 여러 개의 노드들, 노드 위에 파드, 파드 안에 컨테이너가 있다고 생각할 수 있다.
  • 그리고 쿠버네티스는 이러한 노드들을 관리할 수 있게 해준다. 노드들도 갯수가 아주 많기 때문에 쿠버네티스는 마스터 노드와 워커 노드를 따로 두어서 마스터 노드에서 모든 워커 노드를 관리할 수 있게 명령어를 보낼 수 있다.

2. State

  • 쿠버네티스는 상태 관리를 해서 컨테이너에 문제가 생기면 없애고 다시 띄우는 것까지 수행한다.

3. Rollback, Rollout

  • 쿠버네티스는 배포 버전을 관리하고 중앙에서 버전을 업데이트 하거나 롤백 할 수 있다.

4. Scheduling

  • 배포 관리를 해주고 어떤 서버에 뭐가 떠있는지, 어떤 서버에 여유가 있는지 알아보는 스케쥴링 기능을 해줘야 한다.

5. Service Discovery

  • 각 서비스들을 등록하고 조회할 수 있다.

6. Volume

  • 컨테이너가 종료되더라도 파일 시스템이 유지되고 싶다면 쿠버네티스 볼륨을 사용할 수 있다. 볼륨은 Pod의 일부분으로 정의되고, Pod와 동일한 라이프사이클을 갖는 디스크 스토리지이다.

=> 물론 컨테이너 오케스트레이션 도구로는 데이즈, 메소스, 노마드, 도커 스웜 등등 있지만 그 중에서 대세는 쿠버네티스이다.

쿠버네티스 아키텍처

만약 사람 한명에게 컨테이너 하나를 띄워달라고 한다면 관리자는 서버 하나에 컨테이너 하나를 띄우면 된다.

이렇게 명령하는 상태를 'Desired State' 즉 원하는 상태라고 한다. 만약 상태 체크를 했는데 명령한 원하는 상태가 아니라면, 조치를 취해야 한다.

여기서 이제 관리자가 '컨테이너 2개를 띄워줘' 라고 한다면 여러 개의 서버 중 어떤 서버가 여유가 있는지 어떻게 더 효율적으로 사용할 수 있는지 고민하게 되는데 이 작업을 '스케쥴러' 가 하게 된다.

그리고 쿠버네티스에는 단순히 컨테이너만 있는 것이 아니고, 네트워크나 노드 등 여러 체크하는 친구들이 필요할 수 있다.

이 친구들을 Controller라고 하고, 관리자는 Controller를 계속해서 뽑아낼 수 있다.

그리고 위에서 말한 'Desired State', 원하는 상태가 여러 개가 있을 수도 있다. 그렇다면 Controller 또한 여러 종류가 될 수도 있다.

지금까지는 비유를 통해서 설명하였다. 여기서 관리자가 되어서 Scheduler나 Controller 같은 애들을 뽑아내는게 API Server가 하는 역할이다. API Server, Controller, Scheduler 등 어떤 상태를 체크하고 실행하는 영역을 쿠버네티스에서는 Master Node가 담당하게 되고, 이것이 실제로 실행되는 부분을 Worker Node라고 하게 된다.

그리고 이런 상태를 저장하고 조회할 수 있는 데이터베이스가 있는데 이 데이터베이스는 etcd라고 해서 Master에 포함된다.

Master Node

1. etcd

  • 쿠버네티스에서 모든 상태와 데이터를 저장
  • 분산 시스템으로 구성하여 안전성
  • Key - Value의 Dictionary 형태로 데이터를 저장
  • 보통은 3대 정도를 띄워놓는다.

2. API Server

  • 상태를 바꾸거나 조회
  • etcd와 유일하게 통신하는 모듈
  • REST API 형태로 제공
  • 권한을 체크하여 적절한 권한이 없을 경우 요청을 차단
  • 관리자 요청 뿐 아니라 다양한 내부 모듈과 통신
  • 수평으로 확장되도록 디자인 되어 있음

3. Scheduler

  • 새로 생성된 Pod를 감지하고 실행할 노드를 선택함.
  • 노드의 현재 상태와 Pod의 요구사항을 체크
    • 노드에 라벨을 부여해서 체크함

4. Controller

  • 논리적으로 다양한 컨트롤러가 존재
    • 복제 컨트롤러
    • 노드 컨트롤러
    • 엔드포인트 컨트롤러
  • 끊임없이 상태를 체크하고 원하는 상태를 유지
  • 복잡성을 낮추기 위해서 하나의 프로세스로 실행

조회 및 수정 흐름

중간에 API 서버가 껴서 통신을 한다.

  1. 컨트롤러는 컨트롤러가 체크하고 있는 상태를 조회할 때, etcd로 바로 요청하는 게 아니라 API 서버에 물어본다.
  2. API 서버는 해당하는 컨트롤러가 해당하는 리소스를 볼 수 있는지 권한체크를 한다.
  3. 권한이 있다고 판단이 들면 etcd에 정보를 조회해서 알려주게 된다.
  4. etcd는 원하는 상태를 변경한 것도 저장이 되니까 만약에 원하는 상태가 변경이 된다면 컨트롤러한테 지금 원하는 상태가 변경이 되었다고 알려주고, 컨트롤러는 현재 상태랑 원하는 상태가 바뀌었기 때문에 조치를 해야 한다.
  5. 그 조치를 해서 리소스 변경을 하고, 리소스 변경된 결과를 전달을 해준다.
  6. API 서버는 변경할 수 있는 쓰기 권한이 있는지 체크해서 권한이 있다면 etcd에 있는 정보를 갱신한다.

Worker Node

워커 노드에는 크게 2가지의 컴포넌트가 있다. kubelet과 proxy가 있는데 두 노드 모두 마스터랑 통신하게 되면 API 서버를 바라보게 된다.
(마이크로서비스하게 잘 되어 있다)

Proxy

  • 네트워크 프록시와 부하 분산 역할
  • 우리가 생각하는 Proxy는 이제 Pod이 요청을 보내기 전에 거쳐가거나, API Server의 요청을 Proxy가 먼저 받는건가? 라고 생각할 수 있는데,
    쿠버네티스에서 처음에 그렇게 설계하였다가 성능상 이슈가 있어서, 프록시라는 애플리케이션을 별도로 띄우지 않았다.
  • 성능상의 이유로 별도의 프록시 프로그램 대신
    → 성능상 이슈가 있었어서 프록시라는 애플리케이션을 별도로 띄우지 않고, 커널 레벨에서 굉장히 빠른 IP 테이블이나 IPVS를 사용해서 프록시가 실제로 하는 역할은 설정만 관리한다.
  • 설정을 관리하고 실제로 프록시와 부하 분산 역할을 하는 것이다.
  • iptables나 ipvs 방식을 사용(설정만 관리)

Kubelet

직접 Pod랑 통신한다.

kubelet은 각 노드에 다 떠있어야 한다. → 계속해서 Pod를 실행하고 중지하고 상태를 체크하기 때문에

  • 각 노드에 다 떠 있어야 한다
  • 각 노드에서 실행
  • Pod를 실행, 중지하고 상태를 체크
  • CRI (Container Runtime Interface)
    • (도커 말고 다른 런타임들도 있음)
    • docker
    • Containerd
    • CRI-O
  • 컨테이너를 사용할 수 있도록 kubelet이 컨테이너를 직접 쓰는 것이 아니라 Pod라는 것으로 감싸서 사용을 하게 된다고 생각할 수 있다.

Pod을 요청하게 되면 실행되는 흐름

  1. Pod 생성을 요청하면 API Server가 받음
  2. API 서버는 etcd에다가 적어놓는다.
  3. 컨트롤러가 새로생긴 Pod이 있는지 요청을 계속 확인하다가('새로 Pod 생성하라는 명령이 있나?' 체크)
    • 새 Pod 요청 확인을 하게 된다.
    • '요청이 들어왔네?'를 확인하고 Controller가 실제 Pod을 할당하는 요청을 한다.
  4. 그럼 API 서버는 다시 요청을 할당 받아서 etcd의 상태를 바꾸게 된다. ('Pod을 할당 요청 해라!')
  5. 그럼 스케쥴러는 계속 할당 요청이 들어온 Pod가 있는지 확인한다. ('Pod에 할당 요청한 명령어 없나?' 체크)
    • Pod에 할당 요청한 명령을 확인하고, 여러 개의 노드 중에서 어디에 띄울까 고민을 하다가 특정 노드에 저 pod를 할당한다.
  6. 그럼 API 서버가 다시 받아서 Pod를 특정 노드에 할당하는데 상태는 아직 실행되기 전 상태다 라고 etcd에 업데이트 한다.
  7. 그럼 Kubelet이 ‘어 내 노드에 할당된 pod 중에서 아직 실행이 안된 Pod가 있나?’ 확인을 계속 한다.
    1. 체크하다가 요청한 명령을 확인하고, 새로 추가된것이니까 pod를 하나 생성하고
    2. 그 정보를 다시 API 서버가 etcd에 업데이트를 해준다.

etcd에는 'POD이 특정 노드에 할당이 되어 있고, 실행 중이다'라는 상태로 지금 최종 업데이트가 되어 있는 상태이다.

계속해서 스케쥴러, 컨트롤러, 큐블렛은 상태를 체크하고 있는 것이다.

만약 새로 상태가 변한 것이 없다면 체크만 하고 넘어가고, 어떤 업데이트하는 과정이 없는 것이다.

추가적인 컴포넌트들

  • CNI(네트워크)
  • DNS(도메인, 서비스 디스커버리)
  • 대시보드(시각화)

[참고]

https://www.inflearn.com/course/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9E%85%EB%AC%B8/dashboard

728x90
반응형
728x90
반응형

쿠버네티스를 macOs m1 환경에서 실행하기

https://velog.io/@pinion7/macOs-m1-환경에서-kubernetes-시작하기 를 참고하였습니다.

싱글 노드 클러스터 배포판 → minikube를 사용함

kubernetes로 배포 실습

  1. kubectl로 kubernetes 클러스터 생성

kubectl apply -f wordpress-k8s.yml

  1. 생성된 클러스터와 여러 컴포넌트 리스트 확인

kubectl get all

  1. port-forwarding을 해주어야 한다

kubectl port-forward service/wordpress 4000:80

  1. 위에건 로컬로 실행하는 것이고 minikube NodePort를 통해서도 접근할 수 있다.
    minikube service wordpress

  • 커맨드를 입력하면 일단 최종 target port가 보인다. 최종적으로 도달하고자 하는 오브젝트는 Pod이기 때문에, 이건 Pod에 붙는 Port를 의미한다.
  • 다만 클러스터 내부에 있는 Pod를 외부에서 접근할 수는 없기 때문에, NodePort가 외부 트래픽을 받을 수 있는 외부 노출 URL(192.168.49.2:32374)을 만들어 준 것이다. 이렇게 NodePort를 통하면 Pod에 트래픽이 전달될 수 있다.
    • 원래는 192.168.49.2:32374로 접근이 가능해야 하는데, 말했듯 m1 운영체제의 한계로 불가능하다.
  • 그래서 NodePort url을 로컬에 포워딩 할 수 있어야 하는데, minikube service 커맨드가 그걸 가능하게 해준다.
    • 보다시피 포워딩된 로컬 url(127.0.0.1:63828)이 제공된 것이다. (참고로 로컬 포트는 랜덤하게 지정된다)
  • minikube 커맨드는 동시에 해당 로컬 url로 browser를 아래처럼 자동으로 띄워주기도 한다.

4) 실습으로 살펴본 docker와 kubernetes

  • yml 파일로 본 docker와 kubernetes의 구조적 차이
    • docker: 구조가 간단하다. docker의 기본 단위는 컨테이너다.docker-compose.yml을 보면, 하나의 Service 안에 컨테이너가 모두 포함되어 있다. 즉, compose 진행을 위해 필요한 명세는 2개 이상의 컨테이너와 포트 매핑, 기타 환경변수 정도이다.
    • kubernetes: 구조가 복잡하다. kubernetes의 기본 단위는 컨테이너가 아닌 클러스터이다. 클러스터는 최소 1개 이상의 노드로 구성이 되고, 노드에 띄워지는 워크로드를 구성하는 요소들을 오브젝트라고 칭한다. wordpress-k8s.yml 파일을 보면, wordpress 워크로드와 mysql 워크로드 각각에 Deployment & Service 리소스로 만든 오브젝트가 존재한다. 그리고 그 안에는 메타데이터, 컨테이너, 환경변수, 포트 등이 상세하게 명세되어 있다.
  • docker와 kubernetes에 대한 나의 이해..
    • 구조 파악을 통해 확인할 수 있는 차이는, 배포에 있어 필요한 명세나 스케일이 확연히 다르다는 것이다. 깊게 하나하나 공부하지 않은 현 상태에서 아직 명확한 차이를 짚을 수는 없다.
    • 또한 분명한 건 kubernetes를 사용한다고 하여 docker를 배제하는 것은 아니다. minikube만 봐도 알 수 있듯이, 분명 kubernetes 내부에서 docker에 대한 의존성을 가지고 있다. kubernetes 자체적으로 docker를 nesting해서 사용하는 것이 아닐까 싶다.
    • 명확한 kubernetes의 구조 및 철학을 이해하려면 일단 클러스터와 주요 골격이 되는 노드 및 각종 컴포넌트, 오브젝트 등의 개념부터 알아야 할 것으로 보인다. 이는 추후 블로깅에서 자세히 다뤄보겠다.쿠버네티스 환경에서 애플리케이션을 배포하는 과정
    • sudo docker build --build-arg DEPENDENCY=build/dependency -t jakeheon/githubaction --platform linux/amd64 .
    • 컨테이너 이미지 생성컨테이너 레지스트리로 이미지 업로드쿠버네티스에서 컨테이너 이미지를 바탕으로 Deployment 생성
    • 스프링 어플리케이션을 실행시키기 위해서는 gradle wrapper를 사용해 jar 파일을 만든 후 실행시켜야 합니다.
    • 스프링 프로젝트를 Container 이미지 파일로 만들기 위해서는 다양한 방법들이 존재 합니다.용어 정리 및 기타
    • kubectl apply -f deployment.yaml kubectl get deployment kubectl get pod
    • 쿠버네티스에 컨테이너화 된 어플리케이션을 배포하기 위해서는 디플로이먼트를 생성해야합니다.
    • 파드 : 쿠버네티스 클러스터에서 실행되는 최소 단위로 독립적인 공간과 사용 가능한 IP를 지니고 있음
    • 레플리카셋 : 명시된 동일 파드 개수를 항상 실행시켜주는 것을 보장 시켜주는 쿠버네티스 오브젝트
    • 디플로이먼트 : 파드와 레플리카셋에 대한 선언적인 업데이트를 제공하는 쿠버네티스 오브젝트로 오늘날에는 쿠버네티스 상에서 컨테이너를 배포할 때 디플로이먼트를 사용
728x90
반응형
728x90
반응형

ArgoCD

정의

ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes.

ArgoCD는 쿠버네티스를 위한 선언적인 GitOps CD 도구입니다.

선언적이다?

선언적 인프라

  • Infrastructure를 정의하고 관리하는 방법론 중 하나
  • 원하는 상태(Desired state)나 구성을 명시적으로 선언하고, 시스템이 명시된 상태로 수렴하도록 하는 방식
  • 쿠버네티스도 선언적 인프라의 형태를 띄고 있다.

원하는 상태(Desired state)

  • 선언적 인프라에서는 시스템 또는 애플리케이션이 원하는 상태를 정확하게 명시한다.
  • 이 상태를 정의하기 위해 코드 또는 구성 파일을 사용한다.
  • 현재 몇 개가 존재하는지는 중요하지 않고 replicas:2 라고 선언한다.

자동 수렴(Automatic Convergence)

  • 현재 상태(Current state)와 원하는 상태(desired state)간 차이를 자동으로 해결해준다.
  • 원하는 상태로 시스템이 자동으로 수렴시킨다.

상태 관리(State Management)

  • 시스템이 원하는 상태로 유지되도록 변경 사항을 추적하고 관리하는 기능을 제공한다.
  • 변경사항은 지속적으로 기록한다.
  • Mainfest의 정의를 바탕으로 Pod를 생성하게 될 것이고, pod를 유지하려고 하는 특징을 가지고 있다.
  • pod를 삭제하고 재배포한다거나 해서 최종적으로 정상상태로 도달하기 위해 상태를 계속적으로 관리한다.

추상화(Abstraction)

  • 하부의 인프라 세부 사항을 추상화하여 사용자가 구체적인 구현 세부 사항을 모르더라도 인프라를 관리할 수 있도록 함.
  • 인프라 구성을 더 쉽게 관리하고 이식성을 향상
  • 하부에 있는 인프라에 대한 세부사항을 추상화한다.
  • ArgoCD와 쿠버네티스를 사용하였다 → 인프라들은 무엇이든지 상관이 없다.
  • 쿠버네티스에서 다루고 있는 pod나 service나 pv, 와 같은 리소스들을 언제나 배포할 수 있다.

반복 가능성(Reproducibility)

  • 코드를 통해 시스템의 상태를 정의하고 이를 통해 반복 가능한 배포가 가능하며, 여러 환경에서 동일한 결과가 나올 수 있다.

특징

  • ArgoCD는 기본적으로 GitOps 기반 관리를 지원하고 있다.
    • GitOps는 모든 어플리케이션 및 구성 설정을 Git 레포지토리에 저장하고 업데이트 하는 방식으로 작동한다.
    • 모든 설정 및 변경 내역을 Versioning하고 롤백, 협업, 복구 기능을 제공한다.
  • 지정된 대상 환경에 어플리케이션 자동 배포
  • 다양한 구성 관리 툴 지원 (Helm, Kustomize)
  • SSO 제공
  • Kubernetes 멀티 클러스터 관리 기능
  • 웹 UI를 통한 시각화
  • 어플리케이션 자원 상태 자동 체크
  • 어플리케이션 상태 자동 동기화 제공

 

사용 이유

ArgoCD는 GitOps 툴 중에서 점점 많이 사용하는 추세이다. 특히나, 내가 만든 프로젝트는 애플리케이션 상태 체크가 지속적으로 필요했고,
쿠버네티스를 통한 클러스터링을 했으며 Github과 연동해서 CI, CD를 진행해야 했기 때문에 ArgoCD를 선택하게 되었다.

728x90
반응형
728x90
반응형

도커

  • 컨테이너를 실행하거나 컨테이너 이미지를 만들고 배포하는 플랫폼
  • 도커는 응용 프로그램을 실행 환경 단위로 패키지화해서 컨테이너 이미지를 만듬
  • 컨테이너 이미지는 컨테이너 레지스트리를 통해 개발 환경에서 배포 환경으로 배포

도커의 장점

  • 프로그램 속도가 빠르고, 릴리스하는데 문제가 없음
  • 응용 프로그램을 배포하기 쉽고 확장 가능

도커 엔진

  • 컨테이너 및 컨테이너 이미지를 관리하는 응용 프로그램
  • 클라이언트 서버 유형의 응용 프로그램(Client - Server)
  • 클라이언트인 도커 클라이언트에서 서버인 도커 데몬의 API에 접속해 컨테이너 및 컨테이너 이미지에 대한 다양한 작업을 수행할 수 있다.
  • 도커 클라리언트에서 도커 데몬 API에 접속하려면 도커 명령어를 실행해야 한다.

[도커 엔진을 구성하는 세가지 요소]

1. 도커 클라이언트

- 도커 데몬의 사용자 인터페이스가 되는 구성 요소다. 사용자는 도커 클라이언트를 조작해서 도커 데몬과 통신한다.

2. 도커 데몬

- 도커 클라이언트의 요청에 따라 도커 객체를 관리하는 구성 요소. 도커 클라이언트와 도커 데몬은 각각 클라이언트와 서버 역할을 하며, 도커 클라이언트의 요청에 따라 통신이 시작되고 도커 데몬의 응답으로 통신이 종료된다.

3. 컨테이너 레지스트리

- 컨테이너 이미지를 저장하기 위한 구성 요소. 도커 데몬은 도커 클라이언트의 요청에 따라 컨테이너 레지스트리와 통신해서 컨테이너 이미지를 내려받고 업로드한다.

도커 객체

- 도커 데몬의 관리 대상( 네트워크, 볼륨, 컨테이너, 컨테이너 이미지 등)

컨테이너 런타임

컨테이너를 다루는 도구

도커 엔진은 컨테이너 런타임을 포함하는 개념

도커

컨테이너 런타임 중에서 제일 유명한 것

컨테이너

  • 운영 체제에서 실행되는 프로세스인데 다른 프로세스와 격리되어 있다(외부 영향을 받지 않는 독립적인 환경에서 프로세스를 실행할 수 있다.)
    • 컨테이너를 외부와 격리하기 위해 네임스페이스라는 구조를 사용한다.

파일 시스템의 격리

  • 네임스페이스의 작동 방식을 구현하는 데 특히 중요한 것은 파일 시스템의 격리.
    • 파일 시스템 → 운영 체제의 기능 중 하나, 파일 시스템에 의해 데이터를 ‘파일’ 단위로 읽고 쓸 수 있다
  • 컨테이너는 컨테이너 전용 독립 파일 시스템을 사용한다.
  • 네임스페이스에서 파일 시스템을 격리하는 방식은 컨테이너 내 프로세스가 yum 및 apt와 같은 패키지 관리 시스템에 설치된 패키지에 의존하는 경우 특히 유용하다.
    • 리눅스 네임스페이스 : 프로세스를 실행할 때 시스템의 리소스를 분리해서 실행할 수 있도록 도와주는 기능
    • 패키지 : 어떤 기능을 제공하는 프로그램을 한꺼번에 배포 형식으로 만든 것
    • 패키지 관리 시스템 : 패키지의 설치나 종속성을 관리하는 시스템 (yum, apt, brew)
  • 대부분의 경우 운영 체제의 파일 시스템에 다른 버전의 패키지가 공존하기는 어렵다.
  • 컨테이너를 사용하면 다른 버전의 패키지가 공존할 수 있다.

컨테이너와 가상 서버의 차이

  • 컨테이너와 가상 서버는 매우 비슷하다.
  • 결정적인 차이
    • 컨테이너는 본질적으로 프로세스와 동일
      • 하드웨어 에뮬레이션 없이 리눅스 커널을 공유해서 프로세스를 실행 
      • 네임스페이스와 같은 방식으로 다른 컨테이너와 프로세스에서 격리됨
    • 가상 서버는 하드웨어를 모방하는 소프트웨어
      • 운영체제 위에 하드웨어를 에뮬레이션하고 그 위에 운영체제를 올리고 프로세스 실행. 
      • 가상 서버에 운영 체제를 설치해 다른 가상 서버나, 가상 서버를 실행하는 물리 서버와 격리함.
    • 컨테이너는 다른 컨테이너와 프로세스와 운영체제(커널)을 공유함
    • 가상서버는 다른 가상 서버나 물리 서버와 운영 체제를 공유하지 않음
  • 컨테이너를 가상 서버와 비슷하다고 생각하는 것보다 특수한 프로세스라고 생각하는 편이 좋다.

컨테이너로 호스트 OS와 다른 OS를 사용한다는 말의 의미

  • 컨테이너는 호스트 머신의 운영 체제를 이용하고 있지만, 어떤 컨테이너가 우분투에 포함되는 소프트웨어 세트를 갖추고 있다면 그 컨테이너 내에서 실행되는 프로세스를 마치 우분투에서 실행되는 것처럼 보이게 할 수 있다.
  • 여러 운영 체제가 공존할 수 있다.

컨테이너가 있으면 가상 서버는 필요 없다?

  • 컨테이너는 운영 체제를 공유하므로 운영 체제 버전의 차이와 호환성에 따라 제대로 작동하지 않을 수 있다.
  • 반면 가상 서버는 각각 운영 체제를 설치하므로 운영 체제를 포함한 동일한 환경을 재현할 수 있다.

컨테이너 이미지

  • 컨테이너를 실행하기 위한 템플릿 → 컨테이너는 컨테이너 이미지로 생성
    • 클래스(컨테이너 이미지) - 객체(컨테이너)
  • 컨테이너 이미지의 대부분은 컨테이너를 실행하는 데 필요한 파일 시스템
    • 파일 시스템은 레이어라는 층이 겹쳐서 구성됨
    • 도커의 차분 관리와도 관련이 있다.

컨테이너 이미지의 생성

  • 컨테이너 이미지는 기본이 되는 컨테이너 이미지의 파일 시스템에 새로운 레이어가 겹쳐서 생성된다.
    • 예를 들어, Ubuntu OS 컨테이너 이미지가 있고, 이 컨테이너에 웹 서버인 nginx 프로그램과 구성 파일이 포함된 레이어를 겹쳐서 Ubuntu OS에서 실행되는 nginx 서버의 이미지를 만들 수 있다.
  • 컨테이너에서 셸을 기동한 후에 패키지 관리 시스템을 실행해서 수동으로 작성할 수 있다.
  • 그러나 보통은 컨테이너 생성 절차를 설명하는 텍스트 파일인 도커 파일을 사용해서 자동으로 만든다.

운영 체제 설치 디스크와 컨테이너 이미지로 운영 체제 사용하는 것의 차이

운영 체제 설치 디스크는 필요한 것을 복사해서 설치하는 것이지만, 컨테이너 이미지로 운영 체제를 사용하는 개념은 프로그램과 설정 파일을 컨테이너에서 접근해서 사용하는 것이다.

[컨테이너와 도커의 활용]

보통 우리가 애플리케이션을 개발하면, 로컬에서 앱을 만들고, OS, 네트워크 환경을 설정하고, 라이브러리나 미들웨어를 설치하여서 만든다. 근데 문제는 로컬에서는 잘 되던 프로그램이 실제 환경에 배포하면 정상적으로 움직이지 않는다. 이건 환경이랑 설정이 달라서 생기는 문제이다.

컨테이너를 사용하면 애플리케이션 실행에 필요한 모든 파일과 디렉토리를 통째로 컨테이너 이미지로 모을 수 있다.

그럼 우리가 앱을 개발해서 Dockerfile을 이용해서 컨테이너 이미지를 작성한다. 이 이미지는 OS 커널과 호환성이 있어서 컨테이너가 작동하는 환경이라면 이미지를 받아서 어디서든지 작동시킬 수 있다. 그럼 컨테이너를 작동시키는 곳에서는 어디서든 가능하게 되는 것이다.

컨테이너 이미지를 관리해주는 것은 레지스트리이다. 컨테이너 이미지는 레지스트리로 공유할 수 있다. 도커의 공식 레지스트리는 Docker Hub이다. 그럼 이제 우리가 Ubuntu같은 OS를 사용하고 싶으면 Ubuntu 베이스 이미지를 받아서 사용하면 된다.

(private 환경이라면 컨테이너 레지스트리는 도커 hub와 같은 시스템을 별도로 사용해야 한다)

 

 

728x90
반응형

+ Recent posts