도커/쿠버네티스 컨테이너 개발 실전 입문 · · 7 min read
CH06. 쿠버네티스 클러스터 구축
1. Google Kubernetes Engine 환경 설정
4장에서 구축한 TODO 어플리케이션을 쿠버네티스로 전환하고 GKE를 사용해서 배포할 것이다.
GCP 명령행 도구인 구글 클라우드 SDK를 설치하고 마지막으로 GKE에 새로운 쿠버네티스 클러스터를 구축할 것이다.
GCP 프로젝트 생성
- 새로운 프로젝트 생성
- ex ) joon-kuber
- GCP를 명령행 도구로 사용하기 위해서 프로젝트 ID를 사용해야 한다.
- 새로운 프로젝트 생성
구글 클라우드 SDK(gcloud) 설치(ubuntu18.04에 설치함)
kubectl 설치(ubuntu18.04에 설치함)
- curl로 최신 릴리즈 다운로드
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl - curl로 특정 릴리즈 다운로드 ->
curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt을 바꾼다.$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.18.0/bin/linux/amd64/kubectl - kubectl 바이너리를 실행 가능하게 만든다.
$ chmod +x ./kubectl - 바이너리를 PATH가 설정된 디렉터리로 옮긴다.
$ sudo mv ./kubectl /usr/local/bin/kubectl - 설치한 버전을 확인한다.
$ kubectl version --client
- curl로 최신 릴리즈 다운로드
gcloud 컴포넌트 업데이트
$ gcloud components update인증 설정 및 project id 설정, 리전 설정
$ gcloud auth login $ gcloud config set project [프로젝트 이름] $ gcloud config set compute/zone asia-northeast3-a클러스터 생성
$ gcloud container clusters create [클러스터 이름] --cluster-version=[클러스터 버전] --machine-type=[인스턴스 타입] --num-nodes=[노드 수]kubectl로 클러스터를 제어할 수 있도록 kubectl에 인증정보를 설정한다.
$ gcloud container clusters get-credentials [클러스터 이름] $ kubectl get nodes프로젝트 이름 : joon
환경 : ubuntu 18.04
kubectl 설치
- 버전 :
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.3", GitCommit:"2e7996e3e2712684bc73f0dec0200d64eec7fe40", GitTreeState:"clean", BuildDate:"2020-05-20T12:52:00Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
- 버전 :
2. GKE에 TODO 어플리케이션 구축
- 4장에서의 TODO 어플리케이션을 GKE에 구축한다.
- [아키텍처]
3. GKE에 MySQL 마스터-슬레이브 구성으로 구축
퍼시스턴트볼륨과 퍼시스턴트볼륨클레임
- 퍼시스턴트볼륨 : 스토리지 자체, GCP의 GCEPersistentDisk
- 퍼시스턴트볼륨클레임 : 추상화된 논리 리소스로, 퍼시스턴트볼륨과 달리 용량을 필요한 만큼 동적으로 확보할 수 있다.
- 퍼시스턴트볼륨클레임의 매니페스트 파일
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-example spec: accessModes: - ReadWriteOnce storageClassName: ssd resources: requests: storage: 4Gi- accessModes : 파드가 스토리지에 접근하는 방식
- ReadWriteOnce : 마운트될 수 있는 노드를 하나로 제한한다.
- storageClassName : StorageClass 리소스의 종류(어떤 스토리지를 사용할 것인지 정의)
- 파드는 퍼시스턴트볼륨클레임을 직접 마운트할 수 있으며, 데이터를 볼륨에 넣고 나면 파드를 정지하거나 다시 생성해도 어플리케이션의 상태가 유지된다.
- accessModes : 파드가 스토리지에 접근하는 방식
StorageClass
- 퍼시스턴스볼륨으로 확보한 스토리지의 종류를 정의하는 리소스
- GCP 스토리지 종류 : 표준, SSD
- storage-class-ssd.yaml 작성
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: ssd annotations: storageclass.kubernetes.io/is-default-class: "false" labels: kubernetes.io/cluster-service: "true" provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd- 스토리지클래스의 name을 ssd로 지정
- provisioner는 GCP의 퍼시스턴트 스토리지인 GCEPersistentDisk에 해당하는 gcd-pd로 지정
- 파라미터의 type 속성을 pd-ssd로 지정
- 스토리지 클래스 생성
$ kubectl apply -f storage-class-ssd.yaml
스테이트풀세트(StatefulSet)
- 디플로이먼트는 함께 포함된 파드 정의를 따라 파드를 생성하는 리소스로, 하나만 있으면 되는 파드 혹은 퍼시스턴스 데이터를 갖지 않는 상태가 없는(stateless) 어플리케이션을 배포하는데 적합하다.
- 스테이트풀세트는 데이터 스토어처럼 데이터를 계속 유지하는, 상태가 있는(stateful) 어플리케이션을 관리하는 데 적합한 리소스다.
- 마스터용 설정 :: 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: mysql-data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: ssd resources: requests: storage: 4Gi - 슬레이브용 설정 :: 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: abels: app: mysql-slave spec: terminationGracePeriodSeconds: 60 containers: - ame: 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: mysql-data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: ssd resources: requests: storage: 4Gi - 마스터-슬레이브 배포
$ kubectl apply -f mysql-master.yaml $ kubectl apply -f mysql-slave.yaml - 실행 결과 확인 및 데이터 삽입, 확인
$ kubectl get pod $ kubectl exec -it mysql-master-0 -- init-data.sh $ kubectl exec -it mysql-slave-0 bash mysql-slave-0# mysql -u root -pgihyo tododb -e "show tables;"
4. GKE에 TODO API 구축
- 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: name: 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 $ kubectl get pod -l app=todoapi
5. GKE에 TODO 웹 어플리케이션 구축
- 정적파일(애셋)은 Node.js를 거치지 않고 Nginx에서 바로 제공한다.
- todo-api.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: # (1) - name: assets emptyDir: {} containers: - name: nginx image: gihyodocker/nginx-nuxt:latest imagePullPolicy: Always ports: - containerPort: 80 env: - name: WORKER_PROCESSES alue: "2" - name: WORKER_CONNECTIONS value: "1024" - name: LOG_STDOUT value: "true" - name: BACKEND_HOST value: "localhost:3000" volumeMounts: # (2) - mountPath: /var/www/_nuxt name: assets - name: web image: gihyodocker/todoweb:latest imagePullPolicy: Always lifecycle: # (3) postStart: exec: command: - cp - -R - /todoweb/.nuxt/dist - / ports: - containerPort: 3000 env: - name: TODO_API_URL value: http://todoapi volumeMounts: # (4) - mountPath: /dist name: assets- (1) : assets라는 이름으로 볼륨을 생성한다. emptyDir:로 설정해 파드 단위로 할당되는 가상 볼륨을 생성한다.
- (2), (4) : 생성된 볼륨을 컨테이너에 마운트한다. mountPath 속성값으로 경로를 결정한다.
- (3) : web 컨테이너의 애셋 파일을 Nginx 컨테이너에 복사하는 과정(lifecycle 이벤트를 이용하는 것이 좋다.)
@web $ cp -R /todoweb/.nuxt/dist /- /dist로 애셋 파일을 복사한다.
- entrykit의 prehook을 사용해도 되지만 lifecycle을 사용하여 Dockerfile을 수정하지 않고 사용할 수 있다.
- 적용 및 확인
$ kubectl apply -f todo-web.yaml $ kubectl get service todoweb
6. 인그레스로 웹 어플리케이션 노출하기
- GCP의 Cloud Load Balancing을 사용
- ingress.yaml 작성
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress spec: rules: - http: paths: - path: /* backend: serviceName: todoweb servicePort: 80 - 적용 및 확인
$ kubectl apply -f ingress.yaml $ kubectl get ingress- 인그레스 생성후 1분 정도가 지나면 글로벌 IP를 할당받는다.
- 할당된 IP 주소로 접근해보면 GKE에 배포된 TODO 어플리케이션이 브라우저에 표시된다.
7. 온프레미스 환경에서 쿠버네티스 클러스터 구축
- kubespray를 사용해 독단적인 온프레미스 환경에서 클러스터를 스크래치 빌드하는 방법
8. kubespray를 사용한 쿠버네티스 클러스터 구축
클러스터를 구축할 서버 준비하기
- 작업용 서버(ops) * 1
- 쿠버네티스 마스터 서버(master) * 3
- 쿠버네티스 노드 서버(node) * 1
- 위 서버의 운영 체제는 모두 우분투 16.04 TLS
- 모든 서버는 폐쇄된 로컬 네트워크에 구축되며 모두 사설 IP 주소를 갖는다.
이름 사설 IP 역할 master01 10.90.65.11 마스터 master02 10.90.65.12 마스터 master03 10.90.65.13 마스터 node01 10.90.65.21 노드 ops 10.90.65.90 작업용(ops) - 마스터 서버 : 어플리케이션이 배포될 노드 서버나, 서비스 혹은 파드 등의 리소스를 관리하는 사령탑 역할을 한다.
- 노드 서버 : 어플리케이션 파드가 실제로 배포되는 서버이다.
- 작업용 서버 : kubespray를 실행하는 작업용 서버로, 쿠버네티스가 직접 가동되지 않는다.
- 작업용 서버에서 앤서블을 통해 kubespray를 싱행해 마스터 서버와 노드 서버로 쿠버네티스 클러스터를 구성해 볼 것이다.
작업용 서버의 SSH 공개키 등록
(root@ops) $ ssh-keygen -t rsa (root@ops) $ ssh-copy-id root@master01 (root@ops) $ ssh-copy-id root@master02 (root@ops) $ ssh-copy-id root@master03 (root@ops) $ ssh-copy-id root@node01또는
(root@master01) echo "ssh-ras ..." >> ~/.ssh/authorized_keys (root@master02) echo "ssh-ras ..." >> ~/.ssh/authorized_keys (root@master03) echo "ssh-ras ..." >> ~/.ssh/authorized_keys (root@node01) echo "ssh-ras ..." >> ~/.ssh/authorized_keysIPv4 포트 포워딩 활성화
- 일회성 : 즉시 적용
$ sysctl -w net.ipv4.ip_forward=1 - 영구적 : 재부팅 후 적용
$ cat /etc/sysctl.conf # Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1
- 일회성 : 즉시 적용
작업용 서버에 kubespray 설치
(root@ops) $ pip install ansible netaddr (root@ops) $ git clone https://github.com/kubernetes-incubator/kubespray (root@ops) $ cd kubespray && git checkout v2.5.0 (root@ops ~/kubespray) $ pip install -r requirements.txt (root@ops ~/kubespray) $ cp -rfp inventory/sample incentory/mycluster (root@ops ~/kubespray) $ declare -a IPS=(10.90.65.11 10.90.65.12 10.90.65.13 10.90.65.21) (root@ops ~/kubespray) $ CONFIG_FILE=inventory/mycluster/hosts.ini python3 (root@ops ~/kubespray) $ contrib/inventory_builder/inventory.py ${IPS[0]}클러스터 설정
- 클러스터 구성 서버 설정 : inventory/mycluster/host.ini
[all] master01 ansible_host=10.90.65.11 ip=10.90.65.11 master02 ansible_host=10.90.65.12 ip=10.90.65.12 master03 ansible_host=10.90.65.13 ip=10.90.65.13 node01 ansible_host=10.90.65.21 ip=10.90.65.21 [kube-master] master01 master02 master03 [kube-node] node01 [etcd] master01 master02 master03 [k8s-cluster:chidren] kube-node kube-master [calico-rr] [vault] - 쿠버네티스 설정 : inventory/mycluster/group_vars/k8s-cluster.yml
... ## Change this to use another Kubernetes version, e.g. a current beta release kube_version: v1.9.5 # Kubernetes dashboard # RBAC required. see docs/getting-started.md for access details. dashboard_enable: true ...
- 클러스터 구성 서버 설정 : inventory/mycluster/host.ini
클러스터 구축
- 실제 클러스터 구축
(root@ops ~/kubespray) $ ansible-playbook -i inventory/mycluster/hosts.ini cluster.yml - 구축 대상 서버의 사양 등의 요소에 따라 달라지지만, 20~30분 정도면 클러스터 구축이 완료된다.
- master 서버에 kubectl 명령이 실행되는지 확인한다.
(root@master01) $ kubectl get nodes - 직접 구축한 쿠버네티스 클러스터는 매니지드 서비스가 아니기 때문에 마스터 서버의 가용성 확보나 쿠버네티스 버전업 등의 유지보수 작업도 직접 수행해야 한다.
- 운영 환경이라면 이러한 클러스터 유지 보수 체계를 갖추거나 GKE같은 매니지드 서비스를 사용해야 한다.
- 실제 클러스터 구축