【谷粒商城 - k8s、devOps专栏】
创始人
2024-02-20 03:49:20

一、K8s快速入门

其他项目的K8S笔记:https://blog.csdn.net/hancoder/category_11140481.html

1)简介

kubernetes简称k8s。是用于自动部署,扩展和管理容器化应用程序的开源系统。

部署方式的进化:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7TFGxran-1633940167403)(https://d33wubrfki0l68.cloudfront.net/26a177ede4d7b032362289c6fccd448fc4a91174/eb693/images/docs/container_evolution.svg)]

传统部署时代:

早期,各个组织机构在物理服务器上运行应用程序。无法为物理服务器中的应用程序定义资源边界,这会导致资源分配问题。 例如,如果在物理服务器上运行多个应用程序,则可能会出现一个应用程序占用大部分资源的情况, 结果可能导致其他应用程序的性能下降。 一种解决方案是在不同的物理服务器上运行每个应用程序,但是由于资源利用不足而无法扩展, 并且维护许多物理服务器的成本很高。

虚拟化部署时代:

作为解决方案,引入了虚拟化。虚拟化技术允许你在单个物理服务器的 CPU 上运行多个虚拟机(VM)。 虚拟化允许应用程序在 VM 之间隔离,并提供一定程度的安全,因为一个应用程序的信息 不能被另一应用程序随意访问。

虚拟化技术能够更好地利用物理服务器上的资源,并且因为可轻松地添加或更新应用程序 而可以实现更好的可伸缩性,降低硬件成本等等。

每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。

容器部署时代:

容器类似于 VM,但是它们具有被放宽的隔离属性,可以在应用程序之间共享操作系统(OS)。 因此,容器被认为是轻量级的。容器与 VM 类似,具有自己的文件系统、CPU、内存、进程空间等。 由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。

容器因具有许多优势而变得流行起来。下面列出的是容器的一些好处:

  • 敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
  • 持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性),支持可靠且频繁的 容器镜像构建和部署。
  • 关注开发与运维的分离:在构建/发布时而不是在部署时创建应用程序容器镜像, 从而将应用程序与基础架构分离。
  • 可观察性不仅可以显示操作系统级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
  • 跨开发、测试和生产的环境一致性:在便携式计算机上与在云中相同地运行。
  • 跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、 Google Kubernetes Engine 和其他任何地方运行。
  • 以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
  • 松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分, 并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
  • 资源隔离:可预测的应用程序性能。
  • 资源利用:高效率和高密度。

ok不多介绍了,还是好好听课后看我k8s的专栏吧

2)架构

参考其他笔记:https://blog.csdn.net/hancoder/category_11140481.html

(2)master节点架构

kube-apiserver

  • 对外暴露K8S的、接口,是外界进行资源操作的唯一入口
  • 提供认证、授权、访问控制、API注册和发现等机制

etcd

  • 是兼具一致性和高可用性的键值数据库,可以作为保存Kubernetes所有集群数据的后台数据库。
  • Kubernetes集群的数据库通常需要有个备份计划

kube-scheduler

  • 主节点上的组件,该组件监视那些新创建的未指定运行节点的Pod,并选择节点让Pod在上面运行。
  • 所有对k8s的集群操作,都必须经过主节点进行调度

kube-controller-manager

  • 在主节点上运行控制器的组件
  • 这些控制器包括
    • 节点控制器(NodeController):负责在节点出现故障时进行通知和响应。
    • 副本控制器(ReplicationController):负责为系统中的每个副本控制器对象维护正确数量的Pod.
    • 端点控制器(EndpointsController):填充端点(Endpoints)对象(即加入service与Pod)
    • 服务帐户和令牌控制器(Service Account&Token Controllers).为新的命名空间创建默认帐户和API访可令牌

(3)Node节点架构

kubelet

  • 一个在集群中每个Node节点上运行的代理。它保证容器都运行在Pod中。
  • 负责维护容器的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理

kube-proxy

  • 负责为提供cluster内部的服务发现和负载均衡

容器运行环境(Container Runtime)

  • 容器运行环境是负责运行容器的软件。
  • Kubernetes支持多个容器运行环境.Docker、containerd、cri-o、rktlet以及任何实现Kubernetes CRI(容器运行环境接口)。

fluentd

  • 是一个守护进程,它有助于提供集群层面日志集群层面的日志

3)核心组件

可以参考别的项目的笔记,不在赘述:

https://blog.csdn.net/hancoder/article/details/117969869

https://blog.csdn.net/hancoder/article/details/118053399

4)快速体验

(1)安装minikube

https://github.com/kubernetes/minikube/releases
下载minikuber-windows-amd64.exe 改名为minikube.exe
打开virtualBox,打开cmd
运行
minikube start --vm-driver=virtualbox --registry-mirror=https://registry.docker-cn.com
等待20分钟即可。

(2)体验nginx部署升级

# 提交一个nginx deployment
kubectl apply -f https://k8s.io/examples/application/deployment.yaml
# 升级 nginx deployment
kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
# 扩容 nginx deployment

二、K8s集群安装

更详细的安装在别的文章里有https://blog.csdn.net/hancoder/article/details/118053239

看着安装即可,别问我白嫖该文档,不是谷粒的学习笔记

1)kubeadm

kubeadm是官方社区推出的一个用于快速部署kuberneters集群的工具。
这个工具能通过两条指令完成一个kuberneters集群的部署

创建一个master节点

$ kuberneters init
1

将一个node节点加入到当前集群中

$ kubeadm join 
1

2)前置要求

一台或多台机器,操作系统Centos7.x-86_x64
硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多
集群中所有的机器之间网络互通
可以访问外网,需要拉取镜像
禁止Swap分区

3)部署步骤

1. 在所有的节点上安装Docker和kubeadm
2. 部署Kubernetes Master
3. 部署容器网络插件
4. 部署Kubernetes Node,将节点加入Kubernetes集群中
5. 部署DashBoard web页面,可视化查看Kubernetes资源
12345

image-20200503144120720

4)环境准备

(1)准备工作

  • 使用vagrant快速创建三个虚拟机。虚拟机启动前先设置virtualbox的主机网络。现在全部统一为192.168.56.1,以后所有虚拟机都是56.x的ip地址。

image-20200503175351320

  • 在全局设定中,找到一个空间比较大的磁盘用用来存放镜像。

image-20200503180202640

网卡1是NAT,用于虚拟机与本机访问互联网。网卡2是仅主机网络,虚拟机内部共享的虚拟网络

(2)启动三个虚拟机

使用我们提供的vagrant文件,复制到非中文无空格目录下,运行vagrant up启动三个虚拟机。其实vagrant完全可以一键部署全部K8s集群
https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster
http://github.com/davidkbainbridge/k8s-playground

下面是vagrantfile,使用它来创建三个虚拟机,分别为k8s-node1,k8s-node2和k8s-node3.

Vagrant.configure("2") do |config|# 遍历(1..3).each do |i|config.vm.define "k8s-node#{i}" do |node|# 设置虚拟机的Boxnode.vm.box = "centos/7"# 设置虚拟机的主机名node.vm.hostname="k8s-node#{i}"# 设置虚拟机的IP  100 101 102node.vm.network "private_network", ip: "192.168.56.#{99+i}", netmask: "255.255.255.0"# 设置主机与虚拟机的共享目录# node.vm.synced_folder "~/Documents/vagrant/share", "/home/vagrant/share"# VirtaulBox相关配置node.vm.provider "virtualbox" do |v|# 设置虚拟机的名称v.name = "k8s-node#{i}"# 设置虚拟机的内存大小v.memory = 4096# 设置虚拟机的CPU个数v.cpus = 4endendend
end
  • 进入到三个虚拟机,开启root的密码访问权限
