kubectl - 确定在随机本地端口上进行端口转发时使用的本地端口?

kubectl - determine local port used when port-forwarding on random local port?

kubectl 让一个端口像这样转发到远程服务

$ kubectl port-forward service/my-service :12345 

如何找出 kubectl 在此命令中选择并使用的本地端口?

其他详细信息:我希望能够从脚本中确定这一点,该脚本为服务寻找活动的端口转发。如果找不到端口转发,它会在一个空闲的本地端口上创建一个端口转发,并且 returns 端口号(这就是上面所做的)

当你使用$ kubectl port-forward service/my-service :12345命令时,你必须得到这样的输出:

Forwarding from 127.0.0.1:XXXX -> YYYY
Forwarding from [::1]:XXXX -> YYYY

这是您必须识别本地所选端口的唯一方法,您可以在脚本中添加一条指令以从输出中读取该值。 XXXX 号是本地选择的端口号。我可以建议您使用 service type Nodeport instead of port-forward. Using NodePort, you are able to expose your app as a service and have access from outside the Kubernetes. Take a look into the following documentation 来获取更多示例。

This(bash function) 可能不是更简洁的方法,但可以在 kubeadm 集群上运行。尝试同时多次转发不同 pods/svc.

get_local_port()
{
port=""
if [ -z "${port}" ];then
    echo "[Error]: 1st argument should be the remote port number"
    return 1
fi
netstat -plnt |\
awk -vpid="$(ps -eaf |awk -v port=${port} '{if(match([=10=],"port-forward.*"port)) printf "%s|", }'|sed -r 's/.$//g')" '{if(match($NF,"^"pid"/")) {split(,a,":");print a[2]}}'

}

用法:

get_local_port <remote-port-number>

解释:

此命令正在解析 ps -eaf 以获取 port-forwarding 命令的 PID,然后通过 netstat 对其进行解析以获取本地端口。

我找不到任何其他方法来工作,所以我结束了编写动态查找空闲端口的脚本,并设置转发,然后 returns 端口。如果匹配的端口转发已经存在,它可以 return 代替。下面的示例用法。请注意,目前我使用另一个脚本来确定一个可用的本地端口,然后使用该端口号来设置端口转发,并且可以改为解析我在问题中提到的通用 port-forward 命令的输出,但是这种方法不那么脆弱,而且 re-usable,我相信。

$ kpf help

  kubernetes port forward helper
  * helps you transparently ensure a port forward is setup for a service.

  usage: kpf [require|get|create|remove|remove_all] {SERVICEI} {REMOTE_PORT} {EXTRA_ARGS}

  e.g.

  # get or create a port forward for your-service on remote port 8080
  # returns the port if successful, or fails and shows an error message
  kpf require service/your-service 8080

  # return any existing port forwards for your-service on remote port 8080, or nothing
  kpf get service/your-service 8080

  # create a new local port forward for your-service on remote port 8080, returning port used
  kpf create service/your-service 8080

  # remove any port forwards found to your-service on remote port 8080
  kpf remove service/your-service 8080

  # remove ALL port forwards to any kubernetes remote ports
  kpf remove_all

kpf

#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; PATH="$DIR:$PATH"
ME=$(basename "[=11=]")

function show_help()
{
  IT=$(cat <<EOF

  kubernetes port forward helper
  * helps you transparently ensure a port forward is setup for a service.

  usage: $ME [require|get|create|remove|remove_all] {SERVICE} {REMOTE_PORT} {EXTRA_ARGS}

  e.g.

  # get or create a port forward for your-service on remote port 8080
  # returns the port if successful, or fails and shows an error message
  $ME require service/your-service 8080

  # return any existing port forwards for your-service on remote port 8080, or nothing
  $ME get service/your-service 8080

  # create a new local port forward for your-service on remote port 8080, returning port used
  $ME create service/your-service 8080

  # remove any port forwards found to your-service on remote port 8080
  $ME remove service/your-service 8080

  # remove ALL port forwards to any kubernetes remote ports
  $ME remove_all 

  # create a port forward on local port 4569, for localstack, in namespace localstack
  $ME create svc/localstack 4569:4569 --namespace localstack  
EOF
)
  echo "$IT"
  echo
  exit
}

if [ -z "" ]
then
  show_help
fi
if [ "" = "help" ] || [ "" = '?' ] || [ "" = "--help" ] || [ "" = "h" ]; then
  show_help
fi

