적당한 이미지를 deploy한다.



$ kubectl create deployment hog --image vish/stress

deployment.apps/hog created





$ kubectl get deployments

NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

hog       1         1         1            1           7m





$ kubectl describe deployment hog

Name:                   hog

Namespace:              default

CreationTimestamp:      Mon, 26 Nov 2018 20:32:09 +0900

Labels:                 app=hog

Annotations:            deployment.kubernetes.io/revision=2

Selector:               app=hog

Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable

StrategyType:           RollingUpdate

MinReadySeconds:        0

RollingUpdateStrategy:  25% max unavailable, 25% max surge

Pod Template:

  Labels:  app=hog

  Containers:

   stress:

    Image:      vish/stress

    Port:       <none>

    Host Port:  <none>

    Environment:  <none>

    Mounts:       <none>

  Volumes:        <none>

Conditions:

  Type           Status  Reason

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

  Available      True    MinimumReplicasAvailable

  Progressing    True    NewReplicaSetAvailable

OldReplicaSets:  <none>

NewReplicaSet:   hog-58d797c5d8 (1/1 replicas created)

Events:

  Type    Reason             Age   From                   Message

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

  Normal  ScalingReplicaSet  8m    deployment-controller  Scaled up replica set hog-5cc4fdb68 to 1

  Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set hog-58d797c5d8 to 1

  Normal  ScalingReplicaSet  2m    deployment-controller  Scaled down replica set hog-5cc4fdb68 to 0




이를 yaml로 변경할 수 있다. 배포형태로 변경 가능하다.



$ kubectl get deployment hog -o yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

  annotations:

    deployment.kubernetes.io/revision: "2"

  creationTimestamp: 2018-11-26T10:32:09Z

  generation: 2

  labels:

    app: hog

  name: hog

  namespace: default

  resourceVersion: "6576"

  selfLink: /apis/extensions/v3/namespaces/default/deployments/hog

  uid: 873e80d8-f166-11e8-9a46-fa163e964181

spec:

  progressDeadlineSeconds: 600

  replicas: 1

  revisionHistoryLimit: 10

  selector:

    matchLabels:

      app: hog

  strategy:

    rollingUpdate:

      maxSurge: 25%

      maxUnavailable: 25%

    type: RollingUpdate

  template:

    metadata:

      creationTimestamp: null

      labels:

        app: hog

    spec:

      containers:

      - image: vish/stress

        imagePullPolicy: Always

        name: stress

        resources: {}

        terminationMessagePath: /dev/termination-log

        terminationMessagePolicy: File

      dnsPolicy: ClusterFirst

      restartPolicy: Always

      schedulerName: default-scheduler

      securityContext: {}

      terminationGracePeriodSeconds: 30

status:

  availableReplicas: 1

  conditions:

  - lastTransitionTime: 2018-11-26T10:32:15Z

    lastUpdateTime: 2018-11-26T10:32:15Z

    message: Deployment has minimum availability.

    reason: MinimumReplicasAvailable

    status: "True"

    type: Available

  - lastTransitionTime: 2018-11-26T10:32:09Z

    lastUpdateTime: 2018-11-26T10:38:33Z

    message: ReplicaSet "hog-58d797c5d8" has successfully progressed.

    reason: NewReplicaSetAvailable

    status: "True"

    type: Progressing

  observedGeneration: 2

  readyReplicas: 1

  replicas: 1

  updatedReplicas: 1



설정 파일을 다운받고 deployment를 다시 진행한다.



$ kubectl get deployment hog --export -o yaml > hog.yaml


$ vi hog.yaml


resources: {} 부분을 다음과 같이 변경한다


resources: 

  limits:

    memory: "4Gi"

  requests:

    memory: "2500Mi"





이제 변경된 내용으로 배포한다.



$ kubectl replace -f hog.yaml



반영된 부분을 확인할 수 있다. 


