gcp에서 nginx를 사용해 network loadbalancer를 생성하는 방법에 대한 예이다. 


export MY_REGION=[YOUR_REGION]


export MY_ZONE=[YOUR_ZONE]


export CLUSTER_NAME=httploadbalancer



gcloud config set project $DEVSHELL_PROJECT_ID


gcloud config set compute/region $MY_REGION


gcloud config set compute/zone $MY_ZONE




gcp에서 networklb를 생성하고 nginx를 띄웠다. 


$ gcloud container clusters create networklb --num-nodes 3



$ kubectl run nginx --image=nginx --replicas=3deployment "nginx" created


정상적으로 pod가 떠있는지 확인한다.


$ kubectl get pods NAME READY STATUS RESTARTS AGEnginx-7c87f569d-2hsjp 1/1 Running 0 13snginx-7c87f569d-2vhj9 1/1 Running 0 13snginx-7c87f569d-b4krw 1/1 Running 0 13s


nginx 클러스터를 lb로 expose한다.


$ kubectl expose deployment nginx --port=80 --target-port=80 --type=LoadBalancer



이제 lb nginx 정보를 얻는다.

$ kubectl get service nginxNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEnginx LoadBalancer 10.39.253.240 35.230.93.151 80:30516/TCP 13m



정상적이다.



이제 lb를 정리한다. 


큐버네티스 nginx 서비스를 삭제한다.



$ kubectl delete service nginxservice "nginx" deleted



큐버네티스 레플리케이션 콘트롤러와 nginx pod(인스턴스)를 삭제한다.


$ kubectl delete deployment nginxdeployment "nginx" deleted



lb를 내린다. 


