kubernetes 上手指南:概念篇

大家好,我叫谢伟,是一名程序员。

今天的主题:kubernetes 概念篇,通过一些示例,学习 kubernetes(k8s) 的一些核心概念。

这些知识不可避免的受笔者经验的限制,希望大家辩证的看待

kubernetes 角色是“操作系统”

上一节介绍了容器相关的知识,引申出单节点部署多个容器可以使用 docker-compose。而针对多节点的部署方案,docker-compose 无能为力,转而介绍今天的角色:kubernetes。

kubernetes 底层操作的具体资源是容器(学习者需要优先掌握容器的相关知识)。容器的本质是进程,相当于在操作系统上运行的实例。

具体来讲:k8s: 是一个跨主机集群的开源容器调度平台,可以自动化的应用容器的部署、扩展和操作,提供以容器为中心的基础架构。

先抛开 k8s 涉及的具体的知识,就我们熟知的操作系统,你觉得会包含哪些知识?

  • 计算:资源管理
  • 存储:外部的、内部的存储
  • 网络:应用之间互联
  • 扩展:相当于如何安装各种各样的软件
  • 配置:配置文件
  • 权限:对用户操作资源进行权限认证
  • ...

没错,k8s 提供这些能力。

那么 k8s 如何提供的这些能力?

这得从 k8s 架构说起。

k8s 的架构借鉴了 Google 内部的大规模集群管理系统 Borg,负责对 Google 内部很多核心服务的调度和管理。

k8s

整体上来说: k8s 包含两个部分:Master 节点, Node 节点。各节点完成不同的任务。

就 Master 而言包含,其包含4个部分:

  • APIServer: 内部的 web 服务,资源操作的入口
  • Scheduler: 调度器:负责资源的调度,比如把部署的应用部署在哪个节点上
  • Controller manager 负责集群的状态,比如故障诊断、自动扩展等
  • etcd 保存集群的整个信息

这四个服务在 Master 节点部署完成之后,自动的创建出来:

>> docker ps -a --format "table {{.Image}}\t{{.Names}}"
2c4adeb21b4f            k8s_etcd_etcd-node1_kube-system_119fa7d9bc8bb1c755b9e8f2086d43e6_0
5811259ed0c9            k8s_kube-apiserver_kube-apiserver-node1_kube-system_f92ad4b0d2a33f76b05b6fcbe43e172a_0
07193a77f264            k8s_kube-controller-manager_kube-controller-manager-node1_kube-system_e3f0b6817c856cdb3e54f471dcbddf77_0
0f036524b7a2            k8s_kube-scheduler_kube-scheduler-node1_kube-system_0b5f93df7ddfe3fad5529c9f7f253717_0
k8s.gcr.io/pause:3.1    k8s_POD_kube-scheduler-node1_kube-system_0b5f93df7ddfe3fad5529c9f7f253717_0
k8s.gcr.io/pause:3.1    k8s_POD_kube-controller-manager-node1_kube-system_e3f0b6817c856cdb3e54f471dcbddf77_0
k8s.gcr.io/pause:3.1    k8s_POD_kube-apiserver-node1_kube-system_f92ad4b0d2a33f76b05b6fcbe43e172a_0
k8s.gcr.io/pause:3.1    k8s_POD_etcd-node1_kube-system_119fa7d9bc8bb1c755b9e8f2086d43e6_0

其中每个服务起来,看上去都有个奇怪的容器:pause ? 后面介绍

就 Node 节点而言,主要包含:

  • kubelet : 节点代理,维护容器的生命周期(整个操作过程中,我们都不太会显式的操作 kubelet)
  • kube_proxy: 转发代理,负责服务的发现和负载均衡
  • docker: 容器

凭借着 k8s 的开放能力,社区存在很多的插件,完善 k8s 这个“操作系统”的能力,比如有聚焦在网络层面,有聚焦在存储层面的。

我们的演示都聚焦在 k8s 的原生服务的能力基础之上,不使用其他插件

集群部署

之前说过: k8s 的部署由于各种各样的原因,对于初学者来说,整个的部署比较困难,比如对硬件的要求,对网络的要求(拉取基础镜像)。

如果你想尝试试使用 k8s, 可以使用下面这个玩具:

Play With k8s

1. master 节点

kubeadm init --apiserver-advertise-address $(hostname -i)

执行结束,可以看到如何添加其他节点的命令

2. 网络初始化

kubectl apply -n kube-system -f  "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

3. 获取配置文件

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

甚至你可以将配置文件拷贝至本地,在本地使用 kubectl 操作集群资源

安装部署依赖这几个软件:(不过这个玩具都内置了)

  • kubeadm: 一键式的安装部署集群的工具,社区推荐
  • kubelet: 维护容器生命周期
  • docker: 操作容器
  • kubectl: 操作集群的命令行工具

后续主要围绕:kubectl 工具的使用,操作集群

安装部署后其中有几个目录值得我们关注下:

// master 节点
>> ls /etc/kubernetes

admin.conf  controller-manager.conf  kubelet.conf  manifests  pki  scheduler.conf

>> ls manifests
etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml

>> ls pki
apiserver-etcd-client.crt
apiserver-etcd-client.key
apiserver-kubelet-client.crt
apiserver-kubelet-client.key
apiserver.crt
apiserver.key
ca.crt
ca.key
etcd
front-proxy-ca.crt
front-proxy-ca.key
front-proxy-client.crt
front-proxy-client.key
sa.key
sa.pub

主要是自动生成的配置文件,包括组件:apiserver, etcd, controllermanager, scheduler 的配置文件,和一些密钥认证信息。

事实上 k8s 安装组件,是使用配置文件的形式,一般选择 yaml 的形式(虽然也支持 json 形式,但 yaml 的表达能力更佳,推荐使用)。

查看节点信息:

包含两个节点,其中一个角色是 master 节点,另一个是普通 node 节点。

>> kubectl get nodes
NAME    STATUS   ROLES    AGE   VERSION
node1   Ready    <none>   15m   v1.14.9
node2   Ready    master   16m   v1.14.9

如何部署其他组件

一般是通过执行 kubectl 命令:

>> kubectl apply -f 1namespace.yml

作为开发者,在集群部署完成之后,想要部署自己的服务,要点在于编写配置文件,那么配置文件一般都是什么形式的?

apiVersion: v1
kind: Namespace
metadata:
  name: k8s-example
  labels:
    app: k8s-example
    name: k8s-example
    project: k8s-example

各字段表示如下:

  • apiVersion: 版本信息,比如上文 v1
  • kind: 表示资源类型,比如上文 Namespace
  • metadata: 元信息,一般来定义资源的名称等

根据资源类型的不同,还给不同的资源定义了特殊字段。

整体的 yaml 文件包含四个部分:

apiVersion: 表示版本
kind: 表示资源
metadata: 表示元信息
spec: 资源规范字段

那么如何知道部署的 k8s 支持哪些资源和版本?这些东西又是哪里来的?

// 查看所有支持的资源
>> kubectl api-resources

// 查看所有支持的版本
>> kubectl api-versions

还记得 master 节点有个 api-server 吗?是的,定义的 yaml 最终转换成了相应的对象完成资源的增删改查,这就是 api-server 主要的作用,也是 k8s 操作资源的入口。

>> kubectl get namespace
NAME           STATUS   AGE
default        Active   264d
k8s-example    Active   7s
kube-public    Active   264d
kube-system    Active   264d
production     Active   263d

除自身刚刚创建的 namespace 之外,集群搭建过程中自动的生成默认的 namespace。

namespace 是用来进行隔离的,是一个逻辑概念,真实的集群环境中,可以根据使用场景创建不同的 namespace,用来进行隔离。

当然除命令行之外,也可以通过访问 API 路由的形式操作资源。

>> kubectl proxy
Starting to serve on 127.0.0.1:8001

>> curl http://127.0.0.1:8001/api/v1/namespaces/k8s-example | jq .

