JD의 블로그

Docker와 Kubernetes 본문

클라우드/GCP

Docker와 Kubernetes

GDong 2020. 1. 24. 20:50

Docker

  • Docker는 인프라에서 애플리케이션을 분리하고 인프라를 관리형 애플리케이션처럼 처리할 수 있게 해준다.
  • 코드를 더욱 빠르게 출시, 테스트, 배포하고 코드 작성과 실행 주기를 단축하는데 도움이 된다.

Docker 기본 

 

hello world 컨테이너를 실행한다.

docker run hello-world

실행된 컨테이너는 Hello from Docker! 을 반환한다.

 

처음 hello world 컨테이너를 실행하면 Docker 데몬이 hello-world 이미지를 검색한다, 그러나 로컬에서 이미지를 찾지 못했기 때문에, Docker Hub라는 공개 레지스트리에서 이미지를 가져온 후, 가져온 이미지에서 컨테이너를 생성하고, 컨테이너를 실행한다.

 

다음 명령어로, 가져온 컨테이너 이미지를 확인할 수 있다.

docker images

출력된 결과:

REPOSITORY     TAG      IMAGE ID       CREATED       SIZE
hello-world    latest   1815c82652c0   6 days ago    1.84 kB

이미지 ID는 SHA256 해시 형식이며, 이 필드를 통해 프로비저닝된 Docker 이미지를 지정하게 된다.

Docker 데몬은 로컬에서 이미지를 찾을 수 없으면 기본적으로 공개 레지스트리에서 이미지를 검색한다. 

 

다음 명령어를 통해, 실행중인 컨테이너를 확인할 수 있다.

docker ps

출력된 결과:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

실행 중인 컨테이너가 없는데, 그 이유는 방금 실행한 hello-world 컨테이너가 이미 종료되었기 때문이다.

 

실행이 완료된 컨테이너를 포함하여 모든 컨테이너를 보려면 아래의 명령어를 실행해야한다.

docker ps -a

출력된 결과:

CONTAINER ID      IMAGE           COMMAND      ...     NAMES
6027ecba1c39      hello-world     "/hello"     ...     elated_knuth
358d709b8341      hello-world     "/hello"     ...     epic_lewin

보통 컨테이너 이름은 무작위로 생성되지만, docker run --name [지정하고 싶은 컨테이너 이름] hello-world 명령어를 통해 지정할 수 있다.

 

-Docker 빌드

 

Docker 이미지를 빌드하기 위해 Dockerfile이 필요하다.

 

# 공식 노드 런타임을 상위 이미지로 사용한다.
FROM node:6

# 컨테이너의 작업 디렉토리를 /app으로 설정한다.
WORKDIR /app

# 현재 디렉토리 내용을 /app에 있는 컨테이너에 복사한다.
ADD . /app

# 컨테이너의 포트 80을 외부에 공개합니다.
EXPOSE 80

# 컨테이너가 시작될 때 노드를 사용하여 app.js를 실행합니다.
CMD ["node", "app.js"]

 이 파일은 Docker 데몬에게 이미지를 빌드하는 방법을 알려줍니다.

 

app.js 파일은 HTTP 서버로 포트 80을 수신하고 "Hello World"를 반환하는 노드 애플리케이션이다.

 

Docker 이미지를 빌드하기 위해 Dockerfile이 있는 디렉토리를 현재 디렉토리로 설정해야하며, 현재 디렉토리에서 아래의 명령어를 실행한다.

 

docker build -t node-app:0.1 .

-t 옵션은 name:tag 구문을 사용하여 이미지의 이름과 태그를 지정하는 역할을 한다.

이미지의 이름은 node-app 이고 태그는 0.1이다.

Docker 이미지를 빌드할 때 태그를 사용하는 것을 권장한다 왜냐하면 default로 latest로 지정되기 때문에 최신 이미지와 기존 이미지를 구분하기 어려워지기 때문이다. 

 

-Docker 실행

 

아래의 코드를 통해 빌드한 이미지를 기반으로 하는 컨테이너를 실행한다.