$ gcloud container clusters delete networklb --zone='us-west1-a' The following clusters will be deleted. - [networklb] in [us-west1-a]Do you want to continue (Y/n)? YDeleting cluster networklb...done.Deleted [https://container.googleapis.com/v1/projects/111/zones/us-west1-a/clusters/networklb].





이번에는 http load balancer를 사용한다.







$ export MY_ZONE='us-west1-a'


samuel이라는 cluster를 생성한다. 

$ gcloud container clusters create samuel --zone $MY_ZONE


WARNING: Currently node auto repairs are disabled by default. In the future this will change and they will be enabled by default. Use `--[no-]enable-autorepai

r` flag  to suppress this warning.

WARNING: Starting in Kubernetes v1.10, new clusters will no longer get compute-rw and storage-ro scopes added to what is specified in --scopes (though the lat

ter will remain included in the default --scopes). To use these scopes, add them explicitly to --scopes. To use the new behavior, set container/new_scopes_beh

avior property (gcloud config set container/new_scopes_behavior true).

piVersion: extensions/v1beta1

Creating cluster samuel...done.

Created ....

03470e


NAME    LOCATION    MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS

samuel  us-west1-a  1.8.10-gke.0    104.198.107.200  n1-standard-1  1.8.10-gke.0  3          RUNNING



$ kubectl run nginx --image=nginx --port=80

deployment "nginx" created



$ kubectl expose deployment nginx --target-port=80 --type=NodePort

service "nginx" exposed




basic-ingress.yaml 파일을 생성한다.


apiVersion: extensions/v1beta1

kind: Ingress

metadata:

        name: basic-ingress

spec:

        backend:

                serviceName: nginx

                servicePort: 80




$ kubectl create -f basic-ingress.yaml                                                                    

ingress "basic-ingress" created



$ kubectl get ingress basic-ingress --watch

NAME            HOSTS     ADDRESS   PORTS     AGE

basic-ingress   *                   80        7s

basic-ingress   *         35.227.208.116   80        57s

basic-ingress   *         35.227.208.116   80        57s



$ kubectl describe ingress basic-ingress

Name:             basic-ingress

Namespace:        default

Address:          35.227.208.116

Default backend:  nginx:80 (10.36.1.5:80)

Rules:

  Host  Path  Backends

  ----  ----  --------

  *     *     nginx:80 (10.36.1.5:80)

Annotations:

  forwarding-rule:  k8s-fw-default-basic-ingress--27688e79a493971e

  target-proxy:     k8s-tp-default-basic-ingress--27688e79a493971e

  url-map:          k8s-um-default-basic-ingress--27688e79a493971e

  backends:         {"k8s-be-32520--27688e79a493971e":"Unknown"}

Events:

  Type    Reason   Age              From                     Message

  ----    ------   ----             ----                     -------

  Normal  ADD      4m               loadbalancer-controller  default/basic-ingress

  Normal  CREATE   3m               loadbalancer-controller  ip: 35.227.208.116

  Normal  Service  3m (x3 over 3m)  loadbalancer-controller  default backend set to nginx:32520




이제는 정리하는 커맨드를 사용한다.



$ kubectl delete -f basic-ingress.yaml

ingress "basic-ingress" deleted


$ kubectl delete deployment nginx

deployment "nginx" deleted


$ gcloud container clusters delete samuel

ERROR: (gcloud.container.clusters.delete) One of [--zone, --region] must be supplied: Please specify location..


$ gcloud container clusters delete samuel --zone=$MY_ZONE

The following clusters will be deleted.

 - [samuel] in [us-west1-a]

Do you want to continue (Y/n)?  Y

Deleting cluster samuel...done.

Deleted 







kubectl get pods -owideNAME READY STATUS RESTARTS AGE IP NODEnginx-7c87f569d-2hsjp 1/1 Running 0 8s 10.36.0.6 gke-networklb-default-pool-3f6ca419-nmpbnginx-7c87f569d-2vhj9 1/1 Running 0 8s 10.36.2.6 gke-networklb-default-pool-3f6ca419-vs85nginx-7c87f569d-b4krw 1/1 Running 0 8s 10.36.1.6 gke-networklb-default-pool-3f6ca419-wxvl







$ kubectl get pods --all-namespacesNAMESPACE NAME READY STATUS RESTARTS AGEdefault nginx-7c87f569d-2hsjp 1/1 Running 0 2mdefault nginx-7c87f569d-2vhj9 1/1 Running 0 2mdefault nginx-7c87f569d-b4krw 1/1 Running 0 2mkube-system event-exporter-v0.1.8-599c8775b7-nc8xw 2/2 Running 0 3mkube-system fluentd-gcp-v2.0.9-dqrnb 2/2 Running 0 3mkube-system fluentd-gcp-v2.0.9-lrnjr 2/2 Running 0 3mkube-system fluentd-gcp-v2.0.9-zh2qq 2/2 Running 0 3m kube-system heapster-v1.4.3-57c7677fc4-6mqz8 3/3 Running 0 2m kube-system kube-dns-778977457c-8xtfs 3/3 Running 0 2m kube-system kube-dns-778977457c-nvztz 3/3 Running 0 3m kube-system kube-dns-autoscaler-7db47cb9b7-jbdxv 1/1 Running 0 3m kube-system kube-proxy-gke-networklb-default-pool-3f6ca419-nmpb 1/1 Running 0 3m kube-system kube-proxy-gke-networklb-default-pool-3f6ca419-vs85 1/1 Running 0 3m kube-system kube-proxy-gke-networklb-default-pool-3f6ca419-wxvl 1/1 Running 0 3m kube-system kubernetes-dashboard-6bb875b5bc-9r62n 1/1 Running 0 3mkube-system l7-default-backend-6497bcdb4d-sngpv 1/1 Running 0 3m




$ kubectl get pods --include-uninitializedNAME READY STATUS RESTARTS AGEnginx-7c87f569d-2hsjp 1/1 Running 0 3mnginx-7c87f569d-2vhj9 1/1 Running 0 3mnginx-7c87f569d-b4krw 1/1 Running 0 3m





$ kubectl get pods --field-selector=status.phase=RunningNAME READY STATUS RESTARTS AGEnginx-7c87f569d-2hsjp 1/1 Running 0 4mnginx-7c87f569d-2vhj9 1/1 Running 0 4mnginx-7c87f569d-b4krw 1/1 Running 0 4m



echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})nginx-7c87f569d-2hsjp nginx-7c87f569d-2vhj9 nginx-7c87f569d-b4krw



