예제 @ aws)
개발자, 사용자는 다음의 yaml 파일을 만들고 이를 배포해야 한다.
apiVersion: [storage.k8s.io/v1](http://storage.k8s.io/v1)
kind: StorageClass
metadata:
name: fastdisk
provisioner: kubernetes.io/aws-ebs # 온프레미스나 다른 퍼블릭클라우드에서는 사용불가
parameters:
type: gp2
fsType: ext4
zones: ap-northeast-2a
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-fast-sc
spec:
storageClassName: fast
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
nfs 동작 확인 후 작성
pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: "100Mi"
accessModes:
- ReadWriteMany # r,w,여러사람동시접속가능, 혼자만 접속한다면 Once
**storageClassName: testsc**
persistentVolumeReclaimPolicy: Retain # PVC가 제거됐을 때 PV가 작동하는 방법정의(유지는 Retain, 삭제는 Delete)
nfs:
server: 211.183.3.100
path: /shared
root@manager:~/k8slab/pvpvclab# kubectl apply -f pv.yaml
persistentvolume/nfs-pv created
root@manager:~/k8slab/pvpvclab# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv 100Mi RWX Retain Available testsc 21s
pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
spec:
accessModes:
- ReadWriteMany
**storageClassName: testsc**
resources:
requests:
storage: 100Mi
root@manager:~/k8slab/pvpvclab# kubectl apply -f pvc.yaml
root@manager:~/k8slab/pvpvclab# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nfs-pv 100Mi RWX Retain Bound default/pvc testsc 4m25s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc Bound nfs-pv 100Mi RWX testsc 24s
pv.yaml 파일에 라벨 부착해두기
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
**labels:
dev: gildong**
spec:
capacity:
storage: "100Mi"
accessModes:
- ReadWriteMany # r,w,여러사람동시접속가능, 혼자만 접속한다면 Once
persistentVolumeReclaimPolicy: Retain # PVC가 제거됐을 때 PV가 작동하는 방법정의(유지는 Retain, 삭제는 Delete)
nfs:
server: 211.183.3.100
path: /shared
pvc.yaml 파일에는 selector을 이용해 뽑아내기
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
spec:
**selector:
matchLabels:
dev: gildong**
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
배포 후 확인
root@manager:~/k8slab/pvpvclab# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nfs-pv 100Mi RWX Retain Bound default/pvc 39s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc Bound nfs-pv 100Mi RWX 11s
kubernetes는 다양한 오브젝트를 이용하는 것을 사용자(user)가 아닌 시스템레벨에서 동작한다고 하여 useraccount라는 개념보다는 ServiceAccount를 사용한다. 단, 다른 서버에서처럼 사용자를 이용하고 싶다면 별도로 UserAccount를 생성해 사용할 수 있으나 이는 별도의 인증서버를 연결하여야 한다.
우리는 리눅스 root 사용자에게 k8s 전체 시스템에 대한 모든 권한을 갖는 cluster-admin 계정정보를 부여했다. default라는 이름의 secret을 이용하고, 이 secret 내에 토큰을 이용하여 이용할 수 있다.
우리 회사에 서비스를 요청한 사용자가 자신의 ns에 속한 포드들의 정보를 확인하고 싶다면?
kubernetes → linux
default(admin) root(~/.kube/config)에 k8s admin 정보를 포함하고 있다.
각 네임스페이스별로 별도의 SA를 생성하여 고객사에게 전달한다. 이때 고객사에서는 협의 사항에 있지 않은 서비스를 사용할 수 없도록 CREATE와 같은 명령은 제거한다.(GET과 같은 확인 명령만 허용)
SA
ROLE → 특정 네임스페이스에서만 동작
CLUSTERROLE → CLUSTER 전체에서 사용할 수 있는 것 지정
default는 모든 명령을 수행할 수 있으니, 별도의 SA를 제공해 제한을 두어야 한다.
Role (NS에 속함) : Rolebinding 필요
Cluster Role(node,NS,PV) : Cluster Rolebinding 필요
인증서가 필요하다.
vi /etc/kubernetes/manifests/kube-apiserver.yaml
파일에서 - kube-apiserver 밑에
--feature-gates=RemoveSelfLink=false
추가
mkdir shared
chmod 777 shared
vi /etc/exports에 /shared 211.183.3.0/24(rw,no_root_squash,sync)
sa.yaml
kind: ServiceAccount
apiVersion: v1
metadata:
name: nfs-pod-provisioner-sa
---
kind: ClusterRole # Role of kubernetes
apiVersion: rbac.authorization.k8s.io/v1 # auth API
metadata:
name: nfs-provisioner-clusterRole
rules:
- apiGroups: [""] # rules on persistentvolumes
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-provisioner-rolebinding
subjects:
- kind: ServiceAccount
name: nfs-pod-provisioner-sa # defined on top of file
namespace: default
roleRef: # binding cluster role to service account
kind: ClusterRole
name: nfs-provisioner-clusterRole # name defined in clusterRole
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-pod-provisioner-otherRoles
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-pod-provisioner-otherRoles
subjects:
- kind: ServiceAccount
name: nfs-pod-provisioner-sa # same as top of the file
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: nfs-pod-provisioner-otherRoles
apiGroup: rbac.authorization.k8s.io
kubectl apply -f sa.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-pod-provisioner
spec:
selector:
matchLabels:
app: nfs-pod-provisioner
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-pod-provisioner
spec:
serviceAccountName: nfs-pod-provisioner-sa # name of service account created in rbac.yaml
containers:
- name: nfs-pod-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-provisioner-v
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME # do not change
value: nfs-test # SAME AS PROVISONER NAME VALUE IN STORAGECLASS
- name: NFS_SERVER # do not change
value: 211.183.3.100 # Ip of the NFS SERVER
- name: NFS_PATH # do not change
value: /shared # path to nfs directory setup
volumes:
- name: nfs-provisioner-v # same as volumemouts name
nfs:
server: 211.183.3.100
path: /shared
kubectl apply -f provisioner.yaml
root@manager:~/k8slab/pvpvclab# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-pod-provisioner 1/1 1 1 22s
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storageclass # IMPORTANT pvc needs to mention this name
provisioner: nfs-test # name can be anything
parameters:
archiveOnDelete: "false"
kubectl apply -f storageClass.yaml
root@manager:~/k8slab/pvpvclab# kubectl get Storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-storageclass nfs-test Delete Immediate false 5s
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc-test
spec:
storageClassName: nfs-storageclass # SAME NAME AS THE STORAGECLASS
accessModes:
- ReadWriteMany # must be the same as PersistentVolume
resources:
requests:
storage: 50Mi
kubectl apply -f pvc.yaml
root@manager:~/k8slab/pvpvclab# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-3cd03385-e8b4-4b0a-8c1a-e9afc0ce0d23 50Mi RWX Delete Bound default/nfs-pvc-test nfs-storageclass 23s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/nfs-pvc-test Bound pvc-3cd03385-e8b4-4b0a-8c1a-e9afc0ce0d23 50Mi RWX nfs-storageclass 23s
root@manager:~/k8slab/pvpvclab# ls /shared
default-nfs-pvc-test-pvc-3cd03385-e8b4-4b0a-8c1a-e9afc0ce0d23
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: nginx
name: nfs-nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: nfs-test #
persistentVolumeClaim:
claimName: nfs-pvc-test # same name of pvc that was created
containers:
- image: nginx
name: nginx
volumeMounts:
- name: nfs-test # name of volume should match claimName volume
mountPath: /test # mount inside of contianer
kubectl apply -f deployment.yaml
root@manager:~/k8slab/pvpvclab# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-nginx 1/1 1 1 16s
nfs-pod-provisioner 1/1 1 1 10m
root@manager:/shared# ls
default-nfs-pvc-test-pvc-3cd03385-e8b4-4b0a-8c1a-e9afc0ce0d23 index.html
root@manager:/shared# cd default-nfs-pvc-test-pvc-3cd03385-e8b4-4b0a-8c1a-e9afc0ce0d23/
root@manager:/shared/default-nfs-pvc-test-pvc-3cd03385-e8b4-4b0a-8c1a-e9afc0ce0d23# ls
pvpvc_check.txt
default ~ 로 들어가서 touch로 pvpvc_check.txt 만들고 빠져나오기
root@manager:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-nginx-758d59b4c4-s4cpd 1/1 Running 0 3m11s
nfs-pod-provisioner-98bd4898f-4lvwq 1/1 Running 0 13m
root@manager:~# kubectl exec nfs-nginx-758d59b4c4-s4cpd -- ls /test
pvpvc_check.txt
UserAccount는 k8s 내에 있는 오브젝트들을 동작시키는 등은 k8s 시스템에서 제어한다는 의미에서 ServiceAccount를 사용한다. 우리가 알고 있는 일반적인 UserAccount의 개념도 있지만, 주로 SA를 사용하고 만약 UA를 사용하고 싶다면 이는 외부 인증 서버를 연결해야 한다.
자체 인증서
export KUBCONFIG=/etc/kubernetes/admin.conf 또는 cp -i /etc/kubernets/admin.conf ~/.kube/config
위의 과정을 통하면 api-server에 접속할 수 있는 권한을 현재 계정(root)에게 부여하게 된다(kubernetes-admin)
실제 환경이라면 적절한 권한을 부여한 SA를 별도로 생성하여 운영해야 한다. 만약 고객 관리가 필요하다면 고객에게는 해당 프로젝트(네임스페이스) 내에서 get 등을 할 수 있는 SA를 발행해서 제공해줄 수 있어야 한다.
각 네임스페이스별로 기본적으로 default 라는 SA가 있다.
root@manager:~# kubectl create sa user1
serviceaccount/user1 created
root@manager:~# kubectl get sa
NAME SECRETS AGE
default 1 26h
nfs-pod-provisioner-sa 1 149m
user1 1 14s
secret이 하나 있다
root@manager:~# kubectl get secret
NAME TYPE DATA AGE
default-token-sq587 kubernetes.io/service-account-token 3 26h
nfs-pod-provisioner-sa-token-q4qwv kubernetes.io/service-account-token 3 149m
user1-token-lqh4r kubernetes.io/service-account-token 3 53s
User1으로 명령을 실행 해볼것임
root@manager:~# kubectl get svc --as system:serviceaccount:default:user1
Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:user1" cannot list resource "services" in API group "" in the namespace "default"
user1으로 할 때 Forbidden 에러가 뜨는 것을 확인 할 수 있다.
401 error → 인증 에러(authentication) : username/pw
403 error → 인가 에러(authorization) : 인증에 성공한 사용자에게 부여된 권한
root@manager:~/k8slab# kubectl delete sa user1
serviceaccount "user1" deleted
Role
ClusterRole
인증서 없이 접속. 토큰 정보가 포함되지 않은 상태에서 api로 접속했을 경우 결과 내용을 확인할 수 없다. SA가 누구 인가를 알 수 없으므로
root@manager:~# curl https://localhost:6443
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
인증서 있게 접속
root@manager:~# curl https://localhost:6443 -k
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
"reason": "Forbidden",
"details": {
},
"code": 403
}
}root@manager:~# kubectl get secret
NAME TYPE DATA AGE
default-token-sq587 kubernetes.io/service-account-token 3 26h
nfs-pod-provisioner-sa-token-q4qwv kubernetes.io/service-account-token 3 162m
user1-token-lqh4r kubernetes.io/service-account-token 3 13m
kubectl describe secret default-token-sq587
SA → user1 현재 user1은 아무런 role 이 없는 상태이다.
role을 만들어 user1에게 제공할 예정
default - kubernetes-admin
user1 - 매핑된 권한(role)이 없는 상태
api 그룹은 apiversion에서 / 기준으로 구분한다.
v1 → group → “” : core group
apps/v1 → group → apps
만약 서비스에 대하여 명령을 실행할 계획이고, 리스트만 확인하고 싶다면
그룹 → “”
리소스 → service
verb(s) → get
root@manager:~/k8slab/0908# kubectl api-resources | grep -e ^role
rolebindings rbac.authorization.k8s.io/v1 true RoleBinding
roles rbac.authorization.k8s.io/v1 true Role
user1-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: user1role
rules: # 아래에 사용할 수 있는 api그룹, 리소스, 명령 작성
- apiGroups: [""] # "" : coregroup(Pod,service), Deploy/Replicaset은 X
resources: ["services"] # pod도 하려면 , "pods"
verbs: ["get","list"]
kubectl get svc : 서비스 전체 → list
옵션을 사용하여 pod 아닌 특정 pod, 서비스의 정보를 확인하고 싶다면
kubectl get svc testsvc → get
특정 pod 실시간으로 보고싶다면→ watch
user-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: default
name: user1-rolebinding
subjects:
- kind: ServiceAccount
name: user1
namespace: default
roleRef:
kind: Role
name: user1role
apiGroup: rbac.authorization.k8s.io
kubectl apply -f user-rolebinding.yaml
root@manager:~/k8slab/0908# kubectl get pod --as system:serviceaccount:default:user1
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:user1" cannot list resource "pods" in API group "" in the namespace "default": RBAC: role.rbac.authorization.k8s.io "user1role" not found
Quiz.
다음 중 정상 동작 하는 것은? 1번
kubectl get deployment —as list-only : list만 추가
kubectl get deployment —as get-only : get만 추가 → 되게 하려면 list추가해서 해야함
kubectl get deplolyment —as watch-only : watch만 추가 → 되게 하려면 list, get 추가해서 해야함
Quiz.
현재 상태에서 user1이 deployment에 대하여 get 가능하도록 해보자
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: user1role
rules: # 아래에 사용할 수 있는 api그룹, 리소스, 명령 작성
- apiGroups: ["","apps"]
resources: ["services","deployments"]
verbs: ["get","list"]
root@manager:~/k8slab/0908# kubectl get deploy --as system:serviceaccount:default:user1
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-nginx 1/1 1 1 3h34m
nfs-pod-provisioner 1/1 1 1 3h45m
k8s는 각 오브젝트에 대한 접근이 가능하도록 RestAPI가 제공된다. 접근을 위해서는 헤더에 별도의 토큰을 첨부해 접근해야 한다. 접근 경로를 먼저 확인해보고, 생성한 SA의 토큰을 이용하여 웹으로 접근했을 때, svc, pod는 웹상에서 결과를 확인할 수 있는 지 여부를 살펴보자
root@manager:~/k8slab/sa# kubectl proxy
Starting to serve on 127.0.0.1:8001
root@manager:~/k8slab/0908# kubectl get secret
NAME TYPE DATA AGE
default-token-sq587 kubernetes.io/service-account-token 3 28h
nfs-pod-provisioner-sa-token-q4qwv kubernetes.io/service-account-token 3 4h18m
user1-token-lqh4r kubernetes.io/service-account-token 3 109m
root@manager:~/k8slab/0908# export SECNAME=user1-token-lqh4r
전역변수로 선언해서 사용하기
root@manager:~/k8slab/0908# kubectl get secret $SECNAME -o jsonpath='{.data.token}'
토큰 정보 뽑아오기
export DTKN=$(kubectl get secret $SECNAME -o jsonpath='{.data.token}' | base64 -d)
디코딩 되어 있는 값이 필요하기 때문에 해당 명령어를 실행 = 실제 토큰의 값을 전역변수에 넣는다.
셀프 인증서를 사용하기 때문에 -k 옵션을 사용해야 한다.
curl 이용시 -k 옵션을 사용할 경우 공인 인증서 여부 확인 하지 않는다.
root@manager:~/k8slab/0908# curl https://211.183.3.100:6443/apis --header "Authorization: Bearer $DTKN" -k
{
"kind": "APIGroupList",
"apiVersion": "v1",
"groups": [
{
"name": "apiregistration.k8s.io",
"versions": [
{
"groupVersion": "apiregistration.k8s.io/v1",
"version": "v1"
...
다음과 같이 정상적으로 결과를 볼 수 있다.
root@manager:~/k8slab/0908# curl https://localhost:6443/api/v1/namespaces/default/services -k --header "Authorization: Bearer $DTKN"
{
"kind": "ServiceList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/default/services",
"resourceVersion": "95389"
},
"items": [
{
"metadata": {
"name": "kubernetes",
...
= kubectl get svc 정보
curl https://localhost:6443/apis/apps/v1/namespaces/default/deployments -k --header "Authorization: Bearer $DTKN"
= kubectl get deployment 정보
root@manager:~/k8slab/0908# curl https://localhost:6443/api/v1/namespaces/default/services/kubernetes -k --header "Authorization: Bearer $DTKN"
kubectl get svc의 kubernetes 까지 들어갈 수 있음
kubectl create sa testuser
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: testuser
rules:
- apiGroups: [""] # Pod, Service, Deploy/RS(X)
resources: ["services", "pods"]
verbs: ["get", "list", "watch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: testuserrb
# 서비스 어카운트 지정
subjects:
- kind: ServiceAccount
name: testuser
namespace: default
# 연결할 Role 지정
roleRef:
kind: Role
name: testuser
apiGroup: rbac.authorization.k8s.io
kubectl get svc --as system:serviceaccount:default:testuser
kubectl get pod --as system:serviceaccount:default:testuser
kubectl get deploy --as system:serviceaccount:default:testuser # deploy는 Forbidden
export SECNAME=testuser-token-zpfsx
export SECTOKEN=$(kubectl get secret $SECNAME -o jsonpath='{.data.token}' | base64 -d)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: testuser
rules:
- apiGroups: [""] # Pod, Service, Deploy/RS(X)
resources: ["services", "pods", "namespaces"]
verbs: ["get", "list", "watch", "delete"]
curl https://localhost:6443/api/v1/namespaces/default/pods?watch=true --header "Authorization: Bearer $SECTOKEN" -k
kubectl run testpod --image nginx --restart Never
curl -X DELETE https://localhost:6443/api/v1/namespaces/default/pods/testpod1 --header "Authorization: Bearer $SECTOKEN" -k
ps -ef | grep curl
kill -9 'curl 의 PID'
위의 내용이 가능하기 위해서는 testuser 가 watch, delete 가 가능해야 한다