k8s Service 服务
文章目录
- 一、为什么需要 Service
- 二、Kubernetes 中的服务发现与负载均衡 -- Service
- 三、用例解读
- 1、Service 语法
- 2、创建和查看 Service
- 四、Headless Service
- 五、集群内访问 Service
- 六、向集群外暴露 Service
- 七、操作示例
- 1、获取集群状态信息
- 2、创建 Service、Deployment
- 3、创建客户端的测试 Pod
- 4、集群内访问 Service 的三种方式
- 4.1、直接通过 Service 的 clusterIP 访问
- 4.2、直接访问 Service 名
- 4.3、通过环境变量访问
- 5、集群外部访问服务
- 5.1、NodePort
- 5.1.1、创建 NodePort 类型的 Service(NodePort的端口范围是 30000-32767)
- 5.1.2、创建 Deployment 模拟后端一组应用 Pod
- 5.1.3、创建并查看
- 5.1.4、通过节点 IP:port 访问
- 5.1.5、通过nodename 也可以访问
- 5.2、LoadBalancer
- 5.2.1、安装METALLB
- 5.2.2、查看MetalLB的安装方式:Installation by manifest
- 5.2.3、创建 MetalLB 的命名空间
- 5.2.4、下载 MetalLB 的安装文件
- 5.2.5、查看 MetalLB 需要的镜像
- 5.2.6、修改 metallb.yaml 文件
- 5.2.7、worker节点提前下载speaker和controller镜像
- 5.2.8、安装 MetalLB
- 5.2.9、查看 Pod 验证安装情况
- 5.2.10、配置地址池
- 5.2.11、创建地址池
- 5.2.12、创建 LoadBalancer 类型的 SVC
- 5.2.13、通过 svc 的 EXTERNAL-IP 访问集群内部的服务
- 5.3、Ingress
- 5.4、HostNetwork
- 5.4.1、给 node 节点添加 role 标签
- 5.4.2、创建使用 hostnetwork 的 Deployment
- 5.4.3、通过节点 ip 访问
- 5.5、HostPort
- 5.5.1、给 node 节点添加 role 标签
- 5.5.2、创建使用 hostport 的deployment
- 5.5.3、通过标签节点 ip + port / pod ip 访问
- 5.6、ExternalName
- 5.6.1、创建 ExternalName 类型的 Service
- 5.6.2、启动临时 pod 测试 DNS 解析
- 5.6.3、域名解析
- 八、架构设计
- 1、Kubernetes 服务发现架构
- 1.1、组件
- 1.2、实际访问链路
一、为什么需要 Service
在 K8s 集群里面会通过 pod 去部署应用,与传统的应用部署不同,传统应用部署在给定的机器上面去部署,我们知道怎么去调用别的机器的 IP 地址;但是在 K8s 集群里面应用是通过 pod 去部署的, 而 pod 生命周期是短暂的。在 pod 的生命周期过程中,比如它创建或销毁,它的 IP 地址都会发生变化,这样就不能使用传统的部署方式,不能指定 IP 去访问指定的应用。
另外在 K8s 的应用部署里,之前虽然学习了 deployment 的应用部署模式,但还是需要创建一个 pod 组,这些 pod 组需要提供一个统一的访问入口,以及怎么去控制流量负载均衡到这个组里面。
比如说测试环境、预发环境和线上环境,其实在部署的过程中需要保持同样的一个部署模板以及访问方式。因为这样就可以用同一套应用的模板在不同的环境中直接发布。
二、Kubernetes 中的服务发现与负载均衡 – Service
最后应用服务需要暴露到外部去访问,需要提供给外部的用户去调用的。我们知道 pod 的网络跟机器不是同一个段的网络,那怎么让 pod 网络暴露到去给外部访问呢?这时就需要服务发现。
在 K8s 里面,服务发现与负载均衡就是 K8s Service。
上图就是在 K8s 里 Service 的架构,K8s Service 向上提供了外部网络以及 pod 网络的访问,即外部网络可以通过 service 去访问,pod 网络也可以通过 K8s Service 去访问。
向下,K8s 对接了另外一组 pod,可以通过 K8s Service 的方式去负载均衡到一组 pod 上面去,这样相当于解决了前面所说的复发性问题,或者提供了统一的访问入口去做服务发现,然后又可以给外部网络访问,解决不同的 pod 之间的访问,提供统一的访问地址。
三、用例解读
1、Service 语法
cat >> my-service.yaml << EOF
apiVersion: v1
kind: Service
metadata:name: my-servicelabels:app: my-service
spec:selector:app: MyAppports:- protocol: TCPport: 80targetPort: 9376
EOF
定义了用于 K8s Service 服务发现的一个协议以及端口。声明了一个名叫 my-service 的一个 K8s Service,它有一个 app:my-service 的 label,它选择了 app:MyApp 这样一个 label 的 pod 作为它的后端。
最后是定义的服务发现的协议以及端口,我们定义的是 TCP 协议,端口是 80,目的端口是 9376,效果是访问到这个 service 80 端口会被路由到后端的 targetPort,就是只要访问到这个 service 80 端口的都会负载均衡到后端 app:MyApp 这种 label 的 pod 的 9376 端口。
2、创建和查看 Service
kubectl apply -f my-service.yamlkubectl describe svc my-service
service 创建好之后,可以看到它的名字是 my-service。Namespace、Label、Selector 这些都跟我们之前声明的一样,这里声明完之后会生成一个 IP 地址,这个 IP 地址就是 service 的 IP 地址,它在集群里面可以被其它 pod 所访问,相当于通过这个 IP 地址提供了统一的一个 pod 的访问入口,以及服务发现。
这里还有一个 Endpoints 的属性,就是我们通过 Endpoints 可以看到:通过前面所声明的 selector 去选择了哪些 pod?以及这些 pod 都是什么样一个状态?比如说通过 selector,我们看到它选择了这些 pod 的一个 IP,以及这些 pod 所声明的 targetPort 的一个端口。
在 service 创建之后,它会在集群里面创建一个虚拟的 IP 地址以及端口,在集群里,所有的 pod 和 node 都可以通过这样一个 IP 地址和端口去访问到这个 service。这个 service 会把它选择的 pod 及其 IP 地址都挂载到后端,这样通过 service 的 IP 地址访问时,就可以负载均衡到后端这些 pod 上面去。
当 pod 的生命周期有变化时,比如说其中一个 pod 销毁,service 就会自动从后端摘除这个 pod。这样实现了:就算 pod 的生命周期有变化,它访问的端点是不会发生变化的。
四、Headless Service
service 创建的时候可以指定 clusterIP:None,告诉 K8s 说我不需要 clusterIP(就是刚才所说的集群里面的一个虚拟 IP),然后 K8s 就不会分配给这个 service 一个虚拟 IP 地址,它没有虚拟 IP 地址怎么做到负载均衡以及统一的访问入口呢?
它是这样来操作的:pod 可以直接通过 service_name 用 DNS 的方式解析到所有后端 pod 的 IP 地址,通过 DNS 的 A 记录的方式会解析到所有后端的 Pod 的地址,由客户端选择一个后端的 IP 地址,这个 A 记录会随着 pod 的生命周期变化,返回的 A 记录列表也发生变化,这样就要求客户端应用要从 A 记录把所有 DNS 返回到 A 记录的列表里面 IP 地址中,客户端自己去选择一个合适的地址去访问 pod。
apiVersion: v1
kind: Service
metadata:name: my-servicelabels:app: my-service
spec:clusterIP: Noneselector:app: MyAppports:- protocol: TCPport: 80targetPort: 9376
可以从看到跟刚才我们声明的模板的区别,就是在spec下加了一个 clusterIP:None,即表明不需要虚拟 IP。
Headless Service 实际效果就是集群的 pod 访问 my-service 时,会直接解析到所有的 service 对应 pod 的 IP 地址,返回给 pod,然后 pod 里面自己去选择一个 IP 地址去直接访问。
五、集群内访问 Service
在集群里面,其他 pod 要怎么访问到我们所创建的这个 service 呢?
有三种方式:
- 首先我们可以通过 service 的虚拟 IP 去访问,比如说刚创建的 my-service 这个服务,通过 kubectl get svc 或者 kubectl discribe service 都可以看到它的虚拟 IP 地址是 10.96.47.23,端口是 80,然后就可以通过这个虚拟 IP 及端口在 pod 里面直接访问到这个 service 的地址;
- 第二种方式直接访问服务名,依靠 DNS 解析,就是同一个 namespace 里 pod 可以直接通过 service 的名字去访问到刚才所声明的这个 service。不同的 namespace 里面,我们可以通过 service 名字加“.”,然后加 service 所在的哪个 namespace 去访问这个 service,例如我们直接用 curl 去访问,就是 my-service:80 就可以访问到这个 service;
- 第三种是通过环境变量访问,在同一个 namespace 里的 pod 启动时,K8s 会把 service 的一些 IP 地址、端口,以及一些简单的配置,通过环境变量的方式放到 K8s 的 pod 里面。在 K8s pod 的容器启动之后,通过读取系统的环境变量比读取到 namespace 里面其他 service 配置的一个地址,或者是它的端口号等等。
比如在集群的某一个 pod 里面,可以直接通过 curl $ 取到一个环境变量的值,比如取到 MY_SERVICE_SERVICE_HOST 就是它的一个 IP 地址,MY_SERVICE 就是刚才我们声明的 my-service,SERVICE_PORT 就是它的端口号,这样也可以请求到集群里面的 MY_SERVICE 这个 service。
kubectl get pods -l app=MyAppkubectl exec -it my-deployment-6866788946-8v9m6 /bin/bashecho $MY_SERVICE_SERVICE_HOSTecho $MY_SERVICE_SERVICE_PORT
六、向集群外暴露 Service
前面介绍的都是在集群里面 node 或者 pod 去访问 service,service 怎么去向外暴露呢?怎么把应用实际暴露给公网去访问呢?
NodePort:这种方法会在集群的所有节点上开放一个端口(称为 NodePort),外部可以通过任意节点的 IP 地址加上这个端口号来访问服务。NodePort 的端口范围默认是 30000-32767。例如,如果你有一个名为 my-service 的服务,Kubernetes 可能会随机分配一个端口,比如 30001,那么你可以通过 :30001 来访问服务 。
LoadBalancer:在支持外部负载均衡器的云服务提供商(如 AWS、Azure、GCP 等)上,你可以创建一个 LoadBalancer 类型的服务。这将自动创建一个外部负载均衡器,并将外部流量转发到服务。LoadBalancer 类型的服务会在云提供商的负载均衡器上配置一个公共 IP 地址,你可以通过这个 IP 地址访问服务 。
ExternalName:这种方式通过 CNAME 记录将服务映射到一个外部域名。这意味着当请求发送到 Kubernetes 集群时,DNS 服务会返回一个 CNAME 记录,指向你指定的外部域名。这种方法不涉及流量的转发,而是在 DNS 层面上进行重定向 。
External IP:为服务分配一个或多个外部可访问的 IP 地址。这些 IP 地址通常由云服务提供商管理,可以是静态或动态分配。通过 Service 的 externalIPs 字段指定外部 IP 地址,外部流量可以通过指定的 IP 地址访问 Service。
Ingress:Ingress 是 Kubernetes 的一种 API 对象,它管理外部访问到集群内服务的 HTTP 流量。通过定义 Ingress 规则,你可以控制如何将外部请求路由到集群内的服务。Ingress 通常与 Ingress Controller 一起使用,后者负责实现 Ingress 规则并将流量转发到正确的服务 。
HostPort:在 Pod 级别使用,将 Pod 内的端口映射到宿主机的特定端口。这种方式不是通过 Service 资源实现的,而是在 Pod 定义中使用 hostPort 字段。
HostNetwork:在 Pod 级别使用,允许 Pod 直接使用宿主机的网络命名空间。这意味着 Pod 会绕过 Kubernetes 的网络代理,直接在宿主机上监听网络请求。这种方式不是通过 Service 资源实现的,而是在 Pod 定义中设置 hostNetwork: true。
七、操作示例
1、获取集群状态信息
kubectl get cs
2、创建 Service、Deployment
cat >> service.yaml << EOF
apiVersion: v1
kind: Service
metadata:name: nginx-testlabels:run: nginx
spec:selector:run: nginxports:- protocol: TCPport: 80targetPort: 80
EOF
cat >> server.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-testlabels:run: nginx
spec:replicas: 2selector:matchLabels:run: nginxtemplate:metadata:labels:run: nginxspec:containers:- name: nginximage: nginx:1.16.0imagePullPolicy: IfNotPresent
EOF
kubectl apply -f service.yaml -f server.yamlkubectl get pods -o wide -l run=nginxkubectl describe svc nginx-test
3、创建客户端的测试 Pod
现在去创建一个客户端的 pod 实际去感受一下如何去访问这个 K8s Service
cat >> client-test.yaml << EOF
apiVersion: v1
kind: Pod
metadata:name: test-client
spec:containers:- name: client-testimage: busybox:1.31.0command:- sleep- "3600"
EOF
4、集群内访问 Service 的三种方式
4.1、直接通过 Service 的 clusterIP 访问
kubectl exec -it test-client /bin/shwget -qO- 10.96.252.19
4.2、直接访问 Service 名
wget -qO- nginx-test
不同命名空间时也可以通过 .namespace 来访问 service
4.3、通过环境变量访问
通过执行 env 命令看一下它实际注入的环境变量的情况。看一下 nginx-test 的 service 的各种配置已经注册进来了。
env
wget -qO- $NGINX_TEST_SERVICE_HOST
5、集群外部访问服务
5.1、NodePort
NodePort
类型的 Service
提供了一种将集群内部的服务暴露给外部客户端访问的方式。通过创建一个类型为 NodePort
的 Service
,Kubernetes 控制器会在每个运行中的节点上打开一个固定的端口(默认范围是30000-32767,但可以根据需要进行配置)。外部流量可以通过这个节点端口访问到集群内的服务。
5.1.1、创建 NodePort 类型的 Service(NodePort的端口范围是 30000-32767)
cat >> nodeport-svc.yaml << EOF
apiVersion: v1
kind: Service
metadata:name: nodeport-service
spec:type: NodePortports:- port: 8080targetPort: 80nodePort: 30120selector:app: nginx
EOF
5.1.2、创建 Deployment 模拟后端一组应用 Pod
cat >> nodeport-deploy.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:name: nodeport-deploy
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.16.0ports:- containerPort: 80
EOF
5.1.3、创建并查看
kubectl apply -f nodeport-deploy.yaml -f nodeport-svc.yamlkubectl get pods -l app=nginxkubectl get svckubectl describe svc nodeport-service
5.1.4、通过节点 IP:port 访问
curl 192.168.112.10:30120
5.1.5、通过nodename 也可以访问
5.2、LoadBalancer
当创建一个 LoadBalancer
类型的服务时,Kubernetes 会尝试通过云提供商或第三方负载均衡解决方案来创建一个负载均衡器。这个负载均衡器会分配一个静态的外部IP地址(或 DNS 名称),并会将外部流量路由到集群内的服务。
5.2.1、安装METALLB
使用LoadBalancer
的方式进行服务发布,需要借助第三方的工具(METALLB)。
每个svc都有一个私有地址,只能在集群内部访问,如果我们把svc的类型设置为LoadBalancer
,则svc会获取到一个外部IP,这个外部IP来自地址池,示例使用METALLB配置地址池。
METALLB官网
5.2.2、查看MetalLB的安装方式:Installation by manifest
5.2.3、创建 MetalLB 的命名空间
kubectl create ns metallb-system
5.2.4、下载 MetalLB 的安装文件
wget https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/metallb.yamlwget https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/namespace.yaml
5.2.5、查看 MetalLB 需要的镜像
grep image metallb.yaml
5.2.6、修改 metallb.yaml 文件
image同级下添加如下字段
imagePullPolicy: IfNotPresent
5.2.7、worker节点提前下载speaker和controller镜像
所有节点都执行拉取操作
docker pull quay.io/metallb/speaker:v0.11.0docker pull quay.io/metallb/controller:v0.11.0
5.2.8、安装 MetalLB
kubectl apply -f namespace.yaml -f metallb.yaml
5.2.9、查看 Pod 验证安装情况
kubectl get pods -n metallb-system
5.2.10、配置地址池
cat >> IPAddressPool.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:namespace: metallb-systemname: config
data:config: |address-pools:- name: defaultprotocol: layer2addresses:- 192.168.112.100-192.168.112.200
EOF
5.2.11、创建地址池
kubectl apply -f IPAddressPool.yaml
5.2.12、创建 LoadBalancer 类型的 SVC
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:name: podtestlabels:app: podtest
spec:containers:- name: nginximage: nginx:1.16.0ports:- containerPort: 80
EOF
kubectl expose pod podtest --name=loadbalancer-svc --port=80 --target-port=80 --type=LoadBalancer --selector=app=podtest
查看svc,EXTERNAL-IP地址是从地址池里分配的(默认为首个可分配的ip地址)
5.2.13、通过 svc 的 EXTERNAL-IP 访问集群内部的服务
浏览器访问
192.168.112.100
curl 192.168.112.100
5.3、Ingress
Ingress
是 Kubernetes 中另一种将集群内部的服务暴露给外部的方式。不同于 Service
类型(如 NodePort
和 LoadBalancer
),Ingress
提供了一个更高级别的抽象,允许管理员定义更加复杂的路由规则,以便于管理和扩展HTTP和HTTPS服务。
TODO
下一篇单独讲讲吧
5.4、HostNetwork
hostNetwork
是 Kubernetes 中的一个特性选项,但它并不是直接用于将集群内部服务暴露给外部访问的方式,而是指在创建 Pod 时是否使用宿主机的网络栈来进行网络通信。当在 Pod 的规格中设置了 hostNetwork: true
时,Pod 将直接使用宿主机的网络接口而不是 Kubernetes 的服务网络。
5.4.1、给 node 节点添加 role 标签
kubectl label nodes k8s-node1 role=node1kubectl get nodes -L role
5.4.2、创建使用 hostnetwork 的 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginx-deployment
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:nodeSelector: role: node1hostNetwork: truecontainers:- image: nginx:1.16.0imagePullPolicy: IfNotPresentname: nginxports:- containerPort: 8080
5.4.3、通过节点 ip 访问
在和containers 同级的 hostNetwork: true,表示pod使用宿主机网络,配合 nodeSelector,把pod实例化在固定节点,如上,给node1节点加上标签role: node1 , 通过nodeSelector,nginx就会实例化在node1节点,这样就可以通过node1节点的ip就可以访问这个nginx了。
5.5、HostPort
在 Kubernetes 中,hostPort
是一种配置 Pod 的方式,使得 Pod 内部的应用可以通过特定的端口监听宿主机的网络接口。当 Pod 的容器配置了 hostPort
,它将能够接收来自宿主机对应端口的流量。这种方式可以实现外部服务访问集群内部服务的需求,但需要注意的是,使用 hostPort
需要注意端口冲突及安全性问题。
5.5.1、给 node 节点添加 role 标签
kubectl label k8s-node2 role=node2kubectl get nodes -L role
5.5.2、创建使用 hostport 的deployment
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: hostport-deploy
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec: nodeSelector:role: node2containers:- image: nginx:1.16.0imagePullPolicy: IfNotPresentname: nginxports:- containerPort: 8080hostPort: 80
5.5.3、通过标签节点 ip + port / pod ip 访问
和hostNetwork相比多了映射能力,可以把容器端口映射为node节点不同端口,hostPort,当然也需要nodeSelector来固定节点,不然每次创建,节点不同,ip也会改变
5.6、ExternalName
ExternalName
类型的 Service
是 Kubernetes 中用于提供从集群内部访问外部 DNS 名称的一种方式。它的主要目的是简化集群内部应用访问外部服务的过程。当你创建一个类型为 ExternalName
的 Service
时,Kubernetes 会为这个 Service
创建一个 DNS 条目,该条目将解析为指定的外部 DNS 名称。
与 NodePort
或 LoadBalancer
类型的 Service
不同,ExternalName
类型的 Service
不会暴露任何集群内部的服务到外部网络,而是相反,它让集群内部的服务可以访问外部的服务。
5.6.1、创建 ExternalName 类型的 Service
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:name: service-externalnamelabels:type: externalname
spec:type: ExternalNameexternalName: www.baidu.com
EOF
kubectl get svc -l type=externalname
5.6.2、启动临时 pod 测试 DNS 解析
可以看到集群内部是可以访问外部服务www.baidu.com
kubectl run -it --rm --image=alpine:latest dns-test -- sh
5.6.3、域名解析
- 查看 kube-dns 的 ip 地址
kubectl get svc kube-dns -n kube-system -o custom-columns='ClusterIP:.spec.clusterIP'
- 安装 dig 命令
yum provides digyum install -y bind-utils-9.11.4-26.P2.el7_9.16.x86_64
- 从集群内部解析
ExternalName
类型的Service
dig @172.16.0.10 service-externalname.default.svc.cluster.local
八、架构设计
1、Kubernetes 服务发现架构
如上图所示,K8s 服务发现以及 K8s Service 是这样整体的一个架构。
K8s 分为 master 节点和 worker 节点:
- master 里面主要是 K8s 管控的内容;
- worker 节点里面是实际跑用户应用的一个地方。
在 K8s master 节点里面有 APIServer,就是统一管理 K8s 所有对象的地方,所有的组件都会注册到 APIServer 上面去监听这个对象的变化,比如说我们刚才的组件 pod 生命周期发生变化这些事件。
1.1、组件
这里面最关键的有三个组件:
-
一个是 Cloud Controller Manager,负责去配置 LoadBalancer 的一个负载均衡器给外部去访问;
-
另外一个就是 Coredns,通过 Coredns 去观测 APIServer 里面的 service 后端 pod 的一个变化,去配置 service 的 DNS 解析,实现可以通过 service 的名字直接访问到 service 的虚拟 IP,或者是 Headless 类型的 Service 中的 IP 列表的解析;
-
在每个 node 里面会有 kube-proxy 这个组件,它通过监听 service 以及 pod 变化,然后实际去配置集群里面的 node pod 或者是虚拟 IP 地址的一个访问。
1.2、实际访问链路
比如说从集群内部的一个 Client Pod3 去访问 Service:
Client Pod3 首先通过 Coredns 这里去解析出 ServiceIP,Coredns 会返回给它 ServiceName 所对应的 service IP 是什么,这个 Client Pod3 就会拿这个 Service IP 去做请求,它的请求到宿主机的网络之后,就会被 kube-proxy 所配置的 iptables 或者 IPVS 去做一层拦截处理,之后去负载均衡到每一个实际的后端 pod 上面去,这样就实现了一个负载均衡以及服务发现。
对于外部的流量,比如说刚才通过公网访问的一个请求。它是通过外部的一个负载均衡器 Cloud Controller Manager 去监听 service 的变化之后,去配置的一个负载均衡器,然后转发到节点上的一个 NodePort 上面去,NodePort 也会经过 kube-proxy 配置的一个 iptables,把 NodePort 的流量转换成 ClusterIP,紧接着转换成后端的一个 pod 的 IP 地址,去做负载均衡以及服务发现。
相关文章:

k8s Service 服务
文章目录 一、为什么需要 Service二、Kubernetes 中的服务发现与负载均衡 -- Service三、用例解读1、Service 语法2、创建和查看 Service 四、Headless Service五、集群内访问 Service六、向集群外暴露 Service七、操作示例1、获取集群状态信息2、创建 Service、Deployment3、创…...
安全建设当中的冷门知识
今天说点有趣的话题,也是因为在安全建设过程中,安全员也不太可能都按照最理想的状态去工作,有资源的问题,有管理惰性问题,当然也有管理者本身决策的问题。 安全行业起步较晚,16年才施行网络安全法ÿ…...

python画图|极坐标下的3D surface
前述学习过程中,我们已经掌握了3D surface的基本绘制技巧,详见链接: python画图|3D surface基础教程-CSDN博客 基础教程中的3D surface绘制位于笛卡尔坐标系,但有时候会用到极坐标绘图。虽然我们已经学过简单的极坐标绘图技巧&a…...

html+css+js网页设计 旅游 大理旅游7个页面
htmlcssjs网页设计 旅游 大理旅游7个页面 网页作品代码简单,可使用任意HTML辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1&#…...

Day 29~42 JavaWeb
Java Web 1、基本概念 1.1、前言 web开发: web,网页的意思,www.baidu.com静态web html,css 提供给所有人看的数据始终不会发生变化动态web 淘宝,几乎是所有的网站 提供给所有人看的数据始终会发生变…...

小程序开发设计-第一个小程序:创建小程序项目④
上一篇文章导航: 小程序开发设计-第一个小程序:安装开发者工具③-CSDN博客https://blog.csdn.net/qq_60872637/article/details/142219152?spm1001.2014.3001.5501 须知:注:不同版本选项有所不同,并无大碍。 一、创…...

C++设计模式——Mediator中介者模式
一,中介者模式的定义 中介者模式是一种行为型设计模式。它通过一个中介者对象将多个对象之间的交互关系进行封装,使得对象之间的交互需要通过中介者对象来完成。该设计模式的结构很容易理解,以中介者为中心。 中介者模式的设计思想侧重于在…...
微服务之间远程调用实现思路
项目使用的Spring Cloud Alibaba框架,微服务之间远程调用使用OpenFeign,具体实现步骤如下: (1)在api工程定义OpenFeign接口,使用FeignClient注解进行定义。 (2)服务提供方定义Open…...
获取STM32 MCU的唯一ID
STM32每个系列都会有唯一的一个芯片序列号(96位bit) STM32F10X 的起始地址是 0x1FFFF7E8 STM32F20X 的起始地址是 0x1FFF7A10 STM32F30X 的起始地址是 0x1FFFF7AC STM32F40X 的起始地址是 0x1FFF7A10 STM32L0XX 的起始地址是 0x1FF80050 STM32L1XX 的起…...

Debian项目实战——环境搭建篇
Debian系统安装 准备工作 1、系统镜像:根据自己的需要选择合适的版本格式:x86 / arm 架构 | 最好下载离线安装版本 | 清华镜像源 2、制作工具:balenaEtcher 3、系统媒介:16G以上U盘最佳 烧录镜像 打开balenaEtcher进行烧录&am…...

CenterNet官方代码—目标检测模型推理部分解析与项目启动
CenterNet模型推理部分解析 CenterNet官方代码环境部署 CenterNet作为2019年CVPR推出的论文,论文中给出了官方代码所在的github仓库地址。https://github.com/xingyizhou/CenterNet。 整个代码的代码量并不是特别大,但整个项目的难点在于使用了老版本的…...

测试开发基础——测试用例的设计
三、测试用例的设计 1. 什么是测试用例 测试用例(Test Case)是为了实施测试而向被测试的系统提供的一组集合,这组集合包含:测试环境、操作步骤、测试数据、预期结果等要素。 设计测试用例原则一:测试用例中一个必需部分是对预期输出或结果进…...

C++第五十一弹---IO流实战:高效文件读写与格式化输出
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1. C语言的输入与输出 2. 流是什么 3. CIO流 3.1 C标准IO流 3.2 C文件IO流 3.2.1 以写方式打开文件 3.2.1 以读方式打开文件 4 stringstre…...
C++中使用分治法求最大值
在C++中使用分治法(Divide and Conquer)来求一个数组中的最大值是一个经典的问题。分治法是一种通过将原问题分解为若干个小规模相似子问题,递归地求解这些子问题,然后将子问题的解合并成原问题的解的方法。 以下是使用分治法求数组中最大值的步骤: 分解(Divide):将数…...

数据集 CULane 车道线检测 >> DataBall
数据集 CULane 车道线检测 自动驾驶 无人驾驶目标检测 CULane是用于行车道检测学术研究的大规模具有挑战性的数据集。它由安装在六辆由北京不同驾驶员驾驶的不同车辆上的摄像机收集。收集了超过55小时的视频,并提取了133,235帧。数据示例如上所示。我们将数据集分为…...
Android CustomDialog圆角背景不生效的问题
一行解决: window?.setBackgroundDrawableResource(android.R.color.transparent) 原文件: /*** Created by Xinghai.Zhao* 自定义选择弹框*/ SuppressLint("InflateParams", "MissingInflatedId") class CustomDialog(context: Context?) : AlertDia…...

C++速通LeetCode简单第9题-二叉树的最大深度
深度优先算法递归: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right…...
com.microsoft.sqlserver:sqljdbc4:jar:4.0 was not found产生原因及解决步骤
文章目录 问题sqlserver 包找不到 报错原因分析主要原因 解决方案步骤 1:检查 pom.xml 中的依赖声明步骤 2:配置 Microsoft 的 Maven 仓库步骤 3:强制更新 Maven 依赖步骤 4:清理本地仓库缓存步骤 5:手动下载并安装 sq…...
【算法】 滑动窗口—最长无重复子串
“无重复字符的最长子串”,难度为Medium,看下题目: 输入一个字符串 s,请计算 s 中不包含重复字符的最长子串长度。 比如,输入 s "aabab",算法返回2,因为无重复的最长子串是 "ab…...

SpringBoot2:web开发常用功能实现及原理解析-上传与下载
文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端调用Java接口下载文件2、Java接口下载网络文件到本地3、前端调用Java接口下载网络文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和…...
Komiko 视频到视频功能炸裂上线!
Komiko 平台作为行业的创新先锋,近日宣布推出全新的视频到视频(Video-to-Video)功能,这一举措犹如一颗重磅炸弹,瞬间在漫画、动画和插画创作的世界里掀起了惊涛骇浪,进一步巩固了其作为 AI 驱动的一体化创作…...

【k8s】k8s集群搭建
k8s集群搭建 一、环境准备1.1 集群类型1.2 安装方式1.3 主机规划1.4 环境配置1.4.1 说明1.4.2 初始化1.4.3 关闭防火墙和禁止防火墙开机启动1.4.4 设置主机名1.4.5 主机名解析1.4.6 时间同步1.4.7 关闭selinux1.4.8 关闭swap分区1.4.9 将桥接的IPv4流量传递到iptables的链1.4.1…...

计算机网络领域所有CCF-A/B/C类期刊汇总!
本期小编统计了【计算机网络】领域CCF推荐所有期刊的最新影响因子,分区、年发文量以及投稿经验,供大家参考! CCF-A类 1 IEEE Journal on Selected Areas in Communications 【影响因子】13.8 【期刊分区】JCR1区,中科院1区TOP …...
Webhook 配置备忘
本文地址:blog.lucien.ink/archives/552 将下列代码保存为 install.sh,然后 bash install.sh。 #!/usr/bin/env bash set -e wget https://github.mirrors.lucien.ink/https://github.com/adnanh/webhook/releases/download/2.8.2/webhook-linux-amd64.…...
三、元器件的选型
前言:我们确立了题目的功能后,就可以开始元器件的选型,元器件的选型关乎到我们后面代码编写的一个难易。 一、主控的选择 主控的选择很大程度上决定我们后续使用的代码编译器,比如ESP32使用的是VScode,或者Arduino&a…...

