├── .gitignore ├── README.md ├── k8s-in-action-chap02.pdf ├── k8s-in-action-chap04.md ├── k8s-in-action-chap05 ├── README.md ├── service-1.jpg ├── service-10.jpg ├── service-11.png ├── service-12.png ├── service-13.png ├── service-14.jpg ├── service-15.png ├── service-16.png ├── service-17.png ├── service-18.jpg ├── service-19.jpg ├── service-2.png ├── service-20.jpg ├── service-3.jpg ├── service-4.png ├── service-5.png ├── service-6.png ├── service-7.png ├── service-8.jpg └── service-9.jpg ├── k8s-in-action-chap06.md ├── k8s-in-action-chap08.md ├── k8s-in-action-chap09.md ├── k8s-in-action-chap10.md ├── k8s-in-action-chap10.pdf ├── k8s-in-action-chap11 ├── README.md ├── architecture-01.png ├── architecture-02.jpg ├── architecture-03.jpg ├── architecture-04.jpg ├── architecture-05.jpg ├── architecture-06.jpg ├── architecture-07.jpg ├── architecture-08.jpg ├── architecture-09.jpg ├── architecture-10.jpg ├── architecture-11.jpg ├── architecture-12.jpg ├── architecture-13.jpg ├── architecture-14.jpg ├── architecture-15.jpg ├── architecture-16.jpg ├── architecture-17.jpg ├── architecture-18.jpg └── architecture-19.jpg ├── k8s-in-action-chap12.md ├── k8s-in-action-chap13 ├── README.md ├── ch13_01.png ├── ch13_02.png ├── ch13_03.png ├── ch13_04.png └── ch13_05.png ├── k8s-in-action-chap15.pdf ├── k8s-in-action-chap17.pdf ├── k8s-in-action-chap18-3 ├── README.md ├── openshift-route.png ├── platform-1.jpg ├── platform-2.jpg └── platform-3.jpg └── k8s-in-action-chap18.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2019년 신림프로그래머 Kubernetes 스터디 2 | 3 | ## Part-1 4 | 5 | ### 스터디 시간 및 장소 6 | - 시간 : 2019년 1월 10일 ~ 2019년 2월 21일, 매주 목요일 저녁 8시에서 10시, 총 6주 7 | - 장소 : [서울대 입구역 토즈](https://moim.toz.co.kr/branchDetail?path=%25EA%25B3%25B5%25EA%25B0%2584%25EC%25B0%25BE%25EA%25B8%25B0&url=%252FboothSearch&branch_id=26&boothprofile_id=159) 8 | 9 | 10 | ### 스터디 방법 11 | - 각 장별로 발표자를 선정 12 | - 스터디 당일 장당 한시간정도 범위로 발표 진행 13 | - 자유로운 방식으로 진행 (ex. 슬라이드쇼형 발표자료 작성, 데모 시연 등) 14 | - 발표한 내용을 [현재 리포지토리](https://github.com/sillim-programmer/kubernetes-in-action-study)에 푸시 15 | - 매일 두장 정도 분량을 진행 16 | 17 | ### 스케줄 18 | 19 | #### 오리엔테이션 20 | #### 1주차 21 | - 2장 [도커와 쿠버네티스의 첫걸음](k8s-in-action-chap02.pdf) : 송성곤 22 | - 3장 포드: 쿠버네티스에서 컨테이너 실행하기 : 남상균 23 | #### 2주차 24 | - 4장 [레플리케이션과 그밖의 컨트롤러: 포드 배포 관리](k8s-in-action-chap04.md) : 박진수 25 | - 5장 [서비스: 클라이언트가 포드를 검색하고 통신을 가능하게 함](k8s-in-action-chap05) : 김동현 26 | #### 3주차 27 | - 6장 [볼륨: 컨테이너에 디스크 스토리지 연결](k8s-in-action-chap06.md) : 오학섭 28 | - 7장 ConfigMap과 시크릿: 애플리케이션 설정 : 장인수 29 | #### 4주차 30 | - 8장 [애플리케이션에서 포드 메타 데이터와 그 외의 리소스에 접근하기](k8s-in-action-chap08.md) : 이재성 31 | - 9장 [배포: 애플리케이션을 선언적으로 업데이트하기](k8s-in-action-chap09.md) : 최범균 32 | #### 5주차 33 | - 10장 [스테이트풀셋: 복제된 스테이트풀 애플리케이션 복제하기](k8s-in-action-chap10.md) : 우여명 34 | #### 뒷풀이 35 | 36 | 37 | ## Part-2 38 | 39 | ### 스터디 시간 및 장소 40 | - 시간 : 2019년 3월 18일 ~ 2019년 4월 22일, 매주 일요일 저녁 8시에서 10시, 총 6주 41 | - 장소 : [서울대 입구역 토즈](https://moim.toz.co.kr/branchDetail?path=%25EA%25B3%25B5%25EA%25B0%2584%25EC%25B0%25BE%25EA%25B8%25B0&url=%252FboothSearch&branch_id=26&boothprofile_id=159) 42 | 43 | ### 스터디 방법 44 | - 각 장별로 발표자를 선정 45 | - 스터디 당일 장당 한시간정도 범위로 발표 진행 46 | - 자유로운 방식으로 진행 (ex. 슬라이드쇼형 발표자료 작성, 데모 시연 등) 47 | - 발표한 내용을 [현재 리포지토리](https://github.com/sillim-programmer/kubernetes-in-action-study)에 푸시 48 | - 매일 두장 정도 분량을 진행 49 | 50 | ### 추가 규칙 51 | - 본인 발표 당일에 안나오면 벌금 만원 (일주일전에 양해를 구할 경우 봐줌) 52 | 53 | ### 스케줄(초안) 54 | 55 | #### 오리엔테이션 56 | 57 | #### 1주차 58 | - 11장 [쿠버네티스 내부 이해하기](k8s-in-action-chap11) : 김동현 59 | 60 | #### 2주차 61 | - 12장 [쿠버네티스 API 서버 보안](k8s-in-action-chap12.md) : 우여명 62 | - 13장 [클러스터 노드와 네트워크 보안](k8s-in-action-chap13) : 김동진 63 | 64 | #### 3주차 65 | - 14장 포드의 계산 리소스 관리 : 이재성 66 | - 15장 [포드와 클러스터 노드의 오토스케일링](k8s-in-action-chap15.pdf) : 송성곤 67 | 68 | #### 4주차 69 | - 16장. 고급 스케줄링 : 김현민 70 | - 17장 [애플리케이션 개발을 위한 베스트 프랙티스](k8s-in-action-chap17.pdf) : 최용호 71 | 72 | 73 | #### 5주차 74 | - 18장 쿠버네티스 확장히기 : 박진수 75 | - Helm, Rancher, Istio 중 발표 76 | 77 | #### 뒷풀이 78 | 79 | 80 | -------------------------------------------------------------------------------- /k8s-in-action-chap02.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap02.pdf -------------------------------------------------------------------------------- /k8s-in-action-chap04.md: -------------------------------------------------------------------------------- 1 | # 4. 레블리케이션과 그 밖의 컨트롤러: 팟 배포 관리 2 | 3 | ``` 4 | - 팟을 안정적으로 유지 5 | - 동일한 팟의 여러 인스턴스 실행 6 | - 노드 실패 시 자동으로 팟 다시 스케줄링 7 | - 수평으로 팟 스케일링 8 | - 각 클러스터 노드에서 시스템 수준 팟 실행 9 | - 배치 10 | - 작업을 주기적으로 또는 한 번씩 실행하도록 스케줄링 11 | ``` 12 | 13 | ## 실제 환경에서는 배포한 애클리케이션이 자동으로 로드하여 실행되고 수동으로 개입하지 않아도 정상적으로 안정적 상태를 유지하길 원할 것이다. 래플리케이션 컨트롤러 또는 디플로이먼트 같은 유형의 리소스를 생성해 실제 포드를 관리하도로 한다. 14 | 15 | ## 1. 팟을 안정적으로 유지 16 | - 팟이 노드에 스케줄되는 대로, 해당 노드의 Kubeletdms 컨테이너를 실행하고 팟이 존재하는 한 계속 실행된다. 17 | `컨테이너 크래시 발생하면 Kubelet 이 컨테이너를 다시 시작 해준다.` 18 | `어플리케이션 버그 발생시 쿠버네티스가 자동으로 다시 시작후 자동 복구` 19 | `어플리케이션이 무한루프나 교착상태에 빠져 응답을 멈추는 상태라면 외부에서 체크 하도록 해야함` 20 | 21 | ### 1.1 라이브니스 프로브 소개 22 | - 쿠버네티스는 라이브니스 프로브를 통해 컨테이너 헬스 체크 23 | - 팟 사양에서 각 컨테이너에 라이브니스 프로브를 지정할 수 있다. 24 | - 쿠버네티스는 세가지중 하나를 사용해 컨테이너를 검색한다. 25 | A. HTTP GET 프로브는 지정한 IP 주소로 HTTP GET을 요청한다. Status 2xx 또는 3xx 을 응답하지 않으면 컨테이너 재시작 26 | B. TCP 소켓 프로브가 지정된 포트로 TCP 연결시도 실패시 컨테이너가 다시 시작 27 | C. Exec 프로브는 컨테이너 내부에 임의의 명령을 실행 종료후 상태코드 0 이 아니면 오류로 간주 28 | 29 | #### 1.2 HTTP 기반 프로브 생성 30 | 31 | `kubia-liveness-probe.yml` 32 | ``` 33 | apiVersion: v1 34 | kind: Pod 35 | metadata: 36 | name: kubia-liveness 37 | spec: 38 | containers: 39 | - image: luksa/kubia-unhealthy 40 | name: kubia 41 | livenessProbe: 42 | httpGet: 43 | path: / 44 | port: 8080 45 | ``` 46 | `kubectl create -f kubia-liveness-probe.yml` 47 | 48 | #### 1.3 동작중인 라이브니스 프로브 보기 49 | `kubectl get po kubi-liveness` 50 | 51 | `kubectl get pods` 52 | 53 | `kubectl k kubia-liveness --previous` 54 | 55 | `kubectl describe po kubia-liveness` 56 | 57 | `kubectl delete -f kubia-liveness-probe.yml` 58 | - exit code 137 128 + 9 59 | 60 | #### 1.4 라이브니스 프로브의 추가 속성 구성 61 | - kubectl describe 라이브니스 프로브의 추가정보 표시 62 | `Liveness: http-get http://:8080/ delay=0s timeout=1s period=10s #success=1 #failure=3` 63 | 64 | 65 | - yaml에 initialDelaySeconds : 15 15초 지연됨 66 | 67 | - 어플리케이션 시작시간을 고려하여 항상 초기 지연을 설정하는 것을 잊지 말자 68 | 69 | - 종료 코드 137 은 외부 신호에 의해 종료 됐음을 알려준다. (종료 코드 128 + 9(SIGKILL)) 70 | - 143 = 128 + 15 (SIGTERM) 71 | 72 | #### 1.5 효과적인 라이브니스 프로브 생성 73 | - 운영 환경에서 실행 중인 팟의 경우 라이브니스 프로브를 정의해야 한다. 그래야 쿠버네티스가 어플리케이션 생사여부를 알수 있다. 74 | - 프로브 구성시 /health 같은 특정 엔드포인트를 설정하고 인증이 없도록 한다. (DB가 문제인 경우도 고려해야 함) 75 | - 라이브니스 프로브는 가볍게 만든다. 76 | - 자바일 경우 Exec 프로브 대신 HTTP GET 프로브를 사용해야 한다. ? P. 157 77 | - 프로브에서 반복 루푸를 구현하지 말라 78 | 79 | ## 2. 레플리케이션 컨트롤러 80 | - 팟이 항상 실행되도록 유지하는 쿠버네티스 리소스 81 | - 어떤 이유로든 팟이 사라지면 레플리케이션컨트롤러는 누락된 팟을 감지하고 대체 팟을 만든다. 82 | - P 159 그림 4.1 노드 실패시 레플리케이션 컨트롤러는 사라진 팟 B를 대체할 새로운 팟 B2 를 생성하지만 A는 빠이빠이 83 | - 레플리케이션컨트롤러는 단일 팟만 관리하지만 일반적으로 레플리케이션 팟은 복제본을 만들고 관리할 수 있다 --> 레플리 케이션컨트롤러 이름 유래 84 | 85 | ### 2.1 레플리케이션 컨트롤러의 동작 86 | - 실행중인 팟 목록을 지속적으로 모니터링 87 | - 유형의 실제 팟 수가 원하는 수와 일치 하는지 확인 88 | - 너무 많으면 제거 적으면 복제 생성 89 | 90 | > **복제본이 더많이 생성될경우** 91 | - 수동으로 동일한 유형의 팟을 만든경우 92 | - 기존 팟 유형을 변경한 경우 93 | - 원하는 팟 수를 줄였을 경우 등 94 | 95 | - 리플리케이션 컨트롤러는 팟 유형을 가지고 동작하지 않지만 특정 라벨 셀렉터와 일치하는 팟 세트에서 동작한다. 96 | - 컨트롤러의 연결 고리 소개 (P.160 그림 ) 97 | - 리플리케이션컨트롤러 세가지 요소 (P.161 그림) 98 | - 레플리케이션 컨트롤러 범위에 있는 팟을 결정하는 라벨 셀렉터 99 | - 원하는 팟 수를 지정하는 복제본수 100 | - 새로운 팟 복제본을 만들테 사용되는 팟 템플릿 101 | - 복제본수 , 라벨 셀레터 , 팟 템플리 모두 언제나 수정 가능 하지만 복제수의 변경은 기존 팟에 영향을 미친다. 102 | - 컨트롤러의 라벨 셀렉터 또는 팟 템플릿 변경에 따른 효과 103 | - 라벨 셀렉터 및 팟 변경은 미치지 않는다. 104 | - 라벨 셀렉터 변경시 기존 팟이 레플리케이션 컨트롤러의 범위를 벗어나 해당 팟을 신경쓰지 않는다. 105 | - 레플리케이션 컨트롤러는 팟을 만든후 컨테이너 이미지, 환경변수, 기타사항을 신경쓰지 않는다. 따라서 새로운 팟에만 영향을 준다. 106 | - 레플리케이션컨트롤러를 사용해 얻는 이점 107 | 108 | ### 2.2 레플리케이션 컨트롤러의 생성 109 | - 팟이나 다른 쿠버네티스 리소스와 같이 쿠버네티스 API 서버로 JSON 또는 YAML 지시자로 레플리케이션컨트롤러를 생성 할 수 있다. 110 | `kubia-rc.yml` 111 | ``` 112 | apiVersion: v1 113 | kind: ReplicationController 114 | metadata: 115 | name: kubia 116 | spec: 117 | replicas: 3 118 | selector: 119 | app: kubia 120 | template: 121 | metadata: 122 | labels: 123 | app: kubia 124 | spec: 125 | containers: 126 | - name: kubia 127 | image: luksa/kubia 128 | ports: 129 | - containerPort: 8080 130 | ``` 131 | 132 | - 이 컨트롤러는 항상 라벨 셀렉터 app=kubia 와 일치하는 세개의 팟 인스턴스 유지 133 | - 생성시 팟 템플릿으로 새 팟 생성 134 | - 템플릿 팟 라벨과 컨트롤러 라벨 셀렉터와 일치해야 함 그러지 않으면 컨트롤러는 새 팟 무한생성 135 | - 새 팟을 스핀업 시킨다 해도 원하는 복제본 수에 근접한 실제 복제수가 생성되지 않기 때문이다. 이러한 경우를 방지하기 위해 API 서버는 컨트롤러 정의를 검사하고 잘못된경우 수락하지 않는다. 136 | - 셀렉터를 지정하지 않으면 팟 템플릿 라벨에서 자동으로 구성 (YAML 짧고 단순해짐) 137 | 138 | `kubectl create -f kubia-rc.yml` 139 | 140 | ### 2.3 실행 중인 레플리케이션컨트롤러 보기 141 | `kubectl get pods` 142 | 143 | `kubectl delete pod {name}` 144 | 145 | `kubectl get pods` 146 | 147 | `kubectl get rc` : rc는 레플리케이션컨트롤러의 줄임말 148 | 149 | `kubectl describe rc kubia` p .165 150 | 151 | - (P.166 4.4) 팟이 사라지면 레플리케이션 컨트롤러는 팟 수가 적음을 알고 새 팟 생성 152 | 153 | ### 2.4 레플리케이션컨트롤러의 범위 안팎으로 팟 이동 154 | ### 2.5 팟 템플릿 변경 155 | ### 2.6 수평 팟 스케일링 156 | ### 2.7 레플리케이션 컨트롤러 삭제 157 | -------------------------------------------------------------------------------- /k8s-in-action-chap05/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # Chapter 05 7 | # 서비스 8 | 9 | ----------------------------------------- 10 | 11 |
12 | 13 | ## 서비스가 필요한 이유 14 | 15 | * 포드는 일회성 16 | * scale, failure 등의 이유로 노드간 이동이 발생 17 | * 포드가 노드에 할당된 후 시작하기 전에 IP를 할당 18 | * 클라이언트에서 서버의 IP를 알 수 없음 19 | 20 |

