kubernetes pod exec接口調用


正文

一般生產環境上由於網絡安全策略,大多數端口是不能為集群外部訪問的。多個集群之間一般都是通過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"

image.png

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相關資源。


掃碼關注,精彩內容第一時間推給你

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM