[kuber-study] Chapter2. First steps with Docker and Kubernetes

jeonghyun yu·2022년 1월 11일
0

kuber-study

목록 보기
1/7
post-thumbnail

1. 컨테이너 이미지 생성, 실행, 공유

환경 : docker가 설치된 linux machine
docker 다운로드 참고


Hello World 컨테이너 실행

$ docker run busybox echo "Hello world"

docker에서는 다운로드나 설치 없이 docker run 명령어로 다른 프로세스들과 분리된 컨테이너에서 애플리케이션을 실행할 수 있다.

  • 동작 원리

    1) docker가 busybox:latest 이미지가 로컬에 존재하는지 확인
    2) 없다면 docker hub registry에서 이미지를 pull
    3) 이미지 다운로드 후 docker가 컨테이너를 생성하고, 컨테이너 안에서 명령어를 실행
    4) 문장을 출력한 후 프로세스는 삭제되고 컨테이너는 stop

  • 이미지 실행
    $ docker run <image>
    $ docker run <image>:<tag>

Node.js 애플리케이션 생성

  • app.js
const http = require('http');
const os = require('os');

console.log("Kubia server starting...");

var handler = function(request, response) {
 console.log("Received request from " + request.connection.remoteAddress);
 response.writeHead(200);
 response.end("You've hit " + os.hostname() + "\n");
};

var www = http.createServer(handler);
www.listen(8080)

node.js 애플리케이션을 실행시키기 위해서는 node.js 런타임이 설치되어야 하지만, docker를 사용해서 컨테이너 이미지 안에 앱을 패키징 한다면 설치과정이 필요 없다.


컨테이너 이미지

  • Dockerfile
FROM node:7
ADD app.js /app.js
ENTRYPOINT ["node", "app.js"]

docker가 이미지를 빌드할 때 실행 할 목록이 담긴 파일. app.js와 같은 폴더에 있어야 한다.

- FROM : 시작점으로 사용할 컨테이너 이미지
- ADD : 로컬 디렉토리의 app.js를 이미지의 root 디렉토리 안에 app.js로 추가
- ENTRYPOINT : 이미지를 실행시켰을 때 실행 될 명령어

  • 컨테이너 이미지 빌드
    $ docker build -t kubia .

    dockerfile 목록에 따라 kubia 이미지를 빌드한다.

  • Dockerfile로 부터 새로운 컨테이너 이미지 빌드

    1) docker client가 현재 디렉토리(.)의 컨텐츠를 docker 데몬에 업로드
    2) docker가 이미지 node:7.0을 로컬에서 찾거나 hub에서 pull한다
    3) docker 데몬에 새로운 이미지 kubia:latest를 빌드

  • 이미지 레이어
    이미지는 여러 개의 레이어로 구성되어있다.
    이미지를 빌드할 때 base 이미지의 모든 레이어를 가져온 뒤 그 위에 새 레이어를 만들고 해당 레이어에 kubia:latest 이미지에 필요한 파일들을 추가한다.
    docker는 이미지를 설치할 때 각 레이어를 개별적으로 다운로드하고, 이미 다운로드 된 레이어를 제외하고 필요한 레이어만을 다운로드한다.

    밑의 그림과 같이 다른 이미지들 사이에서 같은 레이어를 공유할 수 있다.


  • 로컬에 저장된 이미지 목록 출력
    $ docker images


