Skip to main content

· 5 min read

오늘은 쓸데없는 이야기를 주저리 적어본다.

페친한분이 말씀하신 구절이 떠오른다. "엔지니어라고 해서 정치력이 필요없는것이 아니다. 오히려 혁신적인 기술은 정치력, 권력이 없으면 적용될 수 없다" - 제프리 페퍼교수의 "권력의 기술"

나이 40을 넘긴 엔지니어로서 나는 어떤 위치인가?

Cloud 관련 오픈소스로 비즈니스를 진행하면서 현 직장에서 2년을 보냈다. 보람도 있었고 실패도 있었다.
올해는 기획한 오픈소스 기반 프로젝트로 여러사람의 힘을 합쳐서 서비스를 런칭하기도 하였지만 몇년간 글로벌 플레이어들을 보면서 느낀점은

회사가 글로벌 플레이어가 되기위해 노력하는 방법은 다양하지만
결국 이윤을 목적으로 회사에서 허용하고 투자하는것이고
그걸로 돈을 못벌면 바로 쳐낼수 있다는 경영층의 생각은 변함이 없을것이다.

이런 마음가짐을 변화시키지 않는다면 훌륭한 프로젝트라도 성공이든 실패든 끝까지 가는것은 어렵다고 생각한다.

처음 클라우드 관련 업무를 시작할때를 생각해본다.

  • 2010년
    팀장: 너 클라우드 할래?
    나 : 무슨일하는데요?
    팀장: 가상화 프로젝트하는데 같이 한번 비즈니스 만들어보자, 너가 하고 싶은대로 기획하고 해봐~
    나 : 네^^ 열심히 하겠습니다.

  • 개인 성과 및 팀의 목표는 어느정도 달성하였으나 ROI 달성은 쉽지 않았음

  • 2016년
    팀장: 너 주간보고 똑바로 안해?, 지난번 장애보고는 어떻게 된거야?
    나: ㅠㅠ
    팀장: 우리 팀 수치가 이게 뭐냐... 좀 잘좀 해봐
    나: ㅠㅠ

  • 결국 퇴사

  • 2017년
    팀장: 하고 싶은대로 해봐 나: 진짜요? 뚝딱뚝딱
    회사: 수익모델이 뭐지?
    나: ...

  • 출시하기도 전에 서비스 종료

  • 2018년
    팀장: 하고 싶은거 해봐요. 대신 돈벌어와야해
    나: 진짜요? 뚝딱뚝딱
    회사: 흠 좀 부족하지만 앞으로 좀더 잘해주세요. 레퍼런스 확보에 힘써주세요.
    나: 서비스 기획자가 필요합니다.
    회사: ... 본인이 해야하는거 아닌가요?
    나: ...

  • 기획을 해야하나 아직 기술력도 부족한데...

벌써 직장생활 13년차에 마흔살이 넘어가고 있다.

엔지니어로 살아온날보다 살아가야할날이 적게 남았고 얼마 남지 않았다는 생각이 드는 우울한 하루다. 10년간 공부해왔던 것들보다 2년동안 공부한게 더 파격적이고 다양하고 복잡했다.

여러모로 아쉬운 한해였다...

앞으로 내가 부족한 부분을 기술력으로 메꿀것이냐? 협상과 정치력으로 메꿀것이냐?

잠이 안오는 12월이다.

· 15 min read

3번째 포스팅이다.

11/23 "Kubernetes Meetup" 1Day에서 발표한 이야기 연장선으로 작성한다.

고객에게 오퍼링을 위해 준비한 내용과 Kubernetes monitoring과 연계한 내용에 대해서 적어보려고 한다. 최근 발표를 다니면서 많이 받는 질문이 실제 사용할만 한가?라는 질문과 어떻게 활용해야 하는지에 대한 질문들을 많이 받는다. 오늘 최근 Spinnaker Summit 2018에서 중요하게 다뤘던 Kayenta 프로젝트를 가지고 이야기 해보려고 한다.

Kayenta

Kayenta는 자동 Canary 분석 오픈소스(Automated Canary Service(ACA))로 Spinnaker의 마이크로서비스중 하나로 동작한다.
Automated Canary Deployments에 사용되고 자세한 내용은 canary documentation을 확인하면 된다.

새 종류의 하나인 Canary는 1차 세계대전중에 인간이 해를 입기 전에 독성가스를 탐지하는 용도로 사용되었다고 한다. DevOps에서는 CD(Continuous Deployment) 프로세스의 일부로 사용되며 Canary 릴리즈는 새로운 버전의 Software를 Production에 배포하는 위험을 줄여주는 기술이라고 생각하면 된다.

Canary 신규 버전의 Software를 안정적인 기존 버전과 함께 배포하고 특정사용자나 일부 대상에게 트래픽 일부를 흘려 기존 사용자에게 영향을 최소하하고 새로운 버전의 문제를 신속하게 발견하고 이전의 안정된 버전으로 트래픽을 다시 라우팅시키는것이 주요 기능이라고 보면 된다.

보통 품질 테스트 용도로 현재 운영 버전과 신규 버전의 주요한 지표(주로 Prometheus Metric)를 비교하여 평가를 진행하는데 이를 단위 테스트나 통합 테스트를 대체하여 사용해서는 절대 안된다.
위에서 언급하였듯이 예기치 않은 위험을 최소화 하거나 문제를 신속하게 발견하는 것을 주 목적으로 하기 때문이다.

Spinnaker에서는 기본적으로 3가지 Cluster(Logical Application Group)를 사용한다.

  • Production Cluster - 현재 실행중인 운영 버전으로 Replica는 임의로 변경할 수 있다.
  • Baseline Cluster - Production Cluster와 동일한 버전으로 실행됨
  • Canary Cluster - 변경된 신규 버전의 Software로 실행됨

기본적으로 수동으로 진행할 경우에는 로그와 수집된 메트릭을 분석하고 객관적인 지표로 평가를 진행하는게 기본이다. 하지만 직접 사람이 하는 일이라 메트릭 데이터를 보다 보면 편견과 착오가 발생할 수 밖에 없다.

그래서 Netflix는 ACA(Automated Canary Service)라고 하는 자동화된 접근 방식을 통해 카나리 분석을 진행하고 있다. 수동으로 계산된 여러가지 지표를 가중치 기반으로 점수를 내리고 원하는 점수에 도달하면 배포하는 자동화된 방식이다.

Requirements

  • spinnaker cluster - 1.8+ (1.9.3 이상 추천)
  • halyard - 1.0+
  • kubernetes cluster - 1.9+
  • metric services - datadog, prometheus, stackdriver, signalfx
  • persistent storage - S3(or compatible S3), minio, GCS

Kayenta Service 추가하기

이번 포스팅에서는 아래 환경으로 작성하였다.

  • spinnaker cluster - 1.10.5
  • halyard - 1.11
  • kubernetes cluster - 1.9.7
  • metric services - prometheus
  • persistent storage - compatible S3(IBM Object Storage)

일단 기존 halyard config를 백업하자.

$ hal backup create
+ Create backup
Success
+ Successfully created a backup at location:
/Users/ddii/halbackup-Wed_Nov_28_13-35-31_KST_2018.tar

나중에 복구는 아래와 같이 하면 된다.

$ hal backup restore --backup-path <backup-name>.tar

기존 halyard config를 살펴보면 아래와 같이 canary.enabled=false 로 되어있는 것을 확인 할수 있다.

currentDeployment: default
deploymentConfigurations:
- name: default
canary
enabled: false

canary analysis을 활성화 한다.

$ hal config canary enable

그리고 default judgement algorithm은 NetflixACAJudge-v1.0 로 되어있다 다른 걸 이용하려면 다음과 같이 설정할수 있다.

$ hal config canary edit --default-judge CUSTOM_JUDGE

메트릭 소스로 prometheus를 설정한다. 물론 기존에 사용중인 prometheus endpoint url이 필요하다.

hal config canary prometheus enable
hal config canary prometheus account add my-prometheus --base-url http://YOUR_PROMETHEUS_SERVER:PORT

여기서는 IBM Cloud Object Storage(S3 Compatible)을 사용하였지만 aws로 설정한다.

$ hal config canary aws enable
$ hal config canary aws account add my-s3 --bucket spin-bucket --endpoint \
s3.seo-ap-geo.objectstorage.service.networklayer.com --access-key-id ACCESS_ID \
--secret-access-key
$ hal config canary aws edit --s3-enabled=true

여러개의 메트릭을 동시에 설정 및 수집이 가능하므로 그중 prometheus 및 관련 account를 기본으로 설정한다.

$ hal config canary edit --default-metrics-store prometheus
$ hal config canary edit --default-metrics-account my-prometheus
$ hal config canary edit --default-storage-account my-s3

모든 spinnaker cluster가 준비된 상태의 컨피그는 아래와 같다.

currentDeployment: default
deploymentConfigurations:
- name: default
canary:
enabled: true
serviceIntegrations:
- name: google
enabled: false
accounts: []
gcsEnabled: false
stackdriverEnabled: false
- name: prometheus
enabled: true
accounts:
- name: my-prometheus
endpoint:
baseUrl: http://YOUR_PROMETHEUS_SERVER:PORT
supportedTypes:
- METRICS_STORE
- name: datadog
enabled: false
accounts: []
- name: aws
enabled: true
accounts:
- name: my-s3
bucket: spin-bucket
rootFolder: kayenta
endpoint: s3.seo-ap-geo.objectstorage.service.networklayer.com
accessKeyId: ACCESS_ID
secretAccessKey: ACCESS_KEY
supportedTypes:
- OBJECT_STORE
- CONFIGURATION_STORE
s3Enabled: true
reduxLoggerEnabled: true
defaultMetricsAccount: my-prometheus
defaultStorageAccount: my-s3
defaultJudge: NetflixACAJudge-v1.0
defaultMetricsStore: prometheus
stagesEnabled: true
templatesEnabled: true
showAllConfigsEnabled: true

Spinnaker Cluster를 재배포하게 되면 아래와 같이 spin-kayenta deployments가 추가된 것을 확인할 수 있다.

$ hal deploy apply

$ kubectl get pod
NAME READY STATUS RESTARTS AGE
spin-clouddriver-555cfc9765-kvnl8 1/1 Running 0 6d
spin-deck-85845b5b48-49ncm 1/1 Running 0 6d
spin-echo-5f9dd4d8ff-mvt7g 1/1 Running 0 6d
spin-fiat-5b945645d8-s2qcq 1/1 Running 0 6d
spin-front50-5c57fcf587-tqz28 1/1 Running 0 6d
spin-gate-57576b8c45-w5v6r 1/1 Running 0 6d
spin-kayenta-6dcd7767d6-rgb9w 1/1 Running 0 6d
spin-orca-788df6b9cc-tk6lk 1/1 Running 0 6d
spin-redis-6c87c456fc-6qbl2 1/1 Running 0 6d
spin-rosco-f6f845d49-btjnd 1/1 Running 0 6d

