338 | Conditions:
339 | Type Status Reason
340 | ---- ------ ------
341 | Available True MinimumReplicasAvailable
342 | Progressing True ReplicaSetUpdated
343 | OldReplicaSets: kubia-6b646f577b (3/3 replicas created)
344 | NewReplicaSet: kubia-bb464699b (1/1 replicas created)
345 | ```
346 |
347 | **롤아웃 취소**
348 |
349 | ```
350 | [root@k8s-master ~]# kubectl rollout undo deployment kubia
351 | deployment.extensions/kubia rolled back
352 | ```
353 |
354 | **취소후 포드 상태: 잘못된 포드 삭제**
355 |
356 | ```
357 | [root@k8s-master ~]# kubectl get po
358 | NAME READY STATUS RESTARTS AGE
359 | kubia-6b646f577b-n6nzx 1/1 Running 0 41m
360 | kubia-6b646f577b-nq7nk 1/1 Running 0 41m
361 | kubia-6b646f577b-zlbvf 1/1 Running 0 41m
362 | kubia-bb464699b-gjw7p 0/1 Terminating 0 6m16s
363 | ```
364 |
--------------------------------------------------------------------------------
/k8s-in-action-chap10.md:
--------------------------------------------------------------------------------
1 |
2 | # Chapter 10. StatefulSets: deploying replicated stateful applications
3 |
4 | ## 10.1. Replicating stateful pods
5 |
6 | 각각 다른 상태를 가진 분산형 애플리케이션을 배포해야한다고 가정해보자. ReplicaSet을 이용해서 할 수 있을까? 없다.
7 |
8 | 각각 다른 상태를 가진 분산형 애플리케이션은 죽다가 다시 살아나도 변하지 않는 안정적인 ID(서비스 IP 또는 도메인)을 가져야하기 때문이다.
9 |
10 | 그렇다고 Pod 하나당 ReplicaSet과 Service를 각각 만든다면 어떻게 될까? 개별 포드들은 자신이 어떤 서비스(어떤 주소)로 노출되어있는 지 알 수 없으며, Peer를 찾기도 어렵다.
11 |
12 | 이런 상태가 있는 분산형 애플리케이션을 배포하기 위해서 Kubernetes에서는 StatefulSet이라는 것이 있다.
13 |
14 | ## 10.2. Understanding StatefulSets
15 |
16 | ### 10.2.1. Comparing StatefulSets with ReplicaSets
17 |
18 | #### Stateless, Cattle, ReplicaSet
19 | 상태가 없기 때문에 인스턴스가 소멸되더라도 새 인스턴스를 만들면 문제되지 않는다.
20 |
21 | ReplicaSet으로 관리되는 Pod들은 비유적으로 가축(cattle)과 같다. 언제든 새로운 Pod으로 교체 가능하다.
22 |
23 | #### Stateful, Pet, StatefulSet
24 | 상태(예를 들어 추억)가 있기 때문에 해당 인스턴스가 소멸되면 정확히 같은 것이 아니라면 대체할 수 없다.
25 |
26 | StatefulSet은 같은 애완동울을 부활시키듯, 관리되는 Pod들을 기존 인스턴스와 동일한 이름, 네트워크ID 등 공유정보를 가져와서 상태를 일치시킨다.
27 |
28 |
29 | ### 10.2.2. Providing a stable network identity
30 | StatefulSet에 의해 생성된 Pod들은 규칙적인 이름을 갖는다. ReplicaSet이 만든 Pod의 이름은 뒤에 해시값이 붙지만, StatefulSet이 만든 Pod은 0부터 순서대로 숫자가 붙는다.
31 |
32 |
33 |
34 | #### Introducing the governing Service - Headless Service
35 | 일반적으로 상태가 없는 분산형 서비스라면 그냥 앞에서 로드발란싱을 잘 해주면 되겠지만, 각각 상태가 있는 분산형 서비스는 각 인스턴스들이 개별로 동작하기 때문에 일반적으로 로드발란싱하지 않고 각각 주소를 필요로 한다. StatefulSet에서는 이러한 요구사항에 대응하기 위해서 관리용 Headless Service를 만든다.
36 |
37 | > #### Headless Service
38 | > ClusterIp를 가지지 않고 주소(DNS 이름)만 가지고 있는 서비스로 이 DNS를 조회(lookup)하면 서비스에 연결된 Pod들의 IP 주소를 반환한다.
39 |
40 | 예를 들어, 기본 네임스페이스(default)에 속하고, 서비스 이름이 foo이며, Pod들 중 첫번째 팟의 이름이 A-0일 때, 정규화된 도메인이름인 a-0.foo.default.svc.cluster.local 을 통해서 해당 Pod에 접근 가능하다. 또한 foo.default.svc.cluster.local 도메인의 SRV(Service Locator Record)를 검색하여 StatefulSet의 모든 Pod의 이름을 찾고 이를 통해서 Peer를 찾을 수 있다.
41 |
42 | #### Replacing lost pets
43 | StatefulSet에서 인스턴스가 사라지면(노드가 네트워크 연결이 끊기거나 누군가 Pod을 수동으로 삭제하면) StatefulSet은 새로운 Pod을 만들고 사라진 Pod과 동일한 이름과 호스트 이름을 갖도록 한다.
44 |
45 | #### Scaling a StatefulSet
46 | StatefulSet에서 Pod을 scale-out하면 사용되지 않은 다음 인덱스 번호를 사용해서 새로운 Pod을 생성한다. 그리고 scale-in하면 마지막 인덱스 부터 ***하나씩*** 제거(drain)된다.
47 |
48 | 예를 들어, A-0, A-1 이렇게 두개의 Pod이 있었다면 scale-out 이후에는 A-2 Pod이 생성된다. 그리고 빠질 때는 A-2 부터 빠진다.
49 |
50 | ### 10.2.3. Providing stable dedicated storage to each stateful instance
51 | (각 스테이트풀 인스턴스에 안정적인 전용 스토리지 제공)
52 |
53 | #### 복습 - PersistentVolume & PersistentVolumeClaim
54 |
55 |
56 | 각각 상태가 있기 때문에 각 Pod들의 스토리지는 다른 Pod들과 분리되어야 한다. 그리고 6장에서 PersistentVolume과 PersistentVolumeClaim을 통해서 Pod에 영구 스토리지를 매핑하는 것을 배웠다.
57 |
58 | 결론적으로 StatefulSet은 VolumeClaimTemplate를 통해서 자신만의 PersistentVolumeClaim을 갖는다. 그리고 각각 별개의 PersistentVolume에 스토리지를 요청한다.
59 |
60 | StatefulSet에서 스케일다운 같은 상황을 통해 Pod이 종료되더라도 PersistentVolumeClaim은 삭제되지 않는다. 만약 다시 스케일업 할때 새로 만들어진 인스턴스가 기존 PersistentVolumeClaim에 연결된다. 만약 지우고 싶다면 수동으로 지워야한다.
61 |
62 |
63 |
64 | ### 10.2.4. Understanding StatefulSet guarantees
65 |
66 | StatefulSet은 안정적인 ID와 스토리지를 보장하며, 절대 동일한 ID의 인스턴스가 1개임을 보장해야 한다.
67 |
68 | ## 10.3. Using a StatefulSet (With GKE)
69 |
70 | ### 10.3.1. 앱 및 컨테이너 이미지 만들기
71 |
72 | POST로 받은 요청을 팔일로 저장하는 NodeJS 앱을 만든다. 저장된 파일이 상태를 의미한다. 편하게 저자가 만든 이미지를 그냥 사용한다.
73 |
74 | ```
75 | docker pull luksa/kubia-pet
76 | ```
77 |
78 | ### 10.3.2. 스테이트풀 셋을 통한 애플리케이션 배포
79 | 애플리케이션 배포를 위해 다음의 객체들을 만들어 두어야한다.
80 | - 데이터 파일을 저장하는 PersistentVolume
81 | - StatefulSet에 대한 관리서비스 (Headless Service)
82 | - StatefulSet
83 |
84 | #### PersistentVolume 생성
85 | ```
86 | gcloud compute disks create --size=1GiB --zone=asia-northeast1-a pv-a
87 | gcloud compute disks create --size=1GiB --zone=asia-northeast1-a pv-b
88 | gcloud compute disks create --size=1GiB --zone=asia-northeast1-a pv-c
89 | ```
90 |
91 | 코드 : https://github.com/luksa/kubernetes-in-action/blob/master/Chapter10/persistent-volumes-gcepd.yaml
92 |
93 |
94 | #### 관리 서비스 생성
95 | 코드 : https://github.com/luksa/kubernetes-in-action/blob/master/Chapter10/kubia-service-headless.yaml
96 |
97 | #### StatefulSet 매니페스트 작성
98 | 코드 : https://github.com/luksa/kubernetes-in-action/blob/master/Chapter10/kubia-statefulset.yaml
99 |
100 | ```yaml
101 | ...
102 | volumeClaimTemplates:
103 | - metadata:
104 | name: data
105 | spec:
106 | resources:
107 | requests:
108 | storage: 1Mi
109 | accessModes:
110 | - ReadWriteOnce
111 | ```
112 |
113 | ```
114 | kubectl create -f kubia-statefulset.yaml
115 | ```
116 |
117 | :warning: 중요포인트! Pod이 동시에 생성되는 것이 아니고 하나씩 생성된다.
118 |
119 | ### 10.3.3. 실제 포드로 동작해보기
120 |
121 | API 서버를 통해서 포드와 통신한다.
122 |
123 |
124 |
125 | ```sh
126 | # kube proxy 실행
127 | kuberctl proxy
128 | # GET test
129 | curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
130 | # POST test
131 | curl -X POST -d "Hey there! This greeting was submitted to kubia-0." \
132 | localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
133 | ```
134 |
135 | #### 강제로 팟을 삭제해보고 같은 스토리지에 붙는지 테스트해본다.
136 | ```sh
137 | kubectl delete po kubia-0
138 | ```
139 |
140 |
141 |
142 | ### 10.4. DNS를 이용한 Peer 검색
143 |
144 | SRV 레코드는 특정 서비스를 제공하는 서버의 호스트 이름 및 포트를 가리키는데 사용된다. 쿠버네티스는 헤드리스 서비스를 지원하는 포드의 호스트 이름을 가리키도록 SRV 레코드를 만든다.
145 |
146 | 애플리케이션(팟)은 DNS에 SRV lookup을 통해서 Peer의 주소를 가져올 수 있다.
147 |
148 |
149 |
150 | ### 10.5. 스테이트풀셋 장애 제거
151 | 노드가 네트워크 장애가 생겼을 때, Pod은 제거되지 않고 Unknown 상태가 된다. kubelet이 Pod의 컨테이너가 종료되었음을 API 서버에 알릴 수 없기 때문이다. 그래서 만약 실제로 이런 상황이 생겨서 새로운 팟을 띄워야한다면 장애가 난 팟을 강제로 지워야 한다.
152 |
153 | ```
154 | kubectl delete po kubia-0 --force --grace-period 0
155 | ```
156 |
157 | ## 참고 링크
158 | - 소스코드 : https://github.com/luksa/kubernetes-in-action/tree/master/Chapter10
159 | - StatefulSet으로 MySql 배포하기 : https://kubernetes.io/docs/tasks/run-application/run-replicated-stateful-application/#deploy-mysql
160 |
--------------------------------------------------------------------------------
/k8s-in-action-chap10.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap10.pdf
--------------------------------------------------------------------------------
/k8s-in-action-chap11/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | # Chapter 11
7 | # 쿠버네티스 내부
8 |
9 | -----------------------------------------
10 |
11 |
12 |
13 | ## 아키텍처 이해
14 |
15 | * Master Components (Control Plane)
16 | * etcd Distributed Storage
17 | * API Server
18 | * scheduler
19 | * control manager
20 | * Node Components
21 | * Kubelet
22 | * Kubernetes Service Proxy (kube-proxy)
23 | * container runtime
24 |
25 |
26 |
27 | -----------------------------------------
28 |
29 |
30 |
31 | ## 아키텍처 이해
32 |
33 | * Add-On Components
34 | * Kubernetes DNS Server
35 | * Dashboard
36 | * Ingress Controller
37 | * Heapster
38 | * Container Network Interface Plugin (CNI)
39 |
40 |
41 |
42 | -----------------------------------------
43 |
44 |
45 |
46 | ## 컴포넌트간 상호 종속성
47 |
48 |

49 |
50 |
51 |
52 | --------------------------------------
53 |
54 |
55 |
56 | ## 컴포넌트의 상태 확인
57 |
58 |
59 | ~~~
60 | kubectl get componentstatuses
61 | NAME STATUS MESSAGE ERROR
62 | scheduler Healthy ok
63 | controller-manager Healthy ok
64 | etcd-0 Healthy {"health": "true"}
65 | ~~~
66 |
67 |
68 |
69 | ## 컴포넌트와 커뮤니케이션 하는 방법
70 |
71 | * API Server와만 통신
72 | * 거의 항상 컴포넌트가 API Server로 요청
73 | * kubectl attach, kubectl port-forward 등의 일부 경우에만 API Server가 kubelet에 먼저 연결을 시도
74 |
75 |
76 |
77 | -----------------------------------------
78 |
79 |
80 |
81 | ## 컴포넌트의 고가용성
82 |
83 | * Master Components
84 | * etcd는 Clustering
85 | * API Server는 Multi-Active
86 | * Control Manager, Scheduler는 Active-Standby
87 | * Node Components
88 | * 전체 컴포넌트가 각각의 Worker Node에서 실행
89 |
90 |
91 |
92 | -----------------------------------------
93 |
94 |
95 |
96 | ## 쿠버네티스와 etcd
97 |
98 | * 모든 오브젝트(Pod, RC, Service 등..)는 API Server가 재시작되거나 실패하더라도 유지되거나 복원되기 위해 영구적으로 저장되어야 함.
99 | * Distributed Key-Value Storage인 etcd에 저장.
100 | * API Server외의 컴포넌트는 API Server를 통해 간접적으로 etcd에 접근
101 |
102 |
103 |
104 | ## 쿠버네티스와 API Server를 통해 etcd에 읽고 쓰는 이유
105 |
106 | * 저장소 추상화
107 | * 유효성 검사
108 | * 낙관적(Optimistic) Tx (metadata.resourceVersion) (etcd2를 사용하는 경우)
109 |
110 |
111 |
112 | -----------------------------------------
113 |
114 |
115 |
116 | ## etcd에 리소스를 저장하는 방법
117 |
118 | ~~~
119 | $ export ETCDCTL_API=3
120 | $ etcdctl --endpoints https://127.0.0.1:2379 \
121 | --cacert /etc/kubernetes/pki/etcd/ca.crt \
122 | --cert /etc/kubernetes/pki/etcd/server.crt \
123 | --key /etc/kubernetes/pki/etcd/server.key \
124 | get / --prefix=true --keys-only
125 |
126 | /registry/configmaps
127 | /registry/daemonsets
128 | /registry/deployments
129 | /registry/event
130 | /registry/namespaces
131 | /registry/pods
132 | ...
133 | ~~~
134 |
135 | * /registry/{objectType}/{namespace}/{objectName}
136 |
137 |
138 |
139 | -----------------------------------------
140 |
141 |
142 |
143 | ## etcd 클러스터
144 |
145 | * Quorum 유지를 통한 분산된 etcd 클러스터의 일관성
146 | * 여러 사람의 합의로 운영되는 의사기관에서 의결을 하는데 필요한 최소한의 참석자 수
147 | * RAFT Consensus Algorithm (http://swalloow.github.io/raft-consensus)
148 | * split-brain 방지를 위해 홀수 권장
149 |
150 |

151 |
152 |
153 |
154 | -----------------------------------------
155 |
156 |
157 |
158 | ## API 서버가 하는 일
159 |
160 | * 클러스터 상태를 조회 및 수정할 수 있는 인터페이스 제공.
161 | * 변경된 상태를 etcd에 저장.
162 | * object의 유효성 검사.
163 | * Optimistic Locking 처리 (etcd2를 사용하는 경우. v1.13부터 미지원).
164 | * Kubernetes v1.5.1 이후 etcd3를 사용하면(v1.6부터 default) etcd의 tx를 사용.
165 | * Kubernetes repository - staging/src/k8s.io/apiserver/pkg/storage/etcd/api_object_versioner.go, staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go
166 |
167 |
168 |
169 | -----------------------------------------
170 |
171 |
172 |
173 | ## API 서버의 동작
174 |
175 | * 인증 플러그인 - 클라이언트 인증
176 | * 권한 승인 플러그인 - 클라이언트 권한 승인
177 | * 승인 제어 플러그인 - 요청 받은 리소스를 조회 및 수정
178 | * resource validation - 리소스의 검증 및 저장
179 |
180 |

181 |
182 |
183 |
184 | -----------------------------------------
185 |
186 |
187 |
188 | ## API 서버가 리소스 변경을 통지하는 방법
189 |
190 | * API 서버는 etcd의 watch api를 이용하여 subscribe
191 | * API 서버에 리소스 변경 요청이 오면 해당 요청을 etcd에 저장.
192 | * etcd에서 변경이 일어난 key를 publish
193 | * notification을 받은 API 서버는 watch api를 요청한 클라이언트에 notify
194 | * http1.0 : 응답의 일부만 전달해주어 connection을 유지함.
195 | * http1.1 : Chunked Streaming 방식으로 구현
196 | * Why not implement it as http2?
197 |
198 |
199 |

200 |
201 |
202 |
203 | -----------------------------------------
204 |
205 |
206 |
207 | ## API 서버가 리소스 변경을 통지하는 방법
208 |
209 | ```
210 | $ vi /etc/kubernetes/manifests/kube-apiserver.yaml
211 | $ systemctl restart kubelet
212 |
213 | apiVersion: v1
214 | kind: Pod
215 | metadata:
216 | ...
217 | spec:
218 | containers:
219 | - command:
220 | ...
221 | - --insecure-port=8080
222 | ```
223 |
224 | ```
225 | $ curl --http1.0 http://localhost:8080/api/v1/pods?watch=true
226 |
227 | $ curl http://localhost:8080/api/v1/pods?watch=true
228 | ```
229 |
230 |
231 |
232 |
233 | -----------------------------------------
234 |
235 |
236 |
237 | ## API 서버가 리소스 변경을 통지하는 방법
238 |
239 | * http1.1
240 | ```
241 | $ tcpdump -nlA -i lo port 8080
242 | 05:33:24.628863 IP 127.0.0.1.44242 > 127.0.0.1.8080: Flags [P.], seq 1:101, ack 1, win 342, options [nop,nop,TS val 925974024 ecr 925974024], length 100: HTTP: GET /api/v1/pods?watch=true HTTP/1.1
243 | E....w@.@.o..............Q..jn.....V.......
244 | 71>.71>.GET /api/v1/pods?watch=true HTTP/1.1
245 | Host: localhost:8080
246 | User-Agent: curl/7.58.0
247 | Accept: */*
248 | 05:33:24.629526 IP 127.0.0.1.8080 > 127.0.0.1.44242: Flags [P.], seq 1:117, ack 101, win 342, options [nop,nop,TS val 925974025 ecr 925974024], length 116: HTTP: HTTP/1.1 200 OK
249 | E...;_@.@...............jn...Q.=...V.......
250 | 71> 71>.HTTP/1.1 200 OK
251 | Content-Type: application/json
252 | Date: Fri, 22 Mar 2019 05:33:24 GMT
253 | Transfer-Encoding: chunked
254 | 9cf
255 | {"type":"ADDED","object":{ ... }}
256 | 19a2
257 | {"type":"ADDED","object":{ ... }}
258 | aab
259 | {"type":"MODIFIED","object":{ ... }}
260 | ....
261 | ```
262 |
263 |
264 |
265 | -----------------------------------------
266 |
267 |
268 |
269 | ## API 서버가 리소스 변경을 통지하는 방법
270 |
271 | * http1.0
272 | ```
273 | $ tcpdump -nlA -i lo port 8080
274 |
275 | 05:42:32.087199 IP 127.0.0.1.47318 > 127.0.0.1.8080: Flags [P.], seq 1:101, ack 1, win 342, options [nop,nop,TS val 926521512 ecr 926521512], length 100: HTTP: GET /api/v1/pods?watch=true HTTP/1.0
276 | E...9<@.@.."............rC.u...,...V.......
277 | 79..79..GET /api/v1/pods?watch=true HTTP/1.0
278 | Host: localhost:8080
279 | User-Agent: curl/7.58.0
280 | Accept: */*
281 |
282 | 05:42:32.087785 IP 127.0.0.1.8080 > 127.0.0.1.47318: Flags [P.], seq 1:89, ack 101, win 342, options [nop,nop,TS val 926521513 ecr 926521512], length 88: HTTP: HTTP/1.0 200 OK
283 | E...`c@.@..................,rC.....V.......
284 | 79..79..HTTP/1.0 200 OK
285 | Content-Type: application/json
286 | Date: Fri, 22 Mar 2019 05:42:32 GMT
287 |
288 | 05:42:32.090370 IP 127.0.0.1.8080 > 127.0.0.1.47318: Flags [P.], seq 56470:60566, ack 101, win 342, options [nop,nop,TS val 926521516 ecr 926521515], length 4096: HTTP
289 | {"type":"ADDED", ... "oper
290 | ...
291 | ```
292 |
293 |
294 |
295 | -----------------------------------------
296 |
297 |
298 |
299 | ## Scheduler의 이해
300 |
301 | * pod이 생성되고, node에 할당되지 않았을 때 scheduler가 특정 노드에 pod을 할당 (api-server를 통해 resource만 update)
302 | * pod이 update되면 (pod이 node에 할당되면) 해당 노드의 kubelet이 실제 pod을 생성
303 |
304 |
305 |
306 | -----------------------------------------
307 |
308 |
309 |
310 | ## 기본 스케줄링
311 |
312 | * pod이 schedule될 수 있는 노드의 목록을 필터링
313 | * 허용하는 노드 중 우선순위로 정렬하고 최적의 노드를 선택한다
314 |
315 |
316 |
317 |

