今天又看到邮箱里阿里云提示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: 官方网站。
软件介绍官方写了很多,在使用了几个月后,个人体会是:
- 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,输入一下内容:
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
这个编排文件虽然短短几行,但是做了好几件事:
- 创建一个叫做
traefik
的具名容器,方便今后直接使用docker cmd traefik
进行操作。 - 拉取目前最新的
traefik:1.6.3-alpine
镜像,未来升级,如果API没有变化,直接修改这里的版本,就能获取新版本的软件了。 - 使用Docker简化掉supervisor维护进程守护。
- 将宿主机的配置文件和证书挂载到容器Traefik容器内部。
- 使用用户自定义的配置启动Traefik。
接着创建一个名为traefik.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证书:
acme.sh --issue -d 'soulteary.com' -d '*.soulteary.com' --dns dns_cf --ecc --log
如果你有多个证书,想开启SNI,copy一份证书配置,在下面再重复书写一次就可以了。
当你准备好了docker-compose中依赖的ssl证书,上面的toml配置文件后,执行:
docker-compose up -d
然后访问 https://d.yourdomain.com/ 输入你在配置文件中指定的账号密码,就能看到traefik的管理界面了。
启动应用实例
同样的,为你的应用创建一个docker-compose.yml文件:
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属性。
那么它做了哪些事情呢:
- 使用轻量的Nginx Alpine镜像提供Web服务。
- 配置对外提供服务的域名为www.soulteary.com和soulteary.com。
- 默认启用Https作为服务协议,对Http进行跳转。
- 默认将www.soulteary.com重写为soulteary.com。
- 设置一个宽松的CROS,开启HTTPS严格模式。
- 将数据文件挂载到容器内部。
- 上述的路由暴露相关的操作,全部交给Traefik的服务发现来进行操作,不需编码。
当你准备好网站数据文件之后,同样执行compose命令,你会看到网站很欢脱的启动了起来,并且在刚刚的dashboard中多了一组“前后端程序”,如果你的网站是使用PHP、Java作为backend,可以使用Traefik轻松启用多实例负载。
最后
至此,海外服务器和国内服务器的服务器软件架构就一致了,现在所有的应用升级迁移终于可以完美体验秒级操作,迁移扩展也可以变的更轻松简单,哦也。
此外,完全使用了容器技术和服务发现模式进行应用维护,可用性监控和性能数据收集十分重要,接下来考虑分享一个Docker轻量管理系统以及强大的Prometheus。
–EOF