start-two-nodes-in-docker.sh 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. ## EMQX can only start with longname (https://erlang.org/doc/reference_manual/distributed.html)
  4. ## The host name part of EMQX's node name has to be static, this means we should either
  5. ## pre-assign static IP for containers, or ensure containers can communiate with each other by name
  6. ## this is why a docker network is created, and the containers's names have a dot.
  7. # ensure dir
  8. cd -P -- "$(dirname -- "$0")/../../"
  9. HAPROXY_PORTS=(-p 18083:18083 -p 1883:1883 -p 8883:8883 -p 8084:8084)
  10. NET='emqx.io'
  11. NODE1="node1.$NET"
  12. NODE2="node2.$NET"
  13. COOKIE='this-is-a-secret'
  14. IPV6=0
  15. DASHBOARD_NODES='both'
  16. cleanup() {
  17. docker rm -f haproxy >/dev/null 2>&1 || true
  18. docker rm -f "$NODE1" >/dev/null 2>&1 || true
  19. docker rm -f "$NODE2" >/dev/null 2>&1 || true
  20. docker network rm "$NET" >/dev/null 2>&1 || true
  21. }
  22. show_help() {
  23. echo "Usage: $0 [options] EMQX_IMAGE1 [EMQX_IAMGE2]"
  24. echo ""
  25. echo "Specifiy which docker image to run with EMQX_IMAGE1"
  26. echo "EMQX_IMAGE2 is the same as EMQX_IMAGE1 if not set"
  27. echo ""
  28. echo "Options:"
  29. echo " -h, --help: Show this help message and exit."
  30. echo " -P: Add -p options for docker run to expose more HAProxy container ports."
  31. echo " -6: Test with IPv6"
  32. echo " -c: Cleanup: delete docker network, force delete the containers."
  33. echo " -d: '1', '2', or 'both' (defualt = 'both')"
  34. echo " 1: Only put node 1 behind haproxy"
  35. echo " 2: Only put node 2 behind haproxy"
  36. echo " both: This is the default value, which means both nodes serve dashboard"
  37. echo " This is often needed for tests which want to check one dashboard version"
  38. echo " when starting two different versions of EMQX."
  39. }
  40. while getopts "hc6P:d:" opt
  41. do
  42. case $opt in
  43. # -P option is treated similarly to docker run -P:
  44. # publish ports to random available host ports
  45. P) HAPROXY_PORTS=(-p 18083 -p 1883 -p 8883 -p 8084);;
  46. c) cleanup; exit 0;;
  47. h) show_help; exit 0;;
  48. 6) IPV6=1;;
  49. d) DASHBOARD_NODES="$OPTARG";;
  50. *) ;;
  51. esac
  52. done
  53. shift $((OPTIND - 1))
  54. IMAGE1="${1:-}"
  55. IMAGE2="${2:-${IMAGE1}}"
  56. DASHBOARD_BACKEND1="server emqx-1 $NODE1:18083"
  57. DASHBOARD_BACKEND2="server emqx-2 $NODE2:18083"
  58. case "${DASHBOARD_NODES}" in
  59. 1)
  60. DASHBOARD_BACKEND2=""
  61. ;;
  62. 2)
  63. DASHBOARD_BACKEND1=""
  64. ;;
  65. both)
  66. ;;
  67. esac
  68. if [ -z "${IMAGE1:-}" ] || [ -z "${IMAGE2:-}" ]; then
  69. show_help
  70. exit 1
  71. fi
  72. cleanup
  73. if [ ${IPV6} = 1 ]; then
  74. docker network create --ipv6 --subnet 2001:0DB8::/112 "$NET"
  75. RPC_ADDRESS="::"
  76. PROTO_DIST='inet6_tls'
  77. else
  78. docker network create "$NET"
  79. RPC_ADDRESS="0.0.0.0"
  80. PROTO_DIST='inet_tls'
  81. fi
  82. docker run -d -t --restart=always --name "$NODE1" \
  83. --net "$NET" \
  84. -e EMQX_LOG__CONSOLE_HANDLER__LEVEL=debug \
  85. -e EMQX_NODE_NAME="emqx@$NODE1" \
  86. -e EMQX_NODE_COOKIE="$COOKIE" \
  87. -e EMQX_CLUSTER__PROTO_DIST="${PROTO_DIST}" \
  88. -e EMQX_RPC__LISTEN_ADDRESS="${RPC_ADDRESS}" \
  89. -e EMQX_RPC__IPV6_ONLY="true" \
  90. -e EMQX_listeners__ssl__default__enable=false \
  91. -e EMQX_listeners__wss__default__enable=false \
  92. -e EMQX_listeners__tcp__default__proxy_protocol=true \
  93. -e EMQX_listeners__ws__default__proxy_protocol=true \
  94. "$IMAGE1"
  95. docker run -d -t --restart=always --name "$NODE2" \
  96. --net "$NET" \
  97. -e EMQX_LOG__CONSOLE_HANDLER__LEVEL=debug \
  98. -e EMQX_NODE_NAME="emqx@$NODE2" \
  99. -e EMQX_NODE_COOKIE="$COOKIE" \
  100. -e EMQX_CLUSTER__PROTO_DIST="${PROTO_DIST}" \
  101. -e EMQX_RPC__LISTEN_ADDRESS="${RPC_ADDRESS}" \
  102. -e EMQX_RPC__IPV6_ONLY="true" \
  103. -e EMQX_listeners__ssl__default__enable=false \
  104. -e EMQX_listeners__wss__default__enable=false \
  105. -e EMQX_listeners__tcp__default__proxy_protocol=true \
  106. -e EMQX_listeners__ws__default__proxy_protocol=true \
  107. "$IMAGE2"
  108. mkdir -p tmp
  109. cat <<EOF > tmp/haproxy.cfg
  110. ##----------------------------------------------------------------
  111. ## global 2021/04/05
  112. ##----------------------------------------------------------------
  113. global
  114. log stdout format raw daemon debug
  115. # Replace 1024000 with deployment connections
  116. maxconn 102400
  117. nbproc 1
  118. nbthread 2
  119. cpu-map auto:1/1-2 0-1
  120. tune.ssl.default-dh-param 2048
  121. ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP
  122. # Enable the HAProxy Runtime API
  123. # e.g. echo "show table emqx_tcp_back" | sudo socat stdio tcp4-connect:172.100.239.4:9999
  124. stats socket :9999 level admin expose-fd listeners
  125. ##----------------------------------------------------------------
  126. ## defaults
  127. ##----------------------------------------------------------------
  128. defaults
  129. log global
  130. mode tcp
  131. option tcplog
  132. # Replace 1024000 with deployment connections
  133. maxconn 102400
  134. timeout connect 30000
  135. timeout client 600s
  136. timeout server 600s
  137. ##----------------------------------------------------------------
  138. ## API
  139. ##----------------------------------------------------------------
  140. frontend emqx_dashboard
  141. mode tcp
  142. option tcplog
  143. bind *:18083
  144. default_backend emqx_dashboard_back
  145. backend emqx_dashboard_back
  146. # Must use a consistent dispatch when EMQX is running on different versions
  147. # because the js files for the dashboard is chunked, having the backends sharing
  148. # load randomly will cause the browser fail to GET some chunks (or get bad chunks if names clash)
  149. balance source
  150. mode http
  151. ${DASHBOARD_BACKEND1}
  152. ${DASHBOARD_BACKEND2}
  153. ##----------------------------------------------------------------
  154. ## MQTT Listeners
  155. ##----------------------------------------------------------------
  156. frontend emqx_tcp
  157. mode tcp
  158. option tcplog
  159. bind *:1883
  160. default_backend emqx_backend_tcp
  161. frontend emqx_ssl
  162. mode tcp
  163. option tcplog
  164. bind *:8883 ssl crt /tmp/emqx.pem ca-file /usr/local/etc/haproxy/certs/cacert.pem verify required no-sslv3
  165. default_backend emqx_backend_tcp
  166. frontend emqx_wss
  167. mode tcp
  168. option tcplog
  169. bind *:8084 ssl crt /tmp/emqx.pem ca-file /usr/local/etc/haproxy/certs/cacert.pem verify required no-sslv3
  170. default_backend emqx_backend_ws
  171. backend emqx_backend_tcp
  172. mode tcp
  173. balance static-rr
  174. server emqx-1 $NODE1:1883 check-send-proxy send-proxy-v2-ssl-cn
  175. server emqx-2 $NODE2:1883 check-send-proxy send-proxy-v2-ssl-cn
  176. backend emqx_backend_ws
  177. mode tcp
  178. balance static-rr
  179. server emqx-1 $NODE1:8083 check-send-proxy send-proxy-v2-ssl-cn
  180. server emqx-2 $NODE2:8083 check-send-proxy send-proxy-v2-ssl-cn
  181. EOF
  182. HAPROXY_IMAGE='ghcr.io/haproxytech/haproxy-docker-alpine:2.4.27'
  183. haproxy_cid=$(docker run -d --name haproxy \
  184. --net "$NET" \
  185. -v "$(pwd)/tmp/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg" \
  186. -v "$(pwd)/apps/emqx/etc/certs:/usr/local/etc/haproxy/certs" \
  187. -w /usr/local/etc/haproxy \
  188. "${HAPROXY_PORTS[@]}" \
  189. "${HAPROXY_IMAGE}" \
  190. sh -c 'set -euo pipefail;
  191. cat certs/cert.pem certs/key.pem > /tmp/emqx.pem;
  192. haproxy -f haproxy.cfg')
  193. haproxy_ssl_port=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "8084/tcp") 0).HostPort}}' "$haproxy_cid")
  194. wait_limit=60
  195. wait_for_emqx() {
  196. container="$1"
  197. wait_limit="$2"
  198. wait_sec=0
  199. while ! docker exec "$container" emqx ctl status; do
  200. wait_sec=$(( wait_sec + 1 ))
  201. if [ $wait_sec -gt "$wait_limit" ]; then
  202. echo "timeout wait for EMQX"
  203. exit 1
  204. fi
  205. echo -n '.'
  206. sleep 1
  207. done
  208. }
  209. wait_for_haproxy() {
  210. wait_sec=0
  211. wait_limit="$1"
  212. set +x
  213. while ! openssl s_client \
  214. -CAfile apps/emqx/etc/certs/cacert.pem \
  215. -cert apps/emqx/etc/certs/cert.pem \
  216. -key apps/emqx/etc/certs/key.pem \
  217. localhost:"$haproxy_ssl_port" </dev/null; do
  218. wait_sec=$(( wait_sec + 1 ))
  219. if [ $wait_sec -gt "$wait_limit" ]; then
  220. echo "timeout wait for haproxy"
  221. exit 1
  222. fi
  223. echo -n '.'
  224. sleep 1
  225. done
  226. }
  227. wait_for_emqx "$NODE1" 30
  228. wait_for_emqx "$NODE2" 30
  229. wait_for_haproxy 10
  230. echo
  231. docker exec $NODE1 emqx ctl cluster join "emqx@$NODE2"