[![logo](https://www.hongsnet.net/images/logo.gif)](https://www.hongsnet.net)

# K8s Service Deploy에 대한 비교 및 검토

- **Pod Deployment**에 대한 검증을 수행한다.
- **Service Expose Network**에 대한 검증을 수행한다.


# hello-world (nodejs) Service

> Service Deploy를 검증하는 간단한 nodejs 컨테이너

> Container Image : registry.hongsnet.net/joohan.hong/docker/nodejs:latest


# K8s

> URL : https://k8s-demo.freehongs.net


## Dockerfile

```bash
# cat Dockerfile
FROM node
MAINTAINER Hongs <master@hongsnet.net>

RUN mkdir -p /usr/src/app
COPY index.js /usr/src/app

EXPOSE 8080

CMD ["node", "/usr/src/app"]

```

```bash
# cat index.js
var http = require('http');
var os = require('os');

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    console.log(req.socket.localAddress)
    res.end(`<h1>Docker Container ID -> ${os.hostname()}</h1><br><h1>Server IP -> ${req.socket.localAddress}</h1><h1>Client IP -> ${req.socket.remoteAddress}</h1><br>Container Tag -> Update_20200916`);
}).listen(8080);

```

## Object의 생성

- **Deployment**

```python
# cat hello-world.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: k8s-verify
  name: hello-world
spec:
  replicas: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: k8s-verify
  template:
    metadata:
      labels:
        app: k8s-verify
    spec:
      volumes:
      - name: webeditor-hongsnet-volume
        persistentVolumeClaim:
          claimName: webeditor-hongsnet-volume-pvc
      - name: webdata-hongsnet-volume
        persistentVolumeClaim:
          claimName: webdata-hongsnet-volume-pvc
      - name: webeditor-edu-volume
        persistentVolumeClaim:
          claimName: webeditor-edu-volume-pvc
      - name: webdata-edu-volume
        persistentVolumeClaim:
          claimName: webdata-edu-volume-pvc
      - name: webeditorfile-edu-volume
        persistentVolumeClaim:
          claimName: webeditorfile-edu-volume-pvc
      - name: webeditor-newsystem-volume
        persistentVolumeClaim:
          claimName: webeditor-newsystem-volume-pvc
      - name: webdata-newsystem-volume
        persistentVolumeClaim:
          claimName: webdata-newsystem-volume-pvc
      - name: testsrc-home-volume
        persistentVolumeClaim:
          claimName: testsrc-home-volume-pvc
      containers:
      - image: registry.hongsnet.net/joohan.hong/docker/nodejs:latest
        #imagePullPolicy: Always
        imagePullPolicy: IfNotPresent
        name: hello-world
        volumeMounts:
         - mountPath: /home
           name: testsrc-home-volume
         - mountPath: /home/edu/public_html/HongsBoard/Data
           name: webdata-edu-volume
         - mountPath: /home/edu/public_html/HongsBoard/Web_editor/EDU
           name: webeditor-edu-volume
         - mountPath: /home/edu/public_html/HongsBoard/Web_editor/FILE
           name: webeditorfile-edu-volume
         - mountPath: /home/hongsnet/public_html/Data
           name: webdata-hongsnet-volume
         - mountPath: /home/hongsnet/public_html/Web_editor/FILE
           name: webeditor-hongsnet-volume
         - mountPath: /home/newhongsystem/public_html/Data
           name: webdata-newsystem-volume
         - mountPath: /home/newhongsystem/public_html/Web_editor/FILE
           name: webeditor-newsystem-volume
        resources:
          requests:
            cpu: "500m"
          limits:
            cpu: "500m"
      nodeSelector:
        key: worker

```

- **Service**

```python
# cat hello-world_service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hongsnet
spec:
  type: NodePort
  selector:
    app: k8s-verify
  ports:
    - port: 8080
      targetPort: 8080
      nodePort: 32000
```

- **Scaling**

```python
# cat hello-world_scale.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: k8s-verify-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hello-world
  minReplicas: 5
  maxReplicas: 10
  targetCPUUtilizationPercentage: 5

```

- **Execute**

```bash
# kubectl apply -f hello-world.yaml
# kubectl apply -f hello-world_scale.yaml
# kubectl apply -f hello-world_service.yaml
```

## K8s Cluster 구성

다음과 같이 4 대로 구성했다.

* master : cube01 (172.24.0.222)
* worker01 : cube02 (172.16.0.223)
* worker02 : cube03 (172.16.0.224)
* worker03 : cube04 (172.16.0.225)

구성된 내역은 다음과 같다.

```bash
# kubectl get nodes
NAME     STATUS   ROLES                  AGE   VERSION
cube01   Ready    control-plane,master   24h   v1.20.4
cube02   Ready    <none>                 24h   v1.20.4
cube03   Ready    <none>                 24h   v1.20.4
cube04   Ready    <none>                 24h   v1.20.4
```

k8s의 경우 Swarm과 달리 **Overlay 네트워크**가 기본적으로 제공되지않고, **Network Plugin을 Addon하게 설치**해야한다. 여러가지 Plugin을 제공하며, 여기서는 **Flannel**을 사용한다.

```bash
# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.254.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

```

## Pod Check

```bash
# kubectl get pods -o wide
NAME                           READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
hello-world-7856df46fd-5ws4b   1/1     Running   0          23m   10.244.254.2   cube03   <none>           <none>
hello-world-7856df46fd-cgq88   1/1     Running   0          23m   10.244.254.3   cube02   <none>           <none>
hello-world-7856df46fd-jmrfc   1/1     Running   0          23m   10.244.254.3   cube03   <none>           <none>
hello-world-7856df46fd-jw4gs   1/1     Running   0          23m   10.244.254.2   cube02   <none>           <none>
hello-world-7856df46fd-z25bl   1/1     Running   0          23m   10.244.254.4   cube02   <none>           <none>

```

## Service Check

```bash
# kubectl get services hongsnet
NAME       TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hongsnet   NodePort   10.98.254.117   <none>        8080:32000/TCP   25m
```

> 네트워크 구성중 "NodePort"를 사용하는 이유는 바로 아래의 내용을 참조한다.


## `Service` External Network 노출 방식

각 Pod들이 고유의 IP를 갖고 있기는 하지만, 그 IP들은 서비스의 도움없이 클러스터 외부로 노출되어질 수 없다. 따라서 다음 방식을 통해 내부 또는 외부로의 노출을 검토해야 한다.


1. **CluserIP**

ClusterIP는 `기본 값`이며, 클러스터 내에서 내부 IP 에 대해 서비스를 노출해준다. 이 방식은 오직 클러스터 내에서만 서비스가 접근될 수 있도록 해준다.


2. **NodePort** <= Pick

NAT가 이용되는 클러스터 내에서 각각 선택된 노드들의 동일한 포트에 서비스를 노출시켜준다. <NodeIP>:<NodePort>를 이용하여 클러스터 외부로부터 서비스가 접근할 수 있도록 해준다. **ClusterIP의 상위 집합**이다.


3. **LoadBalancer**

지원 가능한 경우 기존 클라우드에서 외부용 로드밸런서를 생성하고 서비스에 고정된 공인 IP를 할당해준다. **NodePort의 상위 집합**이다.

4. **ExternalName**

이름으로 **CNAME 레코드**를 반환함으로써 임의의 이름(스펙에서 externalName으로 명시)을 이용하여 서비스를 노출시켜준다. 프록시는 사용되지 않는다. 이 방식은 **kube-dns 버전 1.7 이상에서 지원 가능**하다.


> 검증에서는 NodePort를 사용하며, 포트는 **32000/tcp**으로 고정한다. 왜냐하면, Pod 재실행 시 이 포트는 `Random`(기본 값: 30000~32768)하게 변경되기 때문이며, 외부 접속은 Apache Proxy를 사용한다. 이는 현재 Swarm 환경과 거의 동일한 구성이다.


```plaintext
Client -> Proxy(Apache) -> Worker01
                        -> Worker02
                        -> Worker03
```


# Pod의 확장

위의 5개의 Replica로 구성된 상태에서 다음과 같이 **2 개의 Replica를 추가해서 확장**해 보자.

```bash
# kubectl scale deployment/hello-world --replicas=7
deployment.apps/hello-world scaled
```

그럼 다음과 같이 총 7개의 Pod가 실행되고 있는 것을 볼 수 있다.

```bash
# ./kube-podlist.sh
NAME                           READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES
hello-world-7856df46fd-6c286   1/1     Running   0          15s   10.244.254.12   cube02   <none>           <none>
hello-world-7856df46fd-8j5r7   1/1     Running   0          15s   10.244.254.13   cube02   <none>           <none>
hello-world-7856df46fd-lkswt   1/1     Running   0          25m   10.244.254.10   cube02   <none>           <none>
hello-world-7856df46fd-lxsjt   1/1     Running   0          25m   10.244.254.11   cube02   <none>           <none>
hello-world-7856df46fd-mbtkp   1/1     Running   0          25m   10.244.254.9    cube03   <none>           <none>
hello-world-7856df46fd-mdqqw   1/1     Running   0          25m   10.244.254.11   cube03   <none>           <none>
hello-world-7856df46fd-qxvsl   1/1     Running   0          25m   10.244.254.10   cube03   <none>           <none>

```

# Pod의 축소

위의 **7개**의 Pod를 초기 **5개**로 축소해보자.

```bash
# kubectl scale deployment/hello-world --replicas=5
deployment.apps/hello-world scaled
```

그럼, 7개의 Pod 중 2개의 상태가 Terminating 되는 것을 볼 수 있으며, 최종적으로는 5개가 된다.

```bash
# ./kube-podlist.sh
NAME                           READY   STATUS        RESTARTS   AGE    IP              NODE     NOMINATED NODE   READINESS GATES
hello-world-7856df46fd-6c286   1/1     Terminating   0          104s   10.244.254.12   cube02   <none>           <none>
hello-world-7856df46fd-8j5r7   1/1     Terminating   0          104s   10.244.254.13   cube02   <none>           <none>
hello-world-7856df46fd-lkswt   1/1     Running       0          27m    10.244.254.10   cube02   <none>           <none>
hello-world-7856df46fd-lxsjt   1/1     Running       0          27m    10.244.254.11   cube02   <none>           <none>
hello-world-7856df46fd-mbtkp   1/1     Running       0          27m    10.244.254.9    cube03   <none>           <none>
hello-world-7856df46fd-mdqqw   1/1     Running       0          27m    10.244.254.11   cube03   <none>           <none>
hello-world-7856df46fd-qxvsl   1/1     Running       0          27m    10.244.254.10   cube03   <none>           <none>
```
