Traefik v2 下设置反向代理及https redirect

为了解决docker前端的反向代理,在尝试了传统的haproxy后打算试一试新产品。Traefik的吸引力在于

  • v1和v2版本的配置语法不一样,阅读文档需注意
  • 配置可以通过启动参数或配置文件传入,但前者并没有完全实现后者的功能
  • 文档过于简陋
  • 可以自行更新 Let’s encrypt证书,避免3个月手工操作(即使用acme.sh 还是需要配置cron job)
  • 自动发现同一个docker instance上的服务并通过label来定义route/service
  • 多种多样的middleware 可以简单实现ratelimit或者replace path

缺点在于

下面是一个简单例子。docker-compose.yml 中有2个container,其中trafik找到了nginx的服务并且自动issue/renew了证书。

nginx:
    command: nginx -c /etc/netbox-nginx/nginx.conf
    image: nginx:1.17-alpine
    depends_on:
    - netbox
      #ports:
    expose:
      - 8080
    volumes:
    - netbox-static-files:/opt/netbox/netbox/static:ro
    - netbox-nginx-config:/etc/netbox-nginx/:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.netbox_http.rule=Host(`netbox.fastobject.net`)"
      - "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true"
      - "traefik.http.routers.netbox_http.middlewares=https-redirect"
      - "traefik.http.routers.netbox_https.rule=Host(`netbox.fastobject.net`)"
      - "traefik.http.routers.netbox_https.tls=true"
      - "traefik.http.routers.netbox_https.tls.certresolver=le"
      - "traefik.http.services.netbox-service.loadbalancer.server.port=8080"
  traefik:
    image: traefik:v2.1
    container_name: "traefik"
    command:
      --log.level=INFO
      --providers.docker=true
      --providers.docker.exposedbydefault=false
      --entryPoints.web.address=:80
      --entryPoints.websecure.address=:443
      --certificatesResolvers.le.acme.email=henryxxxxx@gmail.com
      --certificatesResolvers.le.acme.storage=acme.json
      --certificatesResolvers.le.acme.httpChallenge.entryPoint=web
    ports:
      - 443:443
      - 80:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

这里还实现了http 80 -> http 443 的自动302跳转。这个配置通过middleware实现,坑点在于需要配置2个route,分别针对http和https,否则跳转是404 not found。另一个容易被忽略的地方是需要被反向代理的container使用expose而不是ports来暴露端口。expose只会把端口暴露给其他container 而ports会把这个端口绑定在host上。在这个例子里只需要把端口expose给trafik即可。

Keepalived实现廉价HA

最近在dockerize各种home lab上的服务,其中遇到的一个问题就是如何实现HA。最方便的实现方式是打开eBay.com搜索F5 Big IP 购买硬件Load Balancer。最廉价的方式是多个设备间跑VRRP协议实现自动Fail over切换。Keepalived 就是一个精细生活VRRP的软件实现。

具体用例为两台Docker node上各跑了一个container运行unbound提供recursive DNS服务。任意一台机器下线(不管是container下线或是node下线)都由另一台机器在同一个IP地址下继续提供服务。切换期间网络不不会中断。

既然已经有了两台运转正常的Docker node,显然Keepalived跑在Container里是最经济且便于管理的。这里用到的docker image是 https://github.com/osixia/docker-keepalived

需要注意的是两台机器的sysctl需要设置 net.ipv4.ip_nonlocal_bind=1 在Ubuntu上可以通过修改 /etc/sysctl.conf 并运行 sysctl -p /etc/sysctl.conf 实现。在RancherOS上需要修改cloud-config.yml 具体参考官方文档

并且这两个container需要跑在host network上且赋予 CAP_NET_ADMIN (--cap-add NET_ADMIN

以下是用docker-composer 和直接运行docker run的两种配置。

# docker-composer.yml

keepalived:
     container_name: keepalived
     image: arcts/keepalived:latest
     environment:
       - KEEPALIVED_AUTOCONF=true
       - KEEPALIVED_STATE=MASTER
       - KEEPALIVED_INTERFACE=eth0
       - KEEPALIVED_VIRTUAL_ROUTER_ID=2
       - KEEPALIVED_UNICAST_SRC_IP=10.2.1.10
       - KEEPALIVED_UNICAST_PEER_0=10.2.1.11
       - KEEPALIVED_TRACK_INTERFACE_1=eth0
       - KEEPALIVED_VIRTUAL_IPADDRESS_1="10.2.1.12/24 dev eth0"
     network_mode: "host"
     cap_add:
       - NET_ADMIN
# docker run command
docker run -d --name keepalived --net=host --cap-add NET_ADMIN \
-e KEEPALIVED_AUTOCONF=true               	\
-e KEEPALIVED_STATE=BACKUP           		\
-e KEEPALIVED_INTERFACE=eth0                \
-e KEEPALIVED_VIRTUAL_ROUTER_ID=2           \
-e KEEPALIVED_UNICAST_SRC_IP=10.2.1.11    	\
-e KEEPALIVED_UNICAST_PEER_0=10.2.1.10     \
-e KEEPALIVED_TRACK_INTERFACE_1=eth0        \
-e KEEPALIVED_VIRTUAL_IPADDRESS_1="10.2.1.12/24 dev eth0" \
arcts/keepalived

至此 IP地址 10.2.1.12 就由 10.2.1.10 为master ,10.2.1.10下线 则10.2.1.11 自动由backup转为master继续提供服务