官方教程,共 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 Service
run Run a particular image on the cluster
set Set specific features on objects
run-container Run a particular image on the cluster. This command is deprecated, use "run" instead Basic Commands (Intermediate):
get Display one or many resources
explain Documentation of resources
edit Edit a resource on the server
delete Delete resources by filenames, stdin, resources and names, or by resources and label selector Deploy Commands:
rollout Manage the rollout of a resource
rolling-update Perform a rolling update of the given ReplicationController
scale Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job
autoscale Auto-scale a Deployment, ReplicaSet, or ReplicationController Cluster Management Commands:
certificate Modify certificate resources.
cluster-info Display cluster info
top Display Resource (CPU/Memory/Storage) usage.
cordon Mark node as unschedulable
uncordon Mark node as schedulable
drain Drain node in preparation for maintenance
taint Update the taints on one or more nodes Troubleshooting and Debugging Commands:
describe Show details of a specific resource or group of resources
logs Print the logs for a container in a pod
attach Attach to a running container
exec Execute a command in a container
port-forward Forward one or more local ports to a pod
proxy Run a proxy to the Kubernetes API server
cp Copy files and directories to and from containers.
auth Inspect authorization Advanced Commands:
apply Apply a configuration to a resource by filename or stdin
patch Update field(s) of a resource using strategic merge patch
replace Replace a resource by filename or stdin
convert Convert config files between different API versions Settings Commands:
label Update the labels on a resource
annotate Update the annotations on a resource
completion 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 files
help Help about any command
plugin Runs a command-line plugin
version Print the client and server version information Usage:
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-bootcamp
Containers:
kubernetes-bootcamp:
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
Port: 8080/TCP
Environment: <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-bootcamp
Containers:
kubernetes-bootcamp:
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
Port: 8080/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Progressing True NewReplicaSetAvailable
Available 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 1
Normal 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=1725225312
run=kubernetes-bootcamp
Annotations: <none>
Status: Running
IP: 172.18.0.2
Controlled By: ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
kubernetes-bootcamp:
Container ID: docker://bd2acdb49f2d9a2e9f796065372425562818156e8f121c69b11c29c260f2f106
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 03 May 2018 14:39:51 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-86srb:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-86srb
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.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 minikube
Normal 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 machine
Normal Created 1m kubelet, minikube Created container
Normal Started 1m kubelet, minikube Started container Name: 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=1725225312
run=kubernetes-bootcamp
Annotations: <none>
Status: Running
IP: 172.18.0.3
Controlled By: ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
kubernetes-bootcamp:
Container ID: docker://40dfdafb48176b77efaa6eaf3c8ed256feffd15e60743d4da51419eb3d5c9a33
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 03 May 2018 14:39:52 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-86srb:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-86srb
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.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 minikube
Normal 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 machine
Normal Created 1m kubelet, minikube Created container
Normal Started 1m kubelet, minikube Started container Name: 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=1725225312
run=kubernetes-bootcamp
Annotations: <none>
Status: Running
IP: 172.18.0.5
Controlled By: ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
kubernetes-bootcamp:
Container ID: docker://822193c72cdd69142f355c1e514b9a63246c9b29b34e8c3424d74e422dbc2461
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 03 May 2018 14:39:52 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-86srb:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-86srb
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.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 minikube
Normal 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 machine
Normal Created 1m kubelet, minikube Created container
Normal Started 1m kubelet, minikube Started container Name: 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=1725225312
run=kubernetes-bootcamp
Annotations: <none>
Status: Running
IP: 172.18.0.4
Controlled By: ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
kubernetes-bootcamp:
Container ID: docker://60f8bd1b989b701a5a51371db665886aba700839e9cf21651bc8385781f13b34
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
Image ID: docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 03 May 2018 14:39:52 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-86srb (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-86srb:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-86srb
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.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 minikube
Normal 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 machine
Normal Created 1m kubelet, minikube Created container
Normal 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. Superset 官方入门教程中文翻译

    本文翻译自 Superset 的官方文档:Toturial - Creating your first dashboard 最新版本的 Superset 界面与功能上与文档中提到的会有些许出入,以实际 ...

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

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

  4. Go 语言官方包函数中文翻译

    Go官方包函数中文翻译 *** import "strings" func Join(a []string, sep string) string Join concatenate ...

  5. Kubernetes --(k8s)入门

    k8s 简介: 什么是k8s? Kubernetes (k8s)是Google开源的容器集群管理系统(谷歌内部:Borg).在Docker技术基础上,为容器化的应用提供部署运行.资源调度.服务发现和动 ...

  6. 官方入门教程和文档 | Visual Studio

    Visual Studio 2017 概述 | Microsoft Docs(直接教你用vs) https://docs.microsoft.com/zh-cn/visualstudio/ide/vi ...

  7. GitHub官方介绍(中文翻译)

    注:本人亲自翻译,转载请注明出处. 官方链接地址 http://guides.github.com/activities/hello-world/ Hello World 项目在计算机编程界是一项历史 ...

  8. SLAM for Dummies SLAM初学者教程 中文翻译 1到4章

    SLAM for Dummies  SLAM初学者教程A Tutorial Approach to Simultaneous Localization and Mapping  一本关于实时定位及绘图 ...

  9. 【转】QT Graphics-View官方介绍(中文翻译)

    一.GraphicsView框架简介 QT4.2开始引入了Graphics View框架用来取代QT3中的Canvas模块,并作出了改进,Graphics View框架实现了模型-视图结构的图形管理, ...

随机推荐

  1. Maven-Eclipse使用maven创建HelloWorld Java项目

    1.依次选择File-->New-->Other-->Maven-->Maven Project,Next 2.选择maven-archetype-quickstart,Nex ...

  2. 面试题思考:Stack和Heap的区别 栈和堆的区别

    堆栈的概念: 堆栈是两种数据结构.堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除.在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常 ...

  3. [BZOJ 2989]数列(二进制分组+主席树)

    [BZOJ 2989]数列(二进制分组+主席树) 题面 给定一个长度为n的正整数数列a[i]. 定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[ ...

  4. Codeforces 429E(欧拉回路)

    题面 传送门 题目大意: 有n条线段,每条线段染红色或蓝色,使得数轴上每个点被红色线段覆盖的次数与被蓝色线段覆盖数差的绝对值小于等于1.输出染色方案. 分析 题意其实可以这样理解: 一段初始全为0 的 ...

  5. [SP3267]DQUERY - D query

    题目传送门 维护一个区间内不同数的个数,最直观的想法是直接排序后用树状数组维护即可.但是我们发现n只有3e4,于是我们想到了可以拿一个$O(n\sqrt{n})$的莫队维护.关于莫队算法如果有不知道的 ...

  6. 一键生成APK

    傻瓜式的生成APK网址:https://www.appbsl.com/ 第一步 第二步 第三步 第四步

  7. wxpython中复选框的基本使用源码实例

    #coding=utf-8 import wx class MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self,None,-1, ...

  8. Bootstrap真的总是好的吗

    原文地址:Bootstrap considered harmful 原文作者:Hidde de Vries 译文出自:neal 译者: Neal 个人主页:http://neal1991.python ...

  9. 通过busybox制作根文件系统

    通过busybox制作根文件系统可以自定义选项,在制作的根文件系统中添加需要的命令,指定生成的根文件系统到相应的目录下. 一. 根文件系统的获取方式--->官网: https://busybox ...

  10. idea hibernate console 执行hql报错

    报错信息 hql> select a from GDXMZD a[2019-08-29 13:45:01] org.hibernate.service.spi.ServiceException: ...