JDSS 는 Jaehong Docker Swarm Service 의 준말이다
개발 파일 ) https://github.com/lijahong/Docker_JDSS_Project



오픈소스 컨테이너 레지스트리 도구인 Harbor 를 이용하여 사설 레지스트리를 구현하자
참조 ) https://judo0179.tistory.com/72
참조 ) https://smoh.tistory.com/328?category=736086
# 설치 파일 다운로드
wget https://github.com/goharbor/harbor/releases/download/v1.10.1/harbor-offline-installer-v1.10.1.tgz
# 설치 파일 압축 해제
tar -xvf harbor-offline-installer-v1.10.1.tgz
# harbor 설정 - http 를 사용하므로 https 부분을 주석처리 하였다
cd harbor
vi harbor.yml
# 설치
sudo ./install.sh



metric Api 를 이용한 Pull 방식을 사용한다
# node-exporter 배포
docker run -d -p 9100:9100 --restart always --name node-exporter prom/node-exporter:v0.14.0
# cAdvisor 배포
docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --volume=/dev/disk/:/dev/disk:ro --publish=8080:8080 --detach=true --name=cadvisor --restart always google/cadvisor:v0.27.0
global:
scrape_interval: 15s
external_labels:
monitor: 'cadvisor-monitor'
scrape_configs:
- job_name: 'cadvisor-exporter'
static_configs:
- targets: ['211.183.3.101:8080', '211.183.3.102:8080']
global:
scrape_interval: 15s
external_labels:
monitor: 'node-monitor'
scrape_configs:
- job_name: 'node-exporter'
static_configs:
- targets: ['211.183.3.101:9100', '211.183.3.102:9100']
# node-exporter 의 Data 를 수집하는 prometheus 배포
docker run -d --name prometheus_node --restart always -h prometheus_node -v $(pwd)/prome-node.yml:/etc/prometheus/prometheus.yml prom/prometheus:v1.7.0 -config.file=/etc/prometheus/prometheus.yml
# cAdvisor 의 Data 를 수집하는 prometheus 배포
docker run -d --name prometheus_cadvisor --restart always -h prometheus_cadvisor -v $(pwd)/prome-cad.yml:/etc/prometheus/prometheus.yml prom/prometheus:v1.7.0 -config.file=/etc/prometheus/prometheus.yml
# Grafana 배포
docker run -d --name grafana -h grafana --restart always -e GF_SECURITY_ADMIN_PASSWORD=test123 -p 3000:3000 --link prometheus_cadvisor:prome_cad --link prometheus_node:prome_node grafana/grafana:4.4.3
Grafana 는 API 방식을 통해 Prometheus 의 Data 를 가져온다. 따라서 Grafana 에서 Prometheus 에서 수집한 Data 를 모니터링하려면, Prometheus 의 Ip 주소를 입력해줘야 한다

- Name 은 식별만 할 수 있으면 된다
- Type 은 Prometheus 로 설정해주자
- Http settings 에는 접속하고자 하는 Prometheus Container 의 주소를 입력해주자. 우리는 Grafana 배포시 link 를 해주었으므로 Prometheus Container 의 이름을 입력해주면, 자동으로 자신의 Hosts 파일에서 해당 Ip 주소를 찾아준다. Port 는 Prometheus Default Port 인 9090 Port 를 사용한다

Data Source 를 통해 가져온 Data 를 시각화 해줘야 한다. 이를 Dashboard 를 통해 Customize 및 조회가 가능하다

- 위와 같이 다른 사용자가 제공하는 json 파일을 Import 해줄 수 있다
- Name 과 해당 Dashboard 에 대응하는 Data Source 를 선택해주자



- 전체 파일은 Git 에 업로드하였다. 참조 ) https://github.com/lijahong/Docker_JDSS_Project


FROM httpd
ADD index.html /usr/local/apache2/htdocs/index.html
- httpd 에 사용자 지정 Web page 를 넣어주게 구현
FROM nginx
ADD index.html /usr/share/nginx/html/index.html
- nginx 도 동일하게 Build 하도록 구현한다