OP=
SERVICE=
REMOTE_PORT=
if [ -n "" ]
then
  shift;
  shift;
  shift;
  EXTRA_ARGS="$@"
fi

LOG_FILE_DIR="/tmp/__${ME}"

function getExistingPortForward(){
  listPortForwards | grep "$SERVICE" | grep -e ":$REMOTE_PORT" | head -n1
}

function coln(){
  COL=
  DELIM=${2:-' '}
  awk -F"$DELIM" -v col="$COL" '{print $col}' | sort | uniq
}

function err(){
  echo "$@" >&2; 
}

function isNumeric(){
  INPUT=$*
  case ${INPUT#[-+]} in
    *[!0-9]* ) echo NO ;;
    * ) echo YES ;;
  esac
}

function getPid(){
  local INFO=$(getExistingPortForward | awk '{print }')

  if [ "$(isNumeric "$INFO")" == "YES" ]; then
    echo "$INFO"
  fi
}

function getLocalPort(){
  local INFO=$(getExistingPortForward)
  INFO=$(echo "$INFO" | awk -F ":$REMOTE_PORT" '{print }' | awk '{print $NF}')

  if [ "$(isNumeric "$INFO")" == "YES" ]; then
    echo "$INFO"
  fi
}

function removePortForward(){
  local PID=$(getPid)
  if [ -z "$PID" ]
  then
    return;
  fi

  set -x
  kill "$PID"
}

function portForward(){
  local LOCAL_PORT=
  local FILE=

  kubectl port-forward "$SERVICE" "$LOCAL_PORT":"$REMOTE_PORT" $EXTRA_ARGS > $FILE 2>&1 &
}

function failIfFileContains(){
  local FILE=
  local SUBSTR=
  local MSG=
  if grep -iq "$SUBSTR" "$FILE"; then
    cat "$FILE" >&2
    err "Failed: $MSG"
    err "Service:$SERVICE, $REMOTE_PORT $EXTRA_ARGS"
    err
    exit 1
  fi
}

function getTargetLocalPort(){
  local RESULT
  if [[ $REMOTE_PORT == *":"* ]]; then
    RESULT=$(echo $REMOTE_PORT | coln 1 ':')
    REMOTE_PORT=$(echo $REMOTE_PORT | coln 2 ':')
  else
    RESULT=$(port_find_free_local_port)
  fi
  echo "$RESULT"
}

function createPortForward(){
  local LOCAL_PORT=$(getTargetLocalPort)
  local LOG_FILE="${LOG_FILE_DIR}/${LOCAL_PORT}_log.txt"
  mkdir -p "$LOG_FILE_DIR"
  rm -f "$LOG_FILE"

  portForward "$LOCAL_PORT" "$LOG_FILE"

  # wait for the log file to indicate success or failure
  while true
  do
    sleep 0.5
    failIfFileContains "$LOG_FILE" "address already" "Port $LOCAL_PORT already in use"
    failIfFileContains "$LOG_FILE" "must be logged" "Please set some PCSK variables, and try again"
    failIfFileContains "$LOG_FILE" "Failed" "Please review the above log, and try again"
    failIfFileContains "$LOG_FILE" "Error" "Please review the above log, and try again"
    if grep -q Forwarding "$LOG_FILE"; then
      # port forward successful
      echo "$LOCAL_PORT"
      break
    fi
  done
}

function getOrCreatePortForward(){
  local PORT=$(getLocalPort)
  if [ -n "$PORT" ]
  then
    echo "$PORT"
    exit;
  fi

  createPortForward
}

function listPortForwards(){
  ps aux | grep kubectl | grep port-forward | sort
}

function removeAll(){
  listPortForwards | awk '{print }' | xargs kill -9
  rm -fr "$LOG_FILE_DIR"
}

function require3Args(){
  if [ -z "$REMOTE_PORT" ]
  then
    show_help
  fi
}

if [ "$OP" = "get" ]; then
  require3Args
  getLocalPort
elif [ "$OP" = "create" ]; then
  require3Args
  createPortForward
elif [ "$OP" = "require" ]; then
  require3Args
  getOrCreatePortForward
elif [ "$OP" = "remove" ]; then
  require3Args
  removePortForward
elif [ "$OP" = "list" ]; then
  listPortForwards
elif [ "$OP" = "remove_all" ]; then
  removeAll
else
  show_help
fi

port_find_free_local_port

#!/usr/bin/env python
# 

import socket
from contextlib import closing

def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.getsockname()[1]

print(find_free_port())