【随笔】个人面试纪录

代码比较核心的几块:

  • 鉴权
  • 日志
  • 入口
  • 进程管理

鉴权举例

import re
import time
import hashlib
import datetime
def md5sum(src):
    m = hashlib.md5()
    m.update(src.encode(encoding='utf-8'))                                    #增加了编码方式转换处理
    return m.hexdigest()
    #鉴权方式A
def a_auth(uri, key, exp):
    p = re.compile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
    if not p:
        return None
    m = p.match(uri)
    scheme, host, path, args = m.groups()
    if not scheme: scheme = "http://"
    if not path: path = "/"
    if not args: args = ""
    rand = "0"      # "0" by default, other value is ok
    uid = "0"       # "0" by default, other value is ok
    sstring = "%s-%s-%s-%s-%s" %(path, exp, rand, uid, key)
    hashvalue = md5sum(sstring)
    auth_key = "%s-%s-%s-%s" %(exp, rand, uid, hashvalue)
    if args:
        return "%s%s%s%s&auth_key=%s" %(scheme, host, path, args, auth_key)
    else:
        return "%s%s%s%s?auth_key=%s" %(scheme, host, path, args, auth_key)
    #鉴权方式B
def b_auth(uri, key, exp):
    p = re.compile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
    if not p:
        return None
    m = p.match(uri)
    scheme, host, path, args = m.groups()
    if not scheme: scheme = "http://"
    if not path: path = "/"
    if not args: args = ""
    # convert unix timestamp to "YYmmDDHHMM" format
    nexp = datetime.datetime.fromtimestamp(exp).strftime('%Y%m%d%H%M')
    sstring = key + nexp + path
    hashvalue = md5sum(sstring)
    return "%s%s/%s/%s%s%s" %(scheme, host, nexp, hashvalue, path, args)
    #鉴权方式C
def c_auth(uri, key, exp):
    p = re.compile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
    if not p:
        return None
    m = p.match(uri)
    scheme, host, path, args = m.groups()
    if not scheme: scheme = "http://"
    if not path: path = "/"
    if not args: args = ""
    hexexp = "%x" %exp
    sstring = key + path + hexexp
    hashvalue = md5sum(sstring)
    return "%s%s/%s/%s%s%s" %(scheme, host, hashvalue, hexexp, path, args)
    #以下内容为uri、key、exp这三个参数的取值代码
def main():
    uri = "http://example.aliyundoc.com/ping?foo=bar"            # original uri
    key = "<input private key>"                         # private key of authorization
    exp = int(time.time()) + 1 * 3600                   # expiration time: 1 hour after current itme
    #“1 * 3600”定义了签算服务器配置的鉴权URL的有效时长,用户可以任意配置,单位是秒。签算服务器配置的鉴权URL有效时长和DCDN配置的鉴权URL有效时长没有对应关系。
    #鉴权URL的实际过期时间=签算服务器的Unix时间戳+签算服务器配置的鉴权URL有效时长+DCDN配置的鉴权URL有效时长
    #以调用鉴权方式A为例,签算服务器的Unix时间戳=1444435200,签算服务器配置的鉴权URL有效时长=3600,DCDN配置的鉴权URL有效时长=1800,则鉴权URL的实际过期时间为1444435200+3600+1800=1444440600
    #以下内容是调用A鉴权算法的代码示例:
    authuri = a_auth(uri, key, exp)                     # auth type: a_auth / b_auth / c_auth
    print("URL : %s\nAUTH: %s" %(uri, authuri))
if __name__ == "__main__":
    main()

日志举例

def setup_logging():
    logs_dir = 'logs'
    if not os.path.exists(logs_dir):
        os.makedirs(logs_dir)
    log_level = logging.INFO
    logging_format = "%(levelname)s - %(message)s"
    file_handler = TimedRotatingFileHandler(filename=os.path.join(logs_dir, f'logs-{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.log'))
    file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    file_handler.setFormatter(file_formatter)
    file_logger = logging.getLogger('file_logger')
    file_logger.setLevel(logging.INFO)
    file_logger.addHandler(file_handler)
    file_logger.info(f"System Architecture: {platform.architecture()[0]}, OS: {platform.platform()}, Python Version: {platform.python_version()}")
    logging.basicConfig(
        level=log_level,
        format=logging_format,
        handlers=[ColorizingStreamHandler(), file_handler])


class ColorizingStreamHandler(logging.StreamHandler):
    def emit(self, record):
        message = self.format(record)
        if record.levelno == logging.INFO:
            colored_message = f"{Fore.LIGHTBLUE_EX}{message}{Style.RESET_ALL}"
        elif record.levelno == logging.WARNING:
            colored_message = f"{Fore.YELLOW}{message}{Style.RESET_ALL}"
        elif record.levelno == logging.ERROR or record.levelno == logging.CRITICAL:
            colored_message = f"{Fore.RED}{message}{Style.RESET_ALL}"
        else:
            colored_message = message
        print(colored_message)
        
logging.info("Starting de4py")

入口举例

import argparse

def _GetPrimaryArgParser():
    version = core.hgx_banner.version
    if version.startswith("hgx-"):
        version = version[4:]
    prog = core.hgx_banner.name + "." + version

    # WAR for DVS which throws an ioctl error when running without a terminal
    try:
        width = os.get_terminal_size().columns
    except:
        width = None

    formatter = lambda prog: argparse.RawDescriptionHelpFormatter(prog, width=width, max_help_position=200)
    parser = argparse.ArgumentParser(prog = prog, description='onediag',
        usage=f"{prog} [OPTIONS]                              Run all tests\n" + \
              f"       {prog} [OPTIONS] <TEST>                       Run one selected test\n" + \
              f"       {prog} [OPTIONS] --tests <TEST> [<TEST2> ...] Run specified tests\n" + \
              f"       {prog} [OPTIONS] --skip <TEST> [<TEST2> ...]  Skip specified tests\n" + \
              f"       {prog} -h|--help                              Print this help message\n",
        formatter_class=formatter,
        add_help=False)

    parser.add_argument("-h", "--help", action="store_true", help="show this help message and exit")
    parser.add_argument("--log", type=str, default=None, metavar="<path>", help="Path to log directory")
    return parser

def _GetFinalArgParser(parser, tests):
    parser.add_argument("--run_spec", type=str, default=None, metavar="<path>", help="Path to spec file to run")
    parser.add_argument("--run_spec_content", type=str, default=None, metavar="\"json spec\"", help="Json spec content to run")
    parser.add_argument("--additional_args", type=str, default=None, metavar="\"{arg spec}\"", help="Add additional args for various tests")

    supported_chassis = core.hgx_product.GetProductConfig("SupportedChassis")
    supported_chassis_help = argparse.SUPPRESS
    if supported_chassis:
        default_chassis = core.hgx_product.GetProductConfig("DefaultChassis")
        supported_chassis_help="Specify chassis in which baseboard is inserted. Supported chassis: {}{}"\
            .format(", ".join(sorted(supported_chassis.keys())), " (default: {})".format(default_chassis) if default_chassis else "")
    parser.add_argument("--force_chassis", type=str, default=None, metavar="<chassis>", help=supported_chassis_help)

    supported_baseboards = core.hgx_product.GetProductConfig("SupportedBaseboards")
    supported_baseboards_help = argparse.SUPPRESS
    if supported_baseboards:
        supported_baseboards_help = "Specify which baseboard the system has: {}".format(", ".join(sorted(supported_baseboards.keys())))
    parser.add_argument("--force_baseboard", type=str, default=None, metavar="<baseboard>", help=supported_baseboards_help)

    supported_interposers = core.hgx_product.GetProductConfig("SupportedInterposers")
    supported_interposers_help = argparse.SUPPRESS
    if supported_interposers and not core.hgx_banner.isfieldiag:
            supported_interposers_help = "Specify which interposer the system has: {}".format(", ".join(sorted(supported_interposers.keys())))
    parser.add_argument("--force_interposer", type=str, default=None, metavar="<interposer>", help=supported_interposers_help)

    parser.add_argument("--override_topology", type=str, default=None, metavar="<path>", help="Topology file name. Use absolute path for custom topologies")

    test_only_tray_help = argparse.SUPPRESS
    baseboard_options_string = ""
    if core.hgx_banner.name == "onediagmfg" and core.hgx_product.GetProductConfig("test_only_tray_supported"):
        # If "test_only_tray_supported" is True: BaseboardsPciIds and BaseboardNames must exist.
        num_baseboards = len(core.hgx_product.GetProductConfig("BaseboardsPciIds"))
        baseboard_names = core.hgx_product.GetProductConfig("BaseboardNames")
        baseboard_options_string = "|".join(str(id) for id in range(num_baseboards))

        baseboard_strings = ""
        for baseboard_id in range(num_baseboards):
            baseboard_strings += f"{baseboard_id} - {baseboard_names[baseboard_id]} "
        test_only_tray_help = "Specify the individual tray: " + baseboard_strings
    parser.add_argument("--test_only_tray", type=int, default=None, metavar=f"<{baseboard_options_string}>", help=test_only_tray_help)

    parser.add_argument("--diag_user", type=str, default=None, metavar="<user>", help="Sets the username recorded when generating chipsdb csv files")
    parser.add_argument("--loops", type=int, default=1, metavar="<#>", help="Loops specified tests for the number of times provided")

    parser.add_argument("--only_nvswitch_devs", type=str, default=None, metavar="<b:d.f>[,<b:d.f>...]", help="Only run tests on specific NVSwitch devices")
    parser.add_argument("--only_gpu_devs", type=str, default=None, metavar="<b:d.f>[,<b:d.f>...]", help="Only run tests on specific GPU devices")
    parser.add_argument("--sfc_file", type=str, default=None, metavar="<file>", help="Path to factory shop floor control datafile")

    parser.add_argument("--test_id", type=str, default=None, metavar="<virtual_id_1>[,<virtual_id_2>...]", help="Runs specific tests with matching virtual_id keys in the spec file")
    parser.add_argument("--skip_id", type=str, default=None, metavar="<virtual_id_1>[,<virtual_id_2>...]", help="Skip specific tests with matching virtual_id keys in the spec file")
    parser.add_argument("--seq_id", type=str, default=None, metavar="<virtual_id_1>", help="Runs the spec file tests starting from the specified test virtual_id")
    parser.add_argument("--powerqual_testargs", type=str, default=None, metavar="<file>", help="Path to powerqual testargs file")
    parser.add_argument("--sku_json", type=str, default=None, metavar="<file>", help="Path to skucheck JSON file")
    parser.add_argument("--install_dir", type=str, default=None, metavar="<file>", help="Path where diag needs to be installed. By default install in current directory")
    parser.add_argument("--bmc_extra_args", type=str, default="", help="Add more args to bmc to use ipmitool on system where")
    parser.add_argument("--test_status_file", type=str, default=None, metavar="<file>", help="Records the test status")

    # Simple Flag Arguments:
    parser.add_argument("--log_texr", action="store_true", help="TODO")
    parser.add_argument("--chips_sanity", action="store_true", help="TODO")
    parser.add_argument("--quickcheck", action="store_true", help="TODO")
    parser.add_argument("--log_unified_summary", action="store_true", help="TODO")
    parser.add_argument("--ignore_nvswitch", action="store_true", help="Ignore all NVSwitch devices")
    parser.add_argument("--ignore_gpu", action="store_true", help="Ignore all GPU devices")
    parser.add_argument("--run_on_error", action="store_true", help="Continue running tests even after a test failure has been detected")
    parser.add_argument("--no_bmc", action="store_true", help="Run diag without any bmc related tasks")
    parser.add_argument("--skip_os_check", action="store_true", help="Skips OS supported check")
    parser.add_argument("--disable_passfail_msg", action="store_true", help="Don't print the pass/fail banner")
    parser.add_argument("--print_only_headers", action="store_true", help="Only print header information and exit")
    parser.add_argument("--disable_fan_override", action="store_true", help="Disable overriding the fan pwms for the whole diag run")
    parser.add_argument("--use_system_python3", action="store_true", help="By default python3 packaged with the diag is used. use this arg to force using system python3")
    parser.add_argument("--disable_blacklist_pages", action="store_true", help="Don't blacklist pages when memory errors are detected during mods run")
    parser.add_argument("--skip_print_product_details", action="store_false", help="Don't print the product detail headers")
    parser.add_argument("--use_system_mods_driver", action="store_true", help="Use mods driver from the system")
    parser.add_argument("--lockdown", action="store_true", help="Kernel is in lockdown mode")

    # --skip, --tests, and <TEST> after the [OPTIONS] command line
    parser.add_argument("--skip", type=str, nargs='*', help=argparse.SUPPRESS)
    parser.add_argument("--tests", type=str, nargs='*', help=argparse.SUPPRESS)
    parser.add_argument("test", type=str, nargs='*', help=argparse.SUPPRESS)

    if MULTINODE_SUPPORT:
        parser = multinode.MultiNodeParser(parser)

    # Print Available Tests:
    epilog = "Available tests:"
    spaces = " " * 32
    for test in tests:
        try:
            if test.IsSupported():
                name = test.name
                epilog += "\n    " + name + spaces[:len(spaces) - len(name)] + " - " + test.info
        except Exception as e:
            PrintToVerboseLogs(f"Exception occured while calling IsSupported() on test: {str(e)}")
            with open("exception.log", "a+") as f:
                traceback.print_exc(file=f)
    epilog += "\n"
    parser.epilog = epilog
    return parser

parser = _GetPrimaryArgParser()
(pargs, remaining_args) = parser.parse_known_args(args)

进程管理举例

class Process:
    def __init__(self, process, logfile, start_time = 0, args = ()):
        self._process   = process
        self._logfile   = logfile[0]
        self._logpath   = logfile[1]
        self._logbegin  = logfile[2]
        self._timed_out = False
        self._logfile_lock = threading.Lock()
        self._pid = process.pid
        self._start_time = start_time
        self._args = args

    def Wait(self):
        self._process.wait()
        self._CloseLog()
        return self._process.returncode

    def Kill(self):
        process_kill_config = GetTestArgs("process_kill_config", default = {})
        sigterm_attempt_limit = process_kill_config.get("sigterm_attempt_limit", 50)
        kill_attempt = 0
        sigterm_post_attempt_delay_sec = process_kill_config.get("sigterm_post_attempt_delay_sec", 1)
        while self.IsAlive() and kill_attempt < sigterm_attempt_limit:
            kill_attempt += 1
            try:
                euid = self.GetEUid()
                if euid is not None and euid == 0:
                    RunProgramSudo(("kill", str(self._pid)), dump_exitcode = True)
                else:
                    self._process.terminate()
            except:
                pass
            finally:
                if self.IsAlive():
                    # Add delay to allow clean up to complete
                    time.sleep(sigterm_post_attempt_delay_sec)

        kill_attempt = 0
        sigkill_attempt_limit = process_kill_config.get("sigkill_attempt_limit", 5)
        while self.IsAlive() and kill_attempt < sigkill_attempt_limit:
            kill_attempt += 1
            try:
                # If sigterm didn't kill the process, try with sigkill
                euid = self.GetEUid()
                if euid is not None and euid == 0:
                    RunProgramSudo(("kill", "-9", str(self._pid)), dump_exitcode = True)
                else:
                    self._process.kill()
            except:
                pass
        try:
            self._CloseLog()
        except:
            pass
        if self.IsAlive():
            return RC.PROCESS_KILL_FAILED
        return RC.OK

面试被问了几个问题。

1.mount怎么用

没答上来,说的 --help 可以看
mount --help | less
mount [ --source ] <source> | [ --target ] <target>

2.ansible怎么用,有哪些常用的模块

ansible <hosts|all> -m <module>

常用的模块有
ansible TEST_HOST -m command -a “chdir=/data ls ./” # 进入目录并查看
ansible TEST_HOST -m shell -a “cd /data && ls ./”
ansible TEST_HOST -m copy -a “src=/tmp/xxx.tar” dest=/data/ mode=755 owner=app group=app"
ansible TEST_HOST -m yum -a “name=nginx”
ansible TEST_HOST -m service -a “name=mysql enabled=yes state=restarted”
ansible TEST_HOST -m user -a “name=app system=yes home=/home/app password=123456”

3.python 如何解析 json串

import json
# 使用 json.loads() 从字符串中读取 JSON 数据
json_str = '{"name": "Kyrie", "age": 31}'
data = json.loads(json_str)

4.问了openstack token 的restful接口怎么请求的。
192.168.5.5:5000/v3/auth/tokens

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "name": "ZH",
                    "domain": {
                        "name": "Default"
                    },
                    "password": "MM"
                }
            }
        }
    }
}

忘了哪个是了,反正有一个是

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "id": "default"
                    },
                    "name": "ZH",
                    "password": "MM"
                }
            }
        },
        "scope": {
            "project": {
                "domain": {
                    "id": "default"
                },
                "name": "default"
            }
        }
    }
}

shell层获取方法

source /etc/keystone/admin-openrc.sh && openstack token issue

获取单个实例

http://192.168.5.20:8774/v2.1/servers
{
    "server" : {
        "accessIPv4": "172.20.200.150",
        "name": "20211222Test",
        "imageRef": "91908526-9033-4020-a140-57b52975109d",
        "flavorRef": "1d03170f-9991-4819-a9e7-25b858aaa610",
        "networks": "93c79209-136d-4a83-abbd-e6cc11514371"
    }
}

获取VNC
http://192.168.5.20:8774/v2.1/b8595e4791494a969909dabb198b566f/servers/d7186412-dbfc-4851-8105-2bdab819b8a4/remote-consoles
<第一个ID,通过openstack project获取>
<第二个ID,通过openstack server list 获取>

