)
wttr.in 基于 Salt Stack 的自动化部署从零构建可运行的天气服务含 pillar、systemd、authbind 全流程【免费下载链接】wttr.in:partly_sunny: The right way to check the weather项目地址: https://gitcode.com/gh_mirrors/wt/wttr.in本指南以仓库中 share/salt/README.md 为骨架结合同目录下的 init.sls、pillar.sls、start.sh、wegorc 与 wttr.service 五份文件完整还原 wttr.in 在 Salt Stack 管理下的部署姿态。读完本文你将掌握如何把 wttr.in 源码、Python 运行时、GeoLite2 地理数据库与 wego 天气后端拼接为一个 systemd 管理的常驻服务并学会用 Pillar 注入 API Key、用 Jinja 模板渲染配置、用 authbind 让非 root 用户绑定 80 端口。该示例同时是一份不错的 Salt 状态文件SLS教学案例——依赖声明、顺序编排、watch 触发与目录权限的写法都值得借鉴。一、示例定位一套有主见的OpinionatedSalt 部署wttr.in 是一个通过命令行与终端直接查询天气的服务官方仓库中除了 Docker 方式见 Dockerfile还提供了一套基于 Salt Stack 的基础设施即代码部署示例即share/salt/目录下的全部内容。README 开篇标题即为Opinionated example of deployment via Salt Stack——有主见意味着这套示例带着明确的取舍它是作者团队实际使用的部署形态而非通用可插拔方案。文章核心就是围绕它给出的 5 个假设、2 个注意点Caveats以及 6 个组成文件展开。1.1 五个前置假设Assumptions原文档明确列出了部署这套 Salt 状态的前提任何想复用它的人都应先对照检查srv:srv用户与组已存在——它被当作通用的服务运行身份generic service runnerwttr 进程、缓存目录、authbind 端口授权都以它为准服务直接暴露在 80 端口——README 特别提醒you really want to add a reverse SSL proxy in between即生产环境强烈建议在它前面再挂一层反向 SSL 代理如 nginx/Caddy 终结 TLS你已经安装或愿意部署 Salt Stackmaster/minion 体系需要手工拼装——pillar.sls要移动到saltroot/pillar/下其余文件放进saltroot/wttr/Salt 通过salt://wttr/...路径引用它们默认使用metric-ms单位制——想要其他单位如 metric、imperialJust roll your own wegorc即自己改 wegorc 里的units项。1.2 两个注意点Caveats不保证适配最新 masterREADME 明确写道这套示例Doesnt do enough to make a recent master checkout work, i.e. needs further improvement最后已知可用版本是提交0d76ba4a3e112694665af3040807835883b22。这一点对当前仓库尤为重要当前 main.go 已是 Go 重写版本go run . srv config.yaml启动通过 share/config/config.yaml 提供配置而 Salt 示例启动的是 Python 时代的bin/srv.py该文件在当前 master 中已不存在。因此本文档所述的 Salt 部署对应的是历史 Python 实现详见下文 1.3 的版本适配说明读者可将其视为部署 wttr.in 的完整工程思路与 Salt 写法来学习落地新版本时需将启动命令替换为 Go 二进制。1.3 与当前仓库 Go 版的对应关系补充说明虽然bin/srv.py已随 Python 时代退场但 Salt 示例中通过环境变量传参的约定在当前仓库仍有迹可循——Dockerfile 第 51-54 行依然设置了ENV WTTR_MYDIR/app ENV WTTR_GEOLITE/app/GeoLite2-City.mmdb ENV WTTR_LISTEN_HOST0.0.0.0 ENV WTTR_LISTEN_PORT8002与 start.sh 中导出的WTTR_*系列变量一脉相承说明源码目录、GeoLite 库路径、监听地址端口这三类部署要素在两种架构下都是核心配置面差异仅在于 Go 版把这些内容收敛进了 YAML 配置监听端口等见 internal/server/config.goGeoLite 路径见 internal/ip/config.go。二、目录结构与 Salt 文件的组装方式share/salt/目录共 5 个文件 1 个 README文件在 Salt 体系中的角色部署落点init.sls状态文件SLS描述全部资源与依赖saltroot/wttr/init.slspillar.slsPillar 数据注入 wttr 的 API Keysaltroot/pillar/pillar.slsstart.sh服务启动脚本导出环境变量/srv/ephemeral/start.shsalt://wttr/start.shwegorcwego 天气后端配置文件Jinja 模板/srv/ephemeral/.wegorcsalt://wttr/wegorcwttr.servicesystemd unit交给 authbind 启动/etc/systemd/system/wttr.servicesalt://wttr/wttr.serviceREADME 明确要求手工拼装pillar.sls移到saltroot/pillar/其余进入saltroot/wttr/。这样 minion 端执行state.apply wttr时salt://wttr/...的引用才能解析到对应源文件。三、Pillar把 API Key 与 SLS 解耦pillar.sls 内容极简只有一段wttr: apikey: insert-api-key-here-and-make-this-pillar-available-to-salt要点它定义了pillar[wttr][apikey]这个键供 init.sls 中的 wegorc 模板读取占位值insert-api-key-here...需要替换为真实的 forecast.io API Key把密钥放进 Pillar 而非 SLS 正文是 Salt 的标准实践Pillar 可以按 minion 加密传输配合 GPG renderer 效果更佳且 SLS 文件可以安全地纳入版本库。四、init.sls 逐段拆解一次 state.apply 装出完整服务init.sls 是整个部署的核心它声明了 9 类资源并借require/require_in/watch编织出安装顺序。下面按逻辑分组讲解。4.1 服务本体watch 驱动自动重启wttr: service.running: - enable: True - watch: - file: /srv/ephemeral/start.sh - git: wttr-repo - require: - pkg: wttr-dependencies - git: wttr-repo - cmd: wego - archive: geolite-dbservice.running管理名为wttr的 systemd 服务对应 wttr.service 的 unit 名watch 语义当被 watch 的start.sh或 git 仓库发生变化时Salt 会自动 restart 服务——这是代码更新即生效的关键require 语义启动服务前必须保证依赖包已装、仓库已检出、wego 已编译、GeoLite 库已解压完成顺序由 Salt 的状态编排保证。4.2 依赖包面向 Ubuntu 18.04 的清单wttr-dependencies: pkg.installed: - pkgs: - golang - gawk - python-setuptools - python-dev - python-dnspython - python-geoip2 - python-geopy - python-gevent - python-flask - python-pil - authbind注释明确包名来自 Ubuntu 18.04换发行版需自行调整。功能归类Go 工具链golang用于编译 wegoPython 运行时与 web 框架python-dev、python-setuptools、python-flask旧版 wttr 是 Flask 应用地理/网络库python-geoip2读 GeoLite2 数据库、python-geopy坐标反查、python-dnspythonDNS 解析、python-gevent并发图像处理python-pil生成 PNG 输出文本处理gawkawk 实现服务内部脚本使用端口绑定authbind见 4.8。4.3 代码检出git.latest 跟踪 masterwttr-repo: git.latest: - name: https://github.com/chubin/wttr.in - rev: master - target: /srv/ephemeral/wttr.in - require: - /srv/ephemeral从官方仓库 clone 到/srv/ephemeral/wttr.in跟踪master分支require指向file.directory的/srv/ephemeral见 4.7保证父目录先创建git.latest会在每次 apply 时检测远端更新并 pull——这也是服务被watch到后自动重启的触发源之一。4.4 启动脚本与 wegorcfile.managed Jinja 模板wttr-start: file.managed: - name: /srv/ephemeral/start.sh - source: salt://wttr/start.sh - mode: 0770 - user: srv - group: srv wegorc: file.managed: - name: /srv/ephemeral/.wegorc - user: srv - group: srv - source: salt://wttr/wegorc - template: jinja - context: apikey: {{ pillar[wttr][apikey] }}start.sh落到/srv/ephemeral/start.sh权限0770属主srv:srvwegorc使用Jinja 模板渲染模板中的{{ apikey }}见 wegorc 第 21 行forecast-api-key{{ apikey }}由context注入取值来自 Pillar。这正是密钥不进 SLS、只进 Pillar的落地方式两文件都属主srv:srv保证服务进程有读权限。4.5 GeoLite2 数据库archive.extracted 自动解压geolite-db: archive.extracted: - name: /srv/ephemeral - source: http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz - source_hash: http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz.md5 - keep_source: True - options: --strip-components1 # flatten directory structure - enforce_toplevel: False从 MaxMind 官方地址拉取GeoLite2-City.tar.gz并校验其.md5哈希--strip-components1打平目录结构使解压后的GeoLite2-City.mmdb直接出现在/srv/ephemeral/下与 start.sh 中WTTR_GEOLITE/srv/ephemeral/GeoLite2-City.mmdb对应keep_source: True保留压缩包enforce_toplevel: False允许压缩包顶层含多个条目不强制单根目录。4.6 wego 后端cmd.run 幂等安装wego: cmd.run: - onlyif: test ! -e /srv/ephemeral/bin/wego - env: - GOPATH: /srv/ephemeral - name: go get -u github.com/schachmat/wego go install github.com/schachmat/wego - cwd: /srv/ephemeral/ - require: - pkg: wttr-dependencies - file: wegorcwegogithub.com/schachmat/wego是 wttr.in 的天气数据后端之一注释指出Could benefit from improvement, wont get updated automatically at all——即这个步骤不会自动更新wegoonlyif: test ! -e .../bin/wego实现幂等二进制已存在就跳过依赖pkg需要 golang与file: wegorc需要先有配置编译产物在$GOPATH/bin/wego与 start.sh 的WTTR_WEGO$GOPATH/bin/wego一致。4.7 目录与权限file.directory Jinja 循环/srv/ephemeral: file.directory: - makedirs: True {% for dir in /srv/ephemeral/wttr.in/log,/srv/ephemeral/wttr.in/cache %} {{ dir }}: file.directory: - user: srv - group: srv - makedirs: True - recurse: - user - group - require_in: - service: wttr {% endfor %}先建/srv/ephemeral根目录再用Jinja 循环批量生成log/与cache/两个运行时目录的状态声明关键技巧require_in: - service: wttr是反向依赖——不是目录依赖服务而是服务依赖目录确保 log/cache 目录一定先于服务启动存在recurse: user/group递归修正目录内属主为srv:srv。4.8 systemd unit 与 authbind让非 root 绑定 80 端口/etc/systemd/system/wttr.service: file: - managed - source: salt://wttr/wttr.service - require: - file: wttr-start - file: authbind-80 - require_in: - service: wttr authbind-80: file: - managed - name: /etc/authbind/byport/80 - user: srv - group: srv - mode: 770 - replace: False - require: - pkg: wttr-dependenciesunit 文件由salt://wttr/wttr.service渲染到/etc/systemd/system/authbind 授权文件/etc/authbind/byport/80属主srv:srv、权限770这使srv用户被授权绑定 80 端口无需 root 运行服务——这是 README直接暴露 80 端口假设的技术前提replace: False若文件已存在则不覆盖防止授权被意外重置两个文件都require_in服务保证服务启动前落盘。配合 wttr.service 看闭环[Unit] DescriptionWttr weather service [Service] ExecStart/usr/bin/authbind --deep /srv/ephemeral/start.sh Restartalways [Install] WantedBymulti-user.targetExecStart用authbind --deep包裹启动脚本--deep表示对脚本内部派生的所有子进程同样授权Restartalways保证进程崩溃自动拉起WantedBymulti-user.target使服务开机自启配合 SLS 里service.running的enable: True。五、start.sh五个环境变量串起整个运行时start.sh 是整个部署的接线图#!/bin/sh export WEGORC/srv/ephemeral/.wegorc export GOPATH/srv/ephemeral export WTTR_MYDIR/srv/ephemeral/wttr.in export WTTR_GEOLITE/srv/ephemeral/GeoLite2-City.mmdb export WTTR_WEGO$GOPATH/bin/wego export WTTR_LISTEN_HOST0.0.0.0 export WTTR_LISTEN_PORT80 python $WTTR_MYDIR/bin/srv.py逐项说明变量值作用WEGORC/srv/ephemeral/.wegorc指向 wego 配置文件即 4.4 渲染出的 Jinja 产物GOPATH/srv/ephemeralwego 二进制所在 GOPATH 根WTTR_MYDIR/srv/ephemeral/wttr.inwttr.in 源码目录服务主程序所在WTTR_GEOLITE/srv/ephemeral/GeoLite2-City.mmdb城市级 GeoIP 数据库来自 4.5WTTR_WEGO$GOPATH/bin/wegowego 可执行文件路径WTTR_LISTEN_HOST0.0.0.0监听所有网卡WTTR_LISTEN_PORT80监听 80 端口配合 authbind最后一行python $WTTR_MYDIR/bin/srv.py是历史 Python 版本的启动入口当前 Go 版对应go run . srv config.yaml见 main.go 第 207-219 行。这也是 README Caveats 提醒不保证适配最新 master的直接原因——部署新版本时只需把此处的启动命令替换为 Go 二进制即可复用整套 Salt 基建。六、wegorcwego 配置语法与全部参数wegorc 采用github.com/schachmat/ingo的配置语法空行与#开头的行被忽略其余必须形如KEYVALUE且VALUE 不能带引号。文件注释里逐条标明了后端backend、前端frontend等归属与默认值整理如下配置项默认值归属说明aat-coordsfalseaat 前端是否显示地理坐标aat-monochromefalseaat 前端是否单色输出backendforecast.io—使用的天气数据后端days3—预报显示天数forecast-api-key空forecast 后端API Key由 Pillar 注入forecast-debugfalseforecast 后端打印原始请求/响应forecast-langenforecast 后端请求语言frontendascii-art-table—输出前端格式jsn-no-indentfalsejson 前端是否不缩进 JSONlocation40.748,-73.985—默认查询坐标纽约owm-api-key空openweathermap 后端OWM 的 API Keyowm-debugfalseopenweathermap 后端调试开关owm-langenopenweathermap 后端请求语言unitsmetric—单位制metric / imperial / si /metric-mswwo-api-key空worldweatheronline 后端WWO 的 API Keywwo-debugfalseworldweatheronline 后端调试开关wwo-langenworldweatheronline 后端请求语言示例中实际取值backendforecast.io、days3、frontendascii-art-table、unitsmetric-msforecast-api-key{{ apikey }}由 Jinja 渲染成 Pillar 中的真实密钥。README 的假设 5metric-sm units即对应这里的unitsmetric-ms。七、部署步骤速览实践清单综合 README 与各文件复现这套部署的完整路径为准备 Salt 环境部署 Salt master/minion确认 minion 可连通放置文件将 pillar.sls 放入saltroot/pillar/替换其中的 API Key 占位符其余 4 个文件放入saltroot/wttr/确保srv:srv用户存在若缺失需先行创建在 minion 上执行salt target state.apply wttrSalt 将按依赖顺序完成装包 → 建目录 → 拉代码 → 渲染 start.sh/wegorc → 解压 GeoLite2 → 编译 wego → 落盘 systemd unit 与 authbind 授权 → 启动并 enable 服务验证访问http://minion-ip:80/应返回天气文本后续修改仓库或 start.sh 后再次 apply服务会被watch自动重启生产加固按 README 建议在服务前增加反向 SSL 代理wttr 本身只监听 80 明文端口。八、局限性与演进建议这套示例的价值与局限都很清晰优点完整演示了 Salt 的核心语法组合——pkg.installed、git.latest、file.managed Jinja、archive.extracted、cmd.run、service.running以及watch/require/require_in三种编排原语authbind 授权非 root 绑定 80 端口的手法同样可复用。局限正如 README 自述它无法保证适配最新 master当前仓库已是 Go 架构Python 的bin/srv.py不再存在wego 不会自动更新依赖包清单固定在 Ubuntu 18.04服务直接暴露明文端口。落地建议在新版 wttr.in 上复用本方案时只需替换 4.6 的 wego 步骤为 Go 构建、将 5.1 启动命令改为 main.go 的srv子命令并指向 share/config/config.yaml同时将监听端口、GeoLite 路径等参数迁移到 YAML 配置中参考 internal/server/config.go 与 internal/ip/config.go 的字段定义即可沿用整套 Salt 状态管理与自动化运维流程。【免费下载链接】wttr.in:partly_sunny: The right way to check the weather项目地址: https://gitcode.com/gh_mirrors/wt/wttr.in创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考