例子來源於《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提供的數據庫服務。
# 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
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
apiVersion : v1
kind : Service
metadata :
name : mysql
spec :
ports :
- port : 3306
selector :
app : mysql
myweb-rc.yaml
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
apiVersion : v1
kind : Service
metadata :
name : myweb
spec :
type : NodePort
ports :
- port : 8080
nodePort : 30001
selector :
app : myweb
啟動方式是順序執行kubectl create -f yaml
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文件,主要內容如下:
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:
env :
- name : MYSQL_SERVICE_HOST
value : '172.17.0.4'
然后重新部署pod。但是結果還是一樣。
網上看來的
網上有個說法說把 MYSQL_SERVICE_HOST 去掉不配置。但是從源碼看,不配置的默認值是localhost,顯然不能具備3306的端口服務。
網絡謎團
通過docker登錄到容器內,互相ping網絡都是通的。
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訪問呢。
# 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的。