vagrant ssh xxx进入到系统后
# vagrant ssh k8s-node1
su root 密码为vagrantvi /etc/ssh/sshd_config修改
PermitRootLogin yes 
PasswordAuthentication yes所有的虚拟机设为4核4G
1234567891011
service sshd restart
192.68.56.100:22
网络配置

关于在"网络地址转换"的连接方式下,三个节点的eth0,IP地址相同的问题。

**问题描述:**查看k8s-node1的路由表:

# 查看默认的网卡
[root@k8s-node1 ~]# ip route show
default via 10.0.2.2 dev eth0 proto dhcp metric 100  # 默认使用eth010.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100  # 网关和ip192.168.56.0/24 dev eth1 proto kernel scope link src 192.168.56.100 metric 101 

能够看到路由表中记录的是,通过端口eth0进行数据包的收发。

分别查看k8s-node1,k8s-node2和k8s-node3的eth0所绑定的IP地址,发现它们都是相同的,全都是10.0.2.15,这些地址是供kubernetes集群通信用的;eth1上的IP地址,是通远程管理使用的。

[root@k8s-node1 ~]# ip addr
...
2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 52:54:00:8a:fe:e6 brd ff:ff:ff:ff:ff:ff# ipinet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0valid_lft 84418sec preferred_lft 84418secinet6 fe80::5054:ff:fe8a:fee6/64 scope link valid_lft forever preferred_lft forever
3: eth1:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:a3:ca:c0 brd ff:ff:ff:ff:ff:ff# ipinet 192.168.56.100/24 brd 192.168.56.255 scope global noprefixroute eth1valid_lft forever preferred_lft foreverinet6 fe80::a00:27ff:fea3:cac0/64 scope link valid_lft forever preferred_lft forever
[root@k8s-node1 ~]# 

**原因分析:**这是因为它们使用是端口转发规则,使用同一个地址,通过不同的端口来区分。但是这种端口转发规则在以后的使用中会产生很多不必要的问题,所以需要修改为NAT网络类型。

下面是临时解决方案:

image-20200503184536343

终极解决方法:

  • 选择三个节点,然后执行“管理”->“全局设定”->“网络”,添加一个NAT网络。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-endXaW6I-1669546538221)(https://fermhan.oss-cn-qingdao.aliyuncs.com/guli/image-20200503184919936.png)]

  • 分别修改每台设备的网络类型,并刷新重新生成MAC地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cRTL7kUB-1669546538222)(https://fermhan.oss-cn-qingdao.aliyuncs.com/guli/image-20200503185344246.png)]

刷新一下MAC地址

1网络是集群交互,2网络是宿主交互

  • 再次查看三个节点的IP
[root@k8s-node1 ~]# ip addr
。。。
2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:7e:dd:f5 brd ff:ff:ff:ff:ff:ff# 10.0.2.15inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0valid_lft 86357sec preferred_lft 86357secinet6 fe80::a00:27ff:fe7e:ddf5/64 scope linkvalid_lft forever preferred_lft 
===================================================
[root@k8s-node2 ~]# ip addr
。。。
2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:86:c0:a2 brd ff:ff:ff:ff:ff:ff# 10.0.2.5inet 10.0.2.5/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0valid_lft 527sec preferred_lft 527secinet6 fe80::a00:27ff:fe86:c0a2/64 scope linkvalid_lft forever preferred_lft forever=================================================
[root@k8s-node3 ~]# ip addr
。。。
2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:a1:94:f9 brd ff:ff:ff:ff:ff:ff# 10.0.2.6inet 10.0.2.6/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0valid_lft 518sec preferred_lft 518secinet6 fe80::a00:27ff:fea1:94f9/64 scope linkvalid_lft forever preferred_lft forever

技巧:MobaXterm上面的Multi-exec模式了解一下

(3)设置Linux环境(三个节点都执行)

  • 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
  • 关闭seLinux
# linux默认的安全策略
sed -i 's/enforcing/disabled/' /etc/selinux/config
setenforce 0
  • 关闭swap
swapoff -a #临时关闭
sed -ri 's/.*swap.*/#&/' /etc/fstab #永久关闭
free -g #验证,swap必须为0
  • 添加主机名与IP对应关系:

查看主机名:

hostname

如果主机名不正确,可以通过“hostnamectl set-hostname :指定新的hostname”命令来进行修改。

vi /etc/hosts
10.0.2.15 k8s-node1
10.0.2.5 k8s-node2
10.0.2.6 k8s-node3

差不多就这个意思,我的ip可能在后来也改了

将桥接的IPV4流量传递到iptables的链:

cat > /etc/sysctl.d/k8s.conf <

应用规则:

sysctl --system

疑难问题:遇见提示是只读的文件系统,运行如下命令

mount -o remount rw /
  • date 查看时间(可选)
yum -y install ntpupdatentpupdate time.window.com #同步最新时间

5)所有节点安装docker、kubeadm、kubelet、kubectl

Kubenetes默认CRI(容器运行时)为Docker,因此先安装Docker。

(1)安装Docker

1、卸载之前的docker

sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine

2、安装Docker -CE

sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
# 设置docker repo的yum位置
sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo# 安装docker,docker-cli
sudo yum -y install docker-ce docker-ce-cli containerd.io   

3、配置docker加速

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{"registry-mirrors": ["https://ke9h1pt4.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

4、启动Docker && 设置docker开机启动

systemctl enable docker

基础环境准备好,可以给三个虚拟机备份一下;

image-20200503192940651

(2)添加阿里与Yum源

cat < /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
EOF

更多详情见: https://developer.aliyun.com/mirror/kubernetes

(3)安装kubeadm,kubelet和kubectl

yum list|grep kube

安装

yum install -y kubelet-1.17.3 kubeadm-1.17.3 kubectl-1.17.3

开机启动

systemctl enable kubelet && systemctl start kubelet

查看kubelet的状态:

systemctl status kubelet

查看kubelet版本:

[root@k8s-node2 ~]# kubelet --version
Kubernetes v1.17.3

6)部署k8s-master

(1)master节点初始化

在Master节点上,创建并执行master_images.sh

#!/bin/bashimages=(kube-apiserver:v1.17.3kube-proxy:v1.17.3kube-controller-manager:v1.17.3kube-scheduler:v1.17.3coredns:1.6.5etcd:3.4.3-0pause:3.1
)for imageName in ${images[@]} ; dodocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
#   docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName  k8s.gcr.io/$imageName
done

查看100的内部通信端口

2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:7e:dd:f5 brd ff:ff:ff:ff:ff:ffinet 10.0.2.4/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0valid_lft 512sec preferred_lft 512secinet6 fe80::a00:27ff:fe7e:ddf5/64 scope linkvalid_lft forever preferred_lft forever
初始化kubeadm
kubeadm init \
--apiserver-advertise-address=10.0.2.7 \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--kubernetes-version   v1.17.3 \
--service-cidr=10.96.0.0/16  \
--pod-network-cidr=10.244.0.0/16

注:

  • –apiserver-advertise-address=10.0.2.4 :这里的IP地址是master主机的地址,为上面的eth0网卡的地址;
  • pod-network-cidr:pod之间的访问

执行结果:

[root@k8s-node1 opt]# kubeadm init \
> --apiserver-advertise-address=10.0.2.15 \
> --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
> --kubernetes-version   v1.17.3 \
> --service-cidr=10.96.0.0/16  \
> --pod-network-cidr=10.244.0.0/16
W0503 14:07:12.594252   10124 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
[init] Using Kubernetes version: v1.17.3
[preflight] Running pre-flight checks[WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
。。。。。。。。。。。。。。。
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
#表示kubernetes已经初始化成功了
Your Kubernetes control-plane has initialized successfully!To start using your cluster, you need to run the following as a regular user:mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/configYou should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:https://kubernetes.io/docs/concepts/cluster-administration/addons/Then you can join any number of worker nodes by running the following on each as root:kubeadm join 10.0.2.15:6443 --token sg47f3.4asffoi6ijb8ljhq \--discovery-token-ca-cert-hash sha256:81fccdd29970cbc1b7dc7f171ac0234d53825bdf9b05428fc9e6767436991bfb 
# 上面他也说了如何加入新结点
# 如果过期了还没有加入,百度 kubeadm token过期

由于默认拉取镜像地址k8s.cr.io国内无法访问,这里指定阿里云仓库地址。可以手动按照我们的images.sh先拉取镜像。

地址变为:registry.aliyuncs.com/googole_containers也可以。

科普:无类别域间路由(Classless Inter-Domain Routing 、CIDR)是一个用于给用户分配IP地址以及在互联网上有效第路由IP数据包的对IP地址进行归类的方法。

拉取可能失败,需要下载镜像。

运行完成提前复制:加入集群的令牌。

(2)测试Kubectl(主节点执行)

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

详细部署文档:https://kubernetes.io/docs/concepts/cluster-administration/addons/

 kubectl get nodes #获取所有节点# 目前Master状态为notready。等待网络加入完成即可。journalctl -u kubelet #查看kubelet日志
12345
kubeadm join 10.0.2.4:6443 --token bt3hkp.yxnpzsgji4a6edy7 \--discovery-token-ca-cert-hash sha256:64949994a89c53e627d68b115125ff753bfe6ff72a26eb561bdc30f32837415a

7)安装POD网络插件(CNI)

在master节点上执行按照POD网络插件

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

以上地址可能被墙,可以直接获取本地已经下载的flannel.yml运行即可(https://blog.csdn.net/lxm1720161656/article/details/106436252 可以去下载),如:

[root@k8s-node1 k8s]# kubectl apply -f  kube-flannel.yml    
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created
[root@k8s-node1 k8s]#

同时flannel.yml中指定的images访问不到可以去docker hub找一个wget yml地址

vi 修改yml 所有amd64的地址修改了即可
等待大约3分钟
kubectl get pods -n kube-system 查看指定名称空间的pods
kubectl get pods -all-namespace 查看所有名称空间的pods$ ip link set cni0 down 如果网络出现问题,关闭cni0,重启虚拟机继续测试
执行watch kubectl get pod -n kube-system -o wide 监控pod进度
等待3-10分钟,完全都是running以后继续

查看命名空间:

[root@k8s-node1 k8s]# kubectl get ns
NAME              STATUS   AGE
default           Active   30m
kube-node-lease   Active   30m
kube-public       Active   30m
kube-system       Active   30m
[root@k8s-node1 k8s]#
1234567
[root@k8s-node1 k8s]# kubectl get pods --all-namespaces       
NAMESPACE     NAME                                READY   STATUS    RESTARTS   AGE
kube-system   coredns-546565776c-9sbmk            0/1     Pending   0          31m
kube-system   coredns-546565776c-t68mr            0/1     Pending   0          31m
kube-system   etcd-k8s-node1                      1/1     Running   0          31m
kube-system   kube-apiserver-k8s-node1            1/1     Running   0          31m
kube-system   kube-controller-manager-k8s-node1   1/1     Running   0          31m
kube-system   kube-flannel-ds-amd64-6xwth         1/1     Running   0          2m50s
kube-system   kube-proxy-sz2vz                    1/1     Running   0          31m
kube-system   kube-scheduler-k8s-node1            1/1     Running   0          31m
[root@k8s-node1 k8s]# 

查看master上的节点信息:

[root@k8s-node1 k8s]# kubectl get nodes
NAME        STATUS   ROLES    AGE   VERSION
k8s-node1   Ready    master   34m   v1.17.3   #status为ready才能够执行下面的命令
[root@k8s-node1 k8s]#

最后再次执行,并且分别在“k8s-node2”和“k8s-node3”上也执行这里命令:

kubeadm join 10.0.2.4:6443 --token bt3hkp.yxnpzsgji4a6edy7 \--discovery-token-ca-cert-hash sha256:64949994a89c53e627d68b115125ff753bfe6ff72a26eb561bdc30f32837415a
12
[root@k8s-node1 opt]# kubectl get nodes;
NAME        STATUS     ROLES    AGE   VERSION
k8s-node1   Ready      master   47m   v1.17.3
k8s-node2   NotReady      75s   v1.17.3
k8s-node3   NotReady      76s   v1.17.3
[root@k8s-node1 opt]# 

监控pod进度

# 在master执行
watch kubectl get pod -n kube-system -o wide

等到所有的status都变为running状态后,再次查看节点信息:

[root@k8s-node1 ~]#  kubectl get nodes;                         
NAME        STATUS   ROLES    AGE     VERSION
k8s-node1   Ready    master   3h50m   v1.17.3
k8s-node2   Ready       3h3m    v1.17.3
k8s-node3   Ready       3h3m    v1.17.3
[root@k8s-node1 ~]# 

8)加入kubenetes的Node节点

在node节点中执行,向集群中添加新的节点,执行在kubeadm init 输出的kubeadm join命令;
确保node节点成功:
token过期怎么办
kubeadm token create --print-join-command

9)入门操作kubernetes集群

1、在主节点上部署一个tomcat
kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8

获取所有的资源:

[root@k8s-node1 k8s]# kubectl get all
NAME                           READY   STATUS              RESTARTS   AGE
pod/tomcat6-7b84fb5fdc-cfd8g   0/1     ContainerCreating   0          41sNAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1            443/TCP   70mNAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tomcat6   0/1     1            0           41sNAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/tomcat6-7b84fb5fdc   1         1         0       41s
[root@k8s-node1 k8s]# 

kubectl get pods -o wide 可以获取到tomcat部署信息,能够看到它被部署到了k8s-node3上了

[root@k8s-node1 k8s]# kubectl get all -o wide
NAME                           READY   STATUS              RESTARTS   AGE   IP       NODE        NOMINATED NODE   READINESS GATES
pod/tomcat6-5f7ccf4cb9-xhrr9   0/1     ContainerCreating   0          77s      k8s-node3              NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/kubernetes   ClusterIP   10.96.0.1            443/TCP   68m   NAME                      READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES               SELECTOR
deployment.apps/tomcat6   0/1     1            0           77s   tomcat       tomcat:6.0.53-jre8   app=tomcat6NAME                                 DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES               SELECTOR
replicaset.apps/tomcat6-5f7ccf4cb9   1         1         0       77s   tomcat       tomcat:6.0.53-jre8   app=tomcat6,pod-template-hash=5f7ccf4cb9

查看node3节点上,下载了哪些镜像:

[root@k8s-node3 k8s]# docker images
REPOSITORY                                                       TAG             IMAGE ID       CREATED         SIZE
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy   v1.17.3         ae853e93800d   14 months ago   116MB
quay.io/coreos/flannel                                           v0.11.0-amd64   ff281650a721   2 years ago     52.6MB
registry.cn-hangzhou.aliyuncs.com/google_containers/pause        3.1             da86e6ba6ca1   3 years ago     742kB
tomcat                                                           6.0.53-jre8     49ab0583115a   3 years ago     290MB

查看Node3节点上,正在运行的容器:

[root@k8s-node3 k8s]# docker ps
CONTAINER ID   IMAGE                                                            COMMAND                  CREATED              STATUS              PORTS     NAMES
8a197fa41dd9   tomcat                                                           "catalina.sh run"        About a minute ago   Up About a minute             k8s_tomcat_tomcat6-5f7ccf4cb9-xhrr9_default_81f186a8-4805-4bbb-8d77-3142269942ed_0
4074d0d63a88   registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1    "/pause"                 2 minutes ago        Up 2 minutes                  k8s_POD_tomcat6-5f7ccf4cb9-xhrr9_default_81f186a8-4805-4bbb-8d77-3142269942ed_0
db3faf3a280d   ff281650a721                                                     "/opt/bin/flanneld -…"   29 minutes ago       Up 29 minutes                 k8s_kube-flannel_kube-flannel-ds-amd64-vcktd_kube-system_31ca3556-d6c3-48b2-b393-35ff7d89a078_0
be461b54cb4b   registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy   "/usr/local/bin/kube…"   30 minutes ago       Up 30 minutes                 k8s_kube-proxy_kube-proxy-ptq2t_kube-system_0e1f7df3-7204-481d-bf15-4b0e09cf0c81_0
88d1ab87f400   registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1    "/pause"                 31 minutes ago       Up 31 minutes                 k8s_POD_kube-flannel-ds-amd64-vcktd_kube-system_31ca3556-d6c3-48b2-b393-35ff7d89a078_0
52be28610a02   registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1    "/pause"                 31 minutes ago       Up 31 minutes                 k8s_POD_kube-proxy-ptq2t_kube-system_0e1f7df3-7204-481d-bf15-4b0e09cf0c81_0

在node1上执行:

[root@k8s-node1 k8s]# kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
tomcat6-7b84fb5fdc-cfd8g   1/1     Running   0          5m35s[root@k8s-node1 k8s]# kubectl get pods --all-namespaces
NAMESPACE     NAME                                READY   STATUS    RESTARTS   AGE
default       tomcat6-7b84fb5fdc-cfd8g            1/1     Running   0          163m
kube-system   coredns-546565776c-9sbmk            1/1     Running   0          3h52m
kube-system   coredns-546565776c-t68mr            1/1     Running   0          3h52m
kube-system   etcd-k8s-node1                      1/1     Running   0          3h52m
kube-system   kube-apiserver-k8s-node1            1/1     Running   0          3h52m
kube-system   kube-controller-manager-k8s-node1   1/1     Running   0          3h52m
kube-system   kube-flannel-ds-amd64-5xs5j         1/1     Running   0          3h6m
kube-system   kube-flannel-ds-amd64-6xwth         1/1     Running   0          3h24m
kube-system   kube-flannel-ds-amd64-fvnvx         1/1     Running   0          3h6m
kube-system   kube-proxy-7tkvl                    1/1     Running   0          3h6m
kube-system   kube-proxy-mvlnk                    1/1     Running   0          3h6m
kube-system   kube-proxy-sz2vz                    1/1     Running   0          3h52m
kube-system   kube-scheduler-k8s-node1            1/1     Running   0          3h52m
[root@k8s-node1 ~]# 

从前面看到tomcat部署在Node3上,现在模拟因为各种原因宕机的情况,将node3关闭电源,观察情况。

docker stop执行的时候,docker ps发现又有新的容器了,这是k8s又新建了,所以选择关机node3

[root@k8s-node1 k8s]# kubectl get nodes
NAME        STATUS     ROLES    AGE   VERSION
k8s-node1   Ready      master   79m   v1.17.3
k8s-node2   Ready         41m   v1.17.3
k8s-node3   NotReady      41m   v1.17.3

得等个几分钟才能容灾恢复

[root@k8s-node1 k8s]# kubectl get pods -o wide
NAME                       READY   STATUS        RESTARTS   AGE     IP           NODE        NOMINATED NODE   READINESS GATES
tomcat6-5f7ccf4cb9-clcpr   1/1     Running       0          4m16s   10.244.1.2   k8s-node2              
tomcat6-5f7ccf4cb9-xhrr9   1/1     Terminating   1          22m     10.244.2.2   k8s-node3              
2、暴露nginx访问

在master上执行

# tomcat镜像端口8080,转发到pod的80端口上,然后转发到虚拟机的XXX端口上(自动生成)
kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort 

pod的80映射容器的8080;server会带来pod的80

查看服务:

[root@k8s-node1 k8s]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1            443/TCP        93m
tomcat6      NodePort    10.96.7.78           80:30055/TCP   8s[root@k8s-node1 k8s]# kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1            443/TCP        103m    
tomcat6      NodePort    10.96.7.78           80:30055/TCP   9m38s   app=tomcat6

浏览器输入:http://192.168.56.100:30055/ ,可以看到tomcat首页

输入下面命令可以看到pod和封装pod 的service,pod是部署产生的,部署还有一个副本

[root@k8s-node1 ~]# kubectl get all
NAME                           READY   STATUS    RESTARTS   AGE
pod/tomcat6-5f7ccf4cb9-clcpr   1/1     Running   0          4h12mNAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1            443/TCP        5h37m
service/tomcat6      NodePort    10.96.7.78           80:30055/TCP   4h3mNAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tomcat6   1/1     1            1           4h30mNAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/tomcat6-5f7ccf4cb9   1         1         1       4h30m
3、动态扩容测试

kubectl get deployment

[root@k8s-node1 ~]# kubectl get deployment
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
tomcat6   2/2     2            2           11h
[root@k8s-node1 ~]# 

应用升级: kubectl set image (–help查看帮助)

扩容:kubectl scale --replicas=3 deployment tomcat6

[root@k8s-node1 ~]# kubectl scale --replicas=3 deployment tomcat6
deployment.apps/tomcat6 scaled
[root@k8s-node1 ~]# kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP           NODE        NOMINATED NODE   READINESS GATES
tomcat6-5f7ccf4cb9-clcpr   1/1     Running   0          4h23m   10.244.1.2   k8s-node2              
tomcat6-5f7ccf4cb9-jbvr4   1/1     Running   0          9s      10.244.2.3   k8s-node3              
tomcat6-5f7ccf4cb9-ng556   1/1     Running   0          9s      10.244.2.4   k8s-node3              [root@k8s-node1 ~]# kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1            443/TCP        5h48m   
tomcat6      NodePort    10.96.7.78           80:30055/TCP   4h15m   app=tomcat6

扩容了多份,所有无论访问哪个node的指定端口,都可以访问到tomcat6

http://192.168.56.101:30055/

缩容:kubectl scale --replicas=2 deployment tomcat6

[root@k8s-node1 ~]# kubectl scale --replicas=1 deployment tomcat6
deployment.apps/tomcat6 scaled[root@k8s-node1 ~]# kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP           NODE        NOMINATED NODE   READINESS GATES
tomcat6-5f7ccf4cb9-clcpr   1/1     Running   0          4h32m   10.244.1.2   k8s-node2              [root@k8s-node1 ~]# kubectl get all
NAME                           READY   STATUS    RESTARTS   AGE
pod/tomcat6-5f7ccf4cb9-clcpr   1/1     Running   0          4h33mNAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1            443/TCP        5h58m
service/tomcat6      NodePort    10.96.7.78           80:30055/TCP   4h24mNAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tomcat6   1/1     1            1           4h51mNAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/tomcat6-5f7ccf4cb9   1         1         1       4h51m

4、以上操作的yaml获取

# 尝试运行,并不会真正的创建镜像
kubectl create deployment web --image=nginx -o yaml --dry-run

5、删除

kubectl delete deploye/nginx
kubectl delete service/nginx-service

kubectl get all

[root@k8s-node1 ~]# kubectl get all
NAME                           READY   STATUS    RESTARTS   AGE
pod/tomcat6-5f7ccf4cb9-clcpr   1/1     Running   0          4h33mNAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1            443/TCP        5h58m
service/tomcat6      NodePort    10.96.7.78           80:30055/TCP   4h24mNAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tomcat6   1/1     1            1           4h51mNAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/tomcat6-5f7ccf4cb9   1         1         1       4h51m
#删除deployment.apps/tomcat6 
[root@k8s-node1 ~]# kubectl delete deployment.apps/tomcat6
deployment.apps "tomcat6" deleted
#查看剩余的资源
[root@k8s-node1 ~]# kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1            443/TCP        6h
service/tomcat6      NodePort    10.96.7.78           80:30055/TCP   4h26m
# 此时没有了部署,但是有service,没有pod只有service也是没有对应的服务的# 查看pod信息
[root@k8s-node1 ~]# kubectl get pods
No resources found in default namespace.

三、docker深入

docker笔记以后再发表吧,没整理好

四、K8s细节

这么这些内容我那个专栏里也有

https://blog.csdn.net/hancoder/category_11140481.html

https://kubernetes.io/zh/docs/reference/kubectl/overview/

1、yaml输出

https://kubernetes.io/zh/docs/reference/kubectl/overview/#资源类型

在此示例中,以下命令将单个 pod 的详细信息输出为 YAML 格式的对象:

kubectl get pod web-pod-13je7 -o yaml
1

请记住:有关每个命令支持哪种输出格式的详细信息,请参阅 kubectl 参考文档。

–dry-run:

–dry-run=‘none’: Must be “none”, “server”, or “client”. If client strategy, only print the object that would be

sent, without sending it. If server strategy, submit server-side request without persisting the resource.

值必须为,或。

  • none
  • server:提交服务器端请求而不持久化资源。
  • client:只打印该发送对象,但不发送它。

也就是说,通过–dry-run选项,并不会真正的执行这条命令。

# 输出yaml
[root@k8s-node1 ~]# kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yamlW0504 03:39:08.389369    8107 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: apps/v1
kind: Deployment
metadata:creationTimestamp: nulllabels:app: tomcat6name: tomcat6
spec:replicas: 1selector:matchLabels:app: tomcat6strategy: {}template:metadata:creationTimestamp: nulllabels:app: tomcat6spec:containers:- image: tomcat:6.0.53-jre8name: tomcatresources: {}
status: {}

实际上我们也可以将这个yaml输出到文件,然后使用kubectl apply -f来应用它

#输出到tomcat6.yaml 
[root@k8s-node1 ~]# kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yaml >tomcat6.yaml
W0504 03:46:18.180366   11151 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client.

tomcat6.yaml 内容,修改一下副本个数为3

apiVersion: apps/v1
kind: Deployment
metadata:creationTimestamp: nulllabels:app: tomcat6name: tomcat6
spec:replicas: 3 # 修改selector:matchLabels:app: tomcat6strategy: {}template:metadata:creationTimestamp: nulllabels:app: tomcat6spec:containers:- image: tomcat:6.0.53-jre8name: tomcatresources: {}
status: {}#应用tomcat6.yaml 
[root@k8s-node1 k8s]# kubectl apply -f tomcat6.yaml
deployment.apps/tomcat6 created
[root@k8s-node1 k8s]# kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
tomcat6-5f7ccf4cb9-hxqfl   1/1     Running   0          7s
tomcat6-5f7ccf4cb9-ksm4n   1/1     Running   0          7s
tomcat6-5f7ccf4cb9-qlzd4   1/1     Running   0          7s

查看某个pod的具体信息:

[root@k8s-node1 ~]# kubectl get pods tomcat6-7b84fb5fdc-5jh6t  -o yaml

2、service

可以去看 https://blog.csdn.net/hancoder/article/details/118070247

Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector

通俗的讲:SVC负责检测Pod的状态信息,不会因pod的改动IP地址改变(因为关注的是标签),导致Nginx负载均衡影响

在这里插入图片描述

Service能够提供负载均衡的能力,但是在使用上有以下限制:

  • 默认只提供 4 层负载均衡能力(IP+端口),而没有 7 层功能(主机名和域名),但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的
  • 后续可以通过Ingress方案,添加7层的能力
# 1、部署一个nginx
kubectl create deployment nginx --image=nginx# 2、暴露nginx访问
kubectl expose deployment nginx --port=80 --type=NodePort

现在我们使用NodePort的方式暴露,这样访问每个节点的端口,都可以访问各个Pod,如果节点宕机,就会出现问题。

前面我们通过命令行的方式,部署和暴露了tomcat,实际上也可以通过yaml的方式来完成这些操作。

#这些操作实际上是为了获取Deployment的yaml模板
[root@k8s-node1 ~]#  kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yaml >tomcat6-deployment.yaml
W0504 04:13:28.265432   24263 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client.
[root@k8s-node1 ~]# ls tomcat6-deployment.yaml
tomcat6-deployment.yaml
[root@k8s-node1 ~]# 

修改“tomcat6-deployment.yaml”内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:labels:app: tomcat6name: tomcat6
spec:replicas: 3selector:matchLabels:app: tomcat6template:metadata: labels:app: tomcat6spec:containers:- image: tomcat:6.0.53-jre8name: tomcat
#部署
[root@k8s-node1 ~]# kubectl apply -f tomcat6-deployment.yaml
deployment.apps/tomcat6 configured#查看资源
[root@k8s-node1 ~]# kubectl get all
NAME                           READY   STATUS    RESTARTS   AGE
pod/tomcat6-7b84fb5fdc-5jh6t   1/1     Running   0          27m
pod/tomcat6-7b84fb5fdc-8lhwv   1/1     Running   0          27m
pod/tomcat6-7b84fb5fdc-j4qmh   1/1     Running   0          27mNAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1            443/TCP   14hNAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tomcat6   3/3     3            3           27mNAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/tomcat6-7b84fb5fdc   3         3         3       27m
[root@k8s-node1 ~]#
kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort  --dry-run -o yaml
1
apiVersion: v1
kind: Service # service
metadata:creationTimestamp: nulllabels:app: tomcat6name: tomcat6
spec:ports:- port: 80protocol: TCPtargetPort: 8080selector:app: tomcat6 # 标签type: NodePort
status:loadBalancer: {}
关联部署和service

将这段输出和“tomcat6-deployment.yaml”用---进行拼接,表示部署完毕并进行暴露服务:

apiVersion: apps/v1
kind: Deployment # 部署
metadata:labels:app: tomcat6 # 标签name: tomcat6
spec:replicas: 3 #副本数selector:matchLabels:app: tomcat6template:metadata: labels:app: tomcat6spec:containers:- image: tomcat:6.0.53-jre8name: tomcat
---
apiVersion: v1
kind: Service
metadata:creationTimestamp: nulllabels:app: tomcat6 # 标签name: tomcat6
spec:ports:- port: 80protocol: TCPtargetPort: 8080selector:app: tomcat6 # 标签type: NodePort
  • 上面类型一个是Deployment,一个是Service

部署并暴露服务

[root@k8s-node1 ~]# kubectl apply -f tomcat6-deployment.yaml
deployment.apps/tomcat6 created
service/tomcat6 created

查看服务和部署信息

[root@k8s-node1 ~]# kubectl get all
NAME                           READY   STATUS    RESTARTS   AGE
pod/tomcat6-7b84fb5fdc-dsqmb   1/1     Running   0          4s
pod/tomcat6-7b84fb5fdc-gbmxc   1/1     Running   0          5s
pod/tomcat6-7b84fb5fdc-kjlc6   1/1     Running   0          4sNAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1               443/TCP        14h
service/tomcat6      NodePort    10.96.147.210           80:30172/TCP   4sNAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tomcat6   3/3     3            3           5sNAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/tomcat6-7b84fb5fdc   3         3         3       5s
[root@k8s-node1 ~]#

访问node1,node1和node3的30172端口:

[root@k8s-node1 ~]# curl -I http://192.168.56.{100,101,102}:30172/
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"7454-1491118183000"
Last-Modified: Sun, 02 Apr 2017 07:29:43 GMT
Content-Type: text/html
Content-Length: 7454
Date: Mon, 04 May 2020 04:35:35 GMTHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"7454-1491118183000"
Last-Modified: Sun, 02 Apr 2017 07:29:43 GMT
Content-Type: text/html
Content-Length: 7454
Date: Mon, 04 May 2020 04:35:35 GMTHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"7454-1491118183000"
Last-Modified: Sun, 02 Apr 2017 07:29:43 GMT
Content-Type: text/html
Content-Length: 7454
Date: Mon, 04 May 2020 04:35:35 GMT[root@k8s-node1 ~]# 

现在的问题是service的3个pod都可以访问,但怎么创建个总的管理者,以便负载均衡

3、Ingress

通过Ingress发现pod进行关联。基于域名访问
通过Ingress controller实现POD负载均衡
支持TCP/UDP 4层负载均衡和HTTP 7层负载均衡

可以把ingress理解为nginx,通过域名访问service端口

  • Ingress管理多个service
  • service管理多个pod

步骤:
(1)部署Ingress controller

执行“k8s/ingress-controller.yaml”,

apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: web
spec:rules:- host:tomcat6.atguigu.comhttp:paths:-backend:serviceName: tomcat6servicePort: 80
123456789101112
[root@k8s-node1 k8s]# kubectl apply -f ingress-controller.yaml 
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
daemonset.apps/nginx-ingress-controller created
service/ingress-nginx created
[root@k8s-node1 k8s]# 

查看

[root@k8s-node1 k8s]# kubectl get pods --all-namespaces
NAMESPACE       NAME                                READY   STATUS              RESTARTS   AGE
default         tomcat6-7b84fb5fdc-dsqmb            1/1     Running             0          16m
default         tomcat6-7b84fb5fdc-gbmxc            1/1     Running             0          16m
default         tomcat6-7b84fb5fdc-kjlc6            1/1     Running             0          16m
ingress-nginx   nginx-ingress-controller-9q6cs      0/1     ContainerCreating   0          40s
ingress-nginx   nginx-ingress-controller-qx572      0/1     ContainerCreating   0          40s
kube-system     coredns-546565776c-9sbmk            1/1     Running             1          14h
kube-system     coredns-546565776c-t68mr            1/1     Running             1          14h
kube-system     etcd-k8s-node1                      1/1     Running             1          14h
kube-system     kube-apiserver-k8s-node1            1/1     Running             1          14h
kube-system     kube-controller-manager-k8s-node1   1/1     Running             1          14h
kube-system     kube-flannel-ds-amd64-5xs5j         1/1     Running             2          13h
kube-system     kube-flannel-ds-amd64-6xwth         1/1     Running             2          14h
kube-system     kube-flannel-ds-amd64-fvnvx         1/1     Running             1          13h
kube-system     kube-proxy-7tkvl                    1/1     Running             1          13h
kube-system     kube-proxy-mvlnk                    1/1     Running             2          13h
kube-system     kube-proxy-sz2vz                    1/1     Running             1          14h
kube-system     kube-scheduler-k8s-node1            1/1     Running             1          14h
[root@k8s-node1 k8s]#

这里master节点负责调度,具体执行交给node2和node3来完成,能够看到它们正在下载镜像

image-20200504124608258

(2)创建Ingress规则

ingress-tomcat6.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: web
spec:rules:- host: tomcat6.kubenetes.comhttp:paths: - backend: serviceName: tomcat6servicePort: 80
123456789101112
[root@k8s-node1 k8s]# touch ingress-tomcat6.yaml
#将上面的规则,添加到ingress-tomcat6.yaml文件中
[root@k8s-node1 k8s]# vi  ingress-tomcat6.yaml  [root@k8s-node1 k8s]# kubectl apply -f ingress-tomcat6.yaml 
ingress.extensions/web created

修改本机的hosts文件,添加如下的域名转换规则:

192.168.56.102 tomcat6.kubenetes.com

测试: http://tomcat6.kubenetes.com/ 访问到tomcat

并且集群中即便有一个节点不可用,也不影响整体的运行。

10、安装kubernetes可视化界面——DashBoard

同样网上可以找到yaml:https://gitee.com/CaiJinHao/kubernetesdashboard/tree/v1.10.1/src/deploy/recommended

1、部署DashBoard

$ kubectl apply -f  kubernetes-dashboard.yaml

2、暴露DashBoard为公共访问

默认DashBoard只能集群内部访问,修改Service为NodePort类型,暴露到外部

kind: Service
apiVersion: v1
metadata:labels:k8s-app: kubernetes-dashboardname: kubernetes-dashboardnamespace: kube-system
spec:type: NodePortports:- port: 443targetPort: 8443nodePort: 3001selector:k8s-app: kubernetes-dashboard

访问地址:http://NodeIP:30001

3、创建授权账号

$ kubectl create serviceaccount dashboar-admin -n kube-sysem$ kubectl create clusterrolebinding dashboar-admin --clusterrole=cluter-admin --serviceaccount=kube-system:dashboard-admin$ kubectl describe secrets -n kube-system $( kubectl -n kube-system get secret |awk '/dashboard-admin/{print $1}' )

使用输出的token登录dashboard

image-20200504153630775

二、kubesphere

这个章节不怎么写了,请参考我的另外一篇博客 https://blog.csdn.net/hancoder/article/details/118053239 在文末

默认的dashboard没啥用,kubesphere可以打通全部的devops链路,kubesphere集成了很多套件,集群要求比较高

DevOps

Mysql集群

数据库的可以去我的专栏阅读:https://blog.csdn.net/hancoder/category_9488515.html

Mysql-MMM简介

MMM(Master-Master replication manager for MySQL)是一套支持双主故障切换和双主日常管理的脚本程序。MMM使用Perl语言开发,主要用来监控和管理MySQL Master-Master(双主)复制,虽然叫做双主复制,但是业务上同一时刻只允许对一个主进行写入,另一台备选主上提供部分读服务,以加速在主主切换时刻备选主的预热,可以说MMM这套脚本程序一方面实现了故障切换的功能,另一方面其内部附加的工具脚本也可以实现多个slave的read负载均衡。

MMM提供了自动和手动两种方式移除一组服务器中复制延迟较高的服务器的虚拟ip,同时它还可以备份数据,实现两节点之间的数据同步等。由于MMM无法完全的保证数据一致性,所以MMM适用于对数据的一致性要求不是很高,但是又想最大程度的保证业务可用性的场景。对于那些对数据的一致性要求很高的业务,非常不建议采用MMM这种高可用架构。

正常工作是一主2从,master1主挂掉之后,monitor会把写IP交给master2,而客户端实际还是用的原来的IP,这个工作就是monitor做的

但是也有数据一致性问题,只是解决了IP偏移问题

优点:

1 稳定和成熟的开源产品,经过了时间的考验 核心技术是mysql自己的技术,只是使用脚本程序来控制,所以在原理上比较容易理解,而且管理能够更智能化。
2 安装简单,配置简单,使用简单
3 功能强大 (HA,failover,tools套件,cluster模式可以一个monitor管理多个mmm组)
123

缺点:

1 由于架构里只有一个写入点,所以扩展性是有限的,但是对一般中型企业够用了。解决方案:对于大应用可以采取垂直拆分到多个mmm架构的方式,使用mmm cluster来管理。 
2 对于读写分离和读负载均衡还是要程序来开发或者使用其他工具完成。

MHA

MHA(Master High Availability)目前在 MySQL 高可用方面是一个相对成熟的解决方案,它由日本 DeNA 公司的 youshimaton(现就职于 Facebook 公司)开发,是一套优秀的作为 MySQL 高可用性环境下故障切换和主从提升的高可用软件。

在 MySQL 故障切换过程中,MHA 能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA 能在最大程度上保证数据的一致性,以达到真正意义上的高可用。

该软件由两部分组成:MHA Manager(管理节点)和 MHA Node(数据节点)。MHA Manager 可以单独部署在一台独立的机器上管理多个 master-slave 集群,也可以部署在一台 slave 节点上。MHA Node 运行在每台 MySQL 服务器上,MHA Manager 会定时探测集群中的 master 节点,当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master,然后将所有其他的 slave 重新指向新的 master。整个故障转移过程对应用程序完全透明。

在 MHA 自动故障切换过程中,MHA 试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA 没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用 MySQL 5.5 的半同步复制,可以大大降低数据丢失的风险。MHA 可以与半同步复制结合起来。如果只有一个 slave 已经收到了最新的二进制日志,MHA 可以将最新的二进制日志应用于其他所有的 slave 服务器上,因此可以保证所有节点的数据一致性。

目前 MHA 主要支持一主多从的架构,要搭建 MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当 master,一台充当备用 master,另外一台充当从库,因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝TMHA已经支持一主一从。

InnoDB Cluster

参考:https://blog.csdn.net/wzy0623/article/details/100779450

最初的MySQL版本只提供一种简单的主从异步复制,满足最基本的数据同步。为了提高复制性能,从单线程到组提交再到多线程复制,基本解决了复制延迟问题。

  • 为了解决从库与主库的一致性读问题,新增了半同步复制
  • 为了提供自动故障转移功能,又提供了组复制功能。
  • 要做到真正的高可用,失败切换必须对应用透明,于是在组复制的基础上,又发展出了InnoDB Cluster。

InnoDB Cluster出现前,实现MySQL数据库的的高可用性,除了原生的复制功能,通常还需要借助第三方中间件,如Keepalived、MHA等等。

整体结构

InnoDB Cluster主要由MySQL ShellMySQL RouterMySQL服务器集群组成,三者协同工作,共同为MySQL提供完整的高可用性解决方案。图1所示为InnoDB Cluster的整体架构。

img

InnoDB Cluster以组复制为基础,集群中的每个MySQL服务器实例都是组复制的成员,提供了在InnoDB Cluster内复制数据的机制,并且具有内置的故障转移功能。

  • MySQL Shell在InnoDB Cluster中充当控制台角色,使用它包含的AdminAPI,可以使安装、配置、管理、维护多个MySQL组复制实例的工作更加轻松。通过AdminAPI的几条交互指令就可自动完成组复制配置。
  • MySQL Router可以根据集群部署信息自动生成配置,将客户端应用程序透明地连接到MySQL服务器实例。如果服务器实例意外故障,群集将自动重新配置。在默认的单主模式下,InnoDB Cluster 具有单个读写主服务器实例。多个辅助服务器实例是主服务器实例的副本。如果主服务器出现故障,则辅助服务器将自动升级为主服务器。MySQL Router可以检测到这种情况并将客户端应用程序自动转发到新的主服务器。

用户连MySQL Router,MySQL Router去调度读写

企业级方案

这里写图片描述

这里写图片描述

在中小型互联网的企业中。mysql的集群一般就是上图的架构。WEB节点读取数据库的时候读取dbproxy服务器。dbproxy服务器通过对SQL语句的判断来进行数据库的读写分离。读请求负载到从库(也可以把主库加上),写请求写主库。

这里的dbproxy是数据库集群的唯一出口所以也需要做高可用。

drproxy是数据库读写分离的常用软件,amoeba、mycat、cobar也很常用。这类软件不仅带有读写分离功能,还可以实现负载均衡以及后端节点的健康检查。

数据库的读写分离除了通过这类数据库中间件软件实现,还可以写在程序中。

通常我们的主库要做双主高可用,实现主库挂掉另一个主库立刻接管。如果不做双主,从库接管主库的时候需要做状态迁移,会有延迟。

数据库主库的高可用重点需要考虑的是数据同步。比较常用的高可用方案有:

  • 1、keepalived+mysql replication。通过keepalived实现VIP飘逸,通过mysql自带的同步方案replication实现数据同步。
  • 2、hearbeat+drbd。通过drbd实现双主数据的同步,这个数据同步是基于块设备的。比一般的同步方案要快很多。通过heartbeat实现VIP漂移以及drbd资源的切换管理。
  • 3、keepalived+mha。

对于从库,最好不要超过5个。我们可以把其中的三个作为用户访问的节点,把另外一个作为内部人员的查询节点。因为内部人员查询节点的时候一般是按照时间段查询,不经过索引,占用的资源比较多,所以要把这个节点单独专用,以免影响客户访问。最后我们应该留一个从库进行数据库的数据备份。

从库的数据一致性保持可以通过直接于主库进行主从辅助,也可以从其他从库那进行主从复制(优点是减少主库压力,缺点是延迟稍大)、

2、MYSQL数据架构

数据库服务器==》数据库(多个实例)》多个库》多个表==》多个字段行列(数据)

在一台数据库服务器上可以跑多个实例,一个实例中有多个库,一个库有多个表,一个表有多个行列。

这里写图片描述

搭建主从复制集群

P362

  • MASTER:创建docker,映射到3307,-v挂载文件夹到主机
  • slave:3317
sudo docker run -p 3307:3306 --name mysql-master \
-v /mydata/mysql/master/log:/var/log/mysql \
-v /mydata/mysql/master/data:/var/lib/mysql \
-v /mydata/mysql/master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
sudo docker run -p 3317:3306 --name mysql-slave1 \
-v /mydata/mysql/slaver/log:/var/log/mysql \
-v /mydata/mysql/slaver/data:/var/lib/mysql \
-v /mydata/mysql/slaver/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
vim /mydata/mysql/master/conf/my.cnf[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
# skip-name-resolve一定要加,否则会很慢
vim /mydata/mysql/slaver/conf/my.cnf[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
# skip-name-resolve一定要加,否则会很慢

然后添加机器信息了

有机会我会把binlog相关的博客发出来

vim /mydata/mysql/master/conf/my.cnfserver_id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin# 不主从复制的数据库
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema

开始配置slave

vim /mydata/mysql/slaver/conf/my.cnfserver_id=2
log-bin=mysql-bin
read-only=1
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin# 不主从复制的数据库
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema

重启、开发master的权限、

#  重启master容器,
docker restart mysql-master mysql-slaver1# 进入master容器内部
docker exec -it mysql /bin/bash
grant all privileges on *.* to 'root'@'%' identified by '123456'
flush privileges;# 在mysql命令行中
GRANT REPLICATION SLAVE ON *.* to 'backup'@'%' identified by '123456'SHOW MASTER STATUS;

在从库中

# 设置主从链接
change master to
master_host='192.168.56.10',master_user='backup',master_password='123456',master_log_file='mysql-bn.000001',master_log_pos=0,master_port=3307l# master上执行
start slave;
show slave status;

ShardingSphere

解决master分片存储,分库分表

比如在配置文件里配置

# 第一个服务器
auto_increment_offset:1 # 初始
auto_increment_increment:2 # 步长# 第2个服务器
auto_increment_offset:2 # 初始
auto_increment_increment:2 # 步长

但是不好维护,不使用这种

https://shardingsphere.apache.org/index_zh.html

Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

ShardingSphere-Proxy Architecture

下载ShardingSphere-Proxy

https://shardingsphere.apache.org/document/current/cn/user-manual/shardingsphere-proxy/

数据源配置:https://shardingsphere.apache.org/document/4.1.0/cn/manual/sharding-proxy/configuration/

Sharding-Proxy支持多逻辑数据源,每个以config-前缀命名的yaml配置文件,即为一个逻辑数据源。以下是config-xxx.yaml的配置配置示例。

schemaName: # 逻辑数据源名称dataSources: # 数据源配置,可配置多个 : # 与 ShardingSphere-JDBC 配置不同,无需配置数据库连接池url: #数据库 URL 连接username: # 数据库用户名password: # 数据库密码connectionTimeoutMilliseconds: # 连接超时毫秒数idleTimeoutMilliseconds: # 空闲连接回收超时毫秒数maxLifetimeMilliseconds: # 连接最大存活时间毫秒数maxPoolSize: 50 # 最大连接数minPoolSize: 1  # 最小连接数     rules: # 与 ShardingSphere-JDBC 配置一致# ...

下载mysql驱动放到ShardingSphere-Proxy /lib中

server.yaml

config-readwrite-splitting.yaml

config-sharding.yaml

schemaName: sharding_db
#
dataSources:ds_0:
#    url: jdbc:postgresql://127.0.0.1:3307/demo_ds_0?serverTimezone=UTC&useSSL=false
#    username: postgres
#    password: postgres
#    connectionTimeoutMilliseconds: 30000
#    idleTimeoutMilliseconds: 60000
#    maxLifetimeMilliseconds: 1800000
#    maxPoolSize: 50
#    minPoolSize: 1
#    maintenanceIntervalMilliseconds: 30000ds_1:
#    url: jdbc:postgresql://127.0.0.1:3317/demo_ds_1?serverTimezone=UTC&useSSL=false
#    username: postgres
#    password: postgres
#    connectionTimeoutMilliseconds: 30000
#    idleTimeoutMilliseconds: 60000
#    maxLifetimeMilliseconds: 1800000
#    maxPoolSize: 50
#    minPoolSize: 1
#    maintenanceIntervalMilliseconds: 30000
#######################################################################################################rules:
#- !SHARDING
#  tables:
#    t_order:
#      actualDataNodes: ds_${0..1}.t_order_${0..1}
#      tableStrategy:
#        standard:shardingColumn: order_id  # 根据这个字段分表shardingAlgorithmName: t_order_inlinekeyGenerateStrategy: # 主键生成策略  #雪花算法column: order_idkeyGeneratorName: snowflaket_order_item:
#      actualDataNodes: ds_${0..1}.t_order_item_${0..1}
#      tableStrategy:
#        standard:shardingColumn: order_id # 这样这2个表就在一个库中了shardingAlgorithmName: t_order_item_inline
#      keyGenerateStrategy:
#        column: order_item_id
#        keyGeneratorName: snowflakebindingTables:- t_order,t_order_itemdefaultDatabaseStrategy: # 分库standard: shardingColumn: user_id # 根据用户id分库shardingAlgorithmName: database_inline

config-master_slave.yaml

dataSources:# 2主2从master_0_ds:url: jdbc:postgresql://192.168.56.10:3307/demo_ds_0?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50slave_ds_0:url: jdbc:postgresql://192.168.56.10:3317/demo_ds_0?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50master_1_ds:url: jdbc:postgresql://192.168.56.10:3307/demo_ds_1?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50slave_ds_1:url: jdbc:postgresql://192.168.56.10:3317/demo_ds_1?serverTimezone=UTC&useSSL=falseusername: rootpassword: rootconnectionTimeoutMilliseconds: 30000idleTimeoutMilliseconds: 60000maxLifetimeMilliseconds: 1800000maxPoolSize: 50
# 2套主从规则
masterSlaveRule:name: ms_ds1masterDataSourceName: master_0_dsslaveDataSourceNames:- slave_ds_0# 另放到一个文件在# name: ms_ds2:# masterDataSourceName: master_1_ds# slaveDataSourceNames:# - slave_ds_1

去docker里先停掉,然后添加上主从复制的库

然后链接上sharding-proxy,insert的时候无需指定主键,雪花算法就生成主键了

在sharding里看到的是全部数据,在物理库中是分开的

Redis集群

  • 传统:jedis判断要去哪个redis、代表有redis sharding。缺点是redis宕机后客户端感知不到没法容灾
  • 代理:和前面讲的意义,客户端连接虚拟代理。代表有Twemproxy、Codis
  • redis-cluster:(官方、推荐)

之前是利用哨兵来主从切换。

1)槽点

cluster是多主多从,用了16384个哈希槽,可以根据性能将不同的槽位分配给redis实例

CRC16(key)%16383
1

缺点:

  • 客户端会随便连个redis,然后redis告诉客户端去哪个redis里找,客户端重新请求一遍
  • mset、mget等操作,多个key批量时只支持slot值相同的批量操作。
  • 事务支持有限。但是我们一般使用lua脚本

2)一致性哈希

不讲了,很多地方有讲。

重分布时只重分别一个区间内的。

可以使用虚拟结点解决数据倾斜的问题。

搭建:简单阅读下下面的步骤,docker原理也差不多

1、准备6台redis,都配置上 masterauth (不清楚的可参考我的另一篇博客《Redis主从、哨兵》)

2、修改核心配置 vim /usr/local/redis/redis.conf

# 开启集群模式
cluster-enabled yes
# 每一个节点需要有一个配置文件,需要6份。每个节点处于集群的角色都需要告知其他所有节点,彼此知道,这个文件用于存储集群模式下的集群状态等信息,这个文件是由redis自己维护,我们不用管。如果你要重新创建集群,那么把这个文件删了就行
cluster-config-file nodes-201.conf
# 超时时间,超时则认为master宕机,随后主备切换
cluster-node-timeout 5000
# 开启AOF
appendonly yes

3、删除工作空间中rdb和aof文件

4、分别启动6台redis

5、创建集群,在任一节点上执行以下命令,执行完会出现日志,6台的主从关系都会显示。

#####
# 注意1:如果你使用的是redis3.x版本,需要使用redis-trib.rb来构建集群,最新版使用C语言来构建了,这个要注意
# 注意2:以下为新版的redis构建方式
###### 创建集群,主节点和从节点比例为1,主从的对应关系会自动分配。
redis-cli --cluster create ip1:port1 ip2:port2 ip3:port3 ip4:port4 ip5:port5 ip6:port6 --cluster-replicas 1

6、检查

redis-cli -a imooc --cluster check 192.168.25.64:6380

7、可自行测试其中一台或几台挂掉是否依然可用。

至此,在保证了高可用的同时,也扩展了容量。

ES集群

ES笔记可以读下:

  • https://blog.csdn.net/hancoder/article/details/113922398
  • 正规笔记没有发表到博客上,有时间整理好再发吧

MASTER结点不存储数据

每一个索引都分片,分片还能有副本

在单机下集群配置只要cluster.name一致就会加入到集群。但是在不同主机上,需要配置一个可链接到的单播主机列表。

笔记不易:

离线笔记均为markdown格式,图片也是云图,10多篇笔记20W字,压缩包仅500k,推荐使用typora阅读。也可以自己导入有道云笔记等软件中

阿里云图床现在每周得几十元充值,都要自己往里搭了,麻烦不要散播与转发

img

打赏后请主动发支付信息到邮箱 553736044@qq.com ,上班期间很容易忽略收账信息,邮箱回邮基本秒回

禁止转载发布,禁止散播,若发现大量散播,将对本系统文章图床进行重置处理。

技术人就该干点技术人该干的事

如果帮到了你,留下赞吧,谢谢支持

上一篇:qn是什么意思

下一篇:关于黑夜的名句

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...