이미지로 컨테이너 실행

  • 컨테이너 이미지 실행
    $ docker run --name kubia-container -p 8080:8080 -d kubia

    kubia 이미지를 사용하여 컨테이너를 실행한다.
    --name : 컨테이너 이름
    -p : 로컬의 port 8080을 컨테이너의 port 8080으로 맵핑 (http://localhost:8080 으로 애플리케이션 접속 가능)
    * -d (detached) : background로 실행

    $ curl localhost:8080
    컨테이너 ID를 반환하면 컨테이너 안에서 애플리케이션이 돌아간다는 것이다.

  • 실행중인 컨테이너 목록 출력
    $ docker ps

  • 컨테이너 정보를 JSON 형태로 출력
    $ docker inspect <container-name>

  • 컨테이너에서 bash shell 실행
    $ docker exec -it kubia-container bash

    kubia-container 컨테이너 안의 bash 쉘을 실행한다.
    -i : STDIN을 유지
    -t : 가상 터미널 할당 (TTY)
    + exit : 쉘 종료

  • 컨테이너 안에서 실행되는 프로세스 출력
    # ps aux

    각 컨테이너는 각자의 PID linux namespace를 사용하며, 독립된 프로세스 트리를 가지기 때문에 host OS의 다른 프로세스들은 출력되지 않는다.

  • host OS에서 실행되는 컨테이너의 프로세스 출력
    $ ps aux | grep app.js

  • 독립된 컨테이너의 파일시스템
    # ls /

    해당 컨테이너 안의 파일들만 보여준다.

  • 컨테이너 종료
    $ docker stop <container-name>
    docker ps -a 옵션으로 검색 시 종료 된 컨테이너도 확인 가능

  • 컨테이너 삭제
    $ docker rm <container-name>


Docker Hub에 이미지 업로드

  빌드한 이미지를 다른 시스템에서 사용하려면, 외부 이미지 레지스트리에 이미지를 등록해야한다. (ex: Docker Hub, Quay.io, Google Container Registry)
이미지의 레파지토리 이름은 본인의 Docker Hub ID로 시작해야한다. (예시에서는 luksa)

  • 이미지에 태그 추가
    $ docker tag <image-name> <dockerHubID>/<image-name>

    같은 이미지 ID를 가진 이미지가 하나 더 생성된다. 즉, 하나의 이미지가 두개의 태그를 가지는 것.

  • 도커 허브 로그인
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. ...
Username: <dockerHubID>
Password:
Login Succeeded

  • 도커 허브에 이미지 등록
    $ docker push luksa/kubia


  • 다른 시스템에서 이미지 실행
    $ docker run -p 8080:8080 -d luksa/kubia

이제 사용자의 애플리케이션은 모든 시스템에서 Node.js가 설치되지 않아도 같은 환경으로 실행될 수 있다.

2. 쿠버네티스 클러스터 설정

  다중 노드 쿠버네티스 클러스터를 구성하는 것은 쉽지 않다. 단일화된 네트워크 환경을 통해 쿠버네티스 클러스터 안의 모든 컨테이너가 연결될 수 있도록 여러 물리/가상 머신에 걸쳐 설치하고, 적절하게 네트워킹 설정이 되는 것을 요구한다. 쿠버네티스 클러스터를 설치하는 메소드들은 http://kubernetes.io 에서 확인 가능하다.


Minikube

  Minikube는 단일 노드 클러스터를 설정할 수 있는 툴이다. 쿠버네티스 테스트와 로컬 앱 개발에 용이하다.

  • minikube 환경 구성
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/\
v0.23.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube\
/usr/local/bin/

Minikube 다운로드 참고

  • minikube 시작
    $ minikube start

  • kubernetes CLI 클라이언트 설치 (kubectl)
$ curl -LO https://storage.googleapis.com/kubernetes-release/release\
/$(curl -s https://storage.googleapis.com/kubernetes-release/release\
/stable.txt)/bin/linux/amd64/kubectl\
&& chmod +x kubectl\
&& sudo mv kubectl /usr/local/bin/

  • 클러스터 정보 확인
    $ kubectl cluster-info

    쿠버네티스의 구성요소들을 보여주는 URL이 출력되면 정상 동작


Google Kubernetes Engine(GKE)으로 호스팅 쿠버네티스 클러스터 사용

GKE 환경 설정을 참고
GKE를 사용하면 직접 모든 클러스터 노드와 네트워킹을 설정 하지않아도 된다.

  • 3개의 worker 노드를 가진 클러스터 생성
    $ gcloud container clusters create kubia --num-nodes 3 --machine-type f1-micro

  • 3개의 워커 노드와 마스터 노드의 연결 이해

    각 워커 노드에서는 docker, kubelet, kube-proxy가 돌아간다. 사용자는 마스터 노드의 REST API 서버에 REST 요청을 발생시키는 kubectl 명령어를 사용한다.

  • 클러스터 노드 출력
    $ kubectl get nodes

  • 노드의 자세한 정보 출력
    $ kubectl describe node <node-name>

    노드 상태, CPU, 메모리 데이터, 시스템 정보, 노드에서 실행중인 컨테이너 등을 출력한다.
    node-name 없이 입력하면, 모든 노드의 정보를 출력한다.


별칭 설정

  • alias 생성
    alias k=kubectl
    ~/.bashrc 파일에 추가해주면 alias로 사용이 가능


3. 쿠버네티스에서 애플리케이션 실행

  • Docker Hub의 이미지로 컨테이너 생성
    $ kubectl run kubia --image=luksa/kubia --port=8080 --generator=run/v1

    --image : 컨테이너 이미지 설정
    --port : 애플리케이션이 리스닝할 port번호 설정
    * --generator : deployment 대신 ReplicationController 생성


Pods

  쿠버네티스는 개별 컨테이너를 직접 관리하지 않고 함께 배치된 여러 컨테이너들을 다룬다. 이 컨테이너 그룹을 pod라고 한다.

  pod는 하나 이상의 밀접하게 연관된 컨테이너의 그룹이다. 컨테이너들은 같은 리눅스 네임스페이스의 같은 워커 노드에서 실행된다. 각 pod는 논리 머신으로 분리되어있는 것처럼 각각 단일 애플리케이션이 돌아가는 IP, hostname, process 등을 가진다. 하나의 pod 안에서 실행되는 컨테이너들은 같은 논리 머신에서 작동되는 것으로 보이는 반면에, 다른 pod 안에서 실행되는 컨테이너들은 같은 워커노드에서 실행될지라도 다른 논리 머신에서 작동되는 것으로 보인다.

각 pod는 각자의 IP와 컨테이너(하나 혹은 그 이상)와 애플리케이션 프로세스를 포함한다. pod는 다른 워커 노드들 사이에 분배되어있다.

  • pod 목록 출력
    $ kubectl get pods

    각 컨테이너들 목록을 확인할 수 없는 대신 pod 목록 확인은 가능하다.

  • pod 상세 정보 확인
    $ kubectl describe pod <pod-name>

  • 컨테이너 이미지가 쿠버네티스에서 실행되는 과정

    1) 사용자가 이미지를 빌드하고 docker hub에 등록
    2) kubectl 명령어를 실행시키면, 쿠버네티스 API 서버로 REST HTTP 요청을 보내서 클러스터에 새로운 ReplicationController 객체를 생성
    3) ReplicationController는 스케줄러에 의해 워커노드 중 하나에 새로운 pod를 할당
    4) kubelet이 docker에게 이미지를 pull하라고 지시
    5) 이미지 다운로드 후 docker는 컨테이너를 생성 및 실행