$ kubectl get deployment hog -o yaml


  resources:

          limits:

            memory: 4Gi

          requests:

            memory: 2500Mi



pod는 잘 동작하고 있다.



$ kubectl get po

NAME                   READY     STATUS    RESTARTS   AGE

hog-58d797c5d8-r5lvd   1/1       Running   0          9m

$ kubectl logs hog-58d797c5d8-r5lvd

I1126 10:38:33.299667       1 main.go:26] Allocating "0" memory, in "4Ki" chunks, with a 1ms sleep between allocations

I1126 10:38:33.299827       1 main.go:29] Allocated "0" memory




자원 스트레스를 주기 위해 resources 설정을 변경한다.



        resources:

          limits:

            cpu: "1"

            memory: "4Gi"

          requests:

            cpu: "0.5"

            memory: "500Mi"

        args:

        - -cpus

        - "2"

        - -mem-total

        - "950Mi"

        - -mem-alloc-size

        - "100Mi"

        - -mem-alloc-sleep

        - "1s"



그리고 deployment을 삭제하고 생성한다.


$ kubectl delete deployment hog

deployment.extensions "hog" deleted


$ kubectl apply -f hog.yaml

deployment.extensions/hog created


$ kubectl get pod

NAME                  READY     STATUS    RESTARTS   AGE

hog-786d4f7b8-mccsn   1/1       Running   0          44s


$ kubectl logs hog-786d4f7b8-mccsn

I1126 10:51:52.311973       1 main.go:26] Allocating "950Mi" memory, in "100Mi" chunks, with a 1s sleep between allocations

I1126 10:51:52.312143       1 main.go:39] Spawning a thread to consume CPU

I1126 10:51:52.312179       1 main.go:39] Spawning a thread to consume CPU

I1126 10:52:05.368902       1 main.go:29] Allocated "950Mi" memory






출처 : linux foundation 공부 자료에서 

Posted by '김용환'
,



LB를 잘 모르는 개발자가 있어서 링크를 모았다.


개발자가 LB 장비를 이해하면 개발 아키텍처에 도움이 될 뿐 아니라

시스템 엔지니어/네트워크 엔지니어가 무엇을 하는지 많은 도움이 된다.




L4 스위치의 inline, DSR 비교


http://travelc.tistory.com/82





DSR과 헬쓰체크

(카카오 그림이지만 네이버와 거의 비슷하다고 볼 수 있다)

http://tech.kakao.com/2014/05/28/l3dsr/






GSLB

https://www.netmanias.com/ko/post/blog/5620/dns-data-center-gslb-network-protocol/global-server-load-balancing-for-enterprise-part-1-concept-workflow


트래픽이 많은 큰회사가 많이 사용한다.


(예, 네이버)



$ nslookup www.naver.com

Server: 10.20.30.60

Address: 10.20.30.60#53


Non-authoritative answer:

www.naver.com canonical name = www.naver.com.nheos.com.

Name: www.naver.com.nheos.com

Address: 210.89.164.90

Name: www.naver.com.nheos.com

Address: 125.209.222.141



Posted by '김용환'
,


쿠버네티스 네트워킹(kubernetes networking)에 대한 쉬운 이해와 문서를 모아둠



이 문서를 가장 먼저 보길 바란다.


https://www.level-up.one/kubernetes-networking-pods-levelup/


https://www.level-up.one/kubernetes-networking-series-two/


https://www.level-up.one/kubernetes-networking-3-level-up/





그 다음에는 아래 문서를 보고.


https://speakerdeck.com/thockin/illustrated-guide-to-kubernetes-networking



이 문서를 보면 더욱 도움 될 것이다.



https://medium.com/@ApsOps/an-illustrated-guide-to-kubernetes-networking-part-1-d1ede3322727


https://medium.com/@ApsOps/an-illustrated-guide-to-kubernetes-networking-part-2-13fdc6c4e24c










쿠버네티스 정식 문서


