CKS – Certified Kubernetes Security Specialist 합격 수기

Certified Kubernetes Security Specialist (CKS) Exam | Linux Foundation

CKAD, CKA 에 이어 CKS 도 pass 했습니다. CKS는 kubernetes 환경에서 OS 부터 시작해서 POD 레벨 container 레벨 docker image 레벨까지 각각의 요소의 취약점을 파악하는 방법을 알아보고 어떤 문제가 있으며 어떻게 해결하면 좋은지 그리고 자동화된 분석도구를 사용해 보도록 가이드 합니다.

무엇을 알게되는가?

CKS 를 취득하면서 배우면 k8s 를 도입하면 각 영역마다 어떤 부분을 체크해야 하는지 어떤 부분이 보안상 취약할 수 있는지를 전체적으로 바라보고 가이드 할 수 있는 역량을 취득할 수 있습니다.

kube-bench, trivy 등으로 각node 와 container image 취약점을 자동 스케닝 하고 Dockerfile 과 kubernetes yaml 파일을 직접 (manually) 점검하는 방법도 익혀야 합니다. 또한 RBAC 을 통한 세부적인 권한 관리와 audit log 를 설정하여 이상 징후를 감지하고 찾아내는 방법도 익힐 수 있습니다.

해킹과 방어, 창과 방패의 싸움에서 해킹은 점점 강해지고 방어하는 기술도 점점 진화해야 합니다. 한순간에 익힌 보안 기술이 전부가 아니겠습니다. CKAD와 CKA 는 3년의 자격 유지가 인정되는 반면 CKS 는 2년의 자격 유지가 인정됩니다. 2년 후에는 새로운 보안 기술을 익혀야 하며, 2년이 지난 보안 기술은 자격자로 인정하기 어렵다는 뜻이겠습니다.

공부 방법과 걸린 시간 간단한 소회

CKAD, CKA 를 공부하게 되면 기본적으로 kubernetes document 를 검색하고 사용하는 방법 yaml 작성 능력과 vim 사용능력을 키우게 되므로 회사 생활과 병행하며 CKS자격을 취득하는 대는 약 한 달의 시간이 소요되었던 것 같습니다. 주말에는 아내가 공부하는 시간을 많이 배려해 주었습니다.

udemy 강의를 구독하여 1차례 수강 후 강사가 제공하는 killer.sh 에서 각 챕터별 실습을 하였고 시험 결제를 하면 총 2회의 모의고사 (같은 문제) 를 풀 수 있습니다. 처음에는 40점이 나왔으나 그냥 꾸준히 틀린 문제를 반복하여 100점에 나올 때 까지 session reset (문제를 풀고 있는 VM 을 초기화) 을 통해 각 문제에서 모른 부분은 더이상 없도록 할 때까지 반복해서 풀었습니다. 이 때 애매한 개념들이 많이 해소 되었습니다.

https://www.udemy.com/course/certified-kubernetes-security-specialist

시험에 출제된 내용과 과정, 시험팁

시험에는 gviser 설정, trivy 돌려보기, docker 및 pod 취약점 있을 만한 것 찾기, pod security policy 구성, RBAC role / clusterrole binding, OPA, CKA 에 자주 등장한 network policy, ETCD를 이용한 secret 찾기 base64 decode 하기 등

공부했던 거의 모든 부분이 출제 되었습니다. 특히 kube-apiserver 취약 설정 찾아내서 고치는 부분의 경우 manifest 의 pod이 재시작 되고 항상 잘 기동되길 바라는 마음을 가져야 했습니다.

docker 가 아닌 crictl 로 구동 되기에 watch -n 0.5 crictl ps 명령을 자주 사용 하여 kube-apiserver 가 재기동 되는 순간을 알아낼 수 있었습니다. 지금에서야 느끼는데 kube-apiserver 가 죽는데는 거의 10~15 초가 걸리고, 되살아나는대는 2~3초 밖에 안 걸린다는 점입니다. udemy 강사는 4~5 초만에 재기동 되는 것 같은데.. 시험보는 VM 서버는 사양이 좋지 않은 모양입니다. 그래서 manifest 를 수정 후, watch -n 0.5 crictl ps 를 치면서 그야말로 그냥 기다리는 시간은 좀 아깝다고 느껴졌습니다. (그래서 다음 문제를 먼저 살펴보기도 했습니다.)