$ kubectl get pods -o json{ "apiVersion": "v1", "items": [ { "apiVersion": "v1", "kind": "Pod", "metadata": { "annotations": { "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\",\"namespace\":\"default\",\"name\":\"nginx-7c87f569d\",\"uid\":\"ef24e95c-6d12-11e8-9bae-42010a8a0201\",\"apiVersion\":\"extensions\",\"resourceVersion\":\"562\"}}\n", "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container nginx" }, "creationTimestamp": "2018-06-11T01:01:12Z", "generateName": "nginx-7c87f569d-", "labels": { "pod-template-hash": "374391258", "run": "nginx" "lastTransitionTime": "2018-06-11T01:01:18Z", }, "name": "nginx-7c87f569d-2hsjp", "namespace": "default", "ownerReferences": [ { "apiVersion": "extensions/v1beta1", "blockOwnerDeletion": true, "controller": true, "kind": "ReplicaSet", "name": "nginx-7c87f569d", "uid": "ef24e95c-6d12-11e8-9bae-42010a8a0201" } ], "resourceVersion": "599", "selfLink": "/api/v1/namespaces/default/pods/nginx-7c87f569d-2hsjp", "uid": "ef2e0e4a-6d12-11e8-9bae-42010a8a0201" }, "spec": { "containers": [ { "image": "nginx", "imagePullPolicy": "Always", "name": "nginx", "resources": { "requests": { "cpu": "100m" } }, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "volumeMounts": [ { "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", "name": "default-token-5khqf", "readOnly": true } ] } ], "dnsPolicy": "ClusterFirst", "nodeName": "gke-networklb-default-pool-3f6ca419-nmpb", "restartPolicy": "Always", "schedulerName": "default-scheduler", "securityContext": {}, "serviceAccount": "default", "serviceAccountName": "default", "terminationGracePeriodSeconds": 30, "tolerations": [ { "effect": "NoExecute", "key": "node.alpha.kubernetes.io/notReady", "operator": "Exists", "tolerationSeconds": 300 }, { "effect": "NoExecute", "key": "node.alpha.kubernetes.io/unreachable", "operator": "Exists", "tolerationSeconds": 300 } ], "volumes": [ { "name": "default-token-5khqf", "secret": { "defaultMode": 420, "secretName": "default-token-5khqf" } } ] }, "status": { "conditions": [ { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:12Z", "status": "True", "type": "Initialized" }, { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:18Z", "status": "True", "type": "Ready" }, { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:12Z", "status": "True", "type": "PodScheduled" } ], "containerStatuses": [ { "containerID": "docker://4530901e7f82ad2b601a759a28f48a693c9944299318e3126ecba9edf0c2b615", "image": "nginx:latest", "imageID": "docker-pullable://nginx@sha256:1f9c00b4c95ef931afa097823d902e7602aebc3ec5532e907e066978075ca3e0", "lastState": {}, "name": "nginx", "ready": true, "restartCount": 0, "state": { "running": { "startedAt": "2018-06-11T01:01:17Z" } } } ], "hostIP": "10.138.0.3", "phase": "Running", "podIP": "10.36.0.6", "qosClass": "Burstable", "startTime": "2018-06-11T01:01:12Z" } }, { "apiVersion": "v1", "kind": "Pod", "metadata": { "annotations": { "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\",\"namespace\":\"default\",\"name\":\"nginx-7c87f569d\",\"uid\":\"ef24e95c-6d12-11e8-9bae-42010a8a0201\",\"apiVersion\":\"extensions\",\"resourceVersion\":\"562\"}}\n", "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container nginx" }, "creationTimestamp": "2018-06-11T01:01:12Z", "generateName": "nginx-7c87f569d-", "labels": { "pod-template-hash": "374391258", "run": "nginx" }, "name": "nginx-7c87f569d-2vhj9", "namespace": "default", "ownerReferences": [ { "apiVersion": "extensions/v1beta1", "blockOwnerDeletion": true, "controller": true, "kind": "ReplicaSet", "name": "nginx-7c87f569d", "uid": "ef24e95c-6d12-11e8-9bae-42010a8a0201" } ], "resourceVersion": "602", "selfLink": "/api/v1/namespaces/default/pods/nginx-7c87f569d-2vhj9", "uid": "ef29bf6b-6d12-11e8-9bae-42010a8a0201" }, "spec": { "containers": [ { "image": "nginx", "imagePullPolicy": "Always", "name": "nginx", "resources": { "requests": { "cpu": "100m" } }, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "volumeMounts": [ { "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", "name": "default-token-5khqf", "readOnly": true } ] } ], "dnsPolicy": "ClusterFirst", "nodeName": "gke-networklb-default-pool-3f6ca419-vs85", "restartPolicy": "Always", "schedulerName": "default-scheduler", "securityContext": {}, "serviceAccount": "default", "serviceAccountName": "default", "terminationGracePeriodSeconds": 30, "tolerations": [ { "effect": "NoExecute", "key": "node.alpha.kubernetes.io/notReady", "operator": "Exists", "tolerationSeconds": 300 }, { "effect": "NoExecute", "key": "node.alpha.kubernetes.io/unreachable", "operator": "Exists", "tolerationSeconds": 300 } ], "volumes": [ { "name": "default-token-5khqf", "secret": { "defaultMode": 420, "secretName": "default-token-5khqf" } } ] }, "status": { "conditions": [ { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:12Z", "status": "True", "type": "Initialized" }, { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:18Z", "status": "True", "type": "Ready" }, { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:12Z", "status": "True", "type": "PodScheduled" } ], "containerStatuses": [ { "containerID": "docker://81a65fd0f30327173eb5d41f1a5c0a7e3752aca7963ac510165aa007c4abcd0b", "image": "nginx:latest", "imageID": "docker-pullable://nginx@sha256:1f9c00b4c95ef931afa097823d902e7602aebc3ec5532e907e066978075ca3e0", "lastState": {}, "name": "nginx", "ready": true, "restartCount": 0, "state": { "running": { "startedAt": "2018-06-11T01:01:18Z" } } } ], "hostIP": "10.138.0.4", "phase": "Running", "podIP": "10.36.2.6", "qosClass": "Burstable", "startTime": "2018-06-11T01:01:12Z" } }, { "apiVersion": "v1", "kind": "Pod", "metadata": { "annotations": { "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\",\"namespace\":\"default\",\"name\":\"nginx-7c87f569d\",\"uid\":\"ef24e95c-6d12-11e8-9bae-42010a8a0201\",\"apiVersion\":\"extensions\",\"resourceVersion\":\"562\"}}\n", "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container nginx" }, "creationTimestamp": "2018-06-11T01:01:12Z", "generateName": "nginx-7c87f569d-", "labels": { "pod-template-hash": "374391258", "run": "nginx" }, "name": "nginx-7c87f569d-b4krw", "namespace": "default", "ownerReferences": [ { "apiVersion": "extensions/v1beta1", "blockOwnerDeletion": true, "controller": true, "kind": "ReplicaSet", "name": "nginx-7c87f569d", "uid": "ef24e95c-6d12-11e8-9bae-42010a8a0201" } ], "resourceVersion": "595", "selfLink": "/api/v1/namespaces/default/pods/nginx-7c87f569d-b4krw", "uid": "ef2d8181-6d12-11e8-9bae-42010a8a0201" }, "spec": { "containers": [ { "image": "nginx", "imagePullPolicy": "Always", "name": "nginx", "resources": { "requests": { "cpu": "100m" } }, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "volumeMounts": [ { "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", "name": "default-token-5khqf", "readOnly": true } ] } ], "dnsPolicy": "ClusterFirst", "nodeName": "gke-networklb-default-pool-3f6ca419-wxvl", "restartPolicy": "Always", "schedulerName": "default-scheduler", "securityContext": {}, "serviceAccount": "default", "serviceAccountName": "default", "terminationGracePeriodSeconds": 30, "tolerations": [ { "effect": "NoExecute", "key": "node.alpha.kubernetes.io/notReady", "operator": "Exists", "tolerationSeconds": 300 }, { "effect": "NoExecute", "key": "node.alpha.kubernetes.io/unreachable", "operator": "Exists", "tolerationSeconds": 300 } ], "volumes": [ { "name": "default-token-5khqf", "secret": { "defaultMode": 420, "secretName": "default-token-5khqf" } } ] }, "status": { "conditions": [ { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:12Z", "status": "True", "type": "Initialized" }, { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:18Z", "status": "True", "type": "Ready" }, { "lastProbeTime": null, "lastTransitionTime": "2018-06-11T01:01:12Z", "status": "True", "type": "PodScheduled" } ], "containerStatuses": [ { "containerID": "docker://7818cd87a8cdf37853d6e44cbdc0f06cd9ca84108cd85772c0a22bc95ddaf41d", "image": "nginx:latest", "imageID": "docker-pullable://nginx@sha256:1f9c00b4c95ef931afa097823d902e7602aebc3ec5532e907e066978075ca3e0", "lastState": {}, "name": "nginx", "ready": true, "restartCount": 0, "state": { "running": { "startedAt": "2018-06-11T01:01:17Z" } } } ], "hostIP": "10.138.0.2", "phase": "Running", "podIP": "10.36.1.6", "qosClass": "Burstable", "startTime": "2018-06-11T01:01:12Z" } } ], "kind": "List", "metadata": { "resourceVersion": "", "selfLink": "" }}





'scribbling' 카테고리의 다른 글

[펌] [YAML] 멀티 라인(multi line)  (0) 2018.06.25
소비자 주도 계약 테스트  (0) 2018.06.18
gradle 과 java 10 이슈  (0) 2018.06.08
롤아웃(roll-out) 의미  (0) 2018.06.04
[펌] consul service discovery & failure detection  (0) 2018.06.01
Posted by '김용환'
,


java10을 설치하고 나서 lombok 어노테이션 기반의 gradle 컴파일에 이슈가 있다. (Intellij는 이상없다)







$ gradle compileJava


> Task :compileJava FAILED

error: cannot find symbol

import com.google.api.entity.opentsdb.DownSample.DownSampleBuilder;

                                                      ^

  symbol:   class DownSampleBuilder

  location: class DownSample

1 error





@Data
@ToString
@Builder
@AllArgsConstructor
public class DownSample {

long interval;
AggregatorType aggregator;
DownSampleFill fill;

}


java 컴파일 이슈로 보인다..


https://github.com/rzwitserloot/lombok/issues/1646


Posted by '김용환'
,



마라톤(marathon)에서 80으로 들어온 특정 도메인 요청(예, plus.google.io)을 443 포트로 띄워진 앱을 실행하는 방법은 다음과 같다. 


참고로 HAPROXY_0_MODE는 tcp와 http만 된다. 따라서 다른 먼가가 필요한다. 그것이 HAPROXY_0_REDIRECT_TO_HTTPS이다. 


"labels": {
"HAPROXY_0_MODE"="http",

"HAPROXY_0_REDIRECT_TO_HTTPS"="true",

"HAPROXY_0_VHOST"="plus.google.io",

"HAPROXY_0_SSL_CERT"="/etc/ssl/marathon/268.pem"

}




실제 80 포트로 접속하면 301로 forwading된다.


$ curl -I -XGET http://...

HTTP/1.1 301 Moved Permanently

Content-length: 0

Location: https://...

Connection: close



Posted by '김용환'
,


간단 코드 예시


import requests


def main():
print('Hello, world!')
response = requests.get('https://httpbin.org/ip')
print(response.status_code)
print(response.headers)
print('Your IP is {0}'.format(response.json()['origin']))

if __name__ == '__main__':
main()



결과는 다음과 같다. 


Hello, world!

200

{'Connection': 'keep-alive', 'Server': 'gunicorn/19.8.1', 'Date': 'Mon, 04 Jun 2018 02:28:09 GMT', 'Content-Type': 'application/json', 'Content-Length': '26', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true', 'Via': '1.1 vegur'}

Your IP is 1.1.1.1





HTTPAdapter를 이용하는 코드이다.


from requests import Session
from requests.adapters import HTTPAdapter


def main():
print('Hello, world!')

session = Session()
session.mount("http://", HTTPAdapter(max_retries=3))
response = session.get('https://httpbin.org/ip', timeout=0)

print(response.status_code)
print(response.headers)
print('Your IP is {0}'.format(response.json()['origin']))

if __name__ == '__main__':
main()




결과는 동일하다.




from requests import Session
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry


def main():
print('Hello, world!')
retries_number = 3
backoff_factor = 0.3
status_forcelist = (500, 400)

retry = Retry(
total=retries_number,
read=retries_number,
connect=retries_number,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
)
session = Session()
session.mount("http://", HTTPAdapter(max_retries=retry))
response = session.get('https://httpbin.org/ip', timeout=0)

print(response.status_code)
print(response.headers)
print('Your IP is {0}'.format(response.json()['origin']))

if __name__ == '__main__':
main()



아래 공식에 따르면, 다음과 같다.  총 소요되는 시간은 1.8인데... 


{backoff factor} * (2 ^ ({number of total retries} - 1))



0.3 * ( 2 ^ ( 1 - 1)) = 0

0.3 * ( 2 ^ ( 2 - 1)) = 0.6

0.3 * ( 2 ^ ( 3 - 1)) = 1.2



1.8 = 0 + 0.6 + 1.2






https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html#module-urllib3.util.retry


backoff_factor (float) –

A backoff factor to apply between attempts after the second try (most errors are resolved immediately by a second try without a delay). urllib3 will sleep for:

{backoff factor} * (2 ^ ({number of total retries} - 1))

seconds. If the backoff_factor is 0.1, then sleep() will sleep for [0.0s, 0.2s, 0.4s, …] between retries. It will never be longer than Retry.BACKOFF_MAX.

By default, backoff is disabled (set to 0).






만약 타임아웃이 생기면, 중간에 쉬는 타임이 생긴다. retries와 timeout을 잘 사용하면 괜찮을 것 같다. 


response = session.get('https://httpbin.org/ip', timeout=5)

Posted by '김용환'
,



https://www.differencebetween.com/difference-between-rollout-and-vs-deploy/



롤아웃(roll-out)이란 간단히 번역하면 신제품 또는 정책 "출시" 또는 "릴리즈"라 할 수 있다. 



Posted by '김용환'
,

consul에 대한 좋은 자료


https://blog.eleven-labs.com/en/consul-service-discovery-failure-detection/



Posted by '김용환'
,



특정 mesos 에만 동작하도록 하는 방법이다.



$ sudo vi /etc/default/mesos-slave

export MESOS_ATTRIBUTES="ip:11.11.11.11;os:Ubuntu;os-version:16;server-type:user;service:google-api"


$ sudo vi /etc/default/mesos-slave

$ sudo service mesos-slave stop


MESOS_ATTRIBUTES를 수정할 때마다 MESOS_WORK_DIR의 모든 메타 파일을 삭제해야 합니다

$ sudo rm -rf $MESOS_WORK_DIR/*


$ sudo service mesos-slave start



확인하는 방법.



curl 'http://슬레이브_ip:5051/slave(1)/state' | jq


...


  "attributes": {

    "ip": "10.61.106.159",

    "os": "Ubuntu",

    "os-version": 16,

    "server-type": "user",

    "service": "google-api"

  },

  "master_hostname": "11.11.11.11",

  "log_dir": "/var/log/mesos",

  "flags": {

    "appc_simple_discovery_uri_prefix": "http://",

    "appc_store_dir": "/tmp/mesos/store/appc",

    "attributes": "ip:11.11.11.11;os:Ubuntu;os-version:16;server-type:user;service:google-api",


...





마라톤 앱에 다음 constraints를 추가한다.



"constraints": [

    [

      "service",

      "CLUSTER",

      "google-api"

    ]

  ],






kubernetes로 가면 저 labeling을 환경 설정(env)로 쉽게 진행할 수 있다. mesos 약간 불편한 점이 있다.


Posted by '김용환'
,



ubunt 16.04에서 docker 재설치 가이드이다. 



기존 환경은 docker 1.11인데, 최신인 docker ce(18.03.1-ce)을 설치한다. 

https://docs.docker.com/install/linux/docker-ce/ubuntu/#set-up-the-repository


root 사용자로 진행한다.



$ apt-get remove docker docker-engine docker.io


$ rm /etc/systemd/system/docker.service.d/docker.conf


$ setproxy apt-get update


$ apt-get install \

    apt-transport-https \

    ca-certificates \

    curl \

    software-properties-common



PROXY를 셋팅한다.

$ export ..



$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

OK



$ apt-key fingerprint 0EBFCD88

pub   4096R/0EBFCD88 2017-02-22

      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88

uid                  Docker Release (CE deb) <docker@docker.com>

sub   4096R/F273FCD8 2017-02-22




$ sudo add-apt-repository \

   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \

   $(lsb_release -cs) \

   stable"


$ apt-get update

   

$ apt-get install docker-ce


$ docker version

Client:

 Version:      18.03.1-ce

 API version:  1.37

 Go version:   go1.9.5

 Git commit:   9ee9f40

 Built:        Thu Apr 26 07:17:20 2018

 OS/Arch:      linux/amd64

 Experimental: false

 Orchestrator: swarm


Server:

 Engine:

  Version:      18.03.1-ce

  API version:  1.37 (minimum version 1.12)

  Go version:   go1.9.5

  Git commit:   9ee9f40

  Built:        Thu Apr 26 07:15:30 2018

  OS/Arch:      linux/amd64

  Experimental: false



$ service docker start 








만약 proxy 이슈라면 다음을 실행한다. 




$ setproxy apt-get update


$ setproxy apt-get install docker-ce


$ service docker start 



$ docker version

Client:

 Version:      18.03.1-ce

 API version:  1.37

 Go version:   go1.9.5

 Git commit:   9ee9f40

 Built:        Thu Apr 26 07:17:20 2018

 OS/Arch:      linux/amd64

 Experimental: false

 Orchestrator: swarm


Server:

 Engine:

  Version:      18.03.1-ce

  API version:  1.37 (minimum version 1.12)

  Go version:   go1.9.5

  Git commit:   9ee9f40

  Built:        Thu Apr 26 07:15:30 2018

  OS/Arch:      linux/amd64

  Experimental: false

  

  

참고

https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-docker-ce-1




Posted by '김용환'
,





marathon 앱의 포트를 지정할 때. 



servicePort는 앱에서 외부로 노출될 서비스 port를 의미한다. 
servicePort: When you create a new application in Marathon (either through the REST API or the front end), you may assign one or more service ports to it. You can specify all valid port numbers as service ports or you can use 0 to indicate that Marathon should allocate free service ports to the app automatically


containerPort는 컨테이너에서 앱이 실행중인 포트를 지정한다.즉 load balancer와 앱 간에 연결되고 있는 port를 의미한다.  이 포트로 health check가 일어난다. 

containerPort: A container port specifies a port within a container. This is only necessary as part of a port mapping when using BRIDGE or USER mode networking with a Docker container.




아래 예를 보면, 외부에서 8080포트로 접근할 수 있고, LB와 마라톤 앱간에는 18080으로 통신한다라는 뜻이다. 


"container": {

    "type": "DOCKER",

    "volumes": [],

    "docker": {

      "image": "imageurl",

      "network": "BRIDGE",

      "portMappings": [

        {

          "containerPort": 8080,

          "hostPort": 0,

          "servicePort": 18080,

          "protocol": "tcp",

          "name": "8080",

          "labels": {}

        }

      ],

      "privileged": false,

      "parameters": [],

      "forcePullImage": true

    }

  },









Posted by '김용환'
,



spark-shell을 이용해  "랜덤 포레스트를 이용한 MNIST 데이터셋 분류" 예를 공부한다. 



이 섹션에서는 랜덤 포레스트를 사용한 분류 예를 소개할 것이다. 코드를 단계별로 분석 해결책을 쉽게 이해할 수 있다.


1단계. LIVSVM 포맷으로 MNIST 데이터셋을 로드하고 파싱한다.



import org.apache.spark.mllib.util.MLUtils

// LIBSVM 포맷의 트레이닝 데이터를 로드한다.

val data = MLUtils.loadLibSVMFile(spark.sparkContext, "data/mnist.bz2")




2단계. 트레이닝과 테스트 셋을 준비한다.

데이터를 트레이닝 셋(75%)과 테스트 셋(25%)으로 나누고 재현하기 위해 다음처럼 시드를 설정한다.



val splits = data.randomSplit(Array(0.75, 0.25), seed = 12345L)

val training = splits(0).cache()

val test = splits(1)




모델을 구축하기 위해 트레이닝 알고리즘을 실행한다.


빈 categoricalFeaturesInfo를 사용해 랜덤 포레스트 모델을 트레이닝시킨다. 모든 피쳐가 데이터셋에서 연속적이기 때문에 관련 작업이 필요하다.




import org.apache.spark.mllib.tree.RandomForest



val numClasses = 10 //MNIST 데이터 셋의 클래스의 개수

val categoricalFeaturesInfo = Map[Int, Int]()

val numTrees = 50 // 실제 상황에서는 더 큰 수를 사용한다. 값이 더 클수록 좋다.

val featureSubsetStrategy = "auto" // 알고리즘을 선택한다.

val impurity = "gini" // 이전에 설명한 랜덤 포레스트를 설명한 노트를 살펴보라.

val maxDepth = 30 // 실제 상황에서는 값이 더 클수록 좋다.

val maxBins = 32 // 실제 상황에서는 값이 더 클수록 좋다.

val model = RandomForest.trainClassifier(training, numClasses, categoricalFeaturesInfo, numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins)





랜덤 포레스트 모델을 트레이닝하는 것은 매우 자원이 소비되는 작업이다. 따라서 더 많은 메모리가 필요하므로 OOM이 발생되지 않도록 주의해야 한다. 



이전에 언급한 성능 메트릭을 사용해 다음처럼 모델을 평가할 수 있도록 테스트 셋의 원 점수를 계산한다.


val scoreAndLabels = test.map { point =>

 val score = model.predict(point.features)

 (score, point.label)

}




평가를 위해 다중 클래스에 대한 메트릭을 초기화한다.


import org.apache.spark.mllib.evaluation.MulticlassMetrics

val metrics = new MulticlassMetrics(scoreAndLabels)



혼동 행렬을 생성한다.



println(metrics.confusionMatrix)


1498.0  0.0     3.0     2.0     0.0     2.0     4.0     0.0     12.0    0.0

0.0     1736.0  8.0     1.0     2.0     1.0     2.0     4.0     0.0     2.0

7.0     0.0     1424.0  2.0     3.0     1.0     5.0     12.0    10.0    4.0

0.0     3.0     20.0    1507.0  0.0     19.0    2.0     13.0    19.0    9.0

3.0     0.0     5.0     0.0     1416.0  0.0     2.0     4.0     4.0     29.0

10.0    2.0     1.0     21.0    4.0     1272.0  14.0    3.0     13.0    1.0

6.0     2.0     0.0     0.0     1.0     9.0     1456.0  0.0     4.0     0.0

2.0     1.0     6.0     0.0     9.0     1.0     0.0     1578.0  8.0     18.0

2.0     6.0     7.0     9.0     5.0     10.0    5.0     2.0     1398.0  10.0

7.0     3.0     0.0     22.0    16.0    4.0     1.0     15.0    13.0    1404.0





이전 코드는 분류를 위해 다음과 같은 혼동 행렬을 출력한다.




이제 모델의 성능을 판단하기 위해 전체 통계를 계산하자.


정확도, 정밀도, 회수율, 참 긍정 비율, 거짓 긍정 비율, F1 점수와 같은 성능 메트릭을 포함하는 다음 출력을 생성한다.



val accuracy = metrics.accuracy

println("Summary Statistics")

println(s"Accuracy = $accuracy")

// 레이블 당 정확도

val labels = metrics.labels

labels.foreach { l =>

 println(s"Precision($l) = " + metrics.precision(l))

}

// 레이블 당 회수율

labels.foreach { l =>

 println(s"Recall($l) = " + metrics.recall(l))

}

// 레이블 당 거짓 긍정 비율

labels.foreach { l =>

 println(s"FPR($l) = " + metrics.falsePositiveRate(l))

}

// 레이블 당 F-측정 값

labels.foreach { l =>

 println(s"F1-Score($l) = " + metrics.fMeasure(l))





실제 실행 결과는 다음과 같다.


scala> val accuracy = metrics.accuracy

accuracy: Double = 0.967591067782096


scala> println("Summary Statistics")

Summary Statistics


scala> println(s"Accuracy = $accuracy")

Accuracy = 0.967591067782096


scala> // 레이블 당 정확도


scala> val labels = metrics.labels

labels: Array[Double] = Array(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)


scala> labels.foreach { l =>

     |  println(s"Precision($l) = " + metrics.precision(l))

     | }

Precision(0.0) = 0.9758957654723127

Precision(1.0) = 0.9903023388476897

Precision(2.0) = 0.966078697421981

Precision(3.0) = 0.9635549872122762

Precision(4.0) = 0.9725274725274725

Precision(5.0) = 0.9643669446550417

Precision(6.0) = 0.9765258215962441

Precision(7.0) = 0.967504598405886

Precision(8.0) = 0.9439567859554355

Precision(9.0) = 0.950575490859851


scala> // 레이블 당 회수율


scala> labels.foreach { l =>

     |  println(s"Recall($l) = " + metrics.recall(l))

     | }

Recall(0.0) = 0.9848783694937541

Recall(1.0) = 0.9886104783599089

Recall(2.0) = 0.9700272479564033

Recall(3.0) = 0.946608040201005

Recall(4.0) = 0.9678742310321258

Recall(5.0) = 0.9485458612975392

Recall(6.0) = 0.9851150202976996

Recall(7.0) = 0.9722735674676525

Recall(8.0) = 0.9614855570839065

Recall(9.0) = 0.9454545454545454


scala> // 레이블 당 거짓 긍정 비율


scala> labels.foreach { l =>

     |  println(s"FPR($l) = " + metrics.falsePositiveRate(l))

     | }

FPR(0.0) = 0.0027086383601756954

FPR(1.0) = 0.001266294227188082

FPR(2.0) = 0.003646175162254795

FPR(3.0) = 0.004194569136801825

FPR(4.0) = 0.0029158769499927103

FPR(5.0) = 0.0033959537572254336

FPR(6.0) = 0.0025541852149164415

FPR(7.0) = 0.003909131140286178

FPR(8.0) = 0.006046477744590952

FPR(9.0) = 0.005330023364485981


scala> // 레이블 당 F-측정 값


scala> labels.foreach { l =>

     |  println(s"F1-Score($l) = " + metrics.fMeasure(l))

     | }

F1-Score(0.0) = 0.9803664921465969

F1-Score(1.0) = 0.9894556853804503

F1-Score(2.0) = 0.9680489462950375

F1-Score(3.0) = 0.9550063371356146

F1-Score(4.0) = 0.9701952723535457

F1-Score(5.0) = 0.956390977443609

F1-Score(6.0) = 0.9808016167059616

F1-Score(7.0) = 0.9698832206515058

F1-Score(8.0) = 0.952640545144804

F1-Score(9.0) = 0.9480081026333558


scala>






이제 전체 통계를 다음처럼 계산하자.


println(s"Weighted precision: ${metrics.weightedPrecision}")

println(s"Weighted recall: ${metrics.weightedRecall}")

println(s"Weighted F1 score: ${metrics.weightedFMeasure}")

println(s"Weighted false positive rate: ${metrics.weightedFalsePositiveRate}")

val testErr = scoreAndLabels.filter(r => r._1 != r._2).count.toDouble / test.count()

println("Accuracy = " + (1-testErr) * 100 + " %")




이전 코드는 가중치 정밀도, 회수율, F1 점수, 거짓 긍정 비율을 포함하는 다음 출력을 출력한다.


Overall statistics

----------------------------

Weighted precision: 0.9676041167963592

Weighted recall: 0.9675910677820959

Weighted F1 score: 0.9675700010426889

Weighted false positive rate: 0.03240893221790396

Accuracy = 96.7591067782096 %




전체 통계에 따르면 모형의 정확도는 96%이상 로지스틱 회귀 분석보다 우수하다. 


그러나 모델을 잘 튜닝하면 더욱 개선될 수 있다.


Posted by '김용환'
,