이후 Dashboard에 접속하고 application중 하나의 Config에 들어가면 Features에 Canary메뉴가 생긴것을 확인할 수 있다. 사용설정하고 캐싱하는데 시간이 다소 필요하고 Tasks 메뉴에서 해당 job에 대한 내용을 확인할수 있다.

canary

이후 application delivery 메뉴를 보면 pipelines, canary configs, canary reports라는 메뉴가 생기게 된다.

delivery

simple deploy pipeline 추가

https://cloud.google.com/solutions/automated-canary-analysis-kubernetes-engine-spinnaker 가이드 처럼 구성해봐도 되나 stackdriver를 써야하고 prometheus metric을 활용한 가이드가 필요해서 적어보고자 한다.

새로 파이프라인을 추가한 다음

newpipe

Pipeline Actions - Edit Pipeline JSON 에서 https://raw.githubusercontent.com/ddiiwoong/canary-demo-spinnaker/master/simple-deploy.json 을 추가해준다.

해당 json pipeline을 추가하고 나면 다음과 같은 화면을 확인할수 있다. pipe1 pipe2

반드시 Deploy Config, Deploy Stage에서 배포할 Account 지정을 해야한다.

sampleapp image - ddiiwoong/canary-demo-spinnaker:latest

해당 pipeline내의 sampleapp은 python flask 기반으로 구성되어 간단히 internal 500 error를 원하는 비율을 configmap 변수로 구현할 수 있다. prometheus python client를 사용하여 Gauge, Counter, Metric 서버를 간단하게 구성을 해보았다. 그리고 코드내에서 500 error rate를 구한 이유는 18년 11월 기준 spinnaker kayenta 버전에서는 PromQL(rate,irate와 같은 함수) 지원이 되지 않는다. 개발중인 코드에 포함이 된것을 확인하였고 12월 kubecon때 정식 릴리즈에 포함될거라 생각한다.

#!/usr/bin/env python

from random import randrange
from flask import Flask
from prometheus_client import start_http_server, Gauge, Counter
import os

app = Flask('kayenta-tester')
c = Counter('requests', 'Number of requests served, by http code', ['http_code'])
g = Gauge('rate_requests', 'Rate of success requests')

responce_500 = 0
responce_200 = 0
rate_responce = 0

@app.route('/')
def hello():
global responce_500
global responce_200
global rate_responce
if randrange(1, 100) > int(os.environ['SUCCESS_RATE']):
c.labels(http_code='500').inc()
responce_500 = responce_500 + 1
rate_responce = responce_500 / (responce_500+responce_200) * 100
g.set(rate_responce)
return "Internal Server Error\n", 500
else:
c.labels(http_code = '200').inc()
responce_200 = responce_200 + 1
rate_responce = responce_500 / (responce_500+responce_200) * 100
g.set(rate_responce)
return "Hello World!\n"

start_http_server(8000)
app.run(host = '0.0.0.0', port = 8080)

해당앱을 Start Manual Execuction을 통해 배포한다. Comfirm Execution창에서 SUCCESS_RATE를 원하는 값(예:70%)으로 선택하고 배포를 하고 나면 Infrastructure - Clusters 메뉴에서 해당 샘플앱을 확인할 수 있다.

manaul deploy success rate manaul deploy2

실제 해당 서비스를 접속해보면 위에 설정한 SUCCESS_RATE 비율로 200화면과 500에러를 확인할 수 있다.

flaskweb flaskweb2

해당 메트릭의 통계를 확인하기 위해 curl을 반복적으로 실행하는 injection container 를 실행한다.

kubectl -n default run injector --image=alpine -- \
/bin/sh -c "apk add --no-cache --yes curl; \
while true; do curl -sS --max-time 3 \
http://sampleapp:8080/; done"

5분정도 후에 Prometheus로 접속하여 코드내 작성한 rate_requests 메트릭을 확인해본다.
PromQL은 아래 쿼리를 실행하였다.

rate_requests{app="sampleapp",version="prod"}

아래 그림과 같이 4개의 pod에서 70% 정도 200 OK, 30% 정도 500 Error가 발생하는 것을 확인할 수 있다.

500rate

이 메트릭을 Spinnaker 에서 확인하기 위해 Canary Pipeline을 만들자.

https://raw.githubusercontent.com/ddiiwoong/canary-demo-spinnaker/master/automated-canary.json를 JSON으로 Pipeline을 생성한다.

canary_auto

Stage별로 살펴 보기전에

  • Prerequisite
    Canary Config 구성이 먼저 필요하다. Delivery - Canary Configs 메뉴에서 신규 컨피그를 작성한다.
    • Configuration Name - kayenta-test
    • Filter Templates 메뉴를 먼저 생성한다. Canary, Baseline구분을 위해 version 정보를 선택하였다.
    • Metrics - Add Metric 은 분석을 위한 Prometheus Metric을 설정하는 단계로 error_rate가 증가(increase)하면 Pipeline을 중단시키고 Metric은 앞에서 확인한 rate_requests를 지정한다. Filter Template은 위에서 지정한 version을 선택한다. metric config
    • SCORING - 어짜피 예제는 한가지 Metric분석으로 0점 아니면 100점으로 나올것이므로 Maginal 75, Pass 95를 설정한다.
  1. 1st Stage
    • Configuration - Pipeline 실행시 초기 입력값(0-100, 10단위)으로 설정가능한 successRate 라는 Parameter를 설정한다.
  2. 2nd Stage
    • Find Baseline - 위에서 작성한 기본 Deploy Pipeline이 선택되었는지와 확인한다.
    • Deploy Canary Config - 앞에서 선택한 새로운 Parameter(successRate)를 신규 배포할 Canary Pod ConfigMap으로 설정하는 단계이다.
  3. 3rd Stage
    • Deploy Canary - yaml manifest로 Canary 버전을 배포한다. Replicas는 1로 설정하였고 배포될 Account(K8s Cluster)를 지정한다.
    • Deploy Baseline - yaml manifest로 Baseline 버전을 배포한다. 위와 동일하게 Replicas는 1로 설정하였고 배포될 Account(K8s Cluster)를 지정한다.
  4. 4th Stage
    • Canary Analysis - 중요한 Canary 분석 단계로 아래와 같이 설정을 확인한다. Prerequisite에서 설정한 Config(kayenta-test)를 선택하고 짧은 분석을 위해 1분(60초) 간격으로 3번 수행을 하도록 한다. Filter Template에서 지정한 version(version="${scope}") 분석을 위해 Baseline, Canary 설정을 하고 Location은 Namespaces로 생각하면 된다. aca config
  5. 5th Stage
    • Deploy to Production - Canary 분석이 통과하였을 경우 운영에 배포
    • Delete Canary, Delete Baseline - 성공이던 실패이던 Canary, Baseline 배포본을 삭제
  6. 6th Stage
    • Successful deployment - Canary 분석이 통과하였을 경우 최종 완료 표기하는 단계

설정이 마무리가 되면 저장을 하고 Canary 분석에 들어간다. 최초에 successRate을 70으로 배포했다면 그 이하로 설정했을 경우에는 아래와 같이 Score 0점으로 배포가 실패하고 Pipeline이 종료된다.

fail

70 이상으로 설정하게 되면 Score 100점으로 정상 배포됨을 확인할 수 있다.

success

정리

간단하게 Spinnaker 와 Prometheus Metric을 활용하여 Kayenta 기반 Canary 배포를 해봤다. 현재 Spinnaker 1.10에서 istio가 지원된다고 하니 다시 한번 확인하고 istio 기반 canary 배포와 함께 사용하는 방법을 더 연구해봐야 할 것 같다.

올해 AWS re:invent 끝나고 작년보다 큰 현자타임이 왔다. 오픈소스로 먹고사는 사람들의 기분은 다 비슷할거 같다고 생각이 든다. 12월 11일 부터 Kubecon이 열린다고 하니 Kubernetes 관련한 새로운 프로젝트와 기능들에 집중해서 남들보다 한발 나아가야하지 않을까? 오픈소스로 먹고사시는 분들 다들 힘냈으면 좋겠다.

· 8 min read

이전 포스팅 Spinnaker on Kubernetes #1에서 검토할때는 많이 개념을 이해하기 어려웠던것 같지만 어느정도 시간이 지났고 또 몇일 후에 발표도 있어서 다른 이야기를 해보고자 한다.

지난 포스팅에 대충 집고 넘어간 용어들에 대한 정리를 다시 하고 기본적인 사상들을 정리해보고자 한다. 허접한 플랫폼 엔지니어 생각이니 언제든 다른 의견을 환영하는 바이다.

What is spinnaker? (+History)

최근 트렌드인 멀티 클라우드를 지향하는 오픈소스 플랫폼이다.
2014년 Netflix의 Asgard로 시작되어 2015년에 오픈소스화 되었다.
빠른 속도와 신뢰도있는 소프트웨어 릴리즈를 위해 만들어졌으며 대부분의 메이저 클라우드 프로바이더들을 지원한다.(AWS,GCP,Azure,openstack..)
현재 Netflix, Google, MS, Veritas등이 Contribution을 하고 있다.

왜 Spinnaker를 써야하지?

여러가지 이유가 있겠지만

  • Multi-Cloud용 Continuous Delivery/Deployment Platform 으로 대체가 가능
  • 다양한 pipeline 형태로 배포가 가능하고 Rollback이 쉬움
  • 빠른 배포가 가능하고 여러번 배포가 용이함
  • 유연한 pipeline management system을 가지고 있음
  • 다양한 배포전략을 가진다(Blue-Green, Rolling Red/Black, Canary)
  • community 활동 활발 (github, slack) - 답은 잘 안해줌 ㅠㅠ
  • VM과 Container 동시에 통합관리 가능
  • CI통합 용이(Jenkins)
  • CLI를 통한 설치 및 관리(halyard)
  • VM, Helm Packaging 가능
  • RBAC 지원
  • Notification - Email, Slack, Hipchat등
  • Safe Deployment - Judgement (승인기능)
  • Chaos Monkey Built-in

이정도면 무조건 써야하지 않을까?

Jenkins vs Spinnaker

JenkinsSpinnaker
강력한 빌드서버클라우드 자원의 1차 연동
완전한 deployment tool이 아님vm & deployments 안에 빌드되어 있음
스크립팅이 많이 필요함별도의 스크립팅이 많이 필요없음
기능들이 모두 플러그인 형태CI tool이 아님(CI tools이 백엔드로)

Kubernetes vs Spinnaker

KubernetesSpinnaker
리소스 사용 제한정의한 퍼센트로 rollout
slow rollout각 단계별 검증 가능
High rollback costFast rollbacks
Linear rolloutsresource 사용량이 큼
검증단계가 없음

Deploy Pipeline

Spinnaker를 사용할때 기본적으로 아래와 같은 파이프라인으로 구성한다.
수동으로 UI나 API로 트리거링할수 있고, 자동으로 Jenkins 등과 트리거 연동하여 빌드완료시 배포되도록 할수 있다.

spinnaker-pipeline

Deployment Strategies

Spinnaker에서의 배포전략은 다음과 같이 제공된다.

deployment-strategies

Red / Black (same as Blue / Green)

  • 동일한 양의 instance로 이루어진 새로운 Server Group을 생성한다
  • 신규 Server Group이 정상상태가 되면 LB는 신규 Server Group에 트래픽을 분산한다.

Rolling red/black

  • 이전과 동일하지만 인스턴스별 또는 그룹별로 rolling

Canary

  • 가장 작은 개수의 인스턴스를 교체시키고
  • 새로운 버전으로 트래픽을 분산시킨다 (1~5프로)
  • 새로운 버전에 이슈가 없을때까지 테스트를 진행하고
  • 특정시간까지 이슈가 없으면 배포를 늘려간다.

용어정리 2탄

이전 post에서 정리한걸 다시 복기하고 추가적인 내용을들 적어봤다.

사용하면서 혼돈이 많이 생기는 부분이다 이게 GCE나 EC2를 쓰면 용어 매칭이 쉬운데 k8s를 위한 별도의 메뉴가 아닌 기능을 통합하다보니 용어가 조금 혼동스럽게 구성이 되었다.
특히 Load Balancer 부분은 Service로 매핑되고 퍼블릭 k8s에서 제공하는 Type LoadBalancer는 미지원한다.
그리고 모든 Resource들은 Deploy, Delete, Scale, Rollout(Undo, Pause, Resume)을 지원하며 Versioning이 지원된다. Versioning은 여기에 설명된 대로 strategy.spinnaker.io/versioned annotation을 통해 manifest별로 재정의가 가능하다.

SpinnakerKubernetes비고
Server GroupWorkloadsCRD의 경우 별도 Build
ClustersLogical Server Group
Load BalancerServicesLoadBalancer(k8s) 미지원
FirewallNetworkPolicies

Application Management

Spinnaker에서 Application 이란 배포하려는 서비스를 나타내는 구조라 생각하면 된다.

  • pipeline
  • Clusters, Server Group의 집합이며, firewall과 loadbalancer를 포함한다.
  • Canary Config

Cluster

Kubernetes의 Cluster가 아니라 Spinnaker에서 Server Group의 논리적인 그룹

Server Group

기본자원인 서버그룹은 배포할수 있는 artifacts(vm image, docker image, source)와 인스턴스(pod) 수, Auto-Scaling, metadata 등 기본 구성등을 가지고 있다.
서버그룹은 LoadBalacer나 Firewall 도 선택적으로 연결되고, vm이나 pod 형태로 배포된 application의 집합체라 볼수 있다.

Cloud Provider

  • IaaS - AWS, GCP, Azure, Oracle, Openstack
  • PaaS - Google App Engine
  • Orchestrator - K8s, DC/OS
  • Docker v2 Registry

Account

Cloud Provider에 인증하기 위한 Spinnaker에서만 사용하는 Account Name

Pipeline

Pipeline은 주요 배포 관리도구로 사용된다. Stage라고하는 일련의 Action으로 구성되며 파이프라인을 따라 Stage간 매개변수 전달이 가능하다.
수동으로 시작하거나, Jenkins 작업완료, Docker Registry 신규 Docker 이미지, Cron일정 또는 다른 Stage와 같은 이벤트에 의해 자동으로 트리거링되도록 구성할수 있다.
Pipeline 실행중에(시작/완료/실패) mail, slack, hipchat(사라짐)을 통해 Alert가 가능하다.

pipeline

Stage (atomic building block)

Pipeline이 수행할 동작을 말한다.
Deploy, Resize, Disable, Manual Judgement 등을 수행할수 있다.

stage

  • Stage - Multiple steps
  • Step - 진행되기전에 교정/폴링이 필요한 tasks
  • Task - 특정 Cloud Platform으로 동시에 여러 API호출
  • Operation - 단위 API

정리

용어나 개념은 어느정도 정리된듯 하고 다음 포스팅에서는 실제 multi cluster 환경에서 deploy하고 pipeline을 사용하는 내용을 적어볼 예정이다.

· 9 min read

Knative

미친놈들 모여서 미친것을 만들었군.. ㅎㅎ 예상했던 내용들을 현실로 만드는 클라스

Kubernetes 관련 비지니스를 하고 있는 입장에서 봐도 놀랄일이지만 인프라 영역부터 개발자 영역까지 모두를 추상화시키는 클라우드 네이티브의 힘이란 참 대단하다.

오늘은 knative에서 주요기능들을 둘러보고자 한다.

개인적인 생각은 Kubernetes 진영이라고 해야하나 CNCF 진영이라고 해야하나.. 그동안 노래를 부르고 주목했던 CRDs, Operators, Serverless Workload, CloudEvents, Mesh Layer 개념과 영역을 흡수해서 그 기반으로 확장시키고 있다.

