Jinseob Kim
October 26, 2018
맞춤형 의학연구 애플리케이션을 위해
마이크로서비스 아키텍처(microservice architecture) 구축
Rstudio와 shiny server가 설치된 Docker 이미지 제작
Docker swarm을 이용해 배포
서버의 종류와 갯수에 구애받지 않음
https 보안이 적용된 subdomain 주소 부여
동적 프록시 서버(dynamic proxy server) 프로그램인 Traefik 이용
서비스가 추가될 때 마다(ex: 홈페이지, Jupyter) 자동 적용.
ShinyApps
흔히 이용되는 의학통계 방법들을 ShinyApps 로 만들어 위의 환경에 배포
데이터 라벨(label) 정보 활용 - 라벨이 적용된 논문용 테이블/그림
장점
깔끔하다.
치우기 쉽다.
다른 가방으로 옮기기 쉽다.
가방 종류에 구애받지 않는다.
단점
실제 쓸 수 있는 공간이 줄어든다.
분리해서 넣기 귀찮다.
물건 찾을 때 지퍼를 한번 더 열어야 된다.
장점
깔끔하다.
삭제가 쉽다.
다른 컴퓨터에 재설치 쉽다.
컴퓨터/서버 종류에 구애받지 않는다.
단점
실제 쓸 수 있는 용량이 줄어든다.
서비스마다 모듈 만들기 귀찮다.
성능저하 우려
가상머신(Virtual machine) 활용이 대표적.
FROM ubuntu:latest
RUN sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
sed -i 's/extras.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
MAINTAINER Jinseob Kim "jinseob2kim@gmail.com"
# Setup apt to be happy with no console input
ENV DEBIAN_FRONTEND noninteractive
# Install dependencies and Download
RUN apt-get update && apt-get install -y \
udev \
locales \
software-properties-common \
file \
curl \
git \
sudo \
wget \
gdebi-core \
vim \
psmisc \
tzdata \
libxml2-dev \
libcairo2-dev \
libgit2-dev \
tk-table \
libcurl4-gnutls-dev \
libssl-dev \
libxt-dev \
supervisor && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Prevent bugging us later about timezones
RUN ln -fs /usr/share/zoneinfo/Asia/Seoul /etc/localtime && dpkg-reconfigure --frontend noninteractive tzdata
# Use UTF-8
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
# Update R -latest version
RUN echo "deb http://cran.rstudio.com/bin/linux/ubuntu bionic-cran35/" | sudo tee -a /etc/apt/sources.list && \
gpg --keyserver keyserver.ubuntu.com --recv-key E084DAB9 && \
gpg -a --export E084DAB9 | sudo apt-key add - && \
apt-get update && \
apt-get install -y r-base r-base-dev
# Install Rstudio-server
ARG RSTUDIO_VERSION
RUN RSTUDIO_LATEST=$(wget --no-check-certificate -qO- https://s3.amazonaws.com/rstudio-server/current.ver) && \
[ -z "$RSTUDIO_VERSION" ] && RSTUDIO_VERSION=$RSTUDIO_LATEST || true && \
wget -q http://download2.rstudio.org/rstudio-server-${RSTUDIO_VERSION}-amd64.deb && \
dpkg -i rstudio-server-${RSTUDIO_VERSION}-amd64.deb && \
rm rstudio-server-*-amd64.deb
# Install Shiny server
RUN wget --no-verbose https://s3.amazonaws.com/rstudio-shiny-server-os-build/ubuntu-14.04/x86_64/VERSION -O "version.txt" && \
VERSION=$(cat version.txt) && \
wget --no-verbose "https://s3.amazonaws.com/rstudio-shiny-server-os-build/ubuntu-14.04/x86_64/shiny-server-$VERSION-amd64.deb" -O ss-latest.deb && \
gdebi -n ss-latest.deb && \
rm -f version.txt ss-latest.deb && \
R -e "install.packages(c('shiny', 'rmarkdown', 'DT', 'data.table', 'ggplot2', 'devtools', 'epiDisplay', 'tableone', 'svglite', 'plotROC', 'pROC', 'labelled', 'geepack', 'lme4', 'PredictABEL', 'shinythemes', 'maxstat', 'manhattanly', 'Cairo', 'future', 'promises', 'GGally', 'fst', 'blogdown', 'metafor', 'roxygen2'), repos='https://cran.rstudio.com/')" && \
R -e "devtools::install_github(c('jinseob2kim/jstable', 'jinseob2kim/jskm', 'emitanaka/shinycustomloader', 'Appsilon/shiny.i18n', 'metrumresearchgroup/sinew'))"
## User setting
COPY ini.sh /etc/ini.sh
## Github
RUN git config --system credential.helper 'cache --timeout=3600' && \
git config --system push.default simple
## Multiple run
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN mkdir -p /var/log/supervisor \
&& chmod 777 -R /var/log/supervisor
EXPOSE 8787 3838
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] 코드: github
프로그램: Docker hub
서버: 클라우드(AWS, Azure, Digital ocean) or 자체 서버
docker run --rm -d \
-p 3838:3838 -p 8787:8787 \
-e USER=js -e PASSWORD=js -e ROOT=TRUE\
jinseob2kim/docker-rshiny–rm -d : 실행 중지시 지움(–rm), 백그라운드 실행(-d)
호스트의 3838포트를 이미지의 3838포트(shiny server)와 연결, 8787포트를 8787포트(rstudio server)에 연결
유저 생성 : js/js, 루트 권한
Docker hub 주소 : jinseob2kim/docker-rshiny
로컬 컴퓨터 - http://localhost:8787, http://localhost:3838 로 접속. 서버 - Your IP:8787, Your IP:3838
서버의 종류와 갯수에 구애받지 않는 마이크로서비스 아키텍처(microservice architecture)…
Server orchestration: 지휘자가 오케스트라 연주하듯이
여러대의 서버를 묶어 마치 하나의 서버를 이용하는 것처럼 느낌.
Docker 에 내장되어 별다른 설치 필요없음.
비슷한 프로그램으로 구글의 Kubernetes
Docker가 설치된 2개 서버: manager, worker node
In manager node
In worker node
TCP port 2377 for cluster management & raft sync communications
TCP and UDP port 7946 for control plane gossip discovery communication between all nodes
UDP port 4789 for data plane VXLAN overlay network traffic
IP Protocol 50 (ESP) if you plan on using overlay network with the encryption option
연결 가능한 서버끼리만 묶을 수 있다.
AWS끼리(O), Azure끼리(O), Digitalocean끼리(O)
AWS와 Azure(X), AWS와 Digitalocean(X)
AWS(Azure, Digitalocean)와 자체서버(X)
자체 이미지 docker-rshiny
docker service create \
--publish 8787:8787 \
--publish 3838:3838 \
-e USER=js -e PASSWORD=js -e ROOT=TRUE \
--name rshiny \
jinseob2kim/docker-rshinyrstudio server: Both IP:8787, shiny server: Both IP:3838추가: tensorflow docker 실행
docker service scale 명령어 이용, 여러 서버에 이미지 설치.
다시 줄이기
여러 대의 서버(docker가 설치된)를 로컬 컴퓨터에서 한 번에 관리할 수 있음.
2376 port 오픈 필요.
export DIGITALOCEAN_ACCESS_TOKEN=<YOUR_DIGITALOCEAN_ACCESS_TOKEN>
export DIGITALOCEAN_IMAGE="ubuntu-18-04-x64"
export DIGITALOCEAN_REGION="sgp1"
echo "### Creating manager nodes ..."
for c in {1..1} ; do
docker-machine create \
--driver digitalocean \
--digitalocean-access-token $DIGITALOCEAN_ACCESS_TOKEN \
--digitalocean-image $DIGITALOCEAN_IMAGE \
--digitalocean-region $DIGITALOCEAN_REGION \
--digitalocean-size "s-2vcpu-4gb" \
manager$c &&\
docker-machine ssh manager$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
doneexport AWS_ACCESS_KEY_ID=<YOUR_AWS_ACEESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>
export AWS_INSTANCE_TYPE="t2.micro"
export AWS_INSTANCE_REGION="ap-northeast-2"
export AWS_SECURITY_GROUP="launch-wizard-2"
export AWS_VPC_ID=<YOUR_AWS_VPC_ID>
export AWS_ZONE=c
for c in {1..1} ; do
docker-machine create \
--driver amazonec2 \
--amazonec2-access-key $AWS_ACCESS_KEY_ID \
--amazonec2-secret-key $AWS_SECRET_ACCESS_KEY \
--amazonec2-region $AWS_INSTANCE_REGION \
--amazonec2-vpc-id $AWS_VPC_ID \
--amazonec2-open-port 3838 \
--amazonec2-open-port 8787 \
--amazonec2-open-port 8000 \
--amazonec2-open-port 8080 \
--amazonec2-open-port 2377 \
--amazonec2-open-port 7946 \
--amazonec2-open-port 7946/udp \
--amazonec2-open-port 4789 \
--amazonec2-open-port 4789/udp \
--amazonec2-open-port 8888 \
--amazonec2-open-port 80 \
--amazonec2-open-port 443 \
manager$c && \
docker-machine ssh manager$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
doneexport sub=<YOUR_AZURE_SUBSCRIPTION_VALUE>
for c in {1..1} ; do
docker-machine create \
--driver azure \
--azure-location "koreacentral" \
--azure-size Standard_B1s \
--azure-subscription-id $sub \
--azure-open-port 3838 \
--azure-open-port 8787 \
--azure-open-port 8000 \
--azure-open-port 8080 \
--azure-open-port 2377 \
--azure-open-port 7946 \
--azure-open-port 7946/udp \
--azure-open-port 4789 \
--azure-open-port 4789/udp \
--azure-open-port 8888 \
--azure-open-port 80 \
--azure-open-port 443 \
manager$c && \
docker-machine ssh manager$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
doneexport DIGITALOCEAN_SIZE="s-1vcpu-1gb"
echo "### Creating worker nodes ..."
for c in {1..1} ; do
docker-machine create \
--driver digitalocean \
--digitalocean-access-token $DIGITALOCEAN_ACCESS_TOKEN \
--digitalocean-image $DIGITALOCEAN_IMAGE \
--digitalocean-region $DIGITALOCEAN_REGION \
--digitalocean-size $DIGITALOCEAN_SIZE \
worker$c && \
docker-machine ssh worker$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
donemanager1 과 worker1 노드를 docker swarm를 활용하여 묶자.
# Get IP from leader node
leader_ip=$(docker-machine ip manager1)
# Init Docker Swarm mode
echo "### Initializing Swarm mode ..."
eval $(docker-machine env manager1)
docker swarm init --advertise-addr $leader_ip
# Swarm tokens
manager_token=$(docker swarm join-token manager -q)
worker_token=$(docker swarm join-token worker -q)
# Joinig manager nodes
echo "### Joining manager modes ..."
for c in {1..1} ; do
eval $(docker-machine env manager$c)
docker swarm join --token $manager_token $leader_ip:2377
done
# Join worker nodes
echo "### Joining worker modes ..."
for c in {1..1} ; do
eval $(docker-machine env worker$c)
docker swarm join --token $worker_token $leader_ip:2377
done
# Clean Docker client environment
echo "### Cleaning Docker client environment ..."
eval $(docker-machine env -u)/server, /app 으로는 안되나?server.DOMAINNAME, app.DOMAINNAME 은?리버스 프록시(reverse proxy) 프로그램이 필요하다.
/server, /app 가능.서비스 실행해서 포트 추가될 때마다 일일히 주소 적용해줘야..
Docker swarm 과는 더 안좋다.
https 적용 불가능따로 비용을 지불하거나
무료 https 적용 프로그램인 Let’s Encrypt 를 수동으로 적용해야됨.
HTTP 구글 크롬서 퇴출 수순…7월부터 “안전하지 않다” 경고
server.DOMAINNAME, app.DOMAINNAME 불가능.Docker swarm 을 위한 dynamic proxy 프로그램
rstudio server 서비스 추가하면 rstudio.DOMAINNAME 로 자동으로 subdomain 적용. tensorflow 서비스 추가하면 tensorflow.DOMAINNAME 으로 적용.
https 자동 적용됨: Let’s Encrypt 연계
https://ian-says.com/articles/traefik-proxy-docker-lets-encrypt/