jenkins 배치 설치를 쿠버네티스로 진행하려 했는데, 


기존처럼  master, slave 구조로 사용할 수 없다. 


https://cloud.google.com/solutions/jenkins-on-kubernetes-engine



The Kubernetes plugin enables using Kubernetes service accounts for authentication, and creating labeled executor configurations with different base images. The plugin creates a pod when an executor is required and destroys the pod when a job ends.




job 단위로 pod를 구성하기 때문에 job을 dockernize를 해야 한다.

job이 실행할 때 pod로 생성한다. 그리고 job이 종료될 때는 해당 pod를 종료한다. 


 따라서 과거처럼 slave에 바이너를 설치하면 사용하는 경우(hdfs 설정, kafka를 사용하는 경우라면) 이 방법을 쓰지 못한다.


job 단위의 dockernize 형태를 구성해야 형태로 구성하는 것이 쿠버네티스 위에서 젠킨스 job을 실행할 수 있다.





아래 문서를 보면..다음과 같이 되어 있다. 



https://www.blazemeter.com/blog/how-to-setup-scalable-jenkins-on-top-of-a-kubernetes-cluster



After both builds are completed, you should see that both build executors have been removed and are not available inside the cluster anymore:



실제 해보니.. 정말 그렇게 동작한다. 

다음을 참조한다.


http://knight76.tistory.com/entry/kubernetes-kubernetesjenkins-%EC%97%B0%EB%8F%99-%EB%A0%88%EC%8B%9C%ED%94%BC




Posted by 김용환 '김용환'

[python] whois 모듈

python 2018.09.03 14:24


python의 whois 모듈을 사용하면 whois 웹 검색과 동일한 결과를 얻을 수 있다..




$ pip install python-whois



$ python

Python 3.7.0 (default, Sep  3 2018, 12:00:39)

[Clang 7.3.0 (clang-703.0.31)] on darwin

Type "help", "copyright", "credits" or "license" for more information.




>>> import whois

>>> print(whois.whois('appspot.com'))


{

  "domain_name": [

    "APPSPOT.COM",

    "appspot.com"

  ],

  "registrar": "MarkMonitor, Inc.",

  "whois_server": "whois.markmonitor.com",

  "referral_url": null,

  "updated_date": [

    "2018-02-06 10:30:28",

    "2018-02-06 02:30:29-08:00"

  ],

  "creation_date": [

    "2005-03-10 02:27:55",

    "2005-03-09 18:27:55-08:00"

  ],

  "expiration_date": [

    "2019-03-10 01:27:55",

    "2019-03-09 00:00:00-08:00"

  ],

  "name_servers": [

    "NS1.GOOGLE.COM",

    "NS2.GOOGLE.COM",

    "NS3.GOOGLE.COM",

    "NS4.GOOGLE.COM",

    "ns1.google.com",

    "ns2.google.com",

    "ns4.google.com",

    "ns3.google.com"

  ],

  "status": [

    "clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited",

    "clientTransferProhibited https://icann.org/epp#clientTransferProhibited",

    "clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited",

    "serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited",

    "serverTransferProhibited https://icann.org/epp#serverTransferProhibited",

    "serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited",

    "clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)",

    "clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)",

    "clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)",

    "serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)",

    "serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)",

    "serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)"

  ],

  "emails": [

    "abusecomplaints@markmonitor.com",

    "whoisrelay@markmonitor.com"

  ],

  "dnssec": "unsigned",

  "name": null,

  "org": "Google LLC",

  "address": null,

  "city": null,

  "state": "CA",

  "zipcode": null,

  "country": "US"

}

>>> print(whois.whois('naver.com'))

{

  "domain_name": [

    "NAVER.COM",

    "naver.com"

  ],

  "registrar": "Gabia, Inc.",

  "whois_server": "whois.gabia.com",

  "referral_url": null,

  "updated_date": [

    "2016-08-05 06:37:57",

    "2018-02-28 11:27:15"

  ],

  "creation_date": [

    "1997-09-12 04:00:00",

    "1997-09-12 00:00:00"

  ],

  "expiration_date": [

    "2023-09-11 04:00:00",

    "2023-09-11 00:00:00"

  ],

  "name_servers": [

    "NS1.NAVER.COM",

    "NS2.NAVER.COM",

    "ns1.naver.com",

    "ns2.naver.com"

  ],

  "status": [

    "clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited",

    "clientTransferProhibited https://icann.org/epp#clientTransferProhibited",

    "clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited",

    "ok https://icann.org/epp#ok"

  ],

  "emails": [

    "white.4818@navercorp.com",

    "dl_ssl@navercorp.com",

    "abuse@gabia.com"

  ],

  "dnssec": "unsigned",

  "name": "NAVER Corp.",

  "org": "NAVER Corp.",

  "address": "6 Buljung-ro, Bundang-gu, Seongnam-si, Gyeonggi-do, 463-867, Korea",

  "city": "Gyeonggi",

  "state": null,

  "zipcode": "463463",

  "country": "KR"

}

