Skip to main content

24 posts tagged with "Kubernetes"

View All Tags

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

· 9 min read

TLS 인증서

제한된 예산과 시간, 인력으로 서비스를 만들다 보니 어려운 점들이 참 많다. 특히나 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에서 발급부터 갱신까지 해주는 오픈소스를 찾아 적용까지 하고 이렇게 포스팅을 한다.

Cert-manager

인증서 없이 구성할수도 있지만 기본적으로 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 같은 것을들 사용하여 인증서 발급을 할 수 있다. 인증서 발급후에도 만료가 되기 전에 바로 갱신을 자동으로 한다.

구성

certmanager

사전준비사항

  • helm
  • k8s cluster (1.7+) - CRD 사용이 가능해야함

설치

$ 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

인증서 설정 및 배포

인증서 등록 및 만료시 noti를 받기 위한 변수 설정을 한다.

$ export EMAIL=zaction@sk.com

그리고 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을 추가하고 재배포를 해야한다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: auth-ingress
spec:
rules:
- host: auth.dev.action.cloudz.co.kr
http:
paths:
- backend:
serviceName: faas-auth-service
servicePort: 8081
path: /

TLS 인증서 발급

인증서를 발급할때는 위에서 만든 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

secret이 생성된것을 확인할 수 있다.

$ kubectl get secret | grep tls
auth-dev-tls kubernetes.io/tls 2 3h

이후 위에서 만든 Ingress에 TLS를 적용한다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: auth-ingress
annotations:
ingress.bluemix.net/hsts: enabled=true maxAge=<31536000> includeSubdomains=true
ingress.bluemix.net/redirect-to-https: "True"
spec:
rules:
- host: auth.dev.action.cloudz.co.kr
http:
paths:
- backend:
serviceName: faas-auth-service
servicePort: 8081
path: /
tls:
- hosts:
- auth.dev.action.cloudz.co.kr
secretName: auth-dev-tls

위 설정을 적용하고 상태를 확인한다.

$ 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

cert-tls

정리

설계의 실수로 시작되었지만 정리하다보니 인증서 갱신이 자동을 된다는 점과 또한 Kubernetes환경에서 TLS관리가 간편하다는것을 알게 되었다. 인증서 비용을 아낀것도 하나의 성과이기도 하다. 인증서는 써야하겠고 정말 많은 애플리케이션들을 생성/삭제를 상시 해야하는 환경이라면 한번 적용해볼만한 가치가 있는것 같다.

· 11 min read

Operators in Kubernetes

Operators 란?

정의(Definition) : Kubernetes Application 을 패키징, 배포, 관리하는 방법. Helm과는 조금 달라 따로 이야기 해보고자 한다.

기본적으로 Kubernetest Application은 kubernetes에 의해 배포되고 kubect과 kube-API를 사용하여 관리한다.

결론적으로 말하면 Kubernetes에 내가 만든 application을 서비스하고 관리하려면 결국 Kubernetes의 API들을 모두 이해하고 사용할수 있어야 한다. 일반적인 개발자에게 진입장벽이 어느정도 있다고 보여지며 이를 모두 이해시키는것도 조직적인 측면에서는 낭비일수 도 있다. 그래서 Helm등을 통한 application배포 전략을 세우기도 하지만 이것도 한계가 있을수 있다. 그래서 Operator는 Kubernetes상에서 application을 관리하는 런타임이라고 생각하는게 맞는것 같다.

etcd

Operators

Operators는 application마다 운영정보를 넣을수 있다.

Kubernetes 에 배포된 응용프로그램의 모든 특성을 알 필요가 없고 이를 통해 사용자는 매니지드 클라우드 서비스 경험(기존 IaaS/PaaS관리와 유사)과 유사하게 운영이 가능하다.

Operator가 배포되면 Kubernetes API확장 개념인 CRDs(Custom Resource Definitions)을 사용하여 관리할수 있다. Kubernetes에 Stateful 서비스를 배포하는 단순하고 좋은 방법이 될수 있다.

예를 들면, Postgres 클러스터, Prometheus 클러스터, Etcd 클러스터 같이 운영측면의 application들을 유지, 운영하는데 쓰일수 있다. (자세한건 뒤쪽 예시로 설명하겠다)

