02 集群搭建

01-kubeadm 阅读更多

0.1. 部署Kubernetes 0.2. Kubeadm原理 0.3. kubeadm步骤 0.3.1. kubeadm init 0.3.1.1. 第一步:Preflight Checks 0.3.1.2. 第二步:生成证书 0.3.1.3. 第三步:生成conf文件 0.3.1.4. 第四步:生成yaml文件 0.3.2. kubeadm join 0.3.3. 配置kubeadm参数 0.4. Kubeadm源代码 0.5. 生产环境部署 0.6. 制作证书的方法 Linux容器相关的技术可以帮助我们快速定位问题,并解决问题。 要真正发挥容器技术的实力的关键在于如何使用这些技术“容器化”应用。 单单通过Docker把一个应用的镜像跑起来,并没有什么用。关键是处理好容器之间的编排关系。比如: 主从容器如何区分? 容器之间的自动发现和通信如何完成? 容器的持久化数据如何保持? 0.1. 部署Kubernetes 主流云厂商使用SaltStack、Ansible等运维工具自动化地执行安装脚本和配置文件。但是,这些工具的学习成本比kubernetes项目还高。 社区开发了一个独立部署的工具:kubeadm,执行以下两条命令就可以部署一个集群: # 创建一个 Master 节点 kubeadm init # 将一个 Node 节点加入到当前集群中 kubeadm join <Master节点的IP和端口> 0.2. Kubeadm原理 传统部署方式:在部署Kubernetes时,它的每一个组件都是一个需要被执行的、单独的二进制文件。使用SaltStack这样的运维工具或者社区维护的脚本,就需要把这些二进制文件传输到指定的节点上,然后编写控制脚本来启停这些组件。 容器化部署方式:给每个组件做一个容器镜像,然后在每台宿主机上运行docker run命令来启动这些组件容器。 容器化部署的一个问题:如何容器化kubelet? Kubelet是Kubernetes项目用来操作Docker等容器运行时的核心组件,除了和容器运行时打交道之外,kubelet在配置容器网络、管理容器数据卷都需要直接操作宿主机。 如果kubelet本身就运行在一个容器中,那么直接操作宿主机就会变得很麻烦。 对于配置网络:kubelet容器可以通过不开启Network Namespace(即Docker的host network模式)的方式,直接共享宿主机的网络栈。 对于操作文件系统:让kubelet隔着容器的Mount Namespace和文件系统,操作宿主机的文件系统,就有点难了。 举个例子:用户想要使用NFS做容器的持久化数据卷,那么kubelet就需要在容器进行绑定挂载前,在宿主机的指定目录上,先挂载NFS的远程目录。那么问题来了,由于现在kubelet是运行在容器里的,这就意味着它要做的这个mount -F nfs命令,被隔离在了一个单独的Mount Namespace中,即kubelet做的挂载操作,不能被“传播”到宿主机上。 因此,妥协的方案就是,kubelet直接运行在宿主机上,然后使用容器部署其他的kubernetes组件。 0.3. kubeadm步骤 使用kubeadm的第一步,在机器上手动安装 kubeadm、kubelet、kubectl 这三个二进制文件。kubeadm已经为各个发行版的Linux准备好了安装包,所以只需要执行如下命令: apt-get install kubeadm #Debain or Ubuntu yum install kubeadm #Redhat or Centos kubeadm init #部署Master 节点 0.3.1. kubeadm init 0.3.1.1. 第一步:Preflight Checks 确定服务器是否可以用来部署kubernetes。 主要包括: Linux内核的版本必须是否是3.10以上? Linux Cgroup模块是否可用? 服务器的hostname是否标准? 在kubernetes项目中,主机名以及一切存储在Etcd中的API对象,都必须使用标准的DNS命名(RFC1123)。 安装的kubeadm和kubelet的版本是否匹配? 服务器上是否已经安装了 Kubernetes的二进制文件? Kubernetes的工作端口10250/10251/10252端口是不是已经被占用? ip、mount等Linux指令是否存在? Docker是否已经安装? 。。。。。。 0.3.1.2. 第二步:生成证书 当通过了Preflight Checks后,kubeadm会生成Kubernetes对外提供服务所需的各种证书和对应的目录。 Kubernetes对外提供服务时,除非专门开启“不安全模式”,否则都需要通过HTTPS才能访问kube-apiserver,这就需要为Kubernetes集群配置好证书文件。 证书存放在Master节点的/etc/kubernetes/pki目录下,其中最主要的证书是ca.crt和对应的私钥ca.key。 用户使用kubectl获取容器日志等streaming操作时,需要通过kube-apiserver向kubelet发起请求,这个链接也必须是安全的。kubeadm 为上述操作生成的是apiserver-kubelet-client.crt文件,对应的私钥是apiserver-kubelet-client.key。 其他的如Aggregate APIServer 等特性,也需要生成专门的证书,同时也可以选择不让kubeadm生成证书,而是拷贝现成的证书到指定的目录中/etc/kubernetes/pki/ca.{crt,key}。那么,此时kubeadm会跳过生成证书的步骤。 0.3.1.3. 第三步:生成conf文件 证书生成后,kubeadm接下来会为其他组件生成访问kube-apiserver所需的配置文件。配置文件的路径是:/etc/kubernetes/xxx.conf ls /etc/kubernetes/ admin.conf controller-manager.conf kubelet.conf scheduler.conf 这些文件里记录的是,当前这个Master节点的 服务器地址、监听端口、证书目录 等信息。这样,对应的客户端(如scheduler、kubelet等),可以直接加载相应的文件,使用里面的信息与kube-apiserver建立安全连接。 0.3.1.4. 第四步:生成yaml文件 kubeadm为Master组件(kube-apiserver、kube-controller-manager、kube-scheduler)生成pod yaml文件,它们都以pod的方式部署起来。 在kubernetes中,有一种特殊的容器启动方法叫 “Static Pod” 。它允许你把要部署的pod的yaml文件放在一个指定的目录里。这样,当这台服务器上的kubelet启动时,它会自动检查这个目录,加载所有的pod yaml文件,然后在这台服务器上启动它们。 在kubeadm中,Master组件的yaml文件会被生成在/etc/kubernetes/manifests路径下。如果需要修改已有集群的kubernetes组件的配置,需要修改对应的yaml文件。 同样通过 Static pod 的方式启动Etcd,所以,Master组件的pod文如下: ls /etc/kubernetes/manifests/ etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml 一旦这些文件出现在被kubelet监视的/etc/kubernetes/manifests目录下,kubelet就会自动创建这些yaml文件中定义的pod,(即Master组件的容器)。 Master组件的容器启动后,kubeadm会通过检查localhost:6443/healthz这个Master组件的健康检查URL,等待Master组件完全运行起来。 kubeadm为集群生成bootstrap token,持有这个token的任何一个kubelet和kubeadm节点,都可以通过kubeadm join加入到这个集群中。(token的值和使用方法会在kubeadm init结束后打印出来。) token生成后,kubeadm会将ca.crt等Master节点的重要信息,通过ConfigMap的方式保存在Etcd中,供后续部署Node节点使用。(这个ConfigMap的名字 cluster-info) kubernetes默认kube-proxy和DNS这两个插件是必须安装的,提供集群的服务发现和DNS功能,这两个插件也是两个容器镜像,创建两个pod即可。 0.3.2. kubeadm join 使用kubeadm init生成的bootstrap token 在安装了kubeadm和kubelet的服务器上执行kubeadm join。 bootstrap token的作用: 一台服务器想要成为kubernetes集群中的节点,就必须在集群的kube-apiserver上注册。 想要与apiserver通信,这台服务器必须获取相应的证书文件(CA文件)。 为了一键安装,就不能手动去拷贝证书文件。 kubeadm至少要发起一次“不安全模式”的访问到kube-apiserver,从而拿到保存在ConfigMap中的cluster-info(这里保存了APIServer的授权信息)。 bootstrap token扮演的就是这个过程中的安全验证的角色,有了cluster-info里的kube-apiserver的地址、端口、证书,kubelet就可以“安全模式”连接到apiserver上。 0.3.3. 配置kubeadm参数 可以通过 --config 参数指定启动时读取的配置文件: kubeadm init --config kubeadm.yaml 这样可以给kubeadm提供一个yaml文件,例如: apiVersion: kubeadm.k8s.io/v1alpha2 kind: MasterConfiguration kubernetesVersion: v1.11.0 api: advertiseAddress: 192.168.0.102 bindPort: 6443 ... etcd: local: dataDir: /var/lib/etcd image: "" imageRepository: k8s.gcr.io kubeProxy: config: bindAddress: 0.0.0.0 ... kubeletConfiguration: baseConfig: address: 0.0.0.0 ... networking: dnsDomain: cluster.local podSubnet: "" serviceSubnet: 10.96.0.0/12 nodeRegistration: criSocket: /var/run/dockershim.sock ... 通过指定这样一个配置文件,可以方便地在文件里填写各种自定义的部署参数。 比如,要自动化kube-apiserver的参数,添加如下信息: ... apiServerExtraArgs: advertise-address: 192.168.0.103 anonymous-auth: false enable-admission-plugins: AlwaysPullImages,DefaultStorageClass audit-log-path: /home/johndoe/audit.log 然后,kubeadm就会使用上面的信息替换/etc/kubernetes/manifests/kube-apiserver.yaml里的command字段里的参数。 更具体的: 修改kubelet的配置 修改kube-proxy 的配置 修改kubernetes使用的基础镜像的URL(默认的k8s.gcr.io/xxx镜像URL在国内不能访问) 指定自己的证书文件 指定特殊的容器运行时 0.4. Kubeadm源代码 源代码在kubernetes/cmd/kubeadm目录下,其中app/phases文件夹下的代码就是上述的步骤。 0.5. 生产环境部署 部署规模化的生产环境,推荐使用: kops saltstack ansible playbook kubespray K8S实验平台 谷歌镜像 0.6. 制作证书的方法 CFSSL OpenSSL easyrsa GnuGPG keybase

02-容器化应用 阅读更多

0.1. YAML示例 0.2. API对象 0.2.1. Metadata 0.2.2. Label Selector 0.2.3. Annotations 0.3. 运行API对象 0.3.1. 示例 0.4. 挂载volume 0.5. 进入容器 使用Kubernetes的必备技能:编写配置文件。这些配置文件可以是 YAML 或者 JSON 格式的,一般都是用YAML格式。 Kubernetes不推荐直接使用命令行的方式运行容器,而是使用YAML文件的方式,即:把容器的定义、参数、配置都记录在一个YAML文件中,然后使用如下命令: kubectl create -f <xxx.yaml> 这么做的最大好处,有一个文件能记录Kubernetes到底运行了什么。 0.1. YAML示例 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 这个 YAML 文件,对应到 Kubernetes 中,就是一个 API Object(API 对象)。 为这个对象的各个字段填好值并提交给Kubernetes之后,Kubernetes就会负责创建出这些对象所定义的容器或者其他类型的API 资源。 可以看到,这个 YAML 文件中的 Kind 字段,指定了这个API对象的类型(Type),是一个Deployment。 所谓 Deployment,是一个定义多副本应用(即多个副本 Pod)的对象,此外,Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。 这个 YAML 文件中: 定义的 Pod 副本个数 (spec.replicas) 是:2 定义了一个 Pod 模版(spec.template),这个模版描述了要创建的 Pod 的细节 这个 Pod 里只有一个容器,这个容器的镜像(spec.containers.image)是nginx:1.7.9,这个容器监听端口(containerPort)是 80 0.2. API对象 Pod就是Kubernetes世界里的“应用”,而一个应用,可以由多个容器组成。 像这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 Kubernetes 中,叫作“控制器”模式(controller pattern)。 在上面的例子中,Deployment扮演的正是 Pod 的控制器的角色。 0.2.1. Metadata 这样的每一个 API 对象都有一个叫作 Metadata 的字段,这个字段就是 API 对象的“标识”,即元数据,它也是从 Kubernetes 里找到这个对象的主要依据,这其中最主要使用到的字段是Labels。 Labels 就是一组 key-value 格式的标签。而像 Deployment 这样的控制器对象,就可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。 比如,在上面这个 YAML 文件中,Deployment 会把所有正在运行的、携带“app: nginx”标签的Pod 识别为被管理的对象,并确保这些 Pod 的总数严格等于两个。 0.2.2. Label Selector 过滤规则的定义,是在 Deployment 的“spec.selector.matchLabels”字段,一般称之为:Label Selector。 0.2.3. Annotations 在 Metadata中,还有一个与Labels格式、层级完全相同的字段叫Annotations,它专门用来携带 key-value 格式的内部信息。 内部信息,指的是对这些信息感兴趣的是Kubernetes 组件本身而不是用户。所以大多数Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。 一个 Kubernetes 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分: 前者存放的是这个对象的元数据,对所有 API 对象来说,这一部分的字段和格式基本上是一样的 后者存放的是属于这个对象独有的定义,用来描述它所要表达的功能 0.3. 运行API对象 # 创建API对象 kubectl create -f nginx-deployment.yaml # 查看API对象,-l参数获取所有匹配标签的pod kubectl get pods -l app=nginx # 查看一个 API 对象的细节 kubectl describe pod nginx kubectl get 指令的作用,就是从 Kubernetes 里面获取(GET)指定的 API 对象。需要注意的是,在命令行中,所有 key-value格式的参数,都使用“=”而非“:”表示。 kubectl describe 命令返回的结果中,可以清楚地看到这个 Pod 的详细信息,比如它的 IP 地址等等。其中,有一个部分值得特别关注,就是Events(事件)。 在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在 kubectl describe 指令返回的结果中。这个部分正是我们将来进行 Debug 的重要依据。如果有异常发生,要第一时间查看这些 Events,往往可以看到非常详细的错误信息。 0.3.1. 示例 上述deployment中的pod运行的是1.7.9的nginx容器,如何升级成1.8? 只要修改刚才的YAML文件即可: ... spec: containers: - name: nginx image: nginx:1.8 # 这里从 1.7.9 修改为 1.8 ports: - containerPort: 80 这样对YAML配置文件的本地修改就完成了,通过如下命令更新到kubernetes集群中: kubectl replace -f nginx-deployment.yaml # 1 kubectl create -f file.yaml # 2 kubectl replace -f file.yaml # 3 kubectl apply -f file.yaml # 上述的命令1和2可以用3替换掉,这也是kubernetes“声明式API”推荐的做法。 通过容器镜像,保证了应用本身在开发和部署环境里的一致性(当应用发生变化时,开发和运维可以依靠容器进行同步) 通过YAML配置文件,保证了应用“部署参数”在开发和部署环境中的一致性(当应用部署参数发生变化时,开发和运维可以依靠YAML配置文件进行沟通) 0.4. 挂载volume 在 Kubernetes 中,Volume 是属于 Pod 对象的一部分。所以,我们就需要修改这个 YAML 文件里的 template.spec 字段,如下所示: apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.8 ports: - containerPort: 80 volumeMounts: - mountPath: "/usr/share/nginx/html" name: nginx-vol volumes: - name: nginx-vol emptyDir: {} 在 Deployment 的 Pod 模板部分添加了一个 volumes 字段,定义了这个 Pod 声明的所有 Volume。它的名字叫作 nginx-vol,类型是 emptyDir。 那什么是 emptyDir类型呢? 它其实就等同于Docker的隐式Volume参数,即:不显式声明宿主机目录的Volume。所以,Kubernetes也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。 Kubernetes 的 emptyDir类型,只是把Kubernetes 创建的临时目录作为Volume的宿主机目录交给Docker,因为 Kubernetes 不想依赖 Docker 创建的 _data 目录。 而 Pod 中的容器,使用的是 volumeMounts 字段来声明自己要挂载哪个 Volume,并通过mountPath 字段来定义容器内的 Volume 目录,比如:/usr/share/nginx/html。 当然,Kubernetes 也提供了显式的 Volume 定义,它叫做 hostPath。比如下面的这个 YAML 文件: ... volumes: - name: nginx-vol hostPath: path: /var/data 这样volume挂载的宿主机目录,就变成了/var/data。 0.5. 进入容器 使用如下命令,进入到Pod中,即容器的Namespace中: kubectl exec -it nginx-deployment-5c678cfb6d-lg9lw -- /bin/bash ls /usr/share/nginx/html 从Kubernetes集群中删除部署的Deployment的命令: kubectl delete -f nginx-deployment.yaml