admin管理员组

文章数量:1794759

TKE集群如何在pod内执行kubectl访问apiserver及登录node节点

腾讯云上创建一个TKE集群,一般都是需要开启集群的内网或者公网访问,才能kubectl访问集群apiserver,如果要登录node节点,需要ssh工具去登录,这种一般要有节点秘钥或者密码,但是一般只有运维才有节点登录方式。 下面我们来说说如何创建一个pod,来访问集群的apiserver,并登录node节点,下面我说的方式,是不需要节点登录密码或者秘钥,也不需要集群开启内网或者公网访问。

1. 前提条件

1. 首先你在腾讯云上有tke集群的cam权限和rbac权限(需要有写权限),能控制台访问集群和在集群内创建资源。 2. API秘钥管理页面获取到你子账号的API密钥,也就是SecretId,和SecretKey,并且有tke接口DescribeClusterSecurity访问权限。

2. 原理说明

集群不开内网或者公网访问,然后通过kubectl访问到apiserver,是基于token和集群apiserver的clusterip实现的,现在集群默认不提供token,我们可以通过DescribeClusterSecurity接口获取,Password字段就是对应的token,CertificationAuthority对应的是集群CA证书。

tke集群创建后,在default下默认有个kubernetes,这里有个clusterip,集群内可以通过这个访问到托管集群的apiserver,这个ip可以在集群内容器环境变量KUBERNETES_PORT_443_TCP获取。

通过接口获取的CA证书,token以及集群apiserver的clusterip,可以生成kubeconfig用于访问集群。

pod内直接登录node节点,则是通过kubectl-node-shell,具体可以参考文档

3. 镜像构建

3.1 镜像构建Dockerfile

代码语言:dockerfile复制
FROM alpine:latest
COPY ./ /root
RUN sed -i 's/dl-cdn.alpinelinux/mirrors.ustc.edu/g' /etc/apk/repositories && \
apk add --no-cache redis mysql-client vim tcpdump curl bind-tools mtr nmap-ncat busybox-extras bash bash-doc bash-completion python3 py3-pip && \
pip3 install tencentcloud-sdk-python-tke --break-system-packages && \
curl -LO .22.5/bin/linux/amd64/kubectl && \
chmod +x ./kubectl && \
mv ./kubectl /usr/local/bin/kubectl && \
mv /root/kubectl-node_shell /usr/local/bin/
CMD ["sh", "-c", "/root/start.sh"]

3.2 启动脚本start.sh

代码语言:shell复制
#!/bin/bash

python /root/get_cluster_token_and_ca.py

TOKEN=`cat /root/token`
apiserver_ip=`echo $KUBERNETES_PORT_443_TCP | awk -F'[/:]' '{print $4}'`
APISRRVICE=https://${apiserver_ip}
kubectl config set-cluster ${ClusterId} --certificate-authority='/root/ca.crt' --embed-certs=true --server=${APISRRVICE} --kubeconfig=${ClusterId}.kubeconfig

kubectl config set-credentials ${ClusterId} --token=${TOKEN} --kubeconfig=${ClusterId}.kubeconfig

kubectl config set-context ${ClusterId}-default --cluster=${ClusterId} --user=${ClusterId} --kubeconfig=${ClusterId}.kubeconfig

kubectl config use-context ${ClusterId}-default --kubeconfig=${ClusterId}.kubeconfig

mkdir -p /root/.kube

mv ${ClusterId}.kubeconfig /root/.kube/config

tail -f /dev/null

3.3 调云api接口脚本get_cluster_token_and_ca.py

代码语言:python代码运行次数:0运行复制
import json
import os
import types
from tencentcloudmon import credential
from tencentcloudmon.profile.client_profile import ClientProfile
from tencentcloudmon.profile.http_profile import HttpProfile
from tencentcloudmon.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.tke.v20180525 import tke_client, models
try:
    SecretId = os.getenv('SecretId')
    SecretKey = os.getenv('SecretKey')
    clusterId = os.getenv('ClusterId')
    cred = credential.Credential(SecretId, SecretKey)
    # 实例化一个http选项,可选的,没有特殊需求可以跳过
    httpProfile = HttpProfile()
    httpProfile.endpoint = "tke.tencentcloudapi"

    # 实例化一个client选项,可选的,没有特殊需求可以跳过
    clientProfile = ClientProfile()
    clientProfile.httpProfile = httpProfile
    # 实例化要请求产品的client对象,clientProfile是可选的
    client = tke_client.TkeClient(cred, "ap-shanghai", clientProfile)

    # 实例化一个请求对象,每个接口都会对应一个request对象
    req = models.DescribeClusterSecurityRequest()
    params = {
        "ClusterId": clusterId
    }
    req.from_json_string(json.dumps(params))

    # 返回的resp是一个DescribeClusterSecurityResponse的实例,与请求对象对应
    resp = client.DescribeClusterSecurity(req)
    # 输出json格式的字符串回包
    res = json.loads(resp.to_json_string())
    token=res["Password"]
    cacert=res["CertificationAuthority"]
    with open("/root/ca.crt", 'w', encoding='utf-8') as file:
      file.write(cacert)
    with open("/root/token", 'w', encoding='utf-8') as file:
      file.write(token)