version: '3.7'
services:
httpd:
image: 211.183.3.103:80/jdss_repo/setname_httpd:v1
deploy:
replicas: set_replicas
placement:
constraints: [node.role == worker]
restart_policy:
condition: on-failure
max_attempts: 2
environment:
SERVICE_PORTS: 80
networks:
- set_network
proxy:
image: dockercloud/haproxy
depends_on:
- httpd
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports: # attach on ingress network 1
- 80
networks: # attach on web network 2 - nginx connect
- set_network
deploy:
mode: global
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
max_attempts: 2
networks:
set_network:
external: true
- worker node 에는 httpd, manager node 에는 HAproxy 를 배포하여, manager node 의 HAproxy 에 접속시 생성되는 Overlay Network 를 통해 httpd 에 접속하게 해준다
version: '3.7'
services:
nginx:
image: 211.183.3.103:80/jdss_repo/setname_nginx:v1
deploy:
replicas: set_replicas
placement:
constraints: [node.role == worker]
restart_policy:
condition: on-failure
max_attempts: 2
environment:
SERVICE_PORTS: 80
networks:
- set_network
proxy:
image: dockercloud/haproxy
depends_on:
- nginx
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports: # attach on ingress network 1
- 80
networks: # attach on web network 2 - nginx connect
- set_network
deploy:
mode: global
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
max_attempts: 2
networks:
set_network:
external: true
- worker node 에는 nginx, manager node 에는 HAproxy 를 배포하여, manager node 의 HAproxy 에 접속시 생성되는 Overlay Network 를 통해 nginx 에 접속하게 해준다
version: '3.7'
services:
wordpress:
image: wordpress
networks:
- setname
depends_on:
- db
environment:
WORDPRESS_DB_PASSWORD: test123
WORDPRESS_DB_USER: root
WORDPRESS_DB_NAME: testdb
WORDPRESS_DB_HOST: db:3306
SERVICE_PORTS: 80
deploy:
replicas: set_replicas
placement:
constraints: [node.role == worker]
restart_policy:
condition: on-failure
max_attempts: 2
db:
image: mysql:5.7
networks:
- setname
volumes:
- dbvol:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: test123
MYSQL_DATABASE: testdb
deploy:
mode: global
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
max_attempts: 2
proxy:
image: dockercloud/haproxy
depends_on:
- wordpress
- db
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports: # attach on ingress network 1
- 80
networks: # attach on web network 2 - nginx connect
- setname
deploy:
mode: global
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
max_attempts: 2
networks:
setname:
external: true
volumes:
dbvol:
- manager node 에 HAproxy 와 mysql 을 설치해준다. manager node 의 HAproxy 에 접속시 생성되는 Overlay Network 를 통해 wordpress 에 접속하게 한다
- manager node 의 Mysql 과 worker node 들의 wordpress 를 연결하여 HAproxy 에서 어떤 wordpress 에 접속하게 해도, Data 를 동일하게 해준다


