微服务Tars入门以及踩坑记录
微服务Tars入门以及踩坑记录
什么是微服务
近几年,微服务这个词闯入我们的视线范围。通常跟微服务相对的是单体应用,即将所有功能都打包成在一个独立单元的应用程序。微服务的架构就是将单个应用程序划分成各种小的、互相连接的微服务,一个微服务完成一个比较单一的功能,相互之间保持独立和解耦合。不同服务内部的开发技术可以不一致。
当然,拆分成多个服务,关于服务的管理也引出了一系列问题。为了解决这些问题,提出了一系列概念:服务注册与发现、服务监控、服务治理等等。微服务的这些问题可以通过引入微服务框架来帮助我们去解决。
目前业界比较成熟的微服务框架有阿里的Dubbo、Pivotal公司的Spring Cloud、腾讯的Tars、google的gRPC等等。
我这里选择Tars作为入门学习的微服务框架。学习过程中,发现这跟我平时使用的PHP框架很不一样,除了要学习使用TarsPHP这一开发框架,微服务的环境部署和发布节点之外,还涉及到很多网络、服务器方面的知识。都是自己所欠缺需要恶补的。而且还需要考虑服务边界的划分,服务与服务之间是如何调用的。
发布节点时报错
使用docker-compose进行部署,发布节点的时候遇到问题: 出现batchPatch err:NodeImp.cpp:patchPro:111| error:ip 172.25.0.1 is invalid错误
看似一个ip无效的报错,其实涉及到容器通信方面,必须先了解很多关于网络的知识,才能逐一排查错误原因。这里就所涉及到的知识进行一个梳理,然后再排查分析。
docker 网络
1. docker network操作
先查看下docker针对网络有哪些操作,使用命令:docker network --help
比如create命令:
# 创建一个名为tars的桥接(bridge)虚拟网络,网关172.25.0.1,网段为172.25.0.0
使用命令:docker network create -d bridge --subnet=172.25.0.0/16 --gateway=172.25.0.1 tars
我们也可以使用命令查看创建网络的可选配置项:docker network create --help
如下图:
2. docker的网络模式
安装 Docker 以后,会默认创建三种网络,可以通过 docker network ls 查看:
1)bridge(桥接模式)
桥接模式是docker 的默认网络设置,当docker服务启动时,会在主机上创建一个名为docker0的虚拟以太网桥,并选择一个和宿主机不同的IP地址和子网分配给docker0网桥。
示意图:
网络模式选择桥接模式的容器,就会连接上docker0这个网桥,再通过nat的转换,通过宿主机的网卡,连接外网,就能达到上外网的目的。
默认情况下,守护进程会创建一对对等虚拟设备接口 veth pair,将其中一个接口设置为容器的 eth0 接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx 这样的名字命名,从而将宿主机上的所有容器都连接到这个内部网络上。
这里,我启动了三个容器mysql、tars-node ,tars-framework,就会有三个容器网卡,以及一个docker0虚拟网卡,这三个容器网卡都会桥接到docker0网卡,docker0网卡与物理机网卡连接使用的是nat技术,如下图:
使用命令:ip addr (ip a 或 ip add list),查看网卡状态
使用命令查看bridge模式的详细信息:docker network inspect bridge
2)host模式
该模式下容器是不会拥有自己的ip地址,而是使用宿主机的ip地址和端口。这种模式的好处就是网络性能比桥接模式的好。缺点就是会占用宿主机的端口,网络的隔离性不太好
3)none
none模式没有IP地址,无法连接外网,等于就是断网的状态,作用就是用于测试,生产环境一般不会用到这种
4)container
新创建的容器不会创建自己的网卡和配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。
3. docker-compose.yml文件分析
1 version: "3" 2 3 services: 4 mysql: 5 image: mysql:5.6 6 container_name: compose-tars-mysql 7 ports: 8 - "3307:3306" 9 restart: always 10 environment: 11 MYSQL_ROOT_PASSWORD: "123456" 12 volumes: 13 - ./mysql/data:/var/lib/mysql:rw 14 - ./source/Shanghai:/etc/localtime 15 networks: 16 internal: 17 ipv4_address: 172.25.1.2 18 framework: 19 image: tarscloud/framework:v2.4.0 20 container_name: compose-tars-framework 21 ports: 22 - "3000:3000" 23 - "3001:3001" 24 restart: always 25 networks: 26 internal: 27 ipv4_address: 172.25.1.3 28 environment: 29 MYSQL_HOST: "172.25.1.2" 30 # MYSQL_HOST: "123.56.***.184" 31 MYSQL_ROOT_PASSWORD: "123456" 32 MYSQL_USER: "root" 33 MYSQL_PORT: 3306 34 # MYSQL_PORT: 3307 35 REBUILD: "false" 36 INET: eth0 37 SLAVE: "false" 38 volumes: 39 - ./framework/data:/data/tars:rw 40 - ./source/Shanghai:/etc/localtime 41 depends_on: 42 - mysql 43 node: 44 image: tarscloud/tars-node:latest 45 container_name: compose-tars-node 46 restart: always 47 networks: 48 internal: 49 ipv4_address: 172.25.1.5 50 volumes: 51 - ./node/data:/data/tars:rw 52 - ./source/Shanghai:/etc/localtime 53 environment: 54 INET: eth0 55 WEB_HOST: http://172.25.1.3:3000 56 # WEB_HOST: http://123.56.***.184:3000 57 ports: 58 - "9000-9009:9000-9009" 59 depends_on: 60 - framework 61 networks: 62 internal: 63 driver: bridge 64 ipam: 65 config: 66 - subnet: 172.25.1.0/16
depends_on:定义容器启动顺序 (此选项解决了容器之间的依赖关系)
4. IP、网段、子网掩码(网络掩码)
从上面看到networks的设置中,网段为172.25.1.0/16,这里面就涉及到ip、相同网段、网络掩码的相关知识。
1)什么是IP地址
IP地址相当于网络中的身份唯一认证ID,跟身份证ID一样是唯一的,唯一不同的是,IP地址是可以变的,只是不管怎么变,都将会是唯一的。Mac地址的性质更加接近于身份证ID,它是设备的唯一ID。
IP地址目前普遍是IPv4版本,由32位二进制数分成4组,每组1字节Byte(8Bit)组成。分别用十进制表示再用圆点隔开,就是现在的172.25.1.0。
2)IP地址的构成
IP地址 = 网络地址 + 主机地址
比如:192.168.1.168(IP地址) = 192.168.1.0 (网络地址) + 0.0.0.168(主机地址)
网络地址、主机地址是怎么计算出来的呢?就需要先简单学习下子网掩码
3)子网掩码(subnet mask)
比如这里的172.25.1.0/16中的16,指的是子网掩码的长度,16代表16个1,用子网掩码来表示,就是:255.255.0.0。它的作用主要是用来区分网络地址和主机地址。
将ip地址转为二进制,子网掩码转为二进制,通过按位与最终得到网段号。根据按位与的特性,可以看到172.25这个网段是不变的,后面的都可以改变。
排查错误原因
下面我们就开始提到用TarsDocker部署后,针对发布节点时遇到的错误进行排查分析。
分析:这个错误是这台机器上的tarnode识别过来的发布请求的非法,不是tarsAdminRegistry的ip。
1)查看想要发布节点到的对应容器
可以看到,这里发布的结点是172.25.1.5,我们是想通过这个ip发布到对应的容器tars-node
现在我们来看下这个容器的详细信息:docker inspect containerid
根据报错的信息:error:ip 172.25.0.1 is invalid,反应出tars找到的容器节点错误,找到的其实是网关,tars无法根据ip:172.25.0.1找到与它连接对应的容器,因此也就无法将服务发布到对相应容器。因此很快报ip无效的错误,流程根本都还没有走到检查打包的代码这一步。
2) 考虑换成容器逐个启动的方式,看能否成功发布节点
我先使用docker-compose down将启动的容器都卸载,然后用docker run将mysql、tars-node、tars-framework逐个启动
1 docker run --name tars-mysql -e MYSQL_ROOT_PASSWORD='123456' -d -p 3307:3306 \ 2 -v/etc/localtime:/etc/localtime \ 3 -v /data/mysql-data:/var/lib/mysql mysql:5.6 4 5 docker run -d \ 6 --name=tars-framework \ 7 -e MYSQL_HOST="123.56.***.184" \ 8 -e MYSQL_ROOT_PASSWORD="123456" \ 9 -e MYSQL_USER=root \ 10 -e MYSQL_PORT=3307 \ 11 -e REBUILD=false \ 12 -e SLAVE=false \ 13 -e INET=eth0 \ 14 -p 3000:3000 \ 15 -p 3001:3001 \ 16 -v /etc/localtime:/etc/localtime \ 17 -v /tmp/test/data:/data/tars \ 18 tarscloud/framework:v2.4.0 19 20 docker run -d \ 21 --name=tars-node \ 22 -e INET=eth0 \ 23 -e WEB_HOST="http://123.56.***.184:3000" \ 24 -p "9000-9010:9000-9010" \ 25 -v /data/tars:/data/tars \ 26 -v /etc/localtime:/etc/localtime \ 27 tarscloud/tars-node:latest 28
这里将服务部署和发布节点的流程走一遍,可以发布成功。于是进行分析:
容器通信的两种方式:
1)不需要给容器建立虚拟网络,直接通过eth0,也就是宿主机的外网环境直接访问
2)容器的虚拟网络,通过暴露端口给宿主机映射的端口,通过宿主机+映射的端口访问。
这个地方用单个容器逐一启动,发布节点时能把服务成功发布到相应容器,容器之间的通信是通过宿主机的外网环境直接访问。
经过反复排查,最终定位到问题到docker的网络设置不对,导致发布时tarnode获得的ip地址是网关地址172.25.0.1,而不是容器的网络地址,因而无法发布服务到相应的容器。
我这边使用的是阿里云服务器,通过重启云服务器,以及重新启动docker。恢复正常。
TarsDocker重新部署
1. 重新部署,然后发布节点
docker-compose up 不加-d,我们来看下容器启动的流程
可以看到,一个容器要等待它所依赖的其他容器启动成功后,才能够启动。这期间开启着定时任务,一直在尝试着启动各个容器,串行执行。
启动之后,通过宿主机地址+端口访问,注意右上角是tars-framework版本
2. 运维管理-服务部署
3. 发布节点
4. 切换到服务管理,刷新下页面
5. 输入地址,访问下运行的服务
Ok,发布成功了,服务正常运行,并且能够成功访问了。
部署和发布的过程中可能会遇到各种各样的问题,有问题是好事,重点是我们要有足够的知识储备,去一步一步排查,分析原因,解决问题。
参考链接:
https://juejin.cn/post/6868086876751085581#heading-1
https://blog.csdn.net/qq_33355821/article/details/104173831
https://segmentfault.com/a/1190000022864573
https://doc.tarsyun.com/adminer/start/index.html#/tarsdoc/gateway/README.md