TheOpenCloudEngine / uEngine5-base

uEngine5 BPMS that totally re-written in Microservices architecture. uEngine5 can act as not only a conventional Workflow or BPMS but also as a REST api orchestrator or a BPaaS (Business process as a service) of members of OCE's MSA components.
MIT License
10 stars 13 forks source link

Deployment of uEngine5 on Kubernetes #116

Open jinyoung opened 5 years ago

jinyoung commented 5 years ago

Definition Service 의 Deploy

Docker image 업로드

git clone https://github.com/TheOpenCloudEngine/uEngine5-base.git
cd uEngine5-base/definition-service
mvn package -B
docker build -t gcr.io/uengine5-k8s-project/uengine-definition-service:v1 .
docker push gcr.io/uengine5-k8s-project/uengine-definition-service:v1

Deployment 만들기

$ nano deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: uengine-definition-deployment
  labels:
    app: uengine-definition
    group: uengine
spec:
  replicas: 3
  selector:
    matchLabels:
      app: uengine-definition
  template:
    metadata:
      labels:
        app: uengine-definition
        group: uengine
    spec:
      containers:
      - name: uengine-definition
        image: gcr.io/uengine5-k8s-project/uengine-definition-service:v1
        ports:
        - containerPort: 8080

Deploy 시키기

kubectl create -f deployment.yml
kubectl get pods # deploy 확인
(pod 3개 생성된 것 확인)
uengine-definition-deployment-79d484b99f-px4fk   1/1       Running            0          1m
uengine-definition-deployment-79d484b99f-qb5hj   1/1       Running            0          1m
uengine-definition-deployment-79d484b99f-tvnp9   1/1       Running            0          1m

Service 생성하기

$ nano service.yml 

apiVersion: v1
kind: Service
metadata:
  name: uengine-definition-svc
  labels:
    app: uengine-definition
    group: uengine
spec:
  type: LoadBalancer
  ports:
  - port: 8080
  selector:
    app: uengine-definition

$ kubectl create -f service.yml

$ kubectl get services   # 확인
NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
eureka-svc               LoadBalancer   10.43.253.142   35.225.148.29   8080:30547/TCP   1h
kubernetes               ClusterIP      10.43.240.1     <none>          443/TCP          1d
uengine-definition-svc   LoadBalancer   10.43.242.217   35.232.6.115    8080:32248/TCP   2m 

웹브라우저를 하나 열어, 확인된 External-IP 인 35.232.6.115 에 8080 으로 접속하면 아래와 같이 서비스가 열린것을 확인 가능:

{
  "_links" : {
    "profile" : {
      "href" : "http://35.232.6.115:8080/profile"
    }
  }
}

[Note] 지금은 테스트를 위해 Definition Service 를 곧바로 LoadBalancer 로 바로 노출 시켜 인터넷에서 확인했지만 나중에 Zuul 로 포장하여 우회시키고 ClusterIP (내부 ip) 로만 서비스를 열 예정이다.

프로세스 정의의 deploy

$ http POST http://35.232.6.115:8080/definition/raw/folder/object1.json definition="Hello" 

HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Date: Mon, 27 Aug 2018 03:15:13 GMT
Transfer-Encoding: chunked
X-Application-Context: definition:msa:8080

{
    "_links": {
        "instantiation": {
            "href": "http://35.232.6.115:8080/instance?defPath=/folder/object1.String.xml"
        }, 
        "raw": {
            "href": "http://35.232.6.115:8080/definition/raw//folder/object1.String.json"
        }, 
        "self": {
            "href": "http://35.232.6.115:8080/definition//folder/object1.String.xml"
        }
    }, 
    "directory": false, 
    "name": "object1.String.xml", 
    "path": "/folder/object1.String.xml"
}

그런데, Deployment 가 3개 있기 때문에 파일의 저장소가 각 인스턴스별 달라져서 External Ip 로 GET할때마다 있을때도 있고 없을때도 있다. 이 문제는 Replica 들이 동일한 저장소를 mount하도록 처리함으로서 해결할 수 있다 (continued)

Google Cloud Disk 의 mount

앞서 인스턴스간 파일 시스템 공유를 위한 작업으로 PersistenceVolume 과 PersistenceVolumeClaim 객체를 만든다:

$ nano pv.yml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-demo2
spec:
  storageClassName:
  capacity:
    storage: 20G
  accessModes:
    - ReadWriteOnce
  gcePersistentDisk:
    pdName: disk-2
    fsType: ext4

$ kubectl create -f pv.yaml

$ nano pvc.yaml

apiVersion: v1
kind : PersistentVolumeClaim
metadata:
  name: pv-claim-demo2
spec:
  storageClassName: ""
  volumeName: pv-demo2
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20G

$ kubectl create -f pvc.yml

각 Storage 는 gcloud 에서 미리 생성해두어야 한다. storage 생성방법은 아래와 같으며, project 와 동일한 region 에 생성시켜주야 한다.

gcloud compute disks create mydisk --size 100gb --type pd_standard

그런후, 생성한 PersistenceVolumeClaim 을 Deployment 내의 spec 란에 mount 시켜주면 된다.

$ nano deployment.yml
...
    spec:
      containers:
      - name: uengine-definition
        image: gcr.io/uengine5-k8s-project/uengine-definition-service:v1
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: storage       
          mountPath: /oce/repository
        volumes:
        - name: storage 
          persistentVolumeClaim:
            claimName: pv-claim-demo2

위와 같이 수정후, 반영:

kubectl apply -f deployment.yml

무정지 재배포

kubectl get pods
kubectl get deployments
kubectl set image deployment/uengine-definition-deployment uengine-definition=uengine-definition-service:v2  #container 명=registry:tag
kubectl rollout status deployment/uengine-definition-deployment
kubectl get deployments
kubectl get pods
kubectl describe pods (podname)

롤백

kubectl get pods
kubectl get deployments -o wide
kubectl rollout undo deployment/uengine-definition-deployment
kubectl get deployments -o wide

오토스케일링

kubectl autoscale deployment uengine-definition-deployment --cpu-percent=50 --min=1 --max=10

Availability 체크 통한 Self-Healing 관리


$ nano deployment.yml

...
    spec:
      containers:
...
      livenessProbe:
        httpGet:
          path: /healthz
          port: 8080
          httpHeaders:
          - name: X-Custom-Header
            value: Awesome
        initialDelaySeconds: 3
        periodSeconds: 3

(저장후 quit)
kubectl apply -f ~/deployment.yml

Netflix OSS 적용하기

Registry 서비스 올리기

cd ~/
docker build -t gcr.io/uengine5-k8s-project/eureka:v1 .   #gcr.io 가 들어가면 goole registry 에 등록됨
docker push gcr.io/uengine5-k8s-project/eureka:v1

$ nano deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: eureka-deployment
  labels:
    app: eureka
spec:
  replicas: 3
  selector:
    matchLabels:
      app: eureka
  template:
    metadata:
      labels:
        app: eureka
    spec:
      containers:
      - name: eureka
        image: gcr.io/uengine5-k8s-project/eureka:v1     #전체 주소를 주야 함.
      ports:
        - containerPort: 8741    # 8741 로 내부에서 오픈했다...