- nginx & httpd 배포시 makeweb.sh 를 실행하며, nginx 와 httpd 에 따라 각각의 createstack.sh 가 실행된다
- wordpress 배포시 makewp.sh 가 실행되어 Stack 을 배포한다
- main 에는 주 Dialog 와 STACK 조회 & 삭제 & 생성 이 정의되어 있다
- nginx & httpd & wordexpress 는 각각 사용자에게 입력받는 Data 가 다르므로, Dialog 후반부는 makeweb.sh 와 makewp.sh 에 구현되어 있다
#!/bin/bash
#create temp file
selectmenu=$(mktemp -t test.XXX) #menu select - main
stacklist=$(mktemp -t test.XXX) #contain stack ls list - showlist
stackselect=$(mktemp -t test.XXX) #selected stack - showlist
stackname_temp=$(mktemp -t test.XXX) #STACK NAME - CREATE
stackservice_temp=$(mktemp -t test.XXX) #Service name - CREATE
stackreplicas_temp=$(mktemp -t test.XXX) #REPLICAS NUMBER - CREATE
#show stack & service list
showlist(){
#get stack ls list by file
count=$[ 0 + 1 ]
echo "" > $stacklist
docker stack ls | while read line
do
#except column name
if [ $count -gt 1 ]
then
name=$(echo $line | grep -v NAME | gawk {'print $1'})
service=$(echo $line | grep -v NAME | gawk {'print "SERVICE=" $2 "==ORCHESTRATOR=" $3'})
#modify for radiolist
echo "$name '$service' OFF" >> $stacklist
fi
count=$[ $count + 1 ]
done
#get variable for stack list file
stackstr=$(cat $stacklist)
#radio list for stack
dialog --title "STACK LIST" --radiolist "CHOOSE STACK FOR FIND SERVICES" 15 50 0 $stackstr 2>$stackselect
if [ $? -eq 0 ] #if you choose ok
then
#if user do wrong usement -> select file is empty
if [ ! -s $stackselect ]
then
dialog --title "WRONG USEMENT" --msgbox "Input Method is space -> enter" 8 20
else
#get service list for choose stack
servicelist=$(mktemp -t test.XXX) #get docker stack ps result file
stackname=$(cat $stackselect)
docker stack ps $stackname > $servicelist
dialog --title "$stackname Service" --textbox $servicelist 0 0
if [ $? -eq 0 ]
then
network_ip=$(docker network inspect $stackname | grep Subnet | gawk {'print $2'} | tr -d ',' | tr -d '"')
network_subnet=$(docker network inspect $stackname | grep Gateway | gawk {'print $2'} | tr -d '"')
network_scope=$(docker network inspect $stackname -f "{{.Scope}}")
network_driver=$(docker network inspect $stackname -f "{{.Driver}}")
host_port=$(docker service inspect ${stackname}_proxy -f "{{.Endpoint.Ports"}} | gawk '{print $4}')
I
dialog --title "STACK INFORMATION" --msgbox "$stackname Information \n Network Ip : $network_ip \n Network Subnet : $network_subnet \n Network_Scope : $network_scope \n Network Driver : $network_driver \n Access Port : $host_port" 10 50
fi
rm -rf $servicelist 2> /dev/null
fi
fi
}
#delect stack function
deletestack(){
count=$[ 0 + 1 ]
echo "" > $stacklist
docker stack ls | while read line
do
#except column name
if [ $count -gt 1 ]
then
name=$(echo $line | grep -v NAME | gawk {'print $1'})
service=$(echo $line | grep -v NAME | gawk {'print "SERVICE=" $2 "==ORCHESTRATOR=" $3'})
#modify for radiolist
echo "$name '$service' OFF" >> $stacklist
fi
count=$[ $count + 1 ]
done
#get variable for stack list file
stackstr=$(cat $stacklist)
#radio list for stack
dialog --title "DELETE STACK" --radiolist "CHOOSE STACK FOR DELETE" 15 50 0 $stackstr 2>$stackselect
if [ $? -eq 0 ] #if you choose ok
then
#if user do wrong usement -> select file is empty
if [ ! -s $stackselect ]
then
dialog --title "WRONG USEMENT" --msgbox "Input Method is space -> enter" 8 20
else
#SHOW CONFIRM MENU FOR DELETE -> file content is exist
stackname=$(cat $stackselect)
dialog --title "Delete Confirm" --yesno "ARE YOU SURE FOR DELETE STACK - $stackname ?" 10 50
#IF YOU CHOOSE OK
if [ $? -eq 0 ]
then
#Delete selected stack & overlay network
docker stack rm $stackname > /dev/null
docker network rm $stackname > /dev/null
#if service is wordpress, then remove volume too
iswordpress=$(docker volume ls | grep $stackname)
if [ -n "$iswordpress" ]
then
dialog --title "Volume Delete" --msgbox "Volume for Mysql will remove" 10 50
sleep 10
docker volume rm -f ${stackname}_dbvol
fi
if [ $? -eq 0 ]
#if delect is complete well, show confirm message
then
dialog --title "Delete Confirm" --msgbox "$stackname STACK DELETE COMPLETE" 10 50
fi
fi
fi
fi
}
stackcreate(){
dialog --title "STACK NAME" --inputbox "Pls Input Stack Name : " 10 50 2>$stackname_temp
stack_name=$(cat $stackname_temp)
if [[ $? -eq 0 ]] && [[ -n $stack_name ]]
then
dialog --title "Select Service" --radiolist "Pls Select Deploy Service" 10 80 0 "nginx" "WEB Service by nginx & HAproxy" ON "httpd" "WEB Service by httpd & HAproxy" OFF "wordpress" "Blog Service by wordpress & mysql & HAproxy" OFF 2> $stackservice_temp
choice=$(cat $stackservice_temp) #user choice service
if [[ $? -eq 0 ]] && [[ -n $choice ]]
then
dialog --title "REPLICAS" --radiolist "Pls Select Replicas Number" 10 50 0 1 "replicas=1" ON 2 "replicas=2" OFF 3 "replicas=3" OFF 4 "replicas=4" OFF 5 "replicas=5" OFF 6 "replicas=6" OFF 2>$stackreplicas_temp
stack_replicas=$(cat $stackreplicas_temp)
if [[ $? -eq 0 ]] && [[ -n $stack_replicas ]]
then
#if user choice web service nginx or http
if [[ "$choice" == "nginx" ]] || [[ "$choice" == "httpd" ]]
then
/home/rapa/jdss/scripts/makeweb.sh $stack_name $stack_replicas $choice
elif [[ "$choice" == "wordpress" ]]
then
/home/rapa/jdss/scripts/makewp.sh $stack_name $stack_replicas $choice
fi
fi
fi
fi
}
#menu start
while [ 1 ]
do
# 메인메뉴 출력하기
dialog --menu "JDSS SERIVE" 20 40 8 1 "SHOW LIST" 2 "DELETE STACK" 3 "CREATE STACK" 0 "종료" 2> $selectmenu
# 종료코드 확인하여 cancel 이면 프로그램 종료
if [ $? -eq 1 ]
then
break
fi
selection=$(cat $selectmenu)
case $selection in
1)
showlist ;;
2)
deletestack ;;
3)
stackcreate ;;
0)
break ;;
*)
dialog --msgbox "WRONG SELECT MENU - Pls SELECT EXIST MENU NUMBER" 10 40
esac
done
#delect temp file
rm -rf $selectmenu 2> /dev/null
rm -rf $stacklist 2> /dev/null
rm -rf $stackselect 2> /dev/null
rm -rf $stackname_temp 2> /dev/null
rm -rf $stackservice_temp 2> /dev/null
rm -rf $stackreplicas_temp 2> /dev/null
- 삭제할 STACK 이 wordpress Service 라면, 해당 Mysql 과 mount 된 Manager node 의 Volume 도 삭제해준다
- STACK 에서 사용하는 Overlay Network 도 삭제한다
- 생성할 STACK 이름 & Replicas & 생성할 Service 종류를 선택하여 Service 종류 별로 해당 생성 Script 파일에 매개변수로 사용자 입력 Data 를 넘겨주어 실행한다
#!/bin/bash
# $1 is name $2 is replicas $3 is choice
#create temp file
gitaddr_temp=$(mktemp -t test.XXX)
#input git addr
dialog --title "Git Repository" --inputbox "Pls Input Git Repositoy address, where index.html exist" 10 50 2>$gitaddr_temp
git_addr=$(cat $gitaddr_temp)
if [[ $? -eq 0 ]] && [[ -n $git_addr ]]
then
#last confirm before deploy
dialog --title "Information Confirmation" --yesno "Pls Confirm your Selection before deploy\n Stack name : $1 \n Service : $3 \n Stack replicas : $2 \n git address : $git_addr " 10 70
if [ $? -eq 0 ]
then
if [[ "$3" == "nginx" ]]
then
/home/rapa/jdss/scripts/makenginx/createstack.sh $1 $2 $git_addr > /dev/null
elif [[ "$3" == "httpd" ]]
then
/home/rapa/jdss/scripts/makehttpd/createstack.sh $1 $2 $git_addr > /dev/null
fi
if [ $? -eq 0 ]
then
#get STACK INFORMATION
network_ip=$(docker network inspect $1 | grep Subnet | gawk {'print $2'} | tr -d ',' | tr -d '"')
network_subnet=$(docker network inspect $1 | grep Gateway | gawk {'print $2'} | tr -d '"')
network_scope=$(docker network inspect $1 -f "{{.Scope}}")
network_driver=$(docker network inspect $1 -f "{{.Driver}}")
host_port=$(docker service inspect ${1}_proxy -f "{{.Endpoint.Ports"}} | gawk '{print $4}')
I
#show STACK INFORMATION
dialog --title "WORK COMPLETE" --msgbox "STACK DEPLOY COMPLETE\n Network Ip : $network_ip \n Network Subnet : $network_subnet \n Network_Scope : $network_scope \n Network Driver : $network_driver \n Access Port : $host_port" 10 80
fi
fi
fi
#delete Temp file
rm -rf $gitaddr_temp 2> /dev/null
#!/bin/bash
# $1 is name $2 is replicas
#last confirm before deploy
dialog --title "Information Confirmation" --yesno "Pls Confirm your Selection before deploy\n Stack name : $1 \n Service : $3 \n Stack replicas : $2 \n " 10 50
if [ $? -eq 0 ]
then
cp /home/rapa/jdss/ymls/wp_service_base.yml /home/rapa/jdss/ymls/wp_service.yml
sed -i "s/setname/$1/g" /home/rapa/jdss/ymls/wp_service.yml
sed -i "s/set_replicas/$2/" /home/rapa/jdss/ymls/wp_service.yml
if [ $? -eq 0 ]
then
#docker network create - overlay
docker network create -d overlay $1 > /dev/null
#docker stack deploy
docker stack deploy -c /home/rapa/jdss/ymls/wp_service.yml --with-registry-auth $1 > /dev/null
#notify mysql information
dialog --title "DB INFORMATION" --msgbox " USER : root \n PASSWORD : test123 \n DB NAME : testdb \n " 10 40
#Delete created file and image
rm -rf /home/rapa/jdss/ymls/wp_service.yml
if [ $? -eq 0 ]
then
#get STACK INFORMATION
network_ip=$(docker network inspect $1 | grep Subnet | gawk {'print $2'} | tr -d ',' | tr -d '"')
network_subnet=$(docker network inspect $1 | grep Gateway | gawk {'print $2'} | tr -d '"')
network_scope=$(docker network inspect $1 -f "{{.Scope}}")
network_driver=$(docker network inspect $1 -f "{{.Driver}}")
host_port=$(docker service inspect ${1}_proxy -f "{{.Endpoint.Ports"}} | gawk '{print $4}')
I
#show STACK INFORMATION
dialog --title "WORK COMPLETE" --msgbox "STACK DEPLOY COMPLETE\n Network Ip : $network_ip \n Network Subnet : $network_subnet \n Network_Scope : $network_scope \n Network Driver : $network_driver \n Access Port : $host_port" 10 80
fi
fi
fi
#!/bin/bash
#creake dir for git clone and get index.html from git
mkdir /home/rapa/jdss/webpage/$1
git clone --quiet $3 /home/rapa/jdss/webpage/$1/ > /dev/null
#it goes to dir, where Dockerfile exist
cp /home/rapa/jdss/webpage/$1/index2.html /home/rapa/jdss/dockerfile_dir/httpd/index.html
#login
docker login 211.183.3.103:80 -uadmin -ptest123 > /dev/null 2>&1
#image build and push
docker build -t 211.183.3.103:80/jdss_repo/${1}_httpd:v1 /home/rapa/jdss/dockerfile_dir/httpd/.
docker push 211.183.3.103:80/jdss_repo/${1}_httpd:v1
#modify stack yml file
cp /home/rapa/jdss/ymls/httpd_service_base.yml /home/rapa/jdss/ymls/httpd_service.yml
sed -i "s/setname/$1/" /home/rapa/jdss/ymls/httpd_service.yml
sed -i "s/set_network/$1/g" /home/rapa/jdss/ymls/httpd_service.yml
sed -i "s/set_replicas/$2/" /home/rapa/jdss/ymls/httpd_service.yml
if [ $? -eq 0 ]
then
#docker network create - overlay
docker network create -d overlay $1
#docker stack deploy
docker stack deploy -c /home/rapa/jdss/ymls/httpd_service.yml --with-registry-auth $1
if [ $? -eq 0 ]
then
#Delete created file and image
rm -rf /home/rapa/jdss/ymls/httpd_service.yml
rm -rf /home/rapa/jdss/webpage/$1
rm -rf /home/rapa/jdss/dockerfile_dir/httpd/index.html
docker image rm 211.183.3.103:80/jdss_repo/${1}_httpd:v1
fi
fi
#!/bin/bash
#creake dir for git clone and get index.html from git
mkdir /home/rapa/jdss/webpage/$1
git clone --quiet $3 /home/rapa/jdss/webpage/$1/ > /dev/null
#it goes to dir, where Dockerfile exist
cp /home/rapa/jdss/webpage/$1/index.html /home/rapa/jdss/dockerfile_dir/nginx/index.html
#login
docker login 211.183.3.103:80 -uadmin -ptest123 > /dev/null 2>&1
#image build and push
docker build -t 211.183.3.103:80/jdss_repo/${1}_nginx:v1 /home/rapa/jdss/dockerfile_dir/nginx/.
docker push 211.183.3.103:80/jdss_repo/${1}_nginx:v1
#modify stack yml file
cp /home/rapa/jdss/ymls/nginx_service_base.yml /home/rapa/jdss/ymls/nginx_service.yml
sed -i "s/setname/$1/" /home/rapa/jdss/ymls/nginx_service.yml
sed -i "s/set_network/$1/g" /home/rapa/jdss/ymls/nginx_service.yml
sed -i "s/set_replicas/$2/" /home/rapa/jdss/ymls/nginx_service.yml
if [ $? -eq 0 ]
then
#docker network create - overlay
docker network create -d overlay $1
#docker stack deploy
docker stack deploy -c /home/rapa/jdss/ymls/nginx_service.yml --with-registry-auth $1
if [ $? -eq 0 ]
then
#Delete created file and image
rm -rf /home/rapa/jdss/ymls/nginx_service.yml
rm -rf /home/rapa/jdss/webpage/$1
rm -rf /home/rapa/jdss/dockerfile_dir/nginx/index.html
docker image rm 211.183.3.103:80/jdss_repo/${1}_nginx:v1
fi
fi
2 일이라는 시간 동안 Project 를 완수하였다. 이 개발 과정이 순탄했다고 하면 거짓말이다. 마지막으로 개발 과정 중 기억나는 Error 에 대해 알아보자
- SERVICE_PORTS : 80 을 추가하여, HAproxy 가 wordpress 의 80 번 Port 를 통해 연결되게 설정하였다
#if service is wordpress, then remove volume too
iswordpress=$(docker volume ls | grep $stackname)
if [ -n "$iswordpress" ]
then
dialog --title "Volume Delete" --msgbox "Volume for Mysql will remove" 10 50
sleep 10
docker volume rm -f ${stackname}_dbvol
fi

1. 위와 같이 $? 의 값을 비교하여 앞선 명령의 결과코드가 0인지 확인하여, ok 를 선택할 때만 다음 단계로 넘어가게 구현했다
2. 이때, 받아온 Data 가 null 인지 비교하여 Data 를 입력했는지를 검사하였다
docker stack deploy -c /home/rapa/jdss/ymls/httpd_service.yml --with-registry-auth $1