NHN Cloud Kubernetes 사용해보기 #1

10 분 소요

image.png


참고자료

https://kubernetes.io/docs/reference/kubectl/cheatsheet/

Container Registry

  • docker image 저장소

image.png


ubuntu@k8s-dev:~$ docker login 702e0b18-kr1-registry.container.cloud.toast.com
Username: hyoyoung.kim@nhnsoft.com
Password:
WARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
  • 패스워드는 Appkey 이용



ubuntu@k8s-dev:~$ docker tag a2741b9780a5 702e0b18-kr1-registry.container.cloud.toast.com/myweb:0.1
ubuntu@k8s-dev:~$ docker images
702e0b18-kr1-registry.container.cloud.toast.com/myweb  0.1                a2741b9780a5   About an hour ago   165MB
myweb                                                  0.1                a2741b9780a5   About an hour ago   165MB

ubuntu@k8s-dev:~$ docker push 702e0b18-kr1-registry.container.cloud.toast.com/myweb:0.1
  • 도커 이미지 Container Registry로 push



image.png


  • 이미지 업로드 확인



image.png


ubuntu@k8s-dev:~$ docker pull 702e0b18-kr1-registry.container.cloud.toast.com/myweb:0.1
  • 이미지 가져오기


kubernetes 장점

  • 애플리케이션 배포 단순화
    • 특정 베어메탈을 필요로 하는 경우(예: SSD/HDD)
  • 하드웨어 활용도 극대화
    • 클러스터의 주변에 자유롭게 이동하여 실행중인 다양한 애플리케이션 구성 요소를 클러스터 노드의 가용 리소스에 최대한 맞춰 서로 섞고 매치
    • 노드의 하드웨어 리소스를 최상으로 활용
  • 상태 확인 및 자가 치유
    • 애플리케이션 구성 요소와 실행되는 노드를 모니터링 하고 노드 장애 발생시 다른 노드로 일정을 자동으로 재조정
    • 운영자는 정규 근무 시간에만 장애가 발생한 노드를 처리
  • 오토스케일링
    • 개별 애플리케이션의 부하를 지속적으로 모니터링할 필요 없이
    • 자동으로 리소스를 모니터링하고 각 애플리케이션에서 실행되는 인스턴스 수를 계속 조정하도록 지시 가능
  • 애플리케이션 개발 단순화
    • 버그 발견 및 수정 (완전히 개발환경과 같은 환경을 제공하기 때문)
    • 새로운 버전 출시 시 자동으로 테스트, 이상 발견 시 롤 아웃
    • 무 중단 업데이트, 다운그레이드



image.png

  • 모놀리식 라이프 사이클과 마이크로 서비스 라이프 사이클 비교


NHN Cloud Kubernetes

NHN Cloud에서 kubernetes 클러스터 생성 후, kube api server와 통신하기 위해서는 kubectl 설치 및 config 설정이 필요

( NHN Cloud에서는 관리용 인스턴스 사용을 위해 작업해주어야 함 )


image.png

  • 쿠버네티스 클러스트의 설정 파일 다운로드



mkdir ~/.kube
vim .kube/config
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.17.6/bin/linux/amd64/kubectl
sudo mv kubectl /usr/local/bin/
  • 다운로드 한 설정파일 업로드 혹은 붙여넣기



ubuntu@k8s-dev:~$ kubectl get nodes -o wide
NAME                                    STATUS   ROLES    AGE    VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION                CONTAINER-RUNTIME
k8s-edu-default-w-acano6xof5me-node-0   Ready    <none>   102m   v1.17.6   192.168.0.14   <none>        CentOS Linux 7 (Core)   3.10.0-1127.19.1.el7.x86_64   docker://19.3.15
k8s-edu-default-w-acano6xof5me-node-1   Ready    <none>   101m   v1.17.6   192.168.0.7    <none>        CentOS Linux 7 (Core)   3.10.0-1127.19.1.el7.x86_64   docker://19.3.15
  • kubernetes node 확인