kubectl apply -f https://storage.googleapis.com/knative-releases/serving/latest/release.yaml

위 코드 내용을 상세하게 보고있지만 담은것들이 정말 많다. 그래도 다 이해하려면 하나하나 챙겨봐야 한다.

오늘은 일단 코어 기능만 살펴본다.

Build

build는 Knative의 주요 custom resource이고 이를 이용하여 fetch, build, package를 수행한다. repo의 소스를 빌드하고 컨테이너로 이미지로 만들고 그다음에 Knative Serving으로 보낸다고 한다.

Build 용어

  • Build는 여러 steps을 포함하고 Builder로 구체화된다
  • Builder는 컨테이너 이미지 유형
  • Buildsteps은 repository에 push를 할 수 있음
  • BuildTemplate 는 재활용가능한 템플릿
  • Buildsource는 kubernetes Volume으로 mount되는 데이터를 정의할수 있고 git, Google Cloud Storage, 컨테이너 이미지를 지원함.
  • kubernetes Secret을 사용하여 ServiceAccount로 인증함

Serving

Knative Serving은 Kubernetes와 Istio를 기반으로 Serverless Workload가 클러스터에서 작동하는 방식을 정의하고 제어하는데 사용된다고 하지만 엄연히 Public FaaS(AWS Lambda등)와는 구별되어야 한다고 생각한다.

Serverless 라고 하는 용어를 쉽게 생각하는 사람들이 많은데 결국 나중에는 간단한 애플리케이션들은 다 Serverless 스타일로 전환될것이라는 사상을 서비스나 플랫폼에 넣고 있는 추세다.

CNCF 진형의 Cloud Event라고 하는 이벤트 그리드방식의 표준화를 따라 가는것인지 아니면 새로운 스타일을 정의하려고 하는것인지는 그들의 향후 Cloud Native 로드맵에 달려있다 해도 무방할것 같다.

  • Serverless Container의 신속한 배치가 가능
  • Automatic scaling up and down to zero
  • Istio Component
  • 배포 된 코드 및 config의 특정 시점 스냅 샷

Serving Resources

CRDs로 정의한 Objects 집합체, 이러한 Object들은 Serverless 워크로드 형태로 정의되고 사용된다. 가장 인상깊고 중요한 문구는 Request-driven compute that can scale to zero 인 것 같다.

한동안 유행했던 OpenPaaS, CF기반의 PaaS 플랫폼을 뛰어넘는 구축형 그것도 스프링부트 영역까지도 kubernetes 기반 이벤트 드리븐 서버리스로 간다는 이야기...

이미 퍼블릭으로는 GA도 되었다.

우리 팀원들과 함께 열심히 vmware dispatch framework으로 개발하고 있지만 결국 pivotal과 vmware는 거의 한몸이기에 더욱 더 변화가 필요한 순간이다.

serving

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

Events

CNCF의 CloudEvent Spec. 기반으로 하는 이벤트를 produce/comsume 하는 방법을 제공한다. 플러그인 형태로 이벤트를 수신할수 있고 다양한 pub/sub 스타일의 broker service를 통해 제공될수 있다.

Azure는 이미 EventGrid서비스를 GA를 한 상황이고 Pivotal 진영도 Serverless Workload는 Knative기반으로 넘어간다고 했으니 Dispatch 도 결국 따라가지 않을까 생각해본다.

eventing_concept

Bus

Kafka나 Nats와 같은 메시지 Bus를 통해 K8s기반의 pub/sub을 제공하는 개념. 이벤트는 Channel에 의해 게시되고 관심있는 사람에게 라우팅됨.

  • Channel : 여기서 이야기 하는 채널은 특정 bus에 사용되는 이벤트를 받기 위한 네트워크 기반 엔드포인트
  • Subscription : Channel에서 수신한 이벤트를 관심있는 target, DNS이름으로 표현되는 이벤트에 연결함
  • Bus : (kafka topic에 이벤트가 전달되는것 처럼) 특정 지속성 전략을 사용하여 Channel과 Subscription을 구현하는데 필요한 적용 계층을 정의함

현재 3가지의 Bus가 제공됨 (Kafka, Stub, GCP PubSub)

Sources

Source는 K8s 외부의 데이터 소스를 프로비저닝하고 이를 클러스터로 라우팅하기 위한 추상화 레이어를 제공함. 아래와 같은 소스들을 제공하고 있음

  • Feed : EventType과 Action (CloudEvents 호환 HTTP endpoint)간의 연결을 정의하는 기본 객체
  • EventType and ClusterEventType : EventSource에서 분리되는 공통스키마로 특정 이벤트의 집합, EventType은 Namespace 범위내에서 사용되고 ClusterEventType은 모든 Namespace에서 사용될수 있도록 관리자에 의해 설치됨
  • EventSource and ClusterEventSource : 하나 이상의 EventTypes를 생성 할 수있는 외부 시스템을 기술함

현재 3가지 Sources를 제공함

  • K8sevents : Kubernetes Events를 수집하고 CloudEvents 타입으로 표시함
  • Github : PR(pull request) notification을 수집하고 CloudEvents 타입으로 표시함
  • GCP PubSub : GCP PubSub topic으로 publish된 이벤트를 수집하고 CloudEvents 타입으로 표시함

Flows

