Kubernetes 관련 비지니스를 하고 있는 입장에서 봐도 놀랄일이지만
인프라 영역부터 개발자 영역까지 모두를 추상화시키는 클라우드 네이티브의 힘이란 참 대단하다.
오늘은 knative에서 주요기능들을 둘러보고자 한다.
개인적인 생각은 Kubernetes 진영이라고 해야하나 CNCF 진영이라고 해야하나..
그동안 노래를 부르고 주목했던 CRDs, Operators, Serverless Workload, CloudEvents, Mesh Layer 개념과 영역을 흡수해서 그 기반으로 확장시키고 있다.
Source는 K8s 외부의 데이터 소스를 프로비저닝하고 이를 클러스터로 라우팅하기 위한 추상화 레이어를 제공함. 아래와 같은 소스들을 제공하고 있음
Feed : EventType과 Action (CloudEvents 호환 HTTP endpoint)간의 연결을 정의하는 기본 객체
EventType and ClusterEventType : EventSource에서 분리되는 공통스키마로 특정 이벤트의 집합, EventType은 Namespace 범위내에서 사용되고 ClusterEventType은 모든 Namespace에서 사용될수 있도록 관리자에 의해 설치됨
EventSource and ClusterEventSource : 하나 이상의 EventTypes를 생성 할 수있는 외부 시스템을 기술함
마지막으로 Source에서 Endpoint까지 묶어주는 Flow라고 부르는 높은 수준의 추상화가 있다.
Spec으로 이벤트가 라우팅된 Channel과 Bus를 기재하여 사용할 수 있다.
Flow는 Eventing에서 최상위 개념으로 사용자가 선택할수 있고, 외부 Source 이벤트에서 목적지까지 원하는 경로를 기술할수 있다.
정리
워낙 방대한 양을 가지고 있고 이해하려고 노력하면서 적다보니 번역위주로 되어버렸다.
원래 다음번에 Nats Streaming을 다룰 예정이였으나,
당분간은 knative 구성요소를 이해하고 적용하는 위주로 포스팅을 할 예정이다. 아마도 모듈별(Serving, Building, Eventing) 시리즈가 될 듯 하다.
Dex는 "connectors"를 통해 다른 identity provider의 포털(게이트웨이, 중계자) 역할을 한다. 이를 통해 LDAP, SAML또는 GitHub, Google, AD(Active Directory)와 같은 기존 인증을 dex에게 위임할 수 있다. 클라이언트는 일단 인증 로직을 작성하여 dex와 통신하면 dex는 주어진 백엔드(여기서는 예시로 kubernetes 클러스터)에 대한 프로토콜을 처리하게 된다.
하지만 향후 Cloud Native 인증 카탈로그 중 하나로서 생각하고 있는 오픈소스이다 보니 테스트는 진행해봐야 할 것 같다.
기본적으로 Dex는 OIDC(OpenID Connect)를 사용한다.
그러므로 kubernetes 클러스터 사용자 인증용도로 Dex를 붙일수 있는 것이다.
또한 위에서 말한것과 같이 다른 identity provider를 사용하여 사용자를 인증하는데 아래 그림과 같이 connectors를 사용한다.
위 그림과 같이 "connectors"는 다른 identity provider를 통해 사용자를 인증하기 위해 dex에서 사용하는 기본 도구다. 결국 Dex는 기존에 활용하던 GitHub, LinkedIn 및 Microsoft AD와 같은 벤더 플랫폼이나 LDAP 및 SAML과 같은 기존 프로토콜 방식을 사용하여 kubernetes 클러스터 사용자 인증을 쉽게 구현할 수 있는 것이다. 큰 회사??들은 OpenLDAP이나 Microsoft Active Directory와 같은 사내 디렉토리 서비스를 운용하거나 내가 있는 작은 규모의 팀들은 대부분 Github이나 Google 계정은 가지고 있을 것이기 때문에 Dex와 같은 오픈소스를 사용함으로써 기존 identity provider 와 kubernetes를 쉽게 통합할 수 있다고 본다.
처음에도 언급했듯이 kubernetes 인증을 위해 무거운 keycloak을 사용을 하는 경우들이 많은데 간단하게 사용할 수 있는 dex를 사용하여 여러가지 identity provider의 connector 역할로 사용하기에는 무난한것 같다.
결국 Dex를 이용하면 GitHub를 비롯해 다양한 OpenID, OAuth 2.0 인증 서비스와 Kubernetes 클러스터를 엮기 쉬울수 있다.
단, 최근 commit이 3-4달전이라는것과 처음 stable helm에 등록된 후 업데이트가 없었다는 점이 걱정되는 부분이긴 하다. (묘하게 Redhat과 합병 일정이 오버랩 되긴한다...)
거의 2주만에 포스팅인데 '18 Google Next 키노트에서 knative라는 현재 개발중인 프로젝트와 유사한 오픈소스가 공개되었다. 다음번엔 knative도 한번 테스트를 해봐야 할듯 하다.
퍼블릭과 프라이빗 클러스터에 대한 고민이 생기기 시작한다. 다시금 오픈소스 플랫폼에 집중하기 위해 프라이빗으로 돌아가야 하나라는 고민에도 빠져있는 상태에서 knative, GKE on Premise 같은 엔터프라이즈에서 프라이빗 구축을 위한 것들도 나오고 있어서 더더욱 고민에 빠진 요즘이다...
제한된 예산과 시간, 인력으로 서비스를 만들다 보니 어려운 점들이 참 많다. 특히나 TLS 인증서 같은 경우 관리하는것이 은근히 귀찮다. 인증서 만료일 관리하는것 부터 어느 페이지까지를 평문으로만 두어야 하는지도... 그런데 바보같은 이슈를 저지르고 말았다. 도메인을 신청하고 fanout, Name based virtual hosting 방식중에 고민하다가 설계를 virtual host로 하였다.
그런데 문제는 어이없는곳에서 나왔다. *.xxxxx.io 라고 wildcard SSL 인증서를 신청하였는데 virtual host방식으로 2차 subdomin (예, api.stg.xxxxx.io)으로 ingress를 마구잡이로 생성하고 인증서를 적용을 했다. 하지만 인증서 오류 ㅋㅋ
인터넷 비지니스를 거의 해보지 않아서 인지, 이런 기본적인 내용도 간과하고 서비스를 구상한 내가 모든걸 책임져야하는 상황이 와버렸다.
설계를 이상하게 해서 wildcard 도메인을 몇십개를 신규로 계약해야하는 쓸데없는 비용을 들어야하는 처지가 되었다. 하지만 그냥 죽으란 법은 없는게 사람사는 세상이고 이런 고민들을 분명 다른사람들도 했을거야라고 생각하면서 떠오른게 let's encrypt 였고 찾아보니 이걸 또 kubernetes에서 발급부터 갱신까지 해주는 오픈소스를 찾아 적용까지 하고 이렇게 포스팅을 한다.
인증서 없이 구성할수도 있지만 기본적으로 kubernetes 는 secret과 ingress의 간단한 설정만으로 TLS를 구성할 수 있다.
Cert-manager : K8s 클러스터내에서 TLS인증서를 자동으로 프로비저닝 및 관리하는 오픈소스
Let’s Encrypt : 자율적으로 작동하는 개방된 CA(Certificate authority-인증기관)으로 공공성(공공의 이익)을 위해서 운영되는 오픈소스
Cert-manager는 AWS의 Certificate Manager와 유사하게 인증서를 발급하고 설정할수 있다. 3개월 유효기간을 가지는 let's encrypt를 사용하기에 인증서 만료시 문제가 될거라 생각했지만 이것도 자동으로 갱신을 해주는 관리 app.을 같이 배포하기 때문에 인증서 관리에 대한 부담을 덜수 있다.
기본적으로 Cert-manager는 let's encrypt, Vault 같은 것을들 사용하여 인증서 발급을 할 수 있다. 인증서 발급후에도 만료가 되기 전에 바로 갱신을 자동으로 한다.
$ helm install --name cert-manager --version v0.3.1 \ --namespace kube-system stable/cert-manager $ helm status cert-manager LAST DEPLOYED: Fri Jun 15 10:02:45 2018 NAMESPACE: kube-system STATUS: DEPLOYED RESOURCES: ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE cert-manager-cert-manager-5f76b676b4-5tdh8 1/1 Running 0 2d ==> v1/ServiceAccount NAME SECRETS AGE cert-manager-cert-manager 1 28d ==> v1beta1/CustomResourceDefinition NAME AGE certificates.certmanager.k8s.io 28d clusterissuers.certmanager.k8s.io 28d issuers.certmanager.k8s.io 28d ==> v1beta1/ClusterRole cert-manager-cert-manager 28d ==> v1beta1/ClusterRoleBinding NAME AGE cert-manager-cert-manager 28d ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE cert-manager-cert-manager 1 1 1 1 28d NOTES: cert-manager has been deployed successfully! In order to begin issuing certificates, you will need to set up a ClusterIssuer or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer). More information on the different types of issuers and how to configure them can be found in our documentation: https://cert-manager.readthedocs.io/en/latest/reference/issuers.html For information on how to configure cert-manager to automatically provision Certificates for Ingress resources, take a look at the `ingress-shim` documentation: https://cert-manager.readthedocs.io/en/latest/reference/ingress-shim.html
그리고 issuer manifest를 배포한다.
(letsencrypt-issuer.yaml)
# Copyright 2018 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: server: https://acme-staging-v02.api.letsencrypt.org/directory email: '' privateKeySecretRef: name: letsencrypt-staging http01: {} --- apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: '' privateKeySecretRef: name: letsencrypt-prod http01: {}
두개의 endpoint를 생성한다. (Staging, Prod)
$ sed -e "s/email: ''/email: $EMAIL/g" letsencrypt-issuer.yaml | \ kubectl apply -f- clusterissuer "letsencrypt-staging" created clusterissuer "letsencrypt-prod" created
Web App을 HTTP Ingress 형태로 배포한다. TLS발급전에 미리 HTTP(80) Ingress를 배포해야 한다. 이후 Let's encrypt TLS배포 후 TLS spec을 추가하고 재배포를 해야한다.
인증서를 발급할때는 위에서 만든 Ingress로 생성하고namespace별 생성이 필요하다.
apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: auth-dev-tls namespace: auth #### namespace별 생성 spec: secretName: auth-dev-tls issuerRef: name: letsencrypt-prod kind: ClusterIssuer commonName: auth.dev.action.cloudz.co.kr dnsNames: - auth.dev.action.cloudz.co.kr acme: config: - http01: ingress: auth-ingress ### 기존에 만든 HTTP ingress 명 domains: - auth.dev.action.cloudz.co.kr
$ kubectl apply -f auth-dev-tls.yaml certificate "auth-dev-tls" created
이후 TLS 생성 상태를 확인한다. 이때 Message에서 Normal CeritifcateIssued Certificated issued successfully 문구가 확인되면 인증서 발급이 정상적으로 되었다고 볼 수 있다.
이때 발급되는 시간이 네트워크 환경에 따라 1~5분정도 소요될때로 있는것으로 보인다.
$ kubectl describe -f auth-dev-tls.yaml ... Type Reason Message ---- ------ ------- Warning ErrorCheckCertificate Error checking existing TLS certificate: secret "auth-dev-tls" not found Normal PrepareCertificate Preparing certificate with issuer Normal PresentChallenge Presenting http-01 challenge for domain foo.kubernetes.tips Normal SelfCheck Performing self-check for domain auth.dev.action.cloudz.co.kr Normal ObtainAuthorization Obtained authorization for domain auth.dev.action.cloudz.co.kr Normal IssueCertificate Issuing certificate... Normal CeritifcateIssued Certificated issued successfully
$ kubectl apply -f auth-tls-ingress.yaml $ kubectl get ing NAME HOSTS ADDRESS PORTS AGE auth-ingress auth.dev.action.cloudz.co.kr 10.1.1.182 80, 443 14h
설계의 실수로 시작되었지만 정리하다보니 인증서 갱신이 자동을 된다는 점과 또한 Kubernetes환경에서 TLS관리가 간편하다는것을 알게 되었다. 인증서 비용을 아낀것도 하나의 성과이기도 하다. 인증서는 써야하겠고 정말 많은 애플리케이션들을 생성/삭제를 상시 해야하는 환경이라면 한번 적용해볼만한 가치가 있는것 같다.
정의(Definition) : Kubernetes Application 을 패키징, 배포, 관리하는 방법. Helm과는 조금 달라 따로 이야기 해보고자 한다.
기본적으로 Kubernetest Application은 kubernetes에 의해 배포되고 kubect과 kube-API를 사용하여 관리한다.
결론적으로 말하면 Kubernetes에 내가 만든 application을 서비스하고 관리하려면 결국 Kubernetes의 API들을 모두 이해하고 사용할수 있어야 한다. 일반적인 개발자에게 진입장벽이 어느정도 있다고 보여지며 이를 모두 이해시키는것도 조직적인 측면에서는 낭비일수 도 있다. 그래서 Helm등을 통한 application배포 전략을 세우기도 하지만 이것도 한계가 있을수 있다. 그래서 Operator는 Kubernetes상에서 application을 관리하는 런타임이라고 생각하는게 맞는것 같다.
주로 저장소나 키-밸류 스토어, RDB등의 운영안정성을 위한 클러스터 구성을 위한것들이 대부분이며 점점 도입을 해나가는 추세이긴것 같긴 하다. 이번에 진행해보고자 하는건 분산 키-밸류 스토어이자 kubernetes의 메인저장소로 쓰이는 etcd 이다.
기본적으로 etcd cluster objects는 CRDs로 생성한다. Kubernetes 기본 resource가 아닌 CRDs 이기 때문에 안정성 측면에서 불안하다고 생각할수도 있다. 하지만 User Aggregated API Servers 를 적용하여 안정성, 유효성 검사 및 버전 관리가 개선되었다고 한다. Aggregated API를 사용하면 사용자에게 최소한의 영향을 주면서 Kubernetes objects가 생성되거나 사용자가 etcd operator를 배포,관리할수 있다. (말이 길어서 그렇지 그냥 etcd 클러스터 구성)
현재 프로젝트는 베타로 0.9.2까지 나와 있으며 RedHat이 CoreOS를 합병하는 바람에 문서들이 업데이트가 늦어지는것 같긴하지만 조만간에 1.0이 나올것 같긴 하다.
Operator SDK를 사용하면 Kubernetes API 상세스펙을 배우지 않고도 쉽게 operator 빌드가 가능하다.
또한 관리를 위한 Operator Lifecycle Manager나 Operator Metering 같은 기능을 사용하여 좀더 관리측면에서 강화하고자 하는 CoreOS진영의 노력이 보이는듯 하다.
아직 alpha, beta단계의 operator 프로젝트들이 대부분이지만 helm차트와 같이 kubernetes API 스펙을 이해하고 사용하지 않고도 쉽게 운영자가 기반 어플리케이션을 관리 할수 있다는것으로 보아 향후 RedHat이나 Oracle 진영에서 본인들 kubernetes 관련 제품들을 홍보하고 서비스라인에 포함시키는 방향으로 적극적으로 개발을 하고 있는것으로 보인다. 앞으로 mysql, redis, kakfa 등을 operator로 배포하고 관리하는 일이 더 많아 질것 같다.
Cilium은 Docker 및 Kubernetes와 같은 리눅스 컨테이너 프레임 워크에 API 기반의 네트워크 보안 필터링을 제공하며,
또한, BPF라는 새로운 Linux 커널 기술을 사용하여 컨테이너/POD ID를 기반으로 네트워크 계층 및 응용 프로그램 계층 보안 정책을 정의 및 적용하는 것에 있어서 간단하면서 효율적인 방법을 제공하고 있습니다.
윗분들처럼 커널 자체를 분석하고 공부하고 기여하려면 소요되는 시간이 더 걸릴거 같고 서비스를 운영하고 개발하는 입장에서는 내 프로젝트에 적용 가능성을 검증하는것이 나을것 같아 정리를 해본다.
iptables을 기반으로 IP와 Port기반의 전통적인 포워딩 기술은 벌써 20년이라는 세월동안 널리 사용되어 왔다. 특히 퍼블릭/프라이빗 클라우드 제품군들 모두 iptables기반의 Security Group등을 기본으로 제공하고 있고 Kubernetes 마저도 CNI 핵심으로 iptables을 활용하고 있다.
동적으로 변화하고 매우 복잡한 마이크로서비스를 사용하는 시대에 전통적인 방식의 IP, Port관리는 비효율적인 측면이 없지 않다. BPF(아래인용)을 활용하여 리눅스 커널내에서 데이터 포워딩을 할 수 있고 Kubernetes Service기반 Load Balancing이나 istio와 같은 Service Mesh를 위한 Proxy Injection 을 통해 여러 활용을 할 수 있을거라고 Cilium 프로젝트는 이야기 하고 있다.
버클리 패킷 필터(Berkeley Packet Filter, BPF)
BPF는 버클리 패킷 필터(Berkeley Packet Filter)의 줄임말이다. 이름 그대로 패킷을 걸러내는 필터이다. 그런데 BSD에서의 BPF는 네트워크 탭(리눅스의 PF_PACKET)까지 아우르는 개념이다. 옛날 옛적에 유닉스에는 CSPF(CMU/Stanford Packet Filter)라는 게 있었는데 BPF라는 새 구조가 이를 대체했다. 이후 리눅스에서는 네트워크 탭을 나름의 방식으로 구현하고 패킷 필터 부분만 가져왔다. 리눅스의 패킷 필터를 리눅스 소켓 필터링(LSF: Linux Socket Filtering)이라고도 한다.
(발췌) https://wariua.github.io/facility/extended-bpf.html
Cilium은 Dockercon 2017에서 최초 announce를 하였고 2018년 4월 24일에 1.0이 정식 Release된 이후 많은 관심을 받을것으로 예상되어 실제 서비스에 적용해볼 필요가 있을거 같아 minikube로 테스트한 내용을 끄젹여 본다.
$ kubectl create -n kube-system -f https://raw.githubusercontent.com/cilium/cilium/v1.1/examples/kubernetes/addons/etcd/standalone-etcd.yaml service "etcd-cilium" created statefulset.apps "etcd-cilium" created
Kubernetes 클러스터에 Cilium을 인스톨한다. 기본적으로 DaemonSet 형태로 배포되기 때문에 Node당 한개의 Cilium Pod를 볼 수 있다. Cilium은 kube-system namespace에서 실행된다.
$ kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.1/examples/kubernetes/1.9/cilium.yaml configmap "cilium-config" created secret "cilium-etcd-secrets" created daemonset.extensions "cilium" created clusterrolebinding.rbac.authorization.k8s.io "cilium" created clusterrole.rbac.authorization.k8s.io "cilium" created serviceaccount "cilium" created
kube-system namespace에 RBAC설정과 함께 Cilium이 배포되고, ConfigMap, DaemonSet 형태로 배포가 된다.
아래 데모그림을 보면 스타워즈의 영감을 받아서인지 deathstar, xwing 등으로 구분한것을 확인할수 있다. deathstar deployments의 경우 80포트로 http 웹서버가 2개의 pod replica로 Load Balancing되고 있다. deathstar 서비스는 우주선이 착륙할수 있도록 활주로를 서비스하고 있다. 하지만 제국군의 tiefighter 만 착륙하도록 해야하므로 보안설정을 해야하는 상황이다.
아래 http-sw-app.yaml 은 세가지 deployment를 가지고 있고 각각의 deployment는 (org=empire, class=deathstar), (org=empire, class=tiefighter), (org=alliance, class=xwing)와 같이 label 정보를 가진다. deathstar Service는 (org=empire, class=deathstar) label을 가지고 Load Balancing을 한다.
$ kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.1/examples/minikube/http-sw-app.yaml service "deathstar" created deployment "deathstar" created deployment "tiefighter" created deployment "xwing" created
총 4개의 pod와 1개의 서비스를 확인할수 있다.
$ kubectl get pods,svc NAME READY STATUS RESTARTS AGE pod/deathstar-566c89f458-mqgfs 1/1 Running 0 1h pod/deathstar-566c89f458-wlc4c 1/1 Running 0 1h pod/tiefighter 1/1 Running 0 1h pod/xwing 1/1 Running 0 1h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/deathstar ClusterIP 10.109.80.174 <none> 80/TCP 1h service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h
각각의 pod는 Cilium에서는 Endpoints형태로 표현된다. 아래와 같이 ingress, egress policy를 확인할수 있고 아직 아무런 network policy 적용을 하지 않았기 때문에 모두 Disabled 상태로 보인다.