pod

컨테이너가 실행되는 공간을 의미, 하나의 파드에 여러개의 컨테이너 (멀티 컨테이너)가 존재할 수 있음

ubuntu@k8s-dev:~$ kubectl get pod -o wide
NAME                  READY   STATUS    RESTARTS   AGE     IP             NODE                                    NOMINATED NODE   READINESS GATES
web-6d44978fd-2gsgh   1/1     Running   0          2m22s   10.100.19.21   k8s-edu-default-w-acano6xof5me-node-1   <none>           <none>
web-6d44978fd-6dnr5   1/1     Running   0          2m22s   10.100.3.18    k8s-edu-default-w-acano6xof5me-node-0   <none>           <none>
web-6d44978fd-hcvsn   1/1     Running   0          2m13s   10.100.3.19    k8s-edu-default-w-acano6xof5me-node-0   <none>           <none>
web-6d44978fd-kglzk   1/1     Running   0          2m22s   10.100.19.20   k8s-edu-default-w-acano6xof5me-node-1   <none>           <none>
web-6d44978fd-pzfnm   1/1     Running   0          2m10s   10.100.19.22   k8s-edu-default-w-acano6xof5me-node-1   <none>           <none>




deployment

deployment는 pod의 상위 개념 replicaset 을 통해 pod의 개수를 유지해주고, 어플리케이션을 다운타임없이 업데이트, 롤백을 가능하도록 함


kubectl create namespace dev
kubectl create namespace prod
  • 네임스페이스 생성 ( 네임스페이스란 보통 작업 공간을 나누는 용도로 사용, 서비스 별로 구분하여 사용하는 것을 권장 )


kubectl run web-dev --image nginx:1.16 --replicas 5 --namespace dev
  • deployment 생성



kubectl set image deployment/web-dev web-dev=nginx:1.17 --record -n dev
kubectl set image deployment/web-dev web-dev=nginx:1.18 --record -n dev
  • nginx 버전 무중단 업데이트 ( 롤백을 위해서는 –recode 옵션을 꼭 지정해주어야 함 )



kubectl rollout history deployment web-dev -n dev
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image deployment/web-dev web-dev=nginx:1.17 --record=true --namespace=dev
3         kubectl set image deployment/web-dev web-dev=nginx:1.18 --record=true --namespace=dev
  • –recode 옵션을 통해 생성된 롤백할 리비전을 선택



kubectl rollout undo deployment web-dev -n dev --to-revision=2
  • 리비전 2로 롤백 진행



kubectl describe pod web-dev-6b48cc6f5d-5z4qd -n dev |grep Image
    Image:          nginx:1.17
  • nginx 1.17 버전으로 무중단 롤백


무중단으로 업데이트, 롤백이 가능한 이유:

replicaset을 하나씩 순차적으로 팟 제거 후, 업데이트 된 팟을 생성하기 때문에 서비스가 중단되지 않음 ( 한번에 파괴 후, 일괄 생성도 가능하나 무 중단은 아님)


service

생성한 deployment를 아래와 같이, 서비스 생성

kubectl expose deployment nginx --port 80 --target-port 80 --type ClusterIP -n dev
  • nginx라는 deployment를 clusterIP로 80 포트로 서비스


kubectl get svc -n dev -o wide
NAMESPACE     NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE    SELECTOR
dev           nginx                       ClusterIP   10.254.83.77    <none>        80/TCP                   51s    run=nginx
  • Cluster IP 80 포트로 서비스 생성 (다만, 클러스터 IP는 내부끼리 통신은 가능하지만, 외부와는 통신이 안됨)



kubectl run apache --image httpd --replicas 5 -n prod
  • 아파치 deploymnet 생성



kubectl expose deployment apacge --port 80 --target-port 80 --type ClusterIP -n prod
  • 아파치 svc 생성