마지막으로 Source에서 Endpoint까지 묶어주는 Flow라고 부르는 높은 수준의 추상화가 있다. Spec으로 이벤트가 라우팅된 Channel과 Bus를 기재하여 사용할 수 있다. Flow는 Eventing에서 최상위 개념으로 사용자가 선택할수 있고, 외부 Source 이벤트에서 목적지까지 원하는 경로를 기술할수 있다.

정리

워낙 방대한 양을 가지고 있고 이해하려고 노력하면서 적다보니 번역위주로 되어버렸다. 원래 다음번에 Nats Streaming을 다룰 예정이였으나, 당분간은 knative 구성요소를 이해하고 적용하는 위주로 포스팅을 할 예정이다. 아마도 모듈별(Serving, Building, Eventing) 시리즈가 될 듯 하다.

· 10 min read

Dex - A federated OpenID Connect provider


dex는 기본적으로 OpenID Connect를 사용하여 다른 애플리케이션의 인증을 하게 해주는 identity 서비스다.

https://github.com/coreos/dex

Dex는 "connectors"를 통해 다른 identity provider의 포털(게이트웨이, 중계자) 역할을 한다. 이를 통해 LDAP, SAML또는 GitHub, Google, AD(Active Directory)와 같은 기존 인증을 dex에게 위임할 수 있다. 클라이언트는 일단 인증 로직을 작성하여 dex와 통신하면 dex는 주어진 백엔드(여기서는 예시로 kubernetes 클러스터)에 대한 프로토콜을 처리하게 된다.

하지만 향후 Cloud Native 인증 카탈로그 중 하나로서 생각하고 있는 오픈소스이다 보니 테스트는 진행해봐야 할 것 같다.

OpenID Connect

기본적으로 Dex는 OIDC(OpenID Connect)를 사용한다.
그러므로 kubernetes 클러스터 사용자 인증용도로 Dex를 붙일수 있는 것이다.
또한 위에서 말한것과 같이 다른 identity provider를 사용하여 사용자를 인증하는데 아래 그림과 같이 connectors를 사용한다.

dex_image

위 그림과 같이 "connectors"는 다른 identity provider를 통해 사용자를 인증하기 위해 dex에서 사용하는 기본 도구다. 결국 Dex는 기존에 활용하던 GitHub, LinkedIn 및 Microsoft AD와 같은 벤더 플랫폼이나 LDAP 및 SAML과 같은 기존 프로토콜 방식을 사용하여 kubernetes 클러스터 사용자 인증을 쉽게 구현할 수 있는 것이다. 큰 회사??들은 OpenLDAP이나 Microsoft Active Directory와 같은 사내 디렉토리 서비스를 운용하거나 내가 있는 작은 규모의 팀들은 대부분 Github이나 Google 계정은 가지고 있을 것이기 때문에 Dex와 같은 오픈소스를 사용함으로써 기존 identity provider 와 kubernetes를 쉽게 통합할 수 있다고 본다.

dex_kube

Kubernetes OIDC

아래 그림처럼 Identity Provider 위치에 Dex를 구성하면 된다.

kube_openid

public managed kubernetes 환경에서는 현실적으로 kubeapi-server를 컨트롤하기 어렵기 때문에 일단 Native 클러스터에 구성을 하도록 한다.

Github 연동하기

테스트로 Github을 연동해보자. 일단 아래와 같이 인증서를 생성하는 스크립트를 하나만든다.
아래에서 DNS.1 부분은 /etc/hosts로 테스트로 등록할 도메인으로 가상으로 만들었다. (generator.sh)

#!/bin/bash

mkdir -p ssl

cat << EOF > ssl/req.cnf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = dex.newtech.academy
EOF

openssl genrsa -out ssl/ca-key.pem 2048
openssl req -x509 -new -nodes -key ssl/ca-key.pem -days 10 -out ssl/ca.pem -subj "/CN=kube-ca"

openssl genrsa -out ssl/key.pem 2048
openssl req -new -key ssl/key.pem -out ssl/csr.pem -subj "/CN=kube-ca" -config ssl/req.cnf
openssl x509 -req -in ssl/csr.pem -CA ssl/ca.pem -CAkey ssl/ca-key.pem -CAcreateserial -out ssl/cert.pem -days 10 -extensions v3_req -extfile ssl/req.cnf

Namespace도 하나 만들자. (dex-ns.yaml)

apiVersion: v1
kind: Namespace
metadata:
name: dex

kubernetest 클러스터용 인증서를 생성한다.

$ ./generator.sh
$ kubectl create -f dex-ns.yaml
$ kubectl create secret tls dex.newtech.academy.tls -n dex --cert=ssl/cert.pem --key=ssl/key.pem
$ mkdir pki
$ cp ssl/ca.pem pki/openid-ca.pem

Github에 가서 아래와 같이 Org OAuth App.을 신규로 생성한다.

github

만든 위 앱정보로 Github-Client Secret을 만든다.

$ export GITHUB_CLIENT_ID=<Client ID>
$ export GITHUB_CLIENT_SECRET=<Client Secret>

$ kubectl create secret \
generic github-client \
-n dex \
--from-literal=client-id=$GITHUB_CLIENT_ID \
--from-literal=client-secret=$GITHUB_CLIENT_SECRET

$ kubectl get secret
NAME TYPE DATA AGE
default-token-f29fb kubernetes.io/service-account-token 3 27m
dex.newtech.academy.tls kubernetes.io/tls 2 27m
github-client Opaque 2 6s