5.openstack 如何通过 IP 获取实例所在的node。

openstack server list
绑定了ip 的就会在这显示出来。

6.数据库里的哪个表查看实例。

MariaDB [(none)]> use nova;
MariaDB [nova]> select * from instances where uuid= ‘19ec09a5-151b-4111-a4bd-636d10794912’ \G

7.shell 实现检测 192.168/16 段哪些 IP 目前被占用的。
这里涉及的点比较多,比如IP太多,检测太慢,异步抛出的进程太多容不容易卡死。
要不要异步,异步池设置多少。

大圈套小圈,防止时间太久
ping -c 2 ${IP}

IP=192.168
for i in $(seq 0 255);do
    bash ./1t.sh ${i} &
done

8.cinder一些信息
https://blog.51cto.com/u_16099352/6355205

9.

kubectl get pod -n vnet -o wide

Q:会显示哪些信息:
A:输出在 vnet 命名空间中的所有 Pods 的详细信息。
NAME: Pod 的名称。
READY: Pod 中容器的就绪状态。显示“就绪”的容器数量/Pod中总容器数量。 例如:0/1 or 1/1
STATUS: Pod 的状态,例如: Running、Failed、Succeeded 、NodeAffinity、Pending、Terminating等。
RESTARTS: Pod 中容器重启的次数。 例如:8、9、52
AGE: Pod 从创建到现在的时间。例如:22h、13d
IP: Pod 的 IP 地址。10.107.xxx.xxx
NODE: Pod 所在的节点名称。例如:bj-zone1-controller01-xxxxxx、sh-zone2-controller02-xxxxxx
NOMINATED NODE: 用于优先级和抢占的提名节点(如果有的话)。
READINESS GATES: Pod 的就绪门(如果有配置的话)。

NodeAffinity

apiVersion: v1  
kind: Pod  
metadata:  
  name: my-pod  
spec:  
	affinity:  
	  nodeAffinity:  
	    requiredDuringSchedulingIgnoredDuringExecution:  
	      nodeSelectorTerms:  
	      - matchExpressions:  
	        - key: "kubernetes.io/zone"  
	          operator: In  
	          values:  
	          - us-east-1a

NOMINATED NODE

要设置 NOMINATED NODE,可以在 Pod 的规范中定义 NodeAffinity 规则,并在其中使用 requiredDuringSchedulingIgnoredDuringExecution 类型来指定 nodeAffinity。

apiVersion: v1  
kind: Pod  
metadata:  
  name: my-pod  
spec:  
  affinity:  
    nodeAffinity:  
      requiredDuringSchedulingIgnoredDuringExecution:  
        nodeSelectorTerms:  
        - matchExpressions:  
          - key: "kubernetes.io/e2e-az-name"  
            operator: In  
            values:  
            - zone-1

使用了 kubernetes.io/e2e-az-name 标签键来匹配具有 zone-1 值的节点。
这表示该 Pod 必须被调度到具有 zone-1 值的节点上。如果找不到符合条件的节点,Pod 将不会被调度。
如果多个 Pod 同时具有相同的 NOMINATED NODE 设置,并且都符合条件,那么它们将被调度到同一个节点上。

10

Q:如何创建一个pod
A:kubectl apply -f my-pod.yaml

apiVersion: v1  
kind: Pod  
metadata:  
  name: my-pod  
  namespace: mynamespace  //指定命名空间
spec:  
  containers:  
    - name: my-container  
      image: my-image  
      env:  
        - name: MY_ENV_VAR  
          value: "my_value"

11

Q:kind支持哪些类型
A:

Pod:Pod 是 Kubernetes 中的基本部署单元,可以包含一个或多个容器。Pod 的作用是运行和管理应用程序的实例。
Service:Service 用于将多个 Pod 实例暴露给外部访问,并提供一个稳定的 IP 地址和 DNS 名,以便通过负载均衡器将流量分发到后端的 Pod。
Deployment:Deployment 是用于管理 Pod 副本的数量和运行状况的资源对象。通过 Deployment,你可以定义应用程序所需的副本数量、更新策略等,并自动管理 Pod 的生命周期。
ReplicaSet:ReplicaSet 是用于确保特定数量的 Pod 副本始终在集群中运行。它提供了一种保证 Pod 副本数量的机制,并可以根据需要进行自动缩放。
StatefulSet:StatefulSet 用于管理有状态的应用程序,例如数据库。它提供了有序部署、自动修复和持久化存储等功能,确保有状态应用的稳定运行。
ResourceQuota:限制命名空间的资源。

12

Q:Pod和Service在Kubernetes中是两个非常重要的概念,它们各自有不同的职责和特点。

A:Pod是Kubernetes中的最小部署单元,它实际上是一个环境,包括容器、存储、网络IP和端口以及容器配置。在Pod中可以运行一个或多个容器,并且这些容器共享所有的资源,包括共享Pod的IP和端口以及磁盘。一个Pod可以包含多个容器,这些容器共享某些基础设施和运行环境,这样能保证资源的利用率,同时也简化了系统的复杂度。此外,Pod是临时性的,当其中的进程结束或者节点发生故障或者资源不足时,Pod就会被销毁。

Service则是一个更为高级的概念,它是为了解决Pod动态变化的问题而设计的。由于Pod是临时性的,它的IP地址可能会频繁变化,这使得其他服务很难找到它。而Service提供了一个稳定的入口,它可以将一组Pod作为后端服务进行管理,前端应用可以通过Service的IP和端口来访问后端的Pod。Service通过负载均衡器将流量分发到后端的多个Pod上,从而实现服务的发现和负载均衡。

13

Q:Kubernetes中如何实现服务发现和负载均衡
A:kube-proxy是一个运行在Kubernetes节点上的组件,它通过实现Service的概念来实现服务发现和负载均衡。每个节点都有一个kube-proxy组件,它会为Service创建一个代理,将流量重定向到后端的Pod上。kube-proxy可以使用userspace模式或iptables模式来转发流量。在userspace模式下,kube-proxy会在节点上为每个Service创建一个临时端口,将流量转发到这个临时端口上,并通过内部的负载均衡机制选择一个后端Pod。在iptables模式下,kube-proxy会设置iptables规则来直接转发流量到后端的Pod。

Ingress控制器:Ingress是Kubernetes中用于管理外部访问的API对象,它可以定义一组路由规则,将外部请求路由到集群内部的Service上。Ingress控制器是一个独立的组件,它可以实现Ingress资源的配置和管理,并提供更高级的路由和负载均衡功能。

14

Q:Kubernetes Service如何创建

A:创建一个名为webapp的Service,可以执行以下命令:

kubectl expose deployment webapp service/webapp

或者使用yaml创建service
如果一个Deployment具有与Service相同的selector标签选择器,那么这个Deployment创建的Pod就会被Service路由流量。

apiVersion: v1  
kind: Service  
metadata:  
  name: webapp-service  
spec:  
  selector:  
    app: webapp-deploment  #这个也是pod的标签。
  ports:  
    - protocol: TCP  
      port: 8080   #service监听的8080端口
      targetPort: 8080  #POD的8080端口
kubectl apply -f service.yaml #应用yml到集群中

验证

kubectl get services

获取 IP 地址

kubectl get services <my-service> -o jsonpath='{.spec.clusterIP}'

要创建一个service首先需要创建Deployment
例如:创建一个名为nginx的Deployment,使用nginx镜像,并设置副本数为2。

kubectl run nginx --image=nginx --replicas=2
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: nginx-deployment
spec:  
  replicas: 2  
  selector:  
    matchLabels:  
      app: nginx    #定义标签,表示只有app=nginx 的Pod才会被这个Deployment管理。
  template:  #定义pod模板
    metadata:  
      labels:  
        app: nginx   #模板的标签键为app,标签值为nginx
    spec:  
      containers:  
      - name: nginx_231225
        image: nginx_1  
        ports:  
        - containerPort: 80

创建一个标签为nginx的POD

apiVersion: v1  
kind: Pod  
metadata:  
  name: nginx-pod  
  labels:  
    app: nginx  
spec:  
  containers:  
  - name: nginx_231225  
    image: nginx_1  
    ports:  
    - containerPort: 80

创建 service 并为 service 关联一个K8S外的外部IP,来提供访问服务

apiVersion: v1  
kind: Service  
metadata:  
  name: my-service  
spec:  
  selector:  
    app: myapp  
  ports:  
    - protocol: TCP  
      port: 80  	 #service监听的端口
      targetPort: 8080  #POD的端口
  externalIPs:  
    - <your-external-ip>

15

Q:readinessGates 代表什么,怎么配置。
A:在Kubernetes中,可以通过在Pod规格中添加readinessGates字段来配置readiness gates。readinessGates字段是一个包含多个条件的列表,每个条件都定义了一个Pod何时被认为是就绪的。

以下为一个POD模板

apiVersion: v1  
kind: Pod  
metadata:  
  name: my-pod  
