# 기초 개념 정리

## 쿠버네티스

### 개념

* 컨테이너 관리를 위한 구글에서 만든 오케스트레이션.&#x20;
* 컨테이너를 이용한 애플리케이션 배포 외에도 다양한 운영관리 업무를 자동화
* 도커호스트 관리, 컨테이너 배치, 스케일링, 여러 개의 컨테이너 그룹 → 로드밸런싱, 헬스체킹 등 기능
* GCP → GKE, AWS → EKS, Azure → AKS

### 로컬환경 설치 (윈도우, Mac)

* 근래는 연동이 가능. 이전에는 mini kube를 사용했음
* 도커아이콘 → Preference → Kubernetes → Install , Kubernetes is running 확인

```bash
# kubectl 설치 (Mac, Linux)
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.10.4/bin/darwin/amd64/kubectl \
&& chmod +x kubectl \
&& mv kubectl /usr/local/bin/

# 대시보드 설치
$ kubecgtl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.8.3/src/deploy/recommended/kubernetes-dashboard.yml

# 상태확인
$ kubectl get pod --namespace=kube-system -l k8s-app=kubernetes-dashboard

# 웹 브라우저로 대시보드 볼 수 있기 프록시서버 설정
$ kubectl proxy # 127.0.0.1:8001
```

### Minikube

* 윈도우/맥용 도커에 쿠버네티스 연동 전까지는 Minikube 를 사용 했음.
* 로컬환경에 dockerd를 새로 띄워 → 쿠버네티스 환경을 구축할 수 있도록 ...
* 로컬에서 dockerd를 2개 다루기 때문에 데탑용 도커보다 더 까다롭긴함
* 윈도우 → 하이퍼바이저, 맥 → 하이퍼바이저.프레임워크 환경에서 실행, minikube → 하이퍼바이저, Virtualbox 에서도 실행이 가능

### 리소스

* 리소스 → 컴포넌트 같은 개념
  * 노드 : 컨테이너가 배치 되는 서버
  * 네임스페이스 : 쿠베 클러스터 안의 `가상 클러스터`
  * 파드 : 컨테이너의 그룹, 집합 등의 `가장 작은 단위`. `컨테이너 실행 방법 정의`
  * 레플리카셋 : `파드 복사`. 파드는 한개만 실행이 가능하나. 이걸 이용하면 여러개의 파드를 복사해서 사용이 가능
  * 디플로이먼트 : `레플리카 리비전`(버전)을 관리
  * 서비스 : `파드의 집합`에 접근하기 위한 경로를 정의
  * 컨피그맵 : **설정 정보를 정의하고 파드에 전달**
  * 스테이트풀셋 : 같은 스펙으로 모두 **동일한 파드를 여러 개 생성하고 관리**

이외 다양한 리소스, 시크릿, 롤 , 롤바인딩...

### 쿠버네티스 클러스와 노드

* 리소스 중 가장 큰 개념 -> `node`
* 서버에 두는데 보통 노드를 3개 구성해서 SPOF 를 방지
* 클러스터 전체를 관리하는 서버인 마스터노드가 한개는 꼭 존재해야됨&#x20;

