官方教程,共 6 个小节。每一小节的第一部分是知识讲解,第二部分是在线测试环境的入口。
kubectl 的命令手册
原文地址

1 创建集群

1.1 使用 Minikube 创建集群

Kubernetes 集群

Kubernetes 协调一个高可用的计算机集群,这个集群连接到一起作为一个单元工作。通过 Kubernetes 中的抽象,可以将容器化应用程序部署到集群,而不必将它们绑定到单个机器上。为了使用这种新的部署模式,应用程序需要以一种将它们与单个主机分离的方式进行打包:容器化。在过去的部署模型中,应用程序作为深度集成到主机中的包直接安装到特定的机器上,相比之下,容器化应用程序更加灵活可用。Kubernetes 以更高效的方式自动化跨集群的应用程序容器的分发和调度。Kubernetes 是一个开放源代码的平台,可以在生产环境中使用。

Kubernetes 集群由两种类型的资源组成:

  • 用于协调集群的 Master
  • 用于作为 worker 运行应用程序的 Node

Kubernetes 是一个生产级的开源平台,负责协调计算机集群内部和跨集群应用程序容器的布局(调度)和执行。

集群图

Master 负责管理集群。Master 协调集群中的所有活动,例如调度应用程序,维护应用程序的期望状态,扩展应用程序以及滚动升级。

Node 节点是 Kubernetes 集群中的 worker 工作节点,可以是虚拟机或物理机。每个节点都安装了 Kubelet,用于管理节点并与 Kubernetes 的 Master 进行通信。每个节点还应具有处理容器操作的工具,例如 Docker 或 rkt。处理生产环境中的流量的 Kubernetes 集群至少需要有三个节点。

Master 用于管理集群,Node 节点用于托管要运行的应用程序。

在 Kubernetes 上部署应用程序时,需要告知 Master 启动应用程序容器。Master 会规划容器运行在集群的某个节点上。每个节点通过 Master 暴露的 Kubernetes API 与 Master 通信。终端用户也可以通过 Kubernetes API 直接与集群交互。

Kubernetes 集群可以部署到物理机或虚拟机上。可以使用 Minikube 来学习 Kubernetes 的部署过程。Minikube 是一个轻量级的 Kubernetes 实现,可以在你的机器上创建虚拟机并部署一个只有单个节点的简单集群。Minikube 可以在 Linux、macOS 或 Windows 上安装。Minikube CLI 命令行界面提供了用于你的集群的基本启动操作,包括 start、stop、status 以及 delete。但是对于这个教程,你会使用一个预先安装了 Minikube 的在线终端。

1.2 交互式教程

Step 1 启动并运行集群

在线终端已经安装好了 Minikube。运行下面的命令确认一下:

minikube version

确认无误后,启动集群:

minikube start

现在,在线终端上有运行中的 Kubernetes 集群了。Minikube 会启动一个虚拟机,Kubernetes 集群运行在虚拟机上。

Step 2 集群的版本

通过 kubectl 命令可以根 Kubernetes 交互。运行 kubectl version 命令查看 kubectl 是否安装成功:

kubectl version

这里会同时看到 client 和 server 两个版本号。其中,client 对应 kubectl 的版本,server 对应 Master 上安装的 Kubernetes 的版本。这里也会显示构建的详情。

Step 3 集群详情

通过 kubectl cluster-info 命令查看集群的详情:

kubectl cluster-info

我们有一个运行中的 Master 和一个 Dashboard。Kubernetes Dashboard 允许你通过 UI 用户界面查看应用程序。在本教程中,我们将专注于通过命令行部署和探索应用程序。运行 kubectl get nodes 命令查看集群中的 Node 节点:

kubectl get nodes

这会显示所有可用于应用程序的节点。现在我们只有一个节点,且其 status 状态是 ready,表示可以用于部署应用程序。

2 部署应用程序

2.1 使用 kubectl 创建 Deployment

Kubernetes Deployments

一旦运行了 Kubernetes 集群,就可以在其上部署容器化的应用程序。为此,需要创建一个 Deployment 用于部署应用程序。Deployment 指示 Kubernetes 如何创建和更新应用程序的实例。创建 Deployment 后,Kubernetes Master 会将应用程序实例分配到集群中的独立节点上。

创建应用程序实例后,Deployment Controller 会一直监控这些实例。如果运行实例的节点出现故障或被删除,则 Deployment Controller 将替换这些节点。这提供了一种自我修复机制来应对机器故障或维护。

在编排前的世界中,安装脚本通常用于启动应用程序,但无法从机器故障中恢复。通过创建应用程序实例并保持它们跨节点运行,Kubernetes Deployment 为应用程序管理提供了一种完全不同的方法。

Deployment 负责创建和更新应用程序的实例

在 Kubernetes 上部署第一个应用程序


可以通过 Kubectl 这个命令行接口来创建并管理 Deployment。Kubectl 使用 Kubernetes API 来跟集群交互。在这一部分你会学习最常用的 Kubectl 命令,创建一个用于在 Kubernetes 集群上运行应用程序的 Deployment。

Deployment 创建完成后,需要为你的应用程序指定容器镜像及需要运行的副本(replicas)数量。可以在后面通过更新 Deployment 来改变这些设置,本教程的第 5 节和第 6 节讲述了如何伸缩及更新 Deployment。

要部署到 Kubernetes 中,应用程序必须打包为 Kubernetes 支持的容器格式。

对于第一个 Deployment,我们会使用一个打包为 Docker 容器的 Node.js 应用。源代码和 Dockerfile 可以在 Kubernetes Bootcamp 的 GitHub 仓库 中下载。

2.2 交互式教程

Step 1 kubectl 基础

类似 Minikube,Kubectl 也安装在在线终端中。输入 kubectl 可以查看命令的具体用法。Kubectl 命令的常用格式是:kubectl 动作 资源。这会在指定的资源(例如 node、container)上执行指定的动作(例如 create、get、describe)。可以在每个可能的参数后面使用 --help 来查看额外信息(kubectl get nodes --help)。

$ kubectl
kubectl controls the Kubernetes cluster manager.Find more information at: https://kubernetes.io/docs/reference/kubectl/overview/Basic Commands (Beginner):create         Create a resource from a file or from stdin.expose         Take a replication controller, service, deployment or pod and expose it as a new Kubernetes Servicerun            Run a particular image on the clusterset            Set specific features on objectsrun-container  Run a particular image on the cluster. This command is deprecated, use "run" insteadBasic Commands (Intermediate):get            Display one or many resourcesexplain        Documentation of resourcesedit           Edit a resource on the serverdelete         Delete resources by filenames, stdin, resources and names, or by resources and label selectorDeploy Commands:rollout        Manage the rollout of a resourcerolling-update Perform a rolling update of the given ReplicationControllerscale          Set a new size for a Deployment, ReplicaSet, Replication Controller, or Jobautoscale      Auto-scale a Deployment, ReplicaSet, or ReplicationControllerCluster Management Commands:certificate    Modify certificate resources.cluster-info   Display cluster infotop            Display Resource (CPU/Memory/Storage) usage.cordon         Mark node as unschedulableuncordon       Mark node as schedulabledrain          Drain node in preparation for maintenancetaint          Update the taints on one or more nodesTroubleshooting and Debugging Commands:describe       Show details of a specific resource or group of resourceslogs           Print the logs for a container in a podattach         Attach to a running containerexec           Execute a command in a containerport-forward   Forward one or more local ports to a podproxy          Run a proxy to the Kubernetes API servercp             Copy files and directories to and from containers.auth           Inspect authorizationAdvanced Commands:apply          Apply a configuration to a resource by filename or stdinpatch          Update field(s) of a resource using strategic merge patchreplace        Replace a resource by filename or stdinconvert        Convert config files between different API versionsSettings Commands:label          Update the labels on a resourceannotate       Update the annotations on a resourcecompletion     Output shell completion code for the specified shell (bash or zsh)Other Commands:api-versions   Print the supported API versions on the server, in the form of "group/version"config         Modify kubeconfig fileshelp           Help about any commandplugin         Runs a command-line pluginversion        Print the client and server version informationUsage:kubectl [flags] [options]Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all
commands).

运行 kubectl version 命令来确认 kubectl 是否已经配置为与集群交互:

kubectl version

如果同时看到了 client 和 server 的版本信息,则配置无误。

通过下面命令查看集群中的节点:

kubectl get nodes

现在我们的例子中只能看到一个节点。Kubernetes 会根据 Node 的可用资源的不同来选择一个最合适的 Node 部署应用程序。

Step 2 部署应用

现在,通过 kubectl run 命令在 Kubernetes 上运行我们的第一个应用。run 命令会创建一个新的 Deployment。我们需要提供 Deployment 的名字以及应用程序的镜像位置(如果不是 Docker Hub,则需要提供完整的仓库 URL)。可以通过 --port 参数使应用运行在特定端口上。国内无法访问 gcr.io,需要替换:

kubectl run kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --port=8080

注意,这里因为 墙 的问题,需要替换镜像路径为 jocatalin/kubernetes-bootcamp,完整命令如下:

kubectl run kubernetes-bootcamp --image=jocatalin/kubernetes-bootcamp:v1 --port=8080

如果需要删除所有通过 kubectl run 命令创建的 Deployment,可以运行下面命令:

kubectl delete deployments --all

现在,通过创建 Deployment 已经成功部署了第一个应用。这为你做了几件事情:

  • 搜索可以运行应用程序实例的合适节点(我们只有 1 个可用节点)
  • 调度应用程序在该节点上运行
  • 配置集群以在需要时将实例重新规划到新节点上

通过下面命令列出所有的 Deployment:

kubectl get deployments

这里会看到一个 Deployment,其中运行着应用程序的一个实例。实例运行在节点的 Docker 容器内。

Step 3 查看应用

Kubernetes 中运行的 Pod 运行在私有的隔离网络中。默认情况下,Pod 仅对同一个 Kubernetes 集群中的 service 和其他 Pod 可见。当我们使用 kubectl 时,会通过 API 终端交互,实现与应用程序的通信。

在第 4 小节,会讲解如何使用将应用暴露到 kubernetes 集群之外的选项。

kubectl 命令可以创建代理,将通信转发到集群范围的专用网络。代理可以通过 control-C 终止,并且在运行时不会显示任何输出。

打开第二个终端窗口来运行代理(或者以后台程序 & 的方式在同一个终端中运行):

kubectl proxy

现在,在我们的主机(在线终端)和 Kubernetes 集群之间通过代理建立了连接。通过代理可以从终端直接访问 Kubernetes API。

可以在使用 proxy 开启了代理的终端上,通过 http://localhost:8001 查看所有 API。例如,可以使用 curl 命令直接通过 API 查询版本:

curl http://localhost:8001/version

API 服务器会基于 Pod 的名字自动为每个 Pod 创建一个终端,这个终端也可以通过代理访问。

首先,需要获取 Pod 的名字,并存入环境变量 POD_NAME 中:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
echo Name of the Pod: $POD_NAME

然后,向 Pod 中运行的应用发送 HTTP 请求:

curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/

URL 就是 Pod 中对应的 API 的路由。

注意:代理需要保持开启状态。官方示例需要新开页签并执行 kubectl proxy 指令。当然,也可以在原页签中后台执行这个命令 kubectl proxy &

3 探索你的应用

3.1 查看 Pod 和 Node

Pod

在上一节中创建了 Deployment 之后,Kubernetes 会创建 Pod 用于托管应用实例。Pod 是一个 Kubernetes 抽象,表示一个由一个或多个应用容器(Docker 或 rkt)及用于这些容器的共享资源组成的分组。这些资源包括:

  • 共享存储,作为 Volume 卷
  • 网络,作为唯一的集群 IP 地址
  • 信息,描述如何运行每个容器,例如容器映像版本或要使用的特定端口

Pod 模拟了特定于应用程序的“逻辑主机”,并且可以包含相对紧密耦合的不同的应用程序容器。例如,Pod 可能包含一个带有 Node.js 应用程序的容器,以及另一个用于向 Node.js Web 服务器提供数据的容器。Pod 中的容器共享一个 IP 地址和端口空间,它们总是地址相同、共同调度(co-located and co-scheduled),并在同一个节点上的共享上下文中运行。

Pod 是 Kubernetes 平台上的原子单元。当在 Kubernetes 上创建一个 Deployment 时,该 Deployment 将创建带有容器的 Pod(而不是直接创建容器)。每个 Pod 在调度到某个节点后,会与该节点绑定,并保持在该位置直到终止(根据重启策略)或删除。如果发生节点故障,则会在集群中的其他可用节点上调度相同的 Pod。

Pod 概览图

Node

Pod 总是在 Node 上运行。Node 是 Kubernetes 中的工作机器,可能是虚拟机或物理机,具体取决于集群。每个 Node 由 Master 管理。一个 Node 上可以有多个 Pod,并且 Kubernetes Master 会自动在集群中的 Node 上调度 Pod。Master 的自动调度考虑了每个节点上的可用资源。

每个 Kubernetes Node 节点至少运行:

  • Kubelet,负责 Kubernetes Master 与 Node 之间的通信,并管理机器上运行的 Pod 和容器。
  • 一个容器运行时(如 Docker,rkt),负责从 registry 中获取容器镜像,解包容器并运行应用程序。

如果容器紧密耦合并需要共享资源(如磁盘),则只能将它们一起安排在一个 Pod 中。

Node 概览图

使用 kubectl 进行故障排除

在第 2 小节中使用了 Kubectl 命令行界面。第 3 小节中会使用 kubectl 获取有关 Deployment 的应用程序及环境的信息。最常见的操作可以通过以下 kubectl 命令完成:

  • kubectl get - 列出资源
  • kubectl describe - 显示有关资源的详细信息
  • kubectl logs - 从 Pod 中的容器打印日志
  • kubectl exec - 在 Pod 中的容器上执行命令

可以使用这些命令查看应用程序的部署时间、当前状态、运行位置以及配置信息。

Node 是 Kubernetes 中执行具体任务的机器,可能是虚拟机或物理机,具体取决于群集。一个 Node 上可以运行多个 Pod。

3.2 交互式教程

Step 1 检查应用的配置

先确认一下前面部署的应用是否正在运行。使用 kubectl get 命令查看存在的 Pod:

kubectl get pods

如果没有运行中的 Pod,再次执行上面命令(部署需要几秒钟的时间)。

然后,查看这个 Pod 里面有哪些容器,这些容器是通过哪些镜像构建的。使用 kubectl describe pods 命令:

kubectl describe pods

这里会看到 Pod 中容器的详情:IP 地址,使用的端口号,和 Pod 生命周期相关的事件列表。

describe 命令的输出很复杂,并且包含了许多目前还没有讲解的概念。在教程最后你会熟悉这一切。

注意:describe 命令可以获取用于大部分 Kubernetes 原语的详情:node、deployments、pods。describe 命令的输出可读性好,不适合用于脚本。

Step 2 在终端中显示应用程序

Pod 运行在一个孤立的专用网络中,需要代理才能访问它们以便调试并与之交互。为此,我们将使用 kubectl proxy 命令在第二个终端窗口中运行代理:

kubectl proxy

现在,获取 Pod 的名称并直接通过代理查询该 Pod。获取 Pod 名称并将其存储在 POD_NAME 环境变量中:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
echo Name of the Pod: $POD_NAME

通过 curl 请求查看我们的应用程序的输出:

curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/

URL 是 Pod 的 API 对应的路由。

Step 3 查看容器日志

应用程序发送到 STDOUT 的任何内容都将成为 Pod 中容器的日志。使用 kubectl logs 命令检索这些日志:

kubectl logs $POD_NAME

注意:这里不需要指定容器名字,因为在我们的 Pod 中只有一个容器。

Step 4 在容器中执行命令

Pod 启动并运行后,可以直接在容器中执行命令。使用 exec 命令时需要指定 Pod 的名字作为参数。列出环境变量:

kubectl exec $POD_NAME env

这里容器的名称可以省略,因为我们在 Pod 中只有一个容器。

下面,让我们在 Pod 的容器中开始一个 bash 会话:

kubectl exec -ti $POD_NAME bash

现在,在我们运行 NodeJS 应用程序的容器中开启了会话。应用程序的源代码是 server.js 文件:

cat server.js

可以通过 curl 命令确认应用程序是否启动并在运行中:

curl localhost:8080

注意:这里是在 NodeJS 容器内执行命令,所以才可以使用 localhost。

通过 exit 命令关闭到容器的连接。

4 暴露应用到外网环境

4.1 通过 Service 暴露应用程序

Kubernetes Service 概述

Kubernetes Pod 是有 生命周期 的。当工作节点宕机时,节点上运行的 Pod 也会丢失。然后,ReplicationController 会创建新的 Pod 来动态地使集群回到所需的状态,以保持应用程序的运行。另一个例子,考虑具有 3 个副本的图像处理后端。副本是可以替代的,前端系统不应该关心后端的副本,哪怕 Pod 丢失并重新创建。也就是说,Kubernetes 集群中的每个 Pod 都具有唯一的 IP 地址,即使是同一个节点上的 Pod 也是如此,因此需要一种自动协调 Pod 切换的方式,以便应用程序持续运行。

Kubernetes 中的 Service 是一个抽象,定义了一个 Pod 的逻辑分组和一个访问这些 Pod 的策略。服务使得相关 Pod 之间建立松散耦合。就像其他 Kubernetes 对象一样,通过 YAML(首选)或 JSON 来定义 Service。Service 所针对的一组 Pod 集合通常由 LabelSelector 标签选择器来确定(请参阅下面的内容以了解为什么您可能需要规范中不包含选择器的 Service。a Service without including selector in the spec)。

虽然每个 Pod 具有唯一的 IP 地址,但是如果没有 Service,这些 IP 不会暴露到集群外部。Service 允许应用程序接收流量。通过在 ServiceSpec 中指定类型,可以以不同的方式暴露 Service:

  • ClusterIP(默认) - 将 Service 公开在集群的内部 IP 上。这种类型使服务只能从集群内访问。
  • NodePort - 使用 NAT 在集群中每个选定节点的同一端口上暴露 Service。使用 <NodeIP>:<NodePort> 使集群外部的服务可访问。ClusterIP 的超集。
  • LoadBalancer - 在当前云中创建外部负载平衡器(如果支持)并为服务分配固定的外部 IP。NodePort 的超集。
  • ExternalName - 使用任意名称(由规范中的externalName指定)通过返回具有名称的 CNAME 记录来暴露 Service。没有使用代理。需要 v1.7 或更高版本的 kube-dns。

有关不同类型 Service 的更多信息可以在 Using Source IP 教程中找到。另请参阅 用服务连接应用程序。

此外,注意,有些 Service 的用例未在 spec 规范中定义 selector。创建的 Service 如果没有 selector,就不会创建相应的 Endpoint 对象。这允许用户手动将 Service 映射到特定的端点。不使用 selector 的另一个可能性是你严格使用 type: ExternalName

Kubernetes Service 是一个抽象层,定义了 Pod 的逻辑分组,并为这一组 Pod 实现外部流量暴露,负载均衡和服务发现。

Service 和 Label

Service 通过一组 Pod 来路由流量。Service 是一个抽象层,允许 Pod 在 Kubernetes 中死亡和复制而不影响应用程序。在相关 Pod 中的发现和路由(例如应用程序中的前端和后端组件)由 Kubernetes Services 处理。

Services 使用 标签和选择器 匹配一组 Pod,这是一个允许对 Kubernetes 中的对象进行逻辑操作的分组原语(Services match a set of Pods using labels and selectors, a grouping primitive that allows logical operation on objects in Kubernetes)。标签是附加到对象的键/值对,可以以多种方式使用:

  • 指定用于开发,测试和生产的对象
  • 嵌入版本标签
  • 使用标签分类对象

可以在创建 Deployment 的同时在 kubectl 中使用 --expose 创建 Service。

标签可以在创建时或之后添加到对象,并且可以随时修改。现在让我们使用服务公开我们的应用程序并应用一些标签。

4.2 交互式教程

Step 1 创建 Service

首先确认应用是否正在运行。使用 kubectl get 命令查看存在的 Pod 及状态:

kubectl get pods

然后,列出集群当前运行的 Service:

kubectl get services
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP          3m

有一个名为 Kubernetes 的 Service,在 Minikube 启动集群时会默认创建这个服务。通过 expose 命令,使用 NodePort 参数(Minikube 暂时不支持 LoadBalancer),创建新 Service 并将其暴露到外网:

kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080

再次列出集群当前运行的 Service:

kubectl get services
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP          3m
kubernetes-bootcamp   NodePort    10.105.185.171   <none>        8080:32327/TCP   9s

kubernetes-bootcamp 这个 Service 已经运行了。可以看到,这个 Service 有一个唯一的 CLUSTER-IP,一个内部端口号以及一个 EXTERNAL-IP(Node 节点的 IP)。

要找出开放到外部的端口(通过 NodePort 选项)是哪个,需要运行 kubectl describe service 命令:

kubectl describe services/kubernetes-bootcamp

创建名为 NODE_PORT 的环境变量,保存分配的节点端口号:

export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')
echo NODE_PORT=$NODE_PORT

现在,可以通过 curl、Node 节点的 IP 地址及暴露的端口号测试应用程序是否可以从外网访问:

curl $(minikube ip):$NODE_PORT

如果成功获取服务器的响应,则 Service 暴露成功。

Step 2: 使用标签

Deployment 自动为 Pod 创建了一个标签。使用 kubectl describe deployment 命令,可以看到标签的名称:

# kubectl describe deployment
Name:                   kubernetes-bootcamp
Namespace:              default
CreationTimestamp:      Mon, 09 Apr 2018 07:26:15 +0000
Labels:                 run=kubernetes-bootcamp
Annotations:            deployment.kubernetes.io/revision=1
Selector:               run=kubernetes-bootcamp
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:Labels:  run=kubernetes-bootcampContainers:kubernetes-bootcamp:Image:        gcr.io/google-samples/kubernetes-bootcamp:v1Port:         8080/TCPEnvironment:  <none>Mounts:       <none>Volumes:        <none>
Conditions:Type           Status  Reason----           ------  ------Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   kubernetes-bootcamp-5dbf48f7d4 (1/1 replicas created)
Events:Type    Reason             Age   From                   Message----    ------             ----  ----                   -------Normal  ScalingReplicaSet  32m   deployment-controller  Scaled up replica set kubernetes-bootcamp-5dbf48f7d4 to 1

可以通过这个标签查询 Pod。使用 kubectl get pods 命令时,通过 -l 参数指定标签值:

# kubectl get pods -l run=kubernetes-bootcamp
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5dbf48f7d4-c8jwv   1/1       Running   0          35m

查看 Service 时,也可以使用这个参数:

# kubectl get services -l run=kubernetes-bootcamp
NAME                  TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes-bootcamp   NodePort   10.105.185.171   <none>        8080:32327/TCP   32m

获取 POD 名字并存储到 POD_NAME 环境变量中:

export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
echo Name of the Pod: $POD_NAME

可以通过 kubectl label 对象类型 对象名字 新标签 命令创建新标签:

kubectl label pod $POD_NAME app=v1

这会为我们的 Pod 应用一个新标签(这里将应用程序的版本固定到 Pod),可以使用 kubectl describe pod 命令检查:

kubectl describe pods $POD_NAME

这里会看到标签附加到了 Pod 上。现在可以使用这个新标签来查询 Pod 列表了:

kubectl get pods -l app=v1

Step 3 删除 Service

通过 kubectl delete service -l your-label 命令删除 Service。这里也可以使用标签:

kubectl delete service -l run=kubernetes-bootcamp

确认 Service 删除是否成功:

kubectl get services

这里会发现 Service 删除成功。可以通过 curl 和之前暴露的 IP 地址和端口号确认路由不再暴露到外网:

curl $(minikube ip):$NODE_PORT

这证明集群外部无法继续访问我们的应用程序了。可以通过在 Pod 内部执行 curl 来验证应用程序仍然在运行中:

kubectl exec -ti $POD_NAME curl localhost:8080

可以看到应用仍在运行。

5 伸缩应用

5.1 运行应用的多个实例

伸缩一个应用

在之前的讲解中,我们创建了一个 Deployment,然后通过一个 Service 将其暴露到外网。Deployment 只创建一个 Pod 来运行我们的应用程序。当流量增加时,我们需要扩展应用以满足用户需求。

应用的伸缩是通过改变 Deployment 中副本的数量来完成的。

可以使用 kubectl run 命令的 --replicas 参数从头开始创建具有多个实例的Deployment。

Scaling 概览图


应用的伸缩是通过改变 Deployment 中副本的数量来完成的。

扩展 Deployment 将确保创建新 Pod 并将其调度到具有可用资源的节点。收缩 Deployment 将减少 Pod 到新的期望数量。Kubernetes 还支持 Pod 的 自动缩放,但它不在本教程的范围之内。伸缩到零也是可能的,这会终止指定 Deployment 的所有 Pod。

运行应用程序的多个实例将需要一种方法将流量分配给所有这些实例。Service 具有集成的负载均衡器,可将网络流量分配给 Deployment 的所有 Pod。Service 将使用 endpoint 持续监视正在运行的 Pod,以确保流量仅发送到可用的 Pod。

应用伸缩是通过更改 Deployment 中副本的数量来完成的。

一旦运行了多个应用程序实例,就可以在不停机的情况下执行滚动更新。在下一小节中介绍会介绍。现在,转到在线终端并扩展我们的应用程序。

5.2 交互式教程

Step 1: 伸缩 Deployment

列出 Pod:

$ kubectl get pods -o wide
NAME                                   READY     STATUS    RESTARTS   AGE       IP         NODE
kubernetes-bootcamp-5c69669756-56z44   1/1       Running   0          11s       172.18.0.2   minikube

列出 Deployment:

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1         1         1            1           2m

我们应该有 1 个 Pod。如果不是,请再次运行该命令。各字段的解释如下:

  • DESIRED 字段显示配置的副本数量
  • CURRENT 字段显示现在有多少副本正在运行
  • UP-TO-DATE 是已更新的副本数量,在滚动更新时,这个数字会从 0 增大到 DESIRED 字段值
  • AVAILABLE 字段显示用户实际可用的副本数量

然后,将 Deployment 扩展到 4 个副本。使用 kubectl scale 命令时,指定 Deployment 类型、名称以及所需实例的数量:

$ kubectl scale deployments/kubernetes-bootcamp --replicas=4
deployment.extensions "kubernetes-bootcamp" scaled

再次列出可用的 Deployment:

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4         4         4            4           4m

更改已生效,现在应用程序有 4 个可用实例。接下来,看一下 Pod 的数量是否发生了变化:

$ kubectl get pods -o wide
NAME                                   READY     STATUS    RESTARTS   AGE       IP         NODE
kubernetes-bootcamp-5c69669756-4xrp6   1/1       Running   0          57s       172.18.0.6   minikube
kubernetes-bootcamp-5c69669756-56z44   1/1       Running   0          5m        172.18.0.2   minikube
kubernetes-bootcamp-5c69669756-8lccx   1/1       Running   0          57s       172.18.0.7   minikube
kubernetes-bootcamp-5c69669756-z97gr   1/1       Running   0          57s       172.18.0.5   minikube

现在有 4 个具有不同 IP 地址的 Pod。更改已在 Deployment 事件日志中注册。使用 describe 命令查看:

$ kubectl describe deployments/kubernetes-bootcamp
Name:                   kubernetes-bootcamp
Namespace:              default
CreationTimestamp:      Thu, 03 May 2018 13:21:05 +0000
Labels:                 run=kubernetes-bootcamp
Annotations:            deployment.kubernetes.io/revision=1
Selector:               run=kubernetes-bootcamp
Replicas:               4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:Labels:  run=kubernetes-bootcampContainers:kubernetes-bootcamp:Image:        gcr.io/google-samples/kubernetes-bootcamp:v1Port:         8080/TCPHost Port:    0/TCPEnvironment:  <none>Mounts:       <none>Volumes:        <none>
Conditions:Type           Status  Reason----           ------  ------Progressing    True    NewReplicaSetAvailableAvailable      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   kubernetes-bootcamp-5c69669756 (4/4 replicas created)
Events:Type    Reason             Age   From                   Message----    ------             ----  ----                   -------Normal  ScalingReplicaSet  6m    deployment-controller  Scaled up replica set kubernetes-bootcamp-5c69669756 to 1Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set kubernetes-bootcamp-5c69669756 to 4

从这个命令的输出中也可以看到现在有 4 个副本。

Step 2: 负载均衡

检查 Service 是否对流量做了负载均衡。可以通过 describe 命令找出暴露的 IP 地址和端口:

kubectl describe services/kubernetes-bootcamp

创建名为 NODE_PORT 的环境变量保存节点的端口号:

$ export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')
$ echo NODE_PORT=$NODE_PORT
NODE_PORT=30999

然后,通过 curl 访问暴露的 IP 地址和端口号。多次执行这个命令:

$ curl $(minikube ip):$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-j58hc | v=1

每个请求会命中不同的 Pod。这证明负载均衡生效了。

Step 3: 缩减应用

再次运行 kubectl scale 命令将 Service 缩减为 2 个副本:

$ kubectl scale deployment/kubernetes-bootcamp --replicas=2
deployment.extensions "kubernetes-bootcamp" scaled

列出 Deployment 来验证变更是否生效:

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   2         2         2            2           2m

期望的副本数是 2。列出 Pod 的数量:

$ kubectl get pods -o wide
NAME                                   READY     STATUS        RESTARTS   AGEIP           NODE
kubernetes-bootcamp-5c69669756-dmbj8   1/1       Terminating   0          2m172.18.0.5   minikube
kubernetes-bootcamp-5c69669756-j58hc   1/1       Terminating   0          2m172.18.0.2   minikube
kubernetes-bootcamp-5c69669756-mvj7v   1/1       Running       0          2m172.18.0.3   minikube
kubernetes-bootcamp-5c69669756-n47zs   1/1       Running       0          2m172.18.0.4   minikube

只剩 2 个运行中的 Pod 了。

6 更新应用

6.1 执行滚动更新

更新应用

用户希望应用程序随时都可以访问,而开发者希望每天能部署几个新版本。在 Kubernetes 中通过滚动更新实现这两个目的。滚动更新使用新的实例逐个更新 Pod(而不是一次全部更新),从而实现不停机对 Deployment 的更新。新 Pod 将分配到具有可用资源的节点上。

在之前的讲解中,我们伸缩了应用程序以运行多个实例。这是执行更新而不影响应用程序可用性的要求。默认情况下,更新过程中允许不可用的 Pod 的最大数量以及可以创建的新 Pod 的最大数量为 1。这两个选项都可以配置为数字或百分比(相对于 Pods)。在 Kubernetes 中,更新是版本化的,任何 Deployment 的更新都可以恢复到以前的(稳定)版本。

滚动更新使用新的实例逐个更新 Pod(而不是一次全部更新),从而实现不停机对 Deployment 的更新。

滚动升级概览




与应用程序伸缩类似,如果 Deployment 暴露到外网,在更新期间 Service 会将流量负载平衡到可用的 Pod。可用的 Pod 是可以响应用户请求的应用程序的可用实例。

滚动更新允许执行以下操作:

  • 将应用程序从一个环境推到另一个环境(通过容器镜像更新)
  • 回退到以前的版本
  • 以零停机时间持续集成和持续交付应用程序

如果 Deployment 暴露到外网,在更新期间 Service 会将流量负载平衡到可用的 Pod。

在下面的交互式教程中,会将我们的应用程序更新为新版本,并执行回滚。

6.2 交互式教程

Step 1: 更新应用程序的版本

列出所有 Deployment:

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4         4         4            4           16s

列出运行中的 Pod:

$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-2qclp   1/1       Running   0          35s
kubernetes-bootcamp-5c69669756-btznb   1/1       Running   0          35s
kubernetes-bootcamp-5c69669756-dfjzr   1/1       Running   0          35s
kubernetes-bootcamp-5c69669756-kl225   1/1       Running   0          35s

通过对 Pod 执行 describe 命令(查看 Image 字段)查看当前应用程序的版本。

$ kubectl describe pods
Name:           kubernetes-bootcamp-5c69669756-2qclp
Namespace:      default
Node:           minikube/172.17.0.33
Start Time:     Thu, 03 May 2018 14:39:49 +0000
Labels:         pod-template-hash=1725225312run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.2
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:kubernetes-bootcamp:Container ID:   docker://bd2acdb49f2d9a2e9f796065372425562818156e8f121c69b11c29c260f2f106Image:          gcr.io/google-samples/kubernetes-bootcamp:v1Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64afPort:           8080/TCPHost Port:      0/TCPState:          RunningStarted:      Thu, 03 May 2018 14:39:51 +0000Ready:          TrueRestart Count:  0Environment:    <none>Mounts:/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:Type           StatusInitialized    TrueReady          TruePodScheduled   True
Volumes:default-token-86srb:Type:        Secret (a volume populated by a Secret)SecretName:  default-token-86srbOptional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s
Events:Type     Reason                 Age              From               Message----     ------                 ----             ----               -------Warning  FailedScheduling       1m (x2 over 1m)  default-scheduler  0/1 nodes areavailable: 1 node(s) were not ready.Normal   Scheduled              1m               default-scheduler  Successfully assigned kubernetes-bootcamp-5c69669756-2qclp to minikubeNormal   SuccessfulMountVolume  1m               kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-86srb"Normal   Pulled                 1m               kubelet, minikube  Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machineNormal   Created                1m               kubelet, minikube  Created containerNormal   Started                1m               kubelet, minikube  Started containerName:           kubernetes-bootcamp-5c69669756-btznb
Namespace:      default
Node:           minikube/172.17.0.33
Start Time:     Thu, 03 May 2018 14:39:49 +0000
Labels:         pod-template-hash=1725225312run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.3
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:kubernetes-bootcamp:Container ID:   docker://40dfdafb48176b77efaa6eaf3c8ed256feffd15e60743d4da51419eb3d5c9a33Image:          gcr.io/google-samples/kubernetes-bootcamp:v1Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64afPort:           8080/TCPHost Port:      0/TCPState:          RunningStarted:      Thu, 03 May 2018 14:39:52 +0000Ready:          TrueRestart Count:  0Environment:    <none>Mounts:/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:Type           StatusInitialized    TrueReady          TruePodScheduled   True
Volumes:default-token-86srb:Type:        Secret (a volume populated by a Secret)SecretName:  default-token-86srbOptional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s
Events:Type     Reason                 Age              From               Message----     ------                 ----             ----               -------Warning  FailedScheduling       1m (x3 over 1m)  default-scheduler  0/1 nodes areavailable: 1 node(s) were not ready.Normal   Scheduled              1m               default-scheduler  Successfully assigned kubernetes-bootcamp-5c69669756-btznb to minikubeNormal   SuccessfulMountVolume  1m               kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-86srb"Normal   Pulled                 1m               kubelet, minikube  Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machineNormal   Created                1m               kubelet, minikube  Created containerNormal   Started                1m               kubelet, minikube  Started containerName:           kubernetes-bootcamp-5c69669756-dfjzr
Namespace:      default
Node:           minikube/172.17.0.33
Start Time:     Thu, 03 May 2018 14:39:50 +0000
Labels:         pod-template-hash=1725225312run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.5
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:kubernetes-bootcamp:Container ID:   docker://822193c72cdd69142f355c1e514b9a63246c9b29b34e8c3424d74e422dbc2461Image:          gcr.io/google-samples/kubernetes-bootcamp:v1Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64afPort:           8080/TCPHost Port:      0/TCPState:          RunningStarted:      Thu, 03 May 2018 14:39:52 +0000Ready:          TrueRestart Count:  0Environment:    <none>Mounts:/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:Type           StatusInitialized    TrueReady          TruePodScheduled   True
Volumes:default-token-86srb:Type:        Secret (a volume populated by a Secret)SecretName:  default-token-86srbOptional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s
Events:Type     Reason                 Age              From               Message----     ------                 ----             ----               -------Warning  FailedScheduling       1m (x3 over 1m)  default-scheduler  0/1 nodes areavailable: 1 node(s) were not ready.Normal   Scheduled              1m               default-scheduler  Successfully assigned kubernetes-bootcamp-5c69669756-dfjzr to minikubeNormal   SuccessfulMountVolume  1m               kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-86srb"Normal   Pulled                 1m               kubelet, minikube  Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machineNormal   Created                1m               kubelet, minikube  Created containerNormal   Started                1m               kubelet, minikube  Started containerName:           kubernetes-bootcamp-5c69669756-kl225
Namespace:      default
Node:           minikube/172.17.0.33
Start Time:     Thu, 03 May 2018 14:39:50 +0000
Labels:         pod-template-hash=1725225312run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.4
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:kubernetes-bootcamp:Container ID:   docker://60f8bd1b989b701a5a51371db665886aba700839e9cf21651bc8385781f13b34Image:          gcr.io/google-samples/kubernetes-bootcamp:v1Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64afPort:           8080/TCPHost Port:      0/TCPState:          RunningStarted:      Thu, 03 May 2018 14:39:52 +0000Ready:          TrueRestart Count:  0Environment:    <none>Mounts:/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:Type           StatusInitialized    TrueReady          TruePodScheduled   True
Volumes:default-token-86srb:Type:        Secret (a volume populated by a Secret)SecretName:  default-token-86srbOptional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s
Events:Type     Reason                 Age              From               Message----     ------                 ----             ----               -------Warning  FailedScheduling       1m (x3 over 1m)  default-scheduler  0/1 nodes areavailable: 1 node(s) were not ready.Normal   Scheduled              1m               default-scheduler  Successfully assigned kubernetes-bootcamp-5c69669756-kl225 to minikubeNormal   SuccessfulMountVolume  1m               kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-86srb"Normal   Pulled                 1m               kubelet, minikube  Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machineNormal   Created                1m               kubelet, minikube  Created containerNormal   Started                1m               kubelet, minikube  Started container

执行 set image 命令,并指定 Deployment 名称及新镜像,升级应用程序的镜像到版本 2。

$ kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2
deployment.apps "kubernetes-bootcamp" image updated

命令通知 Deployment 为应用程序使用不同的镜像,并初始化滚动更新。检查新 Pod 的状态:

kubectl get pods

Step 2: 验证升级

首先,检查应用程序是否在运行。通过 describe service 命令找出暴露的 IP 地址和端口号:

kubectl describe services/kubernetes-bootcamp

创建名为 NODE_PORT 的环境变量,保存分配到的节点端口号:

export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')
echo NODE_PORT=$NODE_PORT

然后,通过 curl 访问暴露的 IP 地址和端口号:

curl $(minikube ip):$NODE_PORT

每次请求命中不同的 Pod,且所有 Pod 都运行最新版本(v2)。

更新也可以通过 rollout status 命令来确认:

kubectl rollout status deployments/kubernetes-bootcamp

通过在 Pod 上执行 describe 命令来查看应用程序当前运行的镜像的版本号:

kubectl describe pods

查看 Image 字段,现在运行的是应用程序的版本 2。

Step 3: 回滚更新

现在执行另外一个更新,部署标记为 v10 的镜像:

kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v10

查看 Deployment 的状态:

kubectl get deployments