시험 보는 중에 trivy 나 gviser 문제에서 해당 document 는 탭으로 하나 더 열어도 된다 라는 내용을 보고 k8s document 외에 2개의 탭을 더 열게 되었는데 감독관이 10분 후쯤에 화면을 잠구더니 그러지 말라 해서 그냥 알겠다고 하고 닫았더니 문제는 없었습니다. 그러니까…. 10분 정도는 감독관이 못 본 것 같고 실시간 감시도 아닌 것 같습니다.

만약 시험이 떨어지게 되면, 거의 비슷한 출제 내용으로 1번 공짜로 볼 수 있습니다. 그래서 자격증만을 위해서 시험을 치루는 경우라면 어서빨리 시험을 보고, 패턴을 익혀서 한주 후 쯤에 또 치루는 것도 방법일 수는 있겠습니다.

마치며

digital badges 를 모으는게 새로운 취미가 된 것 같은데 다음 자격증으로는 elastic search 사의 자격증을 따보려고 합니다. 업무상 kibana 를 많이 사용하는데 로그를 검색 하는 것이 아닌 분석하는 사람이 되고 싶습니다.

elastic 사의 자격증 3가지

elastic 사에서의 3가지 자격증 코스를 운영하고 있다.
Onsite & Online Training for Elasticsearch, Kibana, Beats, Logstash, and more | Elastic Training

Elasticsearch Engineers

클러스터를 관리하고 ES 를 이용해 새로운 검색엔진 구축합니다.

이 자격은 그들의 데이터를 indexing, searching, managing 할 수 있는 다재다능한 Elasticsearch 전문가입니다.

Data Analyst

ES에 적재된 데이터를 이용해 kibana 를 통해 시각화하며 분석합니다.

분석 전문가는 kibana 를 이용해 데이터를 가시적으로 표현하고 분석합니다. 시시각각 변하는 데이터를 분석해 강력한 데시보드를 만들며, 이것으로 머신러닝 작업을 개발할 수 있습니다.

Observability Engineers

로그와 각종 지표, APM 추적결과를 통합하고 시스템 전반의 상태를 관찰할 수 있습니다.

iframe 현대닷컴

redis data migration 레디스 마이그레이션 rump? redis-shake! 쓰자!!

데이터를 무중단으로 migration 해야 할 일이 생겼다. 알아보니 rump 가 있다.

https://github.com/stickermule/rump

새로운 redis 서버로의 데이터 이관을 위해서 기존 서버의 사용은 유지한채 새로운 서버로 데이터를 이관해야 하는 상황

인프라 팀에서는 데이터 이관을 지원해 주지 않은다고 하고 해봤자 *.aof 파일을 복제해서 다시 켜주는 수준

개발자로서 데이터 이관을 알아봐야 하는 상황이었다.

test redis 2개 기동

일단 docker 를 이용해 2개의 redis 를 기동했다. 성능을 올리기 위해서 cpu 와 memory 옵션을 사용한다.

docker run –name redis1 -p 6379:6379 –rm –cpus=4 -m=8g redis

docker run –name redis2 -p 6389:6379 –rm –cpus=4 -m=8g redis

샘플 데이터 입력

https://github.com/nzin4x/import-redis-by-python

위의 코드를 이용해 100만건의 데이터를 넣었다. 약 14분 소요.

RUMP 로 데이터 복제 (비추)

rump 로 data 를 복제한다. rump 를 다운로드 받아서 바로 실행

redis1 의 0 번 DB의 내용을 redis2 의 1번 DB로 복제 하는 코드

curl -SL https://github.com/stickermule/rump/releases/download/1.0.0/rump-1.0.0-linux-amd64 -o rump
./rump -from redis://localhost:6379/0 -to redis://localhost:6389/1 -silent

wsl 이 있어서 얼마나 다행인지 windows 는 좋은 linux 머신이다.

Rump 가 너무 느려서 좀 알아보니

알리바바 그룹의 천재들이 만든 redis-shake 를 알아냈다.

redis-shake! 가 답이다.

sync 중에 원본 redis (redis1) 에 키 입력/조회도 되고 (실시간 사용 및 복제 가능) 속도가 엄청 빠르다.

nz@nzMain:/mnt/c/Users/nzin4/release-v2.1.1-20210903$ ./redis-shake.linux -conf redis-shake.conf -type sync
2022/03/09 21:20:06 [WARN] source.auth_type[auth] != auth
2022/03/09 21:20:06 [WARN] target.auth_type[auth] != auth
2022/03/09 21:20:06 [INFO] input password is empty, skip auth address[127.0.0.1:6389] with type[auth].
2022/03/09 21:20:06 [INFO] input password is empty, skip auth address[127.0.0.1:6379] with type[auth].
2022/03/09 21:20:06 [INFO] input password is empty, skip auth address[127.0.0.1:6379] with type[auth].
2022/03/09 21:20:06 [INFO] source rdb[127.0.0.1:6379] checksum[yes]
2022/03/09 21:20:06 [WARN]
______________________________
\                             \           _         ______ |
 \                             \        /   \___-=O'/|O'/__|
  \   RedisShake, here we go !! \_______\          / | /    )
  /                             /        '/-==__ _/__|/__=-|  -GM
 /        Alibaba Cloud        /         *             \ | |
/                             /                        (o)
------------------------------
if you have any problem, please visit https://github.com/alibaba/RedisShake/wiki/FAQ

2022/03/09 21:20:06 [INFO] redis-shake configuration: {"ConfVersion":1,"Id":"redis-shake","LogFile":"","LogLevel":"info","SystemProfile":9310,"HttpProfile":9320,"Parallel":32,"SourceType":"standalone","SourceAddress":"127.0.0.1:6379","SourcePasswordRaw":"***","SourcePasswordEncoding":"***","SourceAuthType":"auth","SourceTLSEnable":false,"SourceRdbInput":[],"SourceRdbParallel":1,"SourceRdbSpecialCloud":"","TargetAddress":"127.0.0.1:6389","TargetPasswordRaw":"***","TargetPasswordEncoding":"***","TargetDBString":"3","TargetDBMapString":"","TargetAuthType":"auth","TargetType":"standalone","TargetTLSEnable":false,"TargetRdbOutput":"local_dump","TargetVersion":"6.2.6","FakeTime":"","KeyExists":"rewrite","FilterDBWhitelist":[],"FilterDBBlacklist":[],"FilterKeyWhitelist":[],"FilterKeyBlacklist":[],"FilterSlot":[],"FilterLua":false,"BigKeyThreshold":524288000,"Metric":true,"MetricPrintLog":false,"SenderSize":104857600,"SenderCount":4095,"SenderDelayChannelSize":65535,"KeepAlive":0,"PidPath":"","ScanKeyNumber":50,"ScanSpecialCloud":"","ScanKeyFile":"","Qps":200000,"ResumeFromBreakPoint":false,"Psync":true,"NCpu":0,"HeartbeatUrl":"","HeartbeatInterval":10,"HeartbeatExternal":"","HeartbeatNetworkInterface":"","ReplaceHashTag":false,"ExtraInfo":false,"SockFileName":"","SockFileSize":0,"FilterKey":null,"FilterDB":"","Rewrite":false,"SourceAddressList":["127.0.0.1:6379"],"TargetAddressList":["127.0.0.1:6389"],"SourceVersion":"6.2.6","HeartbeatIp":"127.0.0.1","ShiftTime":0,"TargetReplace":false,"TargetDB":3,"Version":"develop,cc226f841e2e244c48246ebfcfd5a50396b59710,go1.15.7,2021-09-03_10:06:55","Type":"sync","TargetDBMap":null}
2022/03/09 21:20:06 [INFO] DbSyncer[0] starts syncing data from 127.0.0.1:6379 to [127.0.0.1:6389] with http[9321], enableResumeFromBreakPoint[false], slot boundary[-1, -1]
2022/03/09 21:20:06 [INFO] input password is empty, skip auth address[127.0.0.1:6379] with type[auth].
2022/03/09 21:20:06 [INFO] DbSyncer[0] psync connect '127.0.0.1:6379' with auth type[auth] OK!
2022/03/09 21:20:06 [INFO] DbSyncer[0] psync send listening port[9320] OK!
2022/03/09 21:20:06 [INFO] DbSyncer[0] try to send 'psync' command: run-id[?], offset[-1]
2022/03/09 21:20:06 [INFO] Event:FullSyncStart  Id:redis-shake
2022/03/09 21:20:06 [INFO] DbSyncer[0] psync runid = 95203197147e62b36935e3b68a3f98e4c7fe99c2, offset = 0, fullsync
2022/03/09 21:20:06 [INFO] DbSyncer[0] +
2022/03/09 21:20:07 [INFO] DbSyncer[0] -
2022/03/09 21:20:07 [INFO] DbSyncer[0] +
2022/03/09 21:20:08 [INFO] DbSyncer[0] -

...
2022/03/09 21:21:31 [INFO] DbSyncer[0] total = 108.17MB -     102.68MB [ 94%]  entry=1064782
2022/03/09 21:21:32 [INFO] DbSyncer[0] total = 108.17MB -     104.14MB [ 96%]  entry=1079907
2022/03/09 21:21:33 [INFO] DbSyncer[0] total = 108.17MB -     105.57MB [ 97%]  entry=1094745
2022/03/09 21:21:34 [INFO] DbSyncer[0] total = 108.17MB -     106.89MB [ 98%]  entry=1108481
2022/03/09 21:21:35 [INFO] DbSyncer[0] total = 108.17MB -     108.17MB [100%]  entry=1122766
...

2022/03/09 21:21:33 [INFO] DbSyncer[0] total = 108.17MB -     105.57MB [ 97%]  entry=1094745
2022/03/09 21:21:34 [INFO] DbSyncer[0] total = 108.17MB -     106.89MB [ 98%]  entry=1108481
2022/03/09 21:21:35 [INFO] DbSyncer[0] total = 108.17MB -     108.17MB [100%]  entry=1122766
2022/03/09 21:21:35 [INFO] DbSyncer[0] sync rdb done
2022/03/09 21:21:35 [INFO] input password is empty, skip auth address[127.0.0.1:6389] with type[auth].
2022/03/09 21:21:35 [INFO] DbSyncer[0] FlushEvent:IncrSyncStart Id:redis-shake
2022/03/09 21:21:35 [INFO] input password is empty, skip auth address[127.0.0.1:6379] with type[auth].
2022/03/09 21:21:36 [INFO] DbSyncer[0] sync:  +forwardCommands=9      +filterCommands=0      +writeBytes=36

112만건 복제시 1분 20초가 소요됨.

복제가 끝나도 계속 연속해서 계속 sync 가 이어지며 (redis1 -> redis2) 당연히 redis1 에 값을 계속 밀어 넣어도 연속해서 sync 가 이어진다.

redis-shake 로 데이터 sync 중에 redis1 에 값 입력 시간 측정

2022-03-09 21:39:29.951538
113.0
114.0
115.0
116.0
117.0
118.0
119.0
120.0
121.0
122.0
2022-03-09 21:41:32.432655

10만건 입력시 1분 53초. rump 할 때 보다 약간 느리지만 같은 속도로 볼 수 있다. 이는 python 의 구현방법에 따라 차이가 있을 수 있겠지만..

redis-shake 가 답이다. alibaba 만세.

자세히 알아보지는 않았지만 rump 는 클라우드로 데이터를 넣는 용으로 쓸 수 있는 듯. 용도가 다른 듯.

minikube 로 기동한 kube cluster 왜 nodeport svc 에 접속되지 않을까? docker desktop kubenetes cluster support 를 쓰자.

Accessing apps | minikube (k8s.io)

minikube 에서 외부 접속 방법으로 정확히 설명 하고 있다.

k get svc -o wide 를 하면 보통 external IP 가 나오기 마련인데 (노드의)

나의 경우는 나오지 않았다. node 의 IP 를 알아야 nodeIP:nodePort 로 접속하는데..

nz@nzYoga7:/mnt/c/src/kubectl/ckad-test$ kubectl get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes         ClusterIP   10.96.0.1       <none>        443/TCP        6d20h
nginx-deployment   ClusterIP   10.97.174.110   <none>        80/TCP         6d19h
nz-deployment      NodePort    10.107.63.94    <none>        80:31438/TCP   12m

minikube 가이드를 보니 명령을 쳐서 특정 서비스를 다시 찾아내야 하는 것 같다.

PS C:\Users\nzin4> minikube service nz-deployment
|-----------|---------------|-------------|---------------------------|
| NAMESPACE |     NAME      | TARGET PORT |            URL            |
|-----------|---------------|-------------|---------------------------|
| default   | nz-deployment |          80 | http://192.168.49.2:31438 |
|-----------|---------------|-------------|---------------------------|
🏃  Starting tunnel for service nz-deployment.
|-----------|---------------|-------------|------------------------|
| NAMESPACE |     NAME      | TARGET PORT |          URL           |
|-----------|---------------|-------------|------------------------|
| default   | nz-deployment |             | http://127.0.0.1:64489 |
|-----------|---------------|-------------|------------------------|
🎉  Opening service default/nz-deployment in default browser...

그럼 일단은 127.0.01: 에 새로 정의된 포트로는 접속이 가능하다.

windows 방화벽에 minikube 가 기동한 포트에 대해서 등록해 보았으나 minikube 는 설치된 docker 에 명령을 줄 뿐 실제로 minikube 가 연 포트에 대한 방화벽 설정은 무의미해 보인다.

docker desktop 에서 kubernetes 활성화

그냥 켜면 된다.

.kube/config 도 변경되는데 기본 context 가 활성화 되지 않는다.

kubectl config use-context docker-desktop

을 입력함으로서 .kube/config 에 이미 정의된 context 를 사용할 수 있게 된다.

minikube 안녕 즐거웠다.

docker desktop 은 nodeport 의 node IP 는 당연 node 그 자체인 localhost.

이제 node port 만 확인해서 오픈하면 된다.

PS C:\src\kubectl\ckad-test> kubectl.exe get svc -o wide
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP        4m49s   <none>
nz-deployment   NodePort    10.105.191.102   <none>        80:32116/TCP   8s      app=pod-nginx
PS C:\src\kubectl\ckad-test>

windows 에서 기동한 minikube cluster 를 wsl 에서 접근하기 위한 자동 설정 ~/.profile 수정

~/.profile 에 아래와 같은 복사 및 경로 구분자 변경하는 명령어를 넣으면 된다.

[USERID] 부분만 자신의 windows user id 로 치환하면 된다.

cp /mnt/c/Users/[USERID]/.kube/config ~/.kube/config #복사
sed -i 's/C:/\/mnt\/c/g' ~/.kube/config # c 드라이브 경로를 mnt로 치환
sed -i 's/\\/\//g' ~/.kube/config # \를 / 로 변환

1. prometheus 로 웹서비스 모니터링 하기 – blackbox_exporter 연계하기

특정 웹서비스를 모니터링 하는 과정을 기록.

pulling metrics

회사에서 운영하는 대외 서비스의 서비스를 모니터링 하기 위해 그 유명하다는 prometheus 를 사용해 보았다. 공식 홈페이지의 기본 예제로 진행해 보았다. 내 식대로 이해해보면 prometheus 는 단순히 metric 이라는 것을 긁어서 저장하는 크롤링 서버다. (pull metrics)

무엇을 크롤링 하는가? 프로메테우스가 이해하기 좋은 형태의 시시각각 새로고침 되는 상태 정보다. 프로메테우스 설정에 등록된 모든 서버들에 프로메테우스가 주기적으로 접속해서 내용을 계속 긁어온다.

exporter

프로메테우스가 데이터를 잘 긁어갈 수 있도록 준비된 서버들이 필요하다. 이것을 보통 exporter 라고 부르며 이것이 준비 되어야 프로메테우스가 긁는 역할을 담당할 수 있다. exporter 는 독립실행형 프로그램이므로 프로메테우스를 설치 하기 전에 실행 할 수 있다. 따라서 잠시 프로메테우스에 대해서 잊도록 하자.

프로메테우스에게 긁힐만한 정보를 가진 서버들은 xx_exporter 라 불리우는 “추출기” 서비스가 기동되어 있어야 한다. 그 서비스의 포트로 프로메테우스가 주기적으로 접속해서 내용들을 주기적으로 가져간다. 물론 xx_exporter 는 계속 내용을 갱신하기 때문에 프로메테우스는 시시각각 변하는 서버의 정보를 끊임없이 모을 수 있게 된다.

공식 아키텍쳐. Retrieval 프로세서가 계속 긁어간다.

아래와 같이 프로메테우스에 등록된 어떤 URL 이 있다고 할 때 그 metric 지표는 아래와 같이 plain_text_key value 쌍으로 구성된 단순 text 여러줄의 모음이다. 중간에 주석이 많은데 그냥 고마운 설명이라 생각하고 넘어가자.

http://somehost.com:9115/metric

# HELP probe_dns_lookup_time_seconds Returns the time taken for probe dns lookup in seconds
# TYPE probe_dns_lookup_time_seconds gauge
# plain_text_key value
probe_dns_lookup_time_seconds 0.199540841
# HELP probe_duration_seconds Returns how long the probe took to complete in seconds
# TYPE probe_duration_seconds gauge
probe_duration_seconds 2.299759862 # 웹서버의 응답시간