>>> print(whois.whois('abc.com'))

{

  "domain_name": [

    "ABC.COM",

    "abc.com"

  ],

  "registrar": "CSC CORPORATE DOMAINS, INC.",

  "whois_server": "whois.corporatedomains.com",

  "referral_url": null,

  "updated_date": [

    "2018-08-08 23:38:25",

    "2018-08-08 17:11:02"

  ],

  "creation_date": "1996-05-22 04:00:00",

  "expiration_date": "2019-05-23 04:00:00",

  "name_servers": [

    "ORNS01.DIG.COM",

    "ORNS02.DIG.COM",

    "SENS01.DIG.COM",

    "SENS02.DIG.COM",

    "orns02.dig.com",

    "orns01.dig.com",

    "sens02.dig.com",

    "sens01.dig.com"

  ],

  "status": [

    "clientTransferProhibited https://icann.org/epp#clientTransferProhibited",

    "serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited",

    "serverTransferProhibited https://icann.org/epp#serverTransferProhibited",

    "serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited",

    "clientTransferProhibited http://www.icann.org/epp#clientTransferProhibited",

    "serverDeleteProhibited http://www.icann.org/epp#serverDeleteProhibited",

    "serverTransferProhibited http://www.icann.org/epp#serverTransferProhibited"

  ],

  "emails": [

    "domainabuse@cscglobal.com",

    "Corp.DNS.Domains@disney.com"

  ],

  "dnssec": "unsigned",

  "name": "ABC, Inc.; Domain Administrator",

  "org": "ABC, Inc.",

  "address": "77 West 66th Street",

  "city": "New York",

  "state": "NY",

  "zipcode": "10023-6298",

  "country": "US"

}



Posted by 김용환 '김용환'



Microservices A Practical Guide를 번역 완료했다. 



https://leanpub.com/microservices-recipes



https://www.amazon.com/Microservices-Practical-Guide-Eberhard-Wolff/dp/1717075908









처음에 클라우드 개발 팀에 합류할 때 처음에 많이 당황했었다. 


그동안 사내 장비만 가지고 Monolithic한 사고 방식으로 많은 개발 방식을 가지고 있었던 지라...개발 방법론이 달랐던 것에 당황했다. 자바 주도적인 개발에서 폴리그랏 마이크로 서비스 개발 방식으로 잘 배워야겠다라는 마음 가짐이 생겼다.


이전 조직에서 ruby, python, R언어도 나름 쓰고 있었지만. 클라우드 개발 팀은 (내 생전 처음 본) 오픈 소스들을 사용하고 있었고 특히 go언어, 고급스러운 python 코딩을 하고 있었다. 많이 그 동안 내가 알던 세계를 떠나오길 잘했다 라는 생각이 들었다.


클라우드/마이크로 서비스는 점점 많이 사용될 것 같다.

그리고 마이크로 서비스 개발 / 비동기 철학에 대한 많은 이해가 보편화될 것 같다.


그런 시대적인 상황에서 이 책을 만나게 되었다.


마이크로 서비스 아키텍처와 오픈 소스, 통신 방법, DDD에 대한 설명이 잘 담겨 있어서 개발자라면 꼭 봐야할 것 같다. 예시를 step-by-step 으로 천천히 설명하지는 않지만 예시 프로젝트를 실행하고 데모를 실행할 때 오는 좋은 충격은 너무 좋았다!!



처음 이 책을 번역해 달라는 출판사의 요청을 거절했었다. 영어 품질이 조금 좋지 않았지만 우려와 달리 생각보다 굉장히 Practical했고.. 저자의 마이크로 서비스 아키텍트에 대한 의도를 느낄 수 있어서 좋았다. 


