Page tree
Skip to end of metadata
Go to start of metadata

로컬 머신에 쿠버네티스 설치 방법

  • 교재 5장(p 186)에 Docker를 이용해서 쿠버네티스를 사용할 수 있었음
  • 로컬 쿠버네티스로 Minikube를 많이 사용했었지만 Docker를 이용하면 조금 더 편리함
  • Minikube와 docker for desktop의 비교하고 있는 페이지는 다음 링크를 참조


쿠버네티스 대시보드

  • 쿠버네티스에 배포된 컨테이너 등에 대한 정보를 한눈에 보여주는 관리도구
  • 쿠버네티스는 kubectl이라는 CLI를 제공하지만 추가적으로 웹 기반의 관리 콘솔도 제공하는데 이를 대시보드라고 함

대시보드 설치

대시보드 UI는 기본으로 배포되지 않기 때문에 다음 명령어를 통해 설치를 합니다.

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml

로컬에서 쿠버네티스에 접근하기 위해서 다음 명령어를 실행합니다.

$ kubectl proxy

설치가 완료 되면 다음 주소를 통해 접근할 수 있습니다.

http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/

대시보드 UI 접근

클러스터 데이터를 보호하기 위해 대시보드는 기본적으로 최소한의 RBAC 설정을 제공합니다. 대시보드에 접근하기 위해서 샘플 사용자를 만들어야 합니다. 현재 대시보드는 Bearer 토큰으로 로그인하는 방법을 제공합니다.

아래의 파일을 이용하여 admin-user라는 사용자를 생성합니다.

admin-user.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system

사용자 등록을 위해 다음 명령어를 실행합니다.

$ kubectl apply -f admin-user.yaml

그 다음 cluster-admin role을 admin-user에 바인딩합니다. 해당 설정 파일은 다음과 같습니다.

admin-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system

마찬가지로 rolebinding을 위해 다음 명령어를 실행해서 해당 설정을 적용합니다.

$ kubectl apply -f admin-rolebinding.yaml

다음 명령어를 통해서 Bearer 토큰 값을 알 수 있습니다. 

$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')

명령어를 입력하면 다음과 같이 토큰이 출력됩니다.

해당 토큰 값을 복사하여 대시보드의 토큰 값에 복사를 하고 로그인을 하면 대시보드를 사용할 수 있습니다.

Sing in

대시보드를 통해 kubectl 명령어를 통해 실행하는 것들을 웹을 통해 실행이 가능해지며 클러스터 관리에 도움을 줍니다.


Todo 애플리케이션 on k8s

로컬 머신에서 다음 그림과 같은 Todo application을 구축하는 예제를 살펴봅니다.

데이터스토어는 mysql을 사용하는데 master-slave 구성을 사용합니다. 

PersistentVolumn과 PersistentVolumnClaim(PVC)

일반적으로 도커에서 퍼시스턴트 데이터를 다루는 경우 데이터 볼륨을 사용합니다. 표준 데이터 볼륨은 컨테이너가 배포된 호스트에 위치 해야 합니다. 이러한 데이터 볼륨을 사용하면 호스트 간에 컨테이너를 재배치 하는 과정이 번거로워집니다.

이러한 문제 해결을 위해 쿠버네티스에서는 호스트와 분리 되어 있는 외부 스토리지를 볼륨으로 사용할 수 있는 기능을 제공하는데 이러한 기능을 구현한 것 중에 하나가 PV와 PVC입니다. 간단하게 그림으로 살펴보면 다음과 같습니다.

퍼시스턴트 볼륨은 물리적인 스토리지 자체입니다. 즉 시스템 관리자가 생성한 물리 디스크를 쿠버네티스 클러스터로 표현하는 것이 PV이고 Pod의 볼륨과 PV를 연결하는 것이 PVC입니다. PVC는 추상화된 논리 리소스로, PV와 달리 용량을 필요한 만큼 동적으로 확보 가능합니다.

로컬 머신에 PersistentVolumn 만들기