웹 애플리케이션 접속

  pod는 IP 주소를 가지지만, 클러스터 내부 IP이기때문에 외부에선 접근이 불가능하다. 외부에서도 접근이 가능하도록 하려면 service 객체에 등록해야한다.

  • ReplicationController 외부 노출
    $ kubectl expose rc kubia --type=LoadBalancer --name kubia-http

    - rc : ReplicationController의 축약형
    +) po = pods, svc = services

  • 서비스 목록 출력
    $ kubectl get services

    service는 pods나 nodes같은 객체이다. 즉, kubectl 명령어로 확인 가능하다.

  • 출력된 외부 IP로 서비스 접속
    $ curl <external-ip>:<port>

    앱이 3개의 노드 쿠버네티스 클러스터의 어딘가에서 실행됨을 확인할 수 있다.
    (minikube라면 single node, ip와 port는 "minikube service kubia-http"를 실행시키면 얻을 수 있다.)


현재 시스템의 논리적인 이해

  • ReplicationController, Pod, Service로 구성된 시스템
    사용자는 kubectl run 명령어로 rc를 생성하고, rc가 pod를 생성한 것. pod를 클러스터 외부에서 접속이 가능하도록 해당 rc가 관리하는 모든 pod를 단일 서비스로 노출하도록 쿠버네티스에게 지시하였다.

  • pod와 컨테이너
    현재 시스템에서는 pod가 단일 컨테이너를 포함한다(보통은 여러개의 컨테이너를 포함). 컨테이너 내부에는 port 8080으로 바인딩되고, HTTP 요청을 기다리는 node.js 프로세스가 있다. pod는 고유한 사설 IP와 hostname을 가진다.

  • ReplicationController의 역할
    kubia의 rc는 항상 하나의 instance가 실행됨을 보장한다. 보통, rc는 pod를 복제하고 실행 상태을 유지하도록 하는 역할을 맡는다. 현재 시스템에서는 pod 복제를 명시하지 않았으므로 단일 pod만을 생성한 상태이다. pod가 어떤 이유에서든 사라진다면, rc가 pod를 재생성할 것이다.

  • service의 역할
    pod는 여러가지 이유에 의해 사라지기 쉽다. rc가 재생성 했을 시, pod는 다른 IP주소를 갖는다. 끊임없이 변화하는 IP주소 문제를 해결하기 위하여 단일 상수 IP와 port 쌍으로 pod를 노출시키는 service가 필요하다.

    service가 생성되면 고정 IP를 부여받는데 이 IP는 service가 살아있는 동안엔 바뀌지 않는다. 때문에 pod로 직접 연결하지 않고 service를 통한 연결방법을 사용한다.

    service는 같은 서비스를 제공하는 pod그룹을 위해 정적 location을 제공한다. service로 오는 요청들은 service에 속한 pod 중 하나의 IP와 port로 향할 것이다.