docker run -p 4000:80 --name my-app -d node-app:0.1

--name 옵션을 통해 원하는 컨테이너 이름을 지정할 수 있다.

-p 옵션은 Docker가 컨테이너의 포트 80에 호스트의 포트 4000을 매핑하도록 지시하는 플래그이다.

-d 옵션은 컨테이너를 터미널 세션에 종속시키기 않고 백그라운드에서 실행할 수 있게 해준다.

 

출력된 결과:

Server running at http://0.0.0.0:80/

 

이제 http://localhost:4000에서 서버에 접속할 수 있다. 포트 매핑이 없으면 localhost에서 컨테이너에 접속할 수 없다.

 

서버를 테스트기 위해 아래의 명령어를 입력한다.

curl http://localhost:4000

 

출력된 결과:

Hello World

다음 명령어를 통해 컨테이너를 중단하고 삭제할 수 있다.

docker stop my-app && docker rm my-app

 

-Docker 디버깅

 

아래의 명령어를 통해 로그 결과를 확인할 수 있다.

docker logs -f [컨테이너 Id]

컨테이너가 실행 중일 때 로그 결과를 확인하려면 -f 플래그를 추가해야한다.

 

실행 중인 컨테이너에서 대화식 Bash 세션을 시작해야할 경우 아래의 명령어를 사용한다.

docker exec -it [컨테이너 id] bash

-it 플래그는 pseudo-tty를 할당하고 stdin을 열린 상태로 유지하여 컨테이너와 상호작용 할 수 있도록 해준다.

 

Docker inspect를 통해 Docker에서 컨테이너의 메타데이터를 조회할 수 있다.

docker inspect [컨테이너 id]

 

출력된 결과:

[
    {
        "Id": "xxxxxxxxxxxx....",
        "Created": "2020-01-24T22:57:49.261726726Z",
        "Path": "node",
        "Args": [
            "app.js"
        ],
...

 

 

Kubernetes

  • 쿠버네티스는 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식성 있고, 확장 가능한 오픈소스 플랫폼이다.

과거의 배포 

  • 애플리케이션을 물리 서버에서 실행
  • 한 물리 서버에서 여러 애플리케이션의 리소스 한계를 지정할 수 없어, 리소스 할당의 문제가 발생
    • 일부 애플리케이션의 성능이 저하될 수 있다.
    • 해결책 : 서로 다른 여러 물리 서버에서 각 애플리케이션을 실행하는 것
      • 이러한 방법은 리소스가 충분히 활용되지 않으며 확장 가능하지 않다는 의미가 된다. 
      • 따라서, 물리 서버를 많이 유지하기 위해 많은 비용이 들었다.

가상화의 도입

  • 단일 물리 서버의 CPU에서 여러 가상 시스템(VM)을 실행
  • 가상화를 사용하여 VM간에 애플리케이션을 격리
    • 애플리케이션의 정보를 다른 애플리케이션에서 자유롭게 액세스 할 수 없어, 일정 수준의 보안성 제공
    • 물리 서버에서 리소스를 보다 효율적으로 활용
      • 쉽게 애플리케이션을 추가, 업데이트
      • 하드웨어 비용을 절감 => 개선된 확장성
  • 각 VM은 가상화된 하드웨어 상에서 자체 OS를 포함한 시스템

컨테이너 기반 

  • 컨테이너는 VM과 유사하지만 격리 속성을 완하하여 애플리케이션 간에 OS를 공유한다.
  • VM과 마찬가지로 자체 파일 시스템, CPU, 메모리, 프로세스 공간 등을 가짐
  • 기본 인프라와의 종속성을 끊어 클라우드나 OS 배포본에 모두 이식 가능
    • 컨테이너의 장점
      • VM 이미지를 사용하는 것에 비해 컨테이너 이미지 생성이 보다 쉽고 효율적이다.
      • CI/CD and deploy : 이미지의 불변성 때문에 안정적이고 주기적으로 빌드하고 배포할 수 있으며 빠르고 쉽게 롤백할 수 있다.
      • 자원 격리 : 애플리케이션 성능을 예측 가능
      • 자원 사용량 : 고효율 고집적
      • 모놀리식 스택에서 구동되지 않고 보다 작고 독립적인 단위로 쪼개져서 동적으로 배포, 관리 될 수 있다.

쿠버네티스가 제공하는 것

  • 프로덕션 환경에서는 애플리케이션을 실행하는 컨테이너를 관리해야할 필요성이 있다
    • 쿠버네티스는 컨테이너 기반 분산 시스템을 탄력적으로 실행하기 위한 프레임 워크를 제공한다.
    • 애플리케이션의 확장과 장애 조치를 처리하고, 배포 패턴 등을 제공한다.
  • 쿠버네티스는 DNS 이름을 사용하거나 자체 IP 주소를 사용하여 컨테이너를 노출 할 수 있다. 또한 네트워크 트래픽을 로드밸런싱하고 배포하여 배포가 안정적으로 이루어질 수 있도록 만든다.
  • 로컬 저장소, 퍼블릭 클라우드 내의 저장소 시스템을 자동으로 탑재할 수 있다.
  • 쿠버네티스를 사용하여 배포된 컨테이너의 원하는 상태를 서술할 수 있으며 현재 상태를 원하는 상태로 설정한 속도에 따라 변경할 수 있다. 
  • 쿠버네티스 클러스터 노드를 통해 각 컨테이너가 필요로 하는 CPU와 메모리(RAM)을 쿠버네티스에 지시하며, 컨테이너를 노드에 맞추어서 리소스를 가장 잘 사용할 수 있게 해준다.
  • 쿠버네티스는 실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하며 상태 검사(Health check)에 응답하지 않는 컨테이너를 죽이고, 서비스 준비가 끝날 때까지 그러한 과정을 클라이언트에게 보여주지 않는다.
  • 쿠버네티스를 사용하면 암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리할 수 있다.

 

Kubernetes Engine 클러스터 만들기

  • 클러스터는 하나 이상의 클러스터 마스터 머신과 노드라는 다수의 작업 머신으로 구성된다. 노드는 클러스터를 구성하기 위해 필요한 Kubernetes 프로세스를 실행하는 Compute Engine VM 인스턴스이다.

 

GKE 에서 클러스터를 생성하기 위해서 다음과 같은 명령어를 사용한다.

gcloud container clusters create [클러스터 이름]

 

출력된 결과:

NAME        LOCATION       ...   NODE_VERSION  NUM_NODES  STATUS
클러스터 이름  us-central1-a  ...   1.13.11-gke.9  3          RUNNING

 

클러스터를 만들고 클러스터와 상호작용하기 위해서는 사용자 인증 정보를 얻어야 한다.

gcloud container clusters get-credentials [클러스터 이름]

 

클러스터에 애플리케이션 배포하기

  • GKE 에서는 Kubernetes 객체를 사용해 클러스터의 리소스를 만들고 관리한다. 
  • Kubernetes에서는 Stateless 애플리케이션의 배포를 위해 Deployment 객체를 제공한다.
  • Service 객체는 인터넷에서 애플리케이션에 액세스하기 위한 규칙 및 부하 분산 방식을 정의한다.

kubectl create 명령어를 실행하여 컨테이너 이미지에서 배포된 객체를 생성할 수 있다. 

kubectl create deployment hello-server --image=gcr.io/google-sample/hello-app:1.0

 

출력된 결과:

deployment.apps/hello-server created

 

kubectl expose 명령어를 통해 Kubernetes service를 생성한다. service는 애플리케이션을 외부 트래픽에 노출할 수 있게 해주는 Kubernetes 리소스이다.

kubectl expose deployment hello-server --type=LoadBalancer --port 8080

type="LoadBalancer"를 통해 컨테이너의 Compute Engine 로드 밸런서가 생성된다.

 

kubectl get 명령어를 통해 생성된 hello-server 서비스를 검사한다.

kubectl get service

 

마지막으로 클러스터를 삭제하기 위해 다음의 명령어를 실행한다.

gcloud container clusters delete [클러스터 이름]