이런 정보가 프로메테우스에 수집되면 그 plain_text_key 키값 그대로 검색을 할 수 있고 시시각각 변하는 값이 그래프로 나타나게 된다.

blackbox_exporter

프로메테우스에게 데이터를 모두 주기위해서 많은 서버들이 스스로 exporter 가 작동하는 등의 노력을 할 수도 있겠지만 내가 관리 할 수 없는 특정 API 의 상태를 체크 하고 싶다면 그 API 를 찔러보는 서버를 대상 서버 밖의 다른 곳에 두는 수 밖에 없다. 따라서 blackbox_exporter 를 이용하면 부족하나마 서비스를 모니터링 할 수 있다.

blackbox.yml

modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      preferred_ip_protocol: "ip4" # 이 설정이 매우 중요
      method: GET
      valid_status_codes: [200, 302]
      headers:
        User-Agent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Mobile Safari/537.36 Edg/96.0.1054.34"
  http_post_2xx:
    prober: http
    http:
      method: POST

여기에는 어떤 서버를 모니터링 할지는 안 나와 있고 blackbox_exporter 자체의 속성만 편집한다. 여기서

  preferred_ip_protocol: "ip4"

가 매우 중요한데, ipv6 를 지원하지 않는 서버의 상태를 조회 할 때 저것을 명시 하지 않으면 조회하지 못한다는 에러가 난다. (보통 다 그럴 것이다)

proxy

blackbox_exporter 는 일종의 proxy 이다. 프로메테우스는 blackbox_exporter 를 경유해서 서버의 상태를 조회하며, blackbox_exporter 는 서버의 상태를 프로메테우스가 이해하기 쉬운 형태로 그 결과를 변환 해 준다.

target 서버를 뭘로 인자를 주느냐에 따라서 그 서버의 http 상태 정보를 추출할 수 있다. prometheus 의 영어 사전을 리턴하는 네이버의 응답 속도를 모니터링 하고 싶다면

blackbox_exporter 의 probe API 에 target 인자로 그 URL 을 주면 된다. 이렇게 target 인자를 프로메테우스가 필요에 따라 설정할 수 있도록 하면 된다.

Prometheus 에서 조회 대상 target 설정

  1. prometheus 에서 조회 대상 설정
  2. blackbox_exporter 서버가 대신 조회
  3. 응답을 prometheus 용으로 생성
  4. prometheus 가 시시각각 변하는 metric 지표를 저장

prometheus

드디어 prometheus 를 시작해보다. 기본 설정에서 blackbox_exporter 관련 설정만 추가 한다.

scrape_configs:
  - job_name: "blackbox_exporter"
    metrics_path: /probe
    params:
       module: [ http_2xx ]  # Look for a HTTP 200 response.
    static_configs:
      - targets:
         - https://nz.pe.kr

         - https://curious.com/api/someapi?hello=world
    relabel_configs:
           - source_labels: [__address__] # 1
             target_label: __param_target
           - source_labels: [__param_target] # 2
             target_label: instance
           - target_label: __address__ # 3
             replacement: localhost:9115 # The blackbox exporter.

localhost:9115 에 떠 있는 blackbox_exporter 에게 targets 에 명시된 서버들을 조회하라는 설정이다. relabel 을 prometheus 의 유연성 중 하나로 언급되기도 한다.

이 설정은 blackbox_exporter 와 연계하여 호환 설정하는 내용이 아님을 명심하자. 사실 prometheus 는 blackbox_exporter 가 주는 값만 중요할 뿐이지 blackbox_exporter 인지는 중요하지 않다.

이 설정은 단지 job_name (배치 job?) 에 대한 시시각각 변경되는 값을 가져오기 위해서 URL 을 어떻게 조합해 갈 것인가에 대한 트릭이 담겨있는 설정이다. 위와 같이 설정하고 나면 prometheus 는 아래와 같이 target 에 명시된 URL 을 크롤링 하기 위한 blackbox_exporter 용 URL 을 생성하여 그것을 pulling 한다.


* localhost:9115/probe?module=http_2xx&target=https://nz.pe.kr # target 뒤의 값은 URL encoding 된다.
* localhost:9115/probe?module=http_2xx&target=https://curious.com/api/someapi?hello=world

#1 여기서 __address__ 는 target 내의 서비스들을 말하는데 [] 로 감싸져있어 배열이다. (그래서 labels 도 복수형) 이것이 __param_ 이 된다. 즉 blackbox_exporter 의 target 파라미터로 대체되어 들어간다.

#2 또한 그 target 들은 prometheus 의 instance label 이 된다. 표시되는 그래프에서 구별해 볼 수 있는 구분자 라벨이다. 즉 #1의 target_label 은 __param_ 이 prefix 이므로 파라미터로 전환되는 것이고 #2는 prefix 가 없으므로 그대로 label이 된다.

#3 마지막으로 마지막으로 __address__ 즉 조회할 서버 (target_label) 의 주소는 localhost:9115 로 대체된다.

targets 정제하기

static_configs:
  - targets:
     - https://nz.pe.kr/too/too/too/too/so/so/long/user?hello=world#GET_USER
     - https://nz.pe.kr/too/too/too/too/so/so/long/and/again/long/point?hello=world#GET_POINT
relabel_configs:
       - source_labels: [__address__]
         target_label: __param_target
       - source_labels: [__param_target]
         regex: .*(#.*)
         replacement: $1
         target_label: instance
       - target_label: __address__
         replacement: localhost:9115 # The blackbox exporter.

너무 target 이 길면 나중에 prometheus 에서 조회한 URL 이 너무 길고 나중나중에 grafana 로 연계 할 때도 안 좋기 때문에 (#.*) # 뒤에 오는 것을 $1번 변수에 담아서 instance 라는 라벨에 할당할 수 있다. 나중에 화면 볼때 쓸데 없이 긴 라벨이 없어 깔끔하다.

서비스별 응답시간

여기까지 prometheus 에 웹서버의 상태를 표시하기 위한 여정을 마친다. metric 을 쭑 훑어보면 웹서버의 응답 시간을 나타내는 probe_duration_seconds 를 찾을 수 있고 최종적으로는 등록된 API 들에 대한 측정치를 확인 할 수 있다.

다음은 응답시간을 받아올 수 없을 때, 즉, blackbox_exporter 의 조회 API 인 probe API 의 조회 실패 케이스 probe_success 0 일 때 slack 으로 알림을 받는 방법을 알아본다.

좀 오래된 go 책을 사서 따라 하다가 안될 때는 GO111MODULE 부터 봐야 할 듯.

몇년된 go 책을 보면 거의 뭔가 안되는데

GO111MODULE 때문인것 같다.

C:\src\learninggo\src\anydir>go run anyfile.go
anyfile.go:3:8: package greet is not in GOROOT (C:\Program Files\Go\src\greet)

C:\src\learninggo\src\anydir>set GO111MODULE=off
or
go env -w GO111MODULE=auto

C:\src\learninggo\src\anydir>go run anyfile.go
Hello greeting pack

책의 go version 과 요즘음의 go version 의 차이로 module 의 기본 사용이 강제 되는 듯.

회사 코드도 컴파일이 안되는게 이것 때문인듯. 오히려 예전 버전의 go를 설치 하고 있는 것도 문제.

windows 에서 docker 로 띄운 container 에 옆/외부 컴퓨터에서 접속할 수 없는 경우 개인 네트워크 공용 네트워크 그리고 윈도우 방화벽

예를 들어서 nginx를 9999 포트로 띄운다고 가정해보자.

docker run -p 9999:80 nginx

localhost:9999 로 붙으면 당연히 잘 될 것이다.

이 컴퓨터의 ip 가 192.168.0.2 라고 가정해 보자.

옆 컴퓨터에서 붙어 보자.

192.168.0.2:9999

이 때 안되는 경우에 대한 이야기를 시작한다.

일단 결론적으로 나의 경우 안되었던 이유는 docker container 를 띄운 windows 컴퓨터가 붙은 공유기를 “공용 네트워크” 로 선택했기 때문이었다.

컴퓨터를 세팅한 첫날부터 잘못된 선택이었다. 처음에는 outbound 로 인터넷만 하는 컴퓨터라 신경을 안 썼는데 “서버” 로 돌릴 host 컴퓨터라면 당연 “개인 네트워크”로 선택하는 것이 좋겠다. 왜냐하면 host 컴퓨터의 입장에서는 내 공유기가 “공용 네트워크” 로 “선언” 했다는 것은 마치 커피숖 wi-fi 붙은거랑 같게 보안을 강화해 달라는 설정을 한 것이나 다름 없기 때문이다.

그러니 옆에 컴퓨터에서 host 컴퓨터에 붙으려고 해도 대부분 기본 차단된다. 이는 커피숖의 다른 누군가가 내 컴퓨터에 접속 시도하는 것을 죄다 막는 “공용 네트워크” 설정 때문이다.

host 컴퓨터가 붙은 wi-fi 가 “개인 네트워크” 설정을 했다면 기본적으로 외부 컴퓨터에서 host 컴퓨터가 열어 놓은 port 에 접속이 잘 되기 마련인데 이는 docker for desktop 등을 설치 했을 때, 처음 서버를 기동 했을 때 windows 가 분명히 물어봤을 것이다.

시작>실행> wf.msc 에서 나오는 인바운드 규칙을 보면 나의 경우는 docker 프로그램이 여는 모든 포트가 허용되되 개인 프로필 즉, 개인 네트워크에 붙어 있을 때에만 허용하고 “공용 프로필” 은 명시적으로 거절하도록 되어 있다.

이것이 무슨 포트인지는 중요하지 않다. docker 로 실행된 모든 포트는 허용을 하게 된다.

windows 에서 리스닝 중인 포트에 대해서 어떠한 프로그램이 그 포트를 열고 있는가를 거꾸로 찾아 들어가는 방법이 있다.

netstat -aon 과 linux 의 grep 격인 findstr 를 조합하면 된다. 9998 을 열고 있는 프로그램의 ID 를 찾는다.

netstat -aon 과 findstr

프로그램 ID 를 찾았다면 이제 프로그램이 무엇인지 단번에 알아낼 수 있다.

linux 의 ps -ef 격인 tasklist 이다. 여기서도 findstr 를 조합한다.

com.docker.backend.exe 가 결국 9998 포트를 열고 있는 프로그램인 것을 알 수 있다.

com.docker.backend.exe 프로그램의 fullpath 를 아는 방법은 아래의 명령으로 가능하나 외우기가 어렵다. 따라서 작업관리자 > 세부정보 탭에서 파일 위치 열기를 하는 것이 낫겠다.

C:\Users\nzin4>wmic process where “name=’com.docker.backend.exe'” get executablepath
ExecutablePath
C:\Program Files\Docker\Docker\resources\com.docker.backend.exe

이제 옆 컴퓨터에서의 테스트가 끝났다면 공유기 외부에서의 접속을 시도해볼 차례다. 공유기는 각 공유기 제조사마다 “포트 포워딩” 가이드를 제공하고 있다. port forwarding 설정을 잘만 한다면 host 즉, windows 컴퓨터의 외부에서의 접속 테스트는 완료된 상태이므로 무리없이 진행될 것이다.

개인 라이센스로 구매한 소프트웨어 (SecureCRT) 회사에서 업무용으로 쓸 수 있는가? :: YES

회사에서 몇가지 s/w 를 구매해야 하는데 그냥 법카로 사서 청구하라고 해서 개인용으로 구매하라는 뜻인지.. 결론적으로 s/w 회사에서 license 를 개인용 / 업무용을 명확하게 나누지 않고서야 개인용으로 구매해도 문제가 없다는 생각이 든다. 즉, 개인용으로 귀속시키면 내 전용 s/w 가 되고 어딜 가서 사용해도 괜찮다는 의미가 되겠다.

SecureCrt 에 문의 메일을 보내서 가능하다는 답변을 받았다.

구매 라이센스는 computer 에 귀속된다.

제목: Re: Can I use personal license S/W at my company? [T02035873:002]

Hello,

Thank you for your e-mail.

Providing you stay in compliance with our End-user License
Agreement and are authorized by your company to install a
personal License on your work computer there should not be
an issue.

That being said SecureCRT is licensed per “computer” as
defined in section 3 of the SecureCRT EULA which can be
found at the following location:

https://www.vandyke.com/pricing/licensing.html

As defined in the SecureCRT EULA, a “computer” can be one of
the following:

  1. A client machine running a client operating system like
    Windows 10, macOS or Linux.
  2. A “seat” which is defined as a software or hardware
    client that has access to use SecureCRT on a terminal
    or RDS server.

You will need 1 SecureCRT License for each “computer” that
has access to use SecureCRT locally or remotely.

A single SecureCRT License can be used by a single
individual to register SecureCRT on one additional
“computer” as defined in the SecureCRT EULA as long as the
individual has SecureCRT running on a single “computer” at a
time.

Please let me know if there is anything else I can do for you.

Misty Buck
VanDyke Software, Inc.
505-332-5710
505-332-5701 (fax)
sales@vandyke.com
www.vandyke.com