수평적 확장

  현재까지 ReplicationController에 의해 모니터링되고 실행이 유지되며 service를 통해 세계에 노출되어있는 애플리케이션을 생성했다.
kubernetes의 장점 중 하나는 deployments를 확장할 수 있다는 점이다.

  • ReplicationController 목록 확인
    $ kubectl get replicationcontrollers
    $ kubectl get rc

    - DESIRED : 유지하기를 원하는 pod 복제본 개수
    - CURRENT : 현재 실행중인 pod 개수

  • 1이던 desired 복제본 개수 추가
    $ kubectl scale rc kubia --replicas=3

    kubernetes에게 정확히 무슨 액션을 할 지 지시하는 대신 사용자는 시스템의 요청 상태값을 변경해주고 쿠버네티스가 상태값을 확인하고 조정하도록 하는 방식은 쿠버네티스의 가장 핵심적인 원리 중 하나이다.

    kubectl get rc로 3개로 바뀐 요청값을 확인하고, kubectl get pods로 3개의 pod가 실행되고 있음을 확인할 수 있다.

    curl <외부IP>:<port>로 접근 시 출력되는 결과로 pod들에 랜덤하게 요청이 가는 것을 확인할 수 있다. 즉, service가 다중 pod들 앞에서 로드밸런서 역할을 한다는 것을 알 수 있다.

  • 현재 시스템의 상태

    단일 service와 단일 replicationcontroller와 rc가 관리하는 세개의 pod
    service는 요청을 세개의 pod들에게 나눠준다.


앱이 실행중인 노드 검사

  kubernetes에서는 pod가 올바르게 실행만 된다면 어떤 node에서 pod가 실행되는지는 중요하지 않다. 어떤 노드에서 실행되는지에 상관없이 컨테이너 내에서 실행되는 모든 애플리케이션이 동일한 유형의 OS 환경을 갖게된다. 때문인지 kubectl get pods 명령어는 노드에 관한 정보는 알려주지 않아서 옵션을 추가해야 노드 확인이 가능하다.

  • pod IP, pod의 node 출력
    $ kubectl get pods -o wide

    또는

    $ kubectl describe pod <pod-name>

    pod의 자세한 정보를 보여주는 명령어에서도 node 확인이 가능하다. 추가적으로 생성 시간, 사용 이미지 등 다른 정보도 확인 가능


쿠버네티스 dashboard

GUI, 그래픽으로 pod, rc, service 등의 객체가 확인하고 싶다면 사용

$ kubectl cluster-info | grep dashboard
출력되는 URL로 dashboard 접속

$ gcloud container clusters describe kubia | grep -E "(username|password):"
출력되는 password와 username 입력

0개의 댓글