318 |
319 |
320 |
321 | -----------------------------------------
322 |
323 |
324 |
325 | ## 수용 가능한 노드 찾기
326 |
327 | * node가 pod를 호스팅할 수 있으려면 아래의 사항들을 통과해야 함
328 |
329 | * node가 pod의 request resource 이상의 여분이 있는가?
330 | * node에 리소스가 부족한가?
331 | * node가 pod 스펙의 노드 셀렉터에 맞는 라벨을 가졌는가?
332 | * pod이 특정 호스트 포트에 바인딩을 요청하는 경우 해당 node에 포트가 이미 사용되고 있는가?
333 | * pod이 특정 볼륨 유형을 요청하는 경우, 이 볼륨을 node의 pod에 마운트할 수 있는가?
334 | * pod는 node의 taints를 허용하는가?
335 |
336 |
337 |
338 | -----------------------------------------
339 |
340 |
341 |
342 | ## Controller 소개
343 |
344 | * API 서버는 Resource를 etcd에 저장하고 변경 사항을 통지하기만 함.
345 | * 변경 사항 반영은 Controller Manager에서 실행되는 Controller가 수행.
346 |
347 |
348 |
349 | ## Controller가 하는 일과 동작 방식
350 |
351 | * API 서버를 통해 리소스의 변경을 감시하고, 오브젝트의 생성, 갱신, 삭제 등의 동작을 수행.
352 | * 실제 상태를 원하는 상태로 변경을 시도.
353 | * 변경 된 실제 상태를 Resource의 Status 필드에 반영.
354 | * Controller 간에는 직접 통신하지 않음.
355 | * Controller는 Kubelet과 직접 통신하지 않음.
356 |
357 |
358 |
359 | -----------------------------------------
360 |
361 |
362 |
363 | ### Replication Manager, ReplicaSet Controller
364 |
365 |

366 |
367 |
368 |
369 | -----------------------------------------
370 |
371 |
372 |
373 | ### DaemonSet, Job Controller
374 |
375 | * Pod Resource를 각 Resource에 정의된 Pod Template으로 생성.
376 | * Pod 정의를 API 서버로 전달하여 Kubelet이 Container를 생성하고 실행하게 만듬.
377 |
378 |
379 |
380 | ### Deployment Controller
381 |
382 | * API 서버에 저장 된 오브젝트와 동기를 맞추기 위해 실제 Deployment 상태를 유지 관리.
383 | * Deployment Object가 변경될 때 마다 새로운 버전으로 Roll-Out.
384 |
385 |
386 |
387 | ### StatefulSet Controller
388 |
389 | * StatefulSet Resource의 Spec에 따라 Pod를 생성, 관리, 삭제.
390 | * 각 Pod Instance의 PersistentVolumeClaim을 인스턴스화 하고 관리.
391 |
392 |
393 |
394 | -----------------------------------------
395 |
396 |
397 |
398 | ### Node Controller
399 |
400 | * Cluster의 Node Resource를 관리
401 | * API 서버에서의 Resource 이름은 Nodes, etcd에서의 Key 이름은 minions
402 |
403 | ### Service Controller
404 |
405 | * LoadBalancer Type의 Service가 생성되면 인프라스트럭처로 LoadBalancer를 요청.
406 |
407 |
408 |
409 | -----------------------------------------
410 |
411 |
412 |
413 | ### Endpoint Controller
414 |
415 | * Service, Pod, Endpoint Resource를 모두 감시
416 | * Service의 label selector와 매칭되는 pod의 ip와 port를 찾아 Endpoint Resource를 최신 상태로 유지
417 |
418 |

419 |
420 |
421 |
422 | -----------------------------------------
423 |
424 |
425 |
426 | ### Namespace Controller
427 |
428 | * Namespace Resource가 제거될 때 해당 Namespace에 속한 모든 Resource를 제거.
429 |
430 |
431 |
432 | ### PersistentVolume Controller
433 |
434 | * PersistentVolumeClaim이 생성되면 적절한 PersistentVolume을 찾아 Binding.
435 |
436 |
437 |
438 | -----------------------------------------
439 |
440 |
441 |
442 | ## Kubelet
443 |
444 | * Worker Node에서 실행되는 모든 것에 책임을 가짐.
445 | * 초기 실행 시 Kubelet이 실행되는 Host를 Node Resource로 등록.
446 | * 해당 Node에 Schedule된 Pod을 모니터링하여 Pod의 Container를 실행.
447 | * 실행 중인 Container를 지속적으로 모니터링하고 상태와 이벤트, 리소스 소모를 API 서버에 통지.
448 | * readness, liveness probe를 실행하는 컴포넌트.
449 | * Pod Resource가 삭제됐을 때 Container를 중지하고 완전히 중지되면 API 서버에 통지.
450 |
451 |
452 |
453 | -----------------------------------------
454 |
455 |
456 |
457 | ## Kubelet
458 |
459 | * 특정 local directory의 manifests 파일 기반으로 Pod 생성 가능
460 | * Control Plane Pods (ex. kube-apiserver, kube-control-manager ... )
461 | ```
462 | $ ls /etc/kubernetes/manifests
463 | etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
464 | ```
465 |
466 |

467 |
468 |
469 |
470 | -----------------------------------------
471 |
472 |
473 |
474 | ## Kubernetes Proxy
475 |
476 | * 모든 Worker Node는 kube-proxy를 실행.
477 | * Service Object로 Client가 연결할 수 있도록 하는 것이 목적.
478 | * Service가 둘 이상의 Pod으로 연결되면 해당 노드에서 LoadBalancing을 수행.
479 | * 초기 버전의 구현은 iptables의 userspace redirect를 이용하여 구현.
480 | * 현재는 일반적인 iptables의 Rule로 구현.
481 |
482 |

483 |
484 |

485 |
486 |
487 |
488 | -----------------------------------------
489 |
490 |
491 |
492 | ## Kubernetes Add-On
493 |
494 | * DNS, Ingress, Web Dashboard 등.
495 | * Yaml Manifests를 통해 Pod처럼 배포.
496 | * Deployments, ReplicationSet/Controller, DaemonSet의 형태로 배포.
497 |
498 |
499 |
500 | -----------------------------------------
501 |
502 |
503 |
504 | ## DNS 서버
505 |
506 | * Kubernetes Cluster의 모든 Pod은 내부 DNS 서버를 사용하도록 구성하는 것이 기본.
507 | * Cluster에 배포되는 모든 Container 내부의 /etc/resolv.conf 파일에 nameserver로 등록
508 | ```
509 | $ root@k8s-master:/home/h# kubectl exec -it sample cat /etc/resolv.conf
510 | nameserver 10.96.0.10
511 | search default.svc.k8s svc.k8s k8s
512 | options ndots:5
513 | ```
514 | * API 서버의 watch interface를 이용하여 Pod, Service, Endpoints의 변화를 관찰
515 | * 최신의 DNS 정보를 유지.
516 | * Resource가 갱신될 때 watch interface를 통해 통지 받는 사이 잠시동안 DNS Record가 유효하지 않을 수 있음.
517 |
518 |
519 |
520 | -----------------------------------------
521 |
522 |
523 |
524 | ## Ingress Controller
525 |
526 | * Nginx등의 Reverse Proxy를 실행.
527 | * Ingress, Service, Endpoint Resource를 감시하여 구성.
528 | * Ingress Resource의 정의가 서비스를 가리키고 있지만 Ingress는 Service IP 대신 직접 Pod로 트래픽을 전달.
529 |
530 | * curl -XGET 'http://service:8080'
531 |
532 | ```
533 | $ root@k8s-worker1:/home/h# tcpdump -i calib43f921251f
534 | 03:08:19.315423 IP 192.168.0.1.60378 > 192.168.1.40.http-alt: Flags [P.], seq 1:83, ack 1, win 229, options [nop,nop,TS val 3164462534 ecr 3348765433], length 82: HTTP: GET / HTTP/1.1
535 | 03:08:19.315760 IP 192.168.1.40.http-alt > 192.168.0.1.60378: Flags [P.], seq 1:143, ack 83, win 227, options [nop,nop,TS val 3348765434 ecr 3164462534], length 142: HTTP: HTTP/1.1 200 OK
536 | ```
537 |
538 | * curl -XGET 'https://ingress/sample'
539 | ```
540 | $ root@k8s-worker1:/home/h# tcpdump -i calib43f921251f
541 | 03:04:09.025471 IP 192.168.1.38.44304 > 192.168.1.40.http-alt: Flags [P.], seq 1:310, ack 1, win 229, options [nop,nop,TS val 2177334759 ecr 1588388826], length 309: HTTP: GET /sample HTTP/1.1
542 | 03:04:09.025669 IP 192.168.1.40.http-alt > 192.168.1.38.44304: Flags [P.], seq 1:143, ack 310, win 235, options [nop,nop,TS val 1588388827 ecr 2177334759], length 142: HTTP: HTTP/1.1 200 OK
543 | ```
544 |
545 |
546 |
547 | -----------------------------------------
548 |
549 |
550 |
551 | ## Controller의 상호 협력 방식
552 |
553 |