spec:  
  containers:  
    - name: my-container  
      image: my-image  
      readinessProbe:  
        httpGet:  
          path: /healthz  
          port: 8080  
      readinessGates:  
        - conditionType: "my-condition"  
          status: "True"

readinessProbe字段定义了一个健康检查,用于检查容器是否已经准备好接收流量。当健康检查成功时,Pod被认为是就绪的。此外,readinessGates字段定义了一个名为"my-condition"的条件,该条件的状态为"True",表示Pod已满足就绪条件。

httpGet:这表示使用 HTTP GET 请求来执行就绪检查。
path: /healthz:这是容器内应用应该提供的 HTTP 路径。Kubernetes 将向此路径发送 GET 请求以检查应用的健康状态。例如,如果容器内运行的是一个 Web 服务器,那么该服务器应该响应 /healthz 路径的请求,并返回一个状态码来表示其健康状态。通常,状态码 200 表示“健康”,而其他状态码(如 500)可能表示“不健康”。

当 Pod 启动时,Kubernetes 会定期(默认是每10秒)执行这个 readinessProbe 检查。只有当此检查成功返回时,Pod 才会被标记为“就绪”,并开始接收流量。如果检查失败,Pod 将被标记为“未就绪”,并从服务中移除,直到它再次变为“就绪”状态。

要在应用中实现这种健康检查机制,你需要在你的 Web 服务器或应用中加入一个端点(如 /healthz),该端点返回应用的当前健康状态。这通常涉及到检查应用的内部状态、数据库连接、依赖服务等,以确定是否可以安全地处理外部请求。

16 node和pod的关系

每个Pod都会被调度到一个或多个Node上,由Node负责运行和提供所需的计算和存储资源。
Node是Pod的宿主:Pod在Node上运行,并依赖Node提供计算、存储和网络资源。
每个Node上都会运行一个或多个Pod,并且每个Pod都会被分配一个独立的IP地址和网络命名空间。
Node负责管理和维护在其上运行的Pod,包括资源的分配、容器的生命周期管理等。
每个Node都会定期向Kubernetes master节点报告其资源使用情况和可用容量。Kubernetes master节点根据这些报告以及其他调度因素(如需求、优先级等)来决定将哪个Pod调度到哪个Node上。

kubectl
pod1、pod2 | pod2、pod3 | pod1、pod3
NODE1 | NODE2 | NODE3

17 每个node节点都需要创建一个service吗?

看情况:
当创建一个Service时,Kubernetes会根据标签选择器选择关联的Pods。这些Pods可以位于集群中的任何节点上。Service使用kube-proxy服务进程来将流量路由到选定的Pods。kube-proxy会根据轮询(round-robin)或其他的策略将请求分发到后端的Pods上。

每个节点上的kube-proxy服务进程都会监听Service的变动,并将最新的Service信息转换成对应的访问规则。这些规则会在集群内的所有节点上都生成,因此无论在哪个节点上访问Service,都能得到相同的结果。

通过这种方式,Kubernetes的Service能够将流量路由到集群中不同节点上的Pods,实现不同节点上Pod的统一。这种机制使得用户无需关心具体的Pod位置,只需通过Service的入口地址即可访问到所需的服务。

18 kube-proxy的工作原理

kube-proxy的工作原理是利用iptables或ipvs等网络代理技术,实现Service与Pod之间的网络流量转发。它在每个Node计算节点上运行,负责将网络请求从Service的Cluster IP转发到后端的Pod。

kube-proxy与Service的关系是密切的。kube-proxy是Service的实现组件之一,负责将Service的网络请求转发到正确的Pod。通过kube-proxy,Kubernetes能够提供集群内部的服务发现和负载均衡功能。

当创建一个Service时,Kubernetes会根据标签选择器选择关联的Pods。这些Pods可以位于集群中的任何节点上。kube-proxy则会根据Service的定义和标签选择器,创建相应的网络规则,实现流量转发。

kube-proxy创建的网络规则是用于实现Service与Pod之间的网络流量转发、端口映射和负载均衡的规则。这些规则确保了集群内部的服务发现和负载均衡功能得以实现。

19 如何加入新的NODE到集群

安装完所需要的软件并配置好网络后
输入命令:就可以加入

kubeadm join <master-node-ip>:<master-node-port> --token <token-id>.<token-id-hash> --discovery-token-ca-cert-hash sha256:<hash>

验证:

$ kubectl get nodes
$ kubectl get pods --all-namespaces

创建新的安全值:

kubectl create secret generic token-id --from-literal=token-id=<token-id>

创建hash值

kubectl get secret <token-id> -o jsonpath="{.data.token-id-hash}" | base64 --decode

--discovery-token-ca-cert-hash sha256:<hash>这个命令是用于验证新节点是否允许加入Kubernetes集群的参数。

sha256可以通过以下方法查看

一:

kubectl get csr <csr-name> -o jsonpath="{.status.certificate}" | openssl x509 -sha256 -fingerprint -noout | awk '{print $2}' | tr -d '='

查看Kubernetes配置文件:Kubernetes的配置文件通常位于/etc/kubernetes/manifests/kubelet.yaml或类似的路径。在该文件中,你可以找到与CA证书相关的配置,并查看其中的哈希值。

  1. 使用Kubernetes API:你可以通过Kubernetes API来获取CA证书的哈希值。运行以下命令来获取API服务器的证书:
kubectl get secret <secret-name> -n <namespace> -o yaml | grep -i crt | awk '{print $2}' | base64 --decode | openssl x509 -sha256 -fingerprint -noout | awk '{print $2}' | tr -d '='

是API服务器的证书的名称
是Kubernetes命名空间

20

Q:K8S的命名空间如何创建
A:

kubectl create namespace <namespace-name>
kubectl get namespaces

使用kubectl命令行工具:运行以下命令来将POD添加到指定的命名空间中

kubectl move <pod-name> <namespace>

21

如何设置命名空间

设置CPU、内存资源
apiVersion: v1  
kind: ResourceQuota  
metadata:  
  name: myquota  
  namespace: mynamespace  
spec:  
  hard:  
    cpu: "1"  
    memory: 2G
kubectl apply -f quota.yaml #应用yml到集群中

验证:

kubectl describe quota <quota-name> -n <namespace>

需要注意的是,资源配额的设置不会立即生效,需要等待一段时间让集群中的现有POD按照新的配额进行调整。同时,对于已经超过配额限制的POD,系统会将其暂停或删除,以遵守资源配额的限制。

资源配额通过限制命名空间中的资源消耗来防止资源过度使用和超出集群容量。
这有助于确保各个命名空间之间的公平共享和使用资源,并避免某些命名空间过度占用资源而导致其他命名空间资源不足的情况发生。

设置网络资源
apiVersion: networking.k8s.io/v1  
kind: NetworkPolicy  
metadata:  
  name: allow-traffic-to-target-namespace  
  namespace: my-namespace  
spec:  
  ingress:  
  - from:  #这定义了允许流量来源的部分。
    - namespace_name: target-namespace  #这指定了允许流量的来源命名空间为“target-namespac"
    podSelector: {}  # 这是一个可选的字段,用于进一步筛选允许访问的Pod。在这里,它没有指定任何条件,意味着允许来自“target-namespace”中所有Pod的流量。

加入集群:

kubectl apply -f network-policy.yaml --namespace my-namespace

验证:

kubectl describe po <pod-name> --namespace my-namespace
设置存储资源
apiVersion: storage.k8s.io/v1  
kind: StorageClass  
metadata:  
  name: slow-provisioner  
provisioner: kubernetes.io/no-provisioner  
parameters:  
  type: slow  
resources:  
  limits:  
    storage: 10Gi # 设置命名空间的存储配额为10GiB

slow-provisioner-storageclass.yaml 加入集群

kubectl apply -f slow-provisioner-storageclass.yaml -n <target-namespace>

当一个或多个POD尝试使用该StorageClass创建持久卷时,Kubernetes将根据设定的配额限制来管理存储资源。如果超过了配额限制,Kubernetes将无法创建新的持久卷,除非释放了足够的存储空间或调整了配额限制。

设置访问规则
apiVersion: rbac.authorization.k8s.io/v1  
kind: Role  
metadata:  
  name: namespace-reader  
  namespace: <target-namespace>  
rules:  
- apiGroups: ["*"]  
  resources: ["*"]  
  verbs: ["get", "list"]

创建或更新角色绑定资源:
user类型的 john 用户。

apiVersion: rbac.authorization.k8s.io/v1  
kind: RoleBinding  
metadata:  
  name: user-namespace-reader  
  namespace: <target-namespace>  
subjects:  
- kind: User  
  name: john  
roleRef:  
  kind: Role  
  name: namespace-reader  
  apiGroup: rbac.authorization.k8s.io

将规则加入集群。

kubectl apply -f role.yaml -n <target-namespace>  
kubectl apply -f rolebinding.yaml -n <target-namespace>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值