feat: recover 2 kubernetes articles (#25)

* feat: recover 2 kuberenetes posts (#19)

* [PUBLISHER] upload files #14

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : 01.jpeg

* [PUBLISHER] upload files #15

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : k8s-01.jpeg

* DELETE FILE : assets/img/posts/01.jpeg

* [PUBLISHER] upload files #16

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : k8s-01.jpeg

* [PUBLISHER] upload files #17

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* [PUBLISHER] upload files #18

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* DELETE FILE : _posts/Development/Kubernetes/Part I - Overview/2021-02-28-02-first-steps-with-docker-and-kubernetes.md

* [PUBLISHER] upload files #20

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : k8s-01.jpeg

* DELETE FILE : assets/img/posts/k8s/k8s-01.jpeg

* DELETE FILE : assets/img/posts/k8s/k8s-02.jpeg

* [PUBLISHER] upload files #21

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : k8s-01.jpeg

* DELETE FILE : assets/img/posts/k8s/k8s-01.jpeg

* DELETE FILE : assets/img/posts/k8s/k8s-02.jpeg

* [PUBLISHER] upload files #22

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* DELETE FILE : assets/img/posts/k8s/k8s-02.jpeg

* [PUBLISHER] upload files #23

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : k8s-01.jpeg

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* DELETE FILE : _posts/Development/Kubernetes/Part I - Overview/2021-02-28-01-introducing-kubernetes.md

* DELETE FILE : _posts/Development/Kubernetes/Part I - Overview/2021-03-07-02-first-steps-with-docker-and-kubernetes.md

* [PUBLISHER] upload files #24

* PUSH NOTE : 수학 공부에 대한 고찰.md

* PUSH NOTE : 01. Introducing Kubernetes.md

* PUSH ATTACHMENT : k8s-01.jpeg

* PUSH NOTE : 02. First Steps with Docker and Kubernetes.md

* PUSH ATTACHMENT : k8s-02.jpeg

* PUSH NOTE : 블로그 이주 이야기.md

* PUSH ATTACHMENT : blog-logo.png

* PUSH ATTACHMENT : github-publisher.png
This commit is contained in:
2023-07-01 13:39:08 +09:00
committed by GitHub
parent 3dd9aae35e
commit 60ccd739e8
5 changed files with 519 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
---
share: true
toc: true
categories: [Development, Kubernetes]
tags: [kubernetes, sre, devops, docker]
title: "01. Introducing Kubernetes"
date: "2021-02-28"
github_title: "2021-02-28-01-introducing-kubernetes"
image:
path: /assets/img/posts/k8s-01.jpeg
---
![k8s-01.jpeg](../../../assets/img/posts/k8s-01.jpeg) _Overview of Kubernetes Architecture (출처: https://livebook.manning.com/book/kubernetes-in-action/chapter-1)_
기존에는 소프트웨어가 커다란 덩어리였지만 최근에는 독립적으로 작동하는 작은 **마이크로서비스**(microservice)로 나뉘고 있다. 이들은 독립적으로 동작하기 때문에, 개발하고 배포하거나 스케일링을 따로 해줄 수 있다는 장점이 있으며, 이 장점은 빠르게 변화하는 소프트웨어의 요구사항을 반영하기에 적합하다.
마이크로서비스를 여러 개 띄우기 시작하면 이들을 효율적으로 관리하는 방법이 필요하다.
**쿠버네티스**(Kubernetes)는 하드웨어 인프라를 추상화하여 데이터 센터 (혹은 소프트웨어가 운영되는 서버나 클라우드)를 커다란 하나의 리소스로 바라볼 수 있게 해준다.
## 1.1 쿠버네티스가 필요한 이유
---
### 1.1.1 하나의 거대한 앱에서 마이크로서비스로
소프트웨어가 거대한 하나의 앱이면 오랜 기간 유지 보수를 거치면서 지나치게 복잡해지고, 의존성이 늘어나기 마련이다.
서버에 부하가 늘어나게 되면 **스케일링**을 해야한다.
#### 스케일링의 종류
- **Vertical scaling** (scaling up): CPU/메모리 추가 등, 코드를 수정할 필요는 없지만 비싸다.
- **Horizontal scaling** (scaling out): 서버 증설 등, vertical scaling 에 비하면 싼 편이지만, 코드를 수정해야 하기도 하며, 때로는 불가능한 경우도 있다 (ex. DB)
만약 앱이 하나의 거대한 덩어리라면, 일부가 스케일링이 불가능하면 전체도 스케일링 할 수 없다. 한편 이를 작은 마이크로서비스로 나누면?
마이크로서비스는 독립적인 프로세스로 동작하며, 다른 마이크로서비스와 API를 이용하여 통신한다. 독립적이기 때문에 해당 마이크로서비스가 하려는 일에 적합한 언어를 사용하여 서비스를 만들면 된다. 또 각 마이크로서비스 개별적으로 개발하고 배포할 수 있다. 다른 마이크로서비스에 변경 사항이 생기더라도 API만 유지된다면 의존성을 크게 고려하지 않아도 된다.
따라서 마이크로서비스는 서비스별로 스케일링해주면 되며, 부하가 심한 마이크로서비스만 스케일링 해주면 되고, 나머지 부분은 그대로 두면 된다!
#### 마이크로서비스의 단점
마이크로서비스로 애플리케이션을 나누게 되면 각 서비스끼리 통신을 하게 될텐데 스케일링을 많이 하면 할수록 통신을 더 많이 해야한다. 복잡도가 증가하게 된다. 복잡한 시스템을 효율적으로, 에러 없이 운영하는 것은 어렵다.
디버깅할 때 골치아프다. 문제가 여러 마이크로서비스에 걸쳐있을 수도 있다.
마이크로서비스 마다 요구하는 라이브러리가 다를 수 있고, 같은 라이브러리를 사용하지만 버전이 다를 수도 있다. 의존성을 관리하기 어려워 질 수도 있다.
### 1.1.2 앱에 일관된 환경 제공
개발 환경과 운영/배포 환경은 다르다!
개발 환경과 배포 환경을 동일하게 만들어서 같은 운영체제, 라이브러리, 시스템 설정, 네트워크 환경 등을 똑같이 할 수 있다면 배포 환경에서만 생기는 문제들 때문에 골치 아파야 할 일은 줄어들 것이다.[^1]
### 1.1.3 지속적인 배포: DevOps and NoOps
**DevOps**: 애플리케이션 개발, 배포와 운영을 담당
개발자가 애플리케이션 배포와 운영에 참여하게 되면서 사용자의 필요나 이슈를 잘 파악할 수 있게 되고, 운영에 있어서 발생하는 어려움을 더 잘 이해하게 된다.
**NoOps**: 개발자가 하드웨어 인프라에 대한 지식 없이, 또 운영 팀의 도움 없이 서비스를 배포
## 1.2 컨테이너 기술 소개
---
쿠버네티스에서는 **리눅스의 컨테이너 기술**을 사용하여 애플리케이션의 동작을 분리한다.
### 1.2.1 컨테이너란?
마이크로서비스를 운영하려면 각 서비스별로 독립적인 환경을 제공해야 한다. 이를 하기 위해서는 각 마이크로서비스를 **가상 머신**(virtual machine)에 올리면 된다.
하지만 VM은 너무 시스템 자원을 많이 소모하고, 각 VM마다 설정을 따로 해야하니 피곤하다.
대안으로 **컨테이너 기술**을 사용한다. 여러 개의 서비스가 하나의 머신에서 작동하면서도 각 서비스에게 독립적인 환경을 제공할 수 있게 되며, VM에 비해 훨씬 가볍다.
컨테이너에서 돌아가는 프로세스는 호스트(컨테이너를 실행한) 머신의 운영체제 아래에서 돌아간다. (다른 프로세스들과 마찬가지!)
#### VM과의 차이점
VM은 독자적인 운영체제를 사용하기 때문에 시스템 프로세스가 필요하고 이는 추가적인 리소스의 소모로 이어진다. (윈도우 VM에 RAM 4GB씩 준다고 생각하면 몇 개를 띄울 수 있을지...)
반면 컨테이너는 호스트 머신에서 돌아가는 프로세스이기 때문에 추가적인 시스템 프로세스가 필요 없어서 애플리케이션이 필요한 리소스만 소모하게 된다. VM에 비해 훨씬 가볍기 때문에 한 머신에서 여러 컨테이너를 돌릴 수 있게 된다.
VM을 사용하게 되면 hypervisor 가 하드웨어 자원을 가상 자원(virtual resource)으로 나누어 각 VM안의 OS가 사용할 수 있게 해준다. VM 안에서 돌아가는 애플리케이션은 VM의 OS에 system call 을 하게 되고, VM의 커널은 hypervisor를 통해 호스트의 CPU에서 명령을 수행하게 된다.
컨테이너는 호스트의 OS 커널에서 돌아가기 때문에 가상화 작업이 불필요하다. 결국 시스템 자원을 VM에 비해 적게 소모하므로 실행도 빠르다.
#### 컨테이너의 동작 원리
- **Linux Namespace**
각 프로세스가 독립적으로 시스템을 바라보게 되어 독립적인 환경을 제공한다. 같은 namespace 에 있는 자원만 확인할 수 있으므로, 여러 namespace 를 만들고 자원을 할당하면 프로세스 입장에서는 시스템 내에서 독립적으로 돌아가는 것 처럼 보일 것이다.
- **Linux Control Groups** (cgroups)
각 프로세스가 사용할 수 있는 자원에 제한을 걸어둔다.
### 1.2.2 Docker (도커)
애플리케이션을 패키징하고, 배포하고 실행하는 플랫폼이다.
VM 이미지가 아니라 용량이 더 작은 **컨테이너 이미지**를 사용한다.
컨테이너 이미지는 여러 layer 로 구성되어 있어서 이를 공유하거나 재사용 할 수 있다. 같은 layer 가 필요한 다른 서비스에서 그대로 가져다 쓸 수 있게 된다.
(실제로 Dockerfile 명령 줄 별로 이미지 layer 를 구성하던 것 같다. Hash 를 떠두고 만들어 둔 이미지가 있으면 새로 만들지 않고 그대로 가져와서 쓰던 기억이 있다.)
공유하는 대신 이미지 layer 는 read-only 이다. 컨테이너가 실행되면 writable layer 가 layer 의 맨 위에 생겨서 해당 layer 에 필요한 작업을 하게 된다.
#### 도커의 3가지 주요 개념
- **이미지**(Images): 애플리케이션과 애플리케이션이 동작하는 환경을 패키징한 것이다. '환경'에는 파일시스템이나, 실행 파일들의 경로와 같은 메타데이터도 들어간다.
- **레지스트리**(Registries): 도커 이미지의 저장소라고 생각하면 쉽다. 이미지를 만들어서 저장소에 push 할 수 있고, 직접 만든 혹은 만들어진 공식 이미지들을 pull 해서 사용할 수 있다.
- **컨테이너**(Containers): 이미지로부터 만들어진 컨테이너로 호스트에서 돌아가는 하나의 프로세스이지만, 리눅스 컨테이너 기술이 적용되어 있어 독립적인 프로세스처럼 돌아간다.
#### 컨테이너 이미지의 한계
모든 컨테이너는 호스트의 머신에서 돌아가는 프로세스이기 때문에 해당 컨테이너가 요구하는 커널 버전이나 운영체제, 아키텍쳐 (x86)가 있다면 해당 조건이 만족 되어야 컨테이너를 실행할 수 있다. M1 맥에서 도커 지원을 위해 추가 개발이 필요한 이유이다.
VM은 자체적으로 OS를 가지고 있기 때문에 VM을 사용하게 되면 이런 문제가 없다.
### 1.2.3 rkt (rock-it)
도커와 같은 컨테이너 엔진이다.
보안, 결합성, 표준을 지키는 것에 조금 더 중점을 둔다.
## 1.3 쿠버네티스 소개
---
마이크로서비스를 스케일링 하고 컨테이너가 많아지면 운영이 어려워진다! 이를 효율적으로 할 수 있게 해줄 도구가 필요하다.
### 1.3.1 쿠버네티스의 기원
구글에서 Borg, Omega 와 같은 서비스 관리 시스템을 자체 개발하여 사용하다가 2014년에 쿠버네티스를 공개했다.
### 1.3.2 High-level에서 바라본 쿠버네티스
쿠버네티스는 컨테이너화 된 애플리케이션을 쉽게 배포하고 운영하게 해주는 소프트웨어이다.
수많은 노드에서 돌아가는 애플리케이션이 마치 한 컴퓨터에서 돌아가는 것처럼 운영해준다.
클러스터를 위한 운영체제로 생각할 수 있다.
개발자의 입장에서는 인프라 관련된 기능들을 애플리케이션에 덜 넣어도 된다. (서비스가 준비되었는지 알아낸다거나, 스케일링, 로드 밸런싱, 에러로부터 회복, leader election 등) 개발자는 이제 애플리케이션의 기능에 더욱 집중할 수 있다. 인프라와 통합하는데 개발 리소스를 투입하지 않아도 된다.
쿠버네티스를 사용하면 사람이 직접 컨테이너와 작업을 스케쥴링 하는 것보다 자원을 효율적으로 사용할 수 있게 된다.
### 1.3.3 쿠버네티스 클러스터 구조
쿠버네티스 시스템에는 **마스터 노드**(master node)와 **워커 노드**(worker node)가 있다.
- 마스터 노드: Kubernetes Control Plane 이 실행되고, 쿠버네티스 시스템을 관리
- 워커 노드: 배포한 애플리케이션이 동작하는 노드
#### Kubernetes Control Plane
클러스터를 제어하고 동작하게 한다.
- **Kubernetes API 서버**: Control Plane 내부의 각 부분이 서로 통신할 때 사용하는 API 서버이다. 워커 노드에서 API를 이용하기도 한다.
- **스케쥴러**: 애플리케이션 배포시 워커 노드에 서비스를 할당한다.
- **Controller Manager**: 클러스터 수준의 기능을 담당한다. 컴포넌트를 복제하거나 워커 노드 개수를 관리하거나, 노드 에러를 처리하는 등 작업을 담당한다.
- **etcd**: 클러스터의 설정을 저장하는 persistent 분산 데이터 스토어이다.
#### Worker Nodes
- **Container runtime**: 컨테이너를 실행해준다.
- **Kubelet**: API 서버와 통신하고 노드의 컨테이너를 관리한다.
- **Kubernetes Service Proxy** (kube-proxy): 애플리케이션 컴포넌트 간 네트워크 로드 밸런싱을 담당한다.
### 1.3.4 쿠버네티스에서 애플리케이션 실행
우선 컨테이너 이미지를 만들어서 이미지 레지스트리에 등록해둬야 한다.
그리고 Kubernetes API 서버에 애플리케이션의 구성 명세를 알려줘야 한다.
명세에는 애플리케이션의 컴포넌트들에 대한 정보, 각 컴포넌트들의 관계, 같은 노드에 실행되어야 하는 컴포넌트들, 컴포넌트의 복제본 개수, 외부로 공개할 컴포넌트 등에 대한 여러 정보가 들어있다.
**API 서버**가 애플리케이션의 구성 명세를 처리하면, **스케줄러**는 컨테이너들을 적절하게 노드에 할당한다. **Kubelet**은 **Container Runtime** 에게 필요한 이미지를 가져와 실행하라고 한다.
컴포넌트의 복제본 개수(replica)를 정해두면 이 값을 지키도록 쿠버네티스가 자동으로 동작한다. 만약 개수가 적으면 더 실행하고, 많으면 줄인다. 또 들어오는 요청이 많거나, CPU 부하가 심할 때와 같이 상황에 맞게 자연스럽게 스케일링도 해준다. 이렇게 하나의 클러스터 내에서 컨테이너를 옮겨다니며 리소스를 효율적으로 쓰기도 하고 관리를 편하게 해준다.
**kube-proxy** 의 경우 하나의 고정된 IP 주소로 들어오는 요청에 대해 로드 밸런싱을 수행하여 클러스터 내의 적절한 컨테이너에게 작업을 넘겨준다.
### 1.3.5 쿠버네티스 사용의 장점
개발자들은 클러스터를 구성하는 서버에 대해서 잘 몰라도 된다. 추상화로 인해 모든 노드/클러스터가 커다란 서버가 된다.
물론 서버에 GPU가 필요하거나 추가적인 제약 조건이 따른다면 이를 잘 조절해 줘야 한다. (AWS GPU 인스턴스, DB 인스턴스 등)
쿠버네티스를 사용하게 되면 애플리케이션과 인프라가 분리된다! 무엇보다 개발 환경과 배포 환경을 유사하게 설정할 수 있어서 편해진다.
쿠버네티스가 하드웨어 리소스를 효율적으로 사용할 수 있도록 도와준다. 컴포넌트들을 관리하며 스케쥴링을 해주고 오류 발생시 자동으로 처리해주고 (노드 재시작 등), 부하가 심한 경우 자동으로 스케일링을 제공한다.
새로운 버전의 애플리케이션을 배포할 때 연속적인 배포를 할 수 있게 된다. 중간에 서비스를 중단하지 않아도 된다.
---
[^1]: 물론 컨테이너를 씀으로 인해 발생하는 새로운 문제를 얻겠지만, 개인적으로 장점이 더 크다고 생각한다.

View File

@@ -0,0 +1,314 @@
---
share: true
toc: true
categories: [Development, Kubernetes]
tags: [kubernetes, sre, devops, docker]
title: "02. First Steps with Docker and Kubernetes"
date: "2021-03-07"
github_title: "2021-03-07-02-first-steps-with-docker-and-kubernetes"
image:
path: /assets/img/posts/k8s-02.jpeg
---
![k8s-02.jpeg](../../../assets/img/posts/k8s-02.jpeg) _Running a container image in Kubernetes (출처: https://livebook.manning.com/book/kubernetes-in-action/chapter-2)_
도커와 쿠버네티스를 사용하여 간단한 애플리케이션을 배포해 보자!
## 2.1 컨테이너 이미지 생성, 실행, 공유하기
---
### 2.1.1 도커 설치
도커 공식 홈페이지에 있는 대로 설치한다!
- Mac 이나 Windows 를 쓰면 도커가 VM을 세팅하고 VM 안에서 도커 데몬이 실행된다.
#### 컨테이너 실행
```bash
$ docker run <IMAGE>:[TAG] [COMMAND]
```
- 이미지를 다운받고 실행해주는 명령
예시로 `docker run busybox echo "Hello world"` 가 있는데, 별거 아닌 것 같아 보여도 한 줄로 앱이 통째로 설치되고 실행이 되고 있기 때문에 생각보다 대단한 것이다!
#### `docker run` 이 실행될 때...
- 우선 해당 이미지의 latest version (`IMAGE:latest`) 가 로컬에 있는지 확인한다. 없으면 Docker Hub 에서 다운받는다.
- 이미지로부터 컨테이너를 생성하고 `[COMMAND]` 를 실행한다.
매 번 `docker run` 을 할 때마다 실행할 명령을 입력하는 것은 불편하므로 보통은 Dockerfile 을 사용해서 실행할 명령을 이미지 생성 과정에 포함시킨다.
이미지도 업데이트 될 수 있기 때문에, 이름은 유지한 채 tag 를 업데이트하여 버전을 관리한다. `latest` 태그는 당연히 이미지의 최신 버전을 가리킨다.
### 2.1.2 Creating a trivial Node.js app
HTTP 요청을 받으면 실행 중인 머신의 hostname 을 포함하여 응답을 돌려주는 앱을 node.js 로 만들고 Docker 이미지로 패키징 한다.
패키징 후 컨테이너를 실행하여 HTTP 요청을 날려보면 응답에 포함된 hostname 을 확인하면 컨테이너 내에서 독립적인 hostname 을 갖는다는 것을 확인할 수 있을 것이다.
(Docker 가 실행 중인 머신의 hostname 이 아닌 다른 결과가 나올 것)
### 2.1.3 Dockerfile 만들기
애플리케이션을 이미지로 패키징하려면 Dockerfile 에 이미지를 만들 때 실행해야 하는 명령들을 넣어주면 된다.
- `FROM` : 베이스 이미지로 사용할 이미지를 정한다. `FROM ubuntu:20.04` 라고 하면 ubuntu 20.04 환경에서 시작한다.
- `ADD <src> <dst>` : 로컬의 `src` 경로의 파일을 컨테이너 내 `dst` 경로로 복사한다.
- `ENTRYPOINT ["executable", "param1", "param2"]` : 컨테이너 내부 shell 에서 `$ executable param1 param2` 를 실행하는 것과 동일하다. 다만 Dockerfile 전체에서 가장 마지막 `ENTRYPOINT` 명령만 실행되므로 한 번만 사용할 수 있다.
### 2.1.4 컨테이너 이미지 빌드
'이미지를 빌드한다'는 것은 Dockerfile 에 있는 명령을 실행한다고 생각하면 될 것이다. 물론 Dockerfile 없이 빌드 할 수 있다. 깃헙에서 commit changes 하듯이, 컨테이너 내부에서 명령을 직접 실행한 뒤 컨테이너의 상태를 저장 할 수 있다. 하지만 당연히 직접 실행하는 것보다는 파일로 관리하는 것이 편할 것이다.
```bash
$ docker build -t <TAG> <PATH>
```
- `-t` 옵션은 이미지의 태그를 지정해준다.
- `PATH` 에 있는 Dockerfile 을 이용하여 이미지를 빌드한다.
이미지 빌드시 `PATH` 의 모든 파일이 도커 데몬에 옮겨진 다음 그 곳에서 이미지를 빌드한다.
#### 이미지 레이어
이미지는 여러 레이어로 구성이 되어있다. `docker build` 를 해보면 step 별로 진행 되는 것을 볼 수 있는데 각 step 마다 layer 가 하나 생성된다. Dockerfile 명령 한 줄마다 layer 하나를 쓰게 된다.
다만 `&&` 를 사용해서 Dockerfile 의 line 수를 줄이면, 나중에 명령을 수정할 때 만들어 둔 layer 를 재사용하지 못하게 되는 경우가 있다. 그러면 이미지 빌드가 느려진다.
이미지는 용량을 많이 차지하게 될 수 있으므로 한 번만 저장하고 같은 레이어는 재사용하게 된다.
```bash
$ docker images
```
- 로컬에 있는 이미지 목록을 보여준다.
### 2.1.5 컨테이너 실행
```bash
$ docker run [OPTIONS] <IMAGE> # 기본형
$ docker run --name <CONTAINER_NAME> -p <HOST_PORT>:<CONTAINER_PORT> -d <IMAGE>
```
- `IMAGE` 로부터 컨테이너를 실행한다.
- `--name` 옵션은 실행된 컨테이너에 `CONTAINER_NAME` 으로 이름을 부여한다.
- `-p` (`--publish`) 옵션은 호스트의 포트를 이용하여 컨테이너의 포트에 접근할 수 있도록 한다. 컨테이너의 포트를 호스트 포트로 publish 한다.
- `-d` (`--detach`) 옵션은 background 에서 작업이 수행되도록 한다.
```bash
$ docker ps
```
- 실행 중인 컨테이너의 목록과 간략한 정보를 확인한다.
- 컨테이너 ID, 이름, 컨테이너 생성에 사용된 이미지, 컨테이너 내부에서 실행 중인 명령을 확인할 수 있다.
- 자세히 보고 싶으면 `docker inspect <CONTAINER>` 를 사용하여 JSON 으로 자세한 정보를 확인한다.
### 2.1.6 컨테이너 내부 들여다보기
안에서 명령을 실행할 일이 분명 생긴다.
```bash
$ docker exec [OPTIONS] <CONTAINER> <COMMAND> [ARGS] # 기본형
$ docker exec -it <CONTAINER> <COMMAND>
```
- `exec` 는 `CONTAINER` 내에서 `COMMAND` 를 실행하라는 의미이다.
- `-i` (`--interactive`) 는 STDIN (표준 입력)을 열어준다.
- `-t` (`--tty`) 는 pseudo-TTY (유사 터미널)을 열어준다.
그러므로 `docker exec -it <CONTAINER> bash` 를 하게 되면 컨테이너 내부에서 `bash` 명령이 터미널 내에서 실행되고, 표준 입력이 연결되어 있으므로 명령을 입력할 수 있게 된다. (쉘을 딸 수 있다는 것이다)
#### 독립된 공간
컨테이너 내에서 `ps aux` 를 입력하면 호스트 머신에서 돌아가는 프로세스들은 보이지 않는다. 한편 호스트 머신에서 `ps aux` 를 입력한후 `grep` 으로 프로세스를 찾아보면 컨테이너 내에서 동작 중인 프로세스가 목록에 있음을 확인할 수 있다.
즉 독립적인 공간에서 실행되는 것처럼 환경을 제공하지만, 실제로는 호스트의 프로세스인 것임을 확인할 수 있다.
마찬가지로 `ls` 명령을 쳐보면 내부에 독립적인 파일시스템을 갖고 있음을 확인할 수 있다. (물론 Dockerfile 에서 직접 추가한 파일이 있을 수도 있고, 특정 디렉토리를 마운트 한 경우 공유가 되기도 한다.)
### 2.1.7 컨테이너 정지 및 삭제
```bash
$ docker stop <CONTAINER> # 정지
$ docker rm <CONTAINER> # 삭제
```
컨테이너를 정지하게 되면 그 잔해가 남아있게 되는데, `rm` 을 이용해 삭제해주면 완전히 제거된다.
정지한 컨테이너는 `docker ps` 로 확인이 불가능하므로 (실행 중인 컨테이너만 보인다), `docker ps -a` 로 확인한다.
### 2.1.8 이미지 레지스트리에 이미지 push 하기
주의사항: Docker Hub 에 push 하려면 이미지의 레포지토리 이름이 Docker Hub ID 로 시작해야 한다.
#### 이미지 태그
```bash
$ docker tag <SOURCE_IMAGE:TAG> <TARGET_IMAGE:TAG>
```
- 새로운 태그 `TARGET_IMAGE` 를 만들어서 `SOURCE_IMAGE` 를 가리키게 한다.
- Alias 를 만든다. 이미지를 복사하는 것이 아니다. `docker images` 로 확인하면 `SOURCE_IMAGE` 가 이름이 바뀐게 아니라 새로 생겼음을 확인할 수 있다. (메모리 낭비 금지)
`docker login` 으로 로그인 한 뒤, `docker push <NAME:TAG>` 으로 이미지를 push 할 수 있다. 당연히 그 반대인 `docker pull` 도 가능하다.
## 2.2 쿠버네티스 클러스터 설정
---
일단 로컬에서 하려면 Minikube 를 설치하면 된다.
### 2.2.1 Minikube 를 이용한 단일 노드 클러스터
- Minikube 를 설치하고, `kubectl` 을 설치하면 된다.
- `minikube start` 하고 `kubectl cluster-info` 하면 클러스터가 생성된 것을 확인할 수 있다.
개인적으로 oh-my-zsh 를 사용하는데 `kubectl` 까지 치면 옆에 현재 context 가 나와서 참 좋다.
### 2.2.2 Google Kubernetes Engine (GKE)
- 세팅은 패스 (요새 무료 크레딧도 주던데...)
```bash
$ gcloud container clusters create <NAME> --num-nodes <NUM> --machine-type <MACHINE_TYPE>
```
- 쿠버네티스 클러스터 (노드의 집합)를 생성한다.
- 클러스터 내에 몇 개의 노드가 있는지 설정할 수 있다.
- `--machine-type` 옵션으로 얼마나 비싼 기계를 쓸지 고를 수 있다.
각 worker 노드에서는 Docker, Kubelet, kube-proxy 가 실행되고 있고, 마스터 노드가 이들을 관리하게 된다.
`kubectl` 을 이용하면 마스터 노드에 있는 REST API 서버를 호출하여 작업하게 된다.
노드를 생성했으니 노드의 목록을 확인하는 방법을 알아보자.
```bash
$ kubectl get nodes
```
- 노드 목록을 보여준다.
사실 `kubectl get` 은 docs 에도 나와 있듯이 '하나 이상의 리소스를 나열'한다. 더 자세히 보고싶으면,
```bash
$ kubectl describe node <NAME>
```
으로 확인하면 된다.
### 2.2.3 Alias for `kubectl`
솔직히 `kubectl` 매번 입력하기 귀찮다. 적당히 자주 쓰는 명령 alias 해둔다.
`bash-completion` 을 사용하면 편리하다고 한다.
## 2.3 Kubernetes 에서 앱 실행하기
---
### 2.3.1 Node.js 앱 배포하기
```bash
$ kubectl run [NAME] --image=[IMAGE] --port=[PORT] --generator=run/v1
```
- 설정 파일 (`.yaml`) 없이도 실행할 수 있게 해준다.
- `--image` 옵션으로 사용할 이미지를 설정할 수 있다.
- `--port` 옵션으로 포트를 개방할 수 있다.
- `--generator` 는 *ReplicationController* 를 생성하기 위함이라고 한다.
쿠버네티스는 컨테이너를 개별적으로 다루지는 않는 편이다. 대신 함께 존재하는 여러 컨테이너들을 다룬다. 이러한 컨테이너의 묶음을 **pod** 라고 한다.
Pod 안에 여러 개의 컨테이너가 존재할 수 있고, 그 컨테이너는 모두 같은 worker 노드에서 실행되며, Linux namespace 를 공유한다. 각 pod 는 마치 개별적인 머신처럼 동작하여 IP, hostname, 프로세스 등을 개별적으로 갖게 된다.
당연히, pod 를 가져오는 명령은
```bash
$ kubectl get pods
```
이다. `READY` 열을 보면 컨테이너가 실행 대기 중인 경우 `0/1` 과 같이 나올 수도 있다.
#### 무슨 일이 일어났나요?
- `kubectl run` 을 실행하면 Kubernetes API 서버에 REST HTTP 요청을 보낸다.
- Kubernetes API 서버는 요청을 받아 새로운 ReplicationController 오브젝트를 생성한다.
- ReplicationController 가 새로운 pod 를 생성하게 되고, 이는 Scheduler 에 의해 어떤 worker 노드에 할당된다.
- Kubelet 이 동작하여 Docker 를 실행하게 하고 Docker 가 컨테이너를 실행한다.
### 2.3.2 애플리케이션에 접근하기
배포한 앱은 pod 에서 실행되므로 IP가 할당되는데, 이 IP는 클러스터 내부에서 사용되는 IP이므로 외부에서 접속이 불가능하다. Service 오브젝트를 사용하여 외부로 공개해야 한다.
`LoadBalancer`-type service 를 생성하면 이 load balancer 의 공개 IP를 이용하여 pod 에 접근할 수 있게 된다.
```bash
$ kubectl expose rc <RC_NAME> --type=LoadBalancer --name <SERVICE_NAME>
```
- `expose` 에서 외부로 공개한다는 의미를 알 수 있다.
- `rc` 는 `ReplicationController` 의 약자다.
서비스 오브젝트를 생성했다면 서비스 목록을 확인한다.
```bash
$ kubectl get services
```
- 실행 중인 서비스 목록을 확인한다.
- `EXTERNAL-IP` 에 공개 IP가 적혀있다. 서비스 생성에 시간이 걸릴 수 있으니 IP가 할당되려면 조금 기다려야 할지도?
### 2.3.3 The logical parts of your system
#### Pod
Kubernetes 의 basic building block 은 pod 이다! Pod 안에는 여러 개의 컨테이너가 실행될 수 있고, pod 는 각각 IP 주소와 hostname 을 갖는다.
#### ReplicationController
Pod 를 만든 것은 ReplicationController 인데, 얘는 pod 의 개수를 조절하고 관리한다. Pod 를 한 개 만들라고 했다면 정확히 1개가 유지되도록 한다. 오류가 나서 종료되더라도 다시 시작시켜 준다. 또 서비스가 굉장히 커져서 pod 하나만으로는 트래픽을 감당할 수 없다면, pod 를 여러 개 복제하여 실행하는데 사용할 수 있다.
#### Service
Pod 의 경우 오류가 나거나 로드 밸런싱이나 효율적인 시스템 자원의 활용을 위해 중지되거나 다른 노드로 옮겨가는 등 수시로 생겼다 사라졌다 할 수 있다. 이 때마다 (내부) IP가 바뀌게 되므로 이를 효율적으로 관리해주는 service 가 필요하게 된다. 공개 IP를 하나 설정하여 외부에서 접근이 가능하도록 한다.
### 2.3.4 Horizontal Scaling
```bash
$ kubectl get replicationcontorllers
```
- 존재하는 ReplicationController 를 확인한다.
- `DESIRED` 와 `CURRENT` 열에서 있어야 하는 pod 개수와 현재 pod 개수를 확인할 수 있다.
```bash
$ kubectl scale rc <RC_NAME> --replica=<NUM>
```
- `DESIRED` 값을 `NUM` 으로 설정한다.
- 그러면 ReplicationController 가 알아서 개수를 맞춰준다.
변경사항이 있다면 `kubectl get rc` 와 `kubectl get pods` 로 `DESIRED` / `CURRENT` 값이 변경 되었고 pod 가 실행 중인지 확인하면 된다. Kubernetes 없이 복제하려 했으면 얼마나 번거로웠을까?
위의 node.js 앱을 배포한 상태에서 pod 가 여러 개 생겼으니 외부에서 HTTP 요청을 보내보면, 안에서 load balancing 이 동작하고 있기 때문에 꼭 같은 hostname 을 반환하지는 않는다.
### 2.3.5 앱이 실행 중인 노드 확인
보통 어느 노드에서 실행 중인지 중요하지는 않지만 확인할 수 있긴 하다. 보통은 할당된 노드의 자원 (CPU 사용량, 메모리) 등을 확인해야 하긴 한다.
```bash
$ kubectl get pods -o wide # 더 많은 정보 출력
$ kubectl describe pod <POD_NAME> # 상세 정보 출력
```
### 2.3.6 Kubernetes Dashboard
GUI 도 지원해 준다.
```bash
$ kubectl cluster-info | grep dashboard
```
- 대시보드가 실행 중인 주소를 알려줄 것이다.
- Minikube 의 경우 `minikube dashboard`.

View File

@@ -6,6 +6,8 @@ tags: [development, web]
title: "블로그 이주 이야기" title: "블로그 이주 이야기"
date: "2023-06-25" date: "2023-06-25"
github_title: "2023-06-25-blog-moving" github_title: "2023-06-25-blog-moving"
image:
path: /assets/img/posts/blog-logo.jpeg
--- ---
![blog-logo.png](../../../assets/img/posts/blog-logo.png) _New blog logo_ ![blog-logo.png](../../../assets/img/posts/blog-logo.png) _New blog logo_

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB