bi系统是一类旨在帮助企业和组织分析、可视化和理解其业务数据的软件工具
之前了解过商业的阿里quickbi,腾讯bi,开源的话用superset,据了解新老公司不少会调研superset
https://github.com/apache/superset/releases/tag/4.0.2
这里我使用了最新的包,经过测试发现一年前的镜像更新到最新版本以后也没有出现版本问题

部署

1、规范部署基本都在/data下,解压目录后

mkdir /data/superset/ -p
包解压下来
$# ls
4.0.2.tar.gz  superset-4.0.2
# 这就是源码目录了
superset-4.0.2

2、介绍一下主要的配置文件

docker-compose.yml # 包含很多部署工具,用这个compose启动的superset web界面各种静态文件刷不出来,基本是空白页,并且多了很多业务用不到的功能,完全没必要用。特点是会把宿主机目录的文件都映射到容器内
docker-compose-non-dev.yml # 纯净版,需要依赖自行安装即可,不会有dev镜像那种静态文件的问题
superset/config.py # 主配置文件,自定义代码和LDAP配置都在这里
docker/docker-bootstrap.sh # 容器启动脚本,可以添加一些命令
docker/docker-init.sh # 初始化脚本,这里经常修改默认用户名密码

3、如果是使用默认的AUTH_DB验证方式,现在就可以启动了

docker-compose  -f docker-compose-non-dev.yml up -d

配置LDAP

由于是py写的,用了flask去做ldap验证,更详细的用法参考如下链接
https://flask-appbuilder.readthedocs.io/en/latest/security.html#authentication-ldap

1、修改superset/config.py,vim下搜索模式找到LDAP,在附近增加代码

from flask_appbuilder.security.manager import AUTH_DB,AUTH_LDAP
AUTH_TYPE = AUTH_LDAP  # 默认是AUTH_DB
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Admin"   # 这里最好用Public
AUTH_LDAP_SERVER = "ldap://ip:port"
AUTH_LDAP_USE_TLS = False
AUTH_LDAP_BIND_USER = 'cn=admin,dc=xx,dc=com'
AUTH_LDAP_BIND_PASSWORD = "password"
AUTH_LDAP_SEARCH = "dc=xx,dc=com"
AUTH_LDAP_UID_FIELD = "cn"
AUTH_LDAP_ALLOW_SELF_SIGNED=True
AUTH_LDAP_APPEND_DOMAIN=False
#AUTH_LDAP_FIRSTNAME_FIELD="givenName"
#AUTH_LDAP_LASTNAME_FIELD="sn"
AUTH_LDAP_USE_TLS=False
AUTH_USER_REGISTRATION=True

2、默认情况下superset只支持一种验证方式,用了LDAP以后DB验证就会失败,所以我们需要自定义脚本同时支持两种登录方式

3、在superset程序目录下增加custom_security_manager.py

from superset.security import SupersetSecurityManager
from flask_appbuilder.security.views import AuthLDAPView
from flask_appbuilder.security.views import expose
from flask import g, redirect, flash
from flask_appbuilder.security.forms import LoginForm_db
from flask_login import login_user
from flask_appbuilder._compat import as_unicode

class AuthLocalAndLDAPView(AuthLDAPView):
    @expose("/login/", methods=["GET", "POST"])
    def login(self):
        if g.user is not None and g.user.is_authenticated:
            return redirect(self.appbuilder.get_url_for_index)
        form = LoginForm_db()
        if form.validate_on_submit():
            user = self.appbuilder.sm.auth_user_ldap(
                form.username.data, form.password.data
            )
            if not user:
                user = self.appbuilder.sm.auth_user_db(
                    form.username.data, form.password.data
                )
            if user:
                login_user(user, remember=False)
                return redirect(self.appbuilder.get_url_for_index)
            else:
                flash(as_unicode(self.invalid_login_message), "warning")
                return redirect(self.appbuilder.get_url_for_login)
        return self.render_template(
            self.login_template, title=self.title, form=form, appbuilder=self.appbuilder
        )


class CustomSecurityManager(SupersetSecurityManager):
    authldapview = AuthLocalAndLDAPView
    def __init__(self, appbuilder):
        super(CustomSecurityManager, self).__init__(appbuilder)

4、导入SupersetSecurityManager支持自定义安全管理器,第二条则是导入我们自定义的包,最后做一个引用

from superset.security import SupersetSecurityManager
from superset.custom_security_manager import CustomSecurityManager
CUSTOM_SECURITY_MANAGER = CustomSecurityManager

5、满足两种验证方式的LDAP配置如下

from superset.security import SupersetSecurityManager
from flask_appbuilder.security.manager import AUTH_DB,AUTH_LDAP
from superset.custom_security_manager import CustomSecurityManager
AUTH_TYPE = AUTH_LDAP
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Admin"
AUTH_LDAP_SERVER = "ldap://10.198.224.89:389"
AUTH_LDAP_USE_TLS = False
AUTH_LDAP_BIND_USER = 'cn=admin,dc=peekaboo,dc=group'
AUTH_LDAP_BIND_PASSWORD = "BAH5vnd_dtv8avz5bxp"
AUTH_LDAP_SEARCH = "dc=peekaboo,dc=group"
AUTH_LDAP_UID_FIELD = "cn"
AUTH_LDAP_ALLOW_SELF_SIGNED=True
AUTH_LDAP_APPEND_DOMAIN=False
#AUTH_LDAP_FIRSTNAME_FIELD="givenName"
#AUTH_LDAP_LASTNAME_FIELD="sn"
AUTH_LDAP_USE_TLS=False
AUTH_USER_REGISTRATION=True
CUSTOM_SECURITY_MANAGER = CustomSecurityManager

6、配置文件没问题以后,我们把宿主机的config.py和custom_security_manager.py挂载到容器里
官方的compose使用了锚点写法,省略一些重复代码

x-superset-volumes:
  &superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
  - ./docker:/app/docker
  - superset_home:/app/superset_home
  #- ./superset:/app/superset
  - ./superset/config.py:/app/superset/config.py
  - ./superset/custom_security_manager.py:/app/superset/custom_security_manager.py

7、指定compose启动

docker-compose  -f docker-compose-non-dev.yml up -d

8、使用http://ip:8088访问
如果DB用户和LDAP用户相同,可以用两种密码登录

小问题

compose安装的superset主流数据库都可以连接,但是没有clickhouse,临时处理方式进入容器pip install clickhouse-connect

这里我们找到docker-compose中的一段代码,发现容器启动脚本是docker-bootstrap.sh

  superset:
    env_file: docker/.env-non-dev
    image: *superset-image
    container_name: superset_app
    command: ["/app/docker/docker-bootstrap.sh", "app-gunicorn"]
    user: "root"
    restart: unless-stopped
    ports:
      - 8088:8088
    depends_on: *superset-depends-on
    volumes: *superset-volumes

去修改脚本,在set -eo pipefail下方增加

# add
# 使用 Python 尝试导入 clickhouse_connect 来检查是否已安装
if ! python -c "import clickhouse_connect" 2>/dev/null; then
    echo "clickhouse-connect not found, installing..."
    pip install clickhouse-connect

    # 再次验证是否安装成功
    python -c "import clickhouse_connect" && echo "clickhouse-connect installed successfully"
else
    echo "clickhouse-connect is already installed."
fi
# end

需要down后up才会生效,可以在superset_app容器启动时看到对应输出

参考
https://medium.com/@ozan/configure-ldap-and-local-user-login-on-superset-69fa4df4ee24