21 | 22 | ## 서비스? 23 | 24 | * 동일한 서비스를 제공하는 Pod 그룹에 대한 단일 진입 지점 25 | * 서비스가 존재하는 동안 변경되지 않는 IP 주소와 Port를 가짐 26 | * 해당 서비스와 연결된 Pod 중 하나로 전달 27 | 28 |
29 | 30 | ----------------------------------------- 31 | 32 |
33 | 34 | 35 | 36 |
37 | 38 | ----------------------------------------- 39 | 40 | 41 |
42 | 43 | ## 서비스 생성 44 | 45 | ~~~ 46 | apiVersion: v1 47 | kind: Service 48 | metadata: 49 | svc: sample 50 | spec: 51 | ports: 52 | - port: 8080 53 | targetPort: 8080 54 | selector: 55 | svc: sample 56 | ~~~ 57 | 58 | ~~~ 59 | $ kubectl expose pod sample --type=LoadBalancer --name sample-http \ 60 | --port=8080 --target-port=8080 --selector='svc=sample' 61 | ~~~ 62 | 63 | 64 | 65 |
66 | 67 | ----------------------------------------- 68 | 69 |
70 | 71 | ## 서비스의 실체..? 72 | 73 | * KUBE-NODEPORTS chain 74 | 75 | * KUBE-SERVICES chain 76 | 77 | * KUBE-SVC-? chain 78 | 79 | 80 |
81 | 82 | -------------------------------------- 83 | 84 |
85 | 86 | ## 컨테이너에 명령 실행 87 | 88 | 89 | 90 | 91 | 92 |
93 | 94 | ----------------------------------------- 95 | 96 |
97 | 98 | ## 서비스의 Session Affinity 구성 99 | 100 | ~~~ 101 | spec: 102 | sessionAffinity: ClientIP 103 | ~~~ 104 | 105 | ~~~ 106 | $kubectl expose pod sample --type=LoadBalancer --name sample-http \ 107 | --session-affinity=ClientIP' 108 | ~~~ 109 | 110 |
111 | 112 | * 고정 세션을 구성 113 | * Sticky Session 114 | * None, ClientIP 타입만 지원 115 | * Srouce IP 기반 고정 세션을 구현 (by netfilter) 116 | 117 |
118 | 119 | ----------------------------------------- 120 | 121 |
122 | 123 | ## 서비스 검색 124 | 125 | * 환경변수를 이용한 서비스 조회 126 | 127 | 128 | 129 | 130 |
131 | 132 | ----------------------------------------- 133 | 134 |
135 | 136 | ## 서비스 검색 137 | 138 | * DNS & FQDN을 이용한 서비스 검색 139 | 140 | ~~~ 141 | root@sample-test:/ $ curl -XGET 'http://sample-http:8080' 142 | ~~~ 143 | 144 | ~~~ 145 | root@sample-test:/ $ curl -XGET 'http://sample-http.default.svc:8080' 146 | ~~~ 147 | 148 |
149 | 150 | ----------------------------------------- 151 | 152 |
153 | 154 | ## 클러스터 외부 서비스 연결 155 | 156 | * k8s 바깥의 서비스들을 수동으로 연결 가능 157 | * ExternalName 타입의 서비스는 DNS 레벨에서 구현 (iptables에 관련 룰은 없음) 158 | 159 | 160 | 161 |
162 | 163 | ----------------------------------------- 164 | 165 |
166 | 167 | ## 클러스터 외부 서비스 연결 168 | 169 | * k8s 바깥의 서비스들을 수동으로 연결 가능 170 | 171 | ~~~ 172 | apiVersion: v1 173 | kind: Service 174 | metadata: 175 | name: external-service 176 | spec: 177 | type: ExternalName 178 | externalName: external.com 179 | ports: 180 | - port: 1234 181 | ~~~ 182 | 183 | ~~~ 184 | apiVersion: v1 185 | kind: Endpoints 186 | metadata: 187 | name: external-service 188 | subsets: 189 | - addresses: 190 | - ip: 10.0.2.21 191 | - ip: 10.0.2.22 192 | ports: 193 | - port: 1234 194 | ~~~ 195 | 196 |
197 | 198 | ----------------------------------------- 199 | 200 |
201 | 202 | ## 외부 클라이언트 203 | 204 | * NodePort 타입의 서비스를 통해 외부 클라이언트에서 접근 할 수 있도록 해줌 205 | 206 | ~~~ 207 | apiVersion: v1 208 | kind: Service 209 | metadata: 210 | name: sample-http 211 | spec: 212 | type: NodePort 213 | ports: 214 | - port: 8080 215 | targetPort: 8080 216 | nodePort: 30123 217 | selector: 218 | svc: sample 219 | ~~~ 220 | 221 |
222 | 223 | ----------------------------------------- 224 | 225 |
226 | 227 | ## 외부 클라이언트 228 | 229 | 230 | 231 |
232 | 233 | ----------------------------------------- 234 | 235 |
236 | 237 | ## 외부 로드 밸런서를 이용한 노출 238 | 239 | 240 | 241 |
242 | 243 | ----------------------------------------- 244 | 245 |
246 | 247 | ## 외부 연결의 특성 248 | 249 | * 불필요한 네트워크 홉의 방지 250 | 251 | ~~~ 252 | spec: 253 | externalTrafficPolicy: Local 254 | ~~~ 255 | 256 | 257 | 258 |
259 | 260 | ----------------------------------------- 261 | 262 |
263 | 264 | ## 외부 연결의 특성 265 | 266 | * KUBE-NODEPORTS Chain (worker node 전체 공통) 267 | 268 | 269 | * KUBE-XLB Chain (worker node 별로 룰이 달라짐) 270 | * 해당 노드에 전달할 pod가 없는 경우 271 | 272 | 273 | 274 | * 해당 노드에 전달할 pod가 있는 경우 275 | 276 | 277 | 278 |
279 | 280 | ----------------------------------------- 281 | 282 |
283 | 284 | ## 인그레스 285 | 286 | 287 | 288 | * install 289 | ~~~ 290 | $ kubectl apply -f \ 291 | https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml 292 | $ kubectl apply -f \ 293 | https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/cloud-generic.yaml 294 | ~~~ 295 | 296 |
297 | 298 | ----------------------------------------- 299 | 300 |
301 | 302 | ## 인그레스 303 | 304 | ~~~ 305 | apiVersion: extensions/v1beta1 306 | kind: Ingress 307 | metadata: 308 | name: sample-ingress 309 | spec: 310 | rules: 311 | - http: 312 | paths: 313 | - path: /sample 314 | backend: 315 | serviceName: sample-service 316 | servicePort: 8080 317 | ~~~ 318 | 319 |
320 | 321 | ----------------------------------------- 322 | 323 |
324 | 325 | ## 인그레스 326 | 327 | 328 | 329 |
330 | 331 | ----------------------------------------- 332 | 333 |
334 | 335 | ## Readness Probe 336 | 337 | * Exec, Http Get, Tcp 타입 사용 가능 338 | 339 | 340 | 341 |
342 | 343 | ----------------------------------------- 344 | 345 |
346 | 347 | ## Readness Probe 348 | 349 | ~~~ 350 | apiVersion: v1 351 | kind: ReplicationController 352 | ... 353 | spec: 354 | ... 355 | template: 356 | ... 357 | spec: 358 | containers: 359 | - name: kubia 360 | image: luksa/kubia 361 | readinessProbe: 362 | exec: 363 | command: 364 | - ls 365 | - /var/ready 366 | ... 367 | ~~~ 368 |
369 | 370 | ----------------------------------------- 371 | 372 |
373 | 374 | ## Headless Service 375 | 376 | * 각 개별 pod의 ip를 전부 얻어오기 위한 서비스 377 | * CluterIP를 None으로 설정하면 DNS 서버는 단일 서비스 IP 대신 pod의 ip 목록을 전달 378 | * 준비 된 Pod의 목록만 전달 (default) 379 | 380 | ~~~ 381 | apiVersion: v1 382 | kind: Service 383 | metadata: 384 | name: sample-headless-service 385 | spec: 386 | clusterIP: None 387 | ports: 388 | - port: 8080 389 | targetPort: 8080 390 | selector: 391 | svc: sample 392 | ~~~ 393 | 394 |
395 | 396 | ----------------------------------------- 397 | 398 |
399 | 400 | ## Headless Service 401 | 402 | * 준비되지 않은 모든 pod를 찾으려면 아래 주석을 추가 403 | 404 | ~~~ 405 | metadata: 406 | name: sample-headless-service 407 | annotations: 408 | service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" 409 | ... 410 | ~~~ 411 | 412 |
413 | -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-1.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-10.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-11.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-12.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-13.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-14.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-15.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-16.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-17.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-18.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-19.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-2.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-20.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-3.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-4.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-5.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-6.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-7.png -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-8.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap05/service-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillim-programmer/kubernetes-in-action-study/c012bb031b145b84242e42585a68ea1ad5251be5/k8s-in-action-chap05/service-9.jpg -------------------------------------------------------------------------------- /k8s-in-action-chap06.md: -------------------------------------------------------------------------------- 1 | # 볼륨 2 | 3 | 쿠버네티스는 실제 데이터가 있는 디렉토리를 보존하기 위해서 저장소 볼륨을 정의한다. 4 | 볼륨은 포드의 일부로 정의되며 포드의 라이프사이클이 같다. 5 | 이는 포드가 시작될 때 볼륨이 작성되고 포드가 삭제될 때 볼륨이 삭제됨을 의미한다. 6 | 7 | - 볼륨은 포드의 컴포넌트이므로 컨테이너와 마찬가지로 포드의 스펙에 정의한다 8 | - 포드의 모든 컨테이너에서 볼륨을 사용할 수 있지만 볼륨에 액세스해야 하는 각 컨테이너에 볼륨을 마운트 해야 한다. 9 | (각 컨테이너에서 파일 시스템의 임의 위치에 볼륨을 마운트 할 수 있다.) 10 | 11 | ## 볼륨 종류 12 | - emptyDir : 일시적인 데이터를 저장하는데 사용되는 비어있는 단순한 디렉토리 13 | - hostPath : 워커노드의 파일 시스템에서 포드로 디렉토리를 마운트하는데 사용 14 | - gitRepo : git 스토리지의 내용을 체크아웃해 초기화된 볼륨 15 | - nfs : 포드에 마운트된 NFS 공유 16 | - gcePersistentDisk(구글 컴퓨트 엔진 영구 디스크), awsElastic-BlockStore(AWS 탄력적 블록 스토리지 볼륨), azureDisk(마이크로소프트 애저 디스크 볼륨) 17 | : 클라우드에서 제공하는 전용 스토리지 18 | - cinder, cephfs, iscsi, flocker, glusterfs, quobyte, rbd, flexVolume, vsphere-Volume, photonPersistentDisk, scaleIO : 다른 유형의 네트워크 스토리지를 마운트 19 | - configMap, secret, downwardAPI : 특정 쿠버네티스 리소스 및 클러스터 정보를 포드에 노출하는 데 사용되는 특수한 유형의 볼륨 20 | - persistentVolumeClaim : 사전 또는 동적으로 프로비저닝된 영구 스토리지를 사용하는 방법 21 | 22 | ### 1. emptyDir 볼륨 - 포드안에 존재 23 | emptyDir 볼륨은 빈 디렉토리로 시작 24 | 볼륨의 수명이 포드와 연관되어 있기 때문에 포드를 삭제하면 볼륨의 내용이 손실된다. 25 | emptyDir 볼륨은 동일한 포드에서 실행중인 컨테이너 같에 파일을 공유할 때 유용하다. 26 | 읽기/쓰기의 권한을 부여 가능하다 27 | 28 | ● 포드 생성 29 | fortune-pod.yaml 30 | ``` 31 | apiVersion: v1 32 | kind: Pod 33 | metadata: 34 | name: fortune 35 | spec: 36 | containers: 37 | - image: luksa/fortune 38 | name: html-generator 39 | volumeMounts: 40 | - name: html 41 | mountPath: /var/htdocs 42 | - image: nginx:alpine 43 | name: web-server 44 | volumeMounts: 45 | - name: html 46 | mountPath: /usr/share/nginx/html 47 | readOnly: true 48 | ports: 49 | - containerPort: 80 50 | protocol: TCP 51 | volumes: 52 | - name: html 53 | emptyDir: {} 54 | ``` 55 | ``` 56 | $ kubectl create -f fortune-pod.yaml 57 | 58 | # fortune 메시즈를 보려면 포드의 액세스를 활성화해야 한다. 59 | # 이 작업은 포트를 로컬 컴퓨터에서 포드로 전달을 수행한다 60 | $ kubectl port-forward fortune 8080:80 61 | 62 | # 이제 로컬 컴퓨터의 포트 8080을 통해 Nginx 서버에 액세스 할수 있다. 63 | $ curl http://localhost:8080 64 | ``` 65 | html-generator 컨테이너가 시작되면 10초마다 /var/htdocs/index.html 파일에 fortune 명령의 출력 결과를 기록한다. 66 | 볼륨이 /var/htdocs에 마운트되므로 컨테이너의 최상위 계층 대신 index.html 파일이 볼륨에 기록된다. 67 | Nginx는 fortune 루프를 실행하는 컨테이너가 작상한 index.html파일을 제공한다. 68 | 결과적으로 포트 80의 포드로 http 요청을 보내는 클라이언트는 응답으로 현재 fortune 메시지를 받는다. 69 | 70 | 디스크 대신 메모리에 emptDir을 생성할 수 있다. 71 | 아래와 같이 매체를 Memory로 설정한다. 72 | ``` 73 | volumes: 74 | -name:html 75 | emptyDir: 76 | medium: Memory 77 | ``` 78 | 79 | ### 2. git repository 볼륨 - 포드안에 존재 80 | gitRepo 볼륨은 깃 저장소에서 데이터를 복제하여 포드의 볼륨에 채워진다. 81 | gitRepo 볼륨이 생성된 후에는 참조하는 repo와 동기화 되지 않는다. 82 | 포드를 새로 만들어야 한다. 83 | gitrepo-volume-pod.yaml 84 | ``` 85 | apiVersion: v1 86 | kind: Pod 87 | metadata: 88 | name: gitrepo-volume-pod 89 | spec: 90 | containers: 91 | - image: nginx:alpine 92 | name: web-server 93 | volumeMounts: 94 | - name: html 95 | mountPath: /usr/share/nginx/html 96 | readOnly: true 97 | ports: 98 | - containerPort: 80 99 | protocol: TCP 100 | volumes: 101 | - name: html 102 | gitRepo: 103 | repository: https://github.com/haksup/kubia-website-example.git 104 | revision: master 105 | directory: . 106 | ``` 107 | ``` 108 | $ kubectl create -f gitrepo-volume-pod.yaml 109 | 110 | $ kubectl port-forward gitrepo-volume-pod 8080:80 111 | ``` 112 | gitRepo 원본 주소 113 | https://github.com/luksa/kubia-website-example.git 114 | 115 | ### 3. hostPath - 워커노드 파일 시스템에 액세스 p226 116 | hostPath 볼륨은 노드의 파일 시스템에 있는 측정 파일 또는 디렉토리를 가리킨다. 117 | hostPath 는 영구 스토리지 유형이다 118 | 볼륨의 내용은 특정 노드의 파일 시스템에 저장되므로 포드가 다른 노드로 다시 실행되면 데이터가 나오지 않는다. 119 | 즉 노드안에서만 포드와 볼륨이 공유된다. 120 | ``` 121 | $ kubectl get pod --namespace kube-system 122 | ``` 123 | host-path-pod.yaml 124 | ``` 125 | apiVersion: v1 126 | kind: Pod 127 | metadata: 128 | name: test-pd 129 | spec: 130 | containers: 131 | - image: k8s.gcr.io/test-webserver 132 | name: test-container 133 | volumeMounts: 134 | - mountPath: /test-pd 135 | name: test-volume 136 | volumes: 137 | - name: test-volume 138 | hostPath: 139 | # directory location on host 140 | path: /data 141 | # this field is optional 142 | type: Directory 143 | ``` 144 | ``` 145 | $ kubectl create -f host-path-pod.yaml 146 | ``` 147 | 148 | ### 4-1. GCE 영구 디스트 - 모든 클러스터 노드에서 액세스 p268 149 | GCE(구글 컴퓨터 엔진) 에서 클러스터 노드를 실행한다. 150 | 151 | 쿠버네티스 클러스터와 동일한 영역에서 생성해야 한다. 152 | 쿠버네티스 클러스터를 나열하는 명령은 아래와 같다 153 | ``` 154 | $ gcloud container clusters list 155 | 156 | # GCE 영구 디스크 생성 157 | $ gcloud compute disks create --size=1Gib --zone=europe-west1-b mongodb 158 | ``` 159 | 160 | ### 4-2. gcePersistentDisk 를 사용해 포드 생성 161 | 물리적 스토리지를 볼륨을 몽고 DB 포드내부에 사용할 수 있다. 162 | ``` 163 | apiVersion: v1 164 | kind: Pod 165 | metadata: 166 | name; mongodb 167 | spec: 168 | volumes: 169 | - name: mongodb-data 170 | gcePersistentDisk: 171 | pdName: mongodb 172 | fsType: ext4 173 | containers: 174 | - image: mongo 175 | name: mongodb 176 | volumeMounts: 177 | - name: mongodb-data 178 | mountPath: /data/db 179 | ports: 180 | - containerPort: 27017 181 | protocol: TCP 182 | ``` 183 | 184 | ### 5 awsElasticBlockStore 볼륨 185 | aws의 영구 디스크 186 | ``` 187 | apiVersion: v1 188 | kind: Pod 189 | metadata: 190 | name: mongodb 191 | spec: 192 | volumes: 193 | - name: mongodb-data 194 | awsElasticBlockStore: 195 | volumeId: my-volume 196 | fsType: ext4 197 | containers: 198 | - image: mongo 199 | name: mongodb 200 | volumeMounts: 201 | - name: mongodb-data 202 | mountPath: /data/db 203 | ports: 204 | - containerPort: 27017 205 | protocol: TCP 206 | ``` 207 | 208 | ### 6. NFS 볼륨 사용 209 | NFS(네트워크 파일시스템) 서버로 볼륨을 마운트 할 경우 210 | ``` 211 | apiVersion: v1 212 | kind: Pod 213 | metadata: 214 | name: mongodb 215 | spec: 216 | volumes: 217 | - name: mongodb-data 218 | nfs: 219 | server: 1.2.3.4 220 | path: /some/path 221 | ``` 222 | https://kubernetes.io/docs/concepts/storage/volumes/#nfs 참고 223 | 224 | ### 7. 이외에도 다양한 스토리지 기술이 존재한다 225 | https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes 226 | 227 | ### 8. 기본 스토리지 기술에서 포드 분리 228 | 지금까지 모든 영구 볼륨 유형은 포드 개발자가 클러스터에서 사용할 수 있는 실제 네트워크 스토리지 인프라 지식을 갖추고 있어야 한다. 229 | 이상적인 것은 쿠버네트스에 애플리케이션을 배포하는 개발자는 포드를 실행하는 데 사용되는 물리적 서버 유형을 알 수 없는 것과 230 | 마찬가지로 사용되는 스토리지 기술의 종류를 몰라도 상관없도록 제공하도록 한다 231 | 232 | ### 9. persistentVolume과 persistentVolumeClaim 233 | 우리가 파일을 업로드 할때 어떤 물리 저장소에 어떤 경로에 저장해줘~ 라고 정의하면서 파일을 업로드 하지 않는다. 234 | 우리는 파일을 업로드 하면 알아서 정의된 위치에 파일이 업로드가 된다. 235 | 지금까지 pod에 세부적으로 볼륨을 정의해서 만들었지만 이제 그런 정의 없이 볼륨을 마운트 한다고 생각하면 된다. 236 | 237 | 1. 개발자가 포드에 기술적으로 세부적인 볼륨을 추가하는 대신 클러스터 관리자가 기본 스토리지를 설정한 다음 PersistentVolume 리소스를 생성해 쿠버네티스 API 서버에 등록한다. 238 | 2. 클러스터 사용자가 포드 중 하나에서 영구 스토리지를 사용해야 하는 경우 먼저 필요한 최소 크기와 액세스 모드를 지정해 PersistentVolumeClaim 매니페스트를 생성한다. 239 | 3. 사용자가 PersistentVolumeClaim 매니페스트를 쿠버네티스 API 서버에 제출하고 쿠버네티스가 적절한 영구 볼륨을 찾아 클레임을 바인딩한다. 240 | 241 | ### 10-1. PersistentVolume 생성 p276 242 | mongodb-pv-gcepd.yaml 243 | ``` 244 | apiversion: v1 245 | kind: PersistentVolume 246 | metadata: 247 | name: mongodb-pv 248 | spec: 249 | capacity: 250 | storage: 1Gi 251 | accessModes: 252 | - ReadWriteOnce 253 | - ReadOnlyMany 254 | PersistentVolumeReclaimPolicy: Retain 255 | gcePersistentDisk: 256 | pdName: mongodb 257 | fsType: ext4 258 | ``` 259 | capacity: storage: 1Gi => PersistentVolume 크기 정의 260 | accessModes:- ReadWriteOnce- ReadOnlyMany => 단일 클라이언트가 읽기 및 쓰기용이나 여러 클라이언트가 읽기 전용으로 마안트 할수 있다 261 | PersistentVolumeReclaimPolicy: Retain => 클레임이 해제된 후에는 PersistentVolume 을 삭제하거나 삭제된 상태로 유지해야 한다. 262 | gcePersistentDisk:pdName: mongodb fsType: ext4 => PersistentVolume을 GCE 영구 디스크를 정의 263 | 264 | ``` 265 | $ kubectl create -f mongodb-pv-gcepd.yaml 266 | ``` 267 | kubectl create 명령을 사용해 PersistentVolume을 생성 후 할당할 준비가 되었는지 확인한다 268 | ``` 269 | $ kubectl get pv 270 | ``` 271 | 272 | ● persistentVolume 은 네임스페이스에 속하지 않는다. 273 | 274 | ### 10-2. PersistentVolumeClaim을 생성해 PersistentVolume 할당 p279 275 | PersistentVolume을 포드에서 직접 사용할 수 없다 우선 그것을 클레임 해야 한다. 276 | mongodb-pvc.yaml 277 | ``` 278 | apiVersion: v1 279 | kind: PersistentVolumeClaim 280 | metadata: 281 | name: mongodb-pvc 282 | spec: 283 | resources: 284 | requests: 285 | storage: 1Gi 286 | accessModes: 287 | - ReadWriteOnce 288 | storageClassName: "" 289 | ``` 290 | metadata: name: mongodb-pvc => 클레임의 네임 포드 볼륨으로 사용할 때 필요하다 291 | requests:storage: 1Gi => 스토리지의 1Gb 요청 292 | accessModes:- ReadWriteOnce => 스토리지가 단일 클라이언트를 지원(읽기 쓰기) 293 | storageClassName => 동적 프로비저닝 294 | 295 | ``` 296 | $ kubectl create -f mongodb-pvc.yaml 297 | $ kubectl get pvc 298 | ``` 299 | 300 | ### 10-3 포드에서 PersistentVolumeClaim 사용 301 | 사용자가 볼륨을 해제할 때까지 다른 사용자는 동일한 볼륨을 할당할수 없다 302 | mongodb-pod-pvc.yaml 303 | ``` 304 | apivVersion: v1 305 | kind: Pod 306 | metadata: 307 | name: mongodb 308 | spec: 309 | containers: 310 | - image: mongo 311 | name: mongodb 312 | volumeMounts: 313 | - name: mongodb-data 314 | mountPath: /data/db 315 | ports: 316 | - containerPort: 27017 317 | protocol: TCP 318 | volumes: 319 | - name: monodb-data 320 | persistentVolumeClaim: 321 | claimName: mongodb-pvc 322 | ``` 323 | ``` 324 | $ kubectl create -f mongodb-pod-pvc.yaml 325 | 326 | $ kubectl exec -it mondofb mongo # 몽고DB 셀을 실행한다 327 | ``` 328 | -------------------------------------------------------------------------------- /k8s-in-action-chap08.md: -------------------------------------------------------------------------------- 1 | 오늘 사용했던 tool 2 | - vscode : 코드 에디터 => 사용 extension : kubernetes, Swagger Viewer 3 | - typora : 마크 다운 에디터 4 | - zsh : 쉘 5 | 6 | ## 8장 애플리케이션에서 포드 메타 데이터와 그 외의 리소스 접근하기 7 | 8 | 우리는 지난 장에서 포드들이 환경변수나 설정할때 필요한 기본 데이터를 ConfigMap과 Secret 을 통해 전달 받는 방법을 배웠다. 9 | 10 | 11 | 12 | ### 8.1 Downward API 를 통한 메타 데이터 전달 13 | 14 | - 만약 포드가 설치되고 나서 이후의 데이터(pod의 ip, host node의 이름, pod의 특정 이름 <= 리플리카로 만들경우 등)이 필요한 경우는 어떻하지? 15 | - ConfigMap 이나 Secret으로는 해결할 수 없다. 16 | - 해당 문제를 해결하기 위한 것이 바로 **Downward API** 이다. 17 | - 애플리케이션에서 API를 요청하여 얻는 데이터가 아니라 k8s api server가 환경변수와 downward API 볼륨에 담아준다. 18 | - 사용가능한 데이터 19 | - 포드 이름 20 | - 포드 ip 21 | - 포드가 속한 namespace 이름 22 | - 포드가 실행되는 node의 이름 23 | - 포드가 실행 중인 service account 이름 24 | - 컨테이너의 메모리 및 cpu 한계 및 요청 25 | - 커널에 요청해서 알 수 있긴함 26 | - 한계는 넘어가면(swap을 사용하거나 메모리 초과로 컨테이너가 죽음) <=> 요청은 넘어가도 안죽음 27 | - 포드의 라벨 및 주석 28 | - 보통 해당 데이터는 볼륨을 통해서 노출됨 29 | - 그 이유는 실행중에 라벨과 주석이 변경 가능해서 첫 환경변수로 초기화된 컨테이너에서 환경변수를 바꾸는 기능을 도커가 지원하지 않기 때문 30 | - 결과적으로 실행될때 환경설정값을 담는 기능 => 실행중에 변경 x 31 | - 라벨과 주석은 변경가능하기 때문에 => 환경변수 보다는 볼륨에 담아서 자주 씀 32 | - 또는 볼륨은 환경변수에 쓰이는 변수가 다른 컨테이너(동일한 포드에서)에 전달하는 패턴에도 사용한다. 33 | - 메타데이터가 상당히 제한적 이지만 해당 정보가 필요하다면 셸 스크립트나 코드 필요없이 간단히 쓰기 좋다. 34 | 35 | ### 8.2 쿠버네티스 API 서버와 통신하기 36 | 37 | - Downward API의 경우 현재 포드의 메타데이터만 접근가능하다. 다른 포드의 데이터 혹은 다른 리소스에 대해서 접근할 필요가 생길 경우에는 접근이 불가능하다. 38 | - 다른 리소스에 대한 데이터가 필요하거나 실시간 정보가 필요한 경우 k8s api와 직접 통신하는게 제일 좋다 39 | 40 | 41 | 42 | ### 8.2.1 k8s api 탐색해보기 43 | 44 | k8s master 서버에 직접 전송해서 테스트 해볼 수 있지만 인증토큰을 전달해줘야 되기 때문에 간편하게 proxy로 하자. 45 | 46 | #### 통신 테스트 해보기 47 | 48 | ```Shell 49 | $ kubectl proxy 50 | ``` 51 | 52 | #### 결과 53 | 54 | ```shell 55 | Starting to serve on 127.0.0.1:8001 56 | ``` 57 | 58 | 127.0.0.1:8001에다가 curl을 쏴주면 통신해 볼 수 있다. 59 | 60 | 61 | 62 | #### api 테스트 63 | 64 | ```shell 65 | $ curl 127.0.0.1:8001 66 | ``` 67 | 68 | #### 결과 69 | 70 | ```shell 71 | { 72 | "paths": [ 73 | "/api", 74 | "/api/v1", 75 | "/apis", 76 | "/apis/", 77 | "/apis/admissionregistration.k8s.io", 78 | "/apis/admissionregistration.k8s.io/v1beta1", 79 | "/apis/apiextensions.k8s.io", 80 | "/apis/apiextensions.k8s.io/v1beta1", 81 | "/apis/apiregistration.k8s.io", 82 | "/apis/apiregistration.k8s.io/v1", 83 | "/apis/apiregistration.k8s.io/v1beta1", 84 | "/apis/apps", 85 | "/apis/apps/v1", 86 | "/apis/apps/v1beta1", 87 | "/apis/apps/v1beta2", 88 | "/apis/authentication.k8s.io", 89 | "/apis/authentication.k8s.io/v1", 90 | "/apis/authentication.k8s.io/v1beta1", 91 | "/apis/authorization.k8s.io", 92 | "/apis/authorization.k8s.io/v1", 93 | "/apis/authorization.k8s.io/v1beta1", 94 | "/apis/autoscaling", 95 | "/apis/autoscaling/v1", 96 | "/apis/autoscaling/v2beta1", 97 | "/apis/batch", 98 | "/apis/batch/v1", 99 | "/apis/batch/v1beta1", 100 | "/apis/certificates.k8s.io", 101 | "/apis/certificates.k8s.io/v1beta1", 102 | "/apis/certmanager.k8s.io", 103 | "/apis/certmanager.k8s.io/v1alpha1", 104 | "/apis/crd.k8s.amazonaws.com", 105 | "/apis/crd.k8s.amazonaws.com/v1alpha1", 106 | "/apis/events.k8s.io", 107 | "/apis/events.k8s.io/v1beta1", 108 | "/apis/extensions", 109 | "/apis/extensions/v1beta1", 110 | "/apis/management.cattle.io", 111 | "/apis/management.cattle.io/v3", 112 | "/apis/networking.k8s.io", 113 | "/apis/networking.k8s.io/v1", 114 | "/apis/policy", 115 | "/apis/policy/v1beta1", 116 | "/apis/project.cattle.io", 117 | "/apis/project.cattle.io/v3", 118 | "/apis/rbac.authorization.k8s.io", 119 | "/apis/rbac.authorization.k8s.io/v1", 120 | "/apis/rbac.authorization.k8s.io/v1beta1", 121 | "/apis/scheduling.k8s.io", 122 | "/apis/scheduling.k8s.io/v1beta1", 123 | "/apis/storage.k8s.io", 124 | "/apis/storage.k8s.io/v1", 125 | "/apis/storage.k8s.io/v1beta1", 126 | "/healthz", 127 | "/healthz/autoregister-completion", 128 | "/healthz/etcd", 129 | "/healthz/ping", 130 | "/healthz/poststarthook/apiservice-openapi-controller", 131 | "/healthz/poststarthook/apiservice-registration-controller", 132 | "/healthz/poststarthook/apiservice-status-available-controller", 133 | "/healthz/poststarthook/bootstrap-controller", 134 | "/healthz/poststarthook/ca-registration", 135 | "/healthz/poststarthook/generic-apiserver-start-informers", 136 | "/healthz/poststarthook/kube-apiserver-autoregistration", 137 | "/healthz/poststarthook/rbac/bootstrap-roles", 138 | "/healthz/poststarthook/scheduling/bootstrap-system-priority-classes", 139 | "/healthz/poststarthook/start-apiextensions-controllers", 140 | "/healthz/poststarthook/start-apiextensions-informers", 141 | "/healthz/poststarthook/start-kube-aggregator-informers", 142 | "/healthz/poststarthook/start-kube-apiserver-admission-initializer", 143 | "/healthz/poststarthook/start-kube-apiserver-informers", 144 | "/metrics", 145 | "/openapi/v2", 146 | "/swagger-2.0.0.json", 147 | "/swagger-2.0.0.pb-v1", 148 | "/swagger-2.0.0.pb-v1.gz", 149 | "/swagger.json", 150 | "/swaggerapi", 151 | "/version" 152 | ] 153 | } 154 | ``` 155 | 156 | 157 | 158 | 나머지는 해당 path와 통신하면서 테스트 해보자. 159 | 160 | 161 | 162 | ### 8.2.2 포드안에서 api 서버에 통신하기 163 | 164 | default 네임스페이스에 생성된 kubernetes 에 요청하면 가능하다. 165 | 166 | 167 | 168 | 지난 5장쯤에서 서비스는 DNS로 요청할 수 있다는 것을 알았다. 169 | 170 | ``` 171 | $ curl https://kubernetes 172 | ``` 173 | 174 | 여기서 kubernetes는 service 이름이 kubernetes이기 때문에 가능한거다. 175 | 176 | 177 | 178 | ### 8.2.3 앰버서더 컨테이너와 API 서버 통신 간소화 179 | 180 | 이번절에서 앞에서 kubectl proxy를 통해서 요청했던 것을 포드에서 구현하는 방식을 설명한다 181 | 182 | ![ambassador container에 대한 이미지 검색결과](https://www.oreilly.com/library/view/designing-distributed-systems/9781491983638/assets/ddis_04in01.png) 183 | 184 | 185 | 186 | 포드안에 컨테이너는 동일한 loopback 네트워크 인터페이스를 공유해서 localhost로 접근할 수 있다. 187 | 188 | 189 | 190 | Kubectl-proxy 컨테이너를 생성하여 localhost를 통해서 요청해보자. 191 | 192 | 193 | 194 | ### 8.2.4 클라이언트 라이브러리를 사용해 API 서버와 통신 195 | 196 | 앞에 앰버서더 컨테이너를 통해서 요청할 수 있듯이 라이브러리를 통해 코드로 해결할 수 있다. 197 | 198 | 책에서는 자바로 하는 것을 보여준다. 199 | -------------------------------------------------------------------------------- /k8s-in-action-chap09.md: -------------------------------------------------------------------------------- 1 | 2 | # 9 디플로이먼트: 애플리케이션을 선언적으로 업데이트 3 | 4 | ## 9.3 선언적으로 애플리케이션을 업데이트하기 위한 디플로이먼트 사용 5 | 6 | 디플로이먼트: 7 | * 애플리케이션을 배포하고 이를 선언적으로 업데이트하는 데 사용하는 상위 수준 리소스 8 | * 디플로이먼트가 생성하는 레플리카셋이 실제로 포드를 관리 9 | 10 | ![그림9.8](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/09fig08.jpg) 11 | 12 | ### 9.3.1 디플로이먼트 생성 13 | 14 | * 디플로이먼트 설정은 레플리카셋과 유사 15 | * 원하는 복제본 수, 라벨 실렉터, 포드 템플릿으로 구성 16 | 17 | ```yaml 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: kubia 22 | spec: 23 | replicas: 3 24 | selector: 25 | matchLabels: 26 | app: kubia 27 | template: 28 | metadata: 29 | name: kubia 30 | labels: 31 | app: kubia 32 | spec: 33 | containers: 34 | - image: luksa/kubia:v1 35 | name: nodejs 36 | ``` 37 | 38 | 생성: kubectl create -f kubia-deployment-v1.yaml 39 | 40 | **디플로이먼트 생성하자마자 포드 상태** 41 | ``` 42 | [root@k8s-master vagrant]# kubectl get pod 43 | NAME READY STATUS RESTARTS AGE 44 | kubia-6b646f577b-8z2mx 0/1 ContainerCreating 0 16s 45 | kubia-6b646f577b-hdd6k 0/1 ContainerCreating 0 16s 46 | kubia-6b646f577b-skvpt 0/1 ContainerCreating 0 16s 47 | ``` 48 | 49 | **디플로이먼트 롤아웃 상태** 50 | ``` 51 | # kubectl rollout status deployment kubia 52 | Waiting for deployment "kubia" rollout to finish: 0 of 3 updated replicas are available... 53 | Waiting for deployment "kubia" rollout to finish: 1 of 3 updated replicas are available... 54 | deployment "kubia" successfully rolled out 55 | ``` 56 | 57 | **디플로이먼트 이후 포드 상태** 58 | ``` 59 | [root@k8s-master vagrant]# kubectl get pod 60 | NAME READY STATUS RESTARTS AGE 61 | kubia-6b646f577b-8z2mx 1/1 Running 0 9m35s 62 | kubia-6b646f577b-hdd6k 1/1 Running 0 9m35s 63 | kubia-6b646f577b-skvpt 1/1 Running 0 9m35s 64 | ``` 65 | 66 | **디플로이먼트 이후 리플리카셋 상태** 67 | ``` 68 | [root@k8s-master ~]# kubectl get rs 69 | NAME DESIRED CURRENT READY AGE 70 | kubia-6b646f577b 3 3 3 14m 71 | ``` 72 | *포드의 이름은 레플리카셋 이름에 포함된 해시값 사용* 73 | 74 | ``` 75 | [root@k8s-master ~]# kubectl get services 76 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 77 | kubernetes ClusterIP 10.96.0.1 443/TCP 6h35m 78 | kubia ClusterIP 10.96.132.53 80/TCP 8s 79 | 80 | [root@k8s-master ~]# curl 10.96.132.53 81 | This is v1 running in pod kubia-6b646f577b-p9vh2 82 | 83 | [root@k8s-master ~]# curl 10.96.132.53 84 | This is v1 running in pod kubia-6b646f577b-sfgqk 85 | 86 | [root@k8s-master ~]# curl 10.96.132.53 87 | This is v1 running in pod kubia-6b646f577b-p9vh2 88 | ``` 89 | 90 | ### 9.3.2 디플로이먼트 업데이트 91 | 92 | 디플로이먼트 전략 93 | * RollingUpdate: 롤링 업데이트 94 | * Recreate: 기존 포드 모구 삭제 후 새로운 포드 생성 95 | 96 | 롤링 업데이트 확인 97 | * 일정 주기로 서비스를 호출해서 변경 확인 98 | * kubia V1을 V2로 변경 99 | 100 | **일정 주기로 서비스 호출** 101 | ``` 102 | [root@k8s-master ~]# while true; do curl http://10.96.132.53; sleep 2; done 103 | ``` 104 | 105 | **kubia V1을 V2로 변경** 106 | ``` 107 | [root@k8s-master ~]# kubectl replace -f kubia-deployment-v2.yaml 108 | deployment.apps/kubia replaced 109 | ``` 110 | 111 | **변경 후 출력** 112 | ``` 113 | This is v1 running in pod kubia-6b646f577b-p9vh2 114 | This is v1 running in pod kubia-6b646f577b-p9vh2 115 | This is v2 running in pod kubia-6969c946dc-xhqrf 116 | This is v2 running in pod kubia-6969c946dc-xhqrf 117 | This is v1 running in pod kubia-6b646f577b-9kp7q 118 | This is v1 running in pod kubia-6b646f577b-sfgqk 119 | This is v1 running in pod kubia-6b646f577b-sfgqk 120 | This is v2 running in pod kubia-6969c946dc-xhqrf 121 | This is v2 running in pod kubia-6969c946dc-xhqrf 122 | This is v2 running in pod kubia-6969c946dc-xhqrf 123 | This is v1 running in pod kubia-6b646f577b-p9vh2 124 | This is v1 running in pod kubia-6b646f577b-sfgqk 125 | This is v2 running in pod kubia-6969c946dc-xhqrf 126 | This is v1 running in pod kubia-6b646f577b-sfgqk 127 | This is v2 running in pod kubia-6969c946dc-cc6v8 128 | This is v2 running in pod kubia-6969c946dc-xhqrf 129 | This is v2 running in pod kubia-6969c946dc-cc6v8 130 | This is v2 running in pod kubia-6969c946dc-kw4vj 131 | This is v2 running in pod kubia-6969c946dc-xhqrf 132 | This is v2 running in pod kubia-6969c946dc-xhqrf 133 | This is v2 running in pod kubia-6969c946dc-cc6v8 134 | This is v2 running in pod kubia-6969c946dc-kw4vj 135 | This is v2 running in pod kubia-6969c946dc-kw4vj 136 | This is v2 running in pod kubia-6969c946dc-cc6v8 137 | ``` 138 | 139 | **업데이트 이후 리플리카셋** 140 | ``` 141 | [root@k8s-master ~]# kubectl get rs 142 | NAME DESIRED CURRENT READY AGE 143 | kubia-6969c946dc 3 3 3 9m11s 144 | kubia-6b646f577b 0 0 0 35m 145 | ``` 146 | 147 | ![그림9.10](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/09fig10_alt.jpg) 148 | 149 | 이전 리플리카셋은 롤백 용으로 사용 150 | 151 | ### 9.3.3 디플로이먼트 롤백 152 | 153 | 오류 발생 버전으로 업데이트 후 롤백 확인 154 | 155 | **오류 발생 버전으로 업데이트** 156 | ``` 157 | [root@k8s-master ~]# kubectl replace -f kubia-deployment-v3.yaml 158 | deployment.apps/kubia replaced 159 | ``` 160 | 161 | **출력 로그** 162 | ``` 163 | [root@k8s-master ~]# while true; do curl http://10.96.132.53; sleep 2; done 164 | ...생략 165 | This is v2 running in pod kubia-6969c946dc-cc6v8 166 | This is v2 running in pod kubia-6969c946dc-xhqrf 167 | This is v3 running in pod kubia-dc86d7c4-h9h8z 168 | This is v2 running in pod kubia-6969c946dc-cc6v8 169 | This is v3 running in pod kubia-dc86d7c4-k8jrz 170 | This is v3 running in pod kubia-dc86d7c4-h9h8z 171 | This is v2 running in pod kubia-6969c946dc-xhqrf 172 | This is v2 running in pod kubia-6969c946dc-xhqrf 173 | This is v3 running in pod kubia-dc86d7c4-h9h8z 174 | This is v3 running in pod kubia-dc86d7c4-k8jrz 175 | This is v3 running in pod kubia-dc86d7c4-8zbch 176 | This is v3 running in pod kubia-dc86d7c4-k8jrz 177 | This is v3 running in pod kubia-dc86d7c4-h9h8z 178 | Some internal error has occurred! This is pod kubia-dc86d7c4-h9h8z 179 | This is v3 running in pod kubia-dc86d7c4-8zbch 180 | Some internal error has occurred! This is pod kubia-dc86d7c4-h9h8z 181 | This is v3 running in pod kubia-dc86d7c4-8zbch 182 | This is v3 running in pod kubia-dc86d7c4-k8jrz 183 | Some internal error has occurred! This is pod kubia-dc86d7c4-h9h8z 184 | This is v3 running in pod kubia-dc86d7c4-8zbch 185 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 186 | Some internal error has occurred! This is pod kubia-dc86d7c4-8zbch 187 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 188 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 189 | ``` 190 | 191 | **롤아웃 되돌리기** 192 | 193 | ``` 194 | [root@k8s-master ~]# kubectl rollout undo deployment kubia 195 | deployment.extensions/kubia rolled back 196 | ``` 197 | 198 | **출력 로그** 199 | ``` 200 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 201 | Some internal error has occurred! This is pod kubia-dc86d7c4-8zbch 202 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 203 | This is v2 running in pod kubia-6969c946dc-vjv6r 204 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 205 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 206 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 207 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 208 | This is v2 running in pod kubia-6969c946dc-wwd8h 209 | This is v2 running in pod kubia-6969c946dc-wwd8h 210 | Some internal error has occurred! This is pod kubia-dc86d7c4-k8jrz 211 | This is v2 running in pod kubia-6969c946dc-nrwtp 212 | This is v2 running in pod kubia-6969c946dc-vjv6r 213 | This is v2 running in pod kubia-6969c946dc-nrwtp 214 | This is v2 running in pod kubia-6969c946dc-wwd8h 215 | ``` 216 | 217 | **롤아웃 히스토리** 218 | 219 | ``` 220 | [root@k8s-master ~]# kubectl rollout history deployment kubia 221 | deployment.extensions/kubia 222 | REVISION CHANGE-CAUSE 223 | 1 224 | 3 225 | 4 226 | ``` 227 | 228 | **특정 디플로이먼트 리비전으로 롤백** 229 | ``` 230 | kubectl rollout undo deployment kubia --to-revision=1 231 | ``` 232 | 233 | 히스토리 보관 제한 설정: 234 | * spec.revisionHistoryLimit: 리비전 히스토리 개수 설정. 기본값은 10. 235 | 236 | ### 9.3.4 롤링 업데이트 롤아웃 속도 통제 237 | 238 | spec.spec.strategy.rollingUpdate의 두 속성으로 제어 239 | * maxSurge: 지정한 리플리카 개수를 초과할 수 있는 포드 인스턴스(비율 또는 개수) 240 | * maxUnavailable: 지정한 리플리카 개수 대비 사용할 수 없는 포드 인스턴수(비율 또는 개수) 241 | 242 | 예: replicas=3, maxSurge=1, maxUnavailable=0 243 | ![그림9.12](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/09fig12_alt.jpg) 244 | 245 | 예: replicas=3, maxSurge=1, maxUnavailable=1 246 | ![그림9.13](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/09fig13_alt.jpg) 247 | 248 | ### 9.3.5 롤아웃 프로세스 일시 중지 249 | 250 | **중지** 251 | ``` 252 | # kubectl rollout pause deployment kubia 253 | ``` 254 | 255 | **재개** 256 | ``` 257 | # kubectl rollout resume deployment kubia 258 | ``` 259 | 260 | ### 9.3.6 잘못된 버전 롤아웃 방지 261 | 262 | 레디니스프로브를 이용해서 잘못된 버전 롤아웃 방지 263 | 264 | ``` 265 | apiVersion: apps/v1 266 | kind: Deployment 267 | metadata: 268 | name: kubia 269 | spec: 270 | minReadySeconds: 10 271 | replicas: 3 272 | strategy: 273 | type: RollingUpdate 274 | rollingUpdate: 275 | maxSurge: 1 276 | maxUnavailable: 0 277 | selector: 278 | matchLabels: 279 | app: kubia 280 | template: 281 | metadata: 282 | name: kubia 283 | labels: 284 | app: kubia 285 | spec: 286 | containers: 287 | - image: luksa/kubia:v3 288 | name: nodejs 289 | readinessProbe: 290 | periodSeconds: 1 291 | httpGet: 292 | path: / 293 | port: 8080 294 | ``` 295 | 296 | **레디니스프로브 설정 적용 후 잘못된 버전 롤아웃** 297 | 298 | ``` 299 | [root@k8s-master ~]# kubectl replace -f kubia-deployment-v3-readiness.yaml 300 | ``` 301 | 302 | **롤아웃 후 포드 상태** 303 | 304 | ``` 305 | [root@k8s-master ~]# kubectl get po 306 | NAME READY STATUS RESTARTS AGE 307 | kubia-6b646f577b-n6nzx 1/1 Running 0 36m 308 | kubia-6b646f577b-nq7nk 1/1 Running 0 35m 309 | kubia-6b646f577b-zlbvf 1/1 Running 0 36m 310 | kubia-bb464699b-gjw7p 0/1 Running 0 41s 311 | ``` 312 | 313 | ![그림9.14](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/09fig14_alt.jpg) 314 | 315 | ``` 316 | [root@k8s-master ~]# kubectl describe deploy kubia 317 | Name: kubia 318 | Namespace: default 319 | CreationTimestamp: Fri, 08 Feb 2019 21:41:32 +0900 320 | Labels: 321 | Annotations: deployment.kubernetes.io/revision: 6 322 | Selector: app=kubia 323 | Replicas: 3 desired | 1 updated | 4 total | 3 available | 1 unavailable 324 | StrategyType: RollingUpdate 325 | MinReadySeconds: 10 326 | RollingUpdateStrategy: 0 max unavailable, 1 max surge 327 | Pod Template: 328 | Labels: app=kubia 329 | Containers: 330 | nodejs: 331 | Image: luksa/kubia:v3 332 | Port: 333 | Host Port: 334 | Readiness: http-get http://:8080/ delay=0s timeout=1s period=1s #success=1 #failure=3 335 | Environment: 336 | Mounts: 337 | Volumes: 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 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig02_alt.jpg) 106 | 107 | - 롤/롤바인딩 : 네임스페이스 수준의 리소스 권한 관리, 108 | - 클러스터롤/클러스터롤바인딩 : 클러스터 수준의 리소스 권한 관리 109 | 110 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig03_alt.jpg) 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 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig04_alt.jpg) 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 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig05_alt.jpg) 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 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig06_alt.jpg) 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 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig07_alt.jpg) 257 | 258 | - 클러스터롤을 이용하여 클러스터 수준에서 파드에 대한 접근 권한을 설정하더라도 롤바인딩으로 바인딩하면 자기 네임스페이스의 파드만 접근 가능하다. (ex, `view` 클러스터롤) 259 | 260 | ![](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/12fig10_alt.jpg) 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 | ![ch13_01](./ch13_01.png) 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 | ![ch13_02](./ch13_02.png) 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 | ![ch13_05](./ch13_05.png) 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 | ![architecture-03.jpg](../k8s-in-action-chap11/architecture-03.jpg) 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 | ![텍스트](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/18fig01_alt.jpg) 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 | ![18.2](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/18fig02.jpg ) 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 | ![18.3](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/18fig03_alt.jpg ) 164 | 165 | - HTTP GET 요청에 의해 API 서버는 모든 웹사이트 객체의 변경시마마다 감시 이벤트를 보낸다. 166 | - API 서버는 새로운 웹사이트 객체가 만들어 질 때마다 ADDED 감시 이벤트를 보낸다. 167 | - 컨트롤러는 ADDED를 받으면 watch 이벤트에 받은 객체에서 웹사이트 이름 Git Repo URL 추출 JSON 매니페스트를 API서버에 게시하여 디플로이먼트 및 서비스 객체를 만든다. 168 | 169 | ![18.4](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/18fig04.jpg ) 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 | ![18.5](https://dpzbhybb2pdcj.cloudfront.net/luksa/Figures/18fig05_alt.jpg ) 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 | --------------------------------------------------------------------------------