https://kubernetes.io/docs/concepts/cluster-administration/networking/






참고로 오픈 스택 네트워킹(provider network, vlan, tunneling, north/south&east/west)애 대한 개념이 생길 것이다.


https://keithtenzer.com/2016/07/18/openstack-networking-101-for-non-network-engineers/

Posted by '김용환'
,



atom 에디터의 장점은 mac command line에서 command 로 atom을 사용할 수 있다는 점인데.


vscode는 이게 없다. 



그러나,


.bash_rc 또는 .bash_profile에


 alias code='open $@ -a "Visual Studio Code"' 를 추가하면 비슷한 기능이 된다.



Posted by '김용환'
,


특정 서비스의 systemd를 설정했더라도 부팅 후에 자동으로 데몬을 띄우지 않기 때문에 

부팅 이후에도 잘 동작시키려면 enable/disable을 사용해야 한다.


확인하는 작업은 is-enabled를 사용한다.


$ sudo systemctl is-enabled kafka

disabled



부팅 후에 자동으로 데몬을 띄우게 하려면 enable를 사용한다.

$ sudo systemctl enable kafka

Created symlink from /etc/systemd/system/multi-user.target.wants/kafka.service to /usr/lib/systemd/system/kafka.service.



확인한다.


$ sudo systemctl is-enabled kafka

enabled


부팅 후에 자동으로 데몬을 안 띄우게 하려면 disable를 사용한다.


$ sudo systemctl disable kafka

Removed symlink /etc/systemd/system/multi-user.target.wants/kafka.service.





systemctl --failed를 사용해 부팅안된 서비스를 확인할 수 있다. 

만약 kafka.service가 실행 중에 문제가 생겼다면 다음과 같이 보일 것이다.


$ systemctl --failed

  UNIT            LOAD   ACTIVE SUB    DESCRIPTION

● kafka.service loaded failed failed Postfix Mail Transport Agent


LOAD   = Reflects whether the unit definition was properly loaded.

ACTIVE = The high-level unit activation state, i.e. generalization of SUB.

SUB    = The low-level unit activation state, values depend on unit type.


1 loaded units listed. Pass --all to see loaded but inactive units, too.

To show all installed unit files use 'systemctl list-unit-files'.


Posted by '김용환'
,




"아름다운 것들은 관심을 바라지 않지" 


"Beautiful things don't ask for attention" 



- 월터의 상상은 현실이 되다.



'영화를 보고' 카테고리의 다른 글

어벤져스 엔드게임 중에 좋았던 대사  (0) 2019.08.20
[더스토리] 영화의 기억나는 대사  (0) 2018.06.26
레디 플레이어 원  (0) 2018.04.04
칠드런 오브 맨  (0) 2016.10.25
[마이크롭 앤 가솔린]  (0) 2016.07.02
Posted by '김용환'
,




백프레셔(Backpressure) – 스트리밍 작업을 처리하다가 폭발적인 데이터(예, 사건/사고, 이벤트)가 발생하면 처리 시스템은 폭발적인 데이터를 우아하게 처리할 수 있어야 한다. 


처리 시간이 배치 간격보다 커지면 다음 배치 잡에서는 지연이 생기고 불안정해 진다. 따라서 불안정 상태가 지속되면 백프레셔에 의해 입력율(input rate)를 줄여 처리량과 처리 시간을 줄인다. 따라서 지연이 0이 될 것이다. 


폭발적인 데이터가 갑자기 카프카에 저장되어 스파크 스트리밍의 카프카 컨슈머에 리턴하는 배치 크기를 제한하고 싶을 수 있다. 이럴 때 스파크 스트리밍 백프레셔를 적용할 수 있다. (이는 대부분의 스트리밍 처리 플랫폼(예, storm, flink)에서 제공된다.)



spark.streaming.backpressure.enabled와 spark.streaming.backpressure.initialRate를 사용하면 된다. 

