작성한 pvc.yaml 파일을 kubectl apply 명령어를 이용해 영구 볼륨 클레임을 생성
kubectl apply -f pvc.yaml
mysql 용도의 디플로이먼트(파드)를 생성하고 내용을 정의함 - 데이터베이스 용도의 파드이기 때문에 영구 볼륨 클레임을 정의함
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql # mysql이라는 디플로이먼트 생성
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:8.0.29 # mysql 8.0.29 버전의 파드 생성
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: password # mysql에 접속하기 위한 패스워드
ports:
- containerPort: 3306 # mysql에서 사용하는 포트
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/mysql # /var/mysql 디렉터리로 볼륨 마운트
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim: # 영구 볼륨 할당 요청
claimName: mysql-pv-claim
kubectl apply 명령어를 실행해 디플로이먼트를 생성함
kubectl apply -f pvc-deployment.yaml
외부에서 접속하기 위한 서비스를 생성하기 위해 mysql_service.yaml이라는 파일을 작성함
apiVersion : v1
kind : Service
metadata :
name : mysql
spec :
clusterIP : None
ports :
- port : 3306
selector :
app : mysql
작성한 서비스 파일을 kubectl create 명령어를 실행하여 서비스를 생성
kubectl create -f mysql_service.yaml
디플로이먼트, 서비스, 영구 볼륨, 영구 볼륨 클레임이 정상적으로 생성되었는지 확인
디플로이먼트 상태 확인 - 디플로이먼트에 문제가 있다면 error 라는 메시지가 보임 - mysql이라는 이름의 컨테이너에 /var/mysql라는 디렉터리로 볼륨이 할당됐음을 볼 수 있음
kubectl describe deployment mysql
파드의 상태 확인
kubectl get pods
서비스 상태 확인 - 명령어에서 service를 짧게 svc로 입력해도 인식함 - mysql로 생성된 서비스의 포트가 3306인 것을 확인할 수 있음
kubectl get service
영구 볼륨 상태 확인 - mysql-pv-volume이라는 이름으로 볼륨이 생성됨 - 볼륨에 대한 자세한 정보는 kubectl describe pv mysql-pv-volume 명령어로 확인 가능
kubectl get pv
RECLAIM POLICY - 영구 볼륨의 라이프 사이클 - 영구 볼륨 클레임이 삭제될 때 영구 볼륨과 연결되어 있던 저장소에 저장된 파일을 어떻게 처리할지에 따라 나뉨 Retain 정책 : 영구 볼륨 클레임이 삭제되어도 저장소에 저장되어 있던 파일을 삭제하지 않는 정책 Delete 정책 : 영구 볼륨 클레임이 삭제되면 영구 볼륨과 연결된 저장소 자체를 삭제함 Recycle 정책 : 영구 볼륨 클레임이 삭제되면 영구 볼륨과 연결된 저장소 데이터는 삭제
STATUS Available(사용가능) : 아직 클레임에 바인딩되지 않은 상태 Bound(바인딩) : 볼륨이 클레임에 의해 바인딩된 상태 Released(릴리즈) : 클레임이 삭제되었지만 클러스터에서 아직 리소스가 반환되지 않은 상태 Failed(실패) : 볼륨이 자동 반환에 실패한 상태
영구 볼륨 클레임에 대한 상태 확인 - mysql-pv-claim이라는 이름의 영구 볼륨 클레임에서 사용하는 볼륨은 mysql-pv-volume이라는 것을 확인할 수 있음 - 영구 볼륨 클레임에 대한 자세한 정보는 kubectl describe pvc mysql-pv-claim 명령어로 확인 가능
kubectl get pvc
ACCESS MODES - 볼륨에 접근할 수 있는 모드를 나타냄 RWO(ReadWriteOnce) : 하나의 노드에서 해당 볼륨이 읽기-쓰기로 마운트 ROX(ReadOnlyMany) : 볼륨은 많은 노드에서 읽기 전용으로 마운트 RWX(ReadWriteMany) : 볼륨은 많은 노드에서 읽기-쓰기로 마운트 RWOP(ReadWriteOncePod) : 볼륨이 단일 파드에서 읽기-쓰기로 마운트. 즉, 전체 클러스터에서 단 하나의 파드만 영구 볼륨 클레임을 읽거나 쓸 수 있어야 하는 경우 ReadWriteOncePod에 사용
mysql에 접속한 후 데이터베이스와 테이블을 생성함
mysql에 접속 - 반응이 없다면 엔터 누름
kubectl run -it --rm --image=mysql:latest --restart=Never mysql-client -- mysql -h mysql -ppassword
데이터베이스 생성 및 선택
CREATE DATABASE mySQL;
use mySQL;
테이블 생성
CREATE TABLE Test
(
ID INT,
Name VARCHAR(30) DEFAULT 'Anonymous',
ReserveDate DATE,
RoomNum INT
);
생성된 테이블 확인
show tables;
- mysql을 빠져나올땐 quit을 입력함
- 데이터베이스와 테이블이 정상적으로 생성되었으며, 테이블에 값이 저장되어 있지 않지만 정상적으로 호출됨
- 영구 볼륨이 정상적으로 동작하고 있음을 확인함
2. 보안을 고려해 파드를 외부에 노출시키기
01. 파드를 외부에 노출시키기
- 파드의 특징 중 하나는 클러스터 내의 노드들로 옮겨 다님. 파드의 IP는 새로운 IP로 변경되기 때문에 외부에서 접근하기 어려움
- 이를 해결하기 위해 서비스를 사용하여 파드가 클러스터 내 어디에 있든지 고정된 IP로 접근할 수 있게 함
명령어 옵션의 의미 -l run=clusterip-nginx : clusterip-nginx라는 이름을 갖는 파드들의 정보를 보여줌. 이때 clusterip-nginx은 앞의 ClusterIP.yaml라는 파일에서 selector.run에 지정한 값 -o wide : 파드의 상세 정보를 확인할 때 사용, --output=wide도 동일한 의미를 가짐
앞에서 생성한 clusterip-nginx 파드를 외부에 노출시킬 서비스를 생성함 - 일반적으로 서비스를 생성하는 방법은 두 가지임 ① 매니페스트를 이용해 생성하는 방법(yaml 파일 이용) ② kubectl expose 명령어를 사용하는 방법 ← 이 방법 사용
kubectl expose deployment/clusterip-nginx
생성된 서비스의 상태와 정보를 확인 - describe 사용시 서비스의 상세 정보 확인 - 생성된 clusterip-nginx 서비스의 CLUSTERIP가 10.96.68.3임을 확인함
kubectl get svc clusterip-nginx
kubectl describe svc clusterip-nginx
파드의 엔드포인트(endpoint)를 확인 - 엔드포인트를 조회한 결과에 값이 출력되었다면 클러스터 내의 다른 파드가 접속할 수 있도록 서비스가 노출되었다는 의미
busybox 이미지를 이용해 파드를 하나 실행하고 파드 내에서 ClusterIP로 접속 - busybox라는 이름의 파드를 하나 생성 - 밑줄 표시된 부분을 입력해야 함
kubectl run busybox --rm -it --image=busybox /bin/sh
- <title>Welcome to nginx!</title>이 출력되었다면 ClusterIP를 이용해 정상적으로 콘텐츠를 가져온 것
명령어 옵션의 의미
kubectl run busybox : busybox라는 이름으로 파드를 실행함
--rm : 컨테이너를 일회성으로 실행할 때 주로 사용 컨테이너가 종료될 때 컨테이너와 관련된 리소스(파일 시스템, 볼륨)까지 제거
-it : -i 옵션과 -t 옵션은 같이 쓰이는 경우가 많음. 이 두 옵션은 컨테이너를 종료하지 않은 상태에서 터미널의 입력을 계속 컨테이너로 전달할 때 사용. -it 옵션은 컨테이너의 셸(shell)이나 CLI 도구를 사용할 때 유용함
--image=busybox : busybox라는 이미지를 이용해 파드를 생성
/bin/sh : 셸 스크립트를 사용할 때 사용
※ 서비스를 만들 때 ClusterIP를 별도로 지정하지 않으면(None으로 설정하면) ClusterIP가 없는 상태가 만들어짐
헤드리스 서비스(headless service)라고 하며 주로 로드밸런싱이나 서비스 IP가 필요 없을 때 사용
헤드리스 서비스에 셀렉터를 설정하면 API를 통해 접근할 수 있는 엔드포인트가 만들어지지만 셀렉터가 없으면 엔드포인트가 만들어지지 않음
ExternalName
- 조금 특별한 경우에 사용하는 서비스
- 클러스터 내부에서 외부의 엔드포인트에 접속하기 위한 서비스
- 엔드포인트는 클러스터 외부에 위치하는 데이터베이스나 애플리케이션의 API 등을 의미
kind : Service
apiVersion : v1
metadata :
name : external-service
spec :
type : ExternalName
externalName : myservice.test.com # external-service와 연결하려는 외부 도메인 값을 설정
명령어의 의미
외부 FQDN : myservice.test.com - FQDN(Fully Qualified Domain Name)은 도메인에 대한 전체 이름 표기 방식을 의미 - www.a.com 혹은 web01.a.com 같은 형태가 FQDN
CNAME : external-service - 하나의 도메인 이름을 다른 이름으로 매핑시키는 역할을 하는 DNS 레코드의 일종
- external-service라는 이름으로 요청이 들어온다면 클러스터 외부에 존재하는 myservice.test.com이라는 주소를 반환하는 것이 ExternalName 서비스
- ExternalName 서비스는 프록시나 특정 이름(혹은 주소)을 다른 이름으로 자동으로 변환하는 리다이렉트 용도로 사용
노드포트
- 모든 워커 노드에 특정 포트(노드포트)를 열고 여기로 들어오는 모든 요청을 노드포트 서비스로 전달
- 그런 후 노드포트 서비스는 해당 업무를 처리할 수 있는 파드로 요청을 전달함
- 노드포트 타입의 서비스는 외부 사용자(혹은 애플리케이션)가 클러스터 내부에 있는 파드에 접속할 때 사용하는 서비스 중 하나
- 노드포트는 30000~32767로 제한되어 있음
- 어떤 서비스의 어떤 파드에 접속할지는 yaml파일에서 정의하여 사용함
- 매니페스트를 이용해서 노드포트를 생성한다는 것은 노드포트 서비스를 하나 만들고 외부에서 어떤 포트로 접속할지를 지정하는 과정
디플로이먼트의 상태 확인 - yaml 파일에서 파드 3개를 생성하도록 작성했으므로 READY에서 3/3의 결과가 출력됨
파드의 정보 가져옴 - 3개의 파드는 모두 다른 이름을 갖는데, 'nginx-deployment+임의의 숫자/문자 조합' 형태로 파드의 이름이 구성됨 - 현재 3개의 파드가 모두 정상 상태임을 확인
생성한 파드를 외부에 노출시키기 위해 서비스 오브젝트를 생성함 - nginx-svc.yaml이라는 이름으로 생성해 둔 파일이 있다면 kubectl delete -f nginx-svc.yaml, rm nginx-svc.yaml를 차례대로 실행해 기존 파일을 삭제한 후 진행함 - nginx-svc.yaml 파일로 생성한 서비스는 my-nginx라는 이름을 가지며 외부에서 접속할 때 사용할 서비스의 노드포트는 별도로 지정하지 않아서 임의로 부여
apiVersion : v1
kind : Service
metadata :
name : my-nginx
labels :
run : my-nginx
spec :
type : NodePort # 외부에서 접속할 때 노드포트 서비스 사용
ports :
- port : 8080 # 노드포트 서비스에서 사용하는 포트
targetPort : 80 # 파드(컨테이너)에서 사용하는 포트
protocol : TCP
name : http
selector :
app : nginx # nginx라는 이름의 레이블을 갖는 파드를 서비스
my-nginx의 서비스 포트를 확인해야 함 - 노드포트는 서비스를 통해 외부에 노출되기 때문에 서비스의 포트를 확인해야 함
kubectl get svc | grep my-nginx
노드포트를 이용해 서비스를 외부에 노출했기 때문에 노드(호스트)의 IP에 뒤에 31899포트를 붙여서 접속할 수 있음
노드포트의 몇 가지 제약사항
포트당 하나의 서비스만 사용할 수 있음
30000에서 32767까지의 포트만 사용할 수 있음. 해당 범위를 넘는 서비스를 운영 중이라면 사용이 불가능함
노드나 가상 머신의 IP 주소가 바뀌면 반드시 반영해야 함
로드밸런서
- 서버에 가해지는 부하(=로드)를 분산(=밸런싱)해주는 장치 또는 기술을 통칭
- 사용자가 한 대의 서버(혹은 파드)에 집중해 접근한다면 그 서버(파드)는 부하를 견디지 못하고 비정상적으로 종료될 수 있음
- 이러한 현상이 발생하지 않도록 부하를 고루 분산하기 위해 사용하는 것이 로드밸런서
L4 로드밸런서 - OSI 7계층에서 네트워크 계층(IP, IPX)이나 트랜스포트 계층(TCP, UDP)의 정보를 바탕으로 로드를 분산함 - IP 주소나 포트 번호 등을 이용해 트래픽을 분산하는 것
L7 로드밸런서 - OSI 7계층에서 애플리케이션 계층(HTTP, FTP, SMTP)을 기반으로 로드를 분산하기 때문에 URL, HTTP 헤더, 쿠키 등과 같은 사용자의 요청을 기준으로 트래픽을 분산 - L7 로드밸런서의 기능은 인그레스를 사용해 구현가능 - 인그레스는 외부에서 파드에 접속할 때 부하 분산 용도로 사용
로드밸런서 실습 - 보통 L4 스위치를 사용하는 로드밸런서는 외부에서 접근할 수 있는 IP를 이용해 로드밸런서를 구성함
- 외부 IP(공인 IP)를 로드밸런서로 설정하면 클러스터 외부에서 접근할 수 있음
httpd 이미지를 이용해 디플로이먼트를 생성함 - 지금까지 yaml 파일을 통해 디플로이먼트를 생성했지만 이미지를 이용해 간단하게 디플로이먼트를 생성할 수도 있음
kubectl create deployment httpd --image=httpd
외부 IP를 이용해 httpd를 외부에 노출하기 위한 서비스를 생성 - 외부 IP를 이용하는 방법은 externalIPs에 외부 IP를 입력해주면 됨 - httpd 서비스를 외부에 노출하기 위해 externalIPs에 워커 노드의 IP를 지정함
포트를 확인하기 위해 서비스 오브젝트에 대한 정보를 얻음 - 외부에서 접속할 때에는 32312 포트 사용
kubectl get services exservice
파드 정보 확인 - worker1에 5개의 파드가 생성된 것을 확인할 수 있음 - 워커 노드가 2개 이상이라면 파드가 분배되어 생성됨
kubectl get pods --output=wide
curl을 명령어를 이용해서 로드밸런서 결과를 확인 - 워커 노드의 IP를 사용해 접속함 - 주소로 접속할 때 만들어진 5개의 파드 중 임의의 파드가 응답하게 됨 - 5개의 파드 중 응답하는 파드가 임의로 선택되고 동일한 내용을 출력하도록 하는 것 - 로드밸런서 서비스는 별도의 스위치 장비가 필요해서 비용이 많이 들어서 대안으로 사용할 수 있는 것
인그레스
- 클러스터 외부에서 내부로 접근하는 요청(HTTP, HTTPS 요청)을 어떻게 처리할지 정의해 둔 규칙들의 모음
- 일반적인 로드밸런서와는 다르게 IP가 아니라 URL로 경로를 찾아줌
- 어떤 URL 경로로 요청이 왔을 때 어떤 서비스로 연결하라는 '규칙'을 정의한 것이 전부
- 실제 인그레스 규칙에 맞게 경로를 찾는 행위를 하는 것은 인그레스 컨트롤러(Ingress Controller)
nginx 인그레스 컨트롤러(Nginx Ingress Controller)를 설치함 - 깃허브에 설치하기 위한 파일 제공
ValidatingWebhookConfiguration을 활성 상태로 두면 인그레스 테스트 과정에서 오류가 발생하니 삭제함
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
인그레스를 테스트하기 위한 서비스와 파드를 생성하여 내용을 작성함 - cafe.yaml이라는 파일을 생성함 - 들어오는 주소(URL)에 따라 서비스를 연결하도록 정의 - tea-svc 서비스에는 tea 파드를 연결하고, coffee-svc 서비스에는 coffee 파드를 연결하도록 작성
apiVersion : apps/v1
kind : Deployment
metadata :
name : coffee # coffee라는 디플로이먼트 생성
spec :
replicas : 2
selector :
matchLabels :
app : coffee
template :
metadata:
labels:
app: coffee
spec :
containers :
- name : coffee
image : nginxdemos/nginx-hello:plain-text
ports :
- containerPort : 8080
---
apiVersion : v1
kind : Service
metadata :
name : coffee-svc # coffee-svc라는 서비스 생성
spec :
ports :
- port : 80
targetPort : 8080
protocol : TCP
name : http
selector :
app : coffee
---
apiVersion : apps/v1
kind : Deployment
metadata :
name : tea # tea라는 디플로이먼트 생성
spec :
replicas : 3
selector :
matchLabels :
app : tea
template :
metadata:
labels:
app: tea
spec :
containers :
- name : tea
image : nginxdemos/nginx-hello:plain-text
ports :
- containerPort : 8080
---
apiVersion : v1
kind : Service
metadata :
name : tea-svc # tea-svc라는 서비스 생성
spec :
ports :
- port : 80
targetPort : 8080
protocol : TCP
name : http
selector :
app : tea
kubectl apply 명령어를 이용해 디플로이먼트와 서비스를 생성함
kubectl apply -f cafe.yaml
생성된 디플로이먼트, 서비스, 파드의 상태를 확인함 - 파드는 Running 상태여야 함 - 디플로이먼트, 서비스, 파드가 생성된 것을 확인할 수 있음
kubectl get deploy,svc,pods
요청 경로에 따라 서비스를 연결하기 위한 cafe-ingress.yaml 파일을 생성함 - 엔드포인트가 /tea인 요청은 tea-svc 서비스에 연결하고 /coffee인 요청은 coffee-svc 서비스에 연결함
쿠버네티스 보안 권고 조치 - 취약점이 있거나 구성이 잘못된 컨테이너 및 파드를 점검할 수 있어야 함 - 최소한의 권한으로 컨테이너와 파드를 실행해야 함 - 네트워크를 분리해 다른 시스템에 영향을 주지 않도록 해야함 - 방화벽을 사용해 불필요한 네트워크 연결을 차단해야 함 - 강력한 인증 및 권한 부여를 사용해 사용자 및 관리자 접근을 제한해야 함 - 관리자가 모든 활동을 모니터링하면서 잠재적/악의적 활동에 대응할 수 있도록 주기적으로 로그 감사를 해야 함 - 정기적으로 모든 쿠버네티스 설정을 검토하고 취약점 스캔 및 보안 패치가 적용되어 있는지 확인해야 함
- 로그인 창이 뜨면 왼쪽 아래 Skip and go to the app을 클릭해 포스트맨 화면으로 넘어감
RBAC
- RBAC(Role-Based Access Control)은 역할을 기반으로 쿠버네티스에 접속할 수 있는 권한을 관리함
- 사용자(user)와 역할(role) 두 가지를 조합해 사용자에게 권한을 부여할 수 있음
ex) 특정 사용자에 대해 쿠버네티스 클러스터에는 접속할 수 없고 특정 파드에만 접속할 수 있게 권한을 설정할 수 있음
- 쿠버네티스에서 RBAC 기반의 권한을 관리하는 것이 서비스 어카운트
- 역할(role)에는 단순 역할과 클러스터 역할 두 가지 유형이 있음
단순 역할 : 그 역할이 속한 네임스페이스에만 적용됨
클러스터 역할 : 클러스터 전체에 적용
kind : Role
apiVersion : rbac.authorization.k8s.io/v1
metadata :
namespace : rbac
name : role
rules : # 권한에 대한 규칙을 지정
- apiGroups : [""] # 역할이 적용될 그룹
resources : ["pods"] # 어떤 자원에 접근할지 명시
verbs : ["get"] # 어떤 동작이 가능한지 명시
서비스 어카운트 개념 및 생성 방법
- 서비스 어카운트(service account)는 클라이언트가 쿠버네티스 API를 호출할 때 사용함
- 사람에게 부여하는 것이 아니라 쿠버네티스 API에 접근하는 시스템에 부여하는 권한
- 서비스 어카운트는 쿠버네티스에 존재하는 오브젝트이며, 네임스페이스마다 정의할 수 있지만, 각 네임스페이스에는 기본적으로 'default 서비스 어카운트'가 자동으로 생성됨
- 서비스 어카운트를 이해하려면 역할과 역할 바인딩이라는 개념을 먼저 이해해야 함
구분
역할
역할 바인딩
설명
어떤 리소스에 어떤 액션을 수행할 수 있는지를 정의함
주체(사용자 혹은 그룹)에 역할을 연결하는 것을 의미함
네임스페이스 생성 - account라는 이름의 네임스페이스를 생성함
kubectl create namespace account
account 네임스페이스에 api-service-account라는 서비스 계정을 만듦 - 생성한 서비스 계정으로 클러스터에 접속하려면 클러스터 역할(ClusterRole)을 정의해야 함 - 클러스터 역할 : 서비스 계정이 클러스터의 어떤 리소스(노드, 파드, 볼륨 등)에 접속할 수 있을지를 정의하는 것
kubectl get serviceaccount api-service-account -o=jsonpath='{.secrets[0].name}' -n account
명령어의 설명
kubectl get serviceaccount api-service-account :api-service-account라는 이름의 서비스 어카운트를 조회
-o=jsonpath='{.secrets[0].name}' : jsonpath는 필요한 정보만 추출할 때 사용하고 '{.secrets[0].name}'은 시크릿 이름만을 출력하겠다는 의미
-n account : account라는 네임스페이스를 사용함
시크릿 이름을 base64로 디코딩된 토큰을 가져옴 - 이 토큰은 API를 호출할 때 토큰으로 사용됨
kubectl get secrets api-service-account-token -o=jsonpath='{.data.token}' -n account | base64 --decode
명령어의 설명
kubectl get secrets api-service-account-token : api-service-account-token이라는 시크릿 이름에서 사용하고 있는 시크릿을 조회함
o=jsonpath='{.data.token}' : 토큰 정보를 출력함
-n account : account라는 네임스페이스를 사용함
base64 --decode : 토큰을 디코딩된 문자열로 표현하되, 64자리 집합을 사용하는 6비트 숫자로 표현
외부에서 접근하기 위해 클러스터 엔드포인트 정보를 가져와야 함 - 클러스터 엔드포인트(IP, DNS) 정보를 가져옴 - 포트 정보만 사용할 예정
kubectl get endpoints | grep kubernetes
포스트맨으로 접속함 - API 호출을 테스트할 URL 정보와 토큰 정보를 이용함
+ 버튼을 클릭해 새로운 요청(request)창을 생성
톱니바퀴 모양 버튼을 누르고 settings를 누름
'SSL certificate verification' 항목을 OFF로 바꿔주고 창을 닫음
주소창에 https://[엔드포인트 주소]/api/v1/namespaces를 입력함 - 앞에서 얻은 엔드포인트 주소는 클러스터 내부에서 사용하는 네트워크 주소 - 외부에서 이 주소로 접속하면 연결되지 않으므로 앞에서 얻은 엔드포인트 주소 대신에 '마스터 노드의 IP 주소'를 입력함 - 그다음 Authorization 메뉴를 클릭하고 'Type'에서 Bearer Token을 선택함 - 이후 'Token' 부분에 앞에서 얻었던 base64로 디코딩된 토큰(혹은 직접 생성한 토큰)을 입력함 - 1.24이상의 버전을 사용한다면 직접 생성한 토큰을 입력함
Send 버튼을 클릭하면 다음과 같은 정보가 나옴 - 출력 결과는 네임스페이스, UID, 리소스 버전 및 기타 많은 정보를 보여 주고 있음 - API 호출에 대한 검증이 성공임을 의미함