kubectl exec -it nginx-6db489d4b7-5kcth -n dev /bin/bash
root@nginx-6db489d4b7-5kcth:/# curl apache.prod.svc.cluster.local:80
<html><body><h1>It works!</h1></body></html>
  • nginx pod 접속 후, apache pod과 통신 테스트 정상 확인 ( ip는 계속 바뀔 수 있기 때문에 coredns를 통해 통신 / apache.prod.svc.cluster.local )



kubectl expose deployment nginx -n dev --port 80 --target-port 80 --type NodePort
  • nginx deployment를 NodePort 로 서비스 생성



kubectl get svc -o wide -n dev
NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
nginx   NodePort   10.254.117.30   <none>        80:30954/TCP   2m32s   run=nginx
  • nodeport로 svc 생성 확인


curl 192.168.0.14:30954

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
  • 노드포트로 통신 테스트 시, 정상 통신 확인


Load Balancer

kubectl expose deployment nginx --name nginx-lg -n dev --port 80 --target-port 80 --type LoadBalancer
  • nginx deployment를 LoadBalancer로 서비스 생성


image.png

  • 자동으로 NHN Cloud에서 LoadBalancer 생성 확인


* LoadBalancer는 클라우드 환경에서 제공되는 기능이며, 기본 온프레미스에서는 제공되지 않음 ( 온프레미스에서 사용하려면, 별도의 로드밸런서 설정, 구성등이 필요 )


ingress

하나의 IP 주소를 통해 여러 서비스를 제공하는 특별한 메커니즘 ( 하나의 IP로, 도메인, URI 별로 서비스를 연결해줄 수 있음 ) nginx proxy와 비슷

image.png


yaml

쿠버네티스 사용을 명령어로만 하게 된다면, 전체적인 서비스가 어떻게 구성 되었는지 직관적으로 알기가 어려움

그렇기 때문에 yaml 파일을 통해 코드화하여 정리하는 것을 권장

  • nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
    name: nginx
    namespace: dev
    labels:
      app: nginx
    annotations:
      creator: dev-team
spec:
    replicas: 2
    selector:
        matchLabels:
            nginx: dev
    template:
        metadata:
            labels:
                nginx: dev
        spec:
          containers:
          - name: nginx
            image: nginx:1.16
            resources:
              requests:
                 memory: 128Mi
                 cpu: 200m
              limits:
                 memory: 256Mi
                 cpu: 200m
            ports:
            - name: nginx
              containerPort: 80


kubectl apply -f nginx.yaml
  • yaml 파일로 deployment 생성


* 다만 수많은 생성 템플릿들을 외울 수 없고, 외우는 것은 쿠버네티스 개발자도 권장하지 않음



kubectl run nginx --image nginx --replicas 3 -n dev --dry-run -o yaml > nginx2.yaml
  • –dry-run ( 실행하지 않고 명령어가 정상적인지 확인하는 옵션 및 -o yaml 명령어를 통해 해당 명령어를 yaml 파일로 출력할 수도 있음 )


configmap

configmap은 key, value 값을 통하여 컨테이너 내에 환경변수를 전달할 수 있음 또한, volume을 전달할 수도 있음

  • configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
data:
  special.how: very
  weather: sun
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
data:
  log_level: INFO
  log_max_vol: "500Mi"
  db_host: "mysql.db.svc.cluster.local:3306"


