kubernetes BIND DNS 성능 시험 - 계속되는 문제
안양에서 이현우 부장님과 함께 BIND 컨테이너 성능 시험을 했다. PORCH은 어느 정도 마무리가 되었는데(솔라리스용 BIND 앱 배포도 완료함), KUBIC은 아직 실전 투입을 하지 못하고 있다.
CentOS7-based Kubernetes Cluster Setup using Ansible (en)
1. Introduction
This guide will take you through the process for a multi-node kubernetes cluster on CentOS 7 using Ansible automation tool. The setup will have one master and two minions.
CentOS7-based Kubernetes Cluster Setup using Ansible (ko)
1. 소개
본 매뉴얼은 CentOS 7에서 Ansible 자동화 도구를 사용하여 multi-node kubernetes cluster의 과정을 설명합니다. master 1개, minion 2개를 설정하겠습니다.
Kubernetes Raspberry Pi 소형 IDC 구축
개요
가상머신이 아닌 실제 하드웨어 규모로 컨테이너 기술을 실험하고 싶었다. 가장 큰 목적으로는 이동하며 kubernetes가 어떻게 작동하는지 시뮬레이션 가능하도록 하는 것이었다.
목적에 적합한 컴팩트한 사이즈와 전력 소비가 적고 저렴한 rapberry pi를 사용하기로 결정하였고 소형 IDC 구상을 시작하였다. 국내에는 rpi를 이용하여 cluster 구성을한 사례를 찾아 볼 수 없었다.
해외 rpi cluster 구축 사이트를 참조하여 구상을 하던 중 아크릴 판을 이용하여 케이스를 제작한 문서를 참고하여 구축하게 되었다.
Kubernetes Internal DNS
kubernetes dns is for service objects. DNS is served as a pod in the cluster.
Set up kubelet to use DNS
Modify kubelet definition file(/etc/kubernetes/kubelet) on all minions. Add the following options.
Kubernetes TLS Setup Howto
Secure communication on Kubernetes cluster.
Kubernetes apiserver supports both insecure HTTP and secure HTTPS/TLS protocol.
The insecure HTTP with port 8080 is the default setup but as the name indicates, it is not secure. So we need to set up secure HTTPS communication on KUBIC cluster. Let’s see how to do it.
Kubernetes TLS Setup Howto (ko)
Kubernetes 클러스터의 보안 통신
Kubernetes api서버는 비보안의 HTTP와 보안 HTTPS/TLS 프로토콜 모두를 지원합니다.
8080 포트를 사용하는 비보안 HTTP는 기본으로 설정되지만, 이름에서 알수 있듯 안전하지 않습니다. 따라서 KUBIC 클러스터에서 안전한 HTTPS 통신을 설정해야 합니다. 그 방법을 기술 합니다.
kube-proxy load balance test (en)
1. What is kube-proxy?
Kubernetes is an orchestration tool for containers. kube-proxy is a component of kubernetes running on kubernetes minion. Its role is the network proxy and load balancer for each container. It uses iptables statistic extension module with random mode and probability settings.
kube-proxy load balance test (ko)
1. kube-proxy란?
Kubernetes는 컨테이너를 위한 통합 도구입니다. kube-proxy의 특징은 다음과 같습니다.
- kube-proxy는 kubernetes minion에 실행되는 kubernetes의 구성 요소입니다.
- kube-proxy는 각각의 컨테이너에서 네트워크 프록시 및 load balancer 역할을 합니다.
- kube-proxy는 랜덤 모드 및 확률 설정을 포함한 iptables 통계 확장 모듈을 사용합니다.
django로 csrf token의 post request 전달하는 방법
운영중인 서버의 was(fcgi)가 가끔 비정상적으로 동작한다.
crontab에 등록하여 일주일에 한번씩 was(fcgi를 restart하고 있지만, 그래도 문제가 발생한다. restart 주기를 더 짧게 설정하는 방법보다 문제가 발생했을때 restart하는것이 효율적일것이다. curl을 이용하여 실질적으로 login하는 과정을 거쳐 main page(/bom/register/)가 response status 200이 아니면 restart 하게끔 script 작성하고자 한다.
curl을 이용한 login
현재 서버의 login page는 http://xxx.xxx.xxx/login/ 이다. 해당 page를 body(post)값으로 username과 password 변수를 같이 넘겨준다면 login이 될것이다.
# curl http://xxx.xxx.xxx/login/ -d "username={ID}&password={PW}"
그런데 403 FORBIDDEN이 뜬다. 원인은 CRSF verification failed다.
<h1>Forbidden <span>(403)</span></h1>
<p>CSRF verification failed. Request aborted.</p>
CSRF
사이트 간 요청 위조(또는 크로스 사이트 요청 위조, 영어: Cross-site request forgery, CSRF, XSRF)는 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격을 말한다.
예를 들자면 아래와 같은 공격이다.
- 사용자가 xxx.xxx.xxx에 login을 한다. sessionid등의 쿠키값을 정상적으로 발급받아 login된다.
- 공격자가 mail이나 게시판등을 이용해 악의적인 http request의 주소를 사용자쪽으로 전달한다. 예를 든다면 http://xxx.xxx.xxx/changepassword?password=abc와 같은 request를 서버로 전송하게끔.
- 사용자가 해당 주소를 실행하여 원하지 않는 request를 전송하게 된다.
- 서버입장에선 로그인을 거친 정상적인 client가 request를 수행한것이니 해당 프로세스를 수행한다.
위 문제를 해결 하기 위해선 changpassword 페이지의 form전달값에 특정한 값을 추가하면 된다.
예를 들어 사용자로 부터 captcha를 값을 입력받게하여, 정상적인 페이지에서의 요청인지 확인하면 되는것이다. django에서는 간단히 crsf token을 발행하여 처리하게 한다.
CSRF TOKEN in DJANGO
form를 가진 django template에 아래와 같이 crsf_input을 설정한다.
<form action="" method="post">{{ csrf_input }}
<input name="username" id="username" type="text" />
<input name="password" id="password" type="password" />
이렇게 되면 아래와 같은 client에서는 아래와 같은 response를 받게 된다.
<form action="" method="post"><input type='hidden' name='csrfmiddlewaretoken' value='d7f6f683188d35958b0f453f6849a8d7' />
csrfmiddlewaretoken라는 이름의 hidden변수에 random 생성된 token값이 날라온다. http header에도 동일한 token을 cookie로 저장하게끔 되어 있다.
Set-Cookie: csrftoken=d7f6f683188d35958b0f453f6849a8d7; expires=Mon, 10-Jul-2017 08:32:17 GMT; Max-Age=31449600; Path=/
따라서 request시 cookie와 body에 위와 동일한 token값을 같이 전송해야 한다. django에서 해당 token값이 없거나 잘못되었을 경우 위처럼 403을 띄우게 되는것이다.
CURL을 이용하여 CRSF token 전송
CSRF token을 받아와야하니 한번의 login page request로는 불가능하다. 아래와 같은 과정이 있어야 할것이다.
- http://xxx.xxx.xxx/login/ 요청후에 csrf token값 저장
- 전달받은 csrf token값을 username, password와 같이 전달. cookie도 마찬가지.
curl을 이용하여 csrf token값을 저장해보자.
# curl -c cookie.txt http://xxx.xxx.xxx/login/
-c 옵션으로 set-cookie를 처리할 수 있다. 즉 cookie.txt가 생성되고 csrf token값이 저장된다. 파일을 열어보면 아래와 같다.
# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
xxx.xxx.xxx FALSE / FALSE 1499671503 csrftoken d7f6f683188d35958b0f453f6849a8d7
이제 해당 cookie값과 post값으로 csrf token값을 전달하면 된다. -b 옵션으로 저장된 cookie을 서버로 전달할수 있다.
# curl -b cookie.txt http://xxx.xxx.xxx/login/ -d "username={ID}&password={PW}&csrfmiddlewaretoken=d7f6f683188d35958b0f453f6849a8d7"
이제 정상적으로 login이 됨을 확인할수 있다. 그런데 /bom/register/ 페이지가 정상적으로 열리지 않는다. login후에 sessionid을 cookie로 처리해야하는데, 그 과정이 없으니 login이 되지 않은 것과 동일한 것이다. 위 login request시 sessionid도 response header로 아래와 넘어 온다.
Set-Cookie: sessionid=a37046e1944553ee8dc2af0bc5c483fc; Path=/
그러니 위의 sessionid도 같이 cookie로 저장해야한다. 위와 동일하게 -c 옵션을 줘서 sessionid도 저장하게한다.
# curl -b cookie.txt http://xxx.xxx.xxx/login/ -d "username={ID}&password={PW}&csrfmiddlewaretoken=d7f6f683188d35958b0f453f6849a8d7" -c cookie.txt
cookie.txt파일을 열어보면 csrftoken값과 sessionid가 같이 저장 됨을 확인할 수 있다.
# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
xxx.xxx.xxx FALSE / FALSE 1499676993 csrftoken 97154f650b5e4e5a63fbec267072cc38
xxx.xxx.xxx FALSE / FALSE 0 sessionid a37046e1944553ee8dc2af0bc5c483fc
이제 위 sessionid cookie를 이용하여 /bom/register/ page에 접근하면 된다.
# curl -b cookie.txt http://xxx.xxx.xxx/bom/register/
이제 위의 response가 200인지만 확인하면 된다.
TIP
firefox나 chrome에는 개발자 도구(F12)등이 포함되어 있다. 이곳에서 request, response의 header/body값과 cookie값등을 모두 확인할 수 있다.
이를 이용하면 실제 브라우져와 동일하게 http request를 요청하게 처리할 수 있을것이다. 위의 경우 curl로 진행했지만, python httplib 도 header와 body, cookie을 동일하게 처리하면 된다.
Script
위 내용을 토대로 http://xxx.xxx.xxx 사이트의 로그인 시도를 주기적으로 시도하여 문제 발생시(status code 400 이상일 경우) was와 mysql을 재시작하는 스크립트를 작성하였다. 해당 스크립트는 일반 사용자 계정으로 crontab에 등록되어 5분간격으로 실행된다.
crontab의 아래와 같이 오분마다 스크립트를 실행하도록 추가하였다.
$ crontab -e
*/5 * * * * ~/check_login.sh 2>&1
check_login.sh 스크립트 내용은 아래와 같다.
#!/bin/bash
USERID="sungmin" <- 사이트의 로그인 아이디를 입력
USERPW="sungmin" <- 사이트의 로그인 패스워드를 입력
DATE=$(date +%m%d%H%M)
LOG="~/log/check_login_${DATE}.log"
curl -c cookie.txt http://xxx.xxx.xxx/login/ -s > /dev/null
TOKEN=$(grep csrftoken cookie.txt | awk '{print $NF}')
curl -b cookie.txt -d "csrfmiddlewaretoken=${TOKEN}&username=${USERID}&password=${USERPW}" http://xxx.xxx.xxx/login/ -c cookie.txt
RESULT=$(curl -b cookie.txt http://xxx.xxx.xxx/bom/register/ -I -s | head -n1 | awk '{print $2}')
echo ${RESULT}
if [ ${RESULT} -gt 399 ]; then <- status code 결과 값이 400 이상일 경우 아래 명령을 실행
echo "## Web Status code" > $LOG
echo ${RESULT} >> $LOG
echo "## Memory Status" >> $LOG
free -m >> $LOG
echo "## Process Check" >> $LOG
ps -eo pid,rsz,vsz,cmd | grep python | grep -v grep >> $LOG
echo "## Load average" >> $LOG
w | head -n1 >> $LOG
echo "Nginx & WAS & MySQL Restart" >> $LOG
sudo /etc/init.d/nginx restart >> $LOG 2>&1
sleep 1
sudo /etc/init.d/mysql restart >> $LOG 2>&1
sleep 1
sudo -u sungmin ~/script/runserver restart >> $LOG 2>&1
sleep 1
echo ${RESULT} >> $LOG 2>&1
mail -s "Server Error & Process Restart" sungmin@xxx.xxx < $LOG
fi
log는 문제 발생 시간에 mysql과 was가 재시작되며 그 전의 메모리 사용률, was 프로세스의 메모리 사용률과 갯수, load average를 log 파일로 기록한다.(로그파일 위치 : ~/check_login/log/)
아래는 mysql을 종료하고 스크립트가 구동되어진 후 로그를 출력. 이후 서비스 정상 확인.
$ cat check_login.log
## Memory Status
total used free shared buffers cached
Mem: 1982 399 1583 0 23 251
-/+ buffers/cache: 124 1857
Swap: 952 0 952
## Process Check
4277 24672 87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
4278 23888 87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
4279 23908 87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
4280 23908 87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
4281 46904 158356 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
4282 23908 87860 /usr/bin/python manage.py runfcgi host=127.0.0.1 port=8080
## Load average
02:52:05 up 5 min, 2 users, load average: 0.10, 0.12, 0.06
## Nginx & WAS & MySQL Restart
sudo: unable to resolve host test
Restarting nginx: nginx.
sudo: unable to resolve host test
Stopping MySQL database server: mysqld.
Starting MySQL database server: mysqld ..
Checking for tables which need an upgrade, are corrupt or were
not closed cleanly..
Stop django server : .Done
Start django server : Done.