일반적으로 로컬 머신에서는 PersistentVolumn을 사용하지 않고 데이터 볼륨을 사용하지만, Todo Application 예제를 위해서 로컬 머신에 PV를 직접 띄우도록 하겠습니다. Google Cloud Platform과 같은 것을 사용하면 바로 클라우드에서 제공하는 PV를 사용할 수 있습니다.

Todo Application의 Mysql의 경우 Master - 2 slaves 로 구성하고 있습니다. 그렇기 때문에 총 3개의 퍼시스턴트 볼륨이 필요합니다.

mysql-pv-1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-1
spec:
  capacity:
    storage: 4Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
#    path: ${/path/to/your/data/directory}
    path: /Users/naver/study/kubenetes/mnt/data1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - docker-for-desktop
mysql-pv-2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-2
spec:
  capacity:
    storage: 4Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /Users/naver/study/kubenetes/mnt/data2
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - docker-for-desktop
mysql-pv-3.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-3
spec:
  capacity:
    storage: 4Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /Users/naver/study/kubenetes/mnt/data3
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - docker-for-desktop

위의 3개의 yaml 파일을 생성합니다. path의 경우 자신의 로컬 디렉토리에 맞춰서 생성해서 연결할 수 있도록 합니다.

그리고 다음 명령어를 통해 PV를 생성합니다.

$ kubectl apply -f mysql-pv-1.yaml
$ kubectl apply -f mysql-pv-2.yaml
$ kubectl apply -f mysql-pv-3.yaml

pv가 정상적으로 생성되면 다음 명령어를 통해 확인할 수 있습니다.

$ kubectl get pv

스토리지클래스(StorageClass)

  • 스토리지클래스는 퍼시스턴트볼륨으로 확보한 스토리지의 종류를 정의하는 리소스
  • 퍼시스턴트볼륨클레임에 나와있는 속성값이 이해 해당
  • 뒤에 어떤식으로 스토리지클래스를 PVC에서 사용하는지 나옴

로컬에서 사용할 스토리지 클래스를 정의합니다.

localstorage.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

스토리지클래스를 다음 명령어를 통해 적용합니다.

$ kubectl apply -f localstorage.yaml

스테이트풀셋(StatefulSet)

데이터스토어 처럼 데이터를 계속 유지하고, 상태가 있는(stateful) 애플리케이션을 관리하는데 적합한 리소스입니다.

MySQL을 마스터-슬레이브로 구성한 파드를 생성하는데 스테이트풀셋을 사용해서 생성할 예정입니다.

MySQL 실행하기

다음은 MySQL의 master 매니페스트 파일입니다.

mysql-master.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-master 
  labels:
    app: mysql-master 
spec:
  ports:
  - port: 3306
    name: mysql 
  clusterIP: None
  selector:
    app: mysql-master 

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-master
  labels:
    app: mysql-master 
spec:
  serviceName: "mysql-master"
  selector:
    matchLabels:
      app: mysql-master 
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql-master
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: mysql 
        image: gihyodocker/tododb:latest
        imagePullPolicy: Always
        args:
        - "--ignore-db-dir=lost+found"
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "gihyo"
        - name: MYSQL_DATABASE
          value: "tododb"
        - name: MYSQL_USER
          value: "gihyo" 
        - name: MYSQL_PASSWORD
          value: "gihyo"
        - name: MYSQL_MASTER
          value: "true"
        volumeMounts:
        - name: example-local-claim
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: example-local-claim
    spec:
      accessModes:
      - ReadWriteOnce
      storageClassName: local-storage
      resources:
        requests:
          storage: 4Gi

위의 파일에서 mysql-master에서 사용할 statefulset을 생성합니다. 파일내에 volumeClaimTemplates을 이용하여 PVC를 파드마다 자동으로 생성하는 템플릿을 선언했습니다. 이러한 기능 덕분에 파드에서 요구하는 PVC를 매번 만들지 않아도 됩니다. 