554 |
555 |
556 |
557 | -----------------------------------------
558 |
559 |
560 |
561 | ## 실행 중인 Pod의 이해
562 |
563 | * Pod이 배포되면 항상 /pause를 실행하는 Pod Infra Container가 같이 배포.
564 | * Pod이 배포되면 Pod Infra Container가 먼저 실행되고 해당 Container가 하나의 namespace를 점유.
565 | * Pod 내의 다른 Container는 Pod Infra Container의 namespace를 공유하여 사용 (https://www.ianlewis.org/en/almighty-pause-container).
566 |
567 |

568 |
569 |
570 |
571 | -----------------------------------------
572 |
573 |
574 |
575 | ## Inter-Pod Networking
576 |
577 | * Pod간 Networking은 CNI(Cloud Network Interface) Plug-In에 의해 수행.
578 | * 상세한 구현 방식은 CNI별로 상이함.
579 | * Pod이 어떤 Node에 있건 관계없이 통신할 수 있어야 함.
580 | * Pod가 통신하는 데 사용하는 IP 주소는 변환되지 않아야 함(no NAT).
581 | * 이는 하나의 스위치에 연결 된 것처럼 간단하고 정확하게 통신 가능하도록 만듬 (Fabric).
582 | * Pod to Node, Node to Pod간의 통신에는 SNAT를 사용.
583 |
584 |
585 |
586 | -----------------------------------------
587 |
588 |
589 |
590 | ## Inter-Pod Networking
591 |
592 |

593 |
594 |
595 |
596 | -----------------------------------------
597 |
598 |
599 |
600 | ## 동일한 노드 상의 Pod간의 통신
601 |
602 |

603 |
604 |
605 |
606 | -----------------------------------------
607 |
608 |
609 |
610 | ## 동일한 Node 상의 Pod간의 통신
611 |
612 | * Pod Infra Container가 실행되기 전 veth 쌍을 생성.
613 | * 하나는 Pod의 Namespace 내에, 하나는 Host의 Namespace 내에 생성.
614 | * Node 내의 모든 Container는 동일한 Bridge에 연결되므로 서로 통신이 가능.
615 |
616 |
617 | ```
618 | $ ifconfig
619 | cali0f45f098f23: flags=4163 mtu 1500
620 | cali60876d4f815: flags=4163 mtu 1500
621 | ```
622 |
623 | ```
624 | $ ip route
625 | ...
626 | 192.168.0.43 dev cali0f45f098f23 scope link
627 | 192.168.0.44 dev cali6a5876e6908 scope link
628 | 192.168.0.45 dev cali60876d4f815 scope link
629 | ...
630 | ```
631 |
632 |
633 |
634 | -----------------------------------------
635 |
636 |
637 |
638 | ## 서로 다른 Node 상의 Pod간의 통신
639 |
640 |

641 |
642 |
643 |
644 | -----------------------------------------
645 |
646 |
647 |
648 | ## 서로 다른 Node 상의 Pod간의 통신
649 |
650 | * Pod의 IP 주소는 클러스터 내에서 유일해야 함.
651 | * Node의 Bridge는 겹치지 않는 주소 범위를 사용.
652 | * Node의 Physical Interface도 Node의 Bridge에 연결 됨.
653 | * Node A의 PodA에서 Node B의 Pod C로 패킷을 보내려면 Node A에서 Node B로 라우팅되도록 라우팅 테이블을 구성해야 함.
654 |
655 | ```
656 | $ tcpdump -n -i tunl0
657 | 04:14:41.984400 IP 192.168.0.45 > 192.168.1.43: ICMP echo request, id 65, seq 85, length 64
658 | 04:14:41.984884 IP 192.168.1.43 > 192.168.0.45: ICMP echo reply, id 65, seq 85, length 64
659 | ```
660 | ```
661 | $ tcpdump -n -i enp0s8 ! port 22
662 | 04:13:31.618048 IP 192.168.56.101 > 192.168.56.102: IP 192.168.0.45 > 192.168.1.43: ICMP echo request, id 65, seq 16, length 64 (ipip-proto-4)
663 | 04:13:31.618480 IP 192.168.56.102 > 192.168.56.101: IP 192.168.1.43 > 192.168.0.45: ICMP echo reply, id 65, seq 16, length 64 (ipip-proto-4)
664 | ```
665 |
666 | ```
667 | $ ip route
668 | 192.168.1.0/24 via 192.168.56.102 dev tunl0 proto bird onlink
669 | 192.168.2.0/24 via 192.168.56.103 dev tunl0 proto bird onlink
670 | ```
671 |
672 |
673 |
674 | -----------------------------------------
675 |
676 |
677 |
678 | ## Service의 구현
679 |
680 | * Service와 관련된 모든 것은 각 노드에서 실행되는 kube-proxy에 의해 처리.
681 | * 각 Service는 고유한 IP와 Port를 얻음.
682 | * Service IP는 가상의 IP.
683 | * Service IP를 물리적으로 갖는 Interface가 없으므로 icmp 요청이 처리되지 않음.
684 | * API 서버에서 Service가 생성되면 가상 IP, Port 쌍이 즉시 할당 됨.
685 | * kube-proxy는 Service Resource의 생성 알람을 받으면 iptables에 규칙을 설정.
686 | * 목적지가 해당 Service인 경우 목적지 주소를 Service와 연결 된 Pod 중 하나로 변경(DNAT)하여 Redirect (Pod to Service).
687 | * Pod 외 (Node, cluster 밖의 client)에서 접근하는 경우 SNAT(Node의 IP), DNAT(Pod의 IP) 모두 필요.
688 |
689 |
690 |
691 | -----------------------------------------
692 |
693 |
694 |
695 | ## Service의 구현
696 |
697 |

698 |
699 |
700 |
701 | -----------------------------------------
702 |
703 |
704 |
705 | ## Service의 구현
706 |
707 | ```
708 | $ iptables -L -t nat
709 |
710 | Chain PREROUTING (policy ACCEPT)
711 | target prot opt source destination
712 | KUBE-SERVICES all -- anywhere anywhere /* kubernetes service portals */
713 |
714 | Chain KUBE-SERVICES (2 references)
715 | target prot opt source destination
716 | KUBE-MARK-MASQ tcp -- !192.168.0.0/16 10.111.118.28 /* default/sample-service: cluster IP */ tcp dpt:http-alt
717 | KUBE-SVC-ZE62HOGUXOIF3MJ5 tcp -- anywhere 10.111.118.28 /* default/sample-service: cluster IP */ tcp dpt:http-alt
718 |
719 | Chain KUBE-SVC-ZE62HOGUXOIF3MJ5 (2 references)
720 | target prot opt source destination
721 | KUBE-SEP-7AE52TSMNDEGV6BO all -- anywhere anywhere statistic mode random probability 0.50000000000
722 | KUBE-SEP-GEQ73U43LIPSQP2Z all -- anywhere anywhere
723 |
724 | Chain KUBE-SEP-7AE52TSMNDEGV6BO (1 references)
725 | target prot opt source destination
726 | KUBE-MARK-MASQ all -- 192.168.1.42 anywhere
727 | DNAT tcp -- anywhere anywhere tcp to:192.168.1.42:8080
728 |
729 | Chain KUBE-SEP-GEQ73U43LIPSQP2Z (1 references)
730 | target prot opt source destination
731 | KUBE-MARK-MASQ all -- 192.168.2.30 anywhere
732 | DNAT tcp -- anywhere anywhere tcp to:192.168.2.30:8080
733 | ```
734 |
735 |
736 |
737 | -----------------------------------------
738 |
739 |
740 |
741 | ## Service의 구현
742 |
743 | ```
744 | Chain KUBE-MARK-DROP (2 references)
745 | target prot opt source destination
746 | MARK all -- anywhere anywhere MARK or 0x8000
747 |
748 | Chain KUBE-MARK-MASQ (18 references)
749 | target prot opt source destination
750 | MARK all -- anywhere anywhere MARK or 0x4000
751 |
752 | Chain KUBE-POSTROUTING (1 references)
753 | target prot opt source destination
754 | MASQUERADE all -- anywhere anywhere /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
755 | ```
756 |
757 |
758 |
759 | -----------------------------------------
760 |
761 |
762 |
763 | ### Pod 밖에서 Service로 요청
764 |
765 | * 192.168.56.1 (Client)
766 | * 192.168.56.103 (Node)
767 | ```
768 | $ tcpdump -i enp0s8 port 30001 -n
769 | 05:17:50.632656 IP 192.168.56.1.55824 > 192.168.56.103.30001: Flags [SEW], seq 920096640, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 449946274 ecr 0,sackOK,eol], length 0
770 | 05:17:50.632886 IP 192.168.56.103.30001 > 192.168.56.1.55824: Flags [S.E], seq 2034560536, ack 920096641, win 28960, options [mss 1460,sackOK,TS val 167059923 ecr 449946274,nop,wscale 7], length 0
771 | 05:17:50.633116 IP 192.168.56.1.55824 > 192.168.56.103.30001: Flags [.], ack 1, win 2058, options [nop,nop,TS val 449946274 ecr 167059923], length 0
772 | 05:17:50.633134 IP 192.168.56.1.55824 > 192.168.56.103.30001: Flags [P.], seq 1:85, ack 1, win 2058, options [nop,nop,TS val 449946274 ecr 167059923], length 84
773 | 05:17:50.633251 IP 192.168.56.103.30001 > 192.168.56.1.55824: Flags [.], ack 85, win 227, options [nop,nop,TS val 167059923 ecr 449946274], length 0
774 | 05:17:50.633746 IP 192.168.56.103.30001 > 192.168.56.1.55824: Flags [P.], seq 1:138, ack 85, win 227, options [nop,nop,TS val 167059924 ecr 449946274], length 137
775 | 05:17:50.633944 IP 192.168.56.1.55824 > 192.168.56.103.30001: Flags [.], ack 138, win 2056, options [nop,nop,TS val 449946275 ecr 167059924], length 0
776 | 05:17:50.633962 IP 192.168.56.1.55824 > 192.168.56.103.30001: Flags [F.], seq 85, ack 138, win 2056, options [nop,nop,TS val 449946275 ecr 167059924], length 0
777 | 05:17:50.634127 IP 192.168.56.103.30001 > 192.168.56.1.55824: Flags [F.], seq 138, ack 86, win 227, options [nop,nop,TS val 167059924 ecr 449946275], length 0
778 | 05:17:50.634320 IP 192.168.56.1.55824 > 192.168.56.103.30001: Flags [.], ack 139, win 2056, options [nop,nop,TS val 449946275 ecr 167059924], length 0
779 | ```
780 | * 10.0.2.22 (Node)
781 | * 192.168.2.30 (Pod)
782 | ```
783 | $ tcpdump -i cali27c81818b22 -n
784 | 05:17:50.632712 IP 10.0.2.22.55824 > 192.168.2.30.8080: Flags [SEW], seq 920096640, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 449946274 ecr 0,sackOK,eol], length 0
785 | 05:17:50.632874 IP 192.168.2.30.8080 > 10.0.2.22.55824: Flags [S.E], seq 2034560536, ack 920096641, win 28960, options [mss 1460,sackOK,TS val 167059923 ecr 449946274,nop,wscale 7], length 0
786 | 05:17:50.633127 IP 10.0.2.22.55824 > 192.168.2.30.8080: Flags [.], ack 1, win 2058, options [nop,nop,TS val 449946274 ecr 167059923], length 0
787 | 05:17:50.633139 IP 10.0.2.22.55824 > 192.168.2.30.8080: Flags [P.], seq 1:85, ack 1, win 2058, options [nop,nop,TS val 449946274 ecr 167059923], length 84: HTTP: GET / HTTP/1.1
788 | 05:17:50.633202 IP 192.168.2.30.8080 > 10.0.2.22.55824: Flags [.], ack 85, win 227, options [nop,nop,TS val 167059923 ecr 449946274], length 0
789 | 05:17:50.633731 IP 192.168.2.30.8080 > 10.0.2.22.55824: Flags [P.], seq 1:138, ack 85, win 227, options [nop,nop,TS val 167059924 ecr 449946274], length 137: HTTP: HTTP/1.1 200 OK
790 | 05:17:50.633960 IP 10.0.2.22.55824 > 192.168.2.30.8080: Flags [.], ack 138, win 2056, options [nop,nop,TS val 449946275 ecr 167059924], length 0
791 | 05:17:50.633966 IP 10.0.2.22.55824 > 192.168.2.30.8080: Flags [F.], seq 85, ack 138, win 2056, options [nop,nop,TS val 449946275 ecr 167059924], length 0
792 | 05:17:50.634115 IP 192.168.2.30.8080 > 10.0.2.22.55824: Flags [F.], seq 138, ack 86, win 227, options [nop,nop,TS val 167059924 ecr 449946275], length 0
793 | 05:17:50.634369 IP 10.0.2.22.55824 > 192.168.2.30.8080: Flags [.], ack 139, win 2056, options [nop,nop,TS val 449946275 ecr 167059924], length 0
794 |
795 | ```
796 |
797 |
798 |
799 | -----------------------------------------
800 |
801 |
802 |
803 | ### Pod에서 Service로 요청 (다른 Node의 Pod으로 서비스 되는 경우)
804 |
805 | * 192.168.1.42 (Source Pod)
806 | * 10.111.118.28 (Service)
807 | ```
808 | $ tcpdump -i calib43f921251f -n
809 | 05:14:05.077057 IP 192.168.1.42.54122 > 10.111.118.28.8080: Flags [S], seq 1210630612, win 29200, options [mss 1460,sackOK,TS val 2710881183 ecr 0,nop,wscale 7], length 0
810 | 05:14:05.077767 IP 10.111.118.28.8080 > 192.168.1.42.54122: Flags [S.], seq 4123667957, ack 1210630613, win 28960, options [mss 1460,sackOK,TS val 411294588 ecr 2710881183,nop,wscale 7], length 0
811 | 05:14:05.077789 IP 192.168.1.42.54122 > 10.111.118.28.8080: Flags [.], ack 1, win 229, options [nop,nop,TS val 2710881184 ecr 411294588], length 0
812 | 05:14:05.078086 IP 192.168.1.42.54122 > 10.111.118.28.8080: Flags [P.], seq 1:83, ack 1, win 229, options [nop,nop,TS val 2710881184 ecr 411294588], length 82: HTTP: GET / HTTP/1.1
813 | 05:14:05.078798 IP 10.111.118.28.8080 > 192.168.1.42.54122: Flags [.], ack 83, win 227, options [nop,nop,TS val 411294589 ecr 2710881184], length 0
814 | 05:14:05.079175 IP 10.111.118.28.8080 > 192.168.1.42.54122: Flags [P.], seq 1:138, ack 83, win 227, options [nop,nop,TS val 411294590 ecr 2710881184], length 137: HTTP: HTTP/1.1 200 OK
815 | 05:14:05.079193 IP 192.168.1.42.54122 > 10.111.118.28.8080: Flags [.], ack 138, win 237, options [nop,nop,TS val 2710881185 ecr 411294590], length 0
816 | 05:14:05.079350 IP 192.168.1.42.54122 > 10.111.118.28.8080: Flags [F.], seq 83, ack 138, win 237, options [nop,nop,TS val 2710881185 ecr 411294590], length 0
817 | 05:14:05.080079 IP 10.111.118.28.8080 > 192.168.1.42.54122: Flags [F.], seq 138, ack 84, win 227, options [nop,nop,TS val 411294591 ecr 2710881185], length 0
818 | 05:14:05.080094 IP 192.168.1.42.54122 > 10.111.118.28.8080: Flags [.], ack 139, win 237, options [nop,nop,TS val 2710881186 ecr 411294591], length 0
819 | ```
820 |
821 | * 192.168.1.42 (Source Pod)
822 | * 192.168.2.30 (Destination Pod)
823 | ```
824 | $ tcpdump -i cali27c81818b22 -n
825 | 05:14:05.099668 IP 192.168.1.42.54122 > 192.168.2.30.8080: Flags [S], seq 1210630612, win 29200, options [mss 1460,sackOK,TS val 2710881183 ecr 0,nop,wscale 7], length 0
826 | 05:14:05.099826 IP 192.168.2.30.8080 > 192.168.1.42.54122: Flags [S.], seq 4123667957, ack 1210630613, win 28960, options [mss 1460,sackOK,TS val 411294588 ecr 2710881183,nop,wscale 7], length 0
827 | 05:14:05.100223 IP 192.168.1.42.54122 > 192.168.2.30.8080: Flags [.], ack 1, win 229, options [nop,nop,TS val 2710881184 ecr 411294588], length 0
828 | 05:14:05.100782 IP 192.168.1.42.54122 > 192.168.2.30.8080: Flags [P.], seq 1:83, ack 1, win 229, options [nop,nop,TS val 2710881184 ecr 411294588], length 82: HTTP: GET / HTTP/1.1
829 | 05:14:05.100806 IP 192.168.2.30.8080 > 192.168.1.42.54122: Flags [.], ack 83, win 227, options [nop,nop,TS val 411294589 ecr 2710881184], length 0
830 | 05:14:05.101125 IP 192.168.2.30.8080 > 192.168.1.42.54122: Flags [P.], seq 1:138, ack 83, win 227, options [nop,nop,TS val 411294590 ecr 2710881184], length 137: HTTP: HTTP/1.1 200 OK
831 | 05:14:05.101710 IP 192.168.1.42.54122 > 192.168.2.30.8080: Flags [.], ack 138, win 237, options [nop,nop,TS val 2710881185 ecr 411294590], length 0
832 | 05:14:05.101944 IP 192.168.1.42.54122 > 192.168.2.30.8080: Flags [F.], seq 83, ack 138, win 237, options [nop,nop,TS val 2710881185 ecr 411294590], length 0
833 | 05:14:05.102144 IP 192.168.2.30.8080 > 192.168.1.42.54122: Flags [F.], seq 138, ack 84, win 227, options [nop,nop,TS val 411294591 ecr 2710881185], length 0
834 | 05:14:05.102585 IP 192.168.1.42.54122 > 192.168.2.30.8080: Flags [.], ack 139, win 237, options [nop,nop,TS val 2710881186 ecr 411294591], length 0
835 | ```
836 |
837 |
838 |
839 | -----------------------------------------
840 |
841 |
842 |
843 | ### Pod에서 Service로 요청 (자신에게 서비스 되는 경우)
844 |
845 | * 192.168.1.42 (Source Pod)
846 | * 10.111.118.28 (Service)
847 | * 10.0.2.21 (Source Pod의 Node)
848 | ```
849 | $ tcpdump -i calib43f921251f -n
850 | 05:15:59.556723 IP 192.168.1.42.54308 > 10.111.118.28.8080: Flags [S], seq 4048875942, win 29200, options [mss 1460,sackOK,TS val 2710995663 ecr 0,nop,wscale 7], length 0
851 | 05:15:59.556770 IP 10.0.2.21.54308 > 192.168.1.42.8080: Flags [S], seq 4048875942, win 29200, options [mss 1460,sackOK,TS val 2710995663 ecr 0,nop,wscale 7], length 0
852 | 05:15:59.556779 IP 192.168.1.42.8080 > 10.0.2.21.54308: Flags [S.], seq 2680204874, ack 4048875943, win 28960, options [mss 1460,sackOK,TS val 1749589035 ecr 2710995663,nop,wscale 7], length 0
853 | 05:15:59.556785 IP 10.111.118.28.8080 > 192.168.1.42.54308: Flags [S.], seq 2680204874, ack 4048875943, win 28960, options [mss 1460,sackOK,TS val 1749589035 ecr 2710995663,nop,wscale 7], length 0
854 | 05:15:59.556792 IP 192.168.1.42.54308 > 10.111.118.28.8080: Flags [.], ack 1, win 229, options [nop,nop,TS val 2710995663 ecr 1749589035], length 0
855 | 05:15:59.556796 IP 10.0.2.21.54308 > 192.168.1.42.8080: Flags [.], ack 1, win 229, options [nop,nop,TS val 2710995663 ecr 1749589035], length 0
856 | 05:15:59.557335 IP 192.168.1.42.54308 > 10.111.118.28.8080: Flags [P.], seq 1:83, ack 1, win 229, options [nop,nop,TS val 2710995663 ecr 1749589035], length 82: HTTP: GET / HTTP/1.1
857 | 05:15:59.557353 IP 10.0.2.21.54308 > 192.168.1.42.8080: Flags [P.], seq 1:83, ack 1, win 229, options [nop,nop,TS val 2710995663 ecr 1749589035], length 82: HTTP: GET / HTTP/1.1
858 | 05:15:59.557361 IP 192.168.1.42.8080 > 10.0.2.21.54308: Flags [.], ack 83, win 227, options [nop,nop,TS val 1749589035 ecr 2710995663], length 0
859 | 05:15:59.557366 IP 10.111.118.28.8080 > 192.168.1.42.54308: Flags [.], ack 83, win 227, options [nop,nop,TS val 1749589035 ecr 2710995663], length 0
860 | 05:15:59.558809 IP 192.168.1.42.8080 > 10.0.2.21.54308: Flags [P.], seq 1:143, ack 83, win 227, options [nop,nop,TS val 1749589037 ecr 2710995663], length 142: HTTP: HTTP/1.1 200 OK
861 | 05:15:59.558856 IP 10.111.118.28.8080 > 192.168.1.42.54308: Flags [P.], seq 1:143, ack 83, win 227, options [nop,nop,TS val 1749589037 ecr 2710995663], length 142: HTTP: HTTP/1.1 200 OK
862 | 05:15:59.558861 IP 192.168.1.42.54308 > 10.111.118.28.8080: Flags [.], ack 143, win 237, options [nop,nop,TS val 2710995665 ecr 1749589037], length 0
863 | 05:15:59.558869 IP 10.0.2.21.54308 > 192.168.1.42.8080: Flags [.], ack 143, win 237, options [nop,nop,TS val 2710995665 ecr 1749589037], length 0
864 | 05:15:59.559946 IP 192.168.1.42.54308 > 10.111.118.28.8080: Flags [F.], seq 83, ack 143, win 237, options [nop,nop,TS val 2710995666 ecr 1749589037], length 0
865 | 05:15:59.559959 IP 10.0.2.21.54308 > 192.168.1.42.8080: Flags [F.], seq 83, ack 143, win 237, options [nop,nop,TS val 2710995666 ecr 1749589037], length 0
866 | 05:15:59.560205 IP 192.168.1.42.8080 > 10.0.2.21.54308: Flags [F.], seq 143, ack 84, win 227, options [nop,nop,TS val 1749589038 ecr 2710995666], length 0
867 | 05:15:59.560265 IP 10.111.118.28.8080 > 192.168.1.42.54308: Flags [F.], seq 143, ack 84, win 227, options [nop,nop,TS val 1749589038 ecr 2710995666], length 0
868 | 05:15:59.560271 IP 192.168.1.42.54308 > 10.111.118.28.8080: Flags [.], ack 144, win 237, options [nop,nop,TS val 2710995666 ecr 1749589038], length 0
869 | 05:15:59.560280 IP 10.0.2.21.54308 > 192.168.1.42.8080: Flags [.], ack 144, win 237, options [nop,nop,TS val 2710995666 ecr 1749589038], length 0
870 | ```
871 |
872 |
873 |
874 | -----------------------------------------
875 |
876 |
877 |
878 | ## High-Availability Cluster
879 |
880 | * Kubernetes를 사용해 Application을 실행하는 이유 중 하나는 장애시에도 지속적으로 서비스를 하기 위함.
881 | * 장애시에도 중단 없이 서비스되려면 Application 뿐 아니라 Kubernetes의 Control Plane도 항상 작동해야 함.
882 |
883 |
884 |
885 | ## Application의 High-Availability
886 |
887 | * RC, RS로 배포하여 적절한 Replica를 구성.
888 | * 수평확장이 불가하더라도 Replica가 1인 RC, RS로 배포하면 장애 시 재배포.
889 | * 수평확장이 불가할 때 Leader Election을 이용해 고가용성을 확보 하면서 하나의 Pod만 동작하게 구성 가능.(https://github.com/kubernetes/contrib/tree/master/election)
890 |
891 |
892 |
893 | -----------------------------------------
894 |
895 |
896 |
897 | ## Kubernetes Control Plane의 High-Availability
898 |
899 |

900 |
901 |
902 |
903 | -----------------------------------------
904 |
905 |
906 |
907 | ### etcd Cluster
908 |
909 | * etcd는 분산 시스템으로 설계 됨.
910 | * Split Brain을 방지하기 위해 홀수개(3, 5, 7)의 Node로 실행
911 |
912 | ### API 서버
913 |
914 | * API 서버는 (거의 완전하게..?) Stateless하므로 필요한 만큼 실행할 수 있음.
915 |
916 | ### Controller와 Scheduler
917 |
918 | * Stateless하지 않은 컴포넌트기 때문에 수평확장이 어려움.
919 | * --leader-elcect=true로 실행되면 선출된 리더일 때만 동작.
920 | * 그 외의 컴포넌트는 대기중이며 현재의 리더가 실패할 경우 새로운 리더가 선출되고 새로 선출된 리더만 동작.
921 | * Endpoints의 Annotation(control-plane.alpha.kubernetes.io/leader)의 holderIdentity에 자신의 id를 등록하여 주기적으로 update.
922 |
923 |
924 |
925 | -----------------------------------------
926 |
927 |
928 |
929 | ### Controller와 Scheduler
930 |
931 |

932 |
933 | ```
934 | $ kubectl describe endpoints kube-scheduler -n kube-system
935 | Name: kube-scheduler
936 | Namespace: kube-system
937 | Labels:
938 | Annotations: control-plane.alpha.kubernetes.io/leader:
939 | {"holderIdentity":"k8s-master_3828400c-4eab-11e9-b23d-0800271eafb6","leaseDurationSeconds":15,"acquireTime":"2019-03-25T03:08:36Z","renewT...
940 | Subsets:
941 | Events:
942 | ```
943 |
944 | ```
945 | $ curl http://localhost:8080/api/v1/namespaces/kube-system/endpoints?watch=true
946 | ```
947 |
948 |
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-01.png
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-02.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-03.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-04.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-05.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-06.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-07.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-08.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-09.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-10.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-11.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-12.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-13.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-14.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-15.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-16.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-17.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-18.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap11/architecture-19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap11/architecture-19.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap12.md:
--------------------------------------------------------------------------------
1 | # 쿠버네티스 API 서버 보안
2 |
3 | ## 12.1 인증 이해하기
4 | - Kubernetes API 서버는 하나 이상의 인증 플러그인으로 구성(승인 플러그인도 마찬가지)
5 | - 기본적으로 인증 플러그인은 사용자 이름, 사용자 ID, 클라이언트가 속한 그룹을 반환
6 |
7 | ### 사용자와 그룹
8 | #### 사용자
9 | - 사용자는 실제 사람, Pod(Pod 내부 애플리케이션) 두가지로 나눠짐
10 | - 사용자는 SSO(Single Sign On) 시스템과 같은 외부 시스템을 통해서 관리됨
11 | - Pod는 Service Accounts 라는 메커니즘 사용
12 | - 이번 12장은 주로 Service Accounts에 대해서 살펴볼 예정
13 | #### 그룹
14 | - 사용자 및 Service Account는 하나 이상의 그룹에 속할 수 있음
15 | - 다른 도구들과 비슷하게 사용자 그룹 전체에 권한 부여할 때 사용
16 | - 시스템에서 제공하는 기본 그룹의 의미
17 | * `system:unauthenticated`: 어느 인증 플러그인도 인증할 수 없는 클라이언트의 요청, 즉 비인증 클라이언트에게 할당되는 그룹
18 | * `system:authenticated`: 성공적으로 인증된 클라이언트에게 자동으로 할당되는 그룹
19 | * `System:serviceaccounts`: 모든 서비스 어카운트에게 할당되어있는 그룹
20 | * `System:serviceaccounts:`: 특정 네임스페이스의 모든 서비스 어카운트에게 할당되어있는 그룹
21 |
22 |
23 | ### 서비스 어카운트 소개
24 | - 서비스어카운트는 Pod, Secret, ConfigMap과 같은 리소스이며 개별 네임스페이스로 범위가 지정됨
25 | - 기본적으로 네임스페이스별 디폴트 서비스어카운트가 자동적으로 생성되며, 서비스어카운트 설정을 안할 경우 디폴트 서비스어카운트로 설정됨.
26 | - Pod의 컨테이너 내부에 볼륨으로 마운트된`/var/run/secrets/kubernetes.io/serviceaccount/token` 파일이 서비스어카운트 인증 토큰
27 | - Pod에 각기 다른 서비스어카운트를 할당하여 접근 가능한 리소스 제어 가능 -> 역할 기반 접근 제어가 거의 표준 플러그인
28 | - 서비스어카운트를 사용하는 이유는 클러스터 보안 때문 -> k8s API 서버의 침입자가 클러스터를 점유할 가능성을 줄이는것
29 |
30 | ### 서비스 어카운트 생성하기
31 |
32 | 서비스 어카운트 생성
33 | ```
34 | kubectl create serviceaccount foo
35 | ```
36 |
37 | 서비스 어카운트 확인
38 | ```
39 | kubectl describe sa foo
40 | ```
41 |
42 | 서비스 어카운트 시크릿 확인
43 | ```
44 | kubectl describe secret foo-token-qenlk
45 | ```
46 | #### UNDERSTANDING A SERVICEACCOUNT’S MOUNTABLE SECRETS ???
47 |
48 | #### UNDERSTANDING A SERVICEACCOUNT’S IMAGE PULL SECRETS
49 | 이미지 풀 시크릿은 컨테이너 이미지를 가져오는데 필요한 자격증명을 가지고 있는 시크릿으로, 서비스어카운트에 이 시크릿을 설정할 경우에 각 파드에 개별적으로 추가할 필요없이 파드가 서비스어카운트를 통해서 이미지를 가져올 수 있게 된다.
50 |
51 | ```yaml
52 | apiVersion: v1
53 | kind: ServiceAccount
54 | metadata:
55 | name: my-service-account
56 | imagePullSecrets:
57 | - name: my-dockerhub-secret
58 | ```
59 |
60 | ### 서비스 어카운트 할당하기
61 | - 파드의 spec.serviceAccountName 필드에 서비스어카운트의 이름을 설정
62 |
63 | ```yaml
64 | apiVersion: v1
65 | kind: Pod
66 | metadata:
67 | name: curl-custom-sa
68 | spec:
69 | serviceAccountName: foo
70 | containers:
71 | - name: main
72 | image: tutum/curl
73 | command: ["sleep", "9999999"]
74 | - name: ambassador
75 | image: luksa/kubectl-proxy:1.6.2
76 | ```
77 |
78 | 엠버서더를 이용하여 서비스어카운트 테스트해보기
79 | ```sh
80 | kubectl exec -it curl-custom-sa -c main curl localhost:8001/api/v1/pods
81 | ```
82 |
83 |
84 | ## 12.2 역할 기반 접근 제어 클러스터 보안
85 | - k8s 1.8.0 버전부터 RBAC 승인 플러그인은 GA(General Availability)로 승격하여 이제는 많은 클러스터에서 기본적으로 활성화됨
86 | - RBAC는 권한이 없는 사용자가 클러스터의 상태를 보거나 수정할 수 없도록 함
87 |
88 | ### RBAC 인증 플러그인 소개
89 |
90 | #### Mapping HTTP methods to authorization verbs
91 | HTTP method | Verb for single resource | Verb for collection
92 | ---|---|---
93 | GET, HEAD | get (and watch for watching) | list (and watch)
94 | POST | create | n/a
95 | PUT | update |n/a
96 | PATCH | patch |n/a
97 | DELETE | delete |deletecollection
98 |
99 | 전체 (쿠버네티스) 리소스 유형에 보안 권한을 적용하는 것 이외에도 `healthz`, `api`, `version` 등 리소스와 무관한 경로에도 권한 적용 가능
100 |
101 | ### RBAC 리소스 소개
102 | - 리소스 등에 대한 권한을 부여하는 롤과 클러스터롤
103 | - 위의 롤을 특정 사용자, 그룹, 서비스어카운트에 바인딩하는 롤바인딩과 클러스터롤바인딩
104 |
105 | 
106 |
107 | - 롤/롤바인딩 : 네임스페이스 수준의 리소스 권한 관리,
108 | - 클러스터롤/클러스터롤바인딩 : 클러스터 수준의 리소스 권한 관리
109 |
110 | 
111 |
112 |
113 | #### RBAC 동작확인
114 | - :warning: GKE에서 사용자에게 관리자 권한 부여하기
115 | ```
116 | gcloud projects add-iam-policy-binding ${PROJECT_NAME} \
117 | --member=user:${USER_EMAIL} --role=roles/container.admin
118 |
119 | kubectl create clusterrolebinding cluster-admin-binding \
120 | -- clusterrole=cluster-admin --user=${USER_EMAIL}
121 | ```
122 |
123 | - foo, bar 네임스페이스를 만들고 kubectl-proxy 단일 컨테이너의 파드를 각각 네임스페이스에서 실행
124 | ```
125 | kubectl create ns foo
126 | kubectl run test --image=luksa/kubectl-proxy -n foo
127 |
128 | kubectl create ns bar
129 | kubectl run test --image=luksa/kubectl-proxy -n bar
130 | ```
131 |
132 | - 컨테이너 내부로 들어가서 API 호출 테스트
133 | ```
134 | kubectl exec -it ${test_POD_NAME} -n foo sh
135 | ```
136 |
137 | ```
138 | curl localhost:8001/api/v1/namespace/foo/services
139 | ```
140 | - 권한 없다는 에러가 발생하면 RBAC가 잘 동작하는 것
141 |
142 | ### 롤과 롤바인딩 사용하기
143 | #### 롤 정의
144 | ```
145 | apiVersion: rbac.authorization.k8s.io/v1
146 | kind: Role
147 | metadata:
148 | namespace: foo
149 | name: service-reader
150 | rules:
151 | - apiGroups: [""]
152 | verbs: ["get", "list"]
153 | resources: ["services"]
154 |
155 | ```
156 |
157 | - :warning: 리소스는 복수
158 | - 각 리소스의 규칙에 맞게 apiGroups를 설정한다. 서비스는 core apiGroup이라 `""`이라고 표기한다.
159 | - resourceNames 필드를 이용하여 특정 서비스 인스턴스에만 권한을 정의할 수 있다.
160 |
161 | 
162 |
163 | - 롤 생성
164 | ```
165 | kubectl create -f service-reader.yaml -n foo
166 |
167 | kubectl create role servcie-reader --verb=get --verb=list --resource=services -n bar
168 | ```
169 |
170 | #### 롤 바인딩
171 | - 롤 바인딩 생성
172 | ```
173 | kubectl create rolebinding test --role=service-reader --serviceaccount=foo:default -n foo
174 | ```
175 |
176 | 
177 |
178 |
179 | - 롤 바인딩 확인
180 | ```
181 | kubectl get rolebinding test -n foo -o yaml
182 | ```
183 | ```
184 | apiVersion: rbac.authorization.k8s.io/v1
185 | kind: RoleBinding
186 | metadata:
187 | creationTimestamp: 2019-03-24T23:37:24Z
188 | name: test
189 | namespace: foo
190 | ...
191 | roleRef:
192 | apiGroup: rbac.authorization.k8s.io
193 | kind: Role
194 | name: service-reader
195 | subjects:
196 | - kind: ServiceAccount
197 | name: default
198 | namespace: foo
199 | ```
200 |
201 | #### 다른 네임스페이스의 서비스 어카운트 포함하기
202 | - 롤바인딩의 경우 다른 네임스페이스의 서비스어카운트 참조 가능
203 | - 다른 네임스페이스에 롤바인딩이 정의된 네임스페이스의 권한 부여 가능
204 | - bar 네임스페이스에서 foo 네임스페이스에 접근 가능하도록 롤바인딩 수정
205 | ```
206 | kubectl edit rolebinding test -n foo
207 | ```
208 | ```
209 | subjects:
210 | - kind: ServiceAccount
211 | name: default
212 | namespace: foo
213 | - kind: ServiceAccount
214 | name: default
215 | namespace: bar
216 | ```
217 |
218 | 
219 |
220 | - 테스트
221 | ```
222 | k exec -it -n foo ${test_POD_NAME} curl localhost:8001/api/v1/namespaces/foo/services
223 | ```
224 | ```
225 | {
226 | "kind": "ServiceList",
227 | "apiVersion": "v1",
228 | "metadata": {
229 | "selfLink": "/api/v1/namespaces/foo/services",
230 | "resourceVersion": "7378771"
231 | },
232 | "items": []
233 | }
234 | ```
235 |
236 | ```
237 | k exec -it -n bar ${test_POD_NAME} curl localhost:8001/api/v1/namespaces/foo/services
238 | ```
239 | ```
240 | {
241 | "kind": "ServiceList",
242 | "apiVersion": "v1",
243 | "metadata": {
244 | "selfLink": "/api/v1/namespaces/foo/services",
245 | "resourceVersion": "7379089"
246 | },
247 | "items": []
248 | ```
249 |
250 | ### 클러스터롤과 클러스터롤바인딩 사용하기
251 | - 노드, 퍼시스턴트볼륨, 네임스페이스 등의 리소스는 네임스페이스와 연관없는 경우가 있다.
252 | - 특정 URI의 경우 리소스를 표현하지 않는 경우(`healthz`)가 있다.
253 | - 위의 경우에 클러스터롤을 이용하여 권한 제어를 할 수 있다.
254 | - 롤바인딩은 클러스터롤을 바인딩하더라도 클러스터 수준의 리소스에 대한 접근 권한을 부여하지 않는다.
255 |
256 | 
257 |
258 | - 클러스터롤을 이용하여 클러스터 수준에서 파드에 대한 접근 권한을 설정하더라도 롤바인딩으로 바인딩하면 자기 네임스페이스의 파드만 접근 가능하다. (ex, `view` 클러스터롤)
259 |
260 | 
261 |
262 | - 클러스터롤/클러스터롤바인딩의 경우 생성 방법은 롤/롤바인딩과 같다.
263 |
264 | #### 롤, 롤바인딩, 클러스터롤, 클러스터롤바인딩 조합 요약
265 |
266 | 접근 | 롤타입 | 사용할 바인딩 타입
267 | ---|---|---
268 | Cluster-level resources (Nodes, PersistentVolumes, ...) | 클러스터롤 | 클러스터롤바인딩
269 | Non-resource URLs (/api, /healthz, ...) | 클러스터롤 | 클러스터롤바인딩
270 | Namespaced resources in any namespace (and across all namespaces) | 클러스터롤 | 클러스터롤바인딩
271 | Namespaced resources in a specific namespace (reusing the same ClusterRole in multiple namespaces) | 클러스터롤 | 롤바인딩
272 | Namespaced resources in a specific namespace (Role must be defined in each namespace) | 롤 | 롤바인딩
273 |
274 | ### system:discovery 클러스터롤과 클러스터롤바인딩 살펴보기
275 | #### system:discovery - clusterrole
276 | ```
277 | kubenetes get clusterrole system:discovery -o yaml
278 | ```
279 | ```yaml
280 | apiVersion: rbac.authorization.k8s.io/v1
281 | kind: ClusterRole
282 | metadata:
283 | annotations:
284 | rbac.authorization.kubernetes.io/autoupdate: "true"
285 | creationTimestamp: 2019-02-18T09:56:32Z
286 | labels:
287 | kubernetes.io/bootstrapping: rbac-defaults
288 | name: system:discovery
289 | resourceVersion: "45"
290 | selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Adiscovery
291 | uid: 7806fa0b-3363-11e9-ac9d-42010a9201be
292 | rules:
293 | - nonResourceURLs:
294 | - /api
295 | - /api/*
296 | - /apis
297 | - /apis/*
298 | - /healthz
299 | - /openapi
300 | - /openapi/*
301 | - /swagger-2.0.0.pb-v1
302 | - /swagger.json
303 | - /swaggerapi
304 | - /swaggerapi/*
305 | - /version
306 | - /version/
307 | verbs:
308 | - get
309 | ```
310 | - 리소스가 아닌 URL들에 대한 권한을 부여하고 있다.
311 |
312 | #### system:discovery - clusterrolebinding
313 | ```
314 | k get clusterrolebinding system:discovery -o yaml
315 | ```
316 | ```yaml
317 | apiVersion: rbac.authorization.k8s.io/v1
318 | kind: ClusterRoleBinding
319 | metadata:
320 | annotations:
321 | rbac.authorization.kubernetes.io/autoupdate: "true"
322 | creationTimestamp: 2019-02-18T09:56:32Z
323 | labels:
324 | kubernetes.io/bootstrapping: rbac-defaults
325 | name: system:discovery
326 | resourceVersion: "99"
327 | selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/system%3Adiscovery
328 | uid: 78552d89-3363-11e9-ac9d-42010a9201be
329 | roleRef:
330 | apiGroup: rbac.authorization.k8s.io
331 | kind: ClusterRole
332 | name: system:discovery
333 | subjects:
334 | - apiGroup: rbac.authorization.k8s.io
335 | kind: Group
336 | name: system:authenticated
337 | - apiGroup: rbac.authorization.k8s.io
338 | kind: Group
339 | name: system:unauthenticated
340 | ```
341 | - subejects를 보면 인증되거나 인증 되지 않은 사용자 모두에게 권한이 부여됨을 알 수 있다.
342 |
343 | ### 디폴트 클러스터롤과 클러스터롤바인딩 이해하기
344 | #### view 클러스터롤
345 | - 롤, 롤바인딩, 시크릿을 제외한 네임스페이스 내의 대부분의 리소스 읽기 권한
346 | - 시크릿을 볼 수 없는 이유는 기존보더 더 큰 권한의 시크릿을 읽어 권한 상승을 방지하기 위해서이다.
347 | #### edit 클러스터롤
348 | - 네임스페이스의 리소스 뿐만 아니라 시크릿을 읽고 수정 가능한 권한
349 | - 권한 상승을 방지하기 위해 롤 또는 롤바인딩을 보거나 수정하는 것은 허용되지 않는다.
350 |
351 | #### admin 클러스터롤
352 | - ResourceQuotas가 이 롤을 가진 주체
353 | - 네임스페이스 자체를 제외한 네임스페이스 내의 리소스를 읽고 수정 가능하며, 롤과 롤바인딩 또한 수정 가능한 권한
354 |
355 | #### cluster-admin 클러스터롤
356 | - 클러스터의 모든 권한을 가지고 있다
357 |
358 | ### 인증 권한을 현명하게 부여하기
359 | - 최소 권한 원칙
360 | - 각 파드에 특정 서비스어카운트를 작성하고 롤/롤바인딩을 통해 맞춤형롤 제공
361 |
362 | ## 12.3 요약
363 | - API 서버 클라이언트는 사용자, 파드에서 실행중인 앱
364 | - 파드는 서비스어카운트를 가지고 있다
365 | - 사용자와 서비스 어카운트는 그룹에 속한다.
366 | - 롤, 클러스터롤은 어떤 리소스 또는 URI에 대해 수행할 수 있는 작업에 대해 정의한다.
367 | - 롤바인딩, 클러스터바인딩은 롤과 클러스터롤을 그룹 및 서비스어카운트에 바인딩한다.
368 | - 각 클러스터에는 기본 클러스터와 클러스터롤바인딩이 있다.
369 | `
370 |
--------------------------------------------------------------------------------
/k8s-in-action-chap13/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 13
2 |
3 | # Securing cluster nodes and the network
4 | - kubernetes를 활용해 production workload를 구성할 경우 cluster manager와 application 개발자간의 협업을 통해 실제 application logic이 container 환경으로 배포된다.
5 | - application 개발자: application logic을 구현하고 docker image를 만들고 kubernetes resource(deployment, pod, demonSet, StatefulSet, ..)을 만든다. 각 container에서 필요한 node의 kernel기능 및 node의 IPC, PID등을 사용할 수 있도록 명세할 수도 있다. 뿐만아니라 해당 container process가 어떤 userId로 실행할지를 명세할 수 있다.
6 | - cluster manager: PSP(PodSecurityPolicy), NetworkPolicy등 cluster level에서 container process의 기능을 제한하고 pod간 networking을 통제한다.
7 |
8 | ## contents
9 | - application 개발자 입장에서의 container process 기능 확장 및 제한
10 | - cluster manager 입장에서의 cluster 보안
11 |
12 |
13 | ---
14 |
15 | ### linux namespace :: 동일 system내에서 독립된 공간을 사용하는 것처럼 격리된 환경을 제공하는 lightweight 가상화 기술.
16 | - UTS namespace : hostname 을 변경하고 분할
17 | - IPC namespace : Inter-process communication. 프로세스간 통신 격리
18 | - PID namespace : PID (Process ID)를 분할 관리
19 | - NS namepsace : file system 의 mount 지점을 분할하여 격리
20 | - NET namespace : Network interface, iptables 등 network 리소스와 관련된 정보를 분할
21 | - USER namespace : user와 group ID를 분할 격리
22 |
23 | > pod내의 container는 node의 기본 namespace에서 실행중인 process와 다른 namespace의 process에서 실행
24 | > - NET namespace -> 자체 ip 및 port공간
25 | > - PID namespace -> 고유한 process tree
26 | > - IPC namespace -> 동일한 pod process 사이에만 IPC 가능
27 |
28 | ## pod내에서 node namespace 사용
29 |
30 | ### pod이 구동중인 node의 NET interface 사용하기
31 | 
32 | ```yaml
33 | apiVersion: v1
34 | kind: Pod
35 | metadata:
36 | name: alpine
37 | spec:
38 | hostNetwork: true
39 | containers:
40 | - name: main
41 | image: express:1
42 | ```
43 | - pod내의 container가 host network namespace를 사용
44 | - container 환경에선 veth interface(pair interface)를 활용해 외부와 통신. 즉, 내부 process에선 외부 network adpater를 볼 수 없다. -> host의 network interface를 사용하므로 실제 host network adpater를 볼 수 있다.
45 | - kubernetes master의 component가 pod으로 배포됬을 때 ```hostNetwork```를 통해 실제로 pod으로 실행되지 않는 것처럼 효율적으로 동작
46 |
47 | ### host NET namespace를 사용하지 않고 host port에 binding
48 | - container NET interface를 가진 상태에서 node의 기본 namespace에 있는 port에 binding
49 | ```yaml
50 | apiVersion: v1
51 | kind: Pod
52 | metadata:
53 | name: express-app
54 | spec:
55 | containers:
56 | - image: express:1
57 | name: main
58 | ports:
59 | - containerPort: 4000
60 | hostPort: 9000
61 | protocol: TCP
62 | ```
63 | - host port에 binding하는 방법
64 | - hostPort
65 | - service를 통한 NodePort
66 |
67 | 
68 |
69 | - hostPort vs NodePort
70 | - 공통점
71 | - 2개의 process가 동일한 host port에 binding될 수 없다 -> 이미 binding된 process가 있을 때 배포시 pending
72 | - 차이점
73 | - hostPort의 경우 배포된 node의 iptables에만 등록
74 | - NodePort로 배포된 경우 모든 node의 iptables에 등록
75 |
76 | > ### Configuration Best Practices
77 | > Don’t specify a hostPort for a Pod unless it is absolutely necessary. When you bind a Pod to a hostPort, it limits the number of places the Pod can be scheduled, because each combination must be unique. If you don’t specify the hostIP and protocol explicitly, Kubernetes will use 0.0.0.0 as the default hostIP and TCP as the default protocol.
78 |
79 | ### node IPC, PID namespace 사용
80 | ```yaml
81 | apiVersion: v1
82 | kind: Pod
83 | metadata:
84 | name: express-app
85 | spec:
86 | hostPID: true
87 | hostIPC: true
88 | containers:
89 | - name: main
90 | image: nodeapp:2
91 | ```
92 | 
93 | - linux에서는 모든 process들이 각각의 고유한 process id(PID)를 가진다. kernel은 이 process들을 tree형태로 관리하는데 이는 parent-child hierarchy를 가진다는 것을 의미한다.
94 | - hostPID를 가진다는 의미는 host에서 실행중인 모든 process들을 볼 수 있다는 의미이다.
95 | - hostIPC를 통해 pod container process가 node에서 실행 중인 다른 모든 process와 통신할 수 있다.
96 |
97 | ```sh
98 | # PID, IPC 적용 전
99 | $ kubectl exec express-app ps aux
100 | PID USER TIME COMMAND
101 | 1 root 0:00 npm
102 | 18 root 0:00 node app.js
103 | 35 root 0:00 ps aux
104 |
105 | ----------------------------------------
106 |
107 | # PID, IPC 적용 후
108 | $ kubectl exec express-app ps aux
109 | PID USER TIME COMMAND
110 | 1 root 0:02 /sbin/init text
111 | 2 root 0:00 [kthreadd]
112 | 3 root 0:01 [ksoftirqd/0]
113 | 5 root 0:00 [kworker/0:0H]
114 | 7 root 0:17 [rcu_sched]
115 | 8 root 0:00 [rcu_bh]
116 | 9 root 0:00 [migration/0]
117 | 10 root 0:00 [lru-add-drain]
118 | ...
119 | ```
120 |
121 | ## container securityContext 설정
122 | - securityContext를 통해 pod과 container 보안 관련 기능을 설정할 수 있다.
123 | - container process에서 실행할 수 있는 userId 지정
124 | - container가 root로 실행되는 것 방지
125 | - container가 privileged mode로 시행해 node의 kernel에 대한 full access권한 부여
126 | - container process에게 개별 kernel 기능 추가 & 삭제
127 | - SELinux(Security Enhanced Linux) option
128 | - process가 container file system에 쓰지 못하게 하기
129 | ```yaml
130 | # pod level에서의 securityContext
131 | apiVersion: v1
132 | kind: Pod
133 | metadata:
134 | name: express-app
135 | spec:
136 | securityContext:
137 | runAsUser: 405 # guest user
138 | containers:
139 | - name: nginx-container
140 | image: nginx
141 | - image: express:3
142 | name: main
143 | ports:
144 | - containerPort: 4000
145 | hostPort: 9000
146 | protocol: TCP
147 |
148 | # container level에서의 securityContext
149 | apiVersion: v1
150 | kind: Pod
151 | metadata:
152 | name: express-app
153 | spec:
154 | securityContext:
155 | runAsUser: 405 # guest user
156 | containers:
157 | - image: express:3
158 | name: main
159 | ports:
160 | - containerPort: 4000
161 | hostPort: 9000
162 | protocol: TCP
163 | ```
164 | - securityContext는 pod level과 container level에서 작성할 수 있으며 pod level에서 작성시 pod에 구동되는 모든 container에 적용되고 container level에서 override 할 수 있다.
165 |
166 | ```yaml
167 | apiVersion: v1
168 | kind: Pod
169 | metadata:
170 | name: security-context-demo
171 | spec:
172 | securityContext:
173 | runAsNonRoot: true # root user로 실행 방지 ()
174 | fsGroup: 2000 # mount된 volume 소유그룹
175 | supplementalGroups: [666, 777] # user 추가 그룹
176 | volumes:
177 | - name: sec-ctx-vol
178 | emptyDir: {}
179 | containers:
180 | - name: nginx
181 | image: nginx
182 | securityContext:
183 | runAsUser: 1000 # 특정 userId로 container 실행
184 | privileged: true # privileged mode로 실행
185 | capabilities: #
186 | add:
187 | - SYS_TIME
188 | drop:
189 | - CHOWN
190 | - name: sec-ctx-demo
191 | image: gcr.io/google-samples/node-hello:1.0
192 | volumeMounts:
193 | - name: sec-ctx-vol
194 | mountPath: /data/demo
195 | securityContext:
196 | runAsUser: 2000 # 특정 userId로 container 실행
197 | allowPrivilegeEscalation: false
198 | readOnlyRootFilesystem: true
199 | ```
200 | - ```runAsNonRoot: true```일 경우 docker image가 root로 실행된다면 pod container 생성 오류가 난다.
201 | ```sh
202 | $ kubectl get po
203 | NAME READY STATUS RESTARTS AGE
204 | express-app 0/1 CreateContainerConfigError 0 24s
205 |
206 | $ kubectl describe po express-app
207 | ...
208 | Events:
209 | Type Reason Age From Message
210 | ---- ------ ---- ---- -------
211 | Normal Scheduled 57s default-scheduler Successfully assigned express-app to docker-for-desktop
212 | Normal SuccessfulMountVolume 56s kubelet, docker-for-desktop MountVolume.SetUp succeeded for volume "default-token-gqvh5"
213 | Normal SandboxChanged 51s (x2 over 54s) kubelet, docker-for-desktop Pod sandbox changed, it will be killed and re-created.
214 | Normal Pulled 11s (x8 over 55s) kubelet, docker-for-desktop Container image "express:2" already present on machine
215 | Warning Failed 11s (x8 over 55s) kubelet, docker-for-desktop Error: container has runAsNonRoot and image will run as root
216 | ```
217 |
218 | ---
219 |
220 | ## pod 보안 관련 기능 사용 제한
221 | - PSP(PodSecurityPolicy)를 통해 pod의 보안 관련 기능에 대한 사용을 제한할 수 있다.
222 | 
223 | - PSP는 master node의 API server에서 PodSecurityPolicy admission control plugin에 의해 수행된다.
224 | - api server로 pod resource가 배포될 때 PodSecurityPolicy admission control plugin에 의해 구성된 PodSecurityPolicy들을 기반으로 유효성 검사를 한다.
225 | - plugin은 정책에 구성된 default 값을 바탕으로 pod resource를 수정한다.
226 | - pod을 새로 생성하거나 update할 때 반영되므로 기존에 배포된 pod에는 영향을 미치지 않는다.
227 | - non-namespace resource로 RBAC 메커니즘을 통해 특정 사용자 또는 그룹에 clusterRole을 지정해 PSP admission control plugin을 적용시킬 수 있다.
228 |
229 | ### PodSecurityPolicy
230 | - pod이 IPC, PID or hostNetwork를 사용할 수 있는지 여부
231 | - pod이 binding할 수 있는 host port
232 | - container가 실행 할 수 있는 userId
233 | - container가 실핼 할 수 있는 fsGroup
234 | - pod를 privileged mode로 실행할 수 있는지 여부
235 | - default securityContext의 capabilities
236 | - pod이 사용할 수 있는 volume type
237 |
238 | - container가 사용할 수 있는 SELinux label
239 | - container가 쓰기 가능한 root file system을 사용할 수 있는지 여부
240 |
241 | ```yaml
242 | apiVersion: extensions/v1beta1
243 | kind: PodSecurityPolicy
244 | metadata:
245 | name: default
246 | spec:
247 | hostIPC: false # host IPC 사용 x
248 | hostPID: false # host PID 사용 x
249 | hostNetwork: false # host network interface 사용 x
250 | hostPorts: # 1000 ~ 11000, 13000 ~ 14000 port만 사용
251 | - min: 10000
252 | max: 11000
253 | - min: 13000
254 | max: 14000
255 | privileged: false # 권한모드 x
256 | readOnlyRootFilesystem: true # root file system에 쓰기 x
257 | runAsUser: # userId 405
258 | rule: 'MustRunAs'
259 | ranges:
260 | - min: 405
261 | max: 405
262 | fsGroup: # any fsGroup
263 | rule: RunAsAny
264 | supplementalGroups:
265 | rule: RunAsAny
266 | seLinux: # any SELinux group
267 | rule: RunAsAny
268 | volumes: # any volume
269 | - emptyDir
270 | - configMap
271 | - secret
272 | - downwardAPI
273 | - persistentVolumeClaim
274 | ```
275 |
276 | > PodSecurityPolicy는 pod 배포시 api server단에서 resource yaml을 검사하는 것이므로 pod의 securityContext에 명시된 runAsUser가 policy가 허용한 범위내의 userId가 아닌경우 배포가 차단되지만 Dockerfile에 명시된 user인 경우 검사하지 못한다. 만약 policy에 min, max값을 동일하게 해서 특정 userId로 실행하도록 설정되어 있다면 Dockerfile에 명신된 USER를 override해서 실행한다.
277 |
278 | ### linux capabilities 제어
279 | - PodSecurityPolicy의 ```allowedCapabilities, defaultAddCapabilities, requiredDropCapabilities```를 통해 pod의 securityContext의 capabilities를 제어할 수 있다.
280 | ```yaml
281 | apiVersion: extensions/v1beta1
282 | kind: PodSecurityPolicy
283 | spec:
284 | allowedCapabilities:
285 | - SYS_TIME
286 | defaultAddCapabilities:
287 | - CHOWN
288 | requiredDropCapabilities:
289 | - SYS_ADMIN
290 | - SYS_MODULE
291 | ...
292 | ```
293 | - allowedCapabilities: securityContext.capabilities.add에 추가할 수 있는 기능들의 지정 (선택지)
294 | - defaultAddCapabilities: securityContext.capabilities에 default로 추가된다.
295 | - 만약 해당 container에서 기능을 가지기를 원하지 않으면 명시적으로 기능을 제거해야 한다.
296 | - requireDropCapabilities: securityContext.capabilities.drop에 자동으로 추가
297 | - securityContext.capabilities.add에 해당 기능들을 명시한다면 실행을 거부한다.
298 |
299 | ### PodSecurityPolicy with RBAC
300 |
301 | ## pod network 분리
302 | - ingress 정책
303 | - podSelector로 특정 pod만 허용
304 | - namespaceSelector로 특정 ns의 모든 pod 허용
305 | - CIDR를 통해 해당 ip 대역 pod 허용
306 | ```yaml
307 | apiVersion: networking.k8s.io/v1
308 | kind: NetworkPolicy
309 | metadata:
310 | name: postgres-netpolicy
311 | spec:
312 | podSelector:
313 | matchLabels:
314 | app: database
315 | ingress:
316 | - from:
317 | - podSelector:
318 | matchLabels:
319 | app: webserver
320 | ports:
321 | - port: 5432
322 | ```
323 | ```yaml
324 | apiVersion: networking.k8s.io/v1
325 | kind: NetworkPolicy
326 | metadata:
327 | name: shoppingcart-netpolicy
328 | spec:
329 | podSelector:
330 | matchLabels:
331 | app: shopping-cart
332 | ingress:
333 | - from:
334 | - namespaceSelector:
335 | matchLabels:
336 | tenant: manning
337 | ports:
338 | - port: 80
339 | ```
340 | ```yaml
341 | apiVersion: networking.k8s.io/v1
342 | kind: NetworkPolicy
343 | metadata:
344 | name: shoppingcart-netpolicy
345 | spec:
346 | podSelector:
347 | matchLabels:
348 | app: shopping-cart
349 | ingress:
350 | - from:
351 | - ipBlock:
352 | cidr: 192.168.1.0/24
353 | ```
354 |
355 | - egress 정책
356 | ```yaml
357 | apiVersion: networking.k8s.io/v1
358 | kind: NetworkPolicy
359 | metadata:
360 | name: egress-policy
361 | spec:
362 | podSelector:
363 | matchLabels:
364 | app: webserver
365 | egress:
366 | - to:
367 | - podSelector:
368 | matchLabels:
369 | app: database
370 | ```
--------------------------------------------------------------------------------
/k8s-in-action-chap13/ch13_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap13/ch13_01.png
--------------------------------------------------------------------------------
/k8s-in-action-chap13/ch13_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap13/ch13_02.png
--------------------------------------------------------------------------------
/k8s-in-action-chap13/ch13_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap13/ch13_03.png
--------------------------------------------------------------------------------
/k8s-in-action-chap13/ch13_04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap13/ch13_04.png
--------------------------------------------------------------------------------
/k8s-in-action-chap13/ch13_05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap13/ch13_05.png
--------------------------------------------------------------------------------
/k8s-in-action-chap15.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap15.pdf
--------------------------------------------------------------------------------
/k8s-in-action-chap17.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap17.pdf
--------------------------------------------------------------------------------
/k8s-in-action-chap18-3/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | # Chapter 18-3
7 | # 쿠버네티스 기반 플랫폼
8 |
9 | -----------------------------------------
10 |
11 |
12 |
13 | ## RedHat OpenShift Container Platform
14 |
15 | * OpenShift v1,2는 k8s와 관련이 없었지만, v3에서 k8s로 새로 빌드.
16 | * 추가적인 Resource 제공.
17 | * 네트워크까지 격리된 Multi-Tenant 환경 제공.
18 |
19 |
20 |
21 | -----------------------------------------
22 |
23 |
24 |
25 | ## 사용자, 그룹, 프로젝트의 이해
26 |
27 | * 강력한 사용자 관리 기능을 기종.
28 | * 각 사용자가 수행할 수 있거나 수행할 수 없는 작업을 지정 가능.
29 | * 각 사용자는 특정 프로젝트에 엑세스 가능.
30 | * Project는 K8S Namespace Resource에 추가 Annotations로 구현.
31 | * Project의 접근 권한은 클러스터 관리자가 부여.
32 |
33 |
34 |
35 | --------------------------------------
36 |
37 |
38 |
39 | ## Application Template
40 |
41 | * K8S는 단일 매니페스트를 통해 리소스를 배포.
42 | * OpenShift는 이 매니페스트를 매개변수화 할 수 있게 지원.
43 | * 매개 변수화 가능한 목록이 Template.
44 | * OpenShift는 Pre-Defined된 Template을 제공하여, 몇 가지 인수를 지정하면 복잡한 Application을 신속하게 배포할 수 있도록 지원.
45 |
46 |

47 |
48 |
49 |
50 | --------------------------------------
51 |
52 |
53 |
54 | ## Application Template
55 |
56 | ```
57 | # redis-template.yaml
58 |
59 | apiVersion: v1
60 | kind: Template
61 | metadata:
62 | name: redis-template
63 | annotations:
64 | description: "Description"
65 | iconClass: "icon-redis"
66 | tags: "database,nosql"
67 | objects:
68 | - apiVersion: v1
69 | kind: Pod
70 | metadata:
71 | name: redis-master
72 | spec:
73 | containers:
74 | - env:
75 | - name: REDIS_PASSWORD
76 | value: ${REDIS_PASSWORD}
77 | image: dockerfile/redis
78 | name: master
79 | ports:
80 | - containerPort: 6379
81 | protocol: TCP
82 | parameters:
83 | - description: Password used for Redis authentication
84 | from: '[A-Z0-9]{8}'
85 | generate: expression
86 | name: REDIS_PASSWORD
87 | labels:
88 | redis: master
89 | ```
90 |
91 |
92 |
93 | --------------------------------------
94 |
95 |
96 |
97 | ## Application Template
98 |
99 | ```
100 | $ oc process -f redis-template.yaml -p REDIS_PASSWORD=PASSWORD | oc create -f -
101 |
102 | or
103 |
104 | $ oc process -f redis-template.yaml --param-file=redis.env | oc create -f -
105 | ```
106 |
107 |
108 |
109 | --------------------------------------
110 |
111 |
112 |
113 | ## BuildConfigs
114 |
115 | * 해당 리소스를 작성하여 소스 코드로부터 이미지를 자동 생성.
116 | * Source To Image(S2I)
117 | * 소스 코드를 입력으로 사용하여 이미지를 쉽게 작성 및 생성하는 프레임워크
118 | * Build Process
119 | * Sources
120 | * S2I Scripts
121 | * Builder Image
122 |
123 |
124 |
125 | --------------------------------------
126 |
127 |
128 |
129 | ## BuildConfigs
130 |
131 | ```
132 | kind: "BuildConfig"
133 | apiVersion: "v1"
134 | metadata:
135 | name: "ruby-sample-build"
136 | spec:
137 | runPolicy: "Serial"
138 | triggers:
139 | -
140 | type: "GitHub"
141 | github:
142 | secret: "secret101"
143 | - type: "Generic"
144 | generic:
145 | secret: "secret101"
146 | -
147 | type: "ImageChange"
148 | source:
149 | git:
150 | uri: "https://github.com/openshift/ruby-hello-world"
151 | strategy:
152 | sourceStrategy:
153 | from:
154 | kind: "ImageStreamTag"
155 | name: "ruby-20-centos7:latest"
156 | output:
157 | to:
158 | kind: "ImageStreamTag"
159 | name: "origin-ruby-sample:latest"
160 | postCommit:
161 | script: "bundle exec rake test"
162 | ```
163 |
164 |
165 |
166 | --------------------------------------
167 |
168 |
169 |
170 | ## DeploymentConfig
171 |
172 | * 새 컨데이너 이미지가 생성되면 자동으로 클러스터에 배치
173 |
174 |

175 |
176 |
177 |
178 | --------------------------------------
179 |
180 |
181 |
182 | ## Route
183 |
184 | * Domain Name으로 Service를 노출시킴으로써 외부의 Client가 접속할 수 있는 통로 역할을 함(Ingress).
185 | * HAProxy를 이용한 Route가 기본 내장.
186 | * TLS 종료 및 트래픽 분할 등 추가 구성을 제공.
187 |
188 |
189 |
190 |
191 |
192 | --------------------------------------
193 |
194 |
195 |
196 | ## Route
197 |
198 |

199 |
200 |
201 |
202 | --------------------------------------
203 |
204 |
205 |
206 | ## Deis Workflows (Hephy)
207 |
208 | * Kubernetes의 Eco-System.
209 | * PaaS.
210 | * Kubernetes에 Application을 쉽게 배포하고 관리할 수 있도록 지원.
211 |
212 |
213 |
214 | --------------------------------------
215 |
216 |
217 |
218 | ## Helm
219 |
220 | * Kubernetes의 패키지 관리자 (yum, apt, brew)
221 | * Cli(client), Tiller(k8s 내에서 pod으로 실행)로 구성
222 |
223 |

224 |
225 |
226 |
227 | --------------------------------------
228 |
229 |
230 |
231 | ## Helm
232 |
233 | * directory tree
234 |
235 | ```
236 | so-vfc-adapter:
237 | Chart.yaml
238 | resources:
239 | config:
240 | overrides:
241 | override.yaml
242 | templates:
243 | configmap.yaml
244 | deployment.yaml
245 | service.yaml
246 | values.yaml
247 | ```
248 |
249 |
250 |
251 | --------------------------------------
252 |
253 |
254 |
255 | ## Helm
256 |
257 | * templates/service.yaml
258 |
259 | ```
260 | apiVersion: v1
261 | kind: Service
262 | metadata:
263 | name: {{ include "common.servicename" . }}
264 | namespace: {{ include "common.namespace" . }}
265 | labels:
266 | app: {{ include "common.name" . }}
267 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
268 | release: {{ .Release.Name }}
269 | heritage: {{ .Release.Service }}
270 | spec:
271 | type: {{ .Values.service.type }}
272 | ports:
273 | {{if eq .Values.service.type "NodePort" -}}
274 | - port: {{ .Values.service.internalPort }}
275 | nodePort: {{ .Values.global.nodePortPrefix | default .Values.nodePortPrefix }}{{ .Values.service.nodePort }}
276 | name: {{ .Values.service.portName }}
277 | {{- else -}}
278 | - port: {{ .Values.service.externalPort }}
279 | targetPort: {{ .Values.service.internalPort }}
280 | name: {{ .Values.service.portName }}
281 | {{- end}}
282 | selector:
283 | app: {{ include "common.name" . }}
284 | release: {{ .Release.Name }}
285 | ```
286 |
287 |
288 |
289 | --------------------------------------
290 |
291 |
292 |
293 | ## Helm
294 |
295 | * values.yaml
296 |
297 | ```
298 | #################################################################
299 | # Global configuration defaults.
300 | #################################################################
301 | global:
302 | nodePortPrefix: 302
303 | nodePortPrefixExt: 304
304 | repository: nexus3.onap.org:10001
305 | readinessRepository: oomk8s
306 | readinessImage: readiness-check:2.0.0
307 | persistence:
308 | mountPath: /dockerdata-nfs
309 |
310 | #################################################################
311 | # Application configuration defaults.
312 | #################################################################
313 | repository: nexus3.onap.org:10001
314 | image: onap/so/vfc-adapter:1.3.3
315 | pullPolicy: Always
316 |
317 | replicaCount: 1
318 | minReadySeconds: 10
319 | containerPort: 8084
320 | logPath: ./logs/vfc/
321 | app: vfc-adapter
322 | service:
323 | type: ClusterIP
324 | internalPort: 8084
325 | externalPort: 8084
326 | portName: so-vfc-port
327 | updateStrategy:
328 | type: RollingUpdate
329 | maxUnavailable: 1
330 | maxSurge: 1
331 | # Resource Limit flavor -By Default using small
332 | flavor: small
333 | # Segregation for Different environment (Small and Large)
334 | resources:
335 | small:
336 | limits:
337 | memory: 4Gi
338 | cpu: 2000m
339 | requests:
340 | memory: 1Gi
341 | cpu: 500m
342 | large:
343 | limits:
344 | memory: 8Gi
345 | cpu: 4000m
346 | requests:
347 | memory: 2Gi
348 | cpu: 1000m
349 | unlimited: {}
350 | livenessProbe:
351 | path: /manage/health
352 | port: 8084
353 | scheme: HTTP
354 | initialDelaySeconds: 600
355 | periodSeconds: 60
356 | timeoutSeconds: 10
357 | successThreshold: 1
358 | failureThreshold: 3
359 | mariadb:
360 | nameOverride: so-mariadb
361 | ingress:
362 | enabled: false
363 | nodeSelector: {}
364 | tolerations: []
365 | affinity: {}
366 | ```
367 |
368 |
--------------------------------------------------------------------------------
/k8s-in-action-chap18-3/openshift-route.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap18-3/openshift-route.png
--------------------------------------------------------------------------------
/k8s-in-action-chap18-3/platform-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap18-3/platform-1.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap18-3/platform-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap18-3/platform-2.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap18-3/platform-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap18-3/platform-3.jpg
--------------------------------------------------------------------------------
/k8s-in-action-chap18.md:
--------------------------------------------------------------------------------
1 | <<<<<<< Updated upstream
2 | # 18장. 쿠버네티스 확장하기
3 | - 거의 다옴
4 |
5 | ## 18.1 사용자 지정 API 객체 정의하기 Defining Custom API Object
6 | - 쿠버네티스 에코시스템이 진화함에 따라, 하이레벨의 객체를 볼것이다.
7 | - 쿠버네티스가 지금 지원하는 리소스보다 더 구체화 될것이이다.
8 | - 디플로이먼트 서비스 Configmap 을 다루는 대신에
9 | 전체 어플리캐션 이나 소프트웨어서비스를 나타내는 객체를 만들고 관리할것이다.
10 |
11 | 예를 들어 쿠버네티스 클러스트에서 메시징 브로커를 실행하려면 큐 리소스의 인스턴스를 만들기만 하면 되며 모든 시크릿 ,디플로이먼트 , 서비스 가 사용자 지정 리소스 컨트롤러에 의해 생성된다.
12 | 쿠버네티스는 이미 이와 같은 사용자 리소스를 추가하는 방법을 제공하고 있다.
13 |
14 |
15 | ### 18.1.1 CustomResourceDefinitions 소개
16 | - 새로운 리소스 유형을 정의하려면 CustomResourceDefinition 객체 CRD 를 쿠버네티스
17 | API 서버에 게시만 하면 된다.
18 |
19 | - 사용자 지저 리소스 유형에 대한 설명이다.
20 |
21 | - CRD가 게시되면 사용자는 다른 리소스와 마친가지로 JSON 또는 YAML매니페스트를 API에 게시해 사용자 지정 리소스의 인스턴스를 만들 수 있다.
22 |
23 | `쿠버네티스 1.7 이전에는 CRD 와 유사하지만 1.8에서 제거된 Third-PartyResource 객체를 통해 사용자 리소스를가 정의됐다.`
24 |
25 | - 사용자가 새로운 유형의 객체를 만들 수 있도록 CRD를 생성하는 것은 클러스터에서 가시적인 일이 발생하지 않는 경우 유용한 기능이 아니다.
26 |
27 | - 각 CRD 에는 대게 관련 컨트롤러가 있다. 이는 모든 핵심 쿠버네티스 리소스에 연결된 컨트롤러가 있는 것과 같은 방식
28 |
29 | - 사용자 지정 객체 인스턴스 추가하는것 외에 CRD 에서 수행할 수 있는 작업을 제대로 표시하려면 컨트롤러도 배포해야 한다.
30 |
31 | #### CustomResourceDefinition 예제 소개
32 |
33 | 1. 팟, 서비스 및 기타 Kubernetes 리소스를 다루지 않고도 Kubernetes 클러스터 사용자가 가능한 한 쉽게 정적 웹 사이트를 실행할 수 있도록 허용한다고 가정 해 봅시다. 당신이 얻고 자하는 것은 사용자가 웹 사이트의 이름과 웹 사이트의 파일 (HTML, CSS, PNG 및 기타)을 가져와야하는 소스 이상을 포함하는 유형의 웹 사이트 개체를 만드는 것입니다.
34 | 2. Git 저장소를 해당 파일의 소스로 사용합니다.
35 | 3. 사용자가 웹 사이트 리소스의 인스턴스를 생성하면 Kubernetes가 그림 18.1과 같이 새로운 웹 서버 팟을 회전시켜 서비스를 통해 노출 시키길 원할 것입니다.
36 | 4. 웹 사이트 리소스를 만들려면 사용자가 다음 목록에 표시된 줄에 매니페스트를 게시해야합니다.
37 |
38 | 
39 | **각각의 웹사이트 객체는 서비스 및 HTTP 서버 팟을 생성해야 한다.**
40 |
41 | `가상의 사용자 지정 리소스 : imaginary-kubia-website.yml p.733`
42 | ```
43 | kind: Website
44 | metadata:
45 | name: kubia
46 | spec:
47 | gitRepo: https://github.com/luksa/kubia-website-example.git
48 | ```
49 | - 리소스에는 kind 및 metadata.name 필드가 포함돼 있다.
50 | - 대부분의 리소스와 마찬가지로 spec 세션 포함
51 | - -it는 웹사이트의 파일이 들어있는 git repo를 지정
52 | - appVersion 필드를 포함해야 하지만 사용자 지정 리소스의 값이 무언이지 아직 모른다.
53 | - 이 리소스를 쿠버네티스에 게시하려고 하면 쿠퍼네티스가 웹사이트 객체가 아직 무엇인지 알지 못하기 때문에 오류 발생
54 |
55 | ```
56 | $ kubectl create -f imaginary-kubia-website.yaml
57 | error: unable to recognize "imaginary-kubia-website.yaml": no matches for
58 | ➥ /, Kind=Website
59 | ```
60 | `사용자 지정 객체의 인스턴스를 만들기 전에 쿠버네티스가 해당 객체를 인식 하도록 해야함`
61 |
62 | #### CustomResourceDefinition 객체 생성 객체 생성
63 | - 쿠버네티스가 사용자 지정 웹사이트 리소스 인스턴스 허용하려면 CRD를 API 서버에 게시해야햠
64 |
65 | `CustomerResourceDefinition 매니페스트 : website-crd.yml p.734`
66 | ```
67 | apiVersion: apiextensions.k8s.io/v1beta1
68 | kind: CustomResourceDefinition
69 | metadata:
70 | name: websites.extensions.example.com
71 | spec:
72 | scope: Namespaced
73 | group: extensions.example.com
74 | version: v1
75 | names:
76 | kind: Website
77 | singular: website
78 | plural: websites
79 | ```
80 |
81 | `디스크립터를 쿠버네티스에 게시하면 사용자 지정 웹사이트 리소스의 인스턴스를 원하는 만큼 만들 수 있음`
82 |
83 | ```
84 | $ kubectl create -f website-crd-definition.yaml
85 | customresourcedefinition "websites.extensions.example.com" created
86 | ```
87 | - CRD 긴이름을 사용하는 이유는 충돌 방지 위함
88 |
89 | #### 사용자 지정 리소스의 인스턴스 생성
90 |
91 | `사용자 지정 웹사이트 리소스 kubia-website.yml p.735`
92 | ```
93 | apiVersion: extensions.example.com/v1
94 | kind: Website
95 | metadata:
96 | name: kubia
97 | spec:
98 | gitRepo: https://github.com/luksa/kubia-website-example.git
99 | ```
100 |
101 | ```
102 | $ kubectl create -f kubia-website.yaml
103 | website "kubia" created
104 | ```
105 |
106 | #### 사용자 지정 리소스의 인스턴스 검색
107 | - 클러스터의 웹사이트 모드를 나열해보자
108 |
109 | ```
110 | $ kubectl get websites
111 | NAME KIND
112 | kubia Website.v1.extensions.example.com
113 | ```
114 |
115 | `API 서버에서 가져온 전체 웹사이트 리소스 정의`
116 |
117 | ```
118 | $ kubectl get website kubia -o yaml
119 | apiVersion: extensions.example.com/v1
120 | kind: Website
121 | metadata:
122 | creationTimestamp: 2017-02-26T15:53:21Z
123 | name: kubia
124 | namespace: default
125 | resourceVersion: "57047"
126 | selfLink: /apis/extensions.example.com/v1/.../default/websites/kubia
127 | uid: b2eb6d99-fc3b-11e6-bd71-0800270a1c50
128 | spec:
129 | gitRepo: https://github.com/luksa/kubia-website-example.git
130 | ```
131 |
132 |
133 | #### 사용자 지정 리소스의 인스턴스 삭제
134 | - 사용장 지정 객체 인스턴스 삭제도 할 수 있다.
135 |
136 | ```
137 | $ kubectl delete website kubia
138 | website "kubia" deleted
139 | ```
140 |
141 | - CustomResourceDefinition 객체 생성하면 API를 서버를 통해 사용자 지정 객체를 저장,검색, 삭제할 수 있다.
142 | - 뭔가를 동작하게 하려면 컨트롤러를 만들어야 한다.
143 |
144 | - 일반적으로 사용자 지정 객체를 생성하는 것이 항상 객체를 생성할 때 일어나는것은 아니다.
145 | - 특정 사용자 지정 객체는 ConfigMap과 같은 더 일반적인 메카니즘을 사요하는 데신 데이터를 저장하는데 사용된다.
146 | - 팟 내부에서 실행되는 어플리케이션은 해당 객체의 API서버를 조회하고 그 객체에 저장된 객체를 읽을 수 있다. 그러나 이경우 웹사이트 객체가 존재함으로 인해 객체에서 참조회 Git 내용을 제공하는 웹서버가 스핀업 하게 된다.
147 |
148 |
149 | ### 18.1.2 사용자 지정 컨트롤러를 사용한 사용자 지정 리소스 자동화
150 | - 웹사이트 객체가 서비스를 통해 노출된 웹서버 팟을 실행하게 하려면 웹사이트 컨트롤러를 빌드하고 배포해야 한다.
151 | 
152 |
153 |
154 | #### 웹사이트 컨트롤러가 하는일
155 | 시작하면 컨트롤러는 다음 URL로 웹사이트 객체 감시
156 |
157 | `http://localhost:8001/apis/extensions.example.com/v1/websites?watch=true`
158 |
159 | - API 서버에 직접 연결하지 않음
160 | - 동일한 팟의 사이트카 컨테이너에서 실행되고 API 서버의 앰배서더 역할을 하는 kubectl 프록시 프로세스에 연결
161 | - 프로시는 TLS암호화 및 인증을 처리하면서 요청을 API 서버로 전달.
162 |
163 | 
164 |
165 | - HTTP GET 요청에 의해 API 서버는 모든 웹사이트 객체의 변경시마마다 감시 이벤트를 보낸다.
166 | - API 서버는 새로운 웹사이트 객체가 만들어 질 때마다 ADDED 감시 이벤트를 보낸다.
167 | - 컨트롤러는 ADDED를 받으면 watch 이벤트에 받은 객체에서 웹사이트 이름 Git Repo URL 추출 JSON 매니페스트를 API서버에 게시하여 디플로이먼트 및 서비스 객체를 만든다.
168 |
169 | 
170 | - 디플로이먼트 리소스에는 두개의 컨테이너 가 있는 팟 템플릿이 있다.
171 | - 하나는 nginx 서버를 실행 다른 하나는 gitsync 프로세스를 실행해 로컬 Git Repo를 동기화댄 상태로 유지
172 | - 로컬 디렉터리가 emptyDir 볼륨을 통해 nginx를 컨테이너와 공유 한다.
173 | - 서비스는 각 노드의 임의 포트(모든 노드에서 동일한 포트 사용)를 통해 웹서버 팟을 호ㅉ출하는 노드 포트 서비스
174 | - 팟이 디플로이먼트 객체에 의해 생성되면 클라이언트는 노드 포트를 통해 웹사이트에 엑세스 할 수 있다.
175 | - API 서버는 웹사이트 리소스 인스턴스 삭제될 때 DELETED 감시 이벤트 보냄 이벤트 수신시 컨트롤러는 이전에 작성한 디플로이먼트 밋 서비스 리소스를 삭제한다.
176 | - 사용자가 웹사이트 인스턴스를 삭제하면 컨트롤러는 해당 웹사이트 게공하는 웹서버 종료 및 제거
177 |
178 |
179 |
180 | #### 팟으로 컨트롤러 실행
181 | - 개발 과정에서 로컬의 개발 래탑에서 컨트롤러를 실행하고 쿠버네티스 API 서버의 앰배서더로 로컬에서 실행되는 kubectl 프록시 프로세스 (팟으로 실행되지 않음)를 사용.
182 | - 소스 코드 변경시마다 컨테이너 이미지를 만들후 쿠버네티스에서 실행할 필요가 없었기에 빨리 개발 할 수 있었다.
183 | - 프로덕션에 컨트롤러를 배포가 준비 되었을때. 가장 좋은 방법은 쿠버네티스 자체적으로 컨트롤러를 실행하는 것이다.
184 |
185 | `웹사이트 컨트롤러 디플로이먼트 : webside-controller.yaml p.741`
186 |
187 | ```
188 | apiVersion: apps/v1beta1
189 | kind: Deployment
190 | metadata:
191 | name: website-controller
192 | spec:
193 | replicas: 1
194 | template:
195 | metadata:
196 | name: website-controller
197 | labels:
198 | app: website-controller
199 | spec:
200 | serviceAccountName: website-controller
201 | containers:
202 | - name: main
203 | image: luksa/website-controller
204 | - name: proxy
205 | image: luksa/kubectl-proxy:1.6.2
206 | ```
207 | - 디플로이먼트에서 두 컨테이너 팟의 단일 복제본을 배포한다.
208 | - 하나의 컨테이너는 컨트롤러를 실행한다.
209 | - 다른 컨테이너는 API 서버와의 좀 더 간다한 통신에 사용되는 앰베서더 컨테이너이다.
210 | - 팟 자체 ServiceAccount 아래에서 실행되기 때문에 컨트롤러 배포전 팟을 만들어야 한다.
211 |
212 | ```
213 | $ kubectl create serviceaccount website-controller
214 | serviceaccount "website-controller" created
215 | ```
216 |
217 | - Role Based Access Control (RBAC) 을 사용하는 경우 쿠버네티스는 컨트롤러가 웹사이트 리소스를 보거나 디플로이먼트 또는 서비스를 만들지 못하게 합니다.
218 | - 이를 강킁헤 하려면 클러스트롤바인딩을 생성해 웹사이트 컨트롤러 ServiceAccount를 clust-admin 클러스트롤에 바인드 해야 한다.
219 |
220 | ```
221 | $ kubectl create clusterrolebinding website-controller
222 | ➥ --clusterrole=cluster-admin
223 | ➥ --serviceaccount=default:website-controller
224 | clusterrolebinding "website-controller" created
225 | ```
226 |
227 | `ServiceAccount와 클러스터롤바인딩을 제자리에 두면 컨트롤러의 디플로이먼트를 배포할 수 있다`
228 |
229 | #### 동작죽인 컨트롤러 보기
230 | - 컨트롤러 실행 중일 때 kubia 웹사이트 리소스를 다시 만든다.
231 |
232 | ```
233 | $ kubectl create -f kubia-website.yaml
234 | website "kubia" created
235 | ```
236 |
237 | ```
238 | $ kubectl logs website-controller-2429717411-q43zs -c main
239 | 2017/02/26 16:54:41 website-controller started.
240 | 2017/02/26 16:54:47 Received watch event: ADDED: kubia: https://github.c...
241 | 2017/02/26 16:54:47 Creating services with name kubia-website in namespa...
242 | 2017/02/26 16:54:47 Response status: 201 Created
243 | 2017/02/26 16:54:47 Creating deployments with name kubia-website in name...
244 | 2017/02/26 16:54:47 Response status: 201 Created
245 | ```
246 | `복붙 오졌다. 2017년`
247 |
248 | ```
249 | $ kubectl get deploy,svc,po
250 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
251 | deploy/kubia-website 1 1 1 1 4s
252 | deploy/website-controller 1 1 1 1 5m
253 |
254 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
255 | svc/kubernetes 10.96.0.1 443/TCP 38d
256 | svc/kubia-website 10.101.48.23 80:32589/TCP 4s
257 |
258 | NAME READY STATUS RESTARTS AGE
259 | po/kubia-website-1029415133-rs715 2/2 Running 0 4s
260 | po/website-controller-1571685839-qzmg6 2/2 Running 1 5m
261 | ```
262 |
263 |
264 | ### 18.1.3 사용자 지정 객체 검증하기
265 | - 웹사이트 CustomResourceDefinition 에서 유효성 검사 스키마도 지정하지 않았다.
266 | - 사용자는 웹사이트 객체에 원하는 필드를 포함 시킬수 있다.
267 | - API 서버는 apVersion, kind, metadata 같은 일반적인 필트 제외하고 YAML 내용을 확인하지 않으므로 잘못된 웹사이트 객체를 만들수 있다 (ex: gitRepo 필드 없이)
268 | - 컨트롤러에 유효성 추가하고 잘못된 객체를 API 서버에서 거부 할 수 없다.
269 | - API 서버가 객체 저장후 다음 클라이언트(kubeclt)에 성공 응답을 반화하고 모든 감시자에게 알린다. (컨트롤도 객체중 하나)
270 | - 컨트롤러는 감시 이벤트에서 객체 수신시 객체를 검증하고 문제 발생시 웹사이트 객체에 오류 메지지를 작성한다.
271 | - 사용자에게 오류를 자동으로 알리지 않는다.
272 | - 웹사이트 객체에 대한 API서버를 쿼리해 오류 메세지를 확인해야 한다.
273 | - API 서버에서 객체의 유효성 검사후 잘못된 객체를 즉시 거부하도록 할 수 있다.
274 | - 사용자 지정 객체의 유효성 검사는 알파 기능으로 1.8에 도입됐다.
275 | - API 서버에서 CRD 검증하려면 API 서버에서 CustomeResourceValidation 기능 게이트를 사용하도록 설정하고 CRD에서 JSON 스키마를 지정해야 한다.
276 |
277 | ### 18.1.4 사용자 지정 객체에 사용자 지정 API 서버 제공
278 | - 쿠버네티스에서 사용자 지정 객체의 지원을 추가하는 더 좋은 방법은 자체 API 서버를 구현하고 클라이언트가 직접 API 서버와 통신토록 하는 것이다.
279 |
280 |
281 | #### API 서버 에그리게이션 소개
282 | - 1.7 버전에서는 API 서버 에그리게이션을 통해 사용자 지정 API 서버를 주요 쿠퍼네티스 API서버와 통합할 수 있다. (p.745)
283 |
284 | 
285 |
286 | - 웹사이트 객체 처리를 담당하는 API서버를 만들수 있다.
287 | - 코어 쿠버네티스 API 서버가 유효성을 검사하는 방식으로 개당 객체의 유효성을 검사할 수 있다.
288 |
289 | #### 사용자 지정 API 서버 등록
290 | - 사용자 지정 API서버를 클러스터에 추가하려면 API 서버를 팟으로 배포하고 서비스를 통해 익스포즈
291 | - 메인 API 서버에 통합하려면 APIService 리소스를 설명하는 YAML 매니페스트를 배포하면 된다.
292 |
293 | ```
294 | apiVersion: apiregistration.k8s.io/v1beta1
295 | kind: APIService
296 | metadata:
297 | name: v1alpha1.extensions.example.com
298 | spec:
299 | group: extensions.example.com
300 | version: v1alpha1
301 | priority: 150
302 | service:
303 | name: website-api
304 | namespace: default
305 | ```
306 | - APIService 리소스를 생성한 후 클라이언트 요청을 extensions.example.com API 그룹의 리소스를 포함하고 있는 주 API 서버로 전송했다.
307 | - 버전 v1/alpha1은 website-api 서비스를 통해 노출되고 있는 사용자 정의 API 서버 팟으로 전달된다.
308 |
309 |
310 | ## 18.2 쿠버네티스 서비스 카탈로그를 이용한 쿠버네티스 확장
311 | ## 18.3 쿠버네티스 기반 플랫폼 (김동현님의 생생한 실무 이야기)
312 |
--------------------------------------------------------------------------------