Kubernetes 学习笔记

主要来源于官网的教程,从基础入手,循序渐进,还有在线实验,非常友好


更新历史

  • 2017.06.05: 开始学习

Kubernetes 基础

先来看一段来自这里的简介(我更新了一下链接):

Kubernetes 是 Google 开源的容器集群管理系统,其提供应用部署、维护、扩展机制等功能,利用Kubernetes 能方便地管理跨机器运行容器化的应用。Kubernetes 可以在物理机或虚拟机上运行,且支持部署到 AWS,Azure,GCE 等多种公有云环境。介绍分布式训练之前,需要对 Kubernetes 有一个基本的认识,下面先简要介绍一下本文用到的几个 Kubernetes 概念。

  • Node 表示一个 Kubernetes 集群中的一个工作节点,这个节点可以是物理机或者虚拟机,Kubernetes 集群就是由 node 节点与 master 节点组成的。
  • Pod 是一组(一个或多个)容器,pod 是 Kubernetes 的最小调度单元,一个 pod 中的所有容器会被调度到同一个 node 上。Pod 中的容器共享 NET,PID,IPC,UTS 等 Linux namespace。由于容器之间共享 NET namespace,所以它们使用同一个 IP 地址,可以通过 localhost 互相通信。不同 pod 之间可以通过IP地址访问。
  • Job 描述 Kubernetes 上运行的作业,一次作业称为一个 job,通常每个 job 包括一个或者多个pods,job 启动后会创建这些 pod 并开始执行一个程序,等待这个程序执行成功并返回 0 则成功退出,如果执行失败,也可以配置不同的重试机制。
  • Volume 存储卷,是 pod 内的容器都可以访问的共享目录,也是容器与 node 之间共享文件的方式,因为容器内的文件都是暂时存在的,当容器因为各种原因被销毁时,其内部的文件也会随之消失。通过 volume,就可以将这些文件持久化存储。Kubernetes 支持多种 volume,例如 hostPath(宿主机目录),gcePersistentDisk,awsElasticBlockStore等。
  • Namespaces 命名空间,在 kubernetes 中创建的所有资源对象(例如上文的 pod,job)等都属于一个命名空间,在同一个命名空间中,资源对象的名字是唯一的,不同空间的资源名可以重复,命名空间主要为了对象进行逻辑上的分组便于管理。本文只使用了默认命名空间。
  • PersistentVolume: 和 PersistentVolumeClaim 结合,将外部的存储服务在 Kubernetes 中描述成为统一的资源形式,便于存储资源管理和 Pod 引用。

如果看不懂,也没有关系,接下来会更详细介绍。PS. 官方文档很有灵性,建议有英文阅读能力的同学去通读一遍,这里只是我的一些学习笔记,不如原版这么有逻辑性。

Kubernetes 集群让多台机器像一个单一组件一样运行,但是有一个前提条件,就是应用需要被打包到容器里。打包好之后 Kubernetes 会自动化地以一种高效的形式去调度容器们。一个 Kubernetes 集群有两种类型的资源:

  • Master 负责调度集群,维护应用状态,扩展应用和滚动更新
  • Nodes 负责运行各个应用。每个节点有一个 Kubelet,是在每个节点上的客户端,负责与 master 交流。一个 Kubernetes 集群至少需要 3 个 node。
    • 如果想要体验一下,可以使用 Minikube,会在单机上创建虚拟机来搭建集群(本文的教程也是用这个来展示的)

创建集群

命令如下

# 查看版本
$> minikube version
minikube version: v0.15.0-katacoda
# 启动集群
$> minikube start
Starting local Kubernetes cluster...
# 查看版本
$> kubectl version
Client Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.2", GitCommit:"08e09955
4f3c31f6e6f07b448ab3ed78d0520507", GitTreeState:"clean", BuildDate:"2017-01-12T04:57:25Z",
GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.2", GitCommit:"08e09955
4f3c31f6e6f07b448ab3ed78d0520507", GitTreeState:"clean", BuildDate:"1970-01-01T00:00:00Z",
GoVersion:"go1.7.1", Compiler:"gc", Platform:"linux/amd64"}
# 查看集群状态
$> kubectl cluster-info
Kubernetes master is running at http://host01:8080
heapster is running at http://host01:8080/api/v1/proxy/namespaces/kube-system/services/heap
ster
kubernetes-dashboard is running at http://host01:8080/api/v1/proxy/namespaces/kube-system/s
ervices/kubernetes-dashboard
monitoring-grafana is running at http://host01:8080/api/v1/proxy/namespaces/kube-system/ser
vices/monitoring-grafana
monitoring-influxdb is running at http://host01:8080/api/v1/proxy/namespaces/kube-system/se
rvices/monitoring-influxdb
# 获取可用的 node
$> kubectl get nodes
NAME STATUS AGE
host01 Ready 15m

部署应用

Kubernetes 集群启动之后,就可以在这之上部署应用了。部署应用的时候 master 会进行调度并选择合适的 node,启动之后仍旧会继续监控,一旦出问题,就会自动重新启用。

我们可以使用 Kubernetes 命令行工具 Kubectl 通过 Kubernetes API 来管理部署。创建部署的时候需要指定镜像和要运行的副本个数(当然也可以后面更新),然后们就实际来部署一波,命令如下:

# 部署应用
$> kubectl run kubernetes-bootcamp --image=docker.io/jocatalin/kubernetes-bootcamp:v1 --port=8080
mp:v1 --port=8080ubernetes-bootcamp --image=docker.io/jocatalin/kubernetes-bootca
deployment "kubernetes-bootcamp" created
# 列出部署
$> kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1 1 1 1 1m
# 查看部署
# 先通过 proxy 连接到正在运行的容器
$> kubectl proxy
Starting to serve on 127.0.0.1:8001
# 然后打开一个新 tab,输出 Pod 名字
$> export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$> echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-390780338-n7q3n
# 获取这个 pod 的输出
$> curl http://localhost:8001/api/v1/proxy/namespaces/default/pods/$POD_NAME/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-390780338-n7q3n | v=1

查看应用

每个应用会在一个 Pod 中运行,一个 Pod 里可以有一个或多个应用(相当于给这些应用创建了一个共有的 localhost 环境,每个 pod 里的网络等环境是共享的)。

每个 Pod 都会运行在一个 Node 上,每个 Node 都由 Master 来管理。其中,每个 Node 都必须要有:

  • Kubelet,负责与 Master 通讯,管理 node 上运行的 pods 和 containers
  • 一个容器的 runtime,比如 docker,用来从 registry 拉取镜像,解压与运行应用

常用的 kubectl 命令有

  • kubectl get 列出所有的资源
  • kubectl describe 显示资源的详细信息
  • kubectl logs 输出一个 pod 中一个 container 的日志
  • kubectl exec 在一个 pod 中的一个 container 中执行命令

例子如下:

# 获取 pod 信息
$> kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-390780338-6b9n3 1/1 Running 0 41m
# 查看 pods 详细信息
# 包括 镜像、IP 等各类信息,describe 不仅可以用于 pods,node 和 deployment 都可以
$> kubectl describe pods
Name: kubernetes-bootcamp-390780338-6b9n3
Namespace: default
Node: host01/172.17.0.41
Start Time: Mon, 05 Jun 2017 03:05:07 +0000
Labels: pod-template-hash=390780338
run=kubernetes-bootcamp
Status: Running
IP: 172.18.0.2
Controllers: ReplicaSet/kubernetes-bootcamp-390780338
Containers:
kubernetes-bootcamp:
Container ID: docker://9308ecb7bb592255b5fb517ec7caa13703f7b5bb41a24145ba1eeb670693a60d
Image: docker.io/jocatalin/kubernetes-bootcamp:v1
Image ID: docker-pullable://jocatalin/kubernetes-bootcamp@sha256:0d6b8ee63bb5
7c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
Port: 8080/TCP
State: Running
Started: Mon, 05 Jun 2017 03:05:08 +0000
Ready: True
Restart Count: 0
Volume Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-74qlr (ro)
Environment Variables: <none>
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-74qlr:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-74qlr
QoS Class: BestEffort
Tolerations: <none>
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
41m 41m 1 {default-scheduler } Normal Scheduled Successfully assigned kubernetes-bootcamp-390780338-6b9n3 to host01
41m 41m 1 {kubelet host01} spec.containers{kubernetes-bootcamp} Normal Pulled Container image "docker.io/jocatalin/kubernetes-bootcamp:v1" already present on machine
41m 41m 1 {kubelet host01} spec.containers{kubernetes-bootcamp} Normal Created Created container with docker id 9308ecb7bb59; Security:[seccomp=unconfined]
41m 41m 1 {kubelet host01} spec.containers{kubernetes-bootcamp} Normal Started Started container with docker id 9308ecb7bb
59
# 获取 pod 信息
$> export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$> echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-390780338-6b9n3
# 查看 pod 日志
$> kubectl logs $POD_NAME
Kubernetes Bootcamp App Started At: 2017-06-05T03:05:08.494Z | Running On: kubernetes-bootcamp-390780338-6b9n3

使用服务暴露应用

Pods 是有生命周期的,也有独立的 IP 地址,随着 Pods 的创建与销毁,一个必不可少的工作就是保证各个应用能够感知这种变化。这就要提到 Service 了,Service 是 YAML 或 JSON 定义的由 Pods 通过某种策略的逻辑组合。更重要的是,Pods 的独立 IP 需要通过 Service 暴露到网络中,有以下几种方式:

  • ClusterIP(默认):只在集群内部可见的地址
  • NodePort:可在集群外访问,需要指定端口
  • LoadBalancer:创建一个负载均衡器,IP 是固定的
  • ExternalName:使用任意的名字暴露服务

依然是通过实例来感受一下

# 查看 pods 信息
$> kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-390780338-76skz 1/1 Running 0 6s
# 查看服务信息
$> kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 <none> 443/TCP 47s
# 暴露服务
$> kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
service "kubernetes-bootcamp" exposed

扩展应用

当业务流量暴涨,就需要根据需要扩展应用(多几个 pods),具体的原理比较简单,我们直接来看例子:

TODO

更新应用

滚动更新可以保证 0 停机时间,其实逻辑和前面的扩展差不多,可以认为是用新版本的扩展,也直接来看例子:

TODO

Hello Minikube

一个简单的教程,学习如何把本机的代码转换成 Kubernetes 可以使用的镜像

部署

hosted solution

turnkey cloud solutions

CentOS Version

参考链接

捧个钱场?