except TencentCloudSDKException as err:
    print(err)

3.4 kubectl-node_shell脚本

这里修改了nsenter容器的reqeust资源值,也就是container_cpu和container_memory变量的值,都改成了0

代码语言:shell复制
#!/usr/bin/env sh
set -e

kubectl=kubectl
version=1.10.0
generator=""
node=""
nodefaultctx=0
nodefaultns=0
container_cpu="${KUBECTL_NODE_SHELL_POD_CPU:-0m}"
container_memory="${KUBECTL_NODE_SHELL_POD_MEMORY:-0Mi}"
volumes="[]"
volume_mounts="[]"
x_mode=0
labels="${KUBECTL_NODE_SHELL_LABELS}"
pod_running_timeout="${KUBECTL_NODE_SHELL_POD_RUNNING_TIMEOUT:-1m}"

if [ -t 0 ]; then
  tty=true
else
  tty=false
fi
while [ $# -gt 0 ]; do
  key="$1"

  case $key in
  -v | --version)
    echo "kubectl-node-shell $version"
    exit 0
    ;;
  --context)
    nodefaultctx=1
    kubectl="$kubectl --context $2"
    shift
    shift
    ;;
  --kubecontext=*)
    nodefaultctx=1
    kubectl="$kubectl --context=${key##*=}"
    shift
    ;;
  --kubeconfig)
    kubectl="$kubectl --kubeconfig $2"
    shift
    shift
    ;;
  --kubeconfig=*)
    kubectl="$kubectl --kubeconfig=${key##*=}"
    shift
    ;;
  -n | --namespace)
    nodefaultns=1
    kubectl="$kubectl --namespace $2"
    shift
    shift
    ;;
  --namespace=*)
    nodefaultns=1
    kubectl="$kubectl --namespace=${key##*=}"
    shift
    ;;
  -x)
    x_mode=1
    volumes='[{"hostPath":{"path":"/","type":""},"name":"host-root"}]'
    volume_mounts='[{"mountPath":"/host","name":"host-root"}]'
    shift
    ;;
  --)
    shift
    break
    ;;
  *)
    if [ -z "$node" ]; then
      node="${1#node/}"
      shift
    else
      echo "exactly one node required"
      exit 1
    fi
    ;;
  esac
done

if [ -z "$node" ]; then
  echo "Please specify node name"
  exit 1
fi

if [ -z "$KUBERNETES_PORT" ]; then
  # Set the default context and namespace to avoid situations where the user switch them during the build process
  [ "$nodefaultctx" = 1 ] || kubectl="$kubectl --context=$(${kubectl} config current-context)"
  [ "$nodefaultns" = 1 ] || kubectl="$kubectl --namespace=$(${kubectl} config view --minify --output 'jsonpath={.contexts..namespace}')"
fi

# Check the node and retrieve the node OS label
os="$($kubectl get node $node -o jsonpath="{.metadata.labels.kubernetes\.io/os}" || exit 1)"

# Set pod configuration per operating system
if [ "$os" = "windows" ]; then
  image="${KUBECTL_NODE_SHELL_IMAGE_WINDOWS:-mcr.microsoft/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0}"
  name="pwsh"
  pod="${name}-$(env LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 6)"
  cmd_start='"cmd.exe", "/c", "powershell.exe", "-nol"'
  cmd_arg_prefix=', "-Command"'
  cmd_default=''
  security_context='{"privileged":true,"windowsOptions":{"hostProcess":true,"runAsUserName":"NT AUTHORITY\\SYSTEM"}}'
else # If the OS isn't windows, assume linux
  image="${KUBECTL_NODE_SHELL_IMAGE:-docker.io/library/alpine}"
  name="nsenter"
  pod="${name}-$(env LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 6)"
  cmd_start='"nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid"'
  cmd_arg_prefix=', "--"'
  cmd_default=', "bash", "-l"'
  security_context='{"privileged":true}'
fi