(내 경험상) 'Spring 마이크로 서비스' 책 보다는 이 책이 더 나은 것 같다는 생각이 들었다. 


아키텍트, 개발자가 쓱쓱 보기에 괜찮은 책인 것 같다.










Posted by 김용환 '김용환'



오랜 시간 동안 http를 썼지만 정식 용어를 모를 때가 많다.


HTTP는 HTTP 응답에서 Last-Modified라는 이름의 헤더를 제공한다. 


Last-Modified 헤더는 데이터가 마지막으로 변경된 시기를 의미한다. Last-Modified 헤더는 피드의 updated 필드의 기능을 대신한다.



클라이언트는 HTTP 헤더의 값을 저장한다. 

그리고 HTTP GET 요청에 클라이언트는 GET 요청과 함께 If-Modified-Since 헤더에 읽기 값을 보낸다. 데이터가 변경되었다면 서버는 HTTP 304(변경 되지 않음) 상태 코드를 응답한다. 그리고 상태 코드를 제외하고 어떠한 데이터를 전송하지 않는다.


데이터가 변경되었는지 쉽게 결정할 수 있다. 예를 들어 데이터베이스의 마지막 변경 시간을 결정하는 코드를 구현할 수 있다. 이는 모든 데이터를 Atom 표현으로 변환하는 것보다 훨씬 효율적이다.


데이터가 실제로 변경되었다면 요청에 대해 200(OK)으로 정상적으로 응답된다. 또한 클라이언트가 HTTP 캐싱을 다시 사용할 수 있도록 Last-Modified 헤더에 새로운 값이 전송된다.


지정된 If-Modified-Since 헤더를 포함한 요청을 데이터베이스에서는 마지막 변경 시간과 비교하고 결정하는 데 사용된다. 데이터가 변경되지 않으면 HTTP 304 상태 코드를 리턴한다. 이 경우 HTTP 요청에 응답하는 데 하나의 데이터베이스 쿼리만 필요하다.



이런 방식을 Conditional Get (조건부 Get)라 한다.

Posted by 김용환 '김용환'

OpenTsdb 분석

scribbling 2018.08.31 12:50





