프로그래밍, 엔지니어링

시스템 트레이딩을 위한 서버 구성 삽질기록 (라떼판다 4GB-32GB, Ubuntu 20.04, Docker20.10.6)

양마에 2021. 6. 3. 16:34

시스템 트레이딩을 위한 서버를 구성하였다.

(결론부터 말하면 구축은 하였으나, 결국 써먹진 못한 시스템 구성기록이다. 라떼판다 구버전 너무 느림 ㅠㅠ)

개인용으로 쓰기때문에 AWS나 Azure같은 클라우드 서버 말고 집에다가 직접 서버시스템을 구성해서

운영하려고한다.

처음 시스템 구성은 라떼판다(DFR0470) 를 여러대 병렬 연결 후 Docker Swarm으로 구성하려고했다.

주변에 16대가 구성된 라떼판다 시스템을 주워다가 설치를 진행했다.


처음 목표로했던 시스템의 아키텍쳐는 이렇게 생각했다.

아주 심플한 구성으로 라떼판다중에 한대를 Manager노드로 구성하고, 나머지에는 Worker로 구성한다.

그 후 라떼판다에 어떤 OS를 설치하면 좋을지 고민을 많이하였다.

라떼판다 초기버전은 성능이 좋지 않아 CoreOS 또는 Photon OS를 설치하는것을 고려했었다.

반나절 정도 삽질했는데, 결론은 "라떼판다에 CoreOS, Photon OS는 설치되지 않는다." 이다.

디스크를 인식못하거나, 화면이 펜딩된다거나 각종 오류로인해 설치를 포기했다.

결국 익숙한 우분투를 설치하기로했고, 다행히 우분투 20.04버전이 라떼판다에 문제 없이 설치되었다.

전체 시스템은 16대의 라떼판다지만, 초기 구성은 4대만 하기로 하였다.

Ubuntu 설치 후 Docker는 아래 명령어를 통해 설치한다.

$
sudo apt install net-tools
$ sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-4keyring.gpg
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
$ sudo usermod -aG docker ${USER}
$ exit (로그아웃 했다가 다시 로그인해야 usermod가 적용됨)
$ (다시 로그인 후 ) docker info

<출처: https://docs.docker.com/engine/install/ubuntu/>

Install Docker Engine on Ubuntu

docs.docker.com


7번째 줄 usermod명령어를 넣으면 sudo 없이도 docker명령어 사용이 가능하다.

한대는 메니저 구성을 위해 아래 명령어를 입력한다.

1$ docker swarm init --advertise-addr 0.0.0.0 (마스터 IP 입력)


그러면 출력으로 join을 위한 명령어가 나오는데 대략 아래모습과 같다.

To add a worker to this swarm, run the following command:

docker swarm join \
--token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx \
0.0.0.0:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.


매니저 노드가 설정되었으면 위에 Join을 위한 명령어를 나머지 라떼판다에 입력해준다.

1$ docker swarm join \
--token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx \
0.0.0.0:2377


아주 쉽게 도커 스웜 구성이 끝났다.

구성이 완료되었다면 메니저 노드에서 "docker node ls" 명령어를 통해 확인가능하다.

$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
urg8ea9iuw8jbzaec9wy4nnur * server1 Ready Active Leader 20.10.6
k3mkreijr2lpn088r70kpllbz server2 Ready Active 20.10.6
oa8dth6xvhsjgjkpf3vlaempn server3 Ready Active 20.10.6
5jz0leju7krcrhp0j1bqweo6e server4 Ready Active 20.10.6


나머지는 직접 만든 코드를 Dockerize하고, 배포만 하면 끝이난다.



도커 스웜모드에서 도커를 동작하는 방법은 Docker service를 이용하는 것이다.

Docker service 구성을 위한 사전 준비로 2가지가 있다

(1) 공유 volume 구성
시스템 트레이딩을 위해서는 많은 양의 데이터를 읽어야 하는데, 노드들마다 데이터를 로드하려면 volume을 이용해야한다.

docker swarm은 따로 공유 volume을 지원하지 않아서, 별도의 파일서버를 구성 후 연결해서 쓰거나, 아니면 각 노드마다 동일한 경로에 동일한 파일을 가지도록 해야한다.

본인은 따로 파일서버를 구성하는 방법보다는 각 노드마다 동일한 경로에 동일한 파일을 가지도록 구성을 했다.