spark.streaming.backpressure.initialRate 기본값은 not set이고,  spark.streaming.backpressure.enabled 기본값은  disabled이다. 



https://spark.apache.org/docs/latest/configuration.html#spark-streaming 설정에 잘 설명되어 있다.


스파크 스트리밍은 spark.streaming.backpressure.enabled를 통해 현재 배치 스케줄링 지연과 처리 시간을 기준으로 수신 속도를 제어할 수 있기 때문에 시스템이 최대한 빠르게 처리할 수 있다. 내부적으로는 수신자의 최대 수신 속도가 동적으로 설정된다. 이 속도는 spark.streaming.receiver.maxRate와 spark.streaming.kafka.maxRatePerPartition 상한 값으로 설정된다.


첫 번째 배치를 제어하거나 좀 더 구체적으로 첫 번째 배치의 메시지 수를 설정하고 싶다면, spark.streaming.backpressure.initialRate을 사용할 수 있다. spark.streaming.backpressure.initialRate은 백프레셔 메커니즘이 활성화(spark.streaming.backpressure.enabled=true)되었을 때 각 리시버가 첫번째 배치에 대한 데이터를 수신하는 최대 수신 속도이다. 


spark.streaming.kafka.maxRatePerPartition의 기본값은 not set인고, 카프카 direct stream API를 사용할 때 각 카프카 파티션에서 데이터를 읽을 최대 속도(초당 레코드 수)으로 설정한다.



예)

spark.streaming.kafka.maxRatePerPartition = "100"

spark.streaming.backpressure.enabled = "true"






Posted by '김용환'
,

[펌] kafka burrow api

kafka 2018. 11. 20. 14:46


카프카 api stat, metric 지표를 보기 위한 burrow(https://github.com/linkedin/Burrow/wiki/HTTP-Endpoint)라는 오픈 소스 툴이 있다.



kafka에서 사용하고 싶은 http endpoint를 보고 호출한다.


https://github.com/linkedin/Burrow/wiki/HTTP-Endpoint#request-endpoints


예)

http://burrow.google.io:8000/v3/kafka/mycluster/topic/logs

{"error":false,"message":"topic offsets returned","offsets":[152223734586,152224559773,152224774276,152224723888,152224644847,152224641838,152224508250,152224383547,152215033727,152215053018,152214434045,152214530227,152215253175,152214990582,152215431432,152213601661,152215273301,152215092394,152214795862,152215123946,152215194674,152215391037,152214877453,152215137734,152215680569,152215360097,152214928462,152215484025,152214933673,152214661665,152214049830,152215021533,152215748420,152214945335,152215126831,152215051384,152214863230,152214966710,152215634739,152214820473,152215165668,152215071434,152214866458,152214865355,152214934334,152214662023,152214830751,152214573022,152215197587,152214836785],"request":{"url":"/v3/kafka/mycluster/topic/logs","host":"burrow-google"}}







Posted by '김용환'
,


apache phoenix 에서 timestamp 타입을 utc로 저장했다.



이를 현재 시간(gmt +9)로 보고 싶다면 아래와 같은 쿼리를 실행한다.




select CONVERT_TZ(timestamp, 'UTC', 'Asia/Seoul')




기타 phoenix 함수는 아래를 참조한다.


https://phoenix.apache.org/language/functions.html


Posted by '김용환'
,



코드를 커밋하기 전에 coding convention에 맞지 않으면 에러를 내고 commit을 안되게 하는 pre-commit이란 툴이 있다. 


pre-commit이 지원하는 언어/툴은 다음과 같다. 자바, 스칼라는 없지만, go, python, ruby, rust, swift 등이 있다.




docker

docker_image

fail

golang

node

python

python_venv

ruby

rust

swift

pcre

pygrep

script

system






아래와 같이 설치를 진행한다.




$ pip install pre-commit


$ cd kemi_api_디렉토리