그러면 먼저 CRDs(Custom Resource Definitions)에 대해서 먼저 알아본다. CRDs에 대해서는 별도로 정리하려 하다가 내용이 많이 않아 핵심적인 내용만 같이 적어본다.

  • Custom Resource Definitions(a.k.a CRDs)
  • k8s Object를 확장해서 사용할 수 있는 가장 간단한 방법
  • CRDs는 Kubernetes의 확장 기능
  • Kubernetes 사용자가 클러스터내에서 직접 custom Object를 yaml형태로 생성, 수정, 삭제, 사용할수 있도록 하는 기능
    • K8s database(etcd)와 API를 그대로 활용할 수 있음)
    • CRUD 가능 (Create, Read, Update, Delete)
  • 모든 클러스터에 동적으로 resource 등록/삭제 가능
  • Operators는 CRDs를 포함하고 있음
    • Operator를 추가하면 CRDs가 등록됨
  • Resource 당 하나의 API 버전만 지원(~1.10버전, 1.11버전 이후 개수 제한 없어짐)
  • 1.8+ 이후부터 JSON 스키마 유효성 검증 가능 *주의사항 : etcd가 별도 분리된 managed서비스나 etcd 인스턴스가 분리된 환경에서 사용권고

주요 활용용도

  • Operators
  • Application 정보 저장
  • RouteRule on istio
  • GameServer on Agones

CRDs 관련 발표자료

https://ddiiwoong.github.io/2018/openinfraday18/

Operators example

etcd, Rook, Prometheus, Vault, MySQL, Postgres, Redis, kafka-비공식 등 Operator로 배포될수 있는 예시들은 해당 링크를 보면 자세히 확인할 수 있다. (오라클이 공격적으로 K8s비지니스로 뛰어드는듯한 그림이다…)

주로 저장소나 키-밸류 스토어, 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이 나올것 같긴 하다.

etcd-operator

etcd operator는 기본적으로 다음과 같은 기능을 한다.

  • Create and Destroy
  • Resize
  • Failover
  • Rolling upgrade
  • Backup and Restore

Requirements

  • Kubernetes 1.8+
  • etcd 3.2.13+

Installation guide

설치는 단순하다. 먼저 RBAC설정을 한다.

$ git clone https://github.com/coreos/etcd-operator.git
$ cd etcd-operator
$ example/rbac/create_role.sh

그리고 etcd-operator 배포

$ kubectl create -f example/deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: etcd-operator
spec:
replicas: 1
template:
metadata:
labels:
name: etcd-operator
spec:
containers:
- name: etcd-operator
image: quay.io/coreos/etcd-operator:v0.9.2
command:
- etcd-operator
# Uncomment to act for resources in all namespaces. More information in doc/clusterwide.md
#- -cluster-wide
env:
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name

deployment.yaml 내용을 보면 CustomResourceDefinition이 존재하지 않는다. etcd-operator가 자동으로 CRD를 생성하기 때문에 아래와 같이 CRD를 확인할 수 있다.

$ kubectl get customresourcedefinitions
NAME KIND
etcdclusters.etcd.database.coreos.com CustomResourceDefinition.v1beta1.apiextensions.k8s.io

etcd cluster create/resize/failover/upgrade

operator를 이용하여 etc cluster를 구성한다. operator를 통한 클러스터 구성내용을 확인하면 아주 단순하다. 버전과 사이즈뿐이다.

$ cat example/example-etcd-cluster.yaml
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdCluster"
metadata:
name: "example-etcd-cluster"
## Adding this annotation make this cluster managed by clusterwide operators
## namespaced operators ignore it
# annotations:
# etcd.database.coreos.com/scope: clusterwide
spec:
size: 3
version: "3.2.13"
$ kubectl create -f example/example-etcd-cluster.yaml
etcdcluster.etcd.database.coreos.com/example-etcd-cluster created

순차적으로 etcd cluster가 구성되는것을 볼수 있다.

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
etcd-operator-69b559656f-wrhxz 1/1 Running 0 3h
example-etcd-cluster-2kd4t667j5 1/1 Running 0 1m
example-etcd-cluster-k4lxm96v7h 1/1 Running 0 1m
example-etcd-cluster-lm7mkhvldw 1/1 Running 0 1m

