NHN Cloud Kubernetes 사용해보기 #1
참고자료
https://kubernetes.io/docs/reference/kubectl/cheatsheet/
Container Registry
- docker image 저장소
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
- 이미지 업로드 확인
ubuntu@k8s-dev:~$ docker pull 702e0b18-kr1-registry.container.cloud.toast.com/myweb:0.1
- 이미지 가져오기
kubernetes 장점
- 애플리케이션 배포 단순화
- 특정 베어메탈을 필요로 하는 경우(예: SSD/HDD)
- 하드웨어 활용도 극대화
- 클러스터의 주변에 자유롭게 이동하여 실행중인 다양한 애플리케이션 구성 요소를 클러스터 노드의 가용 리소스에 최대한 맞춰 서로 섞고 매치
- 노드의 하드웨어 리소스를 최상으로 활용
- 상태 확인 및 자가 치유
- 애플리케이션 구성 요소와 실행되는 노드를 모니터링 하고 노드 장애 발생시 다른 노드로 일정을 자동으로 재조정
- 운영자는 정규 근무 시간에만 장애가 발생한 노드를 처리
- 오토스케일링
- 개별 애플리케이션의 부하를 지속적으로 모니터링할 필요 없이
- 자동으로 리소스를 모니터링하고 각 애플리케이션에서 실행되는 인스턴스 수를 계속 조정하도록 지시 가능
- 애플리케이션 개발 단순화
- 버그 발견 및 수정 (완전히 개발환경과 같은 환경을 제공하기 때문)
- 새로운 버전 출시 시 자동으로 테스트, 이상 발견 시 롤 아웃
- 무 중단 업데이트, 다운그레이드
- 모놀리식 라이프 사이클과 마이크로 서비스 라이프 사이클 비교
NHN Cloud Kubernetes
NHN Cloud에서 kubernetes 클러스터 생성 후, kube api server와 통신하기 위해서는 kubectl 설치 및 config 설정이 필요
( NHN Cloud에서는 관리용 인스턴스 사용을 위해 작업해주어야 함 )
- 쿠버네티스 클러스트의 설정 파일 다운로드
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로 서비스 생성
- 자동으로 NHN Cloud에서 LoadBalancer 생성 확인
* LoadBalancer는 클라우드 환경에서 제공되는 기능이며, 기본 온프레미스에서는 제공되지 않음 ( 온프레미스에서 사용하려면, 별도의 로드밸런서 설정, 구성등이 필요 )
ingress
하나의 IP 주소를 통해 여러 서비스를 제공하는 특별한 메커니즘 ( 하나의 IP로, 도메인, URI 별로 서비스를 연결해줄 수 있음 ) nginx proxy와 비슷
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 생성
* 다만 수많은 생성 템플릿들을 외울 수 없고, 외우는 것은 쿠버네티스 개발자도 권장하지 않음
- https://kubernetes.io/docs/reference/kubectl/cheatsheet/
- 쿠버네티스 문서에서 각 원하는 yaml 파일 템플릿을 검색하여 사용 가능 ( 검색해서 찾는 스킬을 쌓는 것이 중요 )
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
- 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)
- 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로 접속
- 사이트 확인
NHN CLOUD Kubernetes 사용 후기
전반적으로 UI가 깔끔하고 외산 클라우드에 비해 사용하기 편하다라는 느낌을 받았습니다. 물론 GCP나 AWS에서는 지원하는 기능이 NHN Cloud에서는 지원하지 않는 부분도 일부 있었지만, 사소한 부분이고 전체적으로 쓸만하다고 느꼈습니다.
댓글남기기