그리고 해당 파일을 쿠버네티스에 적용해서 생성합니다.

$ kubectl apply -f mysql-master.yaml

다음은 슬레이브용 스테이트풀셋을 생성합니다.

mysql-slave.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-slave
  labels:
    app: mysql-slave 
spec:
  ports:
  - port: 3306
    name: mysql 
  clusterIP: None
  selector:
    app: mysql-slave 

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-slave
  labels:
    app: mysql-slave 
spec:
  serviceName: "mysql-slave"
  selector:
    matchLabels:
      app: mysql-slave
  replicas: 2
  updateStrategy:
    type: OnDelete
  template:
    metadata:
      labels:
        app: mysql-slave
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: mysql 
        image: gihyodocker/tododb:latest
        imagePullPolicy: Always
        args:
        - "--ignore-db-dir=lost+found"
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_MASTER_HOST
          value: "mysql-master"
        - name: MYSQL_ROOT_PASSWORD
          value: "gihyo"
        - name: MYSQL_DATABASE
          value: "tododb"
        - name: MYSQL_USER
          value: "gihyo" 
        - name: MYSQL_PASSWORD
          value: "gihyo"
        - name: MYSQL_REPL_USER
          value: "repl"
        - name: MYSQL_REPL_PASSWORD
          value: "gihyo"
        volumeMounts:
        - name: example-local-claim
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: example-local-claim
    spec:
      accessModes:
      - ReadWriteOnce
      storageClassName: local-storage
      resources:
        requests:
          storage: 4Gi

슬레이브의 경우 replicas 설정을 2로 하여 슬레이브가 2개 생성되도록 하고 있습니다. 또한 마찬가지로 volumeClaimTemplates을 선언하여 슬레이브 파드 마다 PVC가 생성되도록 설정하였습니다. spec에는 실제 master 연결을 위한 설정들이 있습니다.

다음 명령어를 통해 마찬가지로 쿠버네티스에 적용합니다.

$ kubectl apply -f mysql-slave.yaml

마스터용 파드에 init-data.sh를 실행해 초기 데이터를 등록하도록 합니다.  데이터 등록 스크립트는 다음과 같습니다.

init-data.sh
#!/bin/bash -e

for SQL in `ls /sql/*.sql`
do
  mysql -u root -p$MYSQL_ROOT_PASSWORD $MYSQL_DATABASE < $SQL > /dev/null 2>&1
done


그런 다음 그 내용이 슬레이브용 파드에 반영됐는지 확인합니다.

$ kubectl get pod
$ kubectl exec -it mysql-master-0 init-data.sh
$ kubectl exec -it mysql-slave-0 bash


# mysql -u root -pgihyo tododb -e "SHOW TABLES;"

정상적으로 등록된 것을 확인할 수 있습니다.

Todo API 구축

다음과 같이 todo-api.yaml 파일을 작성하고 적용합니다.

todo-api.yaml
apiVersion: v1
kind: Service
metadata:
  name: todoapi
  labels:
    app: todoapi 
spec:
  selector:
    app: todoapi
  ports:
    - name: http
      port: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todoapi 
  labels:
    app: todoapi
spec:
  replicas: 2
  selector:
    matchLabels:
      app: todoapi
  template:
    metadata:
      labels:
        app: todoapi
    spec:
      containers:
      - name: nginx
        image: gihyodocker/nginx:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 80
        env:
        - name: WORKER_PROCESSES
          value: "2"
        - name: WORKER_CONNECTIONS
          value: "1024"
        - name: LOG_STDOUT
          value: "true"
        - name: BACKEND_HOST
          value: "localhost:8080"
      - name: api
        image: gihyodocker/todoapi:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
        env:
        - name: TODO_BIND
          value: ":8080"
        - name: TODO_MASTER_URL
          value: "gihyo:gihyo@tcp(mysql-master:3306)/tododb?parseTime=true"
        - name: TODO_SLAVE_URL
          value: "gihyo:gihyo@tcp(mysql-slave:3306)/tododb?parseTime=true"

