Skip to main content

· 14 min read

https://github.com/GoogleCloudPlatform/microservices-demo에서 소개된 Demo Application인 Hipster Shop을 Kubernetes 기반으로 배포하고 관련 오픈소스들을 더 알아보고자 한다.

위 링크에 가서 보면 알수 있지만 Hipster Shop 아래 그림처럼 10개의 Microservice로 구성되어 있고 상품을 검색 및 구매할 수있는 웹 기반 이커머스 Application으로 구성 되어있다.

arch

각각의 서비스는 gRPC 로 통신하고 외부 사용자만 HTTP로 접근한다. 모든 서비스는 서로 다른 언어(Go, C#, Node.js, Python, Java)로 구성되어 있고 대부분의 Microservice들은 Istio service mesh 형태로 구성할수 있도록 되어있다. Skaffold를 통해 배포하고 OpenCensus라고 하는 gRPC/HTTP기반 Tracing tool을 활용하여 Google Stackdriver로 보내도록 되어있지만 Prometheus에 통합하는 방향으로 작성하기 위해서 Prometheus 기반으로 Metric을 수집하는 Fork된 데모 Application을 검색을 통해 찾을수 있었다.
https://github.com/census-ecosystem/opencensus-microservices-demo

OpenCensus

OpenCensus(OC)는 Microservice 및 Monolith 서비스를 Tracing 및 Metric Monitoring 할 수 있는 라이브러리를 제공하는 오픈소스이다.

아래와 같이 다양한 언어를 지원 한다.

  • Java , Go, Python, C++, Nodejs, Erlang, Ruby, PHP, C#

또한 수집된 데이터를 Prometheus, Stackdriver Tracing and Monitoring, DataDog, Graphite, Zipkin, Jaeger, Azure Insights 등 과 같은 백엔드 Application으로 내보낼 수 있기 때문에 개발자, 운영자 측면에서 좋은 선택사항이 될 수 있다.

Microservice와 같은 분산 시스템에서 개발자/운영자 관점의 가장 중요한 미션은 각각의 수행되는 서비스들의 실행 시간을 확인하고 상호 API간 통신이 얼마나 걸리는지를 확인하고 Span(아래 그림참조)에서 가장 지연이 발생하는 서비스를 빨리 찾아내 확인하고 조치하는 것이라 할 수 있다.

span

OpenCensus는 주로 두가지 기능으로 활용된다.
첫번째는 Metric 수집이고 두번째는 Tracing 기능이다.
Log같은 경우 현재 미지원이지만 다음 메이저 릴리즈에 추가될 예정이라고 하니 조금더 지켜보면 좋을것 같다.

  • Metrics

    • 데이터베이스 및 API의 응답시간, 요청 content length, open file descriptor 수와 같이 추적하고자하는 정량 데이터를 말한다. Metric 을 시각화해서 보면 응용 프로그램 및 서비스의 성능과 품질을 측정하는 데 도움이 될 수 있다. 예를 들면 요청 응답시간의 평균값이나 cache hit/miss 수와 같은 것들이 될 수 있다.
  • Traces

    • 서비스 요청에 대한 애플리케이션 또는 서비스 구조를 확인할수 있고 모든 서비스 간 데이터 흐름을 시각화하여 아키텍처상의 병목 현상을 파악하는 데 도움이 된다.

Hipster Shop Demo

위에서 언급했던 내용처럼 GCP에서 작성한 Hipster Shop Demo는 minikube 및 GCP 데모로 되어있고 코드안에 기본 Metric 설정이 Stackdriver으로 되어있어 Prometheus Exporter 적용을 하려면 코드 수정이 필요하기 때문에 Prometheus기반으로 작성된 Forked Repo를 살펴보기로 하였다.

Requirement

현재 가지고 있는 MacOS 환경에서 구동하였다. 최소 스펙은 따로 기재하지 않았으나 K8s 1.11 이상을 권장한다.

  • kubectl : v1.10.7
  • Minikube : v0.34.1
  • Kubernetes : v1.13.3
  • skaffold : v0.24

minikube 기동

최소 3 CPU, 6Gb Memory가 필요하다. 그냥 minikube를 구동시기면 4 CPU, 8Gb 로 구동이 되기 때문에 별다른 옵션 없이 default로 구동하면 된다.

$ minikube start
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 6h v1.13.3

Repository Clone

$ git clone https://github.com/census-ecosystem/opencensus-microservices-demo.git
$ cd opencensus-microservices-demo

내부 구조를 살펴보면 기본적으로 skaffold를 활용하여 배포를 진행을 하는 것을 알수있다.
skaffold는 로컬에서 Kubernetes 기반 어플리케이션 개발과 배포(CD)를 빠르게 도와주는 CLI tool이다. 소스코드의 변화를 감지하여 build, registry push/tagging, deploy까지 자동으로 할 수 있는 로컬 기반 도구이다.

skaffold dev는 로컬 환경의 반복적인 개발에 활용하고 실제 배포는 CI Process에서 skaffold run을 통해 배포를 진행할 수 있다.

skaffold demo

Kubernetes 배포툴에 대해 비교한 글은 블로그 링크를 통해 확인할 수 있다.

아래에서는 skaffold에 대한 자세한 내용은 미뤄두고 배포하는 도구로서만 설명한다.

기본적으로 구성을 하고자 하는 내용은 helm처럼 template 파일을 사용하게 되는데 프로젝트 root에 skaffold.yaml 에 build를 위한 image name, tag, src 위치등 기본적인 내용을 기재한다. 파일내용을 살펴보면 build에 관련된 내용들을 작성하고 deploy할 manifests의 위치까지 지정하도록 되어있다. 로컬환경에서 확인을 위해 grafana, prometheus, jaeger가 추가된 것을 확인할 수 있다.

apiVersion: skaffold/v1alpha2
kind: Config
build:
tagPolicy:
gitCommit: {}
artifacts:
- imageName: gcr.io/opencensus-microservices-demo/emailservice
workspace: src/emailservice
- imageName: gcr.io/opencensus-microservices-demo/productcatalogservice
workspace: src/productcatalogservice
- imageName: gcr.io/opencensus-microservices-demo/recommendationservice
workspace: src/recommendationservice
- imageName: gcr.io/opencensus-microservices-demo/shippingservice
workspace: src/shippingservice
- imageName: gcr.io/opencensus-microservices-demo/checkoutservice
workspace: src/checkoutservice
- imageName: gcr.io/opencensus-microservices-demo/paymentservice
workspace: src/paymentservice
- imageName: gcr.io/opencensus-microservices-demo/currencyservice
workspace: src/currencyservice
- imageName: gcr.io/opencensus-microservices-demo/cartservice
workspace: src/cartservice
- imageName: gcr.io/opencensus-microservices-demo/frontend
workspace: src/frontend
- imageName: gcr.io/opencensus-microservices-demo/loadgenerator
workspace: src/loadgenerator
- imageName: gcr.io/opencensus-microservices-demo/adservice
workspace: src/adservice
- imageName: gcr.io/opencensus-microservices-demo/grafana
workspace: src/grafana
- imageName: gcr.io/opencensus-microservices-demo/prometheus
workspace: src/prometheus
- imageName: gcr.io/opencensus-microservices-demo/jaeger
workspace: src/jaeger
deploy:
kubectl:
manifests:
- ./kubernetes-manifests/**.yaml

Go로 작성된 Frontend microservice을 살펴보자. [./src/frontend/main.go]

library 추가 및 http handler 초기화

Go기반 exporter 패키지(jaeger,prometheus)를 추가적으로 import 하고 http handler를 위한 ochttp 패키지를 추가하였다.

import (
...
"go.opencensus.io/exporter/jaeger"
"go.opencensus.io/exporter/prometheus"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/plugin/ochttp/propagation/b3"
...
)
func main() {
...
var handler http.Handler = r
handler = &logHandler{log: log, next: handler}
handler = ensureSessionID(handler)
// add opencensus instrumentation
handler = &ochttp.Handler{
Handler: handler,
Propagation: &b3.HTTPFormat{}}
log.Infof("starting server on " + addr + ":" + srvPort)
log.Fatal(http.ListenAndServe(addr+":"+srvPort, handler))
}

exporter 등록(Jaeger Tracing 및 Prometheus exporter)

예시처럼 각각의 서비스에 jaeger와 prometheus exporter Endpoint를 쉽게 등록할수 있다.
또한 initTracing() 에서는 데모를 위해 trace.AlwaysSample()을 사용하였다. 실제 운영환경에서는 다음 링크를 참고해서 사용하는 것을 권고하고 있다.

...
func initJaegerTracing(log logrus.FieldLogger) {
// Register the Jaeger exporter to be able to retrieve
// the collected spans.
exporter, err := jaeger.NewExporter(jaeger.Options{
Endpoint: "http://jaeger:14268",
Process: jaeger.Process{
ServiceName: "frontend",
},
})
if err != nil {
log.Fatal(err)
}
trace.RegisterExporter(exporter)
}

func initTracing(log logrus.FieldLogger) {
// This is a demo app with low QPS. trace.AlwaysSample() is used here
// to make sure traces are available for observation and analysis.
// In a production environment or high QPS setup please use
// trace.ProbabilitySampler set at the desired probability.
trace.ApplyConfig(trace.Config{
DefaultSampler: trace.AlwaysSample(),
})
initJaegerTracing(log)
}

func initPrometheusStatsExporter(log logrus.FieldLogger) *prometheus.Exporter {
exporter, err := prometheus.NewExporter(prometheus.Options{})

if err != nil {
log.Fatal("error registering prometheus exporter")
return nil
}

view.RegisterExporter(exporter)
return exporter
}
func startPrometheusExporter(log logrus.FieldLogger, exporter *prometheus.Exporter) {
addr := ":9090"
log.Infof("starting prometheus server at %s", addr)
http.Handle("/metrics", exporter)
log.Fatal(http.ListenAndServe(addr, nil))
}
...

Demo Application 배포

minikube에 Hipster Shop Demo를 배포한다. 단순하게 skaffold run 명령으로 진행하면 된다.

$ skaffold run

현재 사용중인 2018 Macbook Pro(3.1 GHz Intel Core i7, 16GB) 상의 Docker기반 minikube 환경으로도 배포를 하였는데 시간이 꽤 소요되었다.(20분이상)
코드를 실시간으로 수정하고 빌드, 배포되는 것은 skaffold dev 명령으로 확인할 수 있다. 진행되는 과정을 보면 draft.sh 프로젝트와도 꽤 유사하다고 볼 수 있다.

에러없이 run이 실행되고 난후 minikube에 배포된 pod와 service를 확인한다. 중간에 loadgenerator가 init인 이유는 minikube 자원이 부족해서 발생하는 현상이다.

$ kubectl get pod
NAME READY STATUS RESTARTS AGE
adservice-7c7d687dcb-xzr4m 1/1 Running 1 4h
cartservice-f54bcb9ff-tcfgn 1/1 Running 4 4h
checkoutservice-576446687b-95bwj 1/1 Running 1 4h
currencyservice-5bd99bf97d-28mtz 1/1 Running 1 4h
emailservice-6cb587798d-wwzdh 1/1 Running 1 4h
frontend-6bf9796f7b-ch9pl 1/1 Running 4 4h
grafana-6678c5c5d9-2qx75 1/1 Running 1 4h
jaeger-5b66bdf7f7-csdzx 1/1 Running 2 4h
loadgenerator-7c4f446774-68djg 0/1 Init:0/1 1 4h
paymentservice-fc4c8589-wrfg7 1/1 Running 1 4h
productcatalogservice-84878c8b9c-jhgnw 1/1 Running 1 4h
prometheus-58d98b7578-87td6 1/1 Running 1 4h
recommendationservice-8564f9d894-smlpf 1/1 Running 1 4h
redis-cart-798bc66d58-xn6ff 1/1 Running 1 4h
shippingservice-789656f6bc-rgzrp 1/1 Running 1 4h

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
adservice ClusterIP 10.107.196.115 <none> 9555/TCP,9090/TCP 4h
cartservice ClusterIP 10.98.151.164 <none> 7070/TCP 4h
checkoutservice ClusterIP 10.97.9.197 <none> 5050/TCP 4h
currencyservice ClusterIP 10.103.112.225 <none> 7000/TCP 4h
emailservice ClusterIP 10.97.184.174 <none> 5000/TCP 4h
frontend ClusterIP 10.103.40.138 <none> 80/TCP,9090/TCP 4h
frontend-external LoadBalancer 10.108.170.241 <pending> 80:31944/TCP 4h
grafana ClusterIP 10.104.141.254 <none> 3000/TCP 4h
grafana-external LoadBalancer 10.102.166.138 <pending> 3000:30459/TCP 4h
jaeger ClusterIP 10.98.71.173 <none> 9411/TCP,5775/UDP,6831/UDP,6832/UDP,5778/TCP,16686/TCP,14268/TCP 4h
jaeger-external LoadBalancer 10.96.164.126 <pending> 16686:32362/TCP 4h
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6h
paymentservice ClusterIP 10.109.31.241 <none> 50051/TCP 4h
productcatalogservice ClusterIP 10.101.124.106 <none> 3550/TCP 4h
prometheus ClusterIP 10.103.107.213 <none> 9090/TCP 4h
recommendationservice ClusterIP 10.104.225.28 <none> 8080/TCP 4h
redis-cart ClusterIP 10.101.24.157 <none> 6379/TCP 4h
shippingservice ClusterIP 10.104.224.18 <none> 50051/TCP 4h

서비스 접속 및 Metric/Tracing 확인

로컬 minikube환경이기 때문에 external service가 pending이므로 service를 minikube NAT IP로 expose 시킨다.

$ minikube service frontend-external
$ minikube service grafana-external
$ minikube service jaeger-external
$ minikube service list
|-------------|-----------------------|-----------------------------|
| NAMESPACE | NAME | URL |
|-------------|-----------------------|-----------------------------|
| default | adservice | No node port |
| default | cartservice | No node port |
| default | checkoutservice | No node port |
| default | currencyservice | No node port |
| default | emailservice | No node port |
| default | frontend | No node port |
| default | frontend-external | http://192.168.99.101:31944 |
| default | grafana | No node port |
| default | grafana-external | http://192.168.99.101:30459 |
| default | jaeger | No node port |
| default | jaeger-external | http://192.168.99.101:32362 |
| default | kubernetes | No node port |
| default | paymentservice | No node port |
| default | productcatalogservice | No node port |
| default | prometheus | No node port |
| default | recommendationservice | No node port |
| default | redis-cart | No node port |
| default | shippingservice | No node port |
| kube-system | kube-dns | No node port |
|-------------|-----------------------|-----------------------------|

3개의 서비스로 각각 접속이 되는것을 확인할수 있다. Grafana 대시보드로 들어가면 현재 수집되는 prometheus source(http://prometheus:9090)를 통해 OpenCensus기반 Application Metric을 확인할 수 있다.

hipster_grafana

그림에서 보면 전체 서비스 응답에 대한 99% 백분위 지연시간이 944ms 인것을 확인할 수 있다.

또한 Jaeger를 통해 DAG(Directed acyclic graph) 및 서비스간 Tracing 을 확인할 수 있다.

DAG

tracing

정리

OpenCensus 기반으로 개발자가 코드를 작성하고 Microservice를 minikube에서 배포하고 Prometheus, Jaeger Exporter 연동을 통해 시스템뿐만이 아닌 Application기반 Metrics/Stats을 수집하고 개발자가 작성한 코드를 직접 Tracing하는 간단한 데모를 진행하였다. (Istio를 포함해서 Public환경에 배포해봐도 좋은 공부가 될 것 같다)

향후 OpenMetricOpencensus가 실제 개발자 기반으로 활성화되고 적용이 된다면 Telemetric 측면에서 많은 Use-Case가 도출될 수 있을것 같다.

위에서 언급했듯이 Prometheus기반 Kubernetes 클러스터를 운영하고 있는 팀의 경우 개발자의 작성 코드를 최소화할 수 있는 도구로서 충분히 활용될 수 있어 보인다.

꼭 Cloud Native 기반 Web 개발이 아니더라도 기존 공장, 금융, 병원 등 의 IoT나 센서/설비를 위한 비즈니스에도 Backend로서 확장성있는 도구로서 활용이 될 수 있을것 같다.

· 9 min read

Knative Routing

Knative는 앞에서도 몇번 언급하였지만 기본적으로 Routing을 사용하여 외부에 노출할 서비스들에 대한 HTTP Endpoint를 제공한다. 어떻게 보면 기본적으로 API Gateway 역할을 하기도 하고 Ingress 역할을 하기도 한다. 보통 Service mesh인 Istio를 사용하여 ingress를 구현하는것이 당연하다고 생각하기도 하지만 Istio의 모든 기능이 Knative에 필요하지는 않고 설치되는것 자체가 리소스 소모가 꽤 된다는것은 설치 해본사람은 알고 있을것이다.

Service

Kubernetes

ingress
이미지출처 : https://www.nginx.com/blog/announcing-nginx-ingress-controller-for-kubernetes-release-1-3-0/

Kubernetes에서는 일반적으로 서비스 접속을 구현하게 되면 기본적으로 Pod와 Service를 생성하고 Ingress를 사용하여 클러스터 내부로 들어오는 트래픽을 처리하게 된다.

Knative

Serving
이미지출처 : https://blog.openshift.com/knative-serving-your-serverless-services/

Knative에서는 앞선 Knative 관련 포스팅에서도 설명했듯이 Automatic scaling up and down to zero 특성을 가지고 있기에 Pod가 최초 실행되어있지 않은 상태에서 트래픽이 들어오게 되면 Knative Serving Activator에 의해서 Pod가 없는 Revision을 확인하고 Cold Start 형태로 프로비저닝하게 된다. 나는 이게 진정한 서버리스라고 생각하지만 주변에 반박하시는 분들도 간혹 있다.

이후 Pod가 Warm 상태가 되고 나면 Istio Route(Ingress Gateway)를 통해 트래픽이 Pod로 전달되어 통신이 이뤄지게 된다.

현재 Knative는 현재 Ingress Gateway 의존성을 가지고 있고 Envoy기반 Service Mesh인 Istio, Envoy기반 API Gateway인 Gloo 두가지 옵션으로 Ingress 구현이 가능하다.

Istio

Knative는 기본적으로 Ingress Gateway기능을 탑재하고 있는데 이는 Istio의 기능중 하나다.
Istio는 다음과 같은 Core Feature를 가진다. 상세한 내용은 https://istio.io/docs/concepts/what-is-istio/ 에서 확인하면 된다.

  • Traffic management
  • Security
  • Policies and Telemetry
  • Performance and Scalability

Istio는 48개의 CRDs(CustomResourceDefinition objects)를 가지고 있는데 이중 Knative Serving에서 사용하는건 VirtualService 단 하나다.

Gloo

Gloo는 Kubernetes-native ingress controller이자 Next Generation API Gateway 를 위해 시작된 프로젝트이다. 실제 Redhat에서 Openshift기반 Microservice 및 Istio 개발업무를 하다가 최근에 solo.io의 CTO로 이직한 Christian Posta가 밀고 있는 프로젝트이기도 하다.

gloo

Gloo는 Envoy Proxy 기반으로 동작하며 기존 Legacy부터 Container서비스, FaaS(AWS Lambda, Azure Functions, GCP Functions)영역의 Application들을 REST, gRPC, SOAP, Web Socker기반으로 Aggregate 해서 Function 기반 추상화를 구현해 주는 오픈소스 프로젝트라 정의 할 수 있다.

Istio의 Ingress기능외의 여러가지 부가 기능(Telemetry, Security, Policy Enforcement)들은 Knative에서는 필요로 하지 않는다.

Knative API Gateway 로서 Istio가 아닌 Gloo가 조금더 경량화된 대안으로 결정되었고 Gloo를 통해 Knative 설치가 가능하게 되었다. 단, Knative Eventing 컴포넌트는 현재 지원하지 않는다고 한다.

Install Knative with Gloo

참고: Install with Gloo

간단하게 gloo와 Knative 설치를 해보자.

Requirements

Install Glooctl

gloo CLI (glooctl) Download
https://github.com/solo-io/gloo/releases

또는 직접 Download

$ curl -sL https://run.solo.io/gloo/install | sh
Attempting to download glooctl version v0.8.1
Downloading glooctl-darwin-amd64...
Download complete!, validating checksum...
Checksum valid.
Gloo was successfully installed 🎉

Add the gloo CLI to your path with:
export PATH=$HOME/.gloo/bin:$PATH

Now run:
glooctl install gateway # install gloo's function gateway functionality into the 'gloo-system' namespace
glooctl install ingress # install very basic Kubernetes Ingress support with Gloo into namespace gloo-system
glooctl install knative # install Knative serving with Gloo configured as the default cluster ingress
Please see visit the Gloo Installation guides for more: https://gloo.solo.io/installation/

PATH 등록

$ export PATH=$HOME/.gloo/bin:$PATH

gloo CLI 확인

$ glooctl --version
glooctl version 0.8.1

GCP 무료플랜으로 3-node 클러스터를 생성한다.

$ gcloud container clusters create gloo \
--region=asia-east1-a \
--cluster-version=latest \
--machine-type=n1-standard-2 \
--enable-autorepair \
--num-nodes=3

cluster 생성된것을 확인하고 cluster-admin 권한을 할당한다.

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-gloo-default-pool-f6bcc479-f8v9 Ready <none> 9m v1.11.7-gke.6
gke-gloo-default-pool-f6bcc479-fl78 Ready <none> 9m v1.11.7-gke.6
gke-gloo-default-pool-f6bcc479-gfgw Ready <none> 9m v1.11.7-gke.6

$ kubectl create clusterrolebinding cluster-admin-binding \
> --clusterrole=cluster-admin \
> --user=$(gcloud config get-value core/account)
Your active configuration is: [cloudshell-25974]
clusterrolebinding.rbac.authorization.k8s.io "cluster-admin-binding" created

Gloo와 Knative 설치를 한다. 미리 glooctl install knative --dry-run 으로 전체 manifest를 확인할 수 있다.

$ glooctl install knative

위에서 설치 과정은 생략했지만 Istio에 비해 CRD 개수가 적은 것을 알수있다. 또한 설치된 컴포넌트 역시 Istio에 비해서 간소화된 것을 알수 있다.

$ kubectl get pods --namespace gloo-system                                                                                         
NAME READY STATUS RESTARTS AGE
clusteringress-proxy-59fd6fb56-dmwwm 1/1 Running 0 7m
discovery-779884d4cc-xlql2 1/1 Running 6 7m
gloo-844fc79445-f4zvg 1/1 Running 6 7m
ingress-7d75c99874-s4m76 1/1 Running 6 7m

$ kubectl get pods --namespace knative-serving
NAME READY STATUS RESTARTS AGE
activator-746f6bb684-49tfh 1/1 Running 0 12m
autoscaler-955f679cd-tx5vw 1/1 Running 0 12m
controller-7fc84c6584-jbn69 1/1 Running 0 12m
webhook-7797ffb6bf-6pgsw 1/1 Running 0 12m

이전 포스팅에서도 사용했던 gcr.io/knative-sample/helloworld-go 이미지를 활용하여 샘플앱 Knative Service를 만든다.

service.yaml

$ vi service.yaml

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
runLatest:
configuration:
revisionTemplate:
spec:
container:
image: gcr.io/knative-sample/helloworld-go
env:
- name: TARGET
value: "Go Sample v1"
$ kubectl apply --filename service.yaml
service.serving.knative.dev "helloworld-go" created

앞에서도 설명했지만 Automatic scaling up and down to zero 으로 Cold Start가 되고 잠시후에 아래와 같이 Knative Service를 확인할 수 있다.

$ kubectl get ksvc helloworld-go -n default  --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain]($ kubectl get ksvc helloworld-go -n default  --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.d
omain
NAME DOMAIN
helloworld-go helloworld-go.default.example.com

Gloo Ingress를 확인한다. GKE에서 설치했기 때문에 자동으로 LoadBalancer가 연동되어 있는것을 확인할 수 있다.

$ kubectl get svc -n gloo-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusteringress-proxy LoadBalancer 10.3.244.54 34.**.**.54 80:30978/TCP,443:32448/TCP 39m
gloo ClusterIP 10.3.243.231 <none> 9977/TCP 39m

$ glooctl proxy url --name clusteringress-proxy
http://34.**.**.54:80

위에서 얻은 두가지 정보로 생성된 app을 테스트한다. Cold Start(default timeout 5분) 때문에 응답이 늦어질 수도 있지만 잠시 기다리면 응답을 확인할 수 있다.

$ curl -H "Host: helloworld-go.default.example.com" http://34.**.**.54:80
Hello Go Sample v1!

물론 Revision이나 Route를 활용하여 Knative의 기능에 대해서도 확인이 가능하다.

정리

Gloo는 Knative ClusterIngress CRD를 기반으로 동작하는 Istio의 대안으로서 가능성을 보여주고 있다. 이외에도 The Service Mesh Orchestration Platform SuperGloo, Debugger for microservices Squash 등 다양한 Mesh Layer기반의 오픈소스들을 확인할수 있다. 또다른 스쳐지나갈수도 있는 오픈소스일수도 있겠지만 현재 개발되는 로드맵(https://www.solo.io/)을 보면 Knative가 고도화되는 여정에 같이 가는 모습을 확인할 수 있다.

next-generation API Gateway로서 다양한 프로토콜을 지원하기 때문에 (HTTP1, HTTP2, gRPC, REST/OpenAPISpec, SOAP, WebSockets, Lambda/Cloud Functions) 더욱더 Microservices 및 Serverless Workload를 수행하기에 더욱 적합한 오픈소스로 보인다.

다음 주제

현재 해보고 싶은것은 베어메탈 Kubernetes Cluster기반 BGP로 동작하는 MetalLBCillium on AWS 인데 시간나는 대로 테스트 해봐야 겠다.

· 15 min read

knctl

knctl 은 Knative CLI 툴로 간단하게 knative cluster를 만들고 Knative를 추상화해서 앱까지 배포할 수 있는 오픈소스이다.

참고

Knative 다시 살펴보기

앞선 포스팅에서도 이야기 했지만 기존 FaaS(AWS Lambda, Google Cloud Funtions, Azure Function) 과는 다른 Serverless 개념으로 받아들어야 한다.

다시 한번 특징을 나열해보면 아래와 같다.

  • Serverless Container의 신속한 배치
  • Automatic scaling up and down to zero
  • Istio를 백엔드로 활용하여 Routing 구현
  • 배포 된 코드 및 config의 특정 시점 스냅 샷

그리고 다음과 같은 CRDs(Custom Resource Definitions)로 구성된 오브젝트들로 정의된다.

  • Route는 사용자 서비스에 대한 HTTP endpoint와 Routing을 제공한다.
  • Revisions은 code(function)와 config로 구성된 불변의 스냅샷. Route를 통해 endpoint를 할당받지 못한 Revision은 자동으로 kubernetes resource에서 삭제됨
  • Configuration은 요구되는 Revision 최신 상태를 기록하고 생성하고 추적할수 있음. 소스 패키지(git repo나 archive)를 컨테이너로 변환하기 위한 내용이나 메타데이터등을 포함시킬수 있음.
  • ServiceRoutesConfigurations 리소스의 추상화된 집합체. 모든 워크로드의 lifecycle을 관리함. 트래픽을 항상 최신의 Revision으로 route되도록 정의할수 있음

하나씩 조금 자세히 이야기 하면 아래처럼 정리 할수 있다.

Route

Route는 사용자 서비스(Code와 Configuration의 Revision정보)의 네트워크 Endpoint를 제공한다. kubernetes namespace는 여러개의 Route를 가질수 있다. Route는 하나 이상의 Revisions을 가지면서 수명이 길고 안정적인 HTTP Endpoint를 제공한다. 기본구성은 Route 객체가 Configuration에 의해 생성된 최신의 Revision으로 트래픽을 자동으로 지정한다. 조금더 복잡한 경우로는 istio의 기능을 활용하여 트래픽을 백분율 기준으로 Route를 지정할 수 있다.

Revision

Revision은 Code와 Configuration의 불변의 스냅샷이다. 하나의 Revision은 컨테이너 이미지를 참조하고 선택적으로 Build를 참조할 수 있다. RevisionConfiguration이 업데이트 시 생성된다.
Route를 통해 http주소 지정이 불가능한 Revision은 폐기 되고 관련된 kubernetes 리소스가 삭제가 된다. 시간이 지남에 따라 Configuration이 생성한 Revision 히스토리가 제공되고 사용자는 이전 Revision로 쉽게 롤백 할 수 있다.

Configuration

Configuration은 최신의 Revision상태를 설명하고, 생성하고, 원하는 상태가 갱신될때 Revision의 상태를 추적한다. ConfigurationBuild를 참조하여 소스(git repo 또는 archive)를 컨테이너로 변환하는 방법에 대한 가이드가 포함되어 있거나 단순히 컨테이너 이미지 및 수정에서 필요한 메타 데이터 만 참조 할 수 있다.

Product Integration

2019년 2월 현재 0.3이 릴리스되고 있고 벌써 여러 제품에 통합이 되고 있다.

최근 IBMthink 2019에서 Managed Knative (Experimental)를 내놓기도 하였다.
https://www.ibm.com/blogs/bluemix/2019/02/announcing-managed-knative-on-ibm-cloud-kubernetes-service-experimental/

Istio를 포함한 Knative 마저도 품는 모습으로 managed kubernetes 영역에서 글로벌 플레이어들 모두 서로 치고나가는 모습들을 볼수 있다.

작년 11월에는 Gitlab 제품안에 serverless라는 extension형태의 서비스가 추가 되기도 하였고,
https://about.gitlab.com/press/releases/2018-12-11-gitlab-and-triggermesh-announce-gitlab-serverless.html

triggermesh 라는 곳에서는 serverless management platform이라는 이름으로 knative 기반 멀티 서버리스 플랫폼을 출시하기도 하였다.
https://triggermesh.com/serverless_management_platform/

Pivotal Function Service (PFS), Google GKE SERVERLESS ADD-ON 등은 아직 early access 신청만 받고 있는 상태이다.

오늘은 간단하게 배포할수 있는 툴 knctl과 관련 use-case를 소개하고자 한다.

Kubernetes Cluster 생성

일단 GKE Free tier에서 Cluster를 하나 생성하자.

gcloud container clusters create knative \
--region=asia-east1-a \
--cluster-version=latest \
--machine-type=n1-standard-2 \
--enable-autoscaling --min-nodes=1 --max-nodes=5 \
--enable-autorepair \
--scopes=service-control,service-management,compute-rw,storage-ro,cloud-platform,logging-write,monitoring-write,pubsub,datastore \
--num-nodes=3

cluster 생성된것을 확인하고 cluster-admin 권한을 할당한다.

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-knative-default-pool-d1a39347-5m5t Ready <none> 1m v1.11.7-gke.4
gke-knative-default-pool-d1a39347-l6zh Ready <none> 1m v1.11.7-gke.4
gke-knative-default-pool-d1a39347-qv5r Ready <none> 1m v1.11.7-gke.4

$ kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=$(gcloud config get-value core/account)
Your active configuration is: [cloudshell-4728]
clusterrolebinding.rbac.authorization.k8s.io "cluster-admin-binding" created

knctl 설치

이번 포스팅에서는 Mac OS 설치 기준으로 작성하였다.

homebrew 설치

brew install starkandwayne/kubernetes/knctl

binary

https://github.com/cppforlife/knctl/releases

$ wget https://github.com/cppforlife/knctl/releases/download/v0.1.0/knctl-darwin-amd64
$ mv knctl-* /usr/local/bin/knctl
$ chmod +x /usr/local/bin/knctl

knctl 로 Knative 배포

설치한 knctl로 Knative 배포를 진행한다. 설치되는 내용을 지켜보고 있으면 istio를 먼저 배포하고 그다음에 Knative를 설치하는 것을 확인할 수 있다. 배포되는 모듈들의 상태를 하나하나 체크해서 배포하기 때문에 설치상에 과정들을 확인할 수 있다.

$ knctl install --exclude-monitoring

테스트를 위한 namespace hello-test를 생성한다.

$ kubectl create namespace hello-test
namespace "hello-test" created

knctl deploy 명령으로 최초 revision을 배포한다.
아래 결과를 보면 hello-00001 이라고 하는 최초의 revision을 작성하기 때문에 latest tag를 달고 배포를 하게 된다.

$ knctl deploy \
--namespace hello-test \
--service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev1

Name hello

Waiting for new revision to be created...

Tagging new revision 'hello-00001' as 'latest'

Tagging new revision 'hello-00001' as 'previous'

Annotating new revision 'hello-00001'

Waiting for new revision 'hello-00001' to be ready for up to 5m0s (logs below)...

hello-00001 > hello-00001-deployment-5cdbfc9bc9-hks6t | 2019/02/17 22:27:50 Hello world sample started.

Revision 'hello-00001' became ready

Continuing to watch logs for 5s before exiting

Succeeded
kubectl get svc knative-ingressgateway -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
knative-ingressgateway LoadBalancer 10.63.253.209 34.***.***.248 80:32380/TCP,443:32390/TCP,31400:32400/TCP,15011:30082/TCP,8060:31125/TCP,853:32009/TCP,15030:31102/TCP,15031:31631/TCP 6h

위처럼 Knative가 프로비저닝 되면서 ingress-gateway가 하나 생성이 되어있는 것을 확인할 수 있고 knctl로도 ingress를 확인이 가능하다.

$ knctl ingress list
Ingresses

Name Addresses Ports Age
knative-ingressgateway 34.***.***.248 80 6h
443
31400
15011
8060
853
15030
15031

1 ingresses

Succeeded

Knative custom domain 연결

Domain이 별도로 없기 때문에 Knative는 내부적으로 example.com이라고 하는 기본 domain을 사용한다. 그래서 실제 knctl curl 명령은 내부적으로 hello.hello-test.example.com으로 curl을 실행하게 되고 해당 결과를 아래와 같이 확인할 수 있다.

$ knctl curl --service hello -n hello-test
Running: curl '-sS' '-H' 'Host: hello.hello-test.example.com' 'http://34.***.***.248:80'

Hello Rev1!

Succeeded

kubernetes node가 3개이므로 3개의 pod가 생성된 것을 확인할 수 있다. 일정시간(default:5분)이 지나면 zero to scale 관점에서 pod가 종료되므로 다시 확인할때는 다시 curl 명령을 날리게 되면 다시 pod가 올라오게 된다. 해당 개념은 FaaS또는 AWS Lambda에서 Cold-Start와 동일한 것이라 볼 수 있다.

AWS Cold Start 참고 : https://novemberde.github.io/aws/2018/02/02/Lambda_coldStart.html

$ kubectl get po
NAME READY STATUS RESTARTS AGE
hello-00001-deployment-5cdbfc9bc9-hks6t 3/3 Running 0 4m

가지고 있는 도메인이 있다면 위에서 나온 34.***.***.248 IP를 domain에 매핑해보자. 아래에서는 기존 보유중인 skcloud.io 도메인을 연결하였다.

$ dig knative.skcloud.io
;; ANSWER SECTION:
knative.skcloud.io. 603 IN A 34.***.***.248

knctl domain을 이용하여 default domain을 knative.skcloud.io로 변경한다.

$ knctl domain create -d knative.skcloud.io --default
Succeeded

knctl routes 명령으로 해당 hello-test app의 Endpoint 정보를 확인할 수 있다.

$ knctl routes list -n hello-test
Routes in namespace 'hello-test'

Name Domain Traffic Annotations Conditions Age
hello hello.hello-test.knative.skcloud.io 100% -> hello - 3 OK / 3 1h

1 routes

Succeeded

5분이상 기다렸다가 curl로 확인하면 Cold-Start 되는 시간(몇초) 지연이 발생하고 결과를 확인할 수 있다. 이후에는 바로 응답을 확인할 수 있다.

$ curl http://hello.hello-test.knative.skcloud.io/
Hello Rev1!

revision 추가

이번에는 revision을 추가해보자. TARGET environment 변수를 Rev2로 수정하고 배포를 한다. 기존 hello-00002 revision이 최신 revision으로 갱신되어 배포가 되는것을 확인할 수 있다.

$ knctl deploy --service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev2
Name hello

Waiting for new revision (after revision 'hello-00001') to be created...

Tagging new revision 'hello-00002' as 'latest'

Tagging older revision 'hello-00001' as 'previous'

Annotating new revision 'hello-00002'

Waiting for new revision 'hello-00002' to be ready for up to 5m0s (logs below)...

hello-00002 > hello-00002-deployment-6cf86bbfc7-sblz9 | 2019/02/17 23:25:43 Hello world sample started.

Revision 'hello-00002' became ready

Continuing to watch logs for 5s before exiting

Succeeded

신규 revision 서비스를 추가된것을 확인할 수 있다. 마찬가지로 몇초간의 Cold-Start delay가 발생할 수도 있다.

$ curl http://hello.hello-test.knative.skcloud.io/
Hello Rev2!

$ knctl curl --service hello
Running: curl '-sS' '-H' 'Host: hello.hello-test.knative.skcloud.io' 'http://34.***.***.248:80'

Hello Rev2!

Succeeded

revision list를 확인해보면 현재 latest, previous TAG정보를 확인할 수 있다.

$ knctl revision list --service hello
Revisions for service 'hello'

Name Tags Annotations Conditions Age Traffic
hello-00002 latest - 1 OK / 4 14m 100% -> hello.hello-test.knative.skcloud.io
hello-00001 previous - 1 OK / 4 4h -

2 revisions

Succeeded

Blue/Green 배포

Blue/Green Deploy는 knctl rollout 명령으로 수행할수 있다.
rollout 할때 --managed-route=false 옵션을 줘야 특정 비율로 routing이 가능하다.
아래 예시는 TARGET environment 변수를 blue, green으로 바꿔가면서 배포를 진행하였다.

$ knctl deploy --service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=blue \
--managed-route=false
Name hello

Waiting for new revision (after revision 'hello-00002') to be created...

Tagging new revision 'hello-00003' as 'latest'

Tagging older revision 'hello-00002' as 'previous'

Annotating new revision 'hello-00003'

Waiting for new revision 'hello-00003' to be ready for up to 5m0s (logs below)...

Revision 'hello-00003' became ready

Continuing to watch logs for 5s before exiting

hello-00003 > hello-00003-deployment-99478dcc5-jf267 | 2019/02/17 23:48:20 Hello world sample started.

Succeeded

revision list를 확인하면 아래와 같이 latest로 Traffic 전체가 routing 되는 것을 확인할 수 있다.

$ knctl revision list --service hello
Revisions for service 'hello'

Name Tags Annotations Conditions Age Traffic
hello-00005 latest - 4 OK / 4 44s 100% -> hello.hello-test.knative.skcloud.io
hello-00004 previous - 4 OK / 4 2m -
hello-00003 - - 1 OK / 4 5m -
hello-00002 - - 1 OK / 4 28m -
hello-00001 - - 1 OK / 4 4h -

5 revisions

Succeeded

이후에 rollout을 통해 previous로 90%, latest로 10%로 변경을 하면 즉시 반영이 되고 실제 트래픽도 분산되어 들어온다. %가 낮은 쪽으로 routing이 될 경우 Cold-Start가 발생하게 되면 delay는 발생하게 된다.

$ knctl rollout --route hello -p hello:latest=10% -p hello:previous=90%
Succeeded

$ knctl revision list
Revisions

Service Name Tags Annotations Conditions Age Traffic
hello hello-00005 latest - 2 OK / 4 1h 10% -> hello.hello-test.knative.skcloud.io
~ hello-00004 previous - 2 OK / 4 1h 90% -> hello.hello-test.knative.skcloud.io
~ hello-00003 - - 1 OK / 4 1h -
~ hello-00002 - - 1 OK / 4 1h -
~ hello-00001 - - 1 OK / 4 5h -

5 revisions

Succeeded

간단하게 curl 반복문을 작성하여 돌려보자.

#!/bin/sh
while true
do
curl -sS --max-time 3 http://hello.hello-test.knative.skcloud.io/
done

간단하게 위 sh을 돌리면 아래와 같이 Cold-Start delay가 발생할때 time out이 발생하고 이후 green revision으로 접속이 되는것을 볼 수 있다.

$ ./test.sh
curl: (28) Operation timed out after 3002 milliseconds with 0 bytes received
Hello blue!
Hello blue!
Hello blue!
Hello blue!
Hello blue!
curl: (28) Operation timed out after 3003 milliseconds with 0 bytes received
Hello blue!
Hello blue!
Hello blue!
Hello blue!
Hello blue!
Hello blue!
Hello green!
Hello blue!
Hello blue!
Hello blue!

정리

지금까지 knctl을 사용하여 간단하게 knative를 배포하고 custom domain을 연결하여 blue-green 배포까지 해봤다. 이외에도 Knative Build를 활용하여 Docker image 작업을 하거나 서비스 카탈로그 등을 연동하여 외부 DBaaS를 연동하는 use-case등을 찾아볼수 있다.

아직 초기 단계이지만 Knative는 istio와 함께 IBM, Google, Pivotal등 global player들의 차세대 오픈소스로 부상하고 있다고 볼 수 있다.

Zero to scale 이라는 슬로건아래 Serverless, FaaS 사상을 기반으로 build, serving, event, routing이라고 하는 Cloud Computing 추상화의 끝판으로 진화하고 있다. 앞으로 어떤 모습으로 진화될지 궁금하고 다음번에는 MQ나 Pub/sub를 연동하거나 멀티 클라우드 환경에서 동작하는 모습을 보여주는 것도 좋을것 같다. 희망사항이지만 올해 OpenInfraDay나 Kubernetes Day Korea 행사에서 Hands-on을 진행해보는것도 좋지 않을까?

· 10 min read

오늘도 기술적인 이야기보다는 화두가 되고 있는 하이브리드 클라우드 이야기를 해보고자 한다.

업계 사람들도 하이브리드, 엣지 클라우드 서비스를 긍정적인 시각으로 보고있지만 성숙도 측면에서 문제가 있어 도입을 꺼려왔던게 사실이다. 최근 동향을 봤을때 퍼블릭 클라우드 공급사에서도 프라이빗 클라우드를 포섭해야할 대상으로 인정하고 공격적으로 하이브리드 클라우드 솔루션을 개발하여 제공하려고 하는 움직임을 보이고 있다.

엔터프라이즈에서는 입장에서는 Scale, DR측면에서 On-Prem 에서 퍼블릭으로 확장을 도모하고 있고 글로벌플레이어 입장에서는 퍼블릭에서 On-Prem으로 전이하는 모습으로 비즈니스를 진행하고 있다. 하지만 구글은 특별하게 컨테이너 기반으로 진행중이다. 누가 끝까지 살아남아 승자가 될지 아무도 모른다.

앞으로 열릴 시장은 확실하고 꼭 필요하다는 것은 알지만 기술의 성숙도가 높지 않고 넘어야할 허들이 많다. 아직까지 연계 기술이나 생태계가 비어 있는 부분들이 존재하기 때문에 올해 말쯤 되면 여러 상품들이 출시되면서 정리 되지 않을까 싶다.

https://wikibon.com/aws-outposts-enables-hybrid-cloud/

위 포스팅에서도 2가지 의문점을 제기한다.

  • Will a data egress charge be applied to data resident on the disks to other on-premise workloads?
    예시) S3 → Outpost egress 트래픽(private n/w 이긴 하지만 aws가 돈을 안 받을것인가?, 결국 direct-link는 필수인건지?)

  • Is the data on site under the legal control of AWS or the customer?
    데이터의 소유권 문제(이게 심각한 문제가 될수 있다)

그래서 AWS가 작년에 내놓은 Outpost의 첫그림은 VMware on AWS로 작년 reinvent이후 많은 관심을 받았던 프로젝트이다.

이외에도 메이저 사들도 Azure Stack, VMware on IBM Cloud, VMware on Ncloud와 같은 하이브리드 서비스를 내놓고 있다.

Product별 기본 전략

Azure Stack

  • 호환성 인증받은 서버를 On-prem에 구매/설치하고 Azure UI 및 API와 동일한 UI/UX로 private cloud 사용

AWS Outpost

  • 고객 On-prem에 EC2 cloud instances 제공 (전용 HPC 하드웨어 - EC2 Nitro Platform)

AWS Outpost(VMware based)

  • baremetal 전용 플랫폼으로 VMware - software, AWS - hardware 에 집중

Google Cloud

  • K8s On-prem전략을 기본으로 Cisco와 하이브리드 전략 (based on Istio)

IBM Cloud

  • 구글과 유사하게 Istio를 기본으로 하여 멀티, 하이브리드 클라우드 제품 출시
  • IKS on Vmware on IBM Cloud Baremetal 와 같은 상품도 출시

대부분 VMware 협력을 기반으로 하이브리드 클라우드 전략을 전개하고 있고 내면을 들여다보면 물리적으로 다른곳에 있는 Overlay 네트워크에 대한 해결이 가장 문제이기 때문에 Direct-link나 VPN을 통해 연결하는것도 결국 클라우드 사업자가 네트워크 트래픽 비용을 가져가지 위한 전략으로서 보인다.

실제 퍼블릭 클라우드 벤더의 Cash Cow는 Compute 자원보다는 네트워크 비용과 Blob Storage, API과금 등 에 대한 매출이 큰 비중을 차지한다고 봐야 한다. 혹자는 글로벌 네트워크 전용선에 대한 투자나 고성능 HPC도입 투자 비용에 대한 말들을 하지만 IBM같은 경우 글로벌 네트워크 무료 정책을 통해 Market Share를 획득하고자 하는 부분들을 보면 네트워크가 가장 핵심이 아닐까 싶다.

클라우드를 이야기 할때 기술 성숙도를 보면 Compute > Storage > Network 순으로 전이가 되는데 OpenStack이나 Kubernetes 프로젝트와 같은 오픈소스나 VMware NSX 플랫폼만 봐도 정말 네트워크가 중요한 영역이 되고 있음을 알수 있다.

결국 핵심적으로 봐야하는 부분은 네트워크다. 네트워크 부분에서 아주 강력한 솔루션이 나와야 한다. 결국 회선 비용이 문제고 최근 넷플릭스와 SKB의 속도 분쟁만 봐도 결국 강자의 논리로 네트워크 문제가 해결되는 시대가 온 것이다.

정리

내가 생각한 서비스 사용자 측면에서 우리가 고려해야할 클라우드 옵션들을 정리해보면 아래와 같다.

  • 멀티 클라우드 : 퍼블릭 클라우드 to 퍼블릭 클라우드 운영 솔루션으로 프라이빗 클라우드 영역에는 거의 관여를 안하며, 솔루션에 따라 일부 가상화된 Computing 자원까지 모니터링 지원하기도 함. 멀티 클라우드 운영 플랫폼은 주로 클라우드 Brokerage Service 형태로 제공되어야 하므로 MSP역할이 중요해질 것 같다. 메가존, 베스핀글로벌과 같은곳 뿐아니라 자체적인 클라우드를 운영하는 대형 SI회사를 포함한 기존 IaaS 운영조직의 변화들도 눈여겨 봐야할것 같다. 마진율이 낮은 레드오션 싸움에서 누가 이기느냐가 중요하기 보다는 미래를 보고 투자하는 자세가 필요하다고 본다.

  • 프라이빗 클라우드 : 프라이빗 클라우드 전용 솔루션으로 벤더 또는 특정 IaaS 서비스에 최적화된 경우가 많으므로 VMware, OpenShift, OpenStack Managed 서비스등 기존 제품에서의 연장선에서 얼마나 오픈소스를 이해하고 프로덕션에 적용할수 있는 역량을 기르는것이 중요하다.

  • 하이브리드 클라우드 : 퍼블릭 클라우드 to 프라이빗 클라우드 운영 솔루션으로 프라이빗 클라우드의 인프라스트럭처까지 관여를 하며, 퍼블릭 클라우드 영역은 일반적으로 IaaS 영역과 일부 PaaS / 컨테이너까지 구성, 모니터링 지원해야 하고 이를 위한 3rd Party 솔루션들이 등장하고 있고 점차 영역을 확대할것이다. 이에 있어 컨테이너 플랫폼을 도입하는것이 가장 글로벌 플레이어와 격차를 줄이는데 효과적인 도구라 생각한다. 결국 하이브리드 클라우드 구현은 기존 Public과 Local Cloud간 네트워크 연계가 중요한 포인트이다.

  • 간단히 구현해 볼수 있는 General Hybrid Kubernetes Architecture를 간단히 그려봤다. 기본적으로 StrongSwan VPN을 가지고 CI/CD를 고려한 DevOps관점에서의 구성도이다. hybrid-cloud

위에 내용과 함께 하이브리드 클라우드를 고려할때 데이터 소유권 측면에서의 거버넌스overlay 네트워크에 대한 고민이 동행되었을때 성공적인 하이브리드 클라우드를 구축할수 있다라는 나 혼자만의 생각을 정리해봤다.

· 13 min read

그동안 DevOps 담당자라고 부르짖던 사람들의 Role이 인프라 담당자인지 플랫폼 담당자인지 아니면 개발하는 운영자인지 헷갈릴때가 많았다.

갑자기 국내에서도 SRE 채용 공고가 많아지는것을 보면서 내 자신도 한번 정리를 하고 가야할것 같은 생각이 들었다.

https://docs.microsoft.com/ko-kr/learn/modules/intro-to-site-reliability-engineering/

개념정리 측면에서 위 MS Azure의 온라인 교육내용을 정리해봤다.

SRE

SRE(사이트 안정성 엔지니어링)란 조직이 해당 시스템, 서비스 및 제품에서 적절한 수준의 안정성을 달성하도록 지원하는 엔지니어링 분야이다.

출처 : https://landing.google.com/sre/sre-book/chapters/introduction/

기본적으로 운영경험이 있는 sysadmin, IT전문가, DevOps실행 담당자 등이 대상이 될 수 있다.

적절한 수준의 안정성

가장 핵심이 되는 단어는 안정성이다. 조직에서 만든 애플리케이션이 안정성이 부족하여 작동하지 않거나 불안정한 경우 실제 비즈니스에 피해를 줄 수 있다는건 누구나 알고 있는 당연한 이야기이지만 100%의 안정적인 시스템은 존재하지 않으므로 적절한 수준(정량적으로 측정하긴 어렵지만 이해관계자들이 납들할만한 수준)의 안정성을 달성하기 위한 일련의 작업들을 수행하는 것이 SRE의 기본 속성이라고 볼수 있다.

SRE는 Google에서 2003년부터 7명의 소프트웨어 엔지니어로 구성된 팀에서 시작된 이후 (자세한 내용은 위 링크 ebook 참조) Google 사내에 널리 확산되고 내부적인 문화로 조성되는 중에 밖에서는 DevOps라고 하는 문화가 동시에 확산되었다 한다. 결국 동일한 문제를 해결하기 위한 측면에서 두개의 방법론은 다르다고 봐야한다.

차이점

  • SRE - 안정성을 위한 엔지니어링, DevOps - 개발과 운영조직 각각의 사일로를 해체하기 위한 문화적인 움직임
  • SRE - 저는 SRE 입니다, DevOps - 저는 DevOps를 하는 운영자 또는 개발자 입니다.
  • SRE - 규범으로 인식, DevOps - 문화로 인식

공통점

  • 모니터링/식별 가능, 자동화

주요 SRE원칙 : 선순환

새로운 서비스의 상태의 지표는 어떻게 정의할까? 보통은 무엇을 SLI(서비스 수준 지표)로 사용할지를 정하는것으로 본다. 일반적으로 성공 및 실패 측정값(200 OK,500 Error), 응답시간(ms), 처리량 등의 조합으로 결정될 수 있다. 보통 카나리아 분석을 통해 Scoring을 한 SLI를 200 OK와 500 또는 에러코드의 비율로 결정하는 방법을 사용하는 경우가 많다.

서비스 상태를 식별하는 지표를 정하고 나면 고객이나 우리가 원하는 안정성 수준을 결정해야 한다. 개발팀과 운영팀이 같이 만든 원하는 안전성 수준을 SLO(서비스 수준 목표)라고 말하면 된다.

SLO는 Prometheus나 Datadog과 같은 모니터링 시스템을 활용하여 정확하게 측정하고 표시해야 한다. 서비스의 안정성에 대해서 명확하게 이해할수 있는 목표이기도 하다. 반드시 SLO를 만족하는지 못하는지 명확한 측정값으로 데이터가 존재해야 하고 SLO를 충족하지 못하면 문제가 있는것으로 판단하고 문제를 해결해야 한다.

오류 예산

오류 예산이라는 말을 명확히 이해하려면 흔히 고객과 체결하는 계약서 상의 명시된 서비스 가동율(SLA)을 생각하면 될 것 같다. 보통 99.9% 의 SLO를 정한다고 한다면 1년에 8시간 45분 57초의 서비스 다운타임을 허용하는 것이다. 오류 예산은 서비스의 완벽한 안정성과 원하는 안정성의 차이(100%-99.9%=0.01%)를 말한다. 0.01%로 즉 8시간정도의 오류 예산을 가지는 것은 그 시간을 모두 사용할 때까지 추가 릴리스나 패치를 진행할 수 있다는 이야기와 같다. 어떤 팀은 해당 예산을 신규 기능을 릴리스하는데 사용하기도 하고 장애가 발생했을때 문제의 원인을 발견하고 개선하는데 사용하기도 한다.

일반적으로 서비스에 대한 오류 예산이 모두 사용되면 서비스가 원하는 안정성 수준으로 돌아갈때 까지 해당 서비스의 추가적인 릴리스를 보류하는게 일반적이긴 하지만 특정기간(월 또는 분기 또는 연간) 기준으로 계산되기 때문에 서비스 안정성이 정상 상태라면 오류 예산은 다시 주어지게 되므로 선순환 구조를 가진다고 볼 수 있다.

비난 없는 장애 회고

일반적으로 비즈니스에 크리티컬한 장애가 발생했을때 사후 분석(회고)을 하게 되는데 이때는 비난 없는 분석을 해야한다. 오래전 기억이지만 예전 직장 특정팀에서는 장애가 발생하면 유관자들이 모두 엔지니어 책상 뒤에 서서 모든 화살과 눈총을 주기 때문에 도저히 일을 할 수 없게 만든 경우가 많았었다.

특정 운영자의 작업 실수로 인해 장애가 발생했다 하더라도 해당 이벤트가 발생하게 된 프로세스나 기술기반에 실패 원인을 파악하는데 중점을 두어야 할것이다.

시스템의 안정성을 저하시키고 서비스를 중단시킨 작업(릴리스, 패치 등)을 사유로 개인에게 페널티를 주거나 고과에 반영하는 방법은 장애로 부터 교훈을 얻을수가 없고 해당 담당자의 충성도나 생산성 저하를 유발하는 안좋은 장치가 될 수 있다. 내 경험이기도 하지만 한동안 두려워서 아무 변경도 하지 못하는 경우가 종종 있었다.

회사나 조직, 팀은 시스템 중단으로부터 교훈을 얻고 지속적으로 시스템을 개선할수 있다. 적절한 분석을 통해 후속조치를 수행함으로써 실패를 수용하는 것이 SRE의 핵심 원칙이다. 이러한 내용을 모두에게 공유하고 타 조직이나 팀에게 인사이트를 제공하는 선순환 구조를 만드는것 또한 SRE의 기본 원칙이라고 볼 수 있다.

주요 SRE원칙 : 사람측면

Toil(수고스런 일, 번거로운 작업)

항상 SRE에서는 Toil에 초점을 맞춰야 한다. Toil은 사람이 수행하는 운영 작업 중 특정한 패턴이나 특성이 있는 작업을 말한다. 종종 반복적이고 자동화 될 수 있는 작업일지라도 대부분 수동작업으로 처리를 한다. 서비스의 규모가 커지거나 고객이 많아지게 되면 보통 우리가 이야기 하는 티켓(또는 SR)의 수가 많아지고 공수가 많이 필요하게 된다.

배포때마다 Config를 적용하거나 계정 등록, 볼륨이나 디스크 프로비저닝 등 수동으로 처리해야하는 모든 작업이 운영자에게는 부하로 다가올수 있다. 이러한 반복적인 작업을 티켓시스템을 통해 처리를 하게 되면 티켓 생성, 작업계획서 작성, 검토, 승인 단계를 그때 마다 해야하는 아주 번거로운 작업(Toil)을 해야한다.

SRE는 최대한 이러한 반복적이고 번거로운 작업을 제거하기 위해 노력해야하고 자동화와 Self-Service로 개발자가 편하게 직접 사용할수 있도록 환경을 제공해 주어야 한다. 이러한 요청이나 티켓을 자동으로 처리할 수 있으면 조금 더 생산적인 일을 수행할수 있다.

위에서 이야기한 적절한 안정성과 비슷한 맥락으로 생각하면 될 것 같다. 번거로운 작업을 없애는것이 다른 중요한 작업보다 우선순위가 떨어지기도 하지만 서비스 상에서 Toil을 제거하는것도 SRE의 주된 업무이다.

운영작업의 비율

Toil을 제거하거나 서비스/시스템 안정성을 개선하기 위해 SRE는 장애를 해결하고 티켓을 처리하는 시간 비중을 50%를 넘겨서는 안된다. 티켓이 필요하지 않도록 개발자 Self-Service를 구성/제공하고 효율성을 증대시키기 위해서 IaC등(Shell, Ansible, Terraform)과 같이 자동화를 위한 코드를 만드는데에도 집중을 해야한다.

정리

지금까지 SRE에 대해서 간단하게 소개하고 이해하는 측면에서 정리해 봤다.

Service Reliability Hierarchy

hierarchy

그림과 같이 서비스 안정성 계층 구조에서는 기본적으로 모든 서비스를 모니터링 하는 시스템을 갖추어야 한다고 말한다. 모니터링 시스템이 준비가 되면 서비스에 대한 SLI, SLO를 만들고 모니터링 시스템에서 구현하는것이 첫번째 SRE를 적용하는 방법이라고 할 수 있을 것이다.

참고 서적