例子来源于《Kubernetes实践指南》一书。问题依然没有解决,求助大神。
测试环境
Centos 7.0
docker 1.13.1
kubectl v1.5.2
etcd 3.2.18 都是通过yum安装,防火墙已关闭。
入坑问题 浏览器输入:http://ip:30001/demo/
Error:com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
相关资源rc、pod、service、ep都创建成功,但是myweb的pods无法访问到mysql提供的数据库服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # kubectl get all NAME DESIRED CURRENT READY AGE rc/mysql 1 1 1 4h rc/myweb 1 1 1 2h NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE svc/kubernetes 10.254.0.1 <none> 443/TCP 5d svc/mysql 10.254.67.31 <none> 3306/TCP 4h svc/myweb 10.254.62.177 <nodes> 8080:30001/TCP 2h NAME READY STATUS RESTARTS AGE po/mysql-vc9x6 1/1 Running 0 4h po/myweb-6k7s3 1/1 Running 0 2h # kubectl get ep NAME ENDPOINTS AGE kubernetes 192.168.1.171:6443 5d mysql 172.17.0.4:3306 4h myweb 172.17.0.2:8080 2h
定义文件 mysql-rc.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apiVersion : v1 kind : ReplicationController metadata : name : mysql spec : replicas : 1 selector : app : mysql template : metadata : labels : app : mysql spec : containers : - name : mysql image : mysql ports : - containerPort : 3306 env : - name : MYSQL_ROOT_PASSWORD value : "123456"
mysql-svc.yaml
1 2 3 4 5 6 7 8 9 apiVersion : v1 kind : Service metadata : name : mysql spec : ports : - port : 3306 selector : app : mysql
myweb-rc.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 apiVersion : v1 kind : ReplicationController metadata : name : myweb spec : replicas : 1 selector : app : myweb template : metadata : labels : app : myweb spec : containers : - name : myweb image : tomcat-app:v1 ports : - containerPort : 8080 env : - name : MYSQL_SERVICE_HOST value : 'mysql' - name : MYSQL_SERVICE_PORT value : '3306'
myweb-svc.yaml
1 2 3 4 5 6 7 8 9 10 11 apiVersion : v1 kind : Service metadata : name : myweb spec : type : NodePort ports : - port : 8080 nodePort : 30001 selector : app : myweb
启动方式是顺序执行kubectl create -f yaml
1 2 3 4 kubectl create -f mysql-rc.yaml kubectl create -f mysql-svc.yaml kubectl create -f myweb-rc.yaml kubectl create -f myweb-svc.yaml
启动后即前面提到的问题。
查看源代码 既然无法建立连接,那先看下是如何建立连接的。登录到myweb的docker容器里面,查看index.jsp文件,主要内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 java.sql.Connection conn=null ; java.lang.String strConn; java.sql.Statement stmt=null ; java.sql.ResultSet rs=null ; Class.forName("com.mysql.jdbc.Driver" ).newInstance(); try { Class.forName("com.mysql.jdbc.Driver" ); String ip=System.getenv("MYSQL_SERVICE_HOST" ); String port=System.getenv("MYSQL_SERVICE_PORT" ); ip=(ip==null )?"localhost" :ip; port=(port==null )?"3306" :port; System.out.println("Connecting to database..." ); System.out.println("jdbc:mysql://" +ip+":" +port+"?useUnicode=true&characterEncoding=UTF-8" ); conn = java.sql.DriverManager.getConnection("jdbc:mysql://" +ip+":" +port+"?useUnicode=true&characterEncoding=UTF-8" , "root" ,"123456" ); stmt = conn.createStatement(); }catch (Exception ex){ ... }
就是用jsp创建了一个连接,连接的地址通过ENV方式注入。即在myweb-rc.yaml中配置的MYSQL_SERVICE_HOST和MYSQL_SERVICE_PORT环境变量指定。 既然指定名称为mysql无法解决,那换成mysql容器的IP是否可行呢?
更新配置 通过kubectl get ep 可以看到mysql暴露出的服务接口,那就用这个Ip试试。 修改myweb-rc.yaml:
1 2 3 env : - name : MYSQL_SERVICE_HOST value : '172.17.0.4'
然后重新部署pod。但是结果还是一样。
网上看来的 网上有个说法说把 MYSQL_SERVICE_HOST 去掉不配置。但是从源码看,不配置的默认值是localhost,显然不能具备3306的端口服务。
网络谜团 通过docker登录到容器内,互相ping网络都是通的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mysql:/# ping 172.17.0.4 PING 172.17.0.4 (172.17.0.4): 56 data bytes 64 bytes from 172.17.0.4: icmp_seq=0 ttl=64 time=0.155 ms 64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.139 ms --- 172.17.0.4 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.139/0.147/0.155/0.000 ms myweb:/# ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.089 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.062 ms --- 172.17.0.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.062/0.075/0.089/0.016 ms
很显然网络是互通,但不知为何无法通过端口访问。
clusterIP 除了ep暴露的端口服务外,还有一个ClusterIP存在,是否可以通过ClusterIP访问呢。
1 2 3 4 5 # kubectl get svc NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE svc/kubernetes 10.254.0.1 <none> 443/TCP 5d svc/mysql 10.254.67.31 <none> 3306/TCP 4h svc/myweb 10.254.62.177 <nodes> 8080:30001/TCP 2h
发现ping ClusterIP全部都不通。至此不明白了。
三个IP Kubernetes中管理主要有三种类型的IP:Pod IP 、Cluster IP 和 外部IP。
Pod IP Kubernetes的最小部署单元是Pod。利用Flannel作为不同HOST之间容器互通技术时,由Flannel和etcd维护了一张节点间的路由表。Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
每个Pod启动时,会自动创建一个镜像为gcr.io/google_containers/pause:0.8.0的容器,容器内部与外部的通信经由此容器代理,该容器的IP也可以称为Pod IP。
Cluster IP Pod IP 地址是实际存在于某个网卡(可以是虚拟设备)上的,但Service Cluster IP就不一样了,没有网络设备为这个地址负责。它是由kube-proxy使用Iptables规则重新定向到其本地端口,再均衡到后端Pod的。
外部IP Service对象在Cluster IP range池中分配到的IP只能在内部访问,如果服务作为一个应用程序内部的层次,还是很合适的。如果这个Service作为前端服务,准备为集群外的客户提供业务,我们就需要给这个服务提供公共IP了。
结论 依然不懂为什么会出现链接失败的情况,网络ping通的情况下,无法访问,这个坑填不上 。 怀疑方向:应该是配置层面的问题,可能是k8s的,也可能是docker的。