$ pre-commit


$ vi .pre-commit-config.yaml

repos:

-   repo: https://github.com/pre-commit/pre-commit-hooks

    rev: v2.0.0

    hooks:

    -   id: trailing-whitespace

    -   id: end-of-file-fixer

    -   id: fix-encoding-pragma

    -   id: double-quote-string-fixer

    -   id: requirements-txt-fixer


$ pre-commit install




/common/exceptions.py 파일에 '# -*- coding: utf-8 -*-' 이 포함되어 있는데. 

일부러 그 주석만 삭제하고 다음 커맨드를 실행한다.




$ git add .

$ git commit -m 'test'

gTrim Trailing Whitespace.................................................Passed

Fix End of Files.........................................................Passed

Fix python encoding pragma...............................................Failed

hookid: fix-encoding-pragma


Files were modified by this hook. Additional output:


Added `# -*- coding: utf-8 -*-` to /common/exceptions.py


Fix double quoted strings................................................Passed

Fix requirements.txt.................................(no files to check)Skipped



파이썬 인코딩 부분에서 failed되고 알려준다. 

때로는 고쳐주기도 한다.




프로젝트 디렉토리의 .git/hook/pre-commit 파일이 하나 생긴다. 


[프로젝트/.git/hooks] ls -al pre-commit

-rwxr-xr-x  1 samuel.kim  staff  5257 11  9 18:19 pre-commit




이 파일로 인해서 git commit을 미리 체크한다.


hook에 대한 내용은 https://git-scm.com/book/ko/v1/Git%EB%A7%9E%EC%B6%A4-Git-%ED%9B%85를 참조한다.



#!/usr/bin/env python

"""File generated by pre-commit: https://pre-commit.com"""

from __future__ import print_function


import distutils.spawn

import os

import subprocess

import sys


# work around https://github.com/Homebrew/homebrew-core/issues/30445

os.environ.pop('__PYVENV_LAUNCHER__', None)


HERE = os.path.dirname(os.path.abspath(__file__))

Z40 = '0' * 40

ID_HASH = '138fd403232d2ddd5efb44317e38bf03'

# start templated

CONFIG = '.pre-commit-config.yaml'

HOOK_TYPE = 'pre-commit'

INSTALL_PYTHON = '/Users/samuel.kim/.pyenv/versions/3.7.0/bin/python3.7'

SKIP_ON_MISSING_CONFIG = False

# end templated



class EarlyExit(RuntimeError):

    pass



class FatalError(RuntimeError):

    pass



def _norm_exe(exe):

    """Necessary for shebang support on windows.


    roughly lifted from `identify.identify.parse_shebang`

    """

    with open(exe, 'rb') as f:

        if f.read(2) != b'#!':

            return ()

        try:

            first_line = f.readline().decode('UTF-8')

        except UnicodeDecodeError:

            return ()


        cmd = first_line.split()

        if cmd[0] == '/usr/bin/env':

            del cmd[0]

        return tuple(cmd)



def _run_legacy():

    if HOOK_TYPE == 'pre-push':

        stdin = getattr(sys.stdin, 'buffer', sys.stdin).read()

    else:

        stdin = None


    legacy_hook = os.path.join(HERE, '{}.legacy'.format(HOOK_TYPE))

    if os.access(legacy_hook, os.X_OK):

        cmd = _norm_exe(legacy_hook) + (legacy_hook,) + tuple(sys.argv[1:])

        proc = subprocess.Popen(cmd, stdin=subprocess.PIPE if stdin else None)

        proc.communicate(stdin)

        return proc.returncode, stdin

    else:

        return 0, stdin