$ kubectl get secret github-client -o yaml
apiVersion: v1
data:
client-id: xxxxx
client-secret: xxxxxxx
kind: Secret
metadata:
creationTimestamp: 2018-07-24T15:14:29Z
name: github-client
namespace: dex
resourceVersion: "7044721"
selfLink: /api/v1/namespaces/dex/secrets/github-client
uid: 42dfa0e9-8f54-11e8-8d6d-2aaad36eb114
type: Opaque

위에서 이야기 했지만 여기까지 아무생각없이 퍼블릭에서 구성하다가 생각해보니 kube-apiserver를 변경하려면 직접 클러스터를 써야한다는 이런 바보같은 실수를 또 저지르고 말았다. 그래서 다시 VM에 재구성을 ㅜㅜ

그리고 kube-apiserver manifest 파일에 아래 내용을 추가한다.
/etc/kubernetes/manifests/kube-apiserver.manifest

    - --oidc-issuer-url=https://dex.newtech.academy:32000
- --oidc-client-id=example-app
- --oidc-ca-file=/pki/openid-ca.pem
- --oidc-username-claim=email
- --oidc-groups-claim=groups

그리고 kubelet 재시작을 한다.

$ systemctl restart kubelet

테스트를 위해 dex sample app을 빌드하고 만든 인증서와 함께 실행하게 되면 아래와 같이 샘플페이지를 볼수 있다.

$ sudo apt-get install make golang-1.9
$ git clone https://github.com/coreos/dex.git
$ cd dex
$ git checkout v2.10.0
$ export PATH=$PATH:/usr/lib/go-1.9/bin
$ go get github.com/coreos/dex
$ make bin/example-app
$ export MY_IP=$(curl -s ifconfig.co)
$ ./bin/example-app --issuer https://dex.newtech.academy:32000 --issuer-root-ca /pki/openid-ca.pem --listen http://${MY_IP}:5555 --redirect-uri http://${MY_IP}:5555/callback
2018/07/25 14:37:52 listening on http://169.56.94.55:5555

dex_sample

그리고 로그인을 하게 되면 아래와 같이 Github 인증을 통해 kubernetes 클러스터에서 사용하려고 하는 Token을 획득할 수 있다.

kube_dex

dex_token

다음으로 위에서 만든 token을 사용하기 위해서 Role, RoleBinding과 실제 로그인할 github 계정을 User로 사용하도록 아래와 같이 작성한다.

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: exampleUser
namespace: default
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: exampleUser
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: exampleUser
subjects:
- kind: User
name: ddiiwoong@gmail.com
namespace: default

계정생성을 하고 위에서 만든 token을 가지고 developer 사용자를 설정하고 context설정로 dev-default로 생성한다.

$ kubectl create -f user.yaml
role.rbac.authorization.k8s.io "exampleUser" created
rolebinding.rbac.authorization.k8s.io "exampleUser" created

$ export TOKEN=<dex_sample_app_token>

$ kubectl config set-credentials developer --auth-provider=oidc --auth-provider-arg=idp-issuer-url=https://dex.newtech.academy:32000 --auth-provider-arg=client-id=example-app --auth-provider-arg=idp-certificate-authority=/pki/openid-ca.pem --auth-provider-arg=id-token=${TOKEN}
User "developer" set.

$ kubectl config set-context dev-default --cluster=kubernetes --namespace=default --user=developer
Context "dev-default" created.

위처럼 만들고 나면 현재 클러스터의 context들을 확인할수 있다. 확인후에 context를 dev-default로 바꿔보자.
새로운 context를 확인할수 있다.

$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* admin-cluster.local cluster.local admin-cluster.local
dev-default kubernetes developer default

$ kubectl config use-context dev-default
Switched to context "dev-default".

$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
admin-cluster.local cluster.local admin-cluster.local
* dev-default kubernetes developer default

$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
dex dex-64c4fb5b44-42d44 1/1 Running 0 1h
dex dex-64c4fb5b44-r9p9d 1/1 Running 0 1h
dex dex-64c4fb5b44-w5s2m 1/1 Running 0 1h
kube-system calico-node-2bqll 1/1 Running 0 13h
kube-system calico-node-cljb2 1/1 Running 0 13h
kube-system calico-node-svh5n 1/1 Running 0 13h
kube-system kube-apiserver-node1 1/1 Running 0 1h
kube-system kube-controller-manager-node1 1/1 Running 2 13h
kube-system kube-dns-7bd4d5fbb6-7bb2j 3/3 Running 0 13h
kube-system kube-dns-7bd4d5fbb6-g8lz8 3/3 Running 0 13h
kube-system kube-proxy-node1 1/1 Running 0 13h
kube-system kube-proxy-node2 1/1 Running 0 13h
kube-system kube-proxy-node3 1/1 Running 0 13h
kube-system kube-scheduler-node1 1/1 Running 2 13h
kube-system kubedns-autoscaler-679b8b455-qxlvw 1/1 Running 0 13h
kube-system kubernetes-dashboard-55fdfd74b4-mbkm8 1/1 Running 0 13h
kube-system nginx-proxy-node2 1/1 Running 0 13h
kube-system nginx-proxy-node3 1/1 Running 0 13h
kube-system tiller-deploy-5c688d5f9b-52tls 1/1 Running 0 13h

정리

처음에도 언급했듯이 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 같은 엔터프라이즈에서 프라이빗 구축을 위한 것들도 나오고 있어서 더더욱 고민에 빠진 요즘이다...