正文
一般生產環境上由於網絡安全策略,大多數端口是不能為集群外部訪問的。多個集群之間一般都是通過k8s的ApiServer組件提供的接口通信,如https://192.168.1.101:6443。所以在做雲平台時,集群管理平台(雅稱:觀雲台)需要操作其他集群的資源對象時,必然也得通過ApiServer。
k8s負載均衡器組件ingress-nginx-controller中集成的nginx,當集群ingress、tcp configmaps、udp configmaps等資源發生變化時,ingress-nginx-controller會根據這些資源最新配置,並根據提前設定好的nginx.tmpl模板(nginx配置文件nginx.conf由該模板生成)生成最新的nginx.conf配置,並自動進行nginx -s reload操作。
最近做的一個需求,部分負載均衡器可以在頁面上由運維人員自動配置,通過nginx的server、map配置。根據請求頭的不同將流量分配到不同的服務。可以參考nginx map配置根據請求頭不同分配流量到不同后端服務
配置后需要在觀雲台上手動reload負載均衡器,以使配置生效。這就涉及到從觀雲台去操作多集群的負載均衡器。
通過修改ingress-nginx-controller源碼提供接口reload方案,由於網絡規則限制肯定行不通;
只有6443端口可以走。能不能像操作集群內資源一樣去操作其他集群資源?
1、kubectl命令其實對應的就是調用apiserver去操作資源,在集群內我們都知道可以用以下命令:
kubectl exec -ti ingress-nginx-abab121 -nkube-system -- nginx -s reload
那么從A集群去操作B集群,假設B的ApiServer地址為:https://192.168.1.101:6443,Bearer token為212k1jj1jak12k1kjaeeba,則命令如下:
kubectl exec -ti ingress-nginx-abab121 -nkube-system --server=https://192.168.1.101:6443 --token=212k1jj1jak12k1kjaeeba --insecure-skip-tls-verify=true -- nginx -s reload
通過查看kubelet的源代碼,可以發現$GOPATH\src\k8s.io\kubernetes\pkg\kubelet\server\server.go的InstallDebuggingHandlers方法中注冊了exec、attach、portForward等接口,同時kubelet的內部接口通過api server對外提供服務,所以對API server的這些接口調用,可以直接訪問到kubelet,即client -->> API server --> kubelet
2、可以用curl命令調用如下:
curl -k \
-H "Connection: Upgrade" \
-H "Authorization: Bearer 212k1jj1jak12k1kjaeeba" \
-H "Upgrade: websocket" \
-H "Sec-Websocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
-H "Sec-Websocket-Version: 13" \
"https://192.168.26.19:6443/api/v1/namespaces/liano/pods/nginx/exec?command=ls&stdin=true&stout=true&tty=true"

3、kubernetes開源社區維護了各種語言版本與k8s apiserver交互的client庫,比如java庫地址如下:
https://github.com/kubernetes-client/java
其中提供了調用pod的exec接口代碼示例:
https://github.com/kubernetes-client/java/blob/master/examples/src/main/java/io/kubernetes/client/examples/ExecExample.java
需要先依賴:
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
<version>4.0.0</version>
<scope>compile</scope>
</dependency>
然后根據apiserver地址和Bearer token構建client,到訪問pod exec接口進行nginx -s reload代碼示例如下:
package com.liano.api.test;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.Configuration;
import io.kubernetes.client.Exec;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.credentials.AccessTokenAuthentication;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* @program: k8s-mars
* @description: ExecKubelet
* @author: liano
* @create: 2019-03-06 15:10
**/
public class ExecKubeletDemo {
public static void main(String[] args) throws IOException, ApiException, InterruptedException {
new ExecKubeletDemo().execNginxReload();
}
private void execNginxReload() throws InterruptedException, ApiException, IOException {
//apiServer地址和Bbearer token方式認證
ApiClient client = new ClientBuilder().setBasePath("https://10.10.101.60:6443").setVerifyingSsl(false)
.setAuthentication(new AccessTokenAuthentication("33095a7b86a7a3462ea45a1410624b")).build();
// client.setDebugging(true);
Configuration.setDefaultApiClient(client);
JSONObject res = process("nginx-97ccd777-xk9pw", "kube-system");
System.out.println(JSONObject.toJSONString(res));
}
private JSONObject process(String podName, String namespace) throws IOException, ApiException, InterruptedException {
Exec exec = new Exec();
// final Process proc = exec.exec("default", "nginx-4217019353-k5sn9", new String[]
// {"sh", "-c", "echo foo"}, true, tty);
String[] commands = new String[]{"nginx", "-s", "reload"};
final Process proc = exec.exec(namespace, podName, commands, true, true);
JSONObject res = new JSONObject();
Thread out = new Thread(
new Runnable() {
public void run() {
String copy = copy(proc.getInputStream());
res.put("data", copy);
}
});
out.start();
proc.waitFor();
out.join();
proc.destroy();
if (proc.exitValue() != 0) {
res.put("success", false);
} else {
res.put("success", true);
}
return res;
}
private String copy(InputStream from) {
Preconditions.checkNotNull(from);
BufferedReader in = null;
Reader reader = null;
StringBuilder sb = new StringBuilder();
try {
reader = new InputStreamReader(from);
in = new BufferedReader(reader);
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
System.out.println(e);
} finally {
try {
if (from != null) {
from.close();
}
if (reader != null) {
reader.close();
}
if (in != null) {
in.close();
}
} catch (Exception e) {
System.out.println(e);
}
}
return sb.toString();
}
}
從io.kubernetes.client.Exec源碼中可以看到,需求通過 HTTP/1.1 協議的101狀態碼進行握手進一步建立websocket。websocket是一種在單個TCP連接上進行全雙工通信的協議, 是獨立的、創建在 TCP 上的協議。為了創建Websocket連接,需要通過客戶端發出請求,之后服務器進行回應,這個過程通常稱為“握手”(handshaking)。
一個典型的Websocket握手請求如下:
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
服務器回應
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
字段說明
Connection必須設置Upgrade,表示客戶端希望連接升級。
Upgrade字段必須設置Websocket,表示希望升級到Websocket協議。
Sec-WebSocket-Key是隨機的字符串,服務器端會用這些數據來構造出一個SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一個特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后計算SHA-1摘要,之后進行BASE-64編碼,將結果做為“Sec-WebSocket-Accept”頭的值,返回給客戶端。如此操作,可以盡量避免普通HTTP請求被誤認為Websocket協議。
Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均應當棄用。
Origin字段是可選的,通常用來表示在瀏覽器中發起此Websocket連接所在的頁面,類似於Referer。但是,與Referer不同的是,Origin只包含了協議和主機名稱。
其他一些定義在HTTP協議中的字段,如Cookie等,也可以在Websocket中使用。
本公眾號免費提供csdn下載服務,海量IT學習資源,如果你准備入IT坑,勵志成為優秀的程序猿,那么這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大數據、面試資料、前端 等資源。同時我們組建了一個技術交流群,里面有很多大佬,會不定時分享技術文章,如果你想來一起學習提高,可以公眾號后台回復【2】,免費邀請加技術交流群互相學習提高,會不定期分享編程IT相關資源。
掃碼關注,精彩內容第一時間推給你