出错了。。。没有可用的 Pod 数量。查看所有的 Pod:

kubectl get pods

针对 Pod 的 describe 命令可以提供更多信息:

kubectl describe pods

仓库中没有名为 v10 的镜像。现在要回到之前的可以正常工作的版本。使用 rollout undo 命令:

kubectl rollout undo deployments/kubernetes-bootcamp

rollout 命令将 Deployment 恢复到之前已知的状态(v2 版本的镜像)。更新是版本化的,你可以恢复到任何之前知道的 Deployment 状态。再次列出 Pods:

kubectl get pods

4 个 Pod 正在运行。再次检查部署到这些 Pod 上的镜像:

kubectl describe pods

可以看到 Deployment 使用的是稳定版本的应用程序(v2)。回滚成功了。

Kubernetes tutorial - K8S 官方入门教程 中文翻译相关推荐

  1. Kubernetes tutorial - K8S 官方入门教程

    tutorials 教程 kubectl 的命令手册 1 Creating a Cluster 1.1 Using Minikube to Create a Cluster Kubernetes Cl ...

  2. Fragstats官方入门教程3 批处理多个栅格

    原著:Kevin McGarigal 翻译:地理时政志(公众号.CSDN同名,知乎:Jarviski) 在教程3中,将会教读者如何使用Fragstats完成批处理多个栅格,以及其他要注意的问题 以下原 ...

  3. AFNnetworking快速教程,官方入门教程译

    AFNnetworking快速教程,官方入门教程译 分类: IOS2013-12-15 20:29 12489人阅读 评论(5) 收藏 举报 afnetworkingjsonios入门教程快速教程 A ...

  4. 用TypeScript来写React官方入门教程 .tsx后缀文件,同时入门typescript和React

    用TypeScript来写React官方入门教程 .tsx后缀文件,同时入门typescript和React 1. 项目说明: 这是React官网上那个下井字棋的入门教程,但是我把它换了typesci ...

  5. 抱抱脸(hugging face)教程-中文翻译-模型概要

    模型概要 这是一个总结的模型可在Transformers.假设您熟悉最初的Transformers模型.或者温柔的介绍,看看有注释的Transformers.在我们关注模特之间的高度差异之前.你可以在 ...

  6. Unity游戏开发官方入门教程:飞机大战(六)——创建子弹

    Unity版本:Unity 2018.2.14f1 原视频链接:https://unity3d.com/cn/learn/tutorials/s/space-shooter-tutorial 教程目录 ...

  7. python初级教程 doc_Python 入门教程 中文WORD版

    内容介绍热点排行相关文章下载地址↓ Python是一个高效的语言,读和写的操作都是很简单的,就像普通的英语一样 Python是一个解释执行的语言,我们不需要去编译,我们只要写出代码即可运行 Pytho ...

  8. 第三章 Python Kivy 学习 -- Kivy官方入门教程Pong Game

    系列文章目录 第一章 Python Kivy 学习 – Kivy介绍及环境安装 第二章 Python Kivy 学习 – Kivy项目开发原理(待编辑) 第三章 Python Kivy 学习 – Ki ...

  9. 抱抱脸(hugging face)教程-中文翻译-分享一个模型

    分享一个模型 最后两个教程展示了如何使用 PyTorch. Keras 和 Accelerate 优化分布式设置的模型.下一步就是把你的模型公之于众!我们相信公开分享知识和资源,使人工智能大众化.我们 ...

  10. 抱抱脸(hugging face)教程-中文翻译-预处理

    预处理 在您可以在模型中使用数据之前,需要将数据处理为模型可接受的格式.模型不理解原始文本.图像或音频.这些输入需要转换成数字并组装成张量.在本教程中,您将: 用tokenizer处理文本. 用特征提 ...

最新文章

  1. GlusterFS下如何修复裂脑文件?(续一)
  2. oracle 监控 视图,【转】oracle几个常用的监控视图
  3. 解读WPF中的Xaml
  4. Day13-日历模块
  5. 前端接入HTTP协议浅析
  6. 第七节:在 TypeScript 中什么是类型推论?
  7. angularjs中按回车事件_在AutoCAD中巧用空格键或回车键,制图效率高
  8. vue(一)vue-cli安装
  9. 的ppt_PPT丨清新淡雅年终总结PPT模板
  10. 谷歌“Adobe Flash Player已被屏蔽”的解决办法
  11. 做网赚赚不到钱,你的方法用对了么?
  12. Win11删除英文输入法的方法教程
  13. Vue 项目使用 又拍云 云存储服务
  14. 首届广西网络安全技术大赛初赛通关攻略
  15. 产品经理必备文章50篇合集:从入门到提升
  16. nodejs十六进制转字符串
  17. 二次曲面的绘制函数(一)
  18. Ubuntu中使用vi编辑时方向键乱码或退格键不能使用解决方法
  19. 计算机网络安全的对策有哪些,计算机网络安全措施有哪些
  20. 青龙面板--3个小毛

热门文章

  1. linux如何安装usb转串口驱动,虚拟机下Ubuntu安装usb转串口驱动
  2. java版本导致ASDM连不上ASA
  3. 视频教程-QT/C++从新手到老手系列之QT基础篇-其他
  4. 三次Hermite插值解析
  5. 等级保护第三级安全通用要求笔记(2019)
  6. goeasy服务器发送(发布)消息,微信小程序中怎么使用GoEasy实现实时通讯
  7. python爬虫音乐犯法么_Python爬虫实战之爬取QQ音乐数据!QQ音乐限制太多了
  8. 在3dmax中打开文件时,显示路径无效,如何处理?
  9. cnpm 安装文件找不到_技术员修复 win7系统word2013找不到标尺工具的处理办法 -win7系统使用教程...
  10. VLC播放电视直播rtmp流地址