(kubectl delete deployments -l app=eureka  # 지울일이 있다면.. 그러나 정지상태를 노출할 수 있으니 테스트 때만 사용하고, 운영때는 apply -f 나 image change 를 하는걸 추천)
kubectl apply -f deployment.yml
kubectl get pods
kubectl describe pod eureka-deployment-7cddf689df-hxvzh    #pod가 일단 잘 디플로이 됐는지 확인

서비스(LoadBalancer) 등록 통한 외부노출

apiVersion: v1
kind: Service
metadata:
  name: eureka-svc
  labels:
    app: eureka
spec:
  ports:
  - port: 27017
    protocol: TCP
  selector:
    app: eureka

Registry 서비스의 주소는 환경변수를 통하여 각 서비스에 전파시켜야 하는데, Kubernetes 에서는 각 서비스들의 주소를 전달하는 체계로 환경변수 혹은 내부 DNS 를 사용한다. 환경 변수는 유레카가 내려갔다 올라올때 전체 서비스의 재기동을 야기할 수 있으므로, 여기서는 내부 DNS를 사용하는게 좋다:

$ nano  src/main/resources/application.yml

... dev, stg, prod 에 해당하는 profile 에서 설정 ..

eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka-svc.default.svc.cluster.local:8761/eureka/     #서비스명.네임스페이스명(기본은 default).svc.cluster.local 로 접근가능하다.

설정을 먹히게 하기 위해 1. 메이븐빌드, 2. 도커빌드, 3. 레지스트리 푸시, 4. 쿠버 desired state 변경을 아래와 같이 수행한다:

mvn package -B
docker build -t gcr.io/uengine5-k8s-project/uengine-definition-service:v2 .  # 버전업
docker push gcr.io/uengine5-k8s-project/uengine-definition-service:v2
nano deployment.yml
 (image 를 gcr.io/uengine5-k8s-project/uengine-definition-service:v2 로 변경 후 저장)
kubectl apply -f deployment.yml

[Tip] 환경 변수로 혹시 전달을 해야 하는 경우라면, 다음과 같이 모든 서비스들의 주소는 아래처럼 container 내부의 환경 변수로 전달됨을 확인할 수 있으므로, "Service명_HOST" 를 통하여 얻어낸 후 애플리케이션에 전달해주면 된다.

$ kubectl exec -it uengine-definition-deployment-79d484b99f-px4fk -- /bin/sh
/ # export
export CI_COMMIT_SHA=''
export CI_PROJECT_NAME=''
export EUREKA_SVC_PORT='tcp://10.43.253.142:8080'
export EUREKA_SVC_PORT_8080_TCP='tcp://10.43.253.142:8080'
export EUREKA_SVC_PORT_8080_TCP_ADDR='10.43.253.142'
export EUREKA_SVC_PORT_8080_TCP_PORT='8080'
export EUREKA_SVC_PORT_8080_TCP_PROTO='tcp'
export EUREKA_SVC_SERVICE_HOST='10.43.253.142'
export EUREKA_SVC_SERVICE_PORT='8080'
export HOME='/root'
export HOSTNAME='uengine-definition-deployment-79d484b99f-px4fk'
export JAVA_ALPINE_VERSION='8.111.14-r0'
export JAVA_HOME='/usr/lib/jvm/java-1.8-openjdk'
export JAVA_VERSION='8u111'
export KUBERNETES_PORT='tcp://10.43.240.1:443'
export KUBERNETES_PORT_443_TCP='tcp://10.43.240.1:443'
export KUBERNETES_PORT_443_TCP_ADDR='10.43.240.1'
export KUBERNETES_PORT_443_TCP_PORT='443'
export KUBERNETES_PORT_443_TCP_PROTO='tcp'
export KUBERNETES_SERVICE_HOST='10.43.240.1'
export KUBERNETES_SERVICE_PORT='443'
export KUBERNETES_SERVICE_PORT_HTTPS='443'
export LANG='C.UTF-8'
export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin'
export PWD='/'
export SHLVL='1'
export TERM='xterm'
jinyoung commented 5 years ago

참고 - https://github.com/TheOpenCloudEngine/uEngine-cloud/wiki/Kubernetes-%EC%8B%A4%EC%8A%B5-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8

jinyoung commented 5 years ago

Todos

  1. eureka 에 등록이 안되는 이유 확인 : 유레카 api 를 각 container 내에서 직접 호출해봐야 알 수 있을듯..
  2. zuul 로 연결, zuul 을 LoadBalancer 를 통해 open
  3. zuul 을 ingress 로 변경 - 변경에 의미가 있는가? 성능상 이점. 변경시 발생하는 문제, 유레카에 등록된 serviceId 를 첫번째 path 요소로 하여 자동 라우팅 되던 것이 ingress 에선 어떻게 되는가?
  4. 제대로 rollout 과 deploy 가 안되는거 같다... (disk mount 설정이 어떤 컨테이너에만 반영되어 있는 상황 발견)
  5. 프로메테우스로 로그 수집해서 볼 수 있는 환경 구축 필요함
  6. Spinnaker 혹은 gitlab과 연계
  7. 계정별 접근 권한 처리