4D毫米波雷达产品推荐
供应商链接 :https://mp.weixin.qq.com/s/GYarrc9VEZS0FafxRUeG9w 大陆 ARS548 采埃孚 博世 安波福 -------- Waymo MobileEye 华为(未找到官网资料) ------- 森思泰克 http://www.whst.com/contact.html 芜湖经济技术开发区东区…...

设计模式-2 结构型模式
一、代理模式 1、举例 海外代购 2、代理基本结构图 3、静态代理 1、真实类实现一个接口,代理类也实现这个接口。 2、代理类通过真实对象调用真实类的方法。 4、静态代理和动态代理的区别 1、静态代理在编译时就已经实现了,编译完成后代理类是一个实际…...
java教程笔记(十一)-泛型
Java 泛型(Generics)是 Java 5 引入的重要特性之一,它允许在定义类、接口和方法时使用类型参数。泛型的核心思想是将类型由具体的数据类型推迟到使用时再确定,从而提升代码的复用性和类型安全性。 1.泛型的基本概念 1. 什么是泛…...

深入了解linux系统—— 进程池
前言: 本篇博客所涉及到的代码以同步到本人gitee:进程池 迟来的grown/linux - 码云 - 开源中国 一、池化技术 在之前的学习中,多多少少都听说过池,例如内存池,线程池等等。 那这些池到底是干什么的呢?池…...
MySQL数据库表设计与索引优化终极指南
MySQL数据库表设计与索引优化终极指南 标签:MySQL 数据库设计 索引优化 性能调优 一、前言:为什么表设计和索引如此重要? 在数据库系统中,良好的表设计和高效的索引策略是保证系统性能的关键。据统计,约70%的数据库性…...