이번에는 scale-out 테스트를 진행한다.
example/example-etcd-cluster.yaml 내용에서 size: 3size: 5 로 변경하고 다시 적용하면 2 node가 추가로 생성된다.

$ kubectl apply -f example/example-etcd-cluster.yaml
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
etcd-operator-69b559656f-wrhxz 1/1 Running 0 3h
example-etcd-cluster-2kd4t667j5 1/1 Running 0 9m
example-etcd-cluster-2pwm84lrf4 1/1 Running 0 34s
example-etcd-cluster-97qk6gs4sp 1/1 Running 0 50s
example-etcd-cluster-k4lxm96v7h 1/1 Running 0 9m
example-etcd-cluster-lm7mkhvldw 1/1 Running 0 8m

반대로 줄이는것도 동일하다.

failover 테스트의 경우에도 그냥 pod를 삭제하거나 worker를 날리는것으로도 동일하게 spec을 유지하는 kubernetes resource 특성으로 인해 바로 생성이 되는것을 확인할수 있다.

이번에는 operator만 날려보겠다. 아래처럼 operator deployment만 삭제를 해도 pods는 남아있는것을 볼수 있다.

$ kubectl delete -f example/deployment.yaml
deployment "etcd-operator" deleted
deployment.extensions "etcd-operator" deleted
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
example-etcd-cluster-2kd4t667j5 1/1 Running 0 14m
example-etcd-cluster-2pwm84lrf4 1/1 Running 0 5m
example-etcd-cluster-97qk6gs4sp 1/1 Running 0 5m
example-etcd-cluster-k4lxm96v7h 1/1 Running 0 14m
example-etcd-cluster-lm7mkhvldw 1/1 Running 0 13m

etcd pod 하나를 날려본다. 4개 pod만 남은것을 확인할 수 있다.

$ kubectl delete pod example-etcd-cluster-2kd4t667j5 --now
pod "example-etcd-cluster-2kd4t667j5" deleted
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
example-etcd-cluster-2pwm84lrf4 1/1 Running 0 9m
example-etcd-cluster-97qk6gs4sp 1/1 Running 0 9m
example-etcd-cluster-k4lxm96v7h 1/1 Running 0 18m
example-etcd-cluster-lm7mkhvldw 1/1 Running 0 17m

다시한번 operator를 배포하게 되면 잠시후에 5개로 다시 pod가 복원된것을 확인할수 있다.

$ kubectl create -f example/deployment.yaml
deployment.extensions/etcd-operator created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
etcd-operator-69b559656f-8ks8m 1/1 Running 0 44s
example-etcd-cluster-2pwm84lrf4 1/1 Running 0 11m
example-etcd-cluster-97qk6gs4sp 1/1 Running 0 11m
example-etcd-cluster-k4lxm96v7h 1/1 Running 0 20m
example-etcd-cluster-kskgvlbsm9 1/1 Running 0 10s
example-etcd-cluster-lm7mkhvldw 1/1 Running 0 19m

업그레이드의 경우도 다른 resource와 동일하게 version 부분을 변경하여 rollout이 가능하다.

정리

다음 소스를 통해 직접 operator를 개발이 가능하다.
(Source: https://coreos.com/operators/)

Operator SDK를 사용하면 Kubernetes API 상세스펙을 배우지 않고도 쉽게 operator 빌드가 가능하다. 또한 관리를 위한 Operator Lifecycle Manager나 Operator Metering 같은 기능을 사용하여 좀더 관리측면에서 강화하고자 하는 CoreOS진영의 노력이 보이는듯 하다.

아직 alpha, beta단계의 operator 프로젝트들이 대부분이지만 helm차트와 같이 kubernetes API 스펙을 이해하고 사용하지 않고도 쉽게 운영자가 기반 어플리케이션을 관리 할수 있다는것으로 보아 향후 RedHat이나 Oracle 진영에서 본인들 kubernetes 관련 제품들을 홍보하고 서비스라인에 포함시키는 방향으로 적극적으로 개발을 하고 있는것으로 보인다. 앞으로 mysql, redis, kakfa 등을 operator로 배포하고 관리하는 일이 더 많아 질것 같다.