今天又看到邮箱里阿里云提示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

这个编排文件虽然短短几行,但是做了好几件事:

  1. 创建一个叫做traefik的具名容器,方便今后直接使用 docker cmd traefik 进行操作。
  2. 拉取目前最新的 traefik:1.6.3-alpine 镜像,未来升级,如果API没有变化,直接修改这里的版本,就能获取新版本的软件了。
  3. 使用Docker简化掉supervisor维护进程守护。
  4. 将宿主机的配置文件和证书挂载到容器Traefik容器内部。
  5. 使用用户自定义的配置启动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属性。

那么它做了哪些事情呢:

  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