(2) registry서버 구성
docker service를 실행하면 각노드에서 이미지를 요청하게되는데, 만약에 매니저노드 로컬에 있는 도커 이미지라면 워커노드에서 접근이 불가능하다.

그렇기 때문에, registry서버를 구성 후에 도커이미지를 업로드하여야한다.

registry서버는 매니저 노드에 아래 명령어로 구성하였다.

$ docker run -d -p 5000:5000 --restart=always --name registry registry:2

registry서버가 구성되면 로컬에 있는 도커 이미지를 registry서버에 push 해주어야한다.

$ docker image tag example:latest 0.0.0.0:5000/example:latest

$ docker image push 0.0.0.0:5000/example:latest

여기서 0.0.0.0은 각자 상황에 맞게 registry가 구성된 host의 ip를 넣으면된다.

만약에 [Docker Insecure Registry] "server gave HTTP response to HTTPS client" 라는 메시지가 보인다면

/etc/docker/daemon.json 파일을 만들어서 아래 내용을 넣어준다.

{
"insecure-registries": ["0.0.0.0:5000"]
}

이 과정은 워커 노드에는 모두 넣어주어야 service배포가 정상적으로 진행된다.

만약안해주면 워커노드에 이미지 배포시 [Docker Insecure Registry] "server gave HTTP response to HTTPS client" 를 보게될 것이다.

마지막이로 Docker Service를 배포하는 방법이 2개가 있는데

첫번째는 docker service create 명령어로 직접 서비스를 만드는 방법이고,
두번째는 docker-compose.yml 파일을 작성한 후 docker stack deploy -c docker-compose.yml container-name
로 구성하는 방법이다.

본인은 두번째 방법을 사용했다.

docker-compose.yml파일을 만들기위해 아래링크를 참고했다.

https://docs.docker.com/engine/swarm/stack-deploy/

version: '3.9'

services:
stock-auth:
image: 0.0.0.0:5000/stock-auth
ports:
- 9090:3000
deploy:
replicas: 2
restart_policy:
max_attempts: 3
condition: on-failure
update_config:
parallelism: 3
delay: 10s
placement:
constraints: [node.role==manager]
networks:
- balance

stock-engine:
image: 0.0.0.0:5000/engine
deploy:
replicas: 3
placement:
constraints: [node.role==worker]
volumes:
- type: bind
source: /home/yhc/StockEngineData
target: /app/Data
read_only: true
networks:
- balance
networks:
balance:
driver: overlay

services안에 stock-auth, stock-engine의 두가지 서비스를 운용하는데. 각 서비스가 다른서비를 접근할때는

도메인네임이나 IP를 넣을때 stock-auth, stock-engine을 각각 넣어줘야한다.

예를 들어 stock-auth서비스에서 stock-engine서비스로 post를 할때

axios.post(`http://stock-engine/blablabla`, param).then(engine_res=>{
// blabla
})


같이 코드를 작성하면된다.


최종적으로 서버가 배포되고나면 끝난다.

docker service ls를 통해 아래같은 결과를 확인할 수 있다.

ID NAME MODE REPLICAS IMAGE PORTS
5kbsyu4uakds stock-engine replicated 3/3 0.0.0.0:5000/stock-engine:latest
wd9lfcx3x45f stock-auth replicated 2/2 0.0.0.0:5000/stock-auth:latest *:9090->3000/tcp



시스템은 안정적으로 잘 구성되었고, 클라이언트와도 정상 통신이 가능해졌다.

그런데.. 결론적으로 써먹진 못하게되었다.

위 시스템을 구성하는데 2주가까이 걸렸는데, 막상 결과를 보니

시스템 트레이딩 백테스트 속도가 너무 느렸기때문이다. ㅠㅠ

보통 PC CPU(3세대 i7)에서는 30초면 끝나는 백테스트가, 라떼판다 시스템에서는 3~4분 정도 걸렸다.

백테스트를 한번할때마다 3~4분은 너무 심하지 않은가 ㅠㅠ

결국 어마어마한 라때판다 12대 병렬 시스템은 버리기로하고

그냥 성능좋은 PC1대로 바꿨다...........

병렬처리로 더 좋은 성능을 기대했지만

역시 거북이가 여럿이라도해도 토끼하나는 못이긴다구ㅠ