kubectl describe cm env-config -n dev
Name:         env-config
Namespace:    dev
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration
                {"apiVersion":"v1","data":{"db_host":"mysql.db.svc.cluster.local:3306","log_level":"INFO","log_max_vol":"500Mi"},"kind":"ConfigMap","metad...

Data
====
db_host:
----
mysql.db.svc.cluster.local:3306
log_level:
----
INFO
log_max_vol:
----
500Mi
Events:  <none>
  • db_host:mysql.db.svc.cluster.local:3306 와 같이 key, value 값으로 확인 가능



  • config-env-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: config-env-pod
spec:
  containers:
    - name: test-container
      image: nginx:latest
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: SPECIAL_WEATHER
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: weather
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
        - name: LOG_MAX_VOL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_max_vol
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: db_host
  restartPolicy: Never
  • 생성한 configmap을 사용하는 pod 생성


kubectl exec config-env-pod -n dev env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=config-env-pod
LOG_MAX_VOL=500Mi
DB_HOST=mysql.db.svc.cluster.local:3306
SPECIAL_LEVEL_KEY=very
SPECIAL_WEATHER=sun
LOG_LEVEL=INFO
KUBERNETES_PORT=tcp://10.254.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.254.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.254.0.1
KUBERNETES_SERVICE_HOST=10.254.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
NGINX_VERSION=1.19.10
NJS_VERSION=0.5.3
PKG_RELEASE=1~buster
HOME=/root
  • pod에 환경 변수 적용 확인 가능


  • configmap.yaml
apiVersion: v1
data:
  default.conf: |
    upstream backend {
      server google.com:80;
    }

    server {
      listen 80;
      server_name example.com;
       location / {
         proxy_pass http://backend
         proxy_set_header Host $host;
        }
    }
kind: ConfigMap
metadata:
  name: nginx-conf


kubectl apply -f configmap.yaml -n prod
  • configmap 생성


  • pod-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: config-volume-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
      - name: nginx-volume
        mountPath: /etc/nginx/conf.d
  volumes:
  - name: nginx-volume
    configMap:
      name: nginx-conf
  • volume을 전달하는 configmap 사용하는 nginx image pod yaml 예제


kubectl apply -f pod-volume.yaml -n prod
  • pod 생성



kubectl exec -it config-volume-pod -n prod -- /bin/bash
root@config-volume-pod:/# cd /etc/nginx/conf.d/
root@config-volume-pod:/etc/nginx/conf.d# ls
default.conf
root@config-volume-pod:/etc/nginx/conf.d# cat default.conf
upstream backend {
  server google.com:80;
}

server {
  listen 80;
  server_name example.com;
   location / {
     proxy_pass http://backend
     proxy_set_header Host $host;
    }
}
  • configmap이 파일로 전달된 내용 확인



secret

configmap과 비슷하지만, 비밀번호, OAuth 토큰 및 ssh 키와 같은 민감한 정보는 secret을 통해 전달


  • secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-app
stringData:
  user: toast
  password: dG9hc3QxMjM0


  • secret-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
spec:
  containers:
    - name: test-container
      image: nginx
      env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: secret-app
              key: user
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: secret-app
              key: password


  • 환경 변수 적용 확인
kubectl exec secret-pod -n dev -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=secret-pod
DB_USERNAME=toast
DB_PASSWORD=dG9hc3QxMjM0



  • secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-key
stringData:
  ssh.key: |-
    -----BEGIN RSA PRIVATE KEY-----
    MIIEpAIBAAKCAQEA5N+n2XSPKUOCEwdhLkn3PcicgoBRAFEO9BJbZMnnKmzqVrGn
    P+I14p2wG3ssWqq06vQjGicDR523if7TkWYdSdsayaM50PW/uxwvkYFkSB0zKaw4
    4OLneLZaFmLQD4iA1CdVIBGy0+3ktEbwRnPyZMl7EBvjc7Sy+ENMiflb5ae4ZtzR
    -----END RSA PRIVATE KEY-----


  • secret-vol-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-vol-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
      - name: secret-volume
        mountPath: /confidential
  volumes:
  - name: secret-volume
    secret:
      secretName: secret-key


  • ssh key 값이 secret 통해 전달된 내용 확인
kubectl exec -it secret-vol-pod -n dev -- /bin/bash
root@secret-vol-pod:/# cd /confidential/
root@secret-vol-pod:/confidential# ls
ssh.key
root@secret-vol-pod:/confidential# cat ssh.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA5N+n2XSPKUOCEwdhLkn3PcicgoBRAFEO9BJbZMnnKmzqVrGn
P+I14p2wG3ssWqq06vQjGicDR523if7TkWYdSdsayaM50PW/uxwvkYFkSB0zKaw4
4OLneLZaFmLQD4iA1CdVIBGy0+3ktEbwRnPyZMl7EBvjc7Sy+ENMiflb5ae4ZtzR
-----END RSA PRIVATE KEY-----



Voulme

* pv, pvc의 경우 LoadBalancer와 마찬가지로 클라우드 환경에서 제공되는 기능

  • sc-retain.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: sc-retain
provisioner: kubernetes.io/cinder
reclaimPolicy: Retain
volumeBindingMode: Immediate

스토리지 클래스는 NHN Cloud (혹은 타사 클라우드)에 스토리지를 사용하겠다고 선언하는 개념


  • pvc-dynamic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-dynamic
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: sc-retain

PVC는 스토리지 요청하는 개념


kubectl get pvc -n dev
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-dynamic   Bound    pvc-feee42e8-b962-49a4-b680-c7177d065207   10Gi       RWO            sc-retain      92s

image.png

  • PVC를 통해 10GB를 요청하였고, NHN Cloud에 생성된 모습 확인 ( 연결 정보 없음 )



  • pod.yaml
# pod-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-static-pv
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
      volumeMounts:
        - name: html-volume
          mountPath: "/usr/share/nginx/html"
  volumes:
    - name: html-volume
      persistentVolumeClaim:
        claimName: pvc-dynamic

해당 스토리지를 사용하는 pod 생성


kubectl exec nginx-with-static-pv -n dev mount | grep html
/dev/vdb on /usr/share/nginx/html type ext4 (rw,relatime,data=ordered)

image.png

  • volume 연결된 모습 확인


실전 wordpress 서비스 구성

mysql 구성


  • mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.6
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-password
              key: password
        resources:
          requests:
            memory: 512Mi
            cpu: 100m
          limits:
            memory: 512Mi
            cpu: 200m
        volumeMounts:
        - name: mysql
          mountPath: /var/lib/mysql
        ports:
        - containerPort: 3306
      volumes:
      - name: mysql
        persistentVolumeClaim:
          claimName: mysql-pvc


  • mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  resources:
    requests:
      storage: 10Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: sc-retain



  • mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-password
stringData:
  password: dG9hc3QxMjM0


  • 전체 yaml 파일 apply
kubectl apply -f ./*



Wordpress 설치


  • wp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress:4.8-apache
        env:
         - name: WORDPRESS_DB_HOST
           value: "mysql.db.svc.cluster.local"
         - name: WORDPRESS_DB_PASSWORD
           valueFrom:
             secretKeyRef:
               name: mysql-password
               key: password
        volumeMounts:
        - name: wordpress
          mountPath: /var/www/html
        ports:
        - containerPort: 80
      volumes:
      - name: wordpress
        persistentVolumeClaim:
          claimName: wp-pvc



  • wp-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pvc
spec:
  resources:
    requests:
      storage: 10Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: sc-retain


  • wp-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-password
stringData:
  password: dG9hc3QxMjM0


  • wp-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: wordpress
  type: LoadBalancer


  • 전체 yaml 파일 apply
kubectl apply -f ./*


  • 플로팅 IP로 접속

image.png


  • 사이트 확인

image.png

NHN CLOUD Kubernetes 사용 후기

전반적으로 UI가 깔끔하고 외산 클라우드에 비해 사용하기 편하다라는 느낌을 받았습니다. 물론 GCP나 AWS에서는 지원하는 기능이 NHN Cloud에서는 지원하지 않는 부분도 일부 있었지만, 사소한 부분이고 전체적으로 쓸만하다고 느꼈습니다.

댓글남기기