kubernetes搭建

k8s搭建

生产环境都是二进制安装(预编译安装)

1
2
k8s官网地址
https://kubernetes.io/docs/tasks/tools/install-kubectl/

yum无法自定义安装

编译安装可以自定义安装

预编译介于两者之间

准备三台服务器:

1、一台master

配置: IP:192.168.224.10 ,2H2G

2、两台node

配置: IP:192.168.224.11,192.168.224.12 , 1H2G

一、系统规划

主机名 IP 组件
k8smaster 192.168.224.10 etcd、kube-apiserver、kube-controller-manager、kube-scheduler
k8snode1 192.168.224.11 kubelet、kube-proxy、docker、dns、calico
k8snode2 192.168.224.12 kubelet、kube-proxy、docker、dns、calico

二、初始化系统基础环境

系统初始化时由于3台机器大部分操作都相同,我这里在配置过程中,在一台主机上进行配置文件创建,然后使用ansible进行分发,当然你也可以直接在对应主机上进行操作。

1.设置主机名

在三台机器分别执行对应设置主机名的命令

1
2
3
hostnamectl set-hostname --static k8smaster
hostnamectl set-hostname --static k8snode1
hostnamectl set-hostname --static k8snode2

2.配置免密钥登陆

以k8smaster为主机,对另外3台机器进行免密钥登陆

1
2
3
4
ssh-keygen ##一路回车进行公钥私钥创建
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 root@192.168.224.10
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 root@192.168.224.11
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 root@192.168.224.12

3.安装ansible(可以不安装,把生成文件或者命令在各节点执行即可)

这里只需在master节点安装即可,后续一些操作均在此机器上执行,然后把生成的文件分发至对应节点

1
2
3
[root@k8smaster ~]# yum install -y epel-release
[root@k8smaster ~]#  yum install ansible -y
[root@k8smaster ~]# ansible --version

定义主机组

1
vim /etc/ansible/hosts

[k8smaster] #master节点服务器组k8smaster会报警告

1
2
3
4
5
6
7
8
9
10
11
12
[k8smaster]   #master服务器组 
k8smaster ansible_host=192.168.224.10 ansible_ssh_user=root
#别名 ip 用户

[k8snode] #node节点服务器组
k8snode1 ansible_host=192.168.224.11 ansible_ssh_user=root
k8snode2 ansible_host=192.168.224.12 ansible_ssh_user=root

[k8sall] #k8s集群服务器组
k8smaster ansible_host=192.168.224.10 ansible_ssh_user=root
k8snode1 ansible_host=192.168.224.11 ansible_ssh_user=root
k8snode2 ansible_host=192.168.224.12 ansible_ssh_user=root

测试ansible通讯是否正常

1
ansible k8sall -m ping  #测试ansible是否正常

4.关闭防火墙、selinux(3台机器都执行,我这里使用ansible)

1
2
3
4
5
6
7
[root@k8s_masker ~]# ansible k8sall -m shell -a 'systemctl stop firewalld'
[root@k8s_masker ~]# ansible k8sall -m shell -a 'systemctl disable firewalld'

下面关闭selinux 如果之前设置好了就不用了
[root@k8s_masker ~]# ansible k8sall -m shell -a 'setenforce 0'
[root@k8s_masker ~]# ansible k8sall -m replace -a 'path=/etc/selinux/config regexp="SELINUX=enforcing" replace=SELINUX=disabled'
[root@k8s_masker ~]# ansible k8sall -m replace -a 'path=/etc/sysconfig/selinux regexp="SELINUX=enforcing" replace=SELINUX=disabled'

5.配置host主机域名解析

vim /etc/hosts

1
2
3
4
5
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.224.10 k8smaster
192.168.224.11 k8snode1
192.168.224.12 k8snode2

文件分发到各节点

1
ansible k8sall -m copy -a "src=/etc/hosts dest=/etc/hosts"

关闭缓存

1
2
3
4
5
6
7
8
ansible k8sall -m shell -a 'swapoff -a '

关闭swap分区(不关后面master初始化会失败)
临时关闭:swapoff -a
永久关闭:注释掉/etc/fstab文件中的swap行

ansible k8sall -m shell -a 'free -h'
(确认关闭缓存)

6.设置内核

1
2
3
4
5
6
将桥接的IPv4流量传递到iptables的链
[root@k8smaster ~]# vim /etc/sysctl.d/k8s.conf

net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

文件分发

1
2
3
4
5
ansible k8sall -m copy -a "src=/etc/sysctl.d/k8s.conf dest=/etc/sysctl.d/k8s.conf"

开机自动加载模块
ansible k8sall -m shell -a 'modprobe br_netfilter'
ansible k8sall -m shell -a 'sysctl -p /etc/sysctl.d/k8s.conf'

7.时间同步

1
2
3
ansible k8sall -m yum -a "name=ntpdate  state=latest"

ansible k8sall -m cron -a "name='k8s cluster crontab' minute=*/30 hour=* day=* month=* weekday=* job='ntpdate time7.aliyun.com >/dev/null 2>&1'"

三.安装docker

1.移除旧的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
 sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

ansible卸载其他节点的
ansible k8sall -m shell -a 'sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine -y '

2.安装一些必要的系统依赖工具

1
2
3
yum install -y yum-utils device-mapper-persistent-data lvm2

ansible k8sall -m shell -a 'yum install -y yum-utils device-mapper-persistent-data lvm2'

3.添加软件源信息

1
2
3
4
5
6
7
ansible k8sall -m shell -a 'yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo'

更新yum缓存
ansible k8sall -m shell -a 'yum makecache fast'

列出docker软件版本信息
yum list docker-ce --showduplicates | sort -r

4.安装docker的指定版本

1
2
看哪个版本和k8s没有冲突就安装哪个(我安装18.06的)
ansible k8sall -m shell -a 'yum install -y docker-ce-18.06.1.ce-3.el7'

5.支持端口转发

1
2
3
4
5
ansible k8sall -m shell -a 'echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf '
ansible k8sall -m shell -a 'sysctl -p'

启动docker
ansible k8sall -m shell -a 'systemctl enable docker && systemctl start docker'

6.docker命令tab自动补全

1
2
3
ansible k8sall -m shell -a 'yum install -y bash-completion'
ansible k8sall -m shell -a 'source /usr/share/bash-completion/completions/docker'
ansible k8sall -m shell -a 'source /usr/share/bash-completion/bash_completion'

四.部署Kubernetes

1.k8s的基础配置

1.配置k8s源

1
2
3
4
5
6
7
8
9
vim  /etc/yum.repos.d/kubernetes.repo

[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg

文件分发

1
2
3
ansible k8sall -m copy -a 'src=/etc/yum.repos.d/kubernetes.repo dest=/etc/yum.repos.d/kubernetes.repo'

ansible k8sall -m shell -a 'yum makecache fast -y '

2.安装kubeadm,kubelet和kubectl

1
2
3
4
5
6
7
查看各自的版本号
yum list --showduplicates |grep '^kube'

由于版本更新频繁,这里指定版本号部署:
yum install -y kubelet-1.18.4 kubeadm-1.18.4 kubectl-1.18.4

ansible k8sall -m shell -a ' yum install -y kubelet-1.18.4 kubeadm-1.18.4 kubectl-1.18.4 '

重新加载守护进程和启动docker kubelet

1
2
3
4

ansible k8sall -m shell -a 'systemctl daemon-reload'
ansible k8sall -m shell -a 'systemctl enable docker kubelet'
ansible k8sall -m shell -a 'systemctl restart docker kubelet'

2.1.kubectl 命令tab键补全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
yum install -y bash-completion

source <(kubectl completion bash)

kubectl describe pod ##可以使用了
或者
添加命令自动补全:
yum install -y bash-completion
vim ~/.bashrc
添加
source <(kubectl completion bash)

执行下
source ~/.bashrc

3.部署Kubernetes Master

在master主节点执行

先查看版本,然后初始化网络服务

1
2
[root@k8smaster ~]# kubelet --version
Kubernetes v1.18.4

1)开始初始化

1
2
3
4
kubeadm init --apiserver-advertise-address=192.168.224.10 --kubernetes-version=v1.18.4 --pod-network-cidr=10.244.0.0/16


--apiserver-advertise-address=192.168.224.10 #master组件监听的api地址,这里写masterIP地址即可或者多网卡选择另一个IP地址

按提示创建文件,需要记住两行重要信息

1
2
kubeadm join 192.168.224.10:6443 --token 23ydjv.e7065vwoee1cr6xf \
--discovery-token-ca-cert-hash sha256:9eab18f17699039d57a12fd1552a63b686f893562cae6447cbda862e09827175

以上为kubeadm初始化命令的输出信息,记录输出结果的最后2行。这2行为在集群成员上执行的命令,用于将成员加入集群中。

配置常规用户如何使用kubectl访问集群

1
2
3
4
5
把/etc/kubernetes/admin.conf 复制到当前登录用户下./kube/config 文件中.并修改权限为当前用户

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

初始化成功后docker container ls -a 会发现启动了10个容器

awctN4.png

同时在/etc/kubernetes/目录 会自动生成相关配置文件

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

设置环境变量

1
2
3
4
5
6
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile

source ~/.bash_profile

确认版本信息
kubectl version
1
2
3
这时候查看pod

kubectl get pod -n kube-system -o wide

awgZKx.png

2)初始化失败解决办法

1
2
3
4
5
6
7
8
9
自动删除相关文件和数据
kubeadm reset 所有的都要执行。

// 或者手动删除相关文件和images
rm -rf /etc/kubernetes/*.conf
rm -rf /etc/kubernetes/manifests/*.yaml
docker ps -a |awk '{print $1}' |xargs docker rm -f
systemctl stop kubelet
再次初始化前需要执行清除etcd所有数据的操作

4.master安装Flannel

1
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

查看所有pod

1
kubectl get pod -n kube-system -o wide

awgmqK.png

查看节点

1
2
kubectl get node   
目前就只有master的信息

5.加入node节点

!!!在集群成员节点执行!!!

执行token命令,参考主节点上执行kubeadm init命令的输出结果。

样例(注意不能复制,一定要复制主节点kubeadm init命令的输出结果的最后2行)

1
2
kubeadm join 192.168.224.10:6443 --token 23ydjv.e7065vwoee1cr6xf \
--discovery-token-ca-cert-hash sha256:9eab18f17699039d57a12fd1552a63b686f893562cae6447cbda862e09827175

查看所有node节点加入是否成功

在所有node上执行docker ps 查看k8s组件是否已安装

1
2
3
4
5
6
docker ps 

共有4个
k8s_kube-flannel与k8s_kube-proxy

POD的 kube-proxy与kube-flannel

在master上操作

确认所有节点都加入到集群中,注意:下面的命令在成员加入后要等一段时间才会生效。最快10秒

1
kubectl get nodes

awgKaD.png

1
2
3
kubectl get pods -n kube-system

kubectl get pods -n kube-system -o wide 查看详细信息 有12个在运行,新加入了4个

awglPH.png

以上信息有k8snode1这个从节点的信息,flannel和proxy都有三个pod

到这里,k8s通过kubeadm搭建集群成功

2.安装控制台仪表盘

1.部署官方的 Dashboard

1
官方文档是最重要的参考资料:https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/

!!!在主节点执行!!

1
wget https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml

1.修改yaml文件内容

1
2
3
4
5
6
7
 vim   kubernetes-dashboard.yaml
把112行的 kubernetes-dashboard 镜像地址修改成阿里云的 可以不修改
image: registry.aliyuncs.com/google_container/kubernetes-dashboard-amd64:v1.10.1

在158行新增
type: NodePort
nodePort: 32666

awg8xI.png

安装Dashboard

1
kubectl apply -f kubernetes-dashboard.yaml

使用master节点ip地址+端口来访问,协议是https的

查看Dashboard端口信息:

1
kubectl --namespace=kube-system get service kubernetes-dashboard

awgadS.png

以我自己的服务器为访问对象,使用https://192.168.224.10:32666 即可访问

可以执行以下命令

1
kubectl proxy --address='0.0.0.0'  --accept-hosts='^*$'

这时候就可以通过其它主机访问dashboard了.(以上地址中localhost改为ip地址)

如果没有登陆,则会默认定向到登陆页面,可以使用config或者token方式登陆.我们这里使用token方式登陆.

1
http://192.168.224.10:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/login

2.如果访问出现被防火墙拦截

vim /etc/systemd/system/multi-user.target.wants/docker.service

1
2
3
4
5
#在server字段中添加
ExecStartPost=/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT
#重启docker服务
systemctl daemon-reload
systemctl restart docker

3.创建账号密码文件(这步可以忽略)

1
2
3
 cat <<EOF >  /etc/kubernetes/pki/basic_auth_file
admin,admin,2
EOF

配置密码文件

1
2
3
4
5
vim /etc/kubernetes/manifests/kube-apiserver.yaml

新增一行信息
- --basic_auth_file=/etc/kubernetes/pki/basic_auth_file
--service-node-port-range=2-65535 #这行表示端口映射范围,默认是(30000-32767)

awgTQ1.png

重新启动kubelet服务,使密码配置生效

1
2
3
systemctl restart kubelet

实测新增上面的信息后,重启会出错

应用API服务器配置

1
2
cd /etc/kubernetes/manifests
[root@k8smaster manifests]# kubectl apply -f kube-apiserver.yaml

配置Dashboard

1
2
3
4
5
kubectl get clusterrole/cluster-admin -o yaml

kubectl create clusterrolebinding login-on-dashboard-with-cluster-admin --clusterrole=cluster-admin --user=admin

kubectl get clusterrolebinding/login-on-dashboard-with-cluster-admin -o yaml

4.登录web页面

用火狐浏览器

1
https://192.168.224.10:32666

登陆方式分为俩种:

1.kubeconfig

2.token

在master上执行

1
2
3
4
5
6
7
8
9
创建一个dashboard管理用户
kubectl create serviceaccount dashboard-admin -n kube-system

绑定用户为集群管理用户
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin

执行完以上操作后,由于管理用户的名称为dashboard-admin,生成的对应的secret的值则为dashboard-admin-token-随机字符串我的机器上完整名称为dashboard-admin-token-f99st

kubectl get secret -n kube-system #查看token

aw2VYQ.png

查看token的具体信息

kubectl describe secret dashboard-admin-token-f99st -n kube-system

aw2ZWj.png

或者

1
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep dashboard-admin | awk '{print $1}')

开启跳过登陆

根据使用的版本不同,可能有的版本包含skip按钮,有的则不包含,在1.10.1里面默认不再显然skip按钮,其实dashboard安装有很多坑,如果有读者按照以上设置仍然不能正常成功登陆,但是仍然想要体验dashboard,可以开启默认关闭的skip按钮,这样就可以进入到dashboard管理界面了.

1
2
3
4
5
6
执行命令

kubectl edit deploy -n=kube-system kubernetes-dashboard
在containers下面的args栏里输入

- --enable-skip-login

然后保存即可.刷新web页面,登陆界面就会多出一个skip按钮.

2.部署kuboard

网站文档地址

1
2

https://kuboard.cn/install/install-dashboard.html

稳定版

1
2
3
wget https://kuboard.cn/install-script/kuboard.yaml
kubectl apply -f https://kuboard.cn/install-script/kuboard.yaml
kubectl apply -f https://addons.kuboard.cn/metrics-server/0.3.6/metrics-server.yaml

查看 Kuboard 运行状态:

1
kubectl get pods -l k8s.kuboard.cn/name=kuboard -n kube-system

输出结果如下所示:

1
2
NAME                       READY   STATUS        RESTARTS   AGE
kuboard-54c9c4f6cb-6lf88 1/1 Running 0 45s

五.k8s常用命令集合

1. 创建资源和删除

一般创建资源会有两种方式:通过文件或者命令创建。

1
2
3
4
5
6
7
8
# 通过文件创建一个Deployment
kubectl create -f /path/to/deployment.yaml
cat /path/to/deployment.yaml | kubectl create -f -
# 不过一般可能更常用下面的命令来创建资源
kubectl apply -f /path/to/deployment.yaml

# 通过kubectl命令直接创建
kubectl run nginx_app --image=nginx:1.9.1 --replicas=3

kubectl还提供了一些更新资源的命令,比如kubectl edit、kubectl patch和kubectl replace等。

1
2
3
4
5
6
7
8
9
# kubectl edit:相当于先用get去获取资源,然后进行更新,最后对更新后的资源进行apply
kubectl edit deployment/nginx_app

# kubectl patch:使用补丁修改、更新某个资源的字段,比如更新某个node
kubectl patch node/node-0 -p '{"spec":{"unschedulable":true}}'
kubectl patch -f node-0.json -p '{"spec": {"unschedulable": "true"}}'

# kubectl replace:使用配置文件来替换资源
kubectl replace -f /path/to/new_nginx_app.yaml

删除资源

1
2
3
4
5
6
7
8
9
10
11
kubectl delete - 在 Pod 中的容器执行命令

kubectl delete pod cali-2 -n calib
批量删除namespace 是calib中 状态为Error的所有pod:kubectl get pods -n calib | grep Error | awk '{print $1}' | xargs kubectl delete pod -n calib(注意 “Error”,“Completed”状态得首字母都是大写哦)

根据resource名或label删除resource。

kubectl delete -f rc-nginx.yaml

kubectl delete po rc-nginx-btv4j
kubectl delete po -lapp=nginx-2

2. 查看资源

获取不同种类资源的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 一般命令的格式会如下:
kubectl get <resource_type>

# 比如获取K8s集群下pod的信息
kubectl get pod

# 更加详细的信息
kubectl get pod -o wide

#查看kube-apiserver的详细信息
kubectl get pods -n kube-system kube-apiserver -o wide

# 指定资源的信息,格式:kubectl get <resource_type>/<resource_name>,比如获取deployment nginx-deployment的信息
kubectl get deployment/nginx-deployment -o wide
#deployment表示部署, nginx-deployment表示部署的名字叫nginx-deployment

# 也可以对指定的资源进行格式化输出,比如输出格式为json、yaml等
kubectl get deployment/nginx-deployment -o json
kubectl get deployment/nginx-deployment -o yaml
# 还可以对输出结果进行自定义,比如对pod只输出容器名称和镜像名称
kubectl get pods nginx-deployment-55d5bfd679-nlknm -o custom-columns=CONTAINER:.spec.containers[0].name,IMAGE:.spec.containers[0].image

# 获取某个特定key的值还可以输入如下命令得到,此目录参照go template的用法,且命令结尾'\n'是为了输出结果换行
kubectl get pod nginx-deployment-55d5bfd679-nlknm -o template --template='{{(index spec.containers 0).name}}{{"\n"}}'

# 还有一些可选项可以对结果进行过滤,这儿就不一一列举了,如有兴趣,可参照kubectl get --help说明

kubectl logs - 从 Pod 中的容器打印日志
这个特殊一点哦,这个不用指定TYPE,因为kubeclt logs 默认就是pod类型,所以 kubectl logs pod 会报错,"Error from server (NotFound): pods "pod" not found"
kubectl logs nginx-deployment-55d5bfd679-txglw

2.1, describe方法

describe类似于get,同样用于获取resource的相关信息。不同的是,get获得的是更详细的resource个性的详细信息,describe获得的是resource集群相关的信息。describe命令同get类似,但是describe不支持-o选项,对于同一类型resource,describe输出的信息格式,内容域相同。
注:如果发现是查询某个resource的信息,使用get命令能够获取更加详尽的信息。但是如果想要查询某个resource的状态,如某个pod并不是在running状态,这时需要获取更详尽的状态信息时,就应该使用describe命令。

1
2
3
kubectl describe po nginx-deployment-55d5bfd679-nlknm
查询整个nginx的详细信息
kubectl describe po nginx-deployment

3. 部署命令集

部署命令包括资源的运行管理命令、扩容和缩容命令和自动扩缩容命令。

3.1 rollout命令

管理资源的运行,比如eployment、Daemonet、StatefulSet等资源。

  • 查看部署状态:比如更新deployment/nginx_app中容器的镜像后查看其更新的状态。
1
2
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
kubectl rollout status deployment/nginx-deployment
  • 资源的暂停及恢复:发出一次或多次更新前暂停一个 Deployment,然后再恢复它,这样就能在Deployment暂停期间进行多次修复工作,而不会发出不必要的 rollout。
1
2
3
4
# 暂停
kubectl rollout pause deployment/nginx-deployment
# 完成所有的更新操作命令后进行恢复
kubectl rollout resume deployment/nginx-deployment
  • 回滚:如上对一个Deployment的image做了更新,但是如果遇到更新失败或误更新等情况时可以对其进行回滚。
1
2
3
4
5
6
7
# 回滚之前先查看历史版本信息
kubectl rollout history deployment/nginx-deployment
# 回滚
kubectl rollout undo deployment/nginx-deployment

# 当然也可以指定版本号回滚至指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=<version_index>

3.2 scale命令

对一个Deployment、RS、StatefulSet进行扩/缩容。

1
2
3
4
5
# 扩容
kubectl scale deployment/nginx-deployment --replicas=5
# 如果是缩容,把对应的副本数设置的比当前的副本数小即可
# 另外,还可以针对当前的副本数目做条件限制,比如当前副本数是5则进行缩容至副本数目为3
kubectl scale --current-replicas=5 --replicas=3 deployment/nginx-deployment

3.3 autoscale命令

通过创建一个autoscaler,可以自动选择和设置在K8s集群中Pod的数量。

1
2
# 基于CPU的使用率创建3-10个pod
kubectl autoscale deployment/nginx-deployment --min=3 --max=10 --cpu_percent=80

3.4replace更新替换资源

1
2
3
4
5
6
replace命令用于对已有资源进行更新、替换。如前面create中创建的nginx,当我们需要更新resource的一些属性的时候,如果修改副本数量,增加、修改label,更改image版本,修改端口等。都可以直接修改原yaml文件,然后执行replace命令。
注:名字不能被更新。另外,如果是更新label,原有标签的pod将会与更新label后的rc断开联系,有新label的rc将会创建指定副本数的新的pod,但是默认并不会删除原来的pod。所以此时如果使用get po将会发现pod数翻倍,进一步check会发现原来的pod已经不会被新rc控制,此处只介绍命令不详谈此问题,好奇者可自行实验。

kubectl replace -f deployment.yml
加个参数--force 先删除后再部署
kubectl replace --force -f deployment.yml

4. 集群管理命令

4.1 cordon & uncordon命令

设置是否能够将pod调度到该节点上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
先查看节点当前信息
kubectl get pod -n kube-system -o wide
# 不可调度
kubectl cordon k8snode1
此时节点信息会发生变化
NAME STATUS ROLES AGE VERSION
k8smaster Ready master 140m v1.18.4
k8snode1 Ready,SchedulingDisabled <none> 131m v1.18.4
k8snode2 Ready <none> 131m v1.18.4
#看到k8snode1状态发了了变化

# 当某个节点需要维护时,可以驱逐该节点上的所有pods(会删除节点上的pod,并且自动通过上面命令设置
# 该节点不可调度,然后在其他可用节点重新启动pods)
kubectl drain k8snode1

# 待其维护完成后,可再设置该节点为可调度
kubectl uncordon k8snode1
#这时候k8snode1状态恢复成Ready了

4.2 taint命令

目前仅能作用于节点资源,一般这个命令通常会结合pod的tolerations字段结合使用,对于没有设置对应toleration的pod是不会调度到有该taint的节点上的,这样就可以避免pod被调度到不合适的节点上。一个节点的taint一般会包括key、value和effect(effect只能在NoSchedule, PreferNoSchedule, NoExecute中取值)。

1
2
3
4
# 设置taint
kubectl taint nodes k8snode1 key1=value1:NoSchedule
# 移除taint
kubectl taint nodes k8snode1 key1:NoSchedule

如果pod想要被调度到上述设置了taint的节点node-0上,则需要在该pod的spec的tolerations字段设置:

1
2
3
4
5
6
7
8
9
10
11
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"

# 或者
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoSchedule"

5. 其它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 映射端口允许外部访问
kubectl expose deployment/nginx-deployment --type='NodePort' --port=80
# 然后通过kubectl get services -o wide来查看被随机映射的端口我的是80:32118/TCP
# 如此就可以通过node的外部IP和端口来访问nginx服务了,
http://192.168.224.12:32118/
# 转发本地端口访问Pod的应用服务程序
kubectl port-forward nginx-deployment-89bc67794-6vsc7 8090:80
# 如此,本地可以访问:curl -i localhost:8090

# 在创建或启动某些资源的时候没有达到预期结果,可以使用如下命令先简单进行故障定位
kubectl describe deployment/nginx-deployment
kubectl logs nginx-deployment-89bc67794-6vsc7
kubectl exec deployment/nginx-deployment -c nginx <command>

kubectl exec deployment/nginx-deployment ls /etc/nginx
# 集群内部调用接口(比如用curl命令),可以采用代理的方式,根据返回的ip及端口作为baseurl
kubectl proxy &

# 查看K8s支持的完整资源列表
kubectl api-resources

# 查看K8s支持的api版本
kubectl api-versions

部署管理k8s

一.k8s部署第一个pod应用

Pod的状态描述

1
2
3
4
5
6
状态值        描述
Pending API Server已经创建该Pod,但在Pod内还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程。
Runnung Pod内所有容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态。
Succeeded Pod内所有容器均成功执行后退出,且不会再重启。
Failed Pod内所有容器均已退出,但至少有一个容器退出为失败状态。
Unknown 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致。

1.编写一个pod.yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports:
- containerPort: 5000

然后通过命令创建

1
2
3
kubectl create -f pod.yaml

pod "kube100-site" created

然后我们就可以使用我们前面比较熟悉的 kubectl 命令来查看 POD 的状态了:

1
2
3
4
5
$ kubectl get pods -o wide 

查看到了ip 可以分别访问80端口和5000端口
curl -i 10.244.2.17
curl -i 10.244.2.17:5000

如果有问题也可以删除

1
kubectl delete -f pod.yaml

2.API部署例子

我们使用kubectl run来运行我们的第一个应用 ,run命令用于新建一个部署。我们需要提供部署名称和应用镜像地址(DockerHub以外的镜像需要全路径)作为参数。通过–port参数,还可以指定app使用的端口。

1
2
3
4
5
6
7

kubectl run kubernetes-bootcamp --image=docker.io/jocatalin/kubernetes-bootcamp:v1 --port=8080

具体后台的操作包括:
查找适合这个应用运行的node
调度这个应用在选定的node上运行
配置集群,在需要的时候为这个应用调配新的node

查看应用部署情况。

1
2
3
kubectl get deployments

kubectl get pods

查看应用
在Kubernetes中,pod运行于私有的、隔离的网络。默认情况下,Pod对集群内的其他pod和服务是可见的,但对网络外部是不可见的。我们在使用kubectl时,实际上是通过API端点(endpoint)(可以理解为URL)与应用进行交互。
通过kubectl proxy可以创建一个代理,让你能与集群内的私有网络进行通讯。代理运行过程中没有任何输出,按Ctrl+C可以关闭代理程序。
重新打开一个终端运行代理程序

1
kubectl proxy

这样就建立了一条从本地主机到集群的连接,代理程序允许从终端直接访问API。通过代理端点,你可以查看所有的API,端点地址是:http://localhost:8001 。你可以通过curl命令直接查看:

1
curl http://localhost:8001/version
1
2
3
4
5
6
7
8
9
10
API服务器自动为每个pod建立同名的API端点,而且这些端点可以通过代理访问。
首先获取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
Name of the Pod: kubernetes-bootcamp-390780338-x81xj

然后向pod中运行的应用发送一个http请求,这个url就指向了Pod的API。
$ curl http://localhost:8001/api/v1/proxy/namespaces/default/pods/kubernetes-bootcamp-5d7f968ccb-n5vqb/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-390780338-x81xj | v=1
注意:命令行中的参数“kubernetes-bootcamp-5d7f968ccb-dtdv5”,要从“echo POD_NAME”中获得。

二.k8s部署RC和RS应用

Replication Controller

RC是定义一个期望的场景,声明某种Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分:
Pod期待的副本数量。
用于筛选目标PodLabel Selector
Pod的副本数量小于预期数量时,用于创建新PodPod模板(templete)。

Replication Controller简称RC,RC是Kubernetes系统中的核心概念之一,简单来说,RC可以保证在任意时间运行Pod的副本数量,能够保证Pod总是可用的。如果实际Pod数量比指定的多那就结束掉多余的,如果实际数量比指定的少就新启动一些Pod,当Pod失败、被删除或者挂掉后,RC都会去自动创建新的Pod来保证副本数量,所以即使只有一个Pod,我们也应该使用RC来管理我们的Pod。

现在我们来使用RC来管理我们前面使用的Nginx的Pod,YAML文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: ReplicationController
metadata:
name: rc-demo
labels:
name: rc
spec:
replicas: 3
selector: #可选
name: rc
template:
metadata:
labels:
name: rc
spec:
containers:
- name: nginx-demo
image: nginx
ports:
- containerPort: 80

上面的YAML文件相对于我们之前的Pod的格式:
kind:ReplicationController
spec.replicas: 指定Pod副本数量,默认为1
spec.selector: RC通过该属性来筛选要控制的Pod
spec.template: 这里就是我们之前的Pod的定义的模块,但是不需要apiVersion和kind了
spec.template.metadata.labels: 注意这里的Pod的labels要和spec.selector相同,这样RC就可以来控制当前这个Pod了。
这个YAML文件中的意思就是定义了一个RC资源对象,它的名字叫rc-demo,保证一直会有3个Pod运行,Pod的镜像是nginx镜像。

1
2
注意spec.selector和spec.template.metadata.labels这两个字段必须相同,否则会创建失败的,当然我们也可以不写spec.selector,这样就默认与Pod模板中的metadata.labels相同了。
然后我们来创建上面的RC对象(保存为 rc-demo.yaml):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kubectl create -f rc-demo.yaml
查看RC:
kubectl get rc

查看具体信息:
kubectl describe rc rc-demo

然后我们通过RC来修改下Pod的副本数量为2:
kubectl apply -f rc-demo.yaml
或者
kubectl edit rc rc-demo

最新的k8s貌似已经不支持rolling-update命令了
而且我们还可以用RC来进行滚动升级,比如我们将镜像地址更改为nginx:1.7.9:
kubectl rolling-update rc-demo --image=nginx:1.7.9
但是如果我们的Pod中多个容器的话,就需要通过修改YAML文件来进行修改了:
kubectl rolling-update rc-demo -f rc-demo.yaml
如果升级完成后出现了新的问题,想要一键回滚到上一个版本的话,使用RC只能用同样的方法把镜像地址替换成之前的,然后重新滚动升级。

Replication Set(RS)

Replication Set简称RS,随着Kubernetes的高速发展,官方已经推荐我们使用RS和Deployment来代替RC了,实际上RS和RC的功能基本一致,目前唯一的一个区别就是RC只支持基于等式的selector(env=dev或environment!=qa),但RS还支持基于集合的selector(version in (v1.0, v2.0)),这对复杂的运维管理就非常方便了。

kubectl命令行工具中关于RC的大部分命令同样适用于我们的RS资源对象。不过我们也很少会去单独使用RS,它主要被Deployment这个更加高层的资源对象使用,除非用户需要自定义升级功能或根本不需要升级Pod,在一般情况下,我们推荐使用Deployment而不直接使用Replica Set。

最后总结下关于RC/RS的一些特性和作用吧:

大部分情况下,我们可以通过定义一个RC实现的Pod的创建和副本数量的控制
RC中包含一个完整的Pod定义模块(不包含apiversion和kind)
RC是通过label selector机制来实现对Pod副本的控制的
通过改变RC里面的Pod副本数量,可以实现Pod的扩缩容功能
通过改变RC里面的Pod模板中镜像版本,可以实现Pod的滚动升级功能(但是不支持一键回滚,需要用相同的方法去修改镜像地址)

三.k8s部署deployment和SVC

1.Deployment

Deployment同样也是Kubernetes系统的一个核心概念,主要职责和RC一样的都是保证Pod的数量和健康,二者大部分功能都是完全一致的,我们可以看成是一个升级版的RC控制器,那Deployment又具备那些新特性呢?

RC的全部功能:Deployment具备RC的全部功能
事件和状态查看:可以查看Deployment的升级详细进度和状态
回滚:当升级Pod的时候如果出现问题,可以使用回滚操作回滚到之前的任一版本
版本记录:每一次对Deployment的操作,都能够保存下来,这也是保证可以回滚到任一版本的基础
暂停和启动:对于每一次升级都能够随时暂停和启动

作为对比,我们知道Deployment作为新一代的RC,不仅在功能上更为丰富了,同时我们也说过现在官方也都是推荐使用Deployment来管理Pod的,比如一些官方组件kube-dns、kube-proxy也都是使用的Deployment来管理的,所以当大家在使用的使用也最好使用Deployment来管理Pod。

可以看出一个Deployment拥有多个Replica Set,而一个Replica Set拥有一个或多个Pod。一个Deployment控制多个RS主要是为了支持回滚机制,每当Deployment操作时,Kubernetes会重新生成一个Replica Set并保留,以后有需要的话就可以回滚至之前的状态。
下面创建一个Deployment,它创建了一个Replica Set来启动3个nginx pod,yaml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deploy
labels:
k8s-app: nginx-demo
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

这里需要注意:

1
2
apiVersion: extensions/v1beta1 
kind: Deployment

此部署版本已被弃用,并且在k8s的最新版本(例如1.16)中不再可用。

现在,您必须指定apiVersion: apps/v1并略微修改模板(需要selector.matchLabels字段)。并且与Pod模板中的metadata.labels相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
k8s-app: nginx-demo
spec:
selector: #新增这个字段
matchLabels:
app: nginx #名字和下面的pod模板的labels名相同
replicas: 3
template:
metadata:
labels:
app: nginx #和这里的相同
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

将上面内容保存为: nginx-deployment.yaml,执行命令:

1
2
3
4
5
$ kubectl create -f nginx-deployment.yaml
deployment "nginx-deploy" created

查看deployment
$ kubectl get deployments

可以看到Deployment已经创建了1个Replica Set了,执行下面的命令查看rs和pod:

1
2
3
$ kubectl get rs

$ kubectl get pod --show-labels

上面的Deployment的yaml文件中的replicas:3将会保证我们始终有3个POD在运行

下面是通过Deployment部署的

方法一:通过控制台部署。

部署Nginx到 K8S集群中:
进入 控制台-> 工作负载-> 点击链接“➕创建”位于网页左上角 。复制如下代码来从云服务器自动下载nginx镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

方法二:通过命令行部署

1
2
1. 将内容保存为 yml文件。
2. 执行命令:kubectl create -f deployment.yml --save-config

在Nginx部署完成后,在Master执行下面的命令,确保docker有部署到集群上

1
2
3
4
5
6
7
8
9
10
11
12
kubectl get pods -o wide 

可以内部访问
curl -I 10.244.1.3

通过leber查看pod的详细情况
kubectl get pods -o wide -l app=nginx

删除应用
kubectl delete pods -l app=nginx
这里删除后马上又会开启两个.
kubectl delete pods nginx-deployment-5bf87f5f59-8vbtk

aw2QmV.png

查看应用部署情况

1
2
3
4
kubectl get deployments 

查看节点IP
kubectl get svc
1
2
将Docker中的虚拟机暴露在网络中
kubectl expose deployment nginx-deployment --type="LoadBalancer"

查看Docker镜像在主节点的映射端口。本例中:镜像为80端口,映射到主节点的32118端口上。

1
kubectl get services

查看资源的详细信息

1
kubectl describe pods nginx

2.Service

Service是一种抽象的对象,它定义了一组Pod的逻辑集合和一个用于访问它们的策略,一个Serivce下面包含的Pod集合一般是由Label Selector来决定的。假如我们后端运行了3个副本,这些副本都是可以替代的,因为前端并不关心它们使用的是哪一个后端服务。尽管由于各种原因后端的Pod集合会发生变化,但是前端却不需要知道这些变化,也不需要自己用一个列表来记录这些后端的服务,Service的这种抽象就可以帮我们达到这种解耦的目的。

三种ip

1
2
3
Node IP:Node节点的IP地址
Pod IP:Pod的IP地址
Cluster IP:Service的IP地址

首先,Node IP是Kubernetes集群中节点的物理网卡IP地址(一般为内网),所有属于这个网络的服务器之间都可以直接通信,所以Kubernetes集群外要想访问Kubernetes集群内部的某个节点或者服务,肯定得通过Node IP进行通信(这个时候一般是通过外网IP了)

然后Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的(我们这里使用的是flannel这种网络插件保证所有节点的Pod IP不会冲突)

最后Cluster IP是一个虚拟的IP,仅仅作用于Kubernetes Service这个对象,由Kubernetes自己来进行管理和分配地址,当然我们也无法ping这个地址,他没有一个真正的实体对象来响应,他只能结合Service Port来组成一个可以通信的服务。

定义Service

定义Service的方式和各种资源对象的方式类型一样,假定我们有一组Pod服务,它们对外暴露了 80 端口,同时都被打上了app=myapp这样的标签,那么我们就可以像下面这样来定义一个Service对象:
pod示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
selector:
matchLabels:
app: myapp
replicas: 3
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

service基于pod的示例:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80

然后通过的使用kubectl create -f myservice.yaml就可以创建一个名为myservice的Service对象,它会将请求代理到使用 TCP 端口为 80,具有标签app=myapp的Pod上,这个Service会被系统分配一个我们上面说的Cluster IP,该Service还会持续的监听selector下面的Pod,会把这些Pod信息更新到一个名为myservice的Endpoints对象上去,这个对象就类似于我们上面说的Pod集合了。
需要注意的是,Service能够将一个接收端口映射到任意的targetPort。 默认情况下,targetPort将被设置为与port字段相同的值。 可能更有趣的是,targetPort 可以是一个字符串,引用了 backend Pod 的一个端口的名称。 因实际指派给该端口名称的端口号,在每个 backend Pod 中可能并不相同,所以对于部署和设计 Service ,这种方式会提供更大的灵活性。
另外Service能够支持 TCP 和 UDP 协议,默认是 TCP 协议。

kube-proxy

Kubernetes集群中,每个Node会运行一个kube-proxy进程, 负责为Service实现一种 VIP(虚拟 IP,就是我们上面说的clusterIP)的代理形式,现在的Kubernetes中默认是使用的iptables这种模式来代理。这种模式,kube-proxy会监视Kubernetes master对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会添加上 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某一个个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod。

默认的策略是,随机选择一个 backend。 我们也可以实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 “ClientIP” (默认值为 “None”)。

另外需要了解的是如果最开始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖 readiness probes。

Service 类型

在定义Service的时候可以指定一个自己需要的类型的Service,如果不指定的话默认是ClusterIP类型。

可以使用的服务类型如下:

1、ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的ServiceType。
2、NodePort:通过每个 Node 节点上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 :,可以从集群的外部访问一个 NodePort 服务。
3、LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务,这个需要结合具体的云厂商进行操作。
4、ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。

NodePort 类型

如果设置 type 的值为 “NodePort”,Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 Service。该端口将通过 Service 的 spec.ports[*].nodePort 字段被指定,如果不指定的话会自动生成一个端口。

需要注意的是,Service 将能够通过 :spec.ports[].nodePort 和 spec.clusterIp:spec.ports[].port 而对外可见。

接下来创建一个NodePort的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
selector:
app: myapp
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 80
name: myapp-http
nodePort: 32560

创建该Service:

1
2
3
$ kubectl create -f service-demo.yaml

注意上面有创建myservice服务的需要删掉

然后我们可以查看Service对象信息:

1
2
3
4
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 22h
myservice NodePort 10.100.114.118 <none> 80:32560/TCP 8s

可以看到myservice的 TYPE 类型已经变成了NodePort,后面的PORT(S)部分也多了一个 32560 的随机映射端口。

ExternalName

ExternalName 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

1
2
3
4
5
6
7
8
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com

当查询主机 my-service.prod.svc.cluster.local 时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type,完全不需要修改调用的代码,这样就完全解耦了。

3.1.网站服务应用

在控制台中创建

应用一

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: result
spec:
type: NodePort
ports:
- name: "result-service"
port: 5001
targetPort: 80
nodePort: 31001
selector:
app: result

应用二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: result
spec:
replicas: 1
template:
metadata:
labels:
app: result
spec:
containers:
- image: dockersamples/examplevotingapp_result:before
name: result

查看是否运行

1
kubectl get pods

四.k8s部署PV的应用

概念

PV 的全称是:PersistentVolume(持久化卷),是对底层的共享存储的一种抽象,PV 由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS 等,都是通过插件机制完成与共享存储的对接。

NFS方式

1、关闭防火墙

1
2
$ systemctl stop firewalld.service
$ systemctl disable firewalld.service

2、安装配置 nfs

1
$ yum -y install nfs-utils rpcbind

3、共享目录设置权限:

1
2
 mkdir /data/k8s -p
$ chmod 755 /data/k8s/

4、配置 nfs,nfs 的默认配置文件在 /etc/exports 文件下,在该文件中添加下面的配置信息:

1
2
$ vim /etc/exports
/data/k8s *(rw,sync,no_root_squash)

5、配置说明:
/data/k8s:是共享的数据目录
*:表示任何人都有权限连接,当然也可以是一个网段,一个 IP,也可以是域名
rw:读写的权限
sync:表示文件同时写入硬盘和内存
no_root_squash:当登录 NFS 主机使用共享目录的使用者是 root 时,其权限将被转换成为匿名使用者,通常它的 UID 与 GID,都会变成 nobody 身份

启动服务 nfs 需要向 rpc 注册,rpc 一旦重启了,注册的文件都会丢失,向他注册的服务都需要重启
注意启动顺序,先启动 rpcbind

1
2
3
4
5
 systemctl start rpcbind.service
systemctl enable rpcbind

systemctl status rpcbind
状态信息是正在运行的

看到上面的 Started 证明启动成功了。

然后启动 nfs 服务:

1
2
3
systemctl start nfs.service
systemctl enable nfs
systemctl status nfs

同样看到 Started 则证明 NFS Server 启动成功了。

另外还可以通过下面的命令确认下:

1
2
3
4
5
6
7
$ rpcinfo -p|grep nfs
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
100227 3 tcp 2049 nfs_acl
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
100227 3 udp 2049 nfs_acl

查看具体目录挂载权限:

1
2
$ cat /var/lib/nfs/etab
/data/k8s *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,secure,no_root_squash,no_all_squash)

到这里nfs server就安装成功了,接下来我们在节点192.168.224.11和12上来安装 nfs 的客户端来验证下 nfs

安装 nfs 当前也需要先关闭防火墙:

1
2
systemctl stop firewalld.service
systemctl disable firewalld.service

然后安装 nfs

1
yum -y install nfs-utils rpcbind

安装完成后,和上面的方法一样,先启动 rpc、然后启动 nfs:

1
2
3
4
systemctl start rpcbind.service 
systemctl enable rpcbind.service
systemctl start nfs.service
systemctl enable nfs.service

挂载数据目录 客户端启动完成后,我们在客户端来挂载下 nfs 测试下:
首先检查下 nfs 是否有共享目录:

1
2
3
4
showmount -e 192.168.224.10

Export list for 192.168.224.10:
/data/k8s *

然后我们在客户端上新建目录:

1
$ mkdir /data

将 nfs 共享目录挂载到上面的目录:

1
mount -t nfs 192.168.224.10:/data/k8s /data

挂载成功后,在客户端上面的目录中新建一个文件,然后我们观察下 nfs 服务端的共享目录下面是否也会出现该文件:

1
$ touch /data/test.txt

然后在 nfs 服务端查看:

1
2
3
$ ls -ls /data/k8s/
total 4
0 -rw-r--r-- 1 root root 0 624 02:41 test.txt

如果上面出现了 test.txt 的文件,那么证明我们的 nfs 挂载成功了。

PV

有了上面的 NFS 共享存储,下面我们就可以来使用 PV 和 PVC 了。PV 作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略等关键信息,下面我们来新建一个 PV 对象,使用 nfs 类型的后端存储,1G 的存储空间,访问模式为 ReadWriteOnce,回收策略为 Recyle,对应的 YAML 文件如下:(pv1-demo.yaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /data/k8s
server: 192.168.224.10

Kubernetes 支持的 PV 类型有很多,比如常见的 Ceph、GlusterFs、NFS,甚至 HostPath也可以,不过 HostPath 仅仅可以用于单机测试,更多的支持类型可以前往 Kubernetes PV 官方文档进行查看,因为每种存储类型都有各自的特点,所以我们在使用的时候可以去查看相应的文档来设置对应的参数。

然后同样的,直接使用 kubectl 创建即可:

1
2
3
4
5
6
$ kubectl create -f pv1-demo.yaml
persistentvolume "pv1" created

$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Available

我们可以看到 pv1 已经创建成功了,状态是 Available,表示 pv1 就绪,可以被 PVC 申请。我们来分别对上面的属性进行一些解读。

capacity(存储能力)

一般来说,一个 PV 对象都要指定一个存储能力,通过 PV 的 capacity属性来设置的,目前只支持存储空间的设置,就是我们这里的 storage=1Gi,不过未来可能会加入 IOPS、吞吐量等指标的配置。

accessModes(访问模式)

AccessModes 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

  • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
  • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
  • ReadWriteMany(RWX):读写权限,可以被多个节点挂载

注意:一些 PV 可能支持多种访问模式,但是在挂载的时候只能使用一种访问模式,多种访问模式是不会生效的。

下图是一些常用的 Volume 插件支持的访问模式:

awRAnx.png

persistentVolumeReclaimPolicy(回收策略)

我这里指定的 PV 的回收策略为 Recycle,目前 PV 支持的策略有三种:

  • Retain(保留)- 保留数据,需要管理员手工清理数据
  • Recycle(回收)- 清除 PV 中的数据,效果相当于执行 rm -rf /thevoluem/*
  • Delete(删除)- 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务,比如 ASW EBS。

不过需要注意的是,目前只有 NFS 和 HostPath 两种类型支持回收策略。当然一般来说还是设置为 Retain 这种策略保险一点。

status(状态)

一个 PV 的生命周期中,可能会处于4中不同的阶段:

  • Available(可用):表示可用状态,还未被任何 PVC 绑定
  • Bound(已绑定):表示 PV 已经被 PVC 绑定
  • Released(已释放):PVC 被删除,但是资源还未被集群重新声明
  • Failed(失败): 表示该 PV 的自动回收失败

五.k8s部署PVC的应用

概念

PVC 的全称是:PersistentVolumeClaim(持久化卷声明),PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 和内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。

准备工作

在使用 PVC 之前,我们还得把其他节点上的 nfs 客户端给安装上,比如我们这里:

awREB6.png

需要在所有节点安装 nfs 客户端程序,必须在所有节点都安装 nfs 客户端,否则可能会导致 PV 挂载不上的问题。
安装命令如下:

1
yum -y install  nfs-utils rpcbind

新建 PVC

同样的,我们来新建一个数据卷声明,来请求 1Gi 的存储容量,访问模式也是 ReadWriteOnce,YAML 文件如下:(pvc-nfs.yaml)

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

可以看到这里的声明方法几乎和新建 PV 是一样的,在新建 PVC 之前,可以看下之前创建的 PV 的状态:

1
2
3
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Available 22m

可以看到当前 pv1 是在 Available 的一个状态,所以这个时候 PVC 可以和这个 PV 进行绑定:

1
2
3
4
5
$ kubectl create -f pvc-nfs.yaml
persistentvolumeclaim "pvc-nfs" created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv1 1Gi RWO 10s

可以看到 pvc-nfs 创建成功了,状态是 Bound 状态了,这个时候再看下 PV 的状态呢:

1
2
3
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Bound default/pvc-nfs 24m

可以看到 PV 也是 Bound 状态了,对应的声明是 default/pvc-nfs,就是 default 命名空间下面的 pvc-nfs,证明刚刚新建的 pvc-nfs 和 pv-nfs 绑定成功了。

提问:并没有在 pvc-nfs 中指定关于 pv 的什么标志,它们之间是怎么就关联起来了的呢?
解答:其实这是系统自动帮我们去匹配的,它会根据我们的声明要求去查找处于 Available 状态的 PV,如果没有找到的话那么PVC 就会一直处于 Pending 状态,找到了的话当然就会把当前的 PVC 和目标 PV 进行绑定,这个时候状态就会变成 Bound 状态了。

使用 PVC

使用之前的 nginx 的镜像来测试下:(nfs-pvc-deploy.yaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-pvc
spec:
selector: #新增这个字段
matchLabels:
app: nfs-pvc
replicas: 3
template:
metadata:
labels:
app: nfs-pvc
spec:
containers:
- name: nginx
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts: #挂载容器中的目录到pvc nfs中的目录
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
persistentVolumeClaim: #指定pvc
claimName: pvc-nfs

---
apiVersion: v1
kind: Service
metadata:
name: nfs-pvc
labels:
app: nfs-pvc
spec:
type: NodePort
ports:
- port: 80
targetPort: web #容器端口或名字
selector:
app: nfs-pvc

这里使用 nginx 镜像,将容器的 /usr/share/nginx/html 目录通过 volume 挂载到名为 pvc-nfs 的 PVC 上面,然后创建一个 NodePort 类型的 Service 来暴露服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl create -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" created
service "nfs-pvc" created

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-pvc-588c7b9b5d-h7r2s 1/1 Running 0 34s
nfs-pvc-588c7b9b5d-hss56 1/1 Running 0 34s
nfs-pvc-588c7b9b5d-k2t8z 1/1 Running 0 34s

$ kubectl get svc
AME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25h
nfs-pvc NodePort 10.99.223.226 <none> 80:30061/TCP 75s

然后就可以通过任意节点的 IP:30061 端口来访问这里的 Nginx 服务了,但是这个时候访问会出现403,这是为什么?我们再去看看 nfs 共享数据目录下面有没有数据呢?

1
ls /data/k8s

发现并没有任何数据,这是因为我们把容器目录/user/share/nginx/html和挂载到了pvc-nfs这个 PVC 上面,这个 PVC 就是对应着我们上面的 nfs 的共享数据目录的,该目录下面还没有任何数据,所以我们访问就出现了403,现在我们在/data/k8s这个目录下面新建一个 index.html 的文件:

1
2
3
 echo "<h1>Hello Kubernetes~</h1>" >> /data/k8s/index.html
ls /data/k8s/
index.html

可以看到共享数据目录中已经有一个 index.html 的文件了,由于我们挂载了 pvc2-nfs 到上面的 nginx 容器中去,是不是这个时候容器目录**/user/share/nginx/html下面也有index.html**这个文件了啊?所以这个时候我们再来访问下服务,任一节点IP:30061

awRQgA.png

现在是不是正常了啊,但是我们可以看到我们容器中的数据是直接放到共享数据目录根目录下面的,如果以后有一个新的 nginx 容器也做了数据目录的挂载,会发生冲突,所以这个时候就不太好区分了,可以在 Pod 中使用一个新的属性:subPath,该属性可以来解决这个问题,只需要更改上面的 Pod 的 YAML 文件即可:

创建pvc子目录

1
2
3
4
volumeMounts:
- name: www
subPath: nginxpvc-test
mountPath: /usr/share/nginx/html

更改完 YAML 文件后,我们重新更新即可:

1
2

kubectl apply -f nfs-pvc-deploy.yaml

awR8DP.png

更新完后,我们再去看看 nfs 的数据共享目录:

这个时候是把nginxpvc-test这个目录映射到容器里的/usr/share/nginx/html/目录里了

1
2
3
4
5
6
 ls /data/k8s/
index.html nginxpvc-test

ls /data/k8s/nginxpvc-test/

echo "<h1>Hello yichen~</h1>" >> /data/k8s/nginxpvc-test/index.html

这时候可以再去访问了。

查看在什么节点,然后可以进去容器看看

1
2
3
kubectl get pods  -o wide

docker exec -it k8s_nginx_nfs-pvc-78d847877b-kxhh4_default_4266e /bin/bash

5.1.部署mysql持久化卷

部署mysql持久化卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi

部署MySQL服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.7
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: password # 改成自己实际的密码
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim

创建

1
kubectl create -f  mysql.yaml

查看deployment mysql 资源详细信息

1
kubectl describe deployment mysql

确认下服务是否按装完毕

1
kubectl get pods -l app=mysql

查看mysql-pv-claim 资源详细信息

1
kubectl describe pvc mysql-pv-claim

通过虚拟机宿主机登陆mysql镜像

1
kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

评论


:D 一言句子获取中...

加载中,最新评论有1分钟缓存...