마찬가지로 다음 명령어를 통해 적용합니다.

$ kubectl apply -f todo-api.yaml

todo-api가 정상적으로 실행됐는지 확인을 위해 다음 명령어를 실행합니다.

$ kubectl get pod -l app=todoapi

Todo Web 구축

API와 구축했을 때와 마찬가지로 todo-web.yaml을 생성하여 실행합니다.

todo-web.yaml
apiVersion: v1
kind: Service
metadata:
  name: todoweb
  labels:
    app: todoweb
spec:
  selector:
    app: todoweb
  ports:
    - name: http
      port: 80
  type: NodePort

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todoweb 
  labels:
    name: todoweb
spec:
  replicas: 2
  selector:
    matchLabels:
      app: todoweb
  template:
    metadata:
      labels:
        app: todoweb
    spec:
      volumes:
      - name: assets
        emptyDir: {}
      containers:
      - name: nginx
        image: gihyodocker/nginx-nuxt:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 80
        env:
        - name: WORKER_PROCESSES
          value: "2"
        - name: WORKER_CONNECTIONS
          value: "1024"
        - name: LOG_STDOUT
          value: "true"
        - name: BACKEND_HOST
          value: "localhost:3000"
        volumeMounts:
        - mountPath: /var/www/_nuxt
          name: assets 

      - name: web 
        image: gihyodocker/todoweb:latest
        imagePullPolicy: Always
        lifecycle:
          postStart:
            exec:
              command:
              - cp
              - -R
              - /todoweb/.nuxt/dist
              - /
        ports:
        - containerPort: 3000 
        env:
        - name: TODO_API_URL
          value: http://todoapi
        volumeMounts:
        - mountPath: /dist
          name: assets 
$ kubectl apply -f todo-web.yaml
$ kubectl get pod -l app=todoweb

위의 명령어를 통해 정상적으로 todoweb이 실행되었는지 확인할 수 있습니다. todoweb의 경우 서비스는 나중에 인그레스를 사용해 외부로 노출시킬수 있도록 타입을 NodePort로 하였습니다. 다음과 같이 서비스 상태를 살펴보면 포트가 할당된 것을 확인할 수 있습니다.

$ kubectl get svc todoweb

책에서는 인그레스를 통해서 웹 애플리케이션을 외부로 노출할 수 있는 방법을 보여줍니다. 그러나 로컬에서는 따로 설정해주지 않아도 NodePort를 통해 접속 할수 있습니다. 위 명령어를 통해서 확인한 포트 번호를 확인하고 웹에 localhost:port 번호를 입력하면 다음과 같이 정상적으로 todo-web이 실행되는 것을 확인할 수 있습니다.

정리

이번 장은 쿠버네티스 대시보드와 쿠버네티스 클러스터에서 실제 퍼시스턴트 데이터를 구성하고, 웹 애플리케이션을 구성해서 배포하는 것을 예제로 확인하였습니다. 각 매니페스트 파일을 자세히 보진 않았지만 어떻게 쿠버네티스에서 MySQL을 구성하고 웹 API 서버와 웹 애플리케이션을 작성하는지 간략히 알아보았습니다.

이를 통해 어느 정도 쿠버네티스 클러스터에 애플리케이션을 구성하는 방법에 대해 알수 있었습니다. 물론 각 애플리케이션에서 사용하는 정보들은 각각 익혀야겠지만 한번 구성을 잘 해두면 변경을 반영해서 새로 재배포하는 것이 더욱 수월할 것이라고 생각됩니다.


  • No labels

2 Comments

  1. 노드명은 'docker-for-desktop'가 아닌 'docker-desktop' 일 수도 있다.

  2. 노드명 확인 후 설정하는 것이 좋겠네요.

    > kubectl get node
    
    
    NAME             STATUS   ROLES    AGE     VERSION
    docker-desktop   Ready    master   4h35m   v1.14.3