{
  "kind": "Namespace",
  "apiVersion": "v1",
  "metadata": {
    "name": "k8s-example",
    "selfLink": "/api/v1/namespaces/k8s-example",
    "uid": "d7f3d7d3-1907-11ea-9746-fa163ed7ee23",
    "resourceVersion": "57568285",
    "creationTimestamp": "2019-12-07T15:40:06Z",
    "labels": {
      "project": "k8s-example"
    },
    "annotations": {
      "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"project\":\"k8s-example\"},\"name\":\"k8s-example\"}}\n"
    }
  },
  "spec": {
    "finalizers": [
      "kubernetes"
    ]
  },
  "status": {
    "phase": "Active"
  }
}

这个访问到的比我们之前定义的更详细,k8s 会自动添加一些辅助信息。

简单的说:集群搭建结束,开发者需要编写对应服务的 yaml 配置文件,这些配置文件区分不同的对象有不同的规范要求。

具体不同资源的规范有哪些?读者一方面可以熟悉 k8s 的相关概念了解相应的规范。也可以阅读源代码从源头明确规范的字段。

// Use of multiple namespaces is optional.
type Namespace struct {
    metav1.TypeMeta `json:",inline"`
    // Standard object's metadata.
    // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
    // +optional
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // Spec defines the behavior of the Namespace.
    // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
    // +optional
    Spec NamespaceSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`

    // Status describes the current status of a Namespace.
    // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
    // +optional
    Status NamespaceStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

其中各资源规范字段即 Spec

kubernetes源码: https://github.com/kubernetes/kubernetes

那么如何更新资源?

// 修改 yaml 文件

>> kubectl apply -f 1namespace.yml

那么如何删除资源?

>> kubectl delete -f 1namespace.yml

kubectl 命令行还支持各种参数进行资源的操作,但我仍然建议:显式的定义资源,再进行资源的操作。好处是:1. yaml 的表达能力,能知道具体的执行内容是什么 2. 配置文件可管理

相关概念

比如想部署个 nginx 服务。

我们已经知道需要编写 yaml 文件,再使用 kubectl 部署。

# 2nginxpod.yml

apiVersion: v1
kind: Pod
metadata:
  name: k8s-example-pod
  namespace: k8s-example
  labels:
    app: k8s-example-pod
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80

  • 显式的约定了 namespace: k8s-example,即会部署在 k8s-example 隔离环境下,不影响已有服务
  • spec 约定了部分容器的相关操作,比如镜像,端口

查看服务部署情况:

>> kubectl get pods --namespace k8s-example
NAME              READY   STATUS    RESTARTS   AGE
k8s-example-pod   1/1     Running   0          3m

查询指令中,显式的指定了 namespace, 否则查询默认(default)的 namespace

再已有的知识中,加入 nginx 按照容器的方案部署,我们应该可以在本地访问到其默认主页。

那在 k8s 中如何访问呢?

答案:搭配 service

# 3service.yml
apiVersion: v1
kind: Service
metadata:
  namespace: k8s-example
  name: k8s-example-nginx-service
  labels:
    app: k8s-example-nginx-service
spec:
  selector:
    app: k8s-example-pod
  ports:
    - port: 80
      targetPort: 80
  type: NodePort

  • 显式的指定 namespace: k8s-example
  • 指定了选择器: app: k8s-example-pod
  • 约定了端口映射关系:80:80
  • 约定了类型:NodePort

回归头去看 2nginxpod.yml 配置文件中,定义了元信息:app: k8s-example-pod , 定义了容器端口:80。

所以 service 的角色是和 POD 进行绑定关系,POD 本身不提供对外访问的能力,需要借助 service 进行绑定,再对外服务。

>> kubectl get service --namespace k8s-example
NAME                        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-example-nginx-service   NodePort   10.247.105.43   <none>        80:32511/TCP   1m

master 或者 node 节点:访问:

>> curl  http://10.247.105.43:80

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

这种方式其实是集群分配给 cluserIP, 通过 clusterIP 和对应的端口,可以访问到服务。

那么 NodePort 什么意思?意思是可以使用 该节点的 IP 和对应的端口。

演示的这个节点绑定的对外IP: 119.3.198.221
curl http://119.3.198.221:32511/
也可以访问到服务

概念整理:

  • 什么是 pod ?
  • pod 如何使其被外部访问到?

POD

2nginxpod.yml 文件中可以了解到 POD 资源的定义主要围绕着 容器进行。没错,POD 表示容器组(包含一个或者多个容器),一般把相关的容器定义在一个 POD 内,这样的组合关系,使其共同服务于集群。

POD 是 k8s 调度的最基本的单位。

还记得 docker-compose.yml 是如何编写的吗?

version: 版本
services: 具体服务
volumes: 数据卷
networks: 网络

一般我都会显式的定义 volumes, networks 这样定义的所有的服务都共享 volume 和 network。 POD 就是对标的这种思想。使其组合的形式对外服务,各容器之间共享网络,数据卷等。

还记得之前集群部署有个奇怪的 pause 容器吗?

这是个永远暂停的容器,体积非常小,它的作用就是将 POD 内的容器关联起来。

那么如何记住 POD 内字段的定义?记住 POD 是容器组,那么关于容器的字段基本都会出现。

比如:

  • container
  • image
  • volume
  • port
  • ...

service

开发者编写的服务,如果需要提供对外访问能力,需要 service 进行绑定,这种绑定关系是自动,具体的如何绑定主要根据的 selector 选择器,选择器内定义的字段,在 某个 POD 内出现完全吻合的,那么进行绑定。否则处于监听状态,等待符合的对象。

为什么不直接让 POD 可以直接访问?访问需要绑定 ip 和 端口吧?但是 POD 在集群内是可以随时删除、升级、回滚的。鉴于此不直接提供访问能力,转而通过 service 进行绑定。

总结:

  • POD 是容器组,开发者在会制造镜像的基础上定义自己 POD 的配置文件
  • Service 和 POD 进行绑定,包括访问类型和端口映射关系

控制器

ReplicaSet

集群提供高可用服务的一个重要手段是部署多个相同的服务,应用负载均衡的能力,使其对外服务。意思是,一般开发者都不会单独的定义一个 POD,也很少单独写资源类型是 POD 的配置文件。转而是另一个概念:ReplicaSet (副本)

很明显,副本的意思是多个, ReplicaSet 这种资源对象简单的说是管理 POD 的,比如指定 2 副本,那么管理的 POD , 会有2个一摸一样的。

# 4replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  namespace: k8s-example
  labels:
    app: k8s-example-replicaset
    name: k8s-example-replicaset
  name: k8s-example-replicaset
spec:
  selector:
    matchLabels:
      app: k8s-example-nginx-pod
  replicas: 2
  template:
    metadata:
      labels:
        app: k8s-example-nginx-pod
    spec:
      containers:
        - name: k8s-example-nginx-pod
          image: nginx
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

  • 显式的指定 namespace: k8s-example
  • replicat: 2 表示相应的服务有两套
  • selector: 选择器,将符合条件的 POD 纳入麾下
  • template: 没错 POD 哪些 spec 字段都成为了模版内的内容

同样是 nginx 容器。定义的元信息:app: k8s-example-nginx-pod 和之前的不一样,意味着之前定义的 service 没法和现在的服务进行绑定。那么怎么办?

新增一个 service:

# 3service.yml
apiVersion: v1
kind: Service
metadata:
  namespace: k8s-example
  name: k8s-example-nginx-service
  labels:
    app: k8s-example-nginx-service
spec:
  selector:
    app: k8s-example-pod
  ports:
    - port: 80
      targetPort: 80
  type: NodePort
---
apiVersion: v1
kind: Service
metadata:
  namespace: k8s-example
  name: k8s-example-replicaset-service
  labels:
    app: k8s-example-replicaset-service
spec:
  selector:
    app: k8s-example-nginx-pod
  type: NodePort
  ports:
    - port: 80
      nodePort: 31234
      targetPort: 80

  • 多个配置文件可以写在同一个文件内,使用 --- 分割开
  • 显示的指定了 nodeport: 31234

默认的会自动的分配:30000-32767 之间的任意一个端口

查看 replicaset:

>> kubectl get replicaset --namespace k8s-example
NAME                     DESIRED   CURRENT   READY   AGE
k8s-example-replicaset   2         2         2       11m

# 也可以查看 pod 

>> kubectl get pods --namespace k8s-example
NAME                           READY   STATUS    RESTARTS   AGE
k8s-example-pod                1/1     Running   0          1h
k8s-example-replicaset-b72k4   1/1     Running   0          9m
k8s-example-replicaset-jd7g6   1/1     Running   0          12m
  • 预期 2个,当前2个,服务中2个,使用时长 11min。

  • 副本两个,为区分开来,自动的在名称后加上了随机字符。

查看 service:

>> kubectl get service --namespace k8s-example
NAME                             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-example-nginx-service        NodePort   10.247.105.43   <none>        80:32511/TCP   1h
k8s-example-replicaset-service   NodePort   10.247.134.39   <none>        80:31234/TCP   14m
>> curl http://119.3.198.221:31234/
>> curl http://49.4.54.222:31234/

可以看到都对外提供服务,有人问 119.3.198.22149.4.54.222 是哪里的 IP, 嚯,忘记说了,我这个集群有三个节点:1个 master (不部署除默认之外的组件), 2个 node, 上面的两个 IP 是这两个节点对外的 ip。且均匀的分布在节点上,每个节点一个(当然有规则可以使其只部署在一个节点上,甚至都不部署在我这个两个节点上,直接失败)。

现在明白 type: NodePort 的意思吧?

没错,ReplicaSet 的资源对象比 POD 上一级,集群始终根据副本的个数在调控着,比如,你删掉一个,立马给你启动一个,比如你新增一个,立马给你删除一个等。

Deployment

Deployment 称作无状态工作负载,适合在生产环境中使用。其具备 ReplicaSet 的所有能力,且支持事件和状态查看、回滚、版本记录等能力。

看各种文档,貌似都在弱化 ReplicaSet 的概念,转而直接介绍 Deployment

同样部署 nginx,资源类型是 Deployment 的配置文件如何编写?

# 5deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-example-deployment
  labels:
    app: k8s-example-deployment
    name: k8s-example-deployment
  namespace: k8s-example
spec:
  selector:
    matchLabels:
      app: k8s-example-nginx-deployment-pod
  replicas: 2
  template:
    metadata:
      labels:
        app: k8s-example-nginx-deployment-pod
    spec:
      containers:
        - name: k8s-example-nginx-deployment-pod
          imagePullPolicy: IfNotPresent
          image: nginx
          ports:
            - containerPort: 80


除资源类型不同之外,几乎和 ReplicaSet 配置一致。

查看部署情况:

>> kubectl get deployment --namespace k8s-example
NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
k8s-example-deployment   2         2         2            2           4s

比 ReplicaSet 多了个字段 UP-TO-DATE.

>> kubectl describe deployment k8s-example-deployment --namespace k8s-example

Name:                   k8s-example-deployment
Namespace:              k8s-example
CreationTimestamp:      Sun, 08 Dec 2019 10:06:32 +0800
Labels:                 app=k8s-example-deployment
                        name=k8s-example-deployment
Annotations:            deployment.kubernetes.io/revision: 1
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"k8s-example-deployment","name":"k8s-example-depl...
Selector:               app=k8s-example-nginx-deployment-pod
Replicas:               2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=k8s-example-nginx-deployment-pod
  Containers:
   k8s-example-nginx-deployment-pod:
    Image:        nginx
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   k8s-example-deployment-6fb599d4b8 (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  99s   deployment-controller  Scaled up replica set k8s-example-deployment-6fb599d4b8 to 2

这种查看详情的命令主要用来排除问题,查看的字段是 Events, 如何有问题,会显示出来,比如是拉取镜像出问题,还是调度出问题等。

deployment 管理的 POD 如何提供对外服务能力,是的,搭配 service.

# 3service.yml
apiVersion: v1
kind: Service
metadata:
  name: k8s-example-deployment-service
  labels:
    app: k8s-example-deployment-service
  namespace: k8s-example
spec:
  selector:
    app: k8s-example-nginx-deployment-pod
  clusterIP: None
  ports:
    - port: 80
      targetPort: 80

这时我们设置的 clusterIP: None , 表示服务以 Headless Service 的形式对外访问,实例间通过服务发现访问。

查看下 service:

>> kubectl get service --namespace k8s-example

NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
k8s-example-deployment-service   ClusterIP   None             <none>        80/TCP         2m
k8s-example-nginx-service        NodePort    10.247.69.248    <none>        80:31519/TCP   2m
k8s-example-replicaset-service   NodePort    10.247.252.231   <none>        80:31234/TCP   2m

可以看到 k8s-example-deployment-service 没有显式的提供内部集群IP,那怎么访问到被管理的 POD?

k8s-example-deployment-service.k8s-example.svc.cluster.local
<service-name>.<namespace>.svc.cluster.local

意思是如果你在 service 中定义了 clusterIP: None, 那么该服务的访问的 DNS 地址为:<service-name>.<namespace>.svc.cluster.local,提供内部服务访问。

具体服务是选择何种形式的访问,需要读者根据自己实际的需求进行设置。

配置文件:configmap, sercret

编写程序的过程中,不可避免的会使用到配置文件,自己的程序中一般有两种方式:

  • 本地配置文件
  • 环境变量

同样为说明用法:以部署 mysql 为例。

  • 定义配置文件
# 6setting.yml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: k8s-example
  name: k8s-example-configmap
  labels:
    app: k8s-example-configmap
    name: k8s-example-configmap
data:
  mysql.Database: "root"
  mysql.User: "k8s-example"

---
apiVersion: v1
kind: Secret
metadata:
  name: k8s-example-secret
  labels:
    app: k8s-example-secret
  namespace: k8s-example
data:
  mysql.Port: "MzMwNg=="
  mysql.Password: "cm9vdA=="


两种资源:configMap 、secret 的配置字段几乎一致,区别在于:secret 中的值都需要 bas64转
码。

>> kubectl apply -f 6setting.yml
>> kubectl get configmap --namespace k8s-example
NAME                    DATA   AGE
k8s-example-configmap   2      11m

>> kubectl get secret --namespace k8s-example

NAME                  TYPE                                  DATA   AGE
default-secret        kubernetes.io/dockerconfigjson        1      15h
default-token-64tfz   kubernetes.io/service-account-token   3      15hs
k8s-example-secret    Opaque                                2      11m

当然如何你想看具体的配置文件的详情,都可以使用 kubectl describe 进行查看。

  • 配置 service
# 3service.yml
apiVersion: v1
kind: Service
metadata:
  name: k8s-example-mysql-service
  labels:
    app: k8s-example-mysql-service
    name: k8s-example-mysql-service
  namespace: k8s-example
spec:
  selector:
    app: k8s-example-mysql-pod-with-setting
  type: NodePort
  ports:
    - port: 3306
      targetPort: 3306
      nodePort: 32345

查看部署情况:

>> kubectl get service --namespace k8s-example

NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
k8s-example-deployment-service   ClusterIP   None             <none>        80/TCP           5h
k8s-example-mysql-service        NodePort    10.247.1.4       <none>        3306:32345/TCP   17m
k8s-example-nginx-service        NodePort    10.247.69.248    <none>        80:31519/TCP     5h
k8s-example-replicaset-service   NodePort    10.247.252.231   <none>        80:31234/TCP     5h
  • 配置 无状态工作负载

deployment 中如何引用相应的配置?

# 7deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-example-deployment-with-setting
  labels:
    app: k8s-example-deployment-with-setting
    name: k8s-example-deployment-with-setting
  namespace: k8s-example
spec:
  selector:
    matchLabels:
      app: k8s-example-mysql-pod-with-setting
  replicas: 2
  template:
    metadata:
      labels:
        app: k8s-example-mysql-pod-with-setting
    spec:
      containers:
        - name: k8s-example-mysql-pod-with-setting
          image: mysql
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3306
          resources:
            limits:
              cpu: "1"
              memory: "1024Mi"
            requests:
              cpu: "0.5"
              memory: "500Mi"
          envFrom:
            - configMapRef:
                name: k8s-example-configmap
            - secretRef:
                name: k8s-example-secret
          env:
            - name: MYSQL_DEPLOYMENT
              value: "k8s-example"
            - name: MYSQL_USER
              value: ${mysql.User}
            - name: MYSQL_DATABASE
              value: ${mysql.Database}
            - name: MYSQL_ROOT_PASSWORD
              value: ${mysql.Password}
            - name: METADATA_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: RESOURCE
              valueFrom:
                resourceFieldRef:
                  resource: limits.cpu

其中:resource 字段,可以用来表明服务申请和限制 内存和CPU,具体可以用数字来表示,也可以用百分比表示。这种限制有什么用?其中一个比较厉害的功能是动态的调整 POD,比如你设置某种策略,在服务请求压力比较大的时候,多开几个副本,缓解上游请求压力。在系统比较闲的时候,删除几个副本,释放资源。

>> kubectl apply -f 7deployment.yml
>> kubectl get deployment --namespace k8s-example

NAME                                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
k8s-example-deployment                2         2         2            2           5h
k8s-example-deployment-with-setting   2         2         2            2           13m


>> kubectl get pods --namespace k8s-example

NAME                                                   READY   STATUS    RESTARTS   AGE
k8s-example-deployment-6fb599d4b8-bmrnz                1/1     Running   0          5h
k8s-example-deployment-6fb599d4b8-rcc2w                1/1     Running   0          5h
k8s-example-deployment-with-setting-56c7dd7ccc-9k98p   1/1     Running   0          6m
k8s-example-deployment-with-setting-56c7dd7ccc-htwvz   1/1     Running   0          6m
k8s-example-pod                                        1/1     Running   0          15h
k8s-example-replicaset-b72k4                           1/1     Running   0          15h
k8s-example-replicaset-jd7g6                           1/1     Running   0          15h

进入容器内查看环境变量和登录 mysql,查看数据库试试:

kubectl 对容器的操作几乎和 docker 的命令一致,这意味着,如果你熟悉 docker, 使用 kubectl 操作容器几乎没有学习成本

>> kubectl exec -it k8s-example-deployment-with-setting-56c7dd7ccc-htwvz --namespace k8s-example -- bash

>> env | grep METADATA
METADATA_NAME=k8s-example-deployment-with-setting-56c7dd7ccc-htwvz

>> env | grep MYSQL_DEPLOYMENT
MYSQL_DEPLOYMENT=k8s-example

>> mysql -u root -p

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| k8s-example        |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

除这些概念之外,k8s 还包括:

  • StatefulSet 有状态工作负载,主要应用于工作负载存在先后,主从依赖等服务
  • job 单次运行负载
  • cronjob 定时任务负载
  • daemonSet 守护进程,一个节点上只存在一个,一般用于网络或者日志处理等
  • persistenVolume/persistentVolumeClaim 文件挂载相关

希望这些简单的示例能够让你感受到 k8s 的基本使用方法。

一般我部署组件抽象出这么几个步骤:

  • 定义 namespace : 根据场景定义不同的 namepace 起到逻辑上隔离的作用
  • 定义 configmap/secret 等配置相关的资源
  • 定义 service 用于绑定 pod , 提供对外服务
  • 定义 persistentVolume/persistentVolumeClaim 文件挂载相关
  • 定义 deployment 用于主要的服务,即使副本只有一个,也不采用 POD 的形式
  • 定义 statefulSet 用于有状态的负载
  • 定义 job 服务
  • 定义 cronjob 服务

事实上,规划整套系统的组件的部署的时候,我会采用清单的形式,按照上面的步骤进行操作。尽管各资源配置文件的定义先后关系不影响整体服务,但我依然建议读者总结出一套自己的“清单”。

<完>

参考文档:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容