opentsdb 소스(https://github.com/OpenTSDB/opentsdb )를 살펴보니.. 큰 특징은 다음과 같다.


<내용>

1. grafana를 지원한다. (사실 이것 때문에 쓰는 것임)


2. hbase 쿼리 튜닝은 되어 있지 않다(start, end로 해서 읽어옴, 대용량 데이터에 엄청 취약)


3. gpgl 이다. (소스를 건들 수 없다)

    OpenTSDB is free software and is available under both LGPLv2.1+ and GPLv3+.


4. 성능을 위해 async hbase(Defered)를 사용했다. (그러나 성능 이슈는 앱의 쓰레드 부분이 아니라 쿼리 부분에서 발생한다)


5. 야후 개발자 한명이 혼자 다 개발한다. 따라서 확장성에서 취약하다. 아저씨 코드이다. 


6. 현재 3.0 개발중(google bigtable도 깔끔하게 지원될 것 같다)


7. 데이터가 많아지면 트래픽에 굉장히 취약하다. 대용량 환경에서는 druid가 답이다. 


7. 2.3까지 pre-aggregation, google big table/cassandra 지원은 완벽하지 않다. hbase 빼고는 답이 없다. (대용량에 취약하다.)


8. druid vs opentsdb 

https://community.hortonworks.com/questions/89810/druid-vs-opentsdb-for-tick-data.html

https://www.popit.kr/time-series-olap-druid-%EC%9E%85%EB%AC%B8/ (람다 아키텍처 지원)

https://groups.google.com/forum/#!topic/druid-development/bPJbWO-g4aw

https://java.libhunt.com/compare-opentsdb-vs-druid







<세부 내용>

1. 원래 opentsdb의 의도는 한 대의 host, network를 모니터링을 위해 만들어졌다. kafka* 이런식의 모아보기 용은 아니다.


2. 키 구성

row key : metric_uid + timestamp + tagk1 + tagk2 + <tagkN> + <tagvN>

timestamp 

tagk1=tagv1

tagk2=tagv2



3. 키 검색 순서

time range -> metric -> tags

따라서 동일한 그룹핑 단어가 많아지면 성능은 좋지 않다.(data. agg. 으로 시작하는 단어)



4. opentsdb 2.0부터 4개의 테이블이 생성된다.

data

uid

tree

metadata 테이블 : name <--> id 매핑


5. tagk 길이 제한

rowkey에 저장해야 하기에 5개 태그 미만으로 저장해야 한다.



6. opentsdb가 api에 예민(host 정보 없으면 에러 출력), 성능이 약해서 opentsdb 앞단에 opentsdb compatible api g/w를 사용해야 잘 감싸는 서버가 필요하다. 따라서 custom, 모아보기 기능도 쉽게 진행할 수 있다. 




<opentsb를 활용한 프로젝트>


https://github.com/turn/splicer : 여러 opentsdb 클러스터와 redis를 두어 모니터링(더이상 운영 안됨)


https://github.com/facebookarchive/beringei : facebook에서는 인메모리 TSDB용으로 개발(더이상 운영 안됨)





<참고>

Opentsdb 좋은 공부 자료

http://tsunanet.net/~tsuna/hbasecon-2014-opentsdb-2.0.pdf

http://opentsdb.net/misc/opentsdb-oscon.pdf

http://Marp의 opentsdb 튜닝가이드 https://mapr.com/blog/hbase-key-design-opentsdb/

http://opentsdb.net/docs/build/html/user_guide/backends/bigtable.html







다음은 opentsdb를 분석하며 대충 그려놓은 그림이다. 자료가 없어서 이를 참조하면 좋을 것 같다. 













Posted by 김용환 '김용환'




카프라 토픽에 저장된 데이터가 너무 많아지면 lag의 값은 점점 커진다.


lag이 커지면 처리되야 할 양은 많아지고, 결국 카프카가 저장하는 토픽 저장소의 한계도 넘어서게 된다.


따라서, 이럴 때 간단히 해결할 수 있는 방법은 파티션을 늘리는 것이다. 


topic에 할당한 파티션(partion)의 개수가 5이면 10개로 늘리면, 조금씩 lag이 줄어든다..

10개이면 20개로 늘리면 조금씩 lag이 줄어든다.



consumer group의 병렬 처리 정도는 consume하는 파티션의 수에 의해 제한된다. 따라서 일반적으로 파티션이 많아지면 당연히 처리량, throghput이 높아진다.




그렇다면 파티션을 늘리는 것이 답일까??

먼가 kafka에 영향을 주지 않을까 고민하던데 차에. 아래 문서를 보게 되었다.





http://www.confluent.io/blog/how-to-choose-the-number-of-topicspartitions-in-a-kafka-cluster/




요점 - 발 번역



1. 파티션 개수가 많아지면 오픈 파일 핸들(open file handle)을 더 필요로 한다.


각 파티션은 브로커의 파일 시스템의 디렉토리에 매핑된다. 해당 디렉토리에는 로그 세그먼트 당 두 개의 파일 (인덱스 용, 실제 데이터 용)이 존재한다. 각 브로커는 모든 로그 세그먼트의 인덱스와 데이터 파일 모두에 대한 파일 핸들을 연다. 따라서 파티션이 많을수록 기본 운영 체제에서 열린 파일 핸들 제한을 구성한다. 따라서 카프카 서버 운영자 입장에서는 open file handle(fd) 개수 더 커지지 않도록 운영할 필요가 있다.



2. 파티션 개수가 많아지면 비-가용성이 증가된다.


장애 상황에 이슈가 발생할 수 있다. 브로커가 불명확하게 종료될 비-가용성은 파티션 수에 비례한다. 브로커에 총 2000개의 파티션이 있고 각 파티션에 2 개의 복제본이 있다고 가정하면 해당 브로커는 약 1000개의 파티션을 위한 리더가 될 것이다.  해당 브로커가 불완전히 실패하면 1000개의 파티션을 모두 동시에 사용할 수 없게 된다. 단일 파티션에 대한 새로운 리더를 선출하는 데 5ms가 걸린다고 가정하면 1000개의 모든 파티션에 대한 새로운 리더를 뽑는는 데 최대 5 초가 소요된다. 일부 파티션의 경우 비-가용성은 5초+실패 감지 시간이 될 것이다. 


불행한 경우, 실패한 브로커가 컨트롤러라면 심각하다. 페일 오버는 자동으로 발생하지만 새 컨트롤러는 초기화하는 동안 ZooKeeper에서 모든 파티션의 일부 메타 데이터를 읽어야 한다. 예를 들어 카프카 클러스터에 10,000 개의 파티이 있고 ZooKeeper에서 메타 데이터를 초기화하는 데 파티션 당 2ms가 걸리면 사용 불가능한 창에 20초가 더 걸릴 수 있다.


따라서 최악의 경우를 피하려면 브로커 당 파티션 수를 2개에서 4000개 밑으로 제한하고 클러스터의 총 파티션 수를 수만 개로 제한하는 것이 좋다.



3. 파티션 개수가 많아지면 end-to-end 대기 시간이 증가된다.


카프카의 end-to-end 대기 시간은 producer가 메시지를 퍼블리싱하고 consumer가 메시지를 읽을 때까지의 시간으로 정의된다. 메시지 커밋 시간은 end-to-end 대기 시간의 상당 부분을 차지한다. default로 카프카 브로커는 두 개의 브로커 간에 복제본을 공유하는 모든 파티션에 대해 단일 스레드를 사용하여 다른 브로커의 데이터를 복제한다. 한 브로커에서 다른 브로커로 1000개의 파티션을 복제하면 약 20ms의 대기 시간이 추가될 수 있다. 즉 end-to-end 대기 시간이 최소 20ms임을 의미한다. 


end-to-end 대기 시간을 줄이려면 브로커 당 파티션 수를 100 x 카프카 클러스터의 브로커 수 x replica factor로 제한하는 것이 좋다. 



4. 파티션 개수가 많아지면 producer/consumer 클라이언트에서 많은 메모리를 필요로 한다.

0.8.2 릴리스에서 producoer는 파티션 당 메시지를 버퍼한다. 충분한 데이터가 축적되거나 충분한 시간이 경과하면 축적 된 메시지가 버퍼에서 제거되고 브로커로 전송된다.


파티션 수를 늘리면 producer의 더 많은 파티션에 메시지가 저장하게 된다. 사용된 메모리 총량이 설정된 메모리 제한을 초과할 수 있다. 이 경우 producer는 새로운 메시지를 차단하거나 삭제해야 하며 어느 쪽도 이상적이지 않다. 


따라서 producer가 생성하는 파티션 당 최소 수십 KB를 할당하도록 하고 파티션 수가 크게 늘어날 경우 총 메모리 양을 조정해야 한다.


비슷한 문제가 consumer에도 존재한다. consumer는 파티션 당 메시지 덩어리를 가져온다. 파티션이 클수록 더 많은 메모리가 필요하다.

Posted by 김용환 '김용환'


docker build 할 때 이미지를 한 번 다운로드하면 캐싱하는 것이 정책이다.


따라서 no-cache 옵션을 변경해서 매번 docker build할 때 마다 이미지를 생성하도록 한다.


--no-cache=true


https://docs.docker.com/engine/reference/commandline/build/






Posted by 김용환 '김용환'


self-contained system을 한글로 번역할 때 소프트웨어 공학 개념에서 사용되는 ‘여러 개의 작은 시스템’이 가장 의도에 맞는 듯 하다.

Posted by 김용환 '김용환'



도커 환경을 정리하는 커맨드가 여럿 있다.




· docker container prune은 중지된 모든 컨테이너를 삭제한다.


· docker image prune은 이름 없는 모든 이미지를 삭제한다.


· docker network prune은 사용되지 않는 도커 네트워크를 모두 삭제한다.


· docker volume prune은 도커 컨테이너에서 사용하지 않는 모든 도커 볼륨을 삭제한다.


· docker system prune -a는 중지된 모든 컨테이너, 사용되지 않은 모든 네트워크, 하나 이상의 컨테이너에서 사용되지 않는 모든 이미지를 삭제한다. 따라서 남아있는 컨테이너 또는 이미지는 현재 실행 중인 컨테이너에서 필요하다.



Posted by 김용환 '김용환'



1조원을 모은 할아버지의 투자 비결은


시가 총액 1등을 샀다고 한다



http://www.thebell.co.kr/free/content/ArticleView.asp?key=201711100100018140001123&lcode=00




예전에 사실 항상 들었던 내용이 섹션(산업 분야)에서 1등 주만 사라는 얘기를 들었다.


많은 주식으로 성공하신 대가들의 내용들도 있었던 것 같다. 트렌드보다. 


오직 산업의 1등 주만 봐도 좋을 것 같다는 생각이 든다.

Posted by 김용환 '김용환'