# Build the container command
if [ $# -gt 0 ]; then
  if [ "$x_mode" -eq 1 ]; then
    cmd='['
  else
    cmd="[ $cmd_start $cmd_arg_prefix,"
  fi
  c=""
  while [ $# -gt 0 ]; do
    cmd="${cmd}${c} \"$(echo "$1" | \
      awk '{gsub(/["\\]/,"\\\\&");gsub(/\x1b/,"\\u001b");printf "%s",last;last=$0"\\n"} END{print $0}' \
    )\""
    c=,
    shift
  done
  cmd="$cmd ]"
else
  if [ "$x_mode" = 1 ]; then
    cmd='null'
  else
    cmd="[ $cmd_start $cmd_default ]"
  fi
fi

# test if resource specification is required
resources_json='"resources": {
          "limits":   { "cpu": "'${container_cpu}'", "memory": "'${container_memory}'" },
          "requests": { "cpu": "'${container_cpu}'", "memory": "'${container_memory}'" }
        }'
$kubectl run --image "$image" "$pod" --dry-run=server 2>&1 | grep -q 'failed quota' || resources_json='"resources": {}'

overrides="$(
cat <<EOT
{
  "spec": {
    "nodeName": "$node",
    "hostPID": true,
    "hostNetwork": true,
    "containers": [
      {
        "securityContext": $security_context,
        "image": "$image",
        "name": "$name",
        "stdin": true,
        "stdinOnce": true,
        "tty": $tty,
        "command": $cmd,
        $resources_json,
        "volumeMounts": $volume_mounts
      }
    ],
    "tolerations": [
      { "key": "CriticalAddonsOnly", "operator": "Exists" },
      { "effect": "NoExecute",       "operator": "Exists" }
    ],
    "volumes": $volumes
  }
}
EOT
)"
# Support Kubectl <1.18
m=$(kubectl version --client -o yaml | awk -F'[ :"]+' '$2 == "minor" {print $3+0}')
if [ "$m" -lt 18 ]; then
  generator="--generator=run-pod/v1"
fi

trap "EC=\$?; $kubectl delete pod --wait=false $pod >&2 || true; exit \$EC" EXIT INT TERM

echo "spawning \"$pod\" on \"$node\"" >&2
$kubectl run --image "$image" --restart=Never --overrides="$overrides" --labels="$labels" --pod-running-timeout="$pod_running_timeout" $([ "$tty" = true ] && echo -t) -i "$pod" $generator

3.5 构建目录

将文件按照名称一一复制到构建目录,目录结构如下

代码语言:txt复制
total 20
-rw-r--r-- 1 root root  584 Oct  4 13:07 Dockerfile
-rw-r--r-- 1 root root 1749 Oct  4 15:08 get_cluster_token_and_ca.py
-rwxr-xr-x 1 root root 4908 Oct  4 10:25 kubectl-node_shell
-rwxr-xr-x 1 root root  749 Oct  4 15:37 start.sh

3.6 镜像构建

在构建目录,执行镜像构建命令,构建推送镜像,可以用我构建好的镜像,也可以自行构建镜像

代码语言:txt复制
docker build -t ccrs.tencentyun/niewx-k8s/tke-kubectl-tool:latest .
docker push ccrs.tencentyun/niewx-k8s/tke-kubectl-tool:latest

4. 部署工作负载

下面是部署的yaml,默认会创建一个configmap和deployment。

代码语言:yaml复制
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: kubectl-demo
    qcloud-app: kubectl-demo
  name: kubectl-demo
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: kubectl-demo
      qcloud-app: kubectl-demo
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        k8s-app: kubectl-demo
        qcloud-app: kubectl-demo
    spec:
      affinity: {}
      containers:
      - envFrom:
        - configMapRef:
            name: env
        image: ccrs.tencentyun/niewx-k8s/tke-kubectl-tool:latest
        imagePullPolicy: Always
        name: demo
        resources:
          limits:
            cpu: "0"
            memory: "0"
          requests:
            cpu: "0"
            memory: "0"
        securityContext:
          privileged: true
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      imagePullSecrets:
      - name: qcloudregistrykey
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30

---

apiVersion: v1
data:
  ClusterId: cls-xxxxx
  SecretId: AKxxxx
  SecretKey: 9Wxxxx
kind: ConfigMap
metadata:
  name: env

configmap变量说明:

  • ClusterId:当前需要部署的tke集群id
  • SecretId:api访问秘钥的SecretId
  • SecretKey:api访问秘钥的SecretKey

5. 测试访问集群并登录node

将第四步的yaml,控制台创建后,然后登录对应的容器,kubectl访问和登录node

pod起来后,登录容器,kubectl访问apiserver正常,登录节点也正常,说明配置正常,后续我们就不需要给集群apiserver开启内网或者公网访问来执行kubectl了,也可以直接不通过密码或者秘钥登录节点了。

本文标签: TKE集群如何在pod内执行kubectl访问apiserver及登录node节点