kubernetes,也叫k8s,是一个用于自动化部署、扩展、管理容器化应用的开源系统。
基础概念
-
Cluster
Kubernetes将集群分为一个Master和一些Node。
Master上运行着集群相关的一组进程kube-apiserver 、 kube-controller-manager和kube-scheduler,这些进程实现了整个集群的资源管理、Pod调度、弹性伸缩、安全控制、系统监控和纠错等管理功能,并且都是自动完成的。
Node作为集群中的工作节点,运行真正的应用程序,在Node上Kubernetes管理的最小运行单元是Pod。在Node上运行着Kubernetes的kubelet、kube-proxy服务进程,这些服务进程负责Pod的创建、启动、监控、重启、销毁,以及实现软件模式的负载均衡器。
-
Pod
Pod运行在Node上,Node既可以是物理机也可以是虚拟机,通常一个Node上可以运行多个上百个Pod。每个Pod包含一个Pause容器和多个业务容器,业务容器共用Pause Container的网络栈和Volume挂载卷。通常将一组密切相关的业务放在一个Pod上。
Pod是Kubernetes调度的最小单位,同一Pod中的容器始终被一起调用。
并不是每个Pod和它内部运行的容器都能映射到一个Service上,只有提供服务的Pod才会被映射为一个Service。
-
Service
个人理解Service就是管理一组Pod的网络访问。它有如下特点
拥有唯一指定的名称、拥有一个虚拟IP、提供远程服务能力、被映射到提供这种服务能力的一组容器应用上(也就是一组提供服务的Pod对应一个Service)。
Service一旦创建就不会再变化。
为了建立Service和Pod之间的关系,Kubernetes首先给每个Pod贴上一个标签,然后给相应的Service定义标签选择器(Label Selector),比如标签选择器的选择条件是name=mysql,意味着该Service要作用于所有包含name=mysql 标签的Pod。
-
RC(Replication Controller)
RC控制Pod的运行。
在Kubernetes中为服务器扩容,只需为需要扩容的Service关联的Pod创建一个RC。在一个RC文件中包含以下关键信息:
- 目标Pod定义
- 目标Pod需要运行的副本数量
- 要监控的Pod标签
创建好RC后,K8s通过RC中标签筛选出对应的Pod实例,并实时监控其状态和数量。如果实例数量少于定义的副本数量,则会根据在RC中定义的Pod模板创建一个新的Pod,然后将此Pod调度到合适的Node上启动运行,直到Pod实例的数量达到预定目标。这个过程完全是自动化的,无须人工干预。后续的服务升级也将通过修改RC来自动完成。
Kubernetes安装
操作系统:CentOS Linux release 7.5.1804 (Core)
Docker版本:19.03.11
Kubernetes版本:
2.1 运行时(Container runtimes)
需要在每个Node上安装运行时保证Pod可以运行,Kubernetes常用的运行时有:containerd、CPI-O、Docker。本例采用Docker。
安装Docker
#1. 设置仓库,安装需要的包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
#2. 添加Docker仓库
sudo yum-config-manager --add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
#3. 安装Docker CE
sudo yum update -y && sudo yum install -y \
containerd.io-1.2.13 \
docker-ce-19.03.11 \
docker-ce-cli-19.03.11
#4. 创建/etc/docker
sudo mkdir /etc/docker
#5. 设置Docker deamon
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
EOF
#6. sudo mkdir -p /etc/systemd/system/docker.service.d
7. 重启Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
#8. 添加至开机启动
sudo systemctl enable docker
2.2 使用kubeadm引导集群
2.2.1 安装kubeadm,kubelet,kubectl
- kubeadm 引导集群的命令
- kubelet 在集群中所有计算机上运行的组件,它执行诸如启动Pod和容器之类的操作。
- kubectl 与集群通信的命令行工具。
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
# Set SELinux in permissive mode (effectively disabling it)允许容器访问主机文件
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet
现在kubelet每隔几秒就启动一次,因为它在一个crashloop中等待kubeadm的指令。
在使用Docker时,Kubeadm将自动监测kubelet的cgroup驱动程序,并在运行时将它设置到/var/lib/kubelet/config.yaml
文件
2.2.2 kubeadm config
注意:配置文件仍然被认为是测试版,在未来的版本中可能会改变。
Kubeadm提供了配置文件用于复杂定制,同时kubeadm将配置文件以ConfigMap的形式保存到集群之中,便于后续的查询和升级工作。
kubeadm config子命令提供了对这一组功能的支持。
- kubeadm config upload from-file:由配置文件上传到集群中生成ConfigMap。
- kubeadm config upload from-flags:由配置参数生成ConfigMap。
- kubeadm config print init-defaults:输出kubeadm init默认参数文件的内容。
- kubeadm config print join-defaults:输出kubeadm join默认参数文件的内容。
- kubeadm config migrate:在新旧版本之间进行配置转换。
- kubeadm config images list:列出所需的镜像列表。
- kubeadm config images pull:拉取镜像到本地。
#用默认配置生成文件
[root@test-38 ~]# kubeadm config print init-defaults > init.default.yaml
2.2.3 运行kubeadm init 安装Master
- 关闭Swap
swapoff -a
sed -i 's/.*swap.*/#&/' /etc/fstab
cat /etc/fstab
- 运行Kubeadm init
kubeadm init
//也可自定义kubeadm config ,本例采用默认配置
等待一段时间后,Kubernetes安装Master成功,显示如下成功信息,成功信息包含了加入节点的指令和所需要的token(kubeadm join)
按照信息提示执行命令,复制配置文件到普通用户的home目录下:
[root@test-38 lib]# mkdir -p $HOME/.kube
[root@test-38 lib]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@test-38 lib]# chown $(id -u):$(id -g) $HOME/.kube/config
[root@test-38 lib]#
这样就在Master上安装了Kubernetes,但在集群内还没有可用的工作Node,并缺乏对容器的网络配置。
此时可以验证2.2.2提到的configMap
[root@test-38 ~]# kubectl get -n kube-system configmap
NAME DATA AGE
coredns 1 99m
extension-apiserver-authentication 6 99m
kube-proxy 2 99m
kube-root-ca.crt 1 99m
kubeadm-config 2 99m
kubelet-config-1.20 1 99m
可以看到其中生成了kubeadm-config的ConfigMap对象
2.2.4安装Node,加入集群
对于新节点的添加,系统准备和kubernetes yum源的配置是一样的
kubeadm join 192.168.1.38:6443 --token qfp9bn.47raj0gk26ug6jlq --discovery-token-ca-cert-hash sha256:ae84e70d9559f21ad42e7d9559706f597654531000ad218d0afa9ed6af83e83a
kubeadm在Master上也安装了kubelet,在默认情况下并不参与工作负载。如果希望安装一个单机环境,可以执行下面的命令:
[root@test-38 ~]# kubectl taint nodes --all node-role.kubernetes.io/master-
node/test-38 untainted
这将从任何拥有 node-role.kubernetes.io/master taint 标记的节点中移除该标记, 包括控制平面节点,这意味着调度程序将能够在任何地方调度 Pods。
2.2.5 安装网络插件
执行kubectl get nodes,会发现Master处于NotReady的状态,这是因为还没有安装CNI网络插件:
[root@test-38 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
test-38 NotReady control-plane,master 24m v1.20.4
安装CNI网络插件,这里选择的是weave
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
2.2.6 验证kubernetes集群是否安装完成
kubectl get pods --all-namespaces
如果发现有状态错误的Pod,则可以执行kubectl --namespace=kube-systemdescribe pod<pod_name>来查看错误原因,常见的错误原因是镜像没有下载完成。
2.2.7 部署应用
使用 kubernetes create deployment
部署应用:
kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
部署应用做了以下两件事:
- 寻找合适的可以运行应用实例的Node
- 安排应用程序在该节点上运行
- 配置集群以在需要时在新节点上重新安排实例
查看deployment
[root@test-38 ~]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 103m
2.2.8 访问应用
默认情况下,所有Pod只能在集群内部访问,当使用kubectl时,我们正在通过API端点进行交互来和我们的程序进行通信。
- 通过代理访问API
kubectl可以创建一个代理,该代理会将通信转发到集群范围的专用网络
[root@test-38 ~]# kubectl proxy
Starting to serve on 127.0.0.1:8001
现在我们有了一个我们主机和kubernetes集群的连接,这个代理允许我们通过API进行访问。
[root@test-38 ~]# curl http://localhost:8001/version
{
"major": "1",
"minor": "20",
"gitVersion": "v1.20.4",
"gitCommit": "e87da0bd6e03ec3fea7933c432i63d151aafdusj",
"gitTreeState": "clean",
"buildDate": "2021-02-18T16:03:00Z",
"goVersion": "go1.15.8",
"compiler": "gc",
"platform": "linux/amd64"
}
API 服务器将基于Pod名称自动为每个Pod创建一个端点,该端点也可以通过代理进行访问。
首先我们要现获取Pod名称,然后将名称存储在环境变量POD_NAME:
[root@test-38 ~]# export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
[root@test-38 ~]# echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-57978f5f5d-jcn7k
使用kubectl logs $POD_NAME查看pod日志
[root@test-38 ~]# kubectl logs kubernetes-bootcamp-57978f5f5d-jcn7k
Kubernetes Bootcamp App Started At: 2021-03-01T08:07:32.804Z | Running On: kubernetes-bootcamp-57978f5f5d-jcn7k
还可以在container内部执行命令,具体可以参考https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-interactive/
-
从外部访问应用
查看现有的service可以看到,系统有一个默认创建的service。
[root@test-38 ~]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d17h
要访问应用只能访问容器的8080端口,我们要将容器的8080端口映射到节点的端口。
[root@test-38 ~]# kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
service/kubernetes-bootcamp exposed
[root@test-38 ~]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d17h
kubernetes-bootcamp NodePort 10.99.65.217 <none> 8080:30691/TCP 8s
现在可以看到我们新运行了一个叫kubernetes-bootcamp的service,可以看到应用被映射到节点的30691端口,端口号是随机分配的,可以执行如下命令访问应用:
[root@test-38 ~]# curl localhost:30691
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57978f5f5d-jcn7k | v=1
Deployment会自动为我们的Pod创建一个标签
[root@test-38 ~]# kubectl describe deployments
使用标签获取pod列表:
[root@test-38 ~]# kubectl get pods -l app=kubernetes-bootcamp
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-57978f5f5d-jcn7k 1/1 Running 0 19h
删除service——delete service
[root@test-38 ~]# kubectl delete service -l app=kubernetes-bootcamp
service "kubernetes-bootcamp" deleted
2.2.9 Scale 应用
默认情况下只会运行一个副本,可以通过kubectl get deployments
查看副本
[root@test-38 ~]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 22h
READY
显示的是当前副本和所需副本的比率
UP-TO-DATE
显示的是已更新到所需状态的副本数量
AVAILABLE
显示应用程序有多少个副本可供用户使用
AGE
显示应用程序已运行的时间
现在我们扩展复制集到4个
[root@test-38 ~]# kubectl scale deployments/kubernetes-bootcamp --replicas=4
deployment.apps/kubernetes-bootcamp scaled
[root@test-38 ~]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 4/4 4 4 22h
现在我们查看pod的情况,可以看到当前Pod已经增加到4个
[root@test-38 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubernetes-bootcamp-57978f5f5d-42tp5 1/1 Running 0 2m10s 10.32.0.5 test-38 <none> <none>
kubernetes-bootcamp-57978f5f5d-8k4w4 1/1 Running 0 2m10s 10.32.0.7 test-38 <none> <none>
kubernetes-bootcamp-57978f5f5d-jcn7k 1/1 Running 0 22h 10.32.0.4 test-38 <none> <none>
kubernetes-bootcamp-57978f5f5d-pkfgq 1/1 Running 0 2m10s 10.32.0.6 test-38 <none> <none>
可以验证一下复制集是否进行负载均衡,使用curl进行访问应用,可以各个请求每次发送到不同的Pod,四个副本轮询处理,实现负载均衡
[root@test-38 ~]# curl localhost:32230
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57978f5f5d-pkfgq | v=1
[root@test-38 ~]# curl localhost:32230
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57978f5f5d-jcn7k | v=1
[root@test-38 ~]# curl localhost:32230
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57978f5f5d-42tp5 | v=1
2.2.10 滚动更新
使用kubectl describe pods
查看现在的image版本信息,现在将image升级到v2
kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2
通过kubectl get pods
可以看到滚动更新的过程:v1的Pod逐渐删除,同时启动新的v2 Pod。
回退版本使用kubectl rollout undo
命令,rollout命令将部署还原到先前的已知状态(映像的v2)。 更新是版本控制的,您可以还原到以前任何已知的部署状态。
kubectl rollout undo deployments/kubernetes-bootcamp
🦁🦁🦁🦁🦁完结撒花~
参考:《Kubenetes权威指南》、https://kubernetes.io