今天又看到邮箱里阿里云提示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,输入一下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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的配置文件(我已经做了简单汉化,你可以参考使用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | ################################################################ # 全局设置 ################################################################ # 激活调试模式 (默认关闭) 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证书:
1 | acme.sh --issue -d 'soulteary.com' -d '*.soulteary.com' --dns dns_cf --ecc --log |
如果你有多个证书,想开启SNI,copy一份证书配置,在下面再重复书写一次就可以了。
当你准备好了docker-compose中依赖的ssl证书,上面的toml配置文件后,执行:
1 | docker-compose up -d |
然后访问 https://d.yourdomain.com/ 输入你在配置文件中指定的账号密码,就能看到traefik的管理界面了。
启动应用实例
同样的,为你的应用创建一个docker-compose.yml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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