test(CI): add actions for build emqx packages (#3915)

This commit is contained in:
Rory Z 2020-12-15 13:34:11 +08:00 committed by GitHub
parent 66ebcfa301
commit c0f03ca132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 3093 additions and 7 deletions

View File

@ -0,0 +1,12 @@
ARG BUILD_FROM=emqx/build-env:erl22.3-ubuntu20.04
FROM ${BUILD_FROM}
ARG EMQX_NAME=emqx
COPY . /emqx
WORKDIR /emqx
RUN make ${EMQX_NAME}-pkg || cat rebar3.crashdump
RUN /emqx/.ci/build_packages/tests.sh

171
.ci/build_packages/tests.sh Executable file
View File

@ -0,0 +1,171 @@
#!/bin/sh
set -x -e -u
export EMQX_NAME=${EMQX_NAME:-"emqx"}
export PACKAGE_PATH="/emqx/_packages/${EMQX_NAME}"
export RELUP_PACKAGE_PATH="/emqx/relup_packages/${EMQX_NAME}"
# export EMQX_NODE_NAME="emqx-on-$(uname -m)@127.0.0.1"
# export EMQX_NODE_COOKIE=$(date +%s%N)
emqx_prepare(){
mkdir -p ${PACKAGE_PATH}
if [ ! -d "/paho-mqtt-testing" ]; then
git clone -b develop-4.0 https://github.com/emqx/paho.mqtt.testing.git /paho-mqtt-testing
fi
pip3 install pytest
}
emqx_test(){
cd ${PACKAGE_PATH}
for var in $(ls $PACKAGE_PATH/${EMQX_NAME}-*);do
case ${var##*.} in
"zip")
packagename=`basename ${PACKAGE_PATH}/${EMQX_NAME}-*.zip`
unzip -q ${PACKAGE_PATH}/$packagename
sed -i "/zone.external.server_keepalive/c zone.external.server_keepalive = 60" ${PACKAGE_PATH}/emqx/etc/emqx.conf
sed -i "/mqtt.max_topic_alias/c mqtt.max_topic_alias = 10" ${PACKAGE_PATH}/emqx/etc/emqx.conf
if [ ! -z $(echo ${EMQX_DEPS_DEFAULT_VSN#v} | grep -oE "[0-9]+\.[0-9]+(\.[0-9]+)?-(alpha|beta|rc)\.[0-9]") ]; then
if [ ! -d ${PACKAGE_PATH}/emqx/lib/emqx-${EMQX_DEPS_DEFAULT_VSN#v} ] || [ ! -d ${PACKAGE_PATH}/emqx/releases/${EMQX_DEPS_DEFAULT_VSN#v} ] ;then
echo "emqx zip version error"
exit 1
fi
fi
echo "running ${packagename} start"
${PACKAGE_PATH}/emqx/bin/emqx start || tail ${PACKAGE_PATH}/emqx/log/erlang.log.1
IDLE_TIME=0
while [ -z "$(${PACKAGE_PATH}/emqx/bin/emqx_ctl status |grep 'is running'|awk '{print $1}')" ]
do
if [ $IDLE_TIME -gt 10 ]
then
echo "emqx running error"
exit 1
fi
sleep 10
IDLE_TIME=$((IDLE_TIME+1))
done
pytest -v /paho-mqtt-testing/interoperability/test_client/V5/test_connect.py::test_basic
${PACKAGE_PATH}/emqx/bin/emqx stop
echo "running ${packagename} stop"
rm -rf ${PACKAGE_PATH}/emqx
;;
"deb")
packagename=`basename ${PACKAGE_PATH}/${EMQX_NAME}-*.deb`
dpkg -i ${PACKAGE_PATH}/$packagename
if [ $(dpkg -l |grep emqx |awk '{print $1}') != "ii" ]
then
echo "package install error"
exit 1
fi
echo "running ${packagename} start"
running_test
echo "running ${packagename} stop"
dpkg -r ${EMQX_NAME}
if [ $(dpkg -l |grep emqx |awk '{print $1}') != "rc" ]
then
echo "package remove error"
exit 1
fi
dpkg -P ${EMQX_NAME}
if [ ! -z "$(dpkg -l |grep emqx)" ]
then
echo "package uninstall error"
exit 1
fi
;;
"rpm")
packagename=`basename ${PACKAGE_PATH}/${EMQX_NAME}-*.rpm`
rpm -ivh ${PACKAGE_PATH}/$packagename
if [ -z $(rpm -q emqx | grep -o emqx) ];then
echo "package install error"
exit 1
fi
echo "running ${packagename} start"
running_test
echo "running ${packagename} stop"
rpm -e ${EMQX_NAME}
if [ "$(rpm -q emqx)" != "package emqx is not installed" ];then
echo "package uninstall error"
exit 1
fi
;;
esac
done
}
running_test(){
if [ ! -z $(echo ${EMQX_DEPS_DEFAULT_VSN#v} | grep -oE "[0-9]+\.[0-9]+(\.[0-9]+)?-(alpha|beta|rc)\.[0-9]") ]; then
if [ ! -d /usr/lib/emqx/lib/emqx-${EMQX_DEPS_DEFAULT_VSN#v} ] || [ ! -d /usr/lib/emqx/releases/${EMQX_DEPS_DEFAULT_VSN#v} ];then
echo "emqx package version error"
exit 1
fi
fi
sed -i "/zone.external.server_keepalive/c zone.external.server_keepalive = 60" /etc/emqx/emqx.conf
sed -i "/mqtt.max_topic_alias/c mqtt.max_topic_alias = 10" /etc/emqx/emqx.conf
emqx start || tail /var/log/emqx/erlang.log.1
IDLE_TIME=0
while [ -z "$(emqx_ctl status |grep 'is running'|awk '{print $1}')" ]
do
if [ $IDLE_TIME -gt 10 ]
then
echo "emqx running error"
exit 1
fi
sleep 10
IDLE_TIME=$((IDLE_TIME+1))
done
pytest -v /paho-mqtt-testing/interoperability/test_client/V5/test_connect.py::test_basic
emqx stop || kill $(ps -ef |grep emqx | grep beam.smp |awk '{print $2}')
if [ $(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g') = ubuntu ] \
|| [ $(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g') = debian ] \
|| [ $(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g') = raspbian ];then
service emqx start || tail /var/log/emqx/erlang.log.1
IDLE_TIME=0
while [ -z "$(emqx_ctl status |grep 'is running'|awk '{print $1}')" ]
do
if [ $IDLE_TIME -gt 10 ]
then
echo "emqx service error"
exit 1
fi
sleep 10
IDLE_TIME=$((IDLE_TIME+1))
done
service emqx stop
fi
}
relup_test(){
if [ -d ${RELUP_PACKAGE_PATH} ];then
cd ${RELUP_PACKAGE_PATH }
for var in $(ls ${EMQX_NAME}-*-$(uname -m).zip);do
packagename=`basename ${var}`
unzip $packagename
./emqx/bin/emqx start
./emqx/bin/emqx_ctl status
./emqx/bin/emqx versions
cp ${PACKAGE_PATH}/${EMQX_NAME}-*-${EMQX_DEPS_DEFAULT_VSN#v}-$(uname -m).zip ./emqx/releases
./emqx/bin/emqx install ${EMQX_DEPS_DEFAULT_VSN#v}
[ $(./emqx/bin/emqx versions |grep permanent | grep -oE "[0-9].[0-9].[0-9]") = ${EMQX_DEPS_DEFAULT_VSN#v} ] || exit 1
./emqx/bin/emqx_ctl status
./emqx/bin/emqx stop
rm -rf emqx
done
fi
}
emqx_prepare
emqx_test
relup_test

View File

@ -0,0 +1,62 @@
#!/usr/bin/env bash
#
# Author: Stefan Buck
# License: MIT
# https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447
#
#
# This script accepts the following parameters:
#
# * owner
# * repo
# * tag
# * filename
# * github_api_token
#
# Script to upload a release asset using the GitHub API v3.
#
# Example:
#
# upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./build.zip
#
# Check dependencies.
set -e
xargs=$(which gxargs || which xargs)
# Validate settings.
[ "$TRACE" ] && set -x
CONFIG=$@
for line in $CONFIG; do
eval "$line"
done
# Define variables.
GH_API="https://api.github.com"
GH_REPO="$GH_API/repos/$owner/$repo"
GH_TAGS="$GH_REPO/releases/tags/$tag"
AUTH="Authorization: token $github_api_token"
WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
CURL_ARGS="-LJO#"
if [[ "$tag" == 'LATEST' ]]; then
GH_TAGS="$GH_REPO/releases/latest"
fi
# Validate token.
curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; }
# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)
# Get ID of the asset based on given filename.
eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
[ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; }
# Upload asset
# Construct url
GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)"
curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET

47
.ci/paho_tests/Makefile Normal file
View File

@ -0,0 +1,47 @@
## default globals
TARGET ?= emqx/emqx
EMQX_NAME = $(subst emqx/,,$(TARGET))
.PHONY: all
all: test
define wait_emqx
@while [ "$$(docker inspect -f '{{ .State.Health.Status}}' $$(docker ps -a -q -f name=paho_test_emqx1))" != "healthy" ] || [ "$$(docker inspect -f '{{ .State.Health.Status}}' $$(docker ps -a -q -f name=paho_test_emqx2))" != "healthy" ]; do \
if [ $$(docker ps -a -f name=paho_test_emqx -f status=exited -q | wc -l) -ne 0 ]; then \
echo "['$$(date -u +"%Y-%m-%dT%H:%M:%SZ")']:emqx stop"; \
exit; \
else \
echo "['$$(date -u +"%Y-%m-%dT%H:%M:%SZ")']:waiting emqx"; \
sleep 5; \
fi; \
done
endef
.PHONY: create_container
create_container: clean
@docker-compose -p paho_test up -d
$(call wait_emqx)
.PHONY: test
test: create_container
@docker exec -i $$(docker ps -a -q -f name=paho_test_client) sh -c "apk update && apk add git curl \
&& git clone -b $(PAHO_BRANCH) https://github.com/emqx/paho.mqtt.testing.git /paho.mqtt.testing \
&& pip install pytest \
&& pytest -v /paho.mqtt.testing/interoperability/test_client/ --host node1.emqx.io"
@docker-compose -p paho_test down
.PHONY: cluster_test
cluster_test: create_container
@docker exec -i $$(docker ps -a -q -f name=paho_test_client) sh -c "apk update && apk add git curl \
&& git clone -b $(PAHO_BRANCH) https://github.com/emqx/paho.mqtt.testing.git /paho.mqtt.testing \
&& pip install pytest \
&& pytest -v /paho.mqtt.testing/interoperability/test_client/V5/test_connect.py -k test_basic --host node1.emqx.io \
&& pytest -v /paho.mqtt.testing/interoperability/test_cluster --host1 node1.emqx.io --host2 node2.emqx.io"
@docker-compose -p paho_test down
.PHONY: clean
clean:
@if [ ! -z "$$(docker ps -a -q -f name=paho_test)" ]; then docker-compose -p paho_test down; fi

View File

@ -0,0 +1,65 @@
version: '3'
services:
emqx1:
image: emqx/emqx:build-alpine-amd64
environment:
- "EMQX_NAME=emqx"
- "EMQX_HOST=node1.emqx.io"
- "EMQX_CLUSTER__DISCOVERY=static"
- "EMQX_CLUSTER__STATIC__SEEDS=emqx@node1.emqx.io, emqx@node2.emqx.io"
- "EMQX_ZONE__EXTERNAL__RETRY_INTERVAL=2s"
- "EMQX_MQTT__MAX_TOPIC_ALIAS=10"
command:
- /bin/sh
- -c
- |
sed -i "s 127.0.0.1 $$(ip route show |grep "link" |awk '{print $$1}') g" /opt/emqx/etc/acl.conf
/usr/bin/start.sh
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"]
interval: 5s
timeout: 25s
retries: 5
networks:
emqx-bridge:
aliases:
- node1.emqx.io
emqx2:
image: emqx/emqx:build-alpine-amd64
environment:
- "EMQX_NAME=emqx"
- "EMQX_HOST=node2.emqx.io"
- "EMQX_CLUSTER__DISCOVERY=static"
- "EMQX_CLUSTER__STATIC__SEEDS=emqx@node1.emqx.io, emqx@node2.emqx.io"
- "EMQX_ZONE__EXTERNAL__RETRY_INTERVAL=2s"
- "EMQX_MQTT__MAX_TOPIC_ALIAS=10"
command:
- /bin/sh
- -c
- |
sed -i "s 127.0.0.1 $$(ip route show |grep "link" |awk '{print $$1}') g" /opt/emqx/etc/acl.conf
/usr/bin/start.sh
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"]
interval: 5s
timeout: 25s
retries: 5
networks:
emqx-bridge:
aliases:
- node2.emqx.io
client:
image: python:3.7.2-alpine3.9
depends_on:
- emqx1
- emqx2
tty: true
networks:
emqx-bridge:
networks:
emqx-bridge:
driver: bridge

View File

@ -0,0 +1,393 @@
name: Cross build packages
on:
pull_request:
push:
tags:
- v*
jobs:
windows:
runs-on: windows-2019
steps:
- uses: actions/checkout@v1
- uses: ilammy/msvc-dev-cmd@v1
- uses: gleam-lang/setup-erlang@v1.1.0
id: install_erlang
with:
otp-version: 22.3
- name: build
run: |
# set-executionpolicy remotesigned -s cu
# iex (new-object net.webclient).downloadstring('https://get.scoop.sh')
# # $env:path + ";" + $env:USERPROFILE + "\scoop\shims" + ';C:\Program Files\erl10.4\bin'
# [environment]::SetEnvironmentvariable("Path", ";" + $env:USERPROFILE + "\scoop\shims")
# [environment]::SetEnvironmentvariable("Path", ';C:\Program Files\erl10.4\bin')
# scoop bucket add extras https://github.com/lukesampson/scoop-extras.git
# scoop update
# scoop install sudo curl vcredist2013
$env:PATH = "${{ steps.install_erlang.outputs.erlpath }}\bin;$env:PATH"
$version = $( "${{ github.ref }}" -replace "^(.*)/(.*)/" )
if ($version -match "^v[0-9]+\.[0-9]+(\.[0-9]+)?") {
$regex = "[0-9]+\.[0-9]+(-alpha|-beta|-rc)?\.[0-9]"
$pkg_name = "emqx-windows-$([regex]::matches($version, $regex).value).zip"
}
else {
$pkg_name = "emqx-windows-$($version -replace '/').zip"
}
make deps-emqx || cat rebar3.crashdump
$rebar3 = $env:USERPROFILE + "\rebar3"
(New-Object System.Net.WebClient).DownloadFile('https://s3.amazonaws.com/rebar3/rebar3', $rebar3)
cd _build/emqx/lib/jiffy/
escript $rebar3 compile
cd ../../../../
make emqx
mkdir -p _packages/emqx
Compress-Archive -Path _build/emqx/rel/emqx -DestinationPath _build/emqx/rel/$pkg_name
mv _build/emqx/rel/$pkg_name _packages/emqx
Get-FileHash -Path "_packages/emqx/$pkg_name" | Format-List | grep 'Hash' | awk '{print $3}' > _packages/emqx/$pkg_name.sha256
- name: run emqx
run: |
./_build/emqx/rel/emqx/bin/emqx start
./_build/emqx/rel/emqx/bin/emqx stop
./_build/emqx/rel/emqx/bin/emqx install
./_build/emqx/rel/emqx/bin/emqx uninstall
- uses: actions/upload-artifact@v1
with:
name: emqx
path: ./_packages/emqx/.
mac:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v1
- name: prepare
run: |
brew install curl zip unzip gnu-sed kerl unixodbc freetds
echo "/usr/local/bin" >> $GITHUB_PATH
git config --global credential.helper store
- name: build erlang
run: |
kerl build 22.3.4.13
kerl install 22.3.4.13 $HOME/.kerl/22.3.4.13
- name: build
run: |
. $HOME/.kerl/22.3.4.13/activate
make emqx-pkg
- name: test
run: |
pkg_name=$(basename _packages/emqx/emqx-macos-*.zip)
unzip _packages/emqx/$pkg_name
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
./emqx/bin/emqx_ctl status
./emqx/bin/emqx stop
rm -rf emqx
openssl dgst -sha256 ./_packages/emqx/$pkg_name | awk '{print $2}' > ./_packages/emqx/$pkg_name.sha256
- uses: actions/upload-artifact@v1
with:
name: emqx
path: ./_packages/emqx/.
linux:
runs-on: ubuntu-20.04
strategy:
matrix:
os:
- ubuntu20.04
- ubuntu18.04
- ubuntu16.04
- debian10
- debian9
- opensuse
- centos8
- centos7
- centos6
- raspbian10
- raspbian9
steps:
- name: prepare docker
run: |
mkdir -p $HOME/.docker
echo '{ "experimental": "enabled" }' | tee $HOME/.docker/config.json
echo '{ "experimental": true, "storage-driver": "overlay2", "max-concurrent-downloads": 50, "max-concurrent-uploads": 50}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
docker info
docker buildx create --use --name mybuild
docker run --rm --privileged tonistiigi/binfmt --install all
- uses: actions/checkout@v1
- name: get deps
run: |
docker run -i --rm \
-e GITHUB_RUN_ID=$GITHUB_RUN_ID \
-e GITHUB_REF=$GITHUB_REF \
-v $(pwd):/emqx \
-w /emqx \
emqx/build-env:erl22.3-debian10 \
bash -c "make deps-all"
- name: downloads emqx zip packages
env:
SYSTEM: ${{ matrix.os }}
run: |
set -e -u -x
version=$(echo ${{ github.ref }} | sed -r "s ^refs/heads/|^refs/tags/(.*) \1 g")
if [ "$(echo $version | grep -oE "^[ev0-9]+\.[0-9]+\.[1-9]?")" = "$version" ]; then
tags=$(git tag -l "$(echo $version | grep -oE "^[ev0-9]+\.[0-9]").*" |grep -v "$version")
for tag in ${tags[@]};do
for arch in x86_64 aarch64; do
mkdir -p tmp/relup_packages/emqx
cd tmp/relup_packages/emqx
if [ ! -z "$(echo $(curl -I -m 10 -o /dev/null -s -w %{http_code} https://packages.emqx.io/emqx-ce/v${tag#[e|v]}/emqx-$SYSTEM-${tag#[e|v]}-$arch.zip) | grep -oE "^[23]+")" ];then
wget https://www.emqx.io/downloads/broker/v${tag#[e|v]}/emqx-$SYSTEM-${tag#[e|v]}-$arch.zip
wget https://www.emqx.io/downloads/broker/v${tag#[e|v]}/emqx-$SYSTEM-${tag#[e|v]}-$arch.zip.sha256
echo "$(cat emqx-$SYSTEM-${tag#[e|v]}-$arch.zip.sha256) emqx-$SYSTEM-${tag#[e|v]}-$arch.zip" | sha256sum -c || exit 1
fi
cd -
mkdir -p tmp/relup_packages/emqx-edge
cd tmp/relup_packages/emqx-edge
if [ ! -z "$(echo $(curl -I -m 10 -o /dev/null -s -w %{http_code} https://packages.emqx.io/emqx-edge/v${tag#[e|v]}/emqx-edge-$SYSTEM-${tag#[e|v]}-$arch.zip) | grep -oE "^[23]+")" ];then
wget https://www.emqx.io/downloads/edge/v${tag#[e|v]}/emqx-edge-$SYSTEM-${tag#[e|v]}-$arch.zip
wget https://www.emqx.io/downloads/edge/v${tag#[e|v]}/emqx-edge-$SYSTEM-${tag#[e|v]}-$arch.zip.sha256
echo "$(cat emqx-edge-$SYSTEM-${tag#[e|v]}-$arch.zip.sha256) emqx-edge-$SYSTEM-${tag#[e|v]}-$arch.zip" | sha256sum -c || exit 1
fi
cd -
done
done
fi
- name: build emqx packages on amd64
env:
ERL_OTP: erl22.3
SYSTEM: ${{ matrix.os }}
if: matrix.os != 'raspbian9' && matrix.os != 'raspbian10'
run: |
set -e -u -x
for EMQX in emqx emqx-edge; do
docker buildx build --no-cache \
--platform=linux/amd64 \
-t cross_build_emqx_for_$SYSTEM \
-f .ci/build_packages/Dockerfile \
--build-arg BUILD_FROM=emqx/build-env:$ERL_OTP-$SYSTEM \
--build-arg EMQX_NAME=${EMQX} \
--output type=tar,dest=/tmp/cross-build-$EMQX-for-$SYSTEM.tar .
mkdir -p /tmp/packages/$EMQX
tar -xvf /tmp/cross-build-$EMQX-for-$SYSTEM.tar --wildcards emqx/_packages/$EMQX/*
mv emqx/_packages/$EMQX/* /tmp/packages/$EMQX/
rm -rf /tmp/cross-build-$EMQX-for-$SYSTEM.tar
docker rm -f $(docker ps -a -q)
docker volume prune -f
done
- name: build emqx packages on arm64
if: matrix.os != 'centos6'
env:
ERL_OTP: erl22.3
SYSTEM: ${{ matrix.os }}
run: |
set -e -u -x
for EMQX in emqx emqx-edge; do
if [ "$EMQX" == "emqx" ] && [ ! -z $( echo $SYSTEM | grep -o raspbian) ];then
continue
fi
docker buildx build --no-cache \
--platform=linux/arm64 \
-t cross_build_emqx_for_$SYSTEM \
-f .ci/build_packages/Dockerfile \
--build-arg BUILD_FROM=emqx/build-env:$ERL_OTP-$SYSTEM \
--build-arg EMQX_NAME=${EMQX} \
--output type=tar,dest=/tmp/cross-build-$EMQX-for-$SYSTEM.tar . || df -h
mkdir -p /tmp/packages/$EMQX
tar -xvf /tmp/cross-build-$EMQX-for-$SYSTEM.tar --wildcards emqx/_packages/$EMQX/*
mv emqx/_packages/$EMQX/* /tmp/packages/$EMQX/
rm -rf /tmp/cross-build-$EMQX-for-$SYSTEM.tar
docker rm -f $(docker ps -a -q)
docker volume prune -f
done
- name: create sha256
run: |
if [ -d /tmp/packages/emqx ]; then
cd /tmp/packages/emqx
for var in $(ls emqx-* ); do
bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"
done
cd -
fi
if [ -d /tmp/packages/emqx-edge ]; then
cd /tmp/packages/emqx-edge
for var in $(ls emqx-edge-* ); do
bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"
done
cd -
fi
- uses: actions/upload-artifact@v1
if: matrix.os != 'raspbian9' && matrix.os != 'raspbian10'
with:
name: emqx
path: /tmp/packages/emqx/.
- uses: actions/upload-artifact@v1
with:
name: emqx-edge
path: /tmp/packages/emqx-edge/.
docker:
runs-on: ubuntu-20.04
strategy:
matrix:
arch:
- [amd64, x86_64]
- [arm64v8, aarch64]
- [arm32v7, arm]
- [i386, i386]
- [s390x, s390x]
steps:
- uses: actions/checkout@v1
- name: get deps
run: |
docker run -i --rm \
-e GITHUB_RUN_ID=$GITHUB_RUN_ID \
-e GITHUB_REF=$GITHUB_REF \
-v $(pwd):/emqx \
-w /emqx \
emqx/build-env:erl22.3-alpine-amd64 \
sh -c "make deps-emqx"
- name: build emqx docker image
env:
ARCH: ${{ matrix.arch[0] }}
QEMU_ARCH: ${{ matrix.arch[1] }}
run: |
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
sudo TARGET=emqx/emqx ARCH=$ARCH QEMU_ARCH=$QEMU_ARCH make docker
cd _packages/emqx && for var in $(ls emqx-docker-* ); do sudo bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"; done && cd -
sudo TARGET=emqx/emqx-edge ARCH=$ARCH QEMU_ARCH=$QEMU_ARCH make docker
cd _packages/emqx-edge && for var in $(ls emqx-edge-docker-* ); do sudo bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"; done && cd -
- name: docker test
if: matrix.arch[0] == 'amd64'
run: |
sudo TARGET=emqx/emqx PAHO_BRANCH="develop-4.0" make -C .ci/paho_tests cluster_test
sudo TARGET=emqx/emqx-edge PAHO_BRANCH="develop-4.0" make -C .ci/paho_tests cluster_test
- uses: actions/upload-artifact@v1
with:
name: emqx
path: ./_packages/emqx/.
- uses: actions/upload-artifact@v1
with:
name: emqx-edge
path: ./_packages/emqx-edge/.
upload:
runs-on: ubuntu-20.04
needs: [windows, mac, linux, docker]
steps:
- uses: actions/download-artifact@v2
with:
name: emqx
path: ./_packages/emqx
- uses: actions/download-artifact@v2
with:
name: emqx-edge
path: ./_packages/emqx-edge
- name: install dos2unix
run: sudo apt-get update && sudo apt install -y dos2unix
- name: get packages
run: |
set -e -x -u
for EMQX in emqx emqx-edge; do
cd _packages/$EMQX
for var in $( ls |grep emqx |grep -v sha256); do
dos2unix $var.sha256
echo "$(cat $var.sha256) $var" | sha256sum -c || exit 1
done
cd -
done
- name: upload aws s3
if: contains(github.ref, 'refs/tags/')
run: |
set -e -x -u
version=$(echo ${{ github.ref }} | sed -r "s ^refs/heads/|^refs/tags/(.*) \1 g")
curl "https://d1vvhvl2y92vvt.cloudfront.net/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws configure set default.region us-west-2
aws s3 cp --recursive _packages/emqx s3://packages.emqx/emqx-ce/$version
aws s3 cp --recursive _packages/emqx-edge s3://packages.emqx/emqx-edge/$version
aws cloudfront create-invalidation --distribution-id E170YEULGLT8XB --paths "/emqx-ce/$version/*,/emqx-edge/$version/*"
mkdir packages
mv _packages/emqx/* packages
mv _packages/emqx-edge/* packages
- uses: actions/checkout@v2
with:
path: emqx
- name: update to github and emqx.io
if: contains(github.ref, 'refs/tags/')
run: |
set -e -x -u
version=$(echo ${{ github.ref }} | sed -r "s ^refs/heads/|^refs/tags/(.*) \1 g")
cd packages
for var in $(ls); do
../emqx/.ci/build_packages/upload_github_release_asset.sh owner=emqx repo=emqx tag=$version filename=$var github_api_token=$(echo ${{ secrets.AccessToken }})
sleep 1
done
curl -w %{http_code} \
--insecure \
-H "Content-Type: application/json" \
-H "token: ${{ secrets.EMQX_IO_TOKEN }}" \
-X POST \
-d "{\"repo\":\"emqx/emqx\", \"tag\": \"${version}\" }" \
${{ secrets.EMQX_IO_RELEASE_API }}
- name: push docker image to docker hub
if: contains(github.ref, 'refs/tags/')
run: |
set -e -x -u
version=$(echo ${{ github.ref }} | sed -r "s ^refs/heads/|^refs/tags/(.*) \1 g")
sudo make -C emqx docker-prepare
cd packages && for var in $(ls |grep docker |grep -v sha256); do unzip $var; sudo docker load < ${var%.*}; rm -f ${var%.*}; done && cd -
echo ${{ secrets.DOCKER_HUB_TOKEN }} |sudo docker login -u ${{ secrets.DOCKER_HUB_USER }} --password-stdin
sudo TARGET=emqx/emqx make -C emqx docker-push
sudo TARGET=emqx/emqx make -C emqx docker-manifest-list
sudo TARGET=emqx/emqx-edge make -C emqx docker-push
sudo TARGET=emqx/emqx-edge make -C emqx docker-manifest-list
- name: update repo.emqx.io
if: contains(github.ref, 'refs/tags/')
run: |
set -e -x -u
version=$(echo ${{ github.ref }} | sed -r "s ^refs/heads/|^refs/tags/(.*) \1 g")
curl \
-H "Authorization: token ${{ secrets.AccessToken }}" \
-H "Accept: application/vnd.github.v3+json" \
-X POST \
-d "{\"ref\":\"v1.0.0\",\"inputs\":{\"version\": \"${version}\", \"emqx_ce\": \"true\"}}" \
https://api.github.com/repos/emqx/emqx-ci-helper/actions/workflows/update_repos.yaml/dispatches
- uses: geekyeggo/delete-artifact@v1
with:
name: emqx
- uses: geekyeggo/delete-artifact@v1
with:
name: emqx-edge
# - name: update homebrew packages
# run: |
# version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g")
# if [ ! -z $(echo $version | grep -oE "v[0-9]+\.[0-9]+(\.[0-9]+)?") ] && [ -z $(echo $version | grep -oE "(alpha|beta|rc)\.[0-9]") ]; then
# curl -H "Authorization: token ${{ secrets.AccessToken }}" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" -X POST -d "{\"event_type\":\"update_homebrew\",\"client_payload\":{\"version\": \"$version\"}}" https://api.github.com/repos/emqx/emqx-packages-docker/dispatches
# fi

1
.gitignore vendored
View File

@ -36,5 +36,6 @@ _checkouts
rebar.config.rendered rebar.config.rendered
/rebar3 /rebar3
rebar.lock rebar.lock
.stamp
tmp/ tmp/
_packages _packages

View File

@ -1,6 +1,6 @@
REBAR_VERSION = 3.14.3-emqx-2 REBAR_VERSION = 3.14.3-emqx-3
REBAR = ./rebar3 REBAR = $(CURDIR)/rebar3
export PKG_VSN ?= $(shell git describe --tags --always) export PKG_VSN ?= $(shell git describe --tags --match '[0-9]*' 2>/dev/null || git describe --always)
# comma separated versions # comma separated versions
export RELUP_BASE_VERSIONS ?= export RELUP_BASE_VERSIONS ?=
@ -18,7 +18,7 @@ all: $(REBAR) $(PROFILES)
.PHONY: ensure-rebar3 .PHONY: ensure-rebar3
ensure-rebar3: ensure-rebar3:
@./ensure-rebar3.sh $(REBAR_VERSION) $(CURDIR)/ensure-rebar3.sh $(REBAR_VERSION)
$(REBAR): ensure-rebar3 $(REBAR): ensure-rebar3

View File

@ -0,0 +1,20 @@
apiVersion: v2
name: emqx
description: A Helm chart for EMQ X
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: v4.1.1
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
appVersion: v4.1.1

View File

@ -0,0 +1,77 @@
# Introduction
This chart bootstraps an emqx deployment on a Kubernetes cluster using the Helm package manager.
# Prerequisites
+ Kubernetes 1.6+
+ Helm
# Installing the Chart
To install the chart with the release name `my-emqx`:
+ From github
```
$ git clone https://github.com/emqx/emqx.git
$ cd emqx/deploy/charts/emqx
$ helm install my-emqx .
```
+ From chart repos
```
helm repo add emqx https://repos.emqx.io/charts
helm install my-emqx emqx/emqx
```
> If you want to install an unstable version, you need to add `--devel` when you execute the `helm install` command.
# Uninstalling the Chart
To uninstall/delete the `my-emqx` deployment:
```
$ helm del my-emqx
```
# Configuration
The following table lists the configurable parameters of the emqx chart and their default values.
| Parameter | Description | Default Value |
| --- | --- | --- |
| `replicaCount` | It is recommended to have odd number of nodes in a cluster, otherwise the emqx cluster cannot be automatically healed in case of net-split. |3|
| `image.repository` | EMQ X Image name |emqx/emqx|
| `image.pullPolicy` | The image pull policy |IfNotPresent|
| `image.pullSecrets ` | The image pull secrets |`[]` (does not add image pull secrets to deployed pods)|
| `persistence.enabled` | Enable EMQX persistence using PVC |false|
| `persistence.storageClass` | Storage class of backing PVC |`nil` (uses alpha storage class annotation)|
| `persistence.existingClaim` | EMQ X data Persistent Volume existing claim name, evaluated as a template |""|
| `persistence.accessMode` | PVC Access Mode for EMQX volume |ReadWriteOnce|
| `persistence.size` | PVC Storage Request for EMQX volume |20Mi|
| `initContainers` | Containers that run before the creation of EMQX containers. They can contain utilities or setup scripts. |`{}`|
| `resources` | CPU/Memory resource requests/limits |{}|
| `nodeSelector` | Node labels for pod assignment |`{}`|
| `tolerations` | Toleration labels for pod assignment |`[]`|
| `affinity` | Map of node/pod affinities |`{}`|
| `service.type` | Kubernetes Service type. |ClusterIP|
| `service.mqtt` | Port for MQTT. |1883|
| `service.mqttssl` | Port for MQTT(SSL). |8883|
| `service.mgmt` | Port for mgmt API. |8081|
| `service.ws` | Port for WebSocket/HTTP. |8083|
| `service.wss` | Port for WSS/HTTPS. |8084|
| `service.dashboard` | Port for dashboard. |18083|
| `service.nodePorts.mqtt` | Kubernetes node port for MQTT. |nil|
| `service.nodePorts.mqttssl` | Kubernetes node port for MQTT(SSL). |nil|
| `service.nodePorts.mgmt` | Kubernetes node port for mgmt API. |nil|
| `service.nodePorts.ws` | Kubernetes node port for WebSocket/HTTP. |nil|
| `service.nodePorts.wss` | Kubernetes node port for WSS/HTTPS. |nil|
| `service.nodePorts.dashboard` | Kubernetes node port for dashboard. |nil|
| `service.loadBalancerIP` | loadBalancerIP for Service | nil |
| `service.loadBalancerSourceRanges` | Address(es) that are allowed when service is LoadBalancer | [] |
| `service.annotations` | Service annotations | {}(evaluated as a template)|
| `ingress.dashboard.enabled` | Enable ingress for EMQX Dashboard | false |
| `ingress.dashboard.path` | Ingress path for EMQX Dashboard | / |
| `ingress.dashboard.hosts` | Ingress hosts for EMQX Mgmt API | dashboard.emqx.local |
| `ingress.dashboard.tls` | Ingress tls for EMQX Mgmt API | [] |
| `ingress.dashboard.annotations` | Ingress annotations for EMQX Mgmt API | {} |
| `ingress.mgmt.enabled` | Enable ingress for EMQX Mgmt API | false |
| `ingress.mgmt.path` | Ingress path for EMQX Mgmt API | / |
| `ingress.mgmt.hosts` | Ingress hosts for EMQX Mgmt API | api.emqx.local |
| `ingress.mgmt.tls` | Ingress tls for EMQX Mgmt API | [] |
| `ingress.mgmt.annotations` | Ingress annotations for EMQX Mgmt API | {} |
| `emqxConfig` | Emqx configuration item, see the [documentation](https://hub.docker.com/r/emqx/emqx) | |
| `emqxAclConfig` | Emqx acl configuration item, see the [documentation](https://docs.emqx.io/broker/latest/en/advanced/acl-file.html) | |

View File

@ -0,0 +1,150 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "emqx.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
serviceName: {{ include "emqx.fullname" . }}-headless
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
volumeClaimTemplates:
- metadata:
name: emqx-data
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
annotations:
{{- if .Values.persistence.storageClass }}
volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }}
{{- else }}
volume.alpha.kubernetes.io/storage-class: default
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- end }}
updateStrategy:
type: RollingUpdate
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ include "emqx.name" . }}
version: {{ .Chart.AppVersion }}
app.kubernetes.io/name: {{ include "emqx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
volumes:
- name: emqx-acl
configMap:
name: {{ include "emqx.fullname" . }}-acl
items:
- key: acl.conf
path: acl.conf
{{- if not .Values.persistence.enabled }}
- name: emqx-data
emptyDir: {}
{{- else if .Values.persistence.existingClaim }}
- name: emqx-data
persistentVolumeClaim:
{{- with .Values.persistence.existingClaim }}
claimName: {{ tpl . $ }}
{{- end }}
{{- end }}
{{- if .Values.emqxLicneseSecretName }}
- name: emqx-license
secret:
secretName: {{ .Values.emqxLicneseSecretName }}
{{- end }}
serviceAccountName: {{ include "emqx.fullname" . }}
securityContext:
fsGroup: 1000
{{- if .Values.initContainers }}
initContainers:
{{ toYaml .Values.initContainers | indent 8 }}
{{- end }}
containers:
- name: emqx
image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.image.pullSecrets }}
imagePullSecrets:
{{- range .Values.image.pullSecrets }}
- name: {{ . }}
{{- end }}
{{- end }}
ports:
- name: mqtt
containerPort: {{ .Values.emqxConfig.EMQX_LISTENER__TCP__EXTERNAL | default 1883 }}
- name: mqttssl
containerPort: {{ .Values.emqxConfig.EMQX_LISTENER__SSL__EXTERNAL | default 8883 }}
- name: mgmt
containerPort: {{ .Values.emqxConfig.EMQX_MANAGEMENT__LISTENER__HTTP | default 8081 }}
- name: ws
containerPort: {{ .Values.emqxConfig.EMQX_LISTENER__WS__EXTERNAL | default 8083 }}
- name: wss
containerPort: {{ .Values.emqxConfig.EMQX_LISTENER__WSS__EXTERNAL | default 8084 }}
- name: dashboard
containerPort: {{ .Values.emqxConfig.EMQX_DASHBOARD__LISTENER__HTTP | default 18083 }}
- name: ekka
containerPort: 4370
envFrom:
- configMapRef:
name: {{ include "emqx.fullname" . }}-env
env:
- name: EMQX_NAME
value: {{ .Release.Name }}
- name: EMQX_CLUSTER__K8S__APP_NAME
value: {{ .Release.Name }}
- name: EMQX_CLUSTER__DISCOVERY
value: k8s
- name: EMQX_CLUSTER__K8S__SERVICE_NAME
value: {{ include "emqx.fullname" . }}-headless
- name: EMQX_CLUSTER__K8S__NAMESPACE
value: {{ .Release.Namespace }}
resources:
{{ toYaml .Values.resources | indent 12 }}
volumeMounts:
- name: emqx-data
mountPath: "/opt/emqx/data/mnesia"
- name: emqx-acl
mountPath: "/opt/emqx/etc/acl.conf"
subPath: "acl.conf"
{{ if .Values.emqxLicneseSecretName }}
- name: emqx-license
mountPath: "/opt/emqx/etc/emqx.lic"
subPath: "emqx.lic"
readOnly: true
{{ end }}
readinessProbe:
httpGet:
path: /status
port: {{ .Values.emqxConfig.EMQX_MANAGEMENT__LISTENER__HTTP | default 8081 }}
initialDelaySeconds: 5
periodSeconds: 5
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "emqx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "emqx.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "emqx.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "emqx.fullname" . }}-env
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
data:
{{- range $index, $value := .Values.emqxConfig}}
{{$index}}: "{{ $value }}"
{{- end}}

View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "emqx.fullname" . }}-acl
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
data:
"acl.conf": |
{{ .Values.emqxAclConfig }}

View File

@ -0,0 +1,70 @@
{{- if .Values.ingress.dashboard.enabled -}}
{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ printf "%s-%s" (include "emqx.fullname" .) "dashboard" }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Values.ingress.dashboard.annotations }}
annotations:
{{- toYaml .Values.ingress.dashboard.annotations | nindent 4 }}
{{- end }}
spec:
rules:
{{- range $host := .Values.ingress.dashboard.hosts }}
- host: {{ $host }}
http:
paths:
- path: /
backend:
serviceName: {{ include "emqx.fullname" $ }}
servicePort: {{ $.Values.service.dashboard }}
{{- end -}}
{{- if .Values.ingress.dashboard.tls }}
tls:
{{- toYaml .Values.ingress.dashboard.tls | nindent 4 }}
{{- end }}
---
{{- end }}
{{- if .Values.ingress.mgmt.enabled -}}
{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ printf "%s-%s" (include "emqx.fullname" .) "mgmt" }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Values.ingress.mgmt.annotations }}
annotations:
{{- toYaml .Values.ingress.mgmt.annotations | nindent 4 }}
{{- end }}
spec:
rules:
{{- range $host := .Values.ingress.mgmt.hosts }}
- host: {{ $host }}
http:
paths:
- path: /
backend:
serviceName: {{ include "emqx.fullname" $ }}
servicePort: {{ $.Values.service.mgmt }}
{{- end -}}
{{- if .Values.ingress.mgmt.tls }}
tls:
{{- toYaml .Values.ingress.mgmt.tls | nindent 4 }}
{{- end }}
---
{{- end }}

View File

@ -0,0 +1,34 @@
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: {{ .Release.Namespace }}
name: {{ include "emqx.fullname" . }}
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: {{ .Release.Namespace }}
name: {{ include "emqx.fullname" . }}
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
- watch
- list
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: {{ .Release.Namespace }}
name: {{ include "emqx.fullname" . }}
subjects:
- kind: ServiceAccount
name: {{ include "emqx.fullname" . }}
namespace: {{ .Release.Namespace }}
roleRef:
kind: Role
name: {{ include "emqx.fullname" . }}
apiGroup: rbac.authorization.k8s.io

View File

@ -0,0 +1,130 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "emqx.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Values.service.annotations }}
annotations:
{{ toYaml .Values.service.annotations | indent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if eq .Values.service.type "LoadBalancer" }}
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
{{- if .Values.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }}
{{- end }}
{{- end }}
ports:
- name: mqtt
port: {{ .Values.service.mqtt | default 1883 }}
protocol: TCP
targetPort: mqtt
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.mqtt)) }}
nodePort: {{ .Values.service.nodePorts.mqtt }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
- name: mqttssl
port: {{ .Values.service.mqttssl | default 8883 }}
protocol: TCP
targetPort: mqttssl
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.mqttssl)) }}
nodePort: {{ .Values.service.nodePorts.mqttssl }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
- name: mgmt
port: {{ .Values.service.mgmt | default 8081 }}
protocol: TCP
targetPort: mgmt
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.mgmt)) }}
nodePort: {{ .Values.service.nodePorts.mgmt }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
- name: ws
port: {{ .Values.service.ws | default 8083 }}
protocol: TCP
targetPort: ws
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.ws)) }}
nodePort: {{ .Values.service.nodePorts.ws }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
- name: wss
port: {{ .Values.service.wss | default 8084 }}
protocol: TCP
targetPort: wss
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.wss)) }}
nodePort: {{ .Values.service.nodePorts.wss }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
- name: dashboard
port: {{ .Values.service.dashboard | default 18083 }}
protocol: TCP
targetPort: dashboard
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.dashboard)) }}
nodePort: {{ .Values.service.nodePorts.dashboard }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
selector:
app.kubernetes.io/name: {{ include "emqx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "emqx.fullname" . }}-headless
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "emqx.name" . }}
helm.sh/chart: {{ include "emqx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: ClusterIP
sessionAffinity: None
clusterIP: None
ports:
- name: mqtt
port: {{ .Values.service.mqtt | default 1883 }}
protocol: TCP
targetPort: mqtt
- name: mqttssl
port: {{ .Values.service.mqttssl | default 8883 }}
protocol: TCP
targetPort: mqttssl
- name: mgmt
port: {{ .Values.service.mgmt | default 8081 }}
protocol: TCP
targetPort: mgmt
- name: ws
port: {{ .Values.service.ws | default 8083 }}
protocol: TCP
targetPort: ws
- name: wss
port: {{ .Values.service.wss | default 8084 }}
protocol: TCP
targetPort: wss
- name: dashboard
port: {{ .Values.service.dashboard | default 18083 }}
protocol: TCP
targetPort: dashboard
- name: ekka
port: 4370
protocol: TCP
targetPort: ekka
selector:
app.kubernetes.io/name: {{ include "emqx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@ -0,0 +1,158 @@
## Default values for emqx.
## This is a YAML-formatted file.
## Declare variables to be passed into your templates.
## It is recommended to have odd number of nodes in a cluster, otherwise the emqx cluster cannot be automatically healed in case of net-split.
replicaCount: 3
image:
repository: emqx/emqx
pullPolicy: IfNotPresent
## Optionally specify an array of imagePullSecrets.
## Secrets must be manually created in the namespace.
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
##
# pullSecrets:
# - myRegistryKeySecretName
persistence:
enabled: false
size: 20Mi
## If defined, volume.beta.kubernetes.io/storage-class: <storageClass>
## Default: volume.alpha.kubernetes.io/storage-class: default
# storageClass: "-"
accessMode: ReadWriteOnce
## Existing PersistentVolumeClaims
## The value is evaluated as a template
## So, for example, the name can depend on .Release or .Chart
# existingClaim: ""
resources: {}
# limits:
# cpu: 500m
# memory: 512Mi
# requests:
# cpu: 500m
# memory: 512Mi
# Containers that run before the creation of EMQX containers. They can contain utilities or setup scripts.
initContainers: {}
# - name: mysql-probe
# image: alpine
# command: ["sh", "-c", "for i in $(seq 1 300); do nc -zvw1 mysql 3306 && exit 0 || sleep 3; done; exit 1"]
## EMQX configuration item, see the documentation (https://github.com/emqx/emqx-docker#emq-x-configuration)
emqxConfig:
EMQX_CLUSTER__K8S__APISERVER: "https://kubernetes.default.svc:443"
## The address type is used to extract host from k8s service.
## Value: ip | dns | hostname
## NoteHostname is only supported after v4.0-rc.2
EMQX_CLUSTER__K8S__ADDRESS_TYPE: "hostname"
EMQX_CLUSTER__K8S__SUFFIX: "svc.cluster.local"
## if EMQX_CLUSTER__K8S__ADDRESS_TYPE eq dns
# EMQX_CLUSTER__K8S__SUFFIX: "pod.cluster.local"
## --------------------------------------------------------------------
## [ACL](https://docs.emqx.io/broker/latest/en/advanced/acl-file.html)
## -type(who() :: all | binary() |
## {ipaddr, esockd_access:cidr()} |
## {client, binary()} |
## {user, binary()}).
## -type(access() :: subscribe | publish | pubsub).
## -type(topic() :: binary()).
## -type(rule() :: {allow, all} |
## {allow, who(), access(), list(topic())} |
## {deny, all} |
## {deny, who(), access(), list(topic())}).
## --------------------------------------------------------------------
emqxAclConfig: >
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
{allow, all}.
## EMQX Enterprise Edition requires manual creation of a Secret containing the licensed content. Write the name of Secret to the value of "emqxLicneseSecretName"
## Example:
## kubectl create secret generic emqx-license-secret-name --from-file=/path/to/emqx.lic
emqxLicneseSecretName:
service:
## Service type
##
type: ClusterIP
## Port for MQTT
##
mqtt: 1883
## Port for MQTT(SSL)
##
mqttssl: 8883
## Port for mgmt API
##
mgmt: 8081
## Port for WebSocket/HTTP
##
ws: 8083
## Port for WSS/HTTPS
##
wss: 8084
## Port for dashboard
##
dashboard: 18083
## Specify the nodePort(s) value for the LoadBalancer and NodePort service types.
## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
##
nodePorts:
mqtt:
mqttssl:
mgmt:
ws:
wss:
dashboard:
## Set the LoadBalancer service type to internal only.
## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer
##
# loadBalancerIP:
## Load Balancer sources
## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service
## Example:
## loadBalancerSourceRanges:
## - 10.10.10.0/24
##
loadBalancerSourceRanges: []
## Provide any additional annotations which may be required. Evaluated as a template
##
annotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
ingress:
## ingress for EMQX Dashboard
dashboard:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /
hosts:
- dashboard.emqx.local
tls: []
## ingress for EMQX Mgmt API
mgmt:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /
hosts:
- api.emqx.local
tls: []

80
deploy/docker/Dockerfile Normal file
View File

@ -0,0 +1,80 @@
ARG BUILD_FROM=emqx/build-env:erl22.3-alpine-amd64
ARG RUN_FROM=alpine:3.11
FROM ${BUILD_FROM} AS builder
ARG QEMU_ARCH=x86_64
COPY tmp/qemu-$QEMU_ARCH-stati* /usr/bin/
RUN apk add --no-cache \
git \
curl \
gcc \
g++ \
make \
perl \
ncurses-dev \
openssl-dev \
coreutils \
bsd-compat-headers \
libc-dev \
libstdc++ \
bash
COPY . /emqx
ARG PKG_VSN
ARG EMQX_NAME=emqx
RUN cd /emqx && make $EMQX_NAME
FROM $RUN_FROM
# Basic build-time metadata as defined at http://label-schema.org
LABEL org.label-schema.docker.dockerfile="Dockerfile" \
org.label-schema.license="GNU" \
org.label-schema.name="emqx" \
org.label-schema.version=${PKG_VSN} \
org.label-schema.description="EMQ (Erlang MQTT Broker) is a distributed, massively scalable, highly extensible MQTT messaging broker written in Erlang/OTP." \
org.label-schema.url="http://emqx.io" \
org.label-schema.vcs-type="Git" \
org.label-schema.vcs-url="https://github.com/emqx/emqx-docker" \
maintainer="Raymond M Mouthaan <raymondmmouthaan@gmail.com>, Huang Rui <vowstar@gmail.com>, EMQ X Team <support@emqx.io>"
ARG QEMU_ARCH=x86_64
ARG EMQX_NAME=emqx
COPY deploy/docker/docker-entrypoint.sh deploy/docker/start.sh tmp/qemu-$QEMU_ARCH-stati* /usr/bin/
COPY --from=builder /emqx/_build/$EMQX_NAME/rel/emqx /opt/emqx
RUN ln -s /opt/emqx/bin/* /usr/local/bin/
RUN apk add --no-cache curl ncurses-libs openssl sudo libstdc++ bash
WORKDIR /opt/emqx
RUN adduser -D -u 1000 emqx \
&& echo "emqx ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
RUN chgrp -Rf emqx /opt/emqx && chmod -Rf g+w /opt/emqx \
&& chown -Rf emqx /opt/emqx
USER emqx
VOLUME ["/opt/emqx/log", "/opt/emqx/data", "/opt/emqx/lib", "/opt/emqx/etc"]
# emqx will occupy these port:
# - 1883 port for MQTT
# - 8081 for mgmt API
# - 8083 for WebSocket/HTTP
# - 8084 for WSS/HTTPS
# - 8883 port for MQTT(SSL)
# - 11883 port for internal MQTT/TCP
# - 18083 for dashboard
# - 4369 for port mapping (epmd)
# - 4370 for port mapping
# - 5369 for gen_rpc port mapping
# - 6369 for distributed node
EXPOSE 1883 8081 8083 8084 8883 11883 18083 4369 4370 5369 6369
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
CMD ["/usr/bin/start.sh"]

336
deploy/docker/README.md Normal file
View File

@ -0,0 +1,336 @@
# Quick reference
+ **Where to get help**:
https://emqx.io, https://github.com/emqx/emqx-rel, or https://github.com/emqx/emqx
+ **Where to file issues:**
https://github.com/emqx/emqx/issues
+ **Supported architectures**
`amd64`, `arm64v8`, `arm32v7`, `i386`, `s390x`
+ **Supported Docker versions**:
[the latest release](https://github.com/docker/docker-ce/releases/latest)
# What is EMQ X
[EMQ X MQTT broker](https://emqx.io/products/broker) is a fully open source, highly scalable, highly available distributed MQTT messaging broker for IoT, M2M and Mobile applications that can handle tens of millions of concurrent clients.
Starting from 3.0 release, *EMQ X* broker fully supports MQTT V5.0 protocol specifications and backward compatible with MQTT V3.1 and V3.1.1, as well as other communication protocols such as MQTT-SN, CoAP, LwM2M, WebSocket and STOMP. The 3.0 release of the *EMQ X* broker can scaled to 10+ million concurrent MQTT connections on one cluster.
# How to use this image
### Run emqx
Execute some command under this docker image
``docker run -d --name emqx emqx/emqx:$(tag)``
For example
``docker run -d --name emqx -p 18083:18083 -p 1883:1883 emqx/emqx:latest``
The emqx broker runs as linux user `emqx` in the docker container.
### Configuration
Use the environment variable to configure the EMQ X docker container.
The environment variables which with ``EMQX_`` prefix are mapped to configuration fils.
+ Prefix ``EMQX_`` is removed
+ All upper case letters is replaced with lower case letters
+ ``__`` is replaced with ``.``
Example:
```bash
EMQX_LISTENER__SSL__EXTERNAL__ACCEPTORS <--> listener.ssl.external.acceptors
EMQX_MQTT__MAX_PACKET_SIZE <--> mqtt.max_packet_size
```
Non mapped environment variables:
```bash
EMQX_NAME
EMQX_HOST
```
These environment variables will ignore for configuration file.
#### EMQ X Configuration
> NOTE: All EMQ X Configuration in [etc/emqx.conf](https://github.com/emqx/emqx/blob/master/etc/emqx.conf) could config by environment. The following list is just an example, not a complete configuration.
| Options | Default | Mapped | Description |
| ---------------------------| ------------------ | ------------------------- | ------------------------------------- |
| EMQX_NAME | container name | none | emqx node short name |
| EMQX_HOST | container IP | none | emqx node host, IP or FQDN |
| EMQX_WAIT_TIME | 5 | none | wait time in sec before timeout |
| EMQX_NODE__NAME | EMQX_NAME@EMQX_HOST| node.name | Erlang node name, name@ipaddress/host |
| EMQX_NODE__COOKIE | emqx_dist_cookie | node.cookie | cookie for cluster |
| EMQX_LOG__CONSOLE | console | log.console | log console output method |
| EMQX_ALLOW_ANONYMOUS | true | allow_anonymous | allow mqtt anonymous login |
| EMQX_LISTENER__TCP__EXTERNAL| 1883 | listener.tcp.external | MQTT TCP port |
| EMQX_LISTENER__SSL__EXTERNAL| 8883 | listener.ssl.external | MQTT TCP TLS/SSL port |
| EMQX_LISTENER__WS__EXTERNAL | 8083 | listener.ws.external | HTTP and WebSocket port |
| EMQX_LISTENER__WSS__EXTERNAL| 8084 | listener.wss.external | HTTPS and WSS port |
| EMQX_LISTENER__API__MGMT | 8080 | listener.api.mgmt | MGMT API port |
| EMQX_MQTT__MAX_PACKET_SIZE | 64KB | mqtt.max_packet_size | Max Packet Size Allowed |
The list is incomplete and may changed with [etc/emqx.conf](https://github.com/emqx/emqx/blob/master/etc/emqx.conf) and plugin configuration files. But the mapping rule is similar.
If set ``EMQX_NAME`` and ``EMQX_HOST``, and unset ``EMQX_NODE__NAME``, ``EMQX_NODE__NAME=$EMQX_NAME@$EMQX_HOST``.
For example, set mqtt tcp port to 1883
``docker run -d --name emqx -e EMQX_LISTENER__TCP__EXTERNAL=1883 -p 18083:18083 -p 1883:1883 emqx/emqx:latest``
#### EMQ Loaded Modules Configuration
| Oprtions | Default | Description |
| ------------------------ | ------------------ | ------------------------------------- |
| EMQX_LOADED_MODULES | see content below | default modules emqx loaded |
Default environment variable ``EMQX_LOADED_MODULES``, including
+ ``emqx_mod_acl_internal``
+ ``emqx_mod_presence``
```bash
# The default EMQX_LOADED_MODULES env
EMQX_LOADED_MODULES="emqx_mod_acl_internal,emqx_mod_acl_internal"
```
For example, set ``EMQX_LOADED_MODULES=emqx_mod_delayed,emqx_mod_rewrite`` to load these two modules.
You can use comma, space or other separator that you want.
All the modules defined in env ``EMQX_LOADED_MODULES`` will be loaded.
```bash
EMQX_LOADED_MODULES="emqx_mod_delayed,emqx_mod_rewrite"
EMQX_LOADED_MODULES="emqx_mod_delayed emqx_mod_rewrite"
EMQX_LOADED_MODULES="emqx_mod_delayed | emqx_mod_rewrite"
```
#### EMQ Loaded Plugins Configuration
| Oprtions | Default | Description |
| ------------------------ | ------------------ | ------------------------------------- |
| EMQX_LOADED_PLUGINS | see content below | default plugins emqx loaded |
Default environment variable ``EMQX_LOADED_PLUGINS``, including
+ ``emqx_recon``
+ ``emqx_retainer``
+ ``emqx_management``
+ ``emqx_dashboard``
```bash
# The default EMQX_LOADED_PLUGINS env
EMQX_LOADED_PLUGINS="emqx_recon,emqx_retainer,emqx_management,emqx_dashboard"
```
For example, set ``EMQX_LOADED_PLUGINS= emqx_auth_redis,emqx_auth_mysql`` to load these two plugins.
You can use comma, space or other separator that you want.
All the plugins defined in ``EMQX_LOADED_PLUGINS`` will be loaded.
```bash
EMQX_LOADED_PLUGINS="emqx_auth_redis,emqx_auth_mysql"
EMQX_LOADED_PLUGINS="emqx_auth_redis emqx_auth_mysql"
EMQX_LOADED_PLUGINS="emqx_auth_redis | emqx_auth_mysql"
```
#### EMQ X Plugins Configuration
The environment variables which with ``EMQX_`` prefix are mapped to all emqx plugins' configuration file, ``.`` get replaced by ``__``.
Example:
```bash
EMQX_AUTH__REDIS__SERVER <--> auth.redis.server
EMQX_AUTH__REDIS__PASSWORD <--> auth.redis.password
```
Don't worry about where to find the configuration file of emqx plugins, this docker image will find and config them automatically using some magic.
All plugin of emqx project could config in this way, following the environment variables mapping rule above.
Assume you are using redis auth plugin, for example:
```bash
#EMQX_AUTH__REDIS__SERVER="redis.at.yourserver"
#EMQX_AUTH__REDIS__PASSWORD="password_for_redis"
docker run -d --name emqx -p 18083:18083 -p 1883:1883 -p 4369:4369 \
-e EMQX_LISTENER__TCP__EXTERNAL=1883 \
-e EMQX_LOADED_PLUGINS="emqx_auth_redis" \
-e EMQX_AUTH__REDIS__SERVER="your.redis.server:6379" \
-e EMQX_AUTH__REDIS__PASSWORD="password_for_redis" \
-e EMQX_AUTH__REDIS__PASSWORD_HASH=plain \
emqx/emqx:latest
```
For numbered configuration options where the number is next to a ``.`` such as:
+ backend.redis.pool1.server
+ backend.redis.hook.message.publish.1
You can configure an arbitrary number of them as long as each has a uniq unber for it's own configuration option:
```bash
docker run -d --name emqx -p 18083:18083 -p 1883:1883 -p 4369:4369 \
-e EMQX_BACKEND_REDIS_POOL1__SERVER=127.0.0.1:6379
[...]
-e EMQX_BACKEND__REDIS__POOL5__SERVER=127.0.0.5:6379
-e EMQX_BACKEND__REDIS__HOOK_MESSAGE__PUBLISH__1='{"topic": "persistant/topic1", "action": {"function": "on_message_publish"}, "pool": "pool1"}'
-e EMQX_BACKEND__REDIS__HOOK_MESSAGE__PUBLISH__2='{"topic": "persistant/topic2", "action": {"function": "on_message_publish"}, "pool": "pool1"}'
-e EMQX_BACKEND__REDIS__HOOK_MESSAGE__PUBLISH__3='{"topic": "persistant/topic3", "action": {"function": "on_message_publish"}, "pool": "pool1"}'
[...]
-e EMQX_BACKEND__REDIS__HOOK_MESSAGE__PUBLISH__13='{"topic": "persistant/topic13", "action": {"function": "on_message_publish"}, "pool": "pool1"}'
emqx/emqx:latest
```
#### Mask Sensitive Configuration
Use ``MASK_CONFIG_FILTER`` to hide senstive configuration values from leaking to logging system.
For example, set ``MASK_CONFIG_FILTER="password|token"`` to hide all configuration names containing those keywords.
By default emqx masks the configuration using following filter `"password|passwd|key|token|secret"`. Setting ``MASK_CONFIG_FILTER`` will be merged with the default filter.
The configuration should match whole word (after splitting it by '.') with `MASK_CONFIG_FILTER`. You can use commas, spaces or other required separators to separate different words.
### Cluster
EMQ X supports a variety of clustering methods, see our [documentation](https://docs.emqx.io/broker/latest/en/advanced/cluster.html#emqx-service-discovery) for details.
Let's create a static node list cluster from docker-compose.
+ Create `docker-compose.yaml`:
```yaml
version: '3'
services:
emqx1:
image: emqx/emqx:v4.0.0
environment:
- "EMQX_NAME=emqx"
- "EMQX_HOST=node1.emqx.io"
- "EMQX_CLUSTER__DISCOVERY=static"
- "EMQX_CLUSTER__STATIC__SEEDS=emqx@node1.emqx.io, emqx@node2.emqx.io"
networks:
emqx-bridge:
aliases:
- node1.emqx.io
emqx2:
image: emqx/emqx:v4.0.0
environment:
- "EMQX_NAME=emqx"
- "EMQX_HOST=node2.emqx.io"
- "EMQX_CLUSTER__DISCOVERY=static"
- "EMQX_CLUSTER__STATIC__SEEDS=emqx@node1.emqx.io, emqx@node2.emqx.io"
networks:
emqx-bridge:
aliases:
- node2.emqx.io
networks:
emqx-bridge:
driver: bridge
```
+ Start the docker-compose cluster
```bash
docker-compose -p my_emqx up -d
```
+ View cluster
```bash
$ docker exec -it my_emqx_emqx1_1 sh -c "emqx_ctl cluster status"
Cluster status: #{running_nodes => ['emqx@node1.emqx.io','emqx@node2.emqx.io'],
stopped_nodes => []}
```
### Persistence
If you want to persist the EMQ X docker container, you need to keep the following directories:
+ `/opt/emqx/data`
+ `/opt/emqx/etc`
+ `/opt/emqx/log`
Since data in these folders are partially stored under the `/opt/emqx/data/mnesia/${node_name}`, the user also needs to reuse the same node name to see the previous state. In detail, one needs to specify the two environment variables: `EMQX_NAME` and `EMQX_HOST`, `EMQX_HOST` set as `127.0.0.1` or network alias would be useful.
In if you use docker-compose, the configuration would look something like this:
```YAML
volumes:
vol-emqx-data:
name: foo-emqx-data
vol-emqx-etc:
name: foo-emqx-etc
vol-emqx-log:
name: foo-emqx-log
services:
emqx:
image: emqx/emqx:v4.0.0
restart: always
environment:
EMQX_NAME: foo_emqx
EMQX_HOST: 127.0.0.1
volumes:
- vol-emqx-data:/opt/emqx/data
- vol-emqx-etc:/opt/emqx/etc
- vol-emqx-log:/opt/emqx/log
```
### Kernel Tuning
Under linux host machine, the easiest way is [tuning host machine's kernel](https://docs.emqx.io/broker/latest/en/tutorial/turn.html#turning-guide).
If you want tune linux kernel by docker, you must ensure your docker is latest version (>=1.12).
```bash
docker run -d --name emqx -p 18083:18083 -p 1883:1883 -p 4369:4369 \
--sysctl fs.file-max=2097152 \
--sysctl fs.nr_open=2097152 \
--sysctl net.core.somaxconn=32768 \
--sysctl net.ipv4.tcp_max_syn_backlog=16384 \
--sysctl net.core.netdev_max_backlog=16384 \
--sysctl net.ipv4.ip_local_port_range=1000 65535 \
--sysctl net.core.rmem_default=262144 \
--sysctl net.core.wmem_default=262144 \
--sysctl net.core.rmem_max=16777216 \
--sysctl net.core.wmem_max=16777216 \
--sysctl net.core.optmem_max=16777216 \
--sysctl net.ipv4.tcp_rmem=1024 4096 16777216 \
--sysctl net.ipv4.tcp_wmem=1024 4096 16777216 \
--sysctl net.ipv4.tcp_max_tw_buckets=1048576 \
--sysctl net.ipv4.tcp_fin_timeout=15 \
emqx/emqx:latest
```
> REMEMBER: DO NOT RUN EMQ X DOCKER PRIVILEGED OR MOUNT SYSTEM PROC IN CONTAINER TO TUNE LINUX KERNEL, IT IS UNSAFE.
### Thanks
+ [@je-al](https://github.com/emqx/emqx-docker/issues/2)
+ [@RaymondMouthaan](https://github.com/emqx/emqx-docker/pull/91)
+ [@zhongjiewu](https://github.com/emqx/emqx/issues/3427)

View File

@ -0,0 +1,205 @@
#!/bin/bash
## EMQ docker image start script
# Huang Rui <vowstar@gmail.com>
# EMQ X Team <support@emqx.io>
## Shell setting
if [[ -n "$DEBUG" ]]; then
set -ex
else
set -e
fi
shopt -s nullglob
## Local IP address setting
LOCAL_IP=$(hostname -i | grep -oE '((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])\.){3}(25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])' | head -n 1)
## EMQ Base settings and plugins setting
# Base settings in /opt/emqx/etc/emqx.conf
# Plugin settings in /opt/emqx/etc/plugins
_EMQX_HOME='/opt/emqx'
if [[ -z "$EMQX_NAME" ]]; then
EMQX_NAME="$(hostname)"
export EMQX_NAME
fi
if [[ -z "$EMQX_HOST" ]]; then
if [[ "$EMQX_CLUSTER__K8S__ADDRESS_TYPE" == "dns" ]] && [[ -n "$EMQX_CLUSTER__K8S__NAMESPACE" ]]; then
EMQX_CLUSTER__K8S__SUFFIX=${EMQX_CLUSTER__K8S__SUFFIX:-"pod.cluster.local"}
EMQX_HOST="${LOCAL_IP//./-}.$EMQX_CLUSTER__K8S__NAMESPACE.$EMQX_CLUSTER__K8S__SUFFIX"
elif [[ "$EMQX_CLUSTER__K8S__ADDRESS_TYPE" == 'hostname' ]] && [[ -n "$EMQX_CLUSTER__K8S__NAMESPACE" ]]; then
EMQX_CLUSTER__K8S__SUFFIX=${EMQX_CLUSTER__K8S__SUFFIX:-'svc.cluster.local'}
EMQX_HOST=$(grep -h "^$LOCAL_IP" /etc/hosts | grep -o "$(hostname).*.$EMQX_CLUSTER__K8S__NAMESPACE.$EMQX_CLUSTER__K8S__SUFFIX")
else
EMQX_HOST="$LOCAL_IP"
fi
export EMQX_HOST
fi
if [[ -z "$EMQX_WAIT_TIME" ]]; then
export EMQX_WAIT_TIME=5
fi
if [[ -z "$EMQX_NODE_NAME" ]]; then
export EMQX_NODE_NAME="$EMQX_NAME@$EMQX_HOST"
fi
# Set hosts to prevent cluster mode failed
if [[ -z "$EMQX_NODE__PROCESS_LIMIT" ]]; then
export EMQX_NODE__PROCESS_LIMIT=2097152
fi
if [[ -z "$EMQX_NODE__MAX_PORTS" ]]; then
export EMQX_NODE__MAX_PORTS=1048576
fi
if [[ -z "$EMQX_NODE__MAX_ETS_TABLES" ]]; then
export EMQX_NODE__MAX_ETS_TABLES=2097152
fi
if [[ -z "$EMQX__LOG_CONSOLE" ]]; then
export EMQX__LOG_CONSOLE='console'
fi
if [[ -z "$EMQX_LISTENER__TCP__EXTERNAL__ACCEPTORS" ]]; then
export EMQX_LISTENER__TCP__EXTERNAL__ACCEPTORS=64
fi
if [[ -z "$EMQX_LISTENER__TCP__EXTERNAL__MAX_CLIENTS" ]]; then
export EMQX_LISTENER__TCP__EXTERNAL__MAX_CLIENTS=1000000
fi
if [[ -z "$EMQX_LISTENER__SSL__EXTERNAL__ACCEPTORS" ]]; then
export EMQX_LISTENER__SSL__EXTERNAL__ACCEPTORS=32
fi
if [[ -z "$EMQX_LISTENER__SSL__EXTERNAL__MAX_CLIENTS" ]]; then
export EMQX_LISTENER__SSL__EXTERNAL__MAX_CLIENTS=500000
fi
if [[ -z "$EMQX_LISTENER__WS__EXTERNAL__ACCEPTORS" ]]; then
export EMQX_LISTENER__WS__EXTERNAL__ACCEPTORS=16
fi
if [[ -z "$EMQX_LISTENER__WS__EXTERNAL__MAX_CLIENTS" ]]; then
export EMQX_LISTENER__WS__EXTERNAL__MAX_CLIENTS=250000
fi
# Fix issue #42 - export env EMQX_DASHBOARD__DEFAULT_USER__PASSWORD to configure
# 'dashboard.default_user.password' in etc/plugins/emqx_dashboard.conf
if [[ -n "$EMQX_ADMIN_PASSWORD" ]]; then
export EMQX_DASHBOARD__DEFAULT_USER__PASSWORD=$EMQX_ADMIN_PASSWORD
fi
# echo value of $VAR hiding secrets if any
# SYNOPSIS
# echo_value KEY VALUE
echo_value() {
# get MASK_CONFIG
MASK_CONFIG_FILTER="$MASK_CONFIG_FILTER|password|passwd|key|token|secret"
FORMAT_MASK_CONFIG_FILTER=$(echo "$MASK_CONFIG_FILTER" | sed -r -e 's/^[^A-Za-z0-9_]+//' -e 's/[^A-Za-z0-9_]+$//' -e 's/[^A-Za-z0-9_]+/|/g')
local key=$1
local value=$2
# check if contains sensitive value
if echo "$key" | grep -iqwE "$FORMAT_MASK_CONFIG_FILTER"; then
echo "$key=***secret***"
else
echo "$key=$value"
fi
}
# fill config on specific file if the key exists
# SYNOPSIS
# try_fill_config FILE KEY VALUE
try_fill_config() {
local file=$1
local key=$2
local value=$3
local escaped_key
# shellcheck disable=SC2001
escaped_key=$(echo "$key" | sed 's/[^a-zA-Z0-9_]/\\&/g')
local escaped_value
escaped_value=$(echo "$value" | sed 's/[\/&]/\\&/g')
if grep -qE "^[#[:space:]]*$escaped_key\s*=" "$file"; then
echo_value "$key" "$value"
if [[ -z "$value" ]]; then
sed -i -r "s/^[#[:space:]]*($escaped_key)\s*=\s*(.*)/# \1 = \2/" "$file"
else
sed -i -r "s/^[#[:space:]]*($escaped_key)\s*=\s*(.*)/\1 = $escaped_value/" "$file"
fi
# Check if config has a numbering system, but no existing configuration line in file
elif echo "$key" | grep -qE '\.\d+|\d+\.'; then
if [[ -n "$value" ]]; then
local template
template="$(echo "$escaped_key" | sed -r -e 's/\\\.[0-9]+/\\.[0-9]+/g' -e 's/[0-9]+\\\./[0-9]+\\./g')"
if grep -qE "^[#[:space:]]*$template\s*=" "$file"; then
echo_value "$key" "$value"
sed -i '$a'\\ "$file"
echo "$key = $value" >> "$file"
fi
fi
fi
}
# Catch all EMQX_ prefix environment variable and match it in configure file
CONFIG_FILE="$_EMQX_HOME/etc/emqx.conf"
CONFIG_PLUGINS="$_EMQX_HOME/etc/plugins"
for VAR in $(compgen -e); do
# Config normal keys such like node.name = emqx@127.0.0.1
if echo "$VAR" | grep -q '^EMQX_'; then
VAR_NAME=$(echo "$VAR" | sed -e 's/^EMQX_//' -e 's/__/./g' | tr '[:upper:]' '[:lower:]' | tr -d '[:cntrl:]')
VAR_VALUE=$(echo "${!VAR}" | tr -d '[:cntrl:]')
# Config in emqx.conf
try_fill_config "$CONFIG_FILE" "$VAR_NAME" "$VAR_VALUE"
# Config in plugins/*
for CONFIG_PLUGINS_FILE in "$CONFIG_PLUGINS"/*; do
try_fill_config "$CONFIG_PLUGINS_FILE" "$VAR_NAME" "$VAR_VALUE"
done
fi
done
# fill tuples on specific file
# SYNOPSIS
# fill_tuples FILE [ELEMENTS ...]
fill_tuples() {
local file=$1
local elements=${*:2}
for var in $elements; do
if grep -qE "\{\s*$var\s*,\s*(true|false)\s*\}\s*\." "$file"; then
sed -i -r "s/\{\s*($var)\s*,\s*(true|false)\s*\}\s*\./{\1, true}./1" "$file"
elif grep -q "$var\s*\." "$file"; then
# backward compatible.
sed -i -r "s/($var)\s*\./{\1, true}./1" "$file"
else
sed -i '$a'\\ "$file"
echo "{$var, true}." >>"$file"
fi
done
}
## EMQX Plugin load settings
# Plugins loaded by default
LOADED_PLUGINS="$_EMQX_HOME/data/loaded_plugins"
if [[ -n "$EMQX_LOADED_PLUGINS" ]]; then
EMQX_LOADED_PLUGINS=$(echo "$EMQX_LOADED_PLUGINS" | tr -d '[:cntrl:]' | sed -r -e 's/^[^A-Za-z0-9_]+//g' -e 's/[^A-Za-z0-9_]+$//g' -e 's/[^A-Za-z0-9_]+/ /g')
echo "EMQX_LOADED_PLUGINS=$EMQX_LOADED_PLUGINS"
# Parse module names and place `{module_name, true}.` tuples in `loaded_plugins`.
fill_tuples "$LOADED_PLUGINS" "$EMQX_LOADED_PLUGINS"
fi
## EMQX Modules load settings
# Modules loaded by default
LOADED_MODULES="$_EMQX_HOME/data/loaded_modules"
if [[ -n "$EMQX_LOADED_MODULES" ]]; then
EMQX_LOADED_MODULES=$(echo "$EMQX_LOADED_MODULES" | tr -d '[:cntrl:]' | sed -r -e 's/^[^A-Za-z0-9_]+//g' -e 's/[^A-Za-z0-9_]+$//g' -e 's/[^A-Za-z0-9_]+/ /g')
echo "EMQX_LOADED_MODULES=$EMQX_LOADED_MODULES"
# Parse module names and place `{module_name, true}.` tuples in `loaded_modules`.
fill_tuples "$LOADED_MODULES" "$EMQX_LOADED_MODULES"
fi
exec "$@"

60
deploy/docker/start.sh Executable file
View File

@ -0,0 +1,60 @@
#!/bin/sh
set -e -u
emqx_exit(){
# At least erlang.log.1 exists
if [ -f /opt/emqx/log/erlang.log.1 ]; then
# tail emqx.log.*
erlang_log=$(echo $(ls -t /opt/emqx/log/erlang.log.*) | awk '{print $1}')
num=$(sed -n -e '/LOGGING STARTED/=' ${erlang_log} | tail -1)
[ ! -z $num ] && [ $num -gt 2 ] && tail -n +$(expr $num - 2) ${erlang_log}
fi
echo "['$(date -u +"%Y-%m-%dT%H:%M:%SZ")']:emqx exit abnormally"
exit 1
}
## EMQ Main script
# When receiving the EXIT signal, execute emqx_exit function
trap "emqx_exit" EXIT
# Start and run emqx, and when emqx crashed, this container will stop
/opt/emqx/bin/emqx start
# Sleep 5 seconds to wait for the loaded plugins catch up.
sleep 5
echo "['$(date -u +"%Y-%m-%dT%H:%M:%SZ")']:emqx start"
## Fork tailing erlang.log, the fork is not killed after this script exits
## The assumption is that this is the docker entrypoint,
## hence docker container is terminated after entrypoint exists
tail -f /opt/emqx/log/erlang.log.1 &
# monitor emqx is running, or the docker must stop to let docker PaaS know
# warning: never use infinite loops such as `` while true; do sleep 1000; done`` here
# you must let user know emqx crashed and stop this container,
# and docker dispatching system can known and restart this container.
IDLE_TIME=0
MGMT_CONF='/opt/emqx/etc/plugins/emqx_management.conf'
MGMT_PORT=$(sed -n -r '/^management.listener.http[ \t]=[ \t].*$/p' $MGMT_CONF | sed -r 's/^management.listener.http = (.*)$/\1/g')
while [ $IDLE_TIME -lt 5 ]; do
IDLE_TIME=$(expr $IDLE_TIME + 1)
if curl http://localhost:${MGMT_PORT}/status >/dev/null 2>&1; then
IDLE_TIME=0
# Print the latest erlang.log
now_erlang_log=$(ps -ef |grep "tail -f /opt/emqx/log/erlang.log" |grep -v grep | sed -r "s/.*tail -f (.*)/\1/g")
new_erlang_log="$(ls -t /opt/emqx/log/erlang.log.* | head -1)"
if [ $now_erlang_log != $new_erlang_log ];then
tail -f $new_erlang_log &
kill $(ps -ef |grep "tail -f $now_erlang_log" | grep -v grep | awk '{print $1}')
fi
else
echo "['$(date -u +"%Y-%m-%dT%H:%M:%SZ")']:emqx not running, waiting for recovery in $((25-IDLE_TIME*5)) seconds"
fi
sleep 5
done
# If running to here (the result 5 times not is running, thus in 25s emqx is not running), exit docker image
# Then the high level PaaS, e.g. docker swarm mode, will know and alert, rebanlance this service

24
deploy/packages/README.md Normal file
View File

@ -0,0 +1,24 @@
emqx-packages
=============
EMQ X RPM/Debian Packages
NOTICE: Requires Erlang/OTP R21+ to build since 3.0 release.
How to use
----------------------------
```
cd project-root-directory-path
EMQX_DEPS_DEFAULT_VSN=${version} make emqx-pkg
```
License
-------
Apache License Version 2.0
Author
------
EMQ X Team.

View File

@ -0,0 +1,31 @@
# Keep this short to avoid bloating beam files with long file path info
TOPDIR := /tmp/emqx
SRCDIR := $(TOPDIR)/$(PKG_VSN)
BUILT := $(SRCDIR)/BUILT
EMQX_NAME=$(subst -pkg,,$(EMQX_BUILD))
TAR_PKG := $(EMQX_REL)/_build/$(EMQX_BUILD)/rel/emqx/emqx-$(PKG_VSN).tar.gz
SOURCE_PKG := $(EMQX_NAME)_$(PKG_VSN)_$(shell dpkg --print-architecture)
TARGET_PKG := $(EMQX_NAME)-$(SYSTEM)-$(PKG_VSN)-$(shell uname -m)
.PHONY: all
all: | $(BUILT)
cp -r debian $(SRCDIR)/
sed -i "s#<DATETIME>#$(shell date -u '+%a, %d %b %Y %T %z')#g" $(SRCDIR)/debian/changelog
sed -i "s#<VERSION>#$(PKG_VSN)#g" $(SRCDIR)/debian/changelog
if [ ! -z $(shell echo $(EMQX_NAME) |grep edge) ]; then \
sed -i "s/emqx-pkg/emqx-edge-pkg/g" $(SRCDIR)/debian/rules; \
sed -i "s debian/emqx debian/emqx-edge g" $(SRCDIR)/debian/rules; \
sed -i "s/Package: emqx/Package: emqx-edge/1" $(SRCDIR)/debian/control; \
fi
cd $(SRCDIR) && dpkg-buildpackage -us -uc
mkdir -p $(EMQX_REL)/_packages/$(EMQX_NAME)
cp $(SRCDIR)/../$(SOURCE_PKG).deb $(EMQX_REL)/_packages/$(EMQX_NAME)/$(TARGET_PKG).deb
$(BUILT):
mkdir -p $(TOPDIR) $(SRCDIR)
tar zxf $(TAR_PKG) -C $(SRCDIR)
clean:
rm -rf $(SRCDIR)

View File

@ -0,0 +1,5 @@
emqx (<VERSION>) unstable; urgency=medium
* See github commit history: https://github.com/emqx/emqx
-- emqx <contact@emqx.io> <DATETIME>

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1,12 @@
Source: emqx
Section: unknown
Priority: optional
Maintainer: emqx <contact@emqx.io>
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Homepage: https://www.emqx.io
Package: emqx
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: EMQX, a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OTP

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,150 @@
#! /bin/bash
### BEGIN INIT INFO
# Provides: emqx
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Erlang MQTT Broker
# Description: EMQX, a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OT
### END INIT INFO
NAME=emqx
DAEMON=/usr/bin/$NAME
SCRIPTNAME=/etc/init.d/$NAME
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
. /lib/lsb/init-functions
# `service` strips all environmental VARS so
# if no HOME was set in /etc/default/$NAME then set one here
# to the data directory for erlexec's sake
if [ -z "$HOME" ]; then
export HOME=/var/lib/emqx
fi
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
# Startup with the appropriate user
start-stop-daemon --start \
--name emqx \
--user emqx \
--exec $DAEMON -- start \
|| return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Identify the erts directory
ERTS_PATH=`$DAEMON ertspath`
# Attempt a clean shutdown.
$DAEMON stop
# waiting stop done sleep 5
sleep 5
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
# Make sure it's down by using a more direct approach
start-stop-daemon --stop \
--quiet \
--retry=TERM/30/KILL/5 \
--user emqx \
--exec $ERTS_PATH/run_erl
return $?
}
#
# Function that graceful reload the daemon/service
#
do_reload() {
# Restart the VM without exiting the process
$DAEMON restart && return $? || return 2
}
# Checks the status of a node
do_status() {
$DAEMON ping && echo $"$NAME is running" && return 0
echo $"$NAME is stopped" && return 2
}
case "$1" in
start)
log_daemon_msg "Starting $NAME"
$DAEMON ping >/dev/null 2>&1 && echo $"$NAME is already running" && exit 0
do_start
case "$?" in
0|1) log_end_msg 0 ;;
2) log_end_msg 1
exit 1
;;
esac
;;
stop)
log_daemon_msg "Stopping $NAME"
do_stop
case "$?" in
0|1) log_end_msg 0 ;;
2) log_end_msg 1
exit 1
;;
esac
;;
ping)
# See if the VM is alive
$DAEMON ping || exit $?
;;
reload|force-reload)
log_daemon_msg "Reloading $NAME"
do_reload
ES=$?
log_end_msg $ES
exit $ES
;;
restart)
log_daemon_msg "Restarting $NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 && exit 1 ;; # Old process is still running
*) log_end_msg 1 && exit 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1 && exit 1
;;
esac
;;
status)
do_status && exit 0 || exit $?
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|ping|restart|force-reload|status}" >&2
exit 3
;;
esac

View File

@ -0,0 +1,59 @@
#!/bin/sh
# postinst script for emqx
#
# see: dh_installdeb(1)
set -e
# install startup script
update-rc.d emqx defaults >/dev/null
# create group
if ! getent group emqx >/dev/null; then
addgroup --system emqx
fi
# create user
if ! getent passwd emqx >/dev/null; then
adduser --ingroup emqx \
--home /var/lib/emqx \
--disabled-password \
--system --shell /bin/bash --no-create-home \
--gecos "emqx user" emqx
fi
for i in lib run log; do
chown -R emqx:emqx /var/$i/emqx
done
chown -R emqx:emqx /usr/lib/emqx
chown -R emqx:emqx /etc/emqx
chmod 0755 /var/run/emqx /etc/emqx
chmod 0644 /etc/emqx/*
chmod -R +X /etc/emqx
chmod -R 0755 /usr/lib/emqx/bin
[ -f /usr/bin/emqx ] && rm /usr/bin/emqx
[ -f /usr/bin/emqx_ctl ] && rm /usr/bin/emqx_ctl
ln -s /usr/lib/emqx/bin/emqx /usr/bin/emqx
ln -s /usr/lib/emqx/bin/emqx_ctl /usr/bin/emqx_ctl
case "$1" in
configure)
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@ -0,0 +1,82 @@
#!/bin/sh
# postrm script for emqx
#
# see: dh_installdeb(1)
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
set -e
case "$1" in
purge)
rm -f /etc/default/emqx
# ensure we remove the rc.d scripts installed by postinst
update-rc.d emqx remove >/dev/null
if [ -d /var/lib/emqx ]; then
rm -r /var/lib/emqx
fi
if [ -d /var/log/emqx ]; then
rm -r /var/log/emqx
fi
if [ -d /var/run/emqx ]; then
rm -r /var/run/emqx
fi
if [ -d /etc/emqx ]; then
rm -r /etc/emqx
fi
if [ -e /etc/init.d/emqx ]; then
rm /etc/init.d/emqx
fi
# Remove User & Group, killing any process owned by them
if getent passwd emqx >/dev/null; then
pkill -u emqx || true
deluser --quiet --system emqx
fi
if getent group emqx >/dev/null; then
delgroup --quiet --system --only-if-empty emqx || true
fi
if [ -f /usr/bin/emqx ]; then
rm /usr/bin/emqx
fi
if [ -f /usr/bin/emqx_ctl ]; then
rm /usr/bin/emqx_ctl
fi
if [ -d /usr/lib/emqx ]; then
rm -r /usr/lib/emqx
fi
;;
remove)
rm /usr/bin/emqx
rm /usr/bin/emqx_ctl
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1\`" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@ -0,0 +1,65 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# modified for node_package by dizzyd@basho.com and jared@basho.com
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
## Clear variables that may confound our build of sub-projects; also
## note that it is necessary to use overlay_vars relative to .. as
## the generate command EXECUTES in rel/
build:
clean:
dh_clean
rm -f build
# make clean
## dh_shlibdeps was added to figure out the dependencies on shared libraries
## and will populate the ${shlibs:Depends} callout in the control file
install: build
dh_testdir
dh_testroot
dh_prep
dh_installdirs
mkdir -p debian/emqx/usr/lib/emqx
mkdir -p debian/emqx/var/lib/emqx
mkdir -p debian/emqx/var/run/emqx
mkdir -p debian/emqx/var/log/emqx
mkdir -p debian/emqx/usr/lib/emqx/bin
mkdir -p debian/emqx/etc/emqx
mkdir -p debian/emqx/etc/init.d
cp bin/* debian/emqx/usr/lib/emqx/bin
cp -R lib debian/emqx/usr/lib/emqx
cp -R erts* debian/emqx/usr/lib/emqx
cp -R releases debian/emqx/usr/lib/emqx
cp -R etc/* debian/emqx/etc/emqx
cp -R data/* debian/emqx/var/lib/emqx
install -m755 debian/init.script debian/emqx/etc/init.d/emqx
dh_shlibdeps
# We have nothing to do by default.
binary-indep: install build-stamp
build-stamp:
# Build architecture-dependent files here.
binary-arch: install
dh_strip -a
dh_compress -a
dh_installdeb
dh_gencontrol
dh_builddeb
binary: binary-indep binary-arch

View File

@ -0,0 +1,62 @@
# Keep this short to avoid bloating beam files with long file path info
TOPDIR := /tmp/emqx
SRCDIR := $(TOPDIR)/$(PKG_VSN)
BUILT := $(SRCDIR)/BUILT
dash := -
none :=
space := $(none) $(none)
RPM_VSN ?= $(shell echo $(PKG_VSN) | grep -oE "[0-9]+\.[0-9]+(\.[0-9]+)?")
RPM_REL ?= $(shell echo $(PKG_VSN) | grep -oE "(alpha|beta|rc)\.[0-9]")
ARCH:=$(shell uname -m)
ifeq ($(ARCH),mips64)
ARCH:=mips64el
endif
EMQX_NAME=$(subst -pkg,,$(EMQX_BUILD))
TAR_PKG := $(EMQX_REL)/_build/$(EMQX_BUILD)/rel/emqx/emqx-$(PKG_VSN).tar.gz
TARGET_PKG := $(EMQX_NAME)-$(SYSTEM)-$(PKG_VSN)-$(ARCH)
ifeq ($(RPM_REL),)
# no tail
RPM_REL := 1
endif
SOURCE_PKG := emqx-$(SYSTEM)-$(RPM_VSN)-$(RPM_REL).$(ARCH)
SYSTEMD := $(shell if command -v systemctl >/dev/null 2>&1; then echo yes; fi)
# Not $(PWD) as it does not work for make -C
CURDIR := $(shell pwd)
SERVICE_SRC := $(if $(SYSTEMD),$(CURDIR)/emqx.service,$(CURDIR)/init.script)
SERVICE_DST := $(if $(SYSTEMD),%{_unitdir}/emqx.service,%{_initddir}/emqx)
POST_ADDITION := $(if $(SYSTEMD),%systemd_post %{_name}.service,echo post)
PREUN_ADDITION := $(if $(SYSTEMD),%systemd_preun %{_name}.service,echo preun)
.PHONY: all
all: | $(BUILT)
cd $(SRCDIR)
rpmbuild -v -bb \
--define "_package_name $(EMQX_NAME)" \
--define "_name emqx" \
--define "_topdir $(TOPDIR)" \
--define "_version $(RPM_VSN)" \
--define "_reldir $(SRCDIR)" \
--define "_release $(RPM_REL)" \
--define "_service_src $(SERVICE_SRC)" \
--define "_service_dst $(SERVICE_DST)" \
--define "_post_addition $(POST_ADDITION)" \
--define "_preun_addition $(PREUN_ADDITION)" \
--define "_ostype -$(SYSTEM)" \
--define "_sharedstatedir /var/lib" \
emqx.spec
mkdir -p $(EMQX_REL)/_packages/$(EMQX_NAME)
cp $(TOPDIR)/RPMS/$(ARCH)/$(SOURCE_PKG).rpm $(EMQX_REL)/_packages/$(EMQX_NAME)/$(TARGET_PKG).rpm
$(BUILT):
mkdir -p $(TOPDIR) $(SRCDIR) $(SRCDIR)/BUILT
tar zxf $(TAR_PKG) -C $(SRCDIR)
clean:
rm -rf $(SRCDIR)

View File

@ -0,0 +1,15 @@
[Unit]
Description=emqx daemon
After=network.target
[Service]
User=emqx
Group=emqx
Type=forking
Environment=HOME=/var/lib/emqx
ExecStart=/bin/sh /usr/bin/emqx start
LimitNOFILE=1048576
ExecStop=/bin/sh /usr/bin/emqx stop
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,102 @@
%define debug_package %{nil}
%define _user %{_name}
%define _group %{_name}
%define _conf_dir %{_sysconfdir}/%{_name}
%define _log_dir %{_var}/log/%{_name}
%define _lib_home /usr/lib/%{_name}
%define _var_home %{_sharedstatedir}/%{_name}
%define _build_name_fmt %{_arch}/%{_name}%{?_ostype}-%{_version}-%{_release}.%{_arch}.rpm
%define _build_id_links none
Name: %{_package_name}
Version: %{_version}
Release: %{_release}%{?dist}
Summary: emqx
Group: System Environment/Daemons
License: Apache License Version 2.0
URL: https://www.emqx.io
BuildRoot: %{_tmppath}/%{_name}-%{_version}-root
Provides: %{_name}
AutoReq: 0
%description
EMQX, a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OTP.
%prep
%build
%install
mkdir -p %{buildroot}%{_lib_home}
mkdir -p %{buildroot}%{_log_dir}
mkdir -p %{buildroot}%{_unitdir}
mkdir -p %{buildroot}%{_conf_dir}
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_var_home}
mkdir -p %{buildroot}%{_initddir}
cp -R %{_reldir}/lib %{buildroot}%{_lib_home}/
cp -R %{_reldir}/erts-* %{buildroot}%{_lib_home}/
cp -R %{_reldir}/releases %{buildroot}%{_lib_home}/
cp -R %{_reldir}/bin %{buildroot}%{_lib_home}/
cp -R %{_reldir}/etc/* %{buildroot}%{_conf_dir}/
cp -R %{_reldir}/data/* %{buildroot}%{_var_home}/
install -m644 %{_service_src} %{buildroot}%{_service_dst}
%pre
if [ $1 = 1 ]; then
# Initial installation
/usr/bin/getent group %{_group} >/dev/null || /usr/sbin/groupadd -r %{_group}
if ! /usr/bin/getent passwd %{_user} >/dev/null ; then
/usr/sbin/useradd -r -g %{_group} -m -d %{_sharedstatedir}/%{_name} -c "%{_name}" %{_user}
fi
fi
%post
if [ $1 = 1 ]; then
ln -s %{_lib_home}/bin/emqx %{_bindir}/emqx
ln -s %{_lib_home}/bin/emqx_ctl %{_bindir}/emqx_ctl
fi
%{_post_addition}
if [ -e %{_initddir}/%{_name} ] ; then
/sbin/chkconfig --add %{_name}
else
systemctl enable %{_name}.service
fi
chown -R %{_user}:%{_group} %{_lib_home}
%preun
%{_preun_addition}
# Only on uninstall, not upgrades
if [ $1 = 0 ]; then
if [ -e %{_initddir}/%{_name} ] ; then
/sbin/service %{_name} stop > /dev/null 2>&1
/sbin/chkconfig --del %{_name}
else
systemctl disable %{_name}.service
fi
rm -f %{_bindir}/emqx
rm -f %{_bindir}/emqx_ctl
fi
exit 0
%postun
if [ $1 = 0 ]; then
rm -rf %{_lib_home}
fi
exit 0
%files
%defattr(-,root,root)
%{_service_dst}
%attr(-,%{_user},%{_group}) %{_lib_home}/*
%attr(-,%{_user},%{_group}) %dir %{_var_home}
%attr(-,%{_user},%{_group}) %config(noreplace) %{_var_home}/*
%attr(-,%{_user},%{_group}) %dir %{_log_dir}
%attr(-,%{_user},%{_group}) %config(noreplace) %{_conf_dir}/*
%clean
rm -rf %{buildroot}
%changelog

146
deploy/packages/rpm/init.script Executable file
View File

@ -0,0 +1,146 @@
#!/bin/sh
#
# emqx
#
# chkconfig: 2345 80 30
# description: EMQX, a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OTP
# processname: beam
#
# Source function library.
. /etc/rc.d/init.d/functions
RETVAL=0
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="EMQX, a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OTP"
NAME=emqx
DAEMON=/usr/bin/$NAME
lockfile=/var/lock/subsys/$NAME
mkdir -p /var/run/$NAME
pidfile=/var/run/$NAME/$NAME.pid
# Check for script, config and data dirs
[ -x /usr/bin/$NAME ] || exit 0
[ -d /etc/$NAME ] || exit 0
[ -d /var/lib/$NAME ] || exit 0
# Read configuration variable file if it is present and readable
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
# `service` strips all environmental VARS so
# if no HOME was set in /etc/sysconfig/$NAME then set one here
# to the data directory for erlexec's sake
if [ -z "$HOME" ]; then
export HOME=
fi
status -p $pidfile -l $(basename $lockfile) $NAME >/dev/null 2>&1
running=$?
find_pid() {
ps ax | grep beam.smp | grep -E "\-progname.+$NAME" | awk '{print $1}'
}
check_pid_status() {
local pid="$(find_pid)"
if [ "$pid" = "" ]; then
# prog not running?
return 1
else
# running
return 0
fi
}
start() {
# Start daemons.
echo -n $"Starting emqx: "
$DAEMON start
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
touch $lockfile
find_pid > $pidfile
success
else
failure $"$NAME start"
fi
echo
return $RETVAL
}
stop() {
# Stop daemon.
echo -n $"Shutting down emqx: "
$DAEMON stop 2>/dev/null
for n in $(seq 1 10); do
sleep 1
check_pid_status
RETVAL=$?
if [ $RETVAL -eq 1 ]; then
break
fi
done
if [ $RETVAL -eq 1 ]; then
rm -f $lockfile $pidfile
success
echo && return 0
else
failure $"$NAME stop"
echo && return 1
fi
}
hardstop() {
echo -n $"Shutting down $NAME: "
su - emqx -c "ps -ef | grep beam.smp | grep '\-progname $NAME ' | grep -v grep | awk '{print \$2}' | xargs kill -9"
for n in $(seq 1 10); do
sleep 1
check_pid_status
RETVAL=$?
if [ $RETVAL -eq 1 ]; then
break
fi
done
if [ $RETVAL -eq 1 ]; then
rm -f $lockfile $pidfile
success
echo && return 0
else
failure $"$NAME hardstop"
echo && return 1
fi
}
# See how we were called.
case "$1" in
start)
[ $running -eq 0 ] && exit 0
start
;;
stop)
stop
;;
restart|force-reload)
[ $running -eq 0 ] && stop
start
;;
hardstop)
[ $running -eq 0 ] || exit 0
hardstop
;;
condrestart|try-restart)
[ $running -eq 0 ] || exit 0
restart
;;
status)
status -p $pidfile -l $(basename $lockfile) $NAME
;;
ping)
$DAEMON ping || exit $?
;;
*)
echo $"Usage: $0 {start|stop|restart|force-reload|hardstop|condrestart|try-restart|status|ping}"
exit 1
esac
exit $?

View File

@ -7,6 +7,7 @@ QEMU_ARCH ?= x86_64
ARCH ?= amd64 ARCH ?= amd64
QEMU_VERSION ?= v5.0.0-2 QEMU_VERSION ?= v5.0.0-2
OS ?= alpine OS ?= alpine
PKG_VSN ?= $(shell git describe --tags --match '[0-9]*')
EMQX_NAME = $(subst emqx/,,$(TARGET)) EMQX_NAME = $(subst emqx/,,$(TARGET))
ARCH_LIST = amd64 arm64v8 arm32v7 i386 s390x ARCH_LIST = amd64 arm64v8 arm32v7 i386 s390x
@ -40,7 +41,6 @@ docker-build:
@echo "DOCKER BUILD: qemu arch - $(QEMU_ARCH)." @echo "DOCKER BUILD: qemu arch - $(QEMU_ARCH)."
@echo "DOCKER BUILD: docker repo - $(TARGET) " @echo "DOCKER BUILD: docker repo - $(TARGET) "
@echo "DOCKER BUILD: emqx name - $(EMQX_NAME)." @echo "DOCKER BUILD: emqx name - $(EMQX_NAME)."
@echo "DOCKER BUILD: emqx version - $(EMQX_DEPS_DEFAULT_VSN)."
## Prepare qemu to build images other then x86_64 on travis ## Prepare qemu to build images other then x86_64 on travis
@echo "PREPARE: Qemu" \ @echo "PREPARE: Qemu" \
@ -53,7 +53,7 @@ docker-build:
&& cd - && cd -
@docker build --no-cache \ @docker build --no-cache \
--build-arg EMQX_DEPS_DEFAULT_VSN=$(EMQX_DEPS_DEFAULT_VSN) \ --build-arg PKG_VSN=$(PKG_VSN) \
--build-arg BUILD_FROM=emqx/build-env:erl22.3-alpine-$(ARCH) \ --build-arg BUILD_FROM=emqx/build-env:erl22.3-alpine-$(ARCH) \
--build-arg RUN_FROM=$(ARCH)/alpine:3.11 \ --build-arg RUN_FROM=$(ARCH)/alpine:3.11 \
--build-arg EMQX_NAME=$(EMQX_NAME) \ --build-arg EMQX_NAME=$(EMQX_NAME) \

View File

@ -266,4 +266,4 @@ str(L) when is_list(L) -> L;
str(B) when is_binary(B) -> unicode:characters_to_list(B, utf8). str(B) when is_binary(B) -> unicode:characters_to_list(B, utf8).
erl_opts_i() -> erl_opts_i() ->
[{i, Dir} || Dir <- filelib:wildcard("apps/**/include")]. [{i, Dir} || Dir <- filelib:wildcard(filename:join(["apps", "**", "include"]))].