![](https://1162853276-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1ciKJvsGgeAKQBeHlJ%2F-M3RKMHkbuY_NQ8Ab1dd%2F-M3RMCklx-oDpuFy_GkH%2FIMG_6A4B4C4F4075-1.jpeg?alt=media\&token=52a5e26a-fc2a-4c53-a929-213787f5a094)

```bash
# 노드 확인
$ kubectl get nodes
```

### Namespace

* 클러스터 안의 `가상클러스터`

  ```bash
  # 확인
  $ kubectl get namespace
  ```
* 첫 구성시, default, docker, kube-public, kube-system 네임스페이스 4깨가 만들어져 있음
* 네임스페이스는 `개발팀이 일정 규모 이상일 때 유용`
* 네임스페이스 마다 권한 설정이 가능

### Pod

* `컨테이너 집합`, 하나 이상의 컨테이너들로 구성

![](https://1162853276-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1ciKJvsGgeAKQBeHlJ%2F-M3RKMHkbuY_NQ8Ab1dd%2F-M3RMkTd6KqMpxc_AFBu%2FIMG_317D23072FC2-1.jpeg?alt=media\&token=ccbff13b-0b60-44c0-bc50-ce8248b08f65)

* **노드에 배치 해야됨**
* 같은 파드를  여러노드에 배치 할 수도 있고, 한 노드에 여러개 배치 할 수도 있음
* 그러나, **한 파드 안의 컨테이너는 한 노드에 배치 해야 됨**
* 파드 하나가 여러 노드에 걸쳐 배치 될 수 없음

![](https://1162853276-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1ciKJvsGgeAKQBeHlJ%2F-M3RKMHkbuY_NQ8Ab1dd%2F-M3RMsChaQ3rv0eYwnya%2FIMG_3D51D238542D-1.jpeg?alt=media\&token=8cbeaeec-e2e0-4774-9471-8651e59b1e97)

#### 파드 생성 및 배포하기

* **\*.yaml 설정파일로 관리**하여 배포하는게 효율적

  ```yaml
  # file name : simple-pod.yaml
  apiVersion: v1
  kind: Pod                # 종류에 따라 아래 spec의 스키마가 변화
  metadata:
    name: simple-echo    # 이 서비스의 이름, 즉 여기서는 pod의 이름 정의    
  spec:
    containers:
    - name: nginx    # 컨테이너 이름
      image: gihyodocker/nginx:latest # 이미지 이름, 레지스트리에 등록
      env:
      - name: BACKEND_HOST        # 환경 변수 이름 -> 컨테이너에서 사용
        value: localhost:8080    # 환경 변수 값
      ports:
      - containerPort: 80
    - name: echo
      image: gihyodocker/echo:latest    # 이미지 이름
      ports:
      - containerPort: 8080
  ```

```bash
# 실행 -> -f : 파일명 지정 옵션
$ kubectl apply -f simple-pod.yaml

# 확인 -> 실행 중인 pod 확인
$ kubectl get pod

# 파드 내부의 컨테이너 접속 -> pod명, -c 옵션 : 컨테이너 이름 지정하기
$ kubectl exec -it simple-echo -c nginx
$ kubectl exec -it simple-echo -c echo

# kubectl delete 리소스명 -> kubectl 도구로 리소스를 삭제하려고 하는 경우
# 파드 삭제하기 : kubectl delete pod 파드명 
$ kubectl delete pod simple-echo

# 파드 삭제하기2 : -f 옵션 [파일명]
$ kubectl delete -f simple-pod.yaml
```

* pod의 IP는 내부적으로 컨테이너 끼리 공유,&#x20;
* 서로 접근이 가능, `ip:port` 형식으로
* 그리고 `Pod끼리도 서로 접근이 가능`
* Pod는 `사실상 컨테이너를 담는 가상머신`이나 마찬가지

![](https://1162853276-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1ciKJvsGgeAKQBeHlJ%2F-M3RKMHkbuY_NQ8Ab1dd%2F-M3RMz4H6KQjr3Myvyc3%2FIMG_FFC09E0C13C7-1.jpeg?alt=media\&token=c3140b57-5924-45dc-97ff-f9d29862dd6e)

### 레플리카세트

* `Pod를 정의하는 매니페스트 파일`. 파드는 `파드를 하나 밖에 생성할 수 없는`데, `같은 파드를 여러개 실행`해 가용성을 확보해야 하는 경우에 활용

```yaml
# 파일명 : simple-replicaset.yaml
apiVersion: apps/v1
kind: ReplicaSet    # 리소스 종류 , 레플리카셋
metadata:
    name: echo        # 리소스 이름, 레플리카 이름 -> echo
    labels:
        app: echo
spec:
    replicas: 3 # pod 복제 수, 즉 생성수
    selector:
        matchLabels:
            app: echo
    template: # template 아래는 파드 리소스 정의와 같음
        metadata:
            labels:
                app: echo
        spec:
            containers:
            - name: nginx
              image: gihyodocker/nginx/latest
              env:
              - name: BACKEND_HOST
                value: localhost:8080
              ports:
              - containerPort: 80
            - name: echo
              image: gihyodocker/echo:latest
              ports:
              - containerPort: 8080
```

* 실행시, pod이름은 echo라는 이름 뒤에 접미사로 랜덤하게 붙는다
* 레플리카 갯수로 설정한것 만큼 생성 되고 실행 됨
* 파드의 수를 줄이면 줄인 개수만큼 파드가 삭제 됨 -> **파드 삭제는 복원 불가** 이므로 유의해야 하고, **stateless** 한 상태로  사용을 하기 때문에 **멱등성이 보장됨**

```bash
# 실행
$ kubectl apply -f simple-replicaset.yaml

# 확인
$ kubectl get pod

# 출력 내용
NAME            READY    STATUS    RESTARTS    AGE
echo-bmca        ...
echo-무작위        ...
echo-무작위2    ...

# 삭제
$ kubectl delete -f simple-replicaset.yaml
```

### 디플로이먼트

* 레플리카의 상위 개념의 리소스
* 애플리케이션 **배포의 기본단위**
* 레플리카셋 -> 같은 파드의 레플리케이션 개수관리, 디플로이먼트 -> `레플리카셋 관리 및 다루기 위한 리소스`

![](https://1162853276-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1ciKJvsGgeAKQBeHlJ%2F-M3RKMHkbuY_NQ8Ab1dd%2F-M3RN2nS1KOaHihad0o_%2FIMG_5F0020CBCDA4-1.jpeg?alt=media\&token=7a10dd59-40a5-4d16-a42a-94be669b6026)

```yaml
# 파일명 : simple-deployment.yaml
apiVersion: apps/v1        # 버전
kind: Deployment            # 리소스 종류
metadata:
    name: echo
    labels:
        app: echo
spec:                    # 레플리카셋의 설정과 동일
    replicas: 3            
    selector:            # ?
        matchLabels:
            app: echo
    template:
        metadata:
            labels:
                app: echo
        spec:
            containers:
            - name: nginx
              image: 아무거나/nginx:latest
              env:
              - name: BACKEND_HOST
                value: localhost:8080
              ports:
              - containerPort: 80
            - name: echo
              image: 아무거나/echo:latest
              ports:
              - containerPort: 8080
```

```bash
# 실행 -> —-record 옵션 : 어떤 kubectl의 명령어를 실행했는지 기록을 남김
$ kubectl apply -f simple-deployment.yaml —-record

# 리소스 확인
$ kubectl get pod,replicaset,deployment —selector app=echo

# 디플로이먼트 리비전 확인
$ kubectl rollout history deployment echo
```

* 쿠버네티스는 **디플로이먼트 단위로 배포**를 함
* 실제 운영 -> 레플리카세트를 직접 다루기 보다는 **디플로이먼트 매니페스트 파일을 통해 다루는 경우가 대부분**
* 디플로이먼트 안의 레플리카세트를 어떻게 작동하는지 파악할 줄 알아야함
* 수정시, 레플리카세트가 새로 생성되고, 기존 레플리카세트와 교체가 됨
* **파드 개수만 수정하면** 레플리카가가 새로 생성되지는 않음
* **컨테이너 수정** -> 새로운 파드가 생성되고 기존파드는 단계적으로 정지됨

```bash
# 확인 -> 리비전 변화 확인 1->2
$ kubectl get pod —-selector app=echo

# 롤백 -> 최신 배포가 문제 있을 경우 이전상태로 돌아갈 때 꼭 필요함
$ kubectl rollout undo deployment echo

# 삭제 -> 레플리카, 파드 함께 삭제됨
$ kubectl delete -f simple-deployment.yaml
```

### 서비스

클러스터 안에서 **파드의 집합**(주로 레플리카세트)에 대한 `경로`나 `서비스 디스커버리를 제공`하는 리소스

```yaml
apiVersion: apps/v1
kind ReplicaSet
metadata
    name: echo-spring
    labels:
        app: echo
        release: spring
spec:
    replicas: 1
    selector:
        mathLabels:
            app: echo
            release: spring
        template:
            metadata:
                labels:
                    app: echo
                    release: spring
            spec:
                containers:
                - name: nginx
                  image: ~/nginx:latest
                  env:
                  - name: BACKEND_HOST
                    value: localhost:8080
                  ports:
                  - containerPort: 80
apiVersion: apps/v1
kind: ReplicaSet
metadata:
    name: echo-summer
    labels:
        app: echo
        release: summer
spec:
    replicas: 2
    selector:
        matchLabels:
            app: echo
            release: summer
        spec:
            containers:
            - name: nginx
              image: ~/nginx:latest
              env:
              - name: BACKEND_HOST
                  value: localhost:8080
              ports:
              - containerPort: 80
            - name: echo
              image: ~/echo:latest
              ports:
              - containerPort: 8080
```

```bash
# 확인
$ kubectl apply -f simple-replicaset-with-label.yaml

# l 옵션 : label 
$ kubectl get pod -l app=echo -l release=spring
$ kubectl get pod -l app=echo -l release=summer
```

```yaml
# simple-service.yaml
apiVersion: v1
kind: Service
metadata:
    name: echo    # 서비스명
spec:
    selector:     # 파드의 레이블 == 셀렉터 값 일치 -> 해당파드 서비스의 대상
        app: echo            # pod의 labels : app=echo 와 일치
        release: summer        # pod의 labels : release=summer 와 일치
    ports:
        - name: http
          port: 80
```

![](https://1162853276-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1ciKJvsGgeAKQBeHlJ%2F-M3RKMHkbuY_NQ8Ab1dd%2F-M3RNdo5L9n9GES7w_pi%2FIMG_FBC7BF7F7916-1.jpeg?alt=media\&token=31e1931e-3d6d-4f2c-98e3-66155fc84807)

```bash
# 실행
$ kubectl apply -f simple-service.yaml

# 확인 : service 이름이 echo인 것 출력
$ kubectl get svc echo

# 서비스-> 기본적으로 쿠버네티스 클러스터 안에서 실행됨
# 따라서 외부서 요청을 받기 위해 쿠베 디버깅용 임시컨테이너 배포 -> curl 실행
$ kubectl run -i —-rm —-tty debug —-image=gihyodocker/fundametal:0.1.0 —-restart=Never —-bash -il

# 다음명령어로 로그출력을 확인
$ kubectl logs -f echo-summer-dtblk -c echo
```

#### 서비스의 네임 리졸브

* 쿠버네티스 클러스터의 **DNS 서비스**를 `서비스명.네임스페이스명.svc.local`로 연결
* ex) echo -> default네임스페이스에 배치돼 있으므로

```bash
# 확인
$ curl http://echo.default.svc.local

# svc.local은 생략 가능. 다른 네임스페이스에 있는 서비스를 참조
$ curl http://echo.default

# 같은 네임스페이스 -> 네임스페이스 생략 가능
$ curl http://echo
```

#### ClusterIP 서비스

* 서비스에도 여러 가지 종류가 존재.
* 그 종류를 yaml파일에 지정할 수 있음
* ClusterIP를 사용하면 **쿠버네티스 클러스터의 내부 IP 주소에 서비스를 공개 할 수 있음**
* 이를 이용하면 어떤 파드에서 다른 파드 그룹으로 접근할 때 서비스를 거쳐 가도록 할 수 있고, **서비스명으로 네임 리졸브가 가능**. 그러나 **외부로는 접근 안됨**

#### NodePort 서비스

* NodePort 서비스는 클러스터 외부에서 접근할 수 있는 서비스
* ClusterIP와 생성은 간ㅌ지만, 각노드에서 서비스 포트로 접속하기 위한 `글로벌 포트`를 개방한다는 점이 차이점

```yaml
apiVersion: v1
kind: Service
metadata:
    name: ehco
spec:
    type: NodePort    # 서비스의 한 종류,
    selector:
        app: echo    # 앞에서 살펴 본 바, 파드의 라벨과 매칭
    ports:
        - name: http
          port: 80
```

```bash
# 실행
$ kubectl get svc echo

# 출력
# echo NodePort a.b.c.d <node> 80:31058/TCP ~~~~ # 노드의 31058포트를 통해 접근 -> 이를 이용해 서비스를 쿠버네티스 클러스터 외부로 공개

# 요청확인, L4레벨에서 노출
$ curl http://127.0.0.1:31058 # -> 서비스로 접근
```

#### LoadBalancer 서비스

* **로컬 쿠버네티스 환경에서는 불가능**
* 주로 각 **클라우드 플랫폼에서 제공하는 로드밸런서와 연동하기 위해 사용**

#### ExternalName 서비스

* 셀렉터도, 포트 정의도 없는 서비스
* **쿠버네티스 클러스터에서 외부 호스트를 네임 리졸브하기 위한 별명을 제공**

```yaml
apiVersion: v1
kind: Service
metadata:
    name: gihyo                    # 연결, 참조 : gihyo.jp
spec:
    type: ExternalName
    externalName: gihyo.jp        # 별명 외부에서 다음 정보가 노출
```

### 인그레이스

* 가장 중요함. 쿠버네티스 클러스터 외부로 서비스를 공개. L7 까지 다룸
* 외부노출, 가상호스트 및 **경로 기반의 정교한 HTTP 라우팅** 가능
* HTTP/HTTPS 서비스를 노출 하려는 경우 -> 무조건 빼박으로 **인그레이 사용** 해야됨
* 역시 인그레이스도 로컬환경에서는 불가능

```bash
# 클렇스터 외부 -> HTTP 요청 -> 라우팅하기 위해, nginx_ingress_controller 배포
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.16.2/deploy/mandatory.yaml

$ kubectl apply -f https://raw.githubusercontent.com/ingress-nginx/nginx-0.16.2/deploy/cloud-generic.yaml

# 서비스와 파드가 생성 된 후... 인그레스 리소스 사용하기
$ kubectl -n ingress-nginx get service,pod
```

#### 인그레스를 통해 접근

```yaml
# simple-service.yaml 작성
apiVersion: v1
kind: Service
metadata:
    name: echo
spec:
    selector:
        app: echo
    ports:
    - name: http
    port: 80
```

```bash
# 실행
$ kubectl apply -f simple-service.yaml
```

```yaml
# simple-ingress.yaml 작성
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
    name: echo
spec:
    rules:
    - host: ch05.gihyo.lcoal
      http:
        paths:
        - path: /
        backend:
            serviceName: echo
      servicePort: 80
```

```bash
# 실행
$ kubectl apply -f simple-ingress.yaml

# 확인
$ kubectl get ingress

$ curl http://localhost -H ‘Host: ch05.gihyo.local’
```

* 지정된 호스트 혹은 경로와 일치하는 서비스로 **요청을 전달**

```yaml
apiVersion: extensoins/v1beta1
kind: Ingress
metadata:
    name: echo
    annotaions:
        nginx.ingress.kubernetes.io/server-snippet: |
            set $agentflag 0;

            if ($http_user_agent ~* “(Mobile)”) {
                set $agentflag 1;
            }

            if ($agentflag=1) {
                return 301 http://gihyo.jp/;
            }
spec:
    rules:
    - host: ch05.gihyo.local
      http:
        paths:
        - path: /
          backend:
            serviceName: echo
            servicePort: 80
```

```bash
# User-Agent 값에 Mobile이 포함된 경우 다른 url로 리다이렉트
$ curl http://localhost \
-H ‘Host: ch05.gihyo.local’ \
-H ‘User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_...)a AppleWebKit/604.1.38 ~~~~~ Mobile/15A372 Safari/604.1’
```

#### freshpod 로 이미지 업데이트 탐지, 파드 업데이트하기

* **freshpod** 는 쿠버네티스로 배포된 **컨테이너의 이미지가 업데이트 됐는지 탐지**
* 탐지 후 **파드를 자동으로 다시 배포**하는 도구
* Minikube의 애드온으로 개발된 것. 윈/mac용 도커에서도 사용 가능. 로컬환경에서 쿠버네티스 개발 환경에서 빼놓을 수 없는 도구

```bash
# 설치
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/minikube/master/deploy/addons/freshpod/freshpod-rc.yaml
```

```yaml
# 파일 작성
apiVersion: apps/v1
kind: Deployment
metadata:
    name: echo
    labels:
        app: nginx
spec:
    replicas: 1
    selector:
        app: nginx
    template:
        metadata:    
        labels:
            app: nginx
        spec:
            containers:
            - name: nginx
              image: ch04/nginx:latest
              imagePullPolicy: IfNotPresent #
            env:
            - name: BACKEND_HOST
              value: 127.0.0.1:8080
```

```bash
# freshpod로 컨테이너를 -> imagePullPolicy: Always(항상 최신 이미지 받음)가 아니라 IfNotPresent(전에 받아둔 이미지가 있으면 재사용)으로 설정해야함
$ kubectl get pod -l app=nginx -w

# 이미지 수정 후 빌드 -> 확인, 이미지 생성후 파드 교체
$ docker image build -t ch04/nginx:latest
```

* 로컬 환경에서 효율적인 개발이 가능하게 해주는 도구

#### kube-prompt

* kubectl 명령 및 리소스 이름의 자동완성 기능 제공

#### 쿠버네티스 API

* 쿠버네티스 리소스를 `생성, 수정, 삭제` 하는 작업은 쿠버네티스 클러스터에 배포된 API가 수행
* 이 API를 하나로 묶은 형태로 구성되고, 아 `apiVersion`은 **해당 작업에 사용 되는 API 종류를 나타내는 값**
* 리소스마다 다름.&#x20;
* **쿠버네티스 API 리포지토리를 통해 어떤 api가 리소스를 지원하는지 확인 필요**
* 쿠버네티스 레파지토리 : <https://github.com/kubernetes/api>
* 서비스 or 파드 핵심 api : `v1` / 디플로이먼트는ㄴ 파드 생성 및 제어 api: `apps/v1`
* 인그레스 api : `extension/v1beta1`

```bash
# 사용가능한 api 목록 확인
$ kubectl api-versions
```