def _validate_config():

    cmd = ('git', 'rev-parse', '--show-toplevel')

    top_level = subprocess.check_output(cmd).decode('UTF-8').strip()

    cfg = os.path.join(top_level, CONFIG)

    if os.path.isfile(cfg):

        pass

    elif SKIP_ON_MISSING_CONFIG or os.getenv('PRE_COMMIT_ALLOW_NO_CONFIG'):

        print(

            '`{}` config file not found. '

            'Skipping `pre-commit`.'.format(CONFIG),

        )

        raise EarlyExit()

    else:

        raise FatalError(

            'No {} file was found\n'

            '- To temporarily silence this, run '

            '`PRE_COMMIT_ALLOW_NO_CONFIG=1 git ...`\n'

            '- To permanently silence this, install pre-commit with the '

            '--allow-missing-config option\n'

            '- To uninstall pre-commit run '

            '`pre-commit uninstall`'.format(CONFIG),

        )



def _exe():

    with open(os.devnull, 'wb') as devnull:

        for exe in (INSTALL_PYTHON, sys.executable):

            try:

                if not subprocess.call(

                        (exe, '-c', 'import pre_commit.main'),

                        stdout=devnull, stderr=devnull,

                ):

                    return (exe, '-m', 'pre_commit.main', 'run')

            except OSError:

                pass


    if distutils.spawn.find_executable('pre-commit'):

        return ('pre-commit', 'run')


    raise FatalError(

        '`pre-commit` not found.  Did you forget to activate your virtualenv?',

    )



def _rev_exists(rev):

    return not subprocess.call(('git', 'rev-list', '--quiet', rev))



def _pre_push(stdin):

    remote = sys.argv[1]


    opts = ()

    for line in stdin.decode('UTF-8').splitlines():

        _, local_sha, _, remote_sha = line.split()

        if local_sha == Z40:

            continue

        elif remote_sha != Z40 and _rev_exists(remote_sha):

            opts = ('--origin', local_sha, '--source', remote_sha)

        else:

            # ancestors not found in remote

            ancestors = subprocess.check_output((

                'git', 'rev-list', local_sha, '--topo-order', '--reverse',

                '--not', '--remotes={}'.format(remote),

            )).decode().strip()

            if not ancestors:

                continue

            else:

                first_ancestor = ancestors.splitlines()[0]

                cmd = ('git', 'rev-list', '--max-parents=0', local_sha)

                roots = set(subprocess.check_output(cmd).decode().splitlines())

                if first_ancestor in roots:

                    # pushing the whole tree including root commit

                    opts = ('--all-files',)

                else:

                    cmd = ('git', 'rev-parse', '{}^'.format(first_ancestor))

                    source = subprocess.check_output(cmd).decode().strip()

                    opts = ('--origin', local_sha, '--source', source)


    if opts:

        return opts

    else:

        # An attempt to push an empty changeset

        raise EarlyExit()



def _opts(stdin):

    fns = {

        'commit-msg': lambda _: ('--commit-msg-filename', sys.argv[1]),

        'pre-commit': lambda _: (),

        'pre-push': _pre_push,

    }

    stage = HOOK_TYPE.replace('pre-', '')

    return ('--config', CONFIG, '--hook-stage', stage) + fns[HOOK_TYPE](stdin)



def main():

    retv, stdin = _run_legacy()

    try:

        _validate_config()

        return retv | subprocess.call(_exe() + _opts(stdin))

    except EarlyExit:

        return retv

    except FatalError as e:

        print(e.args[0])

        return 1



if __name__ == '__main__':

    exit(main())




만약 flake8라는 파이썬 코딩 컨벤션 강제 툴을 적용하려면. .pre-commit-config.yaml 파일의 hooks id를 추가한다.



$ vi .pre-commit-config.yaml

repos:

-   repo: https://github.com/pre-commit/pre-commit-hooks

    rev: v2.0.0

    hooks:

    -   id: trailing-whitespace

    -   id: end-of-file-fixer

    -   id: fix-encoding-pragma

    -   id: double-quote-string-fixer

    -   id: requirements-txt-fixer

    -   id: flake8






Posted by '김용환'
,