背景

笔者在阿里云上有1台服务器,本着宁愿数据丢失也要榨干服务器的资源的目的,在那台服务器上启用了docker单节点集群,并且通过部署容器的方式开启了很多服务,比如 基础服务有 mysql、redis、rabbitmq、mongodb
应用服务包括 configserver、discovery 、springboot应用等 服务器本身应用也只是笔者在玩,并没有几个人访问,也没有什么请求量,所以负载均衡部分完全可以去掉,我要的只是大家共享80端口,而docker化的nginx需要一些配置文件,太过繁琐

目前使用的是阿里云针对容器的负载均衡slb,虽然使用的是最低配置的负载均衡实例,但每个月还是需要扣费差不多20元,对于笔者而言,花的这部分钱没有给我带来收益、也没有让我很爽,自然想要砍掉。接下来我们就看从技术上怎么去砍。

思路

前面也提到,我们不需要负载均衡,我们只要路由功能,受阿里云文档(自定义路由-使用手册_服务发现和负载均衡_Swarm 集群) 启发,我尝试使用 acs/proxy 镜像 但由于是基于 swarm 而我们的是swarm mode 自然行不通,后来也尝试过 dockercloud/haproxy 镜像也不行,

转机

终于在google中看到不少人推荐 jwilder/nginx-proxy 但是也没有v3版本的配置文件,但最终google告诉我 Centos7.4下用Docker-Compose部署WordPress(续) 我从中找到了v3版本的配置,终于可以删掉slb了,想想还有点小激动。

##但是 当把服务部署上去发现依然不行,于是尝试找出不行的根源,测试被代理的应用,访问正常,那么问题应该是在nginx上,但以前没有玩过,于是简单看了一下文档,进去nginx容器中查看发现是这样

upstream dev.**.com {
	## Can be connect with "multi-host-network" network
	# springboot_web.1.1ss
	server 10.1.0.8 down;
}

难道是健康检查没过,于是接着找原因,换了各种可能的关键词google才告诉我这篇文章 server down / VIRTUAL_PORT ignored · Issue #802 · jwilder/nginx-proxy 并紧接着在关联的 issue #1050 中找到可能解决的方案

#——> 在dockerfile中加 EXPOSE [容器端口] 大概解释一下为什么会出现这种情况,当部署jwilder/nginx-proxy以后,nginx-proxy由于bug只能读取到dockerfile中定义的EXPOSE 所以在检测被代理的容器是否可用的时候就不能正确判断了,就会导致是down的状态,自然会导致502的情况了。可以放心的删除slb了

总结一下

  • 需要在dockerfile中指定容器的端口才能被代理正确“上线”

  • 需要部署jwilder/nginx-proxy 可以参考v3模板 需要提前创建好网络multi-host-network哦
version: '3.2'
services:
  nginx:
    image: jwilder/nginx-proxy:0.7.0
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro  #将宿主机的docker.sock绑定到nginx,这样,今后添加新的站点时,nginx将会自动发现站点并重启服务
    restart: always
    networks:
      - multi-host-network
    deploy:
      restart_policy:
        condition: on-failure
networks:
  multi-host-network:
    external: true

  • 应用配置
version: '3.2'
services:
  web:
    image: 你的镜像地址
    container_name: springbootweb
    restart: always
    ports:
      - 80
    labels:
      aliyun.probe.initial_delay_seconds: '10'
      aliyun.log_store_myblog: stdout
    environment:
      VIRTUAL_HOST: dev.***.com
    networks:
      - multi-host-network
    deploy:
      restart_policy:
        condition: on-failure
networks:
  multi-host-network:
    external: true

2019-06-26 12:42:30 星期三 [========]

  • 在此基础上,那如何支持https呢 a. 我们需要换一个支持自动更新https证书的acme服务的镜像 b. 我们需要在需要https的服务上添加变量以标明我这个服务需要https,nginx才会自动开启https支持与跳转

https://github.com/Neilpang/nginx-proxy 这个镜像支持https自动申请与续期,但是笔者折腾了很久都不行,所以换了一个 https://github.com/roura356a/tengine-proxy 发现可能是配置了多个域名的原因,保留一个域名即可成功,逐个替换成功后再试两个域名就没问题。

配置如下

version: '3.7'
services:
  nginx:
    image: roura/tengine-proxy
    environment:
      TZ: Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"  # ssl 默认是443端口,因此需要将443端口映射到宿主机上
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro  #将宿主机的docker.sock绑定到nginx,这样,今后添加新的站点时,nginx将会自动发现站点并重启服务
      - /usr/proxy/certs:/etc/nginx/certs
      - /usr/proxy/acme:/acmecerts
      - /usr/proxy/conf.d:/etc/nginx/conf.d
    networks:
      - basork
    deploy:
      replicas: 1
      resources:
        limits:
          cpus: '0.50'
          memory: 400M
      restart_policy:
        condition: on-failure
      update_config:
        parallelism: 1
        delay: 30s
        order: start-first
networks:
  base-network:
    external: 
      name: etWrk

同时需要在应用上添加环境变量

  ENABLE_ACME: 'true'

总的配置如下

version: '3.6'
services:
  web:
    image: registry-vpc.cn-hangzhou.aliyuncs.com/xxxx/oneblog
    restart: always
    ports:
      - 80
    environment:
      VIRTUAL_HOST: blog.xxx.com
      ENABLE_ACME: 'true'
    networks:
      - wrk
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      update_config:
        parallelism: 1
        delay: 30s
        order: start-first
networks:
  web-network:
    external: 
      name: mork