本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2018年06月11日 统计字数: 7074字 阅读时间: 15分钟阅读 本文链接: https://soulteary.com/2018/06/11/use-server-side-discovery-improve-development.html ----- # 使用“服务发现”改善开发体验 今天又看到邮箱里阿里云提示ECS实例要迁移的消息,想了想早晚都要迁,还不如早点迁移了事。 心说如果迁移后服务起不来了,那么就把国内国外的服务都改成微服务模式好了。 顺手点击了迁移,没过多久看到短信通知服务迁移完毕,尝试性的打开网站,发现服务果然起不来了,于是就开始了架构简化之旅。 本文以更新网站架构为例,展示如何使用Traefik进行微服务化的站点快速启停,应用迭代,如果你想直接获取示例配置,请跳转第三小节:实战迁移。 ## 历史问题 从14年把Apache迁移到Nginx之后,就开始用Nginx当服务网关,不管后面的网站和应用怎么拆,前面都势必会起一个Nginx,或者“变种”版本提供一些特殊能力: - Tengine: 文件合并、内存缓存、页面内容替换、动态模块... - OpenResty: 文件合并、内存缓存、Lua WebHook接口... 但是这里有一个很悲催的事情,使用Nginx当“前排”,后面的内容修改之后: - 要么得额外清除一遍文件&内存缓存。 - 要么得把该进程重载或者重启一遍。 - 个人使用的都是自己编译的版本,打成容器文件尺寸惊人,不适合服务器之间传递,失去装箱交付的价值。 而且即使使用了容器技术,这个前排的Nginx也不适合使用容器执行: - Nginx和Docker耦合过重,升级其中一个模块的时候,整体是不可用的。 - 不利于日志的记录和分析。 - 完全没必要让流量多绕一层:`Docker -> Nginx Proxy -> Docker App` 然后整体结构就变成了这小节内容开头的样子: 静态编译一个功能强大的Nginx服务,使用反向代理功能代理后面的Docker App,并在Nginx + Redis中进行强缓存。 配合发布脚本定时重载&重启进行倒是也使用了一阵,但是个人感觉“脏”了许多,使用体验不太舒服。 ## 拥抱未来 基于Nginx的服务发现也不是没有,比如大名鼎鼎的Consul、Etcd,有不少公司就在使用,也有一些公司使用一些动态`upstream`的模块进行内部服务域名注册。 但是有两个额外的问题: - 对Nginx的依赖过重,很多场景下,我们是可以不需要Nginx来提供服务的。 - 对于我之前的玩法,Nginx每次升级都要重新编译一次,开发体验很不友好,还有额外维护成本。 而且 Tengine、OpenResty 毕竟过度定制,很多内容打补丁比较麻烦,比如每次升级OpenSSL的时候。五月的时候,做过了网站应用的简化,拔掉了很多对Nginx的能力的依赖,现在直接使用官方的Nginx Alpine镜像就能满足我的需求。 在寻找简单高效的工具的过程,我发现了Traefik: [官方网站](https://traefik.io/)。 软件介绍官方写了很多,在使用了几个月后,个人体会是: - Nginx能做的事情,多数它都能做,它做起来更简单。 - Nginx不能做的服务发现,是它的主打功能之一。 - 如果你要定制`header`或者进行`redirect`,编写Nginx规则会给Traefik写规则写着顺手。 - 可折腾程度,Traefik没有Nginx高,所以如果追求极致的性能,还是选择Nginx吧。 - 支持一堆主流的服务商和容器编排工具和服务发现工具,包括开头提到的Consul,Etcd... ## 实战迁移 因为是个人服务,使用的实例越少,模块之间的隔离性越好,今后维护起来的成本就越低,于是我采用最简单的架构: - 服务后端:Docker - 编排工具:docker-compose (不使用的话,直接使用docker原生命令也行) - 服务网关:Traefik - 具体应用:Nginx + 静态文件 、 compose编排起来的容器应用 ### 启动 Traefik 实例 安装docker和docker-compose请参考官方网站文档,这里就略过了。 当docker和docker-compose就绪后,创建一个docker-compose.yml,输入一下内容: ```yaml version: '3' services: reverse-proxy: container_name: traefik image: traefik:1.6.3-alpine restart: always ports: - 80:80 - 443:443 networks: - traefik command: traefik -c /etc/traefik.toml --sendAnonymousUsage=false volumes: - /var/run/docker.sock:/var/run/docker.sock - ./traefik.toml:/etc/traefik.toml - ./ssl:/data/ssl - ./logs:/data/logs # 记得先创建外部网卡 # docker network create traefik networks: traefik: external: true ``` 这个编排文件虽然短短几行,但是做了好几件事: 1. 创建一个叫做`traefik`的具名容器,方便今后直接使用 `docker cmd traefik` 进行操作。 2. 拉取目前最新的 `traefik:1.6.3-alpine` 镜像,未来升级,如果API没有变化,直接修改这里的版本,就能获取新版本的软件了。 3. 使用Docker简化掉supervisor维护进程守护。 4. 将宿主机的配置文件和证书挂载到容器Traefik容器内部。 5. 使用用户自定义的配置启动Traefik。 接着创建一个名为traefik.toml的配置文件(我已经做了简单汉化,你可以参考使用): ```Toml ################################################################ # 全局设置 ################################################################ # 激活调试模式 (默认关闭) debug = false # 日志等级 (默认 ERROR) logLevel = "INFO" # 全局入口点类型 (默认 http) defaultEntryPoints = ["https", "http"] # 不上报统计信息 sendAnonymousUsage = false ################################################################ # 入口点设置 ################################################################ [entryPoints] # 默认前端 [entryPoints.http] address = ":80" compress = true [entryPoints.http.redirect] entryPoint = "https" [entryPoints.https] address = ":443" compress = true [entryPoints.https.tls] [[entryPoints.https.tls.certificates]] certFile = "/data/ssl/soulteary.com.cer" keyFile = "/data/ssl/soulteary.com.key" # 控制台端口 [entryPoints.traefik-api] address = ":4399" [entryPoints.traefik-api.auth] [entryPoints.traefik-api.auth.basic] users = [ "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", ] # Ping端口 [entryPoints.traefik-ping] address = ":4398" ################################################################ # 文件代理设置 ################################################################ [file] [backends] [backends.dashboard] [backends.dashboard.servers.server1] url = "http://127.0.0.1:4399" [backends.ping] [backends.ping.servers.server1] url = "http://127.0.0.1:4398" [frontends] [frontends.dashboard] entrypoints = ["https"] backend = "dashboard" [frontends.dashboard.routes.route01] rule = "Host:d.soulteary.com" [frontends.ping] entrypoints = ["https"] backend = "ping" [frontends.ping.routes.route01] rule = "Host:p.soulteary.com" ################################################################ # 日志设置 ################################################################ [traefikLog] # 日志默认会打印到stdout,如果不指定filePath,默认使用stdout filePath = "/data/logs/traefik.log" # 格式目前支持 "json" 和 "common"(默认) format = "common" ################################################################ # 访问日志 配置 ################################################################ [accessLog] # 日志默认会打印到stdout,如果不指定filePath,默认使用stdout filePath = "/data/logs/access.log" # 格式目前支持 "json" 和 "common"(默认) format = "common" ################################################################ # API 及 控制台 配置 ################################################################ # 启用API以及控制台 [api] # 入口点名称 entryPoint = "traefik-api" # 开启控制台(默认开启) dashboard = true # 默认协议 defaultEntryPoints = ["https"] ################################################################ # Ping 配置 ################################################################ # 启用 ping [ping] # 入口点名称 entryPoint = "traefik-ping" ################################################################ # Docker 后端配置 ################################################################ # 启用Docker后端 [docker] # Docker服务后端 endpoint = "unix:///var/run/docker.sock" # 默认域名 domain = "traefix.soulteary.com" # 监控docker变化 watch = true # 使用自定义模板(可选) # filename = "docker.tmpl" # 对容器默认进行暴露(默认开启) # 如果关闭选项,则容器不包含 `traefik.enable=true` 标签,就不会被暴露 exposedbydefault = true # 使用绑定端口的IP地址取代内部私有网络(默认关闭) usebindportip = false # 使用 Swarm Mode (默认关闭) swarmmode = false # 启用 docker TLS 链接. # # 可选配置 # # [docker.tls] # ca = "/etc/ssl/ca.crt" # cert = "/etc/ssl/docker.crt" # key = "/etc/ssl/docker.key" # insecureskipverify = true ``` 关于证书,你可以直接使用acme.sh进行申请,下面演示如何使用CloudFlare DNS验证方式申请ecc证书: ```bash acme.sh --issue -d 'soulteary.com' -d '*.soulteary.com' --dns dns_cf --ecc --log ``` 如果你有多个证书,想开启SNI,copy一份证书配置,在下面再重复书写一次就可以了。 当你准备好了docker-compose中依赖的ssl证书,上面的toml配置文件后,执行: ```bash docker-compose up -d ``` 然后访问 https://d.yourdomain.com/ 输入你在配置文件中指定的账号密码,就能看到traefik的管理界面了。 ### 启动应用实例 同样的,为你的应用创建一个docker-compose.yml文件: ```yaml version: '3' services: nginx: image: nginx:1.15.0-alpine restart: always labels: - "traefik.enable=true" - "traefik.port=80" - "traefik.frontend.rule=Host:soulteary.com,www.soulteary.com,dev.soulteary.com" - "traefik.frontend.entryPoints=https,http" - "traefik.frontend.redirect.regex=^https?://www.soulteary.com/(.*)" - "traefik.frontend.redirect.replacement=https://soulteary.com/$${1}" - "traefik.frontend.headers.SSLRedirect=true" - "traefik.frontend.headers.SSLProxyHeaders=X-Forwarded-Proto:https" - "traefik.frontend.headers.STSSeconds=315360000" - "traefik.frontend.headers.frameDeny=true" - "traefik.frontend.headers.customResponseHeaders=Access-Control-Allow-Origin:*" networks: - traefik expose: - 80 - 443 volumes: - ./mime.types:/etc/nginx/mime.types - ./public:/usr/share/nginx/html extra_hosts: - "soulteary.com:127.0.0.1" - "www.soulteary.com:127.0.0.1" - "dev.soulteary.com:127.0.0.1" networks: traefik: external: true ``` 可以看到,相比较之前的配置文件,这个配置文件多了一堆labels属性。 那么它做了哪些事情呢: 1. 使用轻量的Nginx Alpine镜像提供Web服务。 2. 配置对外提供服务的域名为www.soulteary.com和soulteary.com。 3. 默认启用Https作为服务协议,对Http进行跳转。 4. 默认将www.soulteary.com重写为soulteary.com。 5. 设置一个宽松的CROS,开启HTTPS严格模式。 6. 将数据文件挂载到容器内部。 7. 上述的路由暴露相关的操作,全部交给Traefik的服务发现来进行操作,不需编码。 当你准备好网站数据文件之后,同样执行compose命令,你会看到网站很欢脱的启动了起来,并且在刚刚的dashboard中多了一组“前后端程序”,如果你的网站是使用PHP、Java作为backend,可以使用Traefik轻松启用多实例负载。 ## 最后 至此,海外服务器和国内服务器的服务器软件架构就一致了,现在所有的应用升级迁移终于可以完美体验秒级操作,迁移扩展也可以变的更轻松简单,哦也。 此外,完全使用了容器技术和服务发现模式进行应用维护,可用性监控和性能数据收集十分重要,接下来考虑分享一个Docker轻量管理系统以及强大的Prometheus。 --EOF