数据库gbk乱码问题

*近遇到一个一个问题:工程的编码是utf-8,而mysql数据库编码为gbk,而页面的编码也为utf-8,从而导致正常的提交数据到服务器段出现乱码问题。
于是,我琢磨着尝试三步骤:
1. 设定数据库的url ,尽量不要再.xml文件直接写(便于维护)
数据库链接+useUnicode=true&characterEncoding=UTF-8
然而尝试无效。
补充点额外知识:

显示结果 说明
© 版权 & copy;
® 注册商标 & reg;
× 乘号 & times;
÷ 除号 & divide;

2.然后将前端的数据格式设置为。我是ajax提交的。根据各自情况设定

%title插图%num

根据实际情况设定

3.然后设置服务端代码
通过HttpservletRequest设定接受参数编码格式。
通过这些步骤解决数据库gbk乱码问题。

Kubernetes 和 Docker,到底什么关系?

作为一名容器时代的程序员相信你已经或多或少接触过Docker,但同时你也会发现Docker虽然流行了多年,但之前却很少有公司直接将线上应用通过Docker容器进行大规模地部署。但*近三年,你会发现几乎*大多数有条件的公司都已经在使用Kubernetes部署和发布自己的线上业务了。对一名普通开发人员来说,这一切可能发生得太快,以至于你还没有搞清楚它是怎么发生的,也会疑惑Docker和Kubernetes之间到底是个什么关系。

在今天的内容中,我们从Kubernetes的系统架构及容器编排核心概念两个方面来简单聊一聊这个问题,希望能帮助到你更好地理解Docker和Kubernetes之间因果关系。

%title插图%num

Kubernetes介绍

在具体介绍Kubernetes之前不得不再提一下Docker,如果你用过Docker部署过程序,那么你一定会非常享受它带给你的丝滑体验,而联想到在此之前发布一个程序需要写各种脚本、进行各种环境匹配的糟糕体验,那么相信你的这种感觉会更加强烈。

而Docker之所以能做到这一点,就在于它以“Docker镜像”的方式一举解决了应用打包和发布这一困扰业界多年的技术难题,并且大大降低了普通开发人员运维部署应用的门槛。正是因为解决了应用打包这个根本性的问题,才使得Docker很快就被广大开发/运维人员所接受,迅速成为炙手可热的技术,并在一定时间内引领了容器化技术发展的浪潮。

那么Docker这么好用为什么还会出现Kubernetes呢?事实是Docker作为单一的容器技术工具并不能很好地定义容器的“组织方式”和“管理规范”,难以独立地支撑起生产级大规模容器化部署的要求。因此容器技术的发展就迅速走向了以Kubernetes为代表的“容器编排”的技术路线,而这也是为什么Docker容器没有直接在生产环境中大规模部署的关键原因。

上面我们提到了“容器编排”的概念,了解到相对于Docker单一容器技术而言,Kubernetes容器编排技术可以很好地实现大规模容器的组织和管理,从而使容器技术实现了从“容器”到“容器云”的飞跃!那么Kubernetes技术是从何而来?而又真正解决了什么问题呢?

从背景上说,Kubernetes是由Google与RedHat公司共同主导的开源“容器编排”项目,它起源于Google公司的Borg系统。所以它在超大规模集群管理方面的经验要明显优于其他容器编排技术,加上Kubernetes在社区管理方面的民主化,使得它很快打败了Docker公司推出的容器编排解决方案(Compose+Swarm),从而成为了容器编排领域事实上的标准。

而在功能上Kubernetes是一种综合的基于容器构建分布式系统的基础架构环境,它不仅能够实现基本的拉取用户镜像、运行容器,还可以提供路由网关、水平扩展、监控、备份、灾难恢复等一系列运维能力,而更重要的是Kubernetes可以按照用户的意愿和整个系统的规则,高度自动化的处理好容器之间的各种关系实现“编排”能力。

此外Kubernetes的出现也重新定义了微服务架构的技术方向,目前通常所说的“云原生”及“Service Mesh(服务网格)”等概念,很大程度上也是依赖于Kubernetes所提供的基础能力。由于篇幅和作者知识水平有限,这里就不展开去聊了,感兴趣的读者可以参考其他专业书籍或技术资料。

%title插图%num

Kubernetes整体系统架构

前面我们简单介绍了Kubernetes的起源和背景,接下来看看Kubernetes的整体系统架构,如下图所示:

%title插图%num

如上图所示,Kubernetes在架构上主要由Master和Node两种类型的节点组成,这两种节点分别对应着控制节点和计算节点。其中Master即控制节点,是整个Kubernetes集群的大脑,主要负责编排、管理和调度用户提交的作业,并能根据集群系统资源的整体使用情况将作业任务自动分发到可用Node计算节点。具体看Master节点主要由三个紧密协作的独立组件组合而成,它们分别是:

  • kube-apiserver:是Kubernetes集群API服务的入口,主要提供资源访问操作、认证、授权、访问控制及API注册和发现等功能机制。
  • kube-scheduler:负责Kubernetes的资源调度,能按照预定的调度策略将Pod调度到相应的机器上。
  • kube-controller-manager:负责容器编排及Kubernetes集群状态的维护,例如故障检测、自动扩展、滚动更新等。

需要说明的是,上述组件在工作状态下还会产生许多需要进行持久化的数据,这些数据会通过kube-apiserver处理后统一保存到Etcd存储服务中。所以从这个角度看kube-apiserver不仅是外部访问Kubernetes集群的入口,也是维护整个Kubernetes集群状态的信息中枢。

而在Kubernetes计算节点中,除了上述3个系统组件外,其他基本与Master节点相同,而其中*核心的部分就是kubelet组件。它的核心功能具如下:

  • 通过CRI(Container Runtime Interface)远程接口同容器运行时(如Docker)进行交互,对容器生命周期进行维护。其中CRI接口会定义了容器运行时的各项核心操作,例如启动容器所需的命令及参数等。
  • 通过GRPC协议同Device Plugin插件交互,实现Kubernetes对宿主机物理设备的管理。
  • 此外kubelet另一个重要的功能则是通过CNI(Container Networking Interface)来调用网络插件为容器配置网络,以及通过CSI(Container Storage Interface)和存储插件交互为容器配置持久化存储。

在Kubernetes中kubelet会通过CRI接口同容器运行时进行交互,而容器运行时则通过叫做OCI容器运行时规范与底层Linux操作系统进行交互(涉及对Namespace、Cgroups等资源的操作,具体可以了解下Docker的技术原理)。需要强调的是,这里所说的容器运行时并不仅仅指Docker,而是所有实现了CRI接口规范的容器项目都可以作为Kubernetes的容器运行时存在。这是因为Kubernetes从设计之初就没有把Docker作为整个架构的核心,而只是将其作为*底层的一个容器运行时来实现。

但这并不是说Kubernetes就完全抛弃Docker了,要知道Docker*大的成功并不是它的容器运行时技术,而是它定义的“容器镜像”开创性地解决了困扰业界多年的应用打包难题,所以虽然Kubernetes并不完全依赖于Docker的容器运行时技术,但容器镜像的定义标准却是所有容器技术都绕不开的存在。

况且从Kubernetes架构设计上看,Kubernetes并没有打算重复造轮子而对已有的容器技术进行替代,它更关注的是对运行在大规模集群中的各种任务根据其关系进行作业编排及管理,所以任何实现了CRI、CNI、CSI等协议标准的容器技术都可以无缝地与Kubernetes集成。从这个角度看,Docker与Kubernetes的关系并不是替代的关系,而是平台与组件的关系,Kubernetes可以利用现有的Docker容器运行时技术,但却并不完全依赖Docker。而这也正是Kubernetes为什么被称作容器编排技术而不仅仅只是容器技术的原因。

%title插图%num

Kubernetes容器编排概述

我们说处理任务之间的各种关系,实现容器编排是Kubernetes的核心技术能力,也是其大规模流行的关键原因。那么容器编排到底是个什么概念?在Kubernetes中是如何实现容器编排的呢?

其实所谓容器编排,通俗点举例就是如果两个应用调用关系比较紧密,那么我们希望运行时将它们部署在同一台机器上,从而提升服务之间的通信效率。而能够支持自动将具有此类关系的应用,以容器的方式部署在同一台机器上的技术就是容器编排。

当然,这里所说的紧密关系只是一种形象的说法,实际的技术场景中这种紧密关系可以被划分为很多类型,例如Web应用与数据库之间的访问关系、负载均衡和它后端服务之间的代理关系、门户应用与授权组件之间的调用关系等。

而对于Kubernetes来说,这样的关系描述显然还是过于具体,因为Kubernetes的设计目标不仅仅是能够处理前面提到的所有类型的关系,还要能够支持未来可能出现的更多种类的关系。这就要求Kubernetes要从更宏观地角度来定义任务之间的各种关系,并且能为将来支持更多种类的关系留有余地。

具体来说,Kubernetes是对容器间的访问进行了分类,如果这些应用之间需要非常频繁的交互和访问,或者它们之间存在直接通过本地文件进行信息交换的情况,那么在Kubernetes中可以将这些容器划分为一个“Pod”,而Pod中的容器将共享同一个Network Namespace、同一组数据卷,从而实现高效率通信。

Pod是Kubernetes中*基础的编排对象,是Kubernetes*小的调度单元,也是Kubernetes实现容器编排的载体,其本质上是一组共享了某些系统资源的容器集合。在Kubernetes中围绕Pod可以延伸出其他核心概念,具体如下图所示:

%title插图%num

如上图所示,在Kubernetes中Pod解决了容器间紧密协作(即编排)的问题,而Pod要实现一次启动多个Pod副本就需要Deployment这个Pod多实例管理器;而有了这样一组Pod后,我们又需要通过一个固定网络地址以负载均衡的方式访问它,于是又有了Service。

而根据不同的编排场景Pod又衍生出描述一次性运行任务的Job编排对象、描述每个宿主机上必须且只能运行一个副本的守护进程服务DaemonSet、描述定义任务的CronJob编排对象、以及针对有状态应用的StatefulSet等多种编排对象。而这些编排对象正是Kubernetes定义容器间关系和形态的主要方法。

如何部署一个Kubernetes集群

在上一篇文章《Kubernetes和Docker的关系是什么?》中,和大家分享了关于Kubernetes的基本系统架构以及关于容器编排相关的概念,并总体阐述Kubernetes与Docker之间的基本关系。而要学习Kubernetes容器编排技术,首先要解决的就是学习环境问题,而由于Kubernetes本身具有一定的复杂性,且需要较大的系统资源环境,所以对于刚入门的读者来说如何快速部署一套Kubernetes学习环境就成了一件棘手的事情。

在本篇文章中,我以在Mac笔记本中安装两台Ubantu系统的方式,演示如何部署一套具备一个控制节点(Master)和一个计算节点(Worker)的Kubernetes学习集群。

%title插图%num

系统环境准备

要安装部署Kubernetes集群,首先需要准备机器,*直接的办法可以到公有云(如阿里云等)申请几台虚拟机。而如果条件允许,拿几台本地物理服务器来组建集群自然是*好不过了。但是这些机器需要满足以下几个条件:

  • 要求64位Linux操作系统,且内核版本要求3.10及以上,能满足安装Docker项目所需的要求;
  • 机器之间要保持网络互通,这是未来容器之间网络互通的前提条件;
  • 要有外网访问权限,因为部署的过程中需要拉取相应的镜像,要求能够访问到gcr.io、quay.io这两个dockerregistry,因为有小部分镜像需要从这里拉取;
  • 单机可用资源建议2核CPU、8G内存或以上,如果小一点也可以但是能调度的Pod数量就比较有限了;
  • 磁盘空间要求在30GB以上,主要用于存储Docker镜像及相关日志文件;

在本次实验中由于条件有限,我是在Mac笔记本上通过虚拟软件准备了两台虚拟机,其具体配置如下:

  • 2核CPU、2GB内存,30GB的磁盘空间;
  • Unbantu 20.04 LTS的Sever版本,其Linux内核为5.4.0;
  • 内网互通,外网访问权限不受控制;

%title插图%num

Kubeadm一键部署工具简介

作为典型的分布式系统,Kubernetes的部署一直是困扰初学者进入Kubernetes世界的一大障碍。在发布早期Kubernetes的部署主要依赖于社区维护的各种脚本,但这其中会涉及二进制编译、配置文件以及kube-apiserver授权配置文件等诸多运维工作。目前各大云服务厂商常用的Kubernetes部署方式是使用SaltStack、Ansible等运维工具自动化地执行这些繁琐的步骤,但即使这样,这个部署的过程对于初学者来说依然是非常繁琐的。

正是基于这样的痛点,在志愿者的推动下Kubernetes社区终于发起了kubeadm这一独立的一键部署工具,使用kubeadm我们可以通过几条简单的指令来快速地部署一个kubernetes集群。后面的内容,就将具体演示如何使用kubeadm来部署一个Kubernetes集群。

%title插图%num

安装Kubeadm及Docker环境

在准备的两台虚拟机中,分别安装Kubeadm部署工具及Docker环境。步骤如下:

1)、编辑操作系统安装源配置文件,添加kubernetes镜像源,命令如下:

  1. #添加Kubernetes官方镜像源apt-key
  2. root@kubenetesnode01:~#curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add –
  3. #添加Kubernetes官方镜像源地址
  4. root@kubernetesnode01:~# vim /etc/apt/sources.list
  5. #add kubernetes source
  6. deb http://apt.kubernetes.io/ kubernetes-xenial main

上述操作添加的是kubernetes的官方镜像源,如果apt.kubernetes.io因为网络原因访问不到,也可以换成国内Ubantu镜像源,如阿里云镜像源地址:

  1. #添加阿里云Kubernetes镜像源apt-key
  2. root@kubenetesnode01:~# curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add –
  3. #添加阿里云Kubernetes镜像源地址
  4. root@kubernetesnode01:~# vim /etc/apt/sources.list
  5. deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main

2)、镜像源添加后更新apt资源列表,命令如下:

  1. root@kubernetesnode01:~# apt-get update
  2. Hit:1 http://cn.archive.ubuntu.com/ubuntu focal InRelease
  3. Hit:2 http://cn.archive.ubuntu.com/ubuntu focal-updates InRelease
  4. Hit:3 http://cn.archive.ubuntu.com/ubuntu focal-backports InRelease
  5. Hit:4 http://cn.archive.ubuntu.com/ubuntu focal-security InRelease
  6. Get:5 https://packages.cloud.google.com/apt kubernetes-xenial InRelease [8,993 B]
  7. Get:6 https://packages.cloud.google.com/apt kubernetes-xenial/main amd64 Packages [37.7 kB]
  8. Fetched 46.7 kB in 7s (6,586 B/s)
  9. Reading package lists… Done

3)、完成上述2步后就可以通过apt-get命令安装kubeadm了,如下:

  1. root@kubernetesnode01:~# apt-get install -y docker.io kubeadm
  2. Reading package lists… Done
  3. Building dependency tree
  4. Reading state information… Done
  5. The following additional packages will be installed:
  6. bridge-utils cgroupfs-mount conntrack containerd cri-tools dns-root-data dnsmasq-base ebtables kubectl kubelet kubernetes-cni libidn11 pigz runc socat ubuntu-fan
  7. ….

这里直接使用Ubantu的docker.io安装源。在上述安装kubeadm的过程中,kubeadm和kubelet、kubectl、kubernetes-cni这几个kubernetes核心组件的二进制文件都会被自动安装好。

4)、Docker服务启动及限制修改

完成上述步骤侧操作后,系统中会自动安装Docker引擎,但是在具体运行kubernetes部署之前需要对Docker的配置信息进行一些调整。

首先,编辑系统/etc/default/grub文件,在配置项GRUB_CMDLINE_LINUX中添加如下参数:

  1. GRUB_CMDLINE_LINUX=” cgroup_enable=memory swapaccount=1″

完成编辑后保存执行如下命令,并重启服务器,命令如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# update-grub
  2. root@kubernetesnode01:/opt/kubernetes-config# reboot

上述修改主要解决的是可能出现的“docker警告WARNING: No swap limit support”问题。

其次,编辑创建/etc/docker/daemon.json文件,添加如下内容:

  1. {
  2.   “exec-opts”: [“native.cgroupdriver=systemd”]
  3. }

完成保存后执行重启Docker命令,如下:

root@kubernetesnode01:/opt/kubernetes-config# systemctl restart docker

此时可以查看Docker的Cgroup信息,如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# docker info | grep Cgroup
  2.  Cgroup Driver: systemd

上述修改主要解决的是“Dockercgroup driver. The recommended driver is “systemd””的问题。需要强调的是以上修改只是作者在具体安装操作是遇到的具体问题的解决整理,如在实践过程中遇到其他问题还需要自行查阅相关资料!

*后,需要注意由于kubernetes禁用虚拟内存,所以要先关闭掉swap否则就会在kubeadm初始化kubernetes的时候报错,具体如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# swapoff -a

该命令只是临时禁用swap,如要保证系统重启后仍然生效则需要“edit /etc/fstab”文件,并注释掉swap那一行。

完成以上操作后启动系统Docker服务,命令如下:

root@kubenetesnode02:~# systemctl enable docker.service

%title插图%num

部署Kubernetes的Master节点

在Kubernetes中Master节点是集群的控制节点,它是由三个紧密协作的独立组件组合而成,分别是负责API服务的kube-apiserver、负责调度的kube-scheduler以及负责容器编排的kube-controller-manager,其中整个集群的持久化数据由kube-apiserver处理后保存在Etcd中。

要部署Master节点可以直接通过kubeadm进行一键部署,但这里我们希望能够部署一个相对完整的Kubernetes集群,可以通过配置文件来开启一些实验性的功能。具体在系统中新建/opt/kubernetes-config/目录,并创建一个给kubeadm用的YAML文件(kubeadm.yaml),具体内容如下:

  1. apiVersion: kubeadm.k8s.io/v1beta2
  2. kind: ClusterConfiguration
  3. controllerManager:
  4.  extraArgs:
  5.      horizontal-pod-autoscaler-userestclients: “true
  6.      horizontalpodautoscalersyncperiod: “10s
  7.      nodemonitorgraceperiod: “10s
  8. apiServer:
  9.   extraArgs:
  10.      runtimeconfig: “api/all=true
  11. kubernetesVersion: “v1.18.1″

在上述yaml配置文件中“horizontal-pod-autoscaler-use-rest-clients: “true””这个配置,表示将来部署的kuber-controller-manager能够使用自定义资源(CustomMetrics)进行自动水平扩展,感兴趣的读者可以自行查阅相关资料!而“v1.18.1”就是要kubeadm帮我们部署的Kubernetes版本号。

需要注意的是,如果执行过程中由于国内网络限制问题导致无法下载相应的Docker镜像,可以根据报错信息在国内网站(如阿里云)上找到相关镜像,然后再将这些镜像重新tag之后再进行安装。具体如下:

  1. #从阿里云Docker仓库拉取Kubernetes组件镜像
  2. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver-amd64:v1.18.1
  3. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager-amd64:v1.18.1
  4. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler-amd64:v1.18.1
  5. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64:v1.18.1
  6. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd-amd64:3.4.3-0
  7. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2
  8. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.7

下载完成后再将这些Docker镜像重新tag下,具体命令如下:

  1. #重新tag镜像
  2. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 k8s.gcr.io/pause:3.2
  3. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.7 k8s.gcr.io/coredns:1.6.7
  4. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/etcd-amd64:3.4.3-0 k8s.gcr.io/etcd:3.4.3-0
  5. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler-amd64:v1.18.1 k8s.gcr.io/kube-scheduler:v1.18.1
  6. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager-amd64:v1.18.1 k8s.gcr.io/kube-controller-manager:v1.18.1
  7. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver-amd64:v1.18.1 k8s.gcr.io/kube-apiserver:v1.18.1
  8. docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64:v1.18.1 k8s.gcr.io/kube-proxy:v1.18.1

此时通过Docker命令就可以查看到这些Docker镜像信息了,命令如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# docker images
  2. REPOSITORY                                                                          TAG                 IMAGE ID            CREATED             SIZE
  3. k8s.gcr.io/kube-proxy                                                               v1.18.1             4e68534e24f6        2 months ago        117MB
  4. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64                v1.18.1             4e68534e24f6        2 months ago        117MB
  5. k8s.gcr.io/kube-controller-manager                                                  v1.18.1             d1ccdd18e6ed        2 months ago        162MB
  6. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager-amd64   v1.18.1             d1ccdd18e6ed        2 months ago        162MB
  7. k8s.gcr.io/kube-apiserver                                                           v1.18.1             a595af0107f9        2 months ago        173MB
  8. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver-amd64            v1.18.1             a595af0107f9        2 months ago        173MB
  9. k8s.gcr.io/kube-scheduler                                                           v1.18.1             6c9320041a7b        2 months ago        95.3MB
  10. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler-amd64            v1.18.1             6c9320041a7b        2 months ago        95.3MB
  11. k8s.gcr.io/pause                                                                    3.2                 80d28bedfe5d        4 months ago        683kB
  12. registry.cn-hangzhou.aliyuncs.com/google_containers/pause                           3.2                 80d28bedfe5d        4 months ago        683kB
  13. k8s.gcr.io/coredns                                                                  1.6.7               67da37a9a360        4 months ago        43.8MB
  14. registry.cn-hangzhou.aliyuncs.com/google_containers/coredns                         1.6.7               67da37a9a360        4 months ago        43.8MB
  15. k8s.gcr.io/etcd                                                                     3.4.3-0             303ce5db0e90        8 months ago        288MB
  16. registry.cn-hangzhou.aliyuncs.com/google_containers/etcd-amd64                      3.4.3-0             303ce5db0e90        8 months ago        288MB

解决镜像拉取问题后再次执行kubeadm部署命令就可以完成Kubernetes Master控制节点的部署了,具体命令及执行结果如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# kubeadm init –config kubeadm.yaml –v=5
  2. Your Kubernetes control-plane has initialized successfully!
  3. To start using your cluster, you need to run the following as a regular user:
  4.   mkdir -p $HOME/.kube
  5.   sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  6.   sudo chown $(id -u):$(id -g) $HOME/.kube/config
  7. You should now deploy a pod network to the cluster.
  8. Run “kubectl apply -f [podnetwork].yaml” with one of the options listed at:
  9.   https://kubernetes.io/docs/concepts/cluster-administration/addons/
  10. Then you can join any number of worker nodes by running the following on each as root:
  11. kubeadm join 10.211.55.6:6443 –token jfulwi.so2rj5lukgsej2o6 \
  12.     –discovery-token-ca-cert-hash sha256:d895d512f0df6cb7f010204193a9b240e8a394606090608daee11b988fc7fea6

从上面部署执行结果中可以看到,部署成功后kubeadm会生成如下指令:

  1. kubeadm join 10.211.55.6:6443 –token d35pz0.f50zacvbdarqn2vi \
  2.     –discovery-token-ca-cert-hash sha256:58958a3bf4ccf4a4c19b0d1e934e77bf5b5561988c2274364aaadc9b1747141d

这个kubeadm join命令就是用来给该Master节点添加更多Worker(工作节点)的命令,后面具体部署Worker节点的时候将会使用到它。此外,kubeadm还会提示我们*次使用Kubernetes集群所需要配置的命令:

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

而需要这些配置命令的原因在于Kubernetes集群默认是需要加密方式访问的,所以这几条命令就是将刚才部署生成的Kubernetes集群的安全配置文件保存到当前用户的.kube目录,之后kubectl会默认使用该目录下的授权信息访问Kubernetes集群。如果不这么做的化,那么每次通过集群就都需要设置“export KUBE CONFIG 环境变量”来告诉kubectl这个安全文件的位置。

执行完上述命令后,现在我们就可以使用kubectlget命令来查看当前Kubernetes集群节点的状态了,执行效果如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# kubectl get nodes
  2. NAME               STATUS     ROLES    AGE   VERSION
  3. kubernetesnode01   NotReady   master   35m   v1.18.4

在以上命令输出的结果中可以看到Master节点的状态为“NotReady”,为了查找具体原因可以通过“kuberctl describe”命令来查看下该节点(Node)对象的详细信息,命令如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# kubectl describe node kubernetesnode01

该命令可以非常详细地获取节点对象的状态、事件等详情,这种方式也是调试Kubernetes集群时*重要的排查手段。根据显示的如下信息:

  1. Conditions
  2. Ready False… KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized

可以看到节点处于“NodeNotReady”的原因在于尚未部署任何网络插件,为了进一步验证着一点还可以通过kubectl检查这个节点上各个Kubernetes系统Pod的状态,命令及执行效果如下:

  1. root@kubernetesnode01:/opt/kubernetes-config# kubectl get pods -n kube-system
  2. NAME                                       READY   STATUS    RESTARTS   AGE
  3. coredns-66bff467f8-l4wt6                   0/1     Pending   0          64m
  4. coredns-66bff467f8-rcqx6                   0/1     Pending   0          64m
  5. etcd-kubernetesnode01                      1/1     Running   0          64m
  6. kube-apiserver-kubernetesnode01            1/1     Running   0          64m
  7. kube-controller-manager-kubernetesnode01   1/1     Running   0          64m
  8. kube-proxy-wjct7                           1/1     Running   0          64m
  9. kube-scheduler-kubernetesnode01            1/1     Running   0          64m

命令中“kube-system”表示的是Kubernetes项目预留的系统Pod空间(Namespace),需要注意它并不是Linux Namespace,而是Kuebernetes划分的不同工作空间单位。回到命令输出结果,可以看到coredns等依赖于网络的Pod都处于Pending(调度失败)的状态,这样说明了该Master节点的网络尚未部署就绪。

%title插图%num

部署Kubernetes网络插件

前面部署Master节点中由于没有部署网络插件,所以节点状态显示“NodeNotReady”状态。接下来的内容我们就来具体部署下网络插件。在Kubernetes“一切皆容器”的设计理念指导下,网络插件也会以独立Pod的方式运行在系统中,所以部署起来也很简单只需要执行“kubectl apply”指令即可,例如以Weave网络插件为例:

  1. root@kubernetesnode01:/opt/kubernetes-config# kubectl apply -f https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d ‘\n’)
  2. serviceaccount/weave-net created
  3. clusterrole.rbac.authorization.k8s.io/weave-net created
  4. clusterrolebinding.rbac.authorization.k8s.io/weave-net created
  5. role.rbac.authorization.k8s.io/weave-net created
  6. rolebinding.rbac.authorization.k8s.io/weave-net created
  7. daemonset.apps/weave-net created

部署完成后通过“kubectl get”命令重新检查Pod的状态:

  1. root@kubernetesnode01:/opt/kubernetes-config# kubectl get pods -n kube-system
  2. NAME                                       READY   STATUS    RESTARTS   AGE
  3. coredns-66bff467f8-l4wt6                   1/1     Running   0          116m
  4. coredns-66bff467f8-rcqx6                   1/1     Running   0          116m
  5. etcd-kubernetesnode01                      1/1     Running   0          116m
  6. kube-apiserver-kubernetesnode01            1/1     Running   0          116m
  7. kube-controller-manager-kubernetesnode01   1/1     Running   0          116m
  8. kube-proxy-wjct7                           1/1     Running   0          116m
  9. kube-scheduler-kubernetesnode01            1/1     Running   0          116m
  10. weave-net-746qj

可以看到,此时所有的系统Pod都成功启动了,而刚才部署的Weave网络插件则在kube-system下面新建了一个名叫“weave-net-746qj”的Pod,而这个Pod就是容器网络插件在每个节点上的控制组件。

到这里,Kubernetes的Master节点就部署完成了,如果你只需要一个单节点的Kubernetes,那么现在就可以使用了。但是在默认情况下,Kubernetes的Master节点是不能运行用户Pod的,需要通过额外的操作进行调整,感兴趣的朋友可以自己查阅下其他资料。

%title插图%num

部署Kubernetes的Worker节点

为了构建一个完整的Kubernetes集群,这里还需要继续介绍如何部署Worker节点。实际上Kubernetes的Worker节点和Master节点几乎是相同的,它们都运行着一个kubelet组件,主要的区别在于“kubeadm init”的过程中,kubelet启动后,Master节点还会自动启动kube-apiserver、kube-scheduler及kube-controller-manager这三个系统Pod。

在具体部署之前与Master节点一样,也需要在所有Worker节点上执行前面“安装kubeadm及Decker环境”小节中的所有步骤。之后在Worker节点执行部署Master节点时生成的“kubeadm join”指令即可,具体如下:

  1. root@kubenetesnode02:~# kubeadm join 10.211.55.6:6443 –token jfulwi.so2rj5lukgsej2o6     –discovery-token-ca-cert-hash sha256:d895d512f0df6cb7f010204193a9b240e8a394606090608daee11b988fc7fea6 –v=5
  2. This node has joined the cluster:
  3. * Certificate signing request was sent to apiserver and a response was received.
  4. * The Kubelet was informed of the new secure connection details.
  5. Run ‘kubectl get nodes’ on the control-plane to see this node join the cluster.

完成集群加入后为了便于在Worker节点执行kubectl相关命令,需要进行如下配置:

  1. #创建配置目录
  2. root@kubenetesnode02:~# mkdir -p $HOME/.kube
  3. #将Master节点中$/HOME/.kube/目录中的config文件拷贝至Worker节点对应目录
  4. root@kubenetesnode02:~# scp root@10.211.55.6:$HOME/.kube/config $HOME/.kube/
  5. #权限配置
  6. root@kubenetesnode02:~# sudo chown $(id -u):$(id -g) $HOME/.kube/config

之后可以在Worker或Master节点执行节点状态查看命令“kubectl get nodes”,具体如下:

  1. root@kubernetesnode02:~# kubectl get nodes
  2. NAME               STATUS     ROLES    AGE   VERSION
  3. kubenetesnode02    NotReady   <none>   33m   v1.18.4
  4. kubernetesnode01   Ready      master   29h   v1.18.4

通过节点状态显示此时Work节点还处于NotReady状态,具体查看节点描述信息如下:

  1. root@kubernetesnode02:~# kubectl describe node kubenetesnode02
  2. Conditions:
  3. Ready False … KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized

根据描述信息,发现Worker节点NotReady的原因也在于网络插件没有部署,继续执行“部署Kubernetes网络插件”小节中的步骤即可。但是要注意部署网络插件时会同时部署kube-proxy,其中会涉及从k8s.gcr.io仓库获取镜像的动作,如果无法访问外网可能会导致网络部署异常,这里可以参考前面安装Master节点时的做法,通过国内镜像仓库下载后通过tag的方式进行标记,具体如下:

  1. root@kubenetesnode02:~# docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64:v1.18.1
  2. root@kubenetesnode02:~# docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64:v1.18.1 k8s.gcr.io/kube-proxy:v1.18.1

如若一切正常,则继续查看节点状态,命令如下:

  1. root@kubenetesnode02:~# kubectl get node
  2. NAME               STATUS   ROLES    AGE     VERSION
  3. kubenetesnode02    Ready    <none>   7h52m   v1.18.4
  4. kubernetesnode01   Ready    master   37h     v1.18.4

可以看到此时Worker节点的状态已经变成“Ready”,不过细心的读者可能会发现Worker节点的ROLES并不像Master节点那样显示“master”而是显示了<none>,这是因为新安装的Kubernetes环境Node节点有时候会丢失ROLES信息,遇到这种情况可以手工进行添加,具体命令如下:

  1. root@kubenetesnode02:~# kubectl label node kubenetesnode02 node-role.kubernetes.io/worker=worker

再次运行节点状态命令就能看到正常的显示了,命令效果如下:

  1. root@kubenetesnode02:~# kubectl get node
  2. NAME               STATUS   ROLES    AGE   VERSION
  3. kubenetesnode02    Ready    worker   8h    v1.18.4
  4. kubernetesnode01   Ready    master   37h   v1.18.4

到这里就部署完成了具有一个Master节点和一个Worker节点的Kubernetes集群了,作为实验环境它已经具备了基本的Kubernetes集群功能。

%title插图%num

部署Dashboard可视化插件

在Kubernetes社区中,有一个很受欢迎的Dashboard项目,它可以给用户一个可视化的Web界面来查看当前集群中的各种信息。该插件也是以容器化方式进行部署,操作也非常简单,具体可在Master、Worker节点或其他能够安全访问Kubernetes集群的Node上进行部署,命令如下:

  1. root@kubenetesnode02:~# kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.3/aio/deploy/recommended.yaml

部署完成后就可以查看Dashboard对应的Pod运行状态,执行效果如下:

  1. root@kubenetesnode02:~# kubectl get pods -n kubernetes-dashboard
  2. NAME                                         READY   STATUS    RESTARTS   AGE
  3. dashboard-metrics-scraper-6b4884c9d5-xfb8b   1/1     Running   0          12h
  4. kubernetes-dashboard-7f99b75bf4-9lxk8        1/1     Running   0          12h

除此之外还可以查看Dashboard的服务(Service)信息,命令如下:

  1. root@kubenetesnode02:~# kubectl get svc -n kubernetes-dashboard
  2. NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
  3. dashboard-metrics-scraper   ClusterIP   10.97.69.158    <none>        8000/TCP   13h
  4. kubernetes-dashboard        ClusterIP   10.111.30.214   <none>        443/TCP    13h

需要注意的是,由于Dashboard是一个Web服务,从安全角度出发Dashboard默认只能通过Proxy的方式在本地访问。具体方式为在本地机器安装kubectl管理工具,并将Master节点$HOME/.kube/目录中的config文件拷贝至本地主机相同目录,之后运行“kubectl proxy”命令,如下:

  1. qiaodeMacBook-Pro-2:.kube qiaojiang$ kubectl proxy
  2. Starting to serve on 127.0.0.1:8001

本地proxy代理启动后,访问Kubernetes Dashboard地址,具体如下:

  1. http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

如果访问正常,就会看到相应的界面。

发掘 CPU 与超级工厂的共性,程序员的心思你别猜

18世纪流水线的诞生带来了制造技术的变革,人类当今拥有琳琅满目物美价廉的商品和流水线技术的发明密不可分,因此当你喝着可乐、吹着空调、坐在特斯拉里拿着智能手机刷这篇文章时需要感谢流水线技术

%title插图%num

一段有趣的代码

有这样一段代码:

  1. for (int k = 0; k < 10000; k++){
  2.   for (int i = 0; i < arr.size(); i++) {
  3.       if (arr[i] > 256)
  4.           sum += arr[i];
  5.   }
  6. }

这段代码非常简单,给定一个数组,计算所有大于256 的元素之和,重复计算 10000 遍。这段代码本身平淡无奇,但有趣的是:如果这个数组是有序的,那么这段代码的运行速度会比处理无序数组快将近 10 倍(不同的机器、CPU架构可能会稍有差异)。可这是为什么呢?这和制造业使用的流水线又有什么关系呢?且听我慢慢道来。

%title插图%num

流水线技术的诞生

1769年,英国人乔赛亚·韦奇伍德开办了一家陶瓷工厂,这家工厂生产的陶瓷乏善可陈,但其内部的管理方式*具创新性,传统的方法都是由制陶工专人来完成,但韦奇伍德研究后将整个制陶工艺流程分成了几十道工序,每一道工序都交给专人完成,这样传统的制陶人不复存在,这便是工业流水线*早的雏形。

虽然流水线技术可以说是英国人发明的,但发扬光大的却是美国人,这便是福特与T型车。

%title插图%num

20世纪初,福特将流水线技术应用到汽车的批量生产,效率得到千倍提高,使得汽车这种奢侈品开始能够为大众消费,深刻影响了现代社会的方方面面,注意上图中一辆车的价格。

100 年后又一个美国人携带他的时尚电动车再一次席卷全球,这就是特斯拉。

接下来我们仔细看一下流水线技术。

%title插图%num

特斯拉与流水线

假设组装一辆特斯拉需要经过:组装车架、安装引擎、安装电池、检验四道工序,同时假设每个步骤需要 20 分钟,因此如果所有工序都由一个组装站点来完成,那么组装一辆特斯拉需要80分钟。

但如果每个步骤都交给一个特定站点来组装的话就不一样了,此时生产一辆车的时间依然是80分钟,但这只是*辆车所需要的时间,此后工厂可以每20分钟就交付一辆特斯拉。

%title插图%num

注意,流水线并没有减少组装一辆车的时间,只是增加了工厂的吞吐能力。流水线技术的使用*大增加了工厂交付车辆的效率。

%title插图%num

CPU 与超级工厂

其实 CPU 本身也是一座超级工厂。只不过CPU这座工厂生产的不是特斯拉,而是机器指令。工厂内部有流水线*大提高了生产效率,CPU 没有理由不拥有。

你可以想象一下,不管你现在看这篇文章用的是PC 还是智能手机,其内部的 CPU 都有一条复杂度不亚于特斯拉超级工厂的流水生产线

如果我们把CPU处理的一条机器指令当做一辆特斯拉的话,那么对于现代CPU这座超级工厂来说,一秒钟的时间内可以交付数十亿量特斯拉,效率完爆任何当今制造界的工业流水线,CPU 才是一座名副其实的超级工厂

如果特斯拉超级工厂也如 CPU 一般高效的话,特斯拉可能比现在的自行车都要便宜,地球人民人手一辆特斯拉不成问题,算上外星人也不成问题。

%title插图%num

机器指令与流水线

实际上说 CPU 生产机器指令是不正确的,CPU 其实不是在生产机器指令而是在处理机器指令,生产机器指令的是编译器,CPU需要处理机器指令以此来指挥整个计算机系统工作。

同生产一辆特斯拉需要四道工序一样,处理一条机器指令大体上也可以分为四个步骤:取指、译码、执行、回写,这几个阶段分别由特定的硬件来完成 (注意,真实 CPU 内部可能会将执行一条指令分解为数十个阶段)。

%title插图%num

怎么样,是不是和超级工厂生产特斯拉没什么区别,当今CPU用每秒处理数十亿机器指令的能力驱动着智能手机好让你流畅的刷公众号、短视频、刷微博、刷知乎,这里,流水线技术功不可没。

%title插图%num

当 if 遇到流水线

实际上 CPU 内部的流水线和现实中的并不完全一样。程序员在代码中编写的 if 语句一般会被编译器翻译成一条跳转指令,if 语句其实起到一种分支的作‍‍用,如果条件成立则需要执行if内部的逻辑,否则不执行;因此跳转指令会依赖自身的执行结果来决定到底要不要跳转,这会对流水线产生影响。

有的同学可能不明白,这能产生什么影响呢?

现在,让我们仔细观察一下特斯拉流水线,你会发现当前一辆车还没有完全制造完成时后一辆车就已经进入到流水线了

%title插图%num

对于CPU来说道理是一样的,当一条跳转指令还没有完成时后面的指令就需要进入到流水线,因此问题来了:

跳转指令需要依赖自身的执行结果来决定到底要不要跳转,那么在跳转指令没有执行完的情况下 CPU 怎么知道后面哪个分支的指令能进入到流水线呢

%title插图%num

CPU 能预测未来吗?

%title插图%num

预测未来

对此 CPU 当然是不知道的。那么该怎么办呢?很简单,一个字,

你没有看错,CPU 会猜一下 if 语句可能会走哪个分支,如果猜对了流水线照常继续,如果猜错了,对不起,流水线上已经执行的后续指令全部作废,因此我们可以看到如果CPU猜错了会有性能损耗。

现代 CPU 将“猜”的这个过程称为分支预测

当然,CPU 中的分支预测并不是简单的抛硬币式的随机瞎猜,而且有特定策略,比如可能会基于执行跳转指令的历史去进行预测等等。

知道了分支预测就可以解释文章开头的问题了。

现在我们知道,程序员编写的 if 语句对应的是跳转指令:

if (arr[i] >= 256) {sum += arr[i];}

CPU 在执行完跳转指令之前必须决定后续哪个分支的指令会进入到流水线,猜对了流水线照常进行,猜错了有性能损耗。

那么如果一个数组是有序的:

%title插图%num

而如果一个数组是无序的:

%title插图%num

你觉得哪种更好猜一些?

如果你给CPU一个无序数组,那么 Arr[i] 是否大于256 基本上就是随机的,对于随机事件,不要说CPU的分支预测,任何其它预测手段都将无效,否则这就不是随机事件了

如果 CPU 猜的不对,那么流水线上的后续指令将作废,这就解释了为什么处理有序数组要比处理无序数组性能好了,因为在数组有序的情况下,CPU 的分支预测几乎不会猜错,流水线上的指令不会被频繁作废。

这对程序员的启示就是:如果你编写了 if 语句,那么你*好让 CPU 大概率能猜对

有的同学看到这里,可能会觉得每一条 if 语句都性能低下,恨不得从此不再写if else,真的是这样吗?

%title插图%num

编写 If else时需要注意什么

实际上如果你编写的if语句没有位于对性能要求很高的核心代码部分,那么分支预测失败这种问题无需关心。

实际上现代 CPU 的分支预测是很聪明的,对于非核心部分的if 语句分支预测失败带来的性能损失可以忽略不计。

但是对于文章开头提到的代码,程序的大部分时间都用在了 for 循环中,这时你就要注意了,当然前提还是这段代码对时间要求非常严苛,否则你也没必要为了这点性能去优化。

好奇的同学可能会问,如果给定的数组是无序的,那么上面提到的这段该怎么优化呢?

%title插图%num

性能优化

实际上非常简单,只需要移除 if 语句就可以,该怎么移除呢?

没有 if 语句的话,那么 sum 每次都必须加上一个数,如果arr[i]比256大,那么 sum 加上差值,否则 sum 加 0即可,这样就消除了if 判断。

我们计算arr[i] – 256的值,并将其向右移动31位:

(arr[i] - 256) >> 31

这样得到的数不是0 (0x00000000),就是 -1 (0xffffffff),然后我们对其取反,再次与上 arr[i] 即可:

sum += ~((arr[i] - 256) >> 31) & arr[i];

也就是说如果arr[i] – 256 大于0 的话那么差值会与上 0xffffffff,其结果就是保持不变,否则会与上0,其结果就是sum会加上0,这样就不需要 if 判断了。

利用位运算,即使数组是无序的也不会有性能问题,代价就是代码可读性会降低很多,这里,我们再一次看到天下没有免费的午餐

%title插图%num

总结

虽然 CPU 体积很小,只有指甲那么大,但 CPU 可能是人类有史以来建造过的*复杂的东西,在这里实现了很多有趣的功能,程序员只有彻底理解 CPU 才能更好的利用这些功能编写性能优异的程序。

华为庞鑫:闪存3.0时代,四大变化激发全闪存数据中心潜能释放

从2005年到2019年间,中国数字经济总体规模由2.6万亿元增加至35.8万亿元,数字经济在GDP的占比也由14.2%提升至36.2%。随着数字经济蓬勃发展,数据也成为当之无愧的关键生产要素,是基础性资源和战略性资源。数据洪流的到来进一步驱动了数据价值链的成熟,进而驱动企业的决策和创新,获取数字红利。数据中心作为数据的载体,承担着数据资源集中存储、计算、交换的功能,转型升级迫在眉睫。

4月13日,在2021华为全球分析师大会上,华为数据存储与机器视觉营销工程部总裁庞鑫向全行业解读了华为对存储产业以及全闪存数据中心的理解与洞察。

%title插图%num

%title插图%num

全闪存市场是刚需,但当前全球全闪存市场占比仍然很低

庞鑫表示,全闪存凭借出色的性能优势和绿色节能已经成为市场刚需。在性能方面,从IOPS、低延时、吞吐量三个常用的存储性能指标来看,闪存SSD在性能上远超机械硬盘HDD。闪存具有高的IOPS,一个SSD的IOPS可达数万次之多,而一个HDD的IOPS只能达到几百次,这表明需要许多HDD才能达到一个SSD的性能。同时闪存还具有低延迟(常以毫秒或微秒为度量单位)性能和大吞吐量。

在耗能方面,数据中心已经成为事实上的耗电大户。目前全球数据中心的耗电大约占据全球每年总用电量的2%-3%,电费在数据中心运营成本中占比超过50%。而在国内,全国数据中心的耗电量已连续八年以超过12%的速度增长,未来占社会总用电量的比例将持续增长。相对于HDD,在相同的容量下,SSD的电力能耗降低70%,空间占用节省50%,不仅能大幅降低数据中心的TCO总成本,并且能有效推进数据中心低碳运转。

虽然闪存在性能及绿色节能上优势明显,但是当前闪存在存储市场的占比仍然不高。数据显示,全球存储市场闪存占比43.7%,在中国市场这一数字仅为24.7%。加速闪存替代机械硬盘已经势在必行。

%title插图%num

四大变化,将全面激发全闪存数据中心潜能释放

对于过去十年闪存存储产业的发展,庞鑫将其总结为2010~2015年闪存1.0时代,2015~2020年闪存2.0时代,未来5年,将迈入闪存3.0时代。在闪存1.0时代,仅关注性能,主要通过高性能硬件实现,全闪存作为存储系统的加速层,软件特性非常缺乏,价格昂贵。在闪存2.0时代,闪存开始进入主存储系统,通过系统化设计,提供丰富企业特性,同时通过数据缩减技术不断降低闪存存储成本,全闪存进入快速发展期。进入闪存3.0时代,四大变化将全面激发全闪存数据中心潜能释放,进一步提升端到端性能,并实现绿色节能、安全可靠。

*个变化是全场景闪存普惠。全闪存将从之前只用于核心数据存储,比如金融核心、运营商计费系统,越来越多地走向全行业、全场景,比如超算、大数据场景也逐渐开始使用性能好、耗能低的全闪存存储。

第二个变化是全场景数据保护。随着全闪存应用场景不断演进,数据全生命周期保护出现新的要求,包括保护等级的变化、保护对象的变化、保护性能的变化以及保护规模的变化。保护等级上,以银行为例,资产规模千亿以下时要求建立同城灾备,当总资产规模超过千亿,就需要建立两地三中心灾备。保护对象上,以前是结构化数据保护,现在需要同时保护结构化和非结构化数据。比如医院HIS系统是结构化数据,而PACS影像是非结构化数据,要求保存10年以上,并且至少3年在线,可供快速调阅和诊断使用。保护性能上,灾备的冷数据需要能变成温热数据,发挥数据的价值。保护规模上,数据从PB级增长到EB级。比如L4级自动驾驶数据,每车每天路测数据达60TB,是L2级的50倍。

第三个变化是多元融合管理。从传统的烟囱式建设到全闪存整合,形成PB级资源池,这就需要存储系统具备多元融合管理的能力。

第四个变化是高速网络联接。未来的存储网络需要用NVMe协议来替代SCSI协议,让全闪存*大程度释放性能潜力。

%title插图%num

全闪存数据中心四大能力引领未来建设

在庞鑫看来,建设全闪存数据中心,不仅仅是对介质进行升级,同时也是将数据中心资源进行整合,架构进行重构,以应对未来业务多样性的需求。华为通过提供全场景闪存化、全场景数据保护、全生命周期自动化运维管理以及全IP数据中心网络四大能力实现数据中心效率更高、安全等级更高、运维更智能、网络无瓶颈、绿色低成本的目标。

%title插图%num

1. 全场景闪存化,绿色节能,迈向碳中和

全场景闪存化是指多类型的数据负载均应通过闪存存储进行业务加速,除了主存的全闪化替代之外,华为建议存储灾备系统也应加大闪存配比,让灾备数据快取快用,实现存储容灾的常态化和备份存储的生产化能力。

在主存上,借助*新的SCM介质,可以对热数据进一步加速,时延低至亚毫秒级;在备份上,通过闪存加速,有效提升备份系统性能,将原来的备份时间由小时级缩短为分钟级,确保备份数据即时可用;在归档上,SSD作为归档缓存提供高访问性能,归档数据访问时间从小时级降低到分钟级。

华为还致力于*数据缩减算法研究,实现有限的空间能存放更多的数据,加速闪存普惠。华为在全球数学圣地法国巴黎和俄罗斯莫斯科,分别建立了算法研究中心,将数据的缩减做到*致。去年华为举办的全球数据压缩大赛,里面有不少算法就开始应用到华为全闪存存储产品上。

2. 全场景数据保护,保护对象更广、业务响应更快、留存时间更久

随着数据量越来越多,数据越来越重要,更大规模和更多类型的数据需要保护,数据灾备速度、恢复速度、副本使用频度、乃至留存时间,都需要大幅的提升。

从保护对象的角度来看,灾备数据的范围变大,从核心到通用到一般,从生产核心扩大到开发测试、分析训练、视频监控,从结构化数据扩大到非结构化数据。这一趋势导致灾备数据量迅速变大,但备份窗口不变或变小。数据灾备的等级需要提升,从本地的双活/主备容灾,到异地3DC,再到灾备上云,进行全方位的数据保护。同时备份数据需要立即可用,不需要数据恢复到生产设备后再使用。在数据留存方面,数据合规通常要求重要数据留存30年以上,同时具备防删改、防病毒,适应较为宽泛的机房环境。

3. 全生命周期自动化运维管理,提升运维效率

全闪存的普及将加速数据资源池化,而数据池化加剧了数据中心运维的难度,华为全闪存数据中心具备全生命周期自动化运维的能力,即在规划、建设、运维、优化的各个阶段,均具备自动化智能化的能力。

在规划阶段从关注设备到关注数据的全生命周期,实现精准规划;在建设阶段实现从人工到自动化,核心是全局资源发放;在运维阶段变被动巡检为主动发现,实现全栈智能运维;在优化阶段变依赖专家经验为智能算法自治,达到资源配置敏捷优化。

4. 全IP数据中心网络,*大化释放闪存性能潜力

全场景闪存化也推动了数据中心的网络改革,NVMe*大化释放了SSD介质的能力,更快的介质和协议呼唤更快的网络。NoF(NVMe over Fabric)存储网络应运而生,通过使用IP网络进行对专用网络的创新性革新,实现了更快吞吐带宽和更低时延,同时也兼具IP易管理优势,是更好地实现端到端NVMe存储网络的*佳方案,NoF方案也是目前业界*主流的趋势。

基于当下业界主流标准NoF方案,华为又依托在网络和存储领域的深厚积累,从可靠性、性能、易用性上对其进行提升,基于智能无损网络面向华为OceanStor存储提出了NoF+方案,将下一代数据中心存储网络推向更广阔的发展空间。

在演讲*后,庞鑫表示,包含了OceanStor全闪存、OceanProtect数据保护、DME全生命周期智能运维、NoF+高速存储网络在内的华为全闪存数据中心解决方案,是打造未来安全高效、绿色节能数据中心行之有效的方法。全闪存数据中心必将在未来大展身手,推动社会经济生产迈向新征程。


	

数据库选择编码方式(GBK、UTF-8)

1、毫无疑问,只要存储、处理文字,编码是必需的;网站一般绕不开文字,但数据库可以不管文字,只存些数值、日期、图片之类的。

2、GBK、UTF-8刚好是很多编码中的两种,也是两种风格(ANSI和UNICODE)中的代表

3、网站和数据库的编码可以不一致,通过中间程序能够相互转换就行;

4、对于存储汉字,UTF-8一般占3个字节,UTF-16LE占2个字节,GBK也是2个字节,存储空间是否充足也是选择时要考虑的

5、如果有存储多国语言文字的需要,那么就放弃GBK,投身unicode系列吧,UTF-8/UTF-16LE均可

6、数据库的字符编码其实也分服务器端和客户端两种,服务器端负责存储,客户端只是用来“沟通”,两者也可以不一样,只要能转换(一说“映射”)就行

gbk与utf8的区别和比较

GBK的文字编码是双字节来表示的,即不论中、英文字符均使用双字节来表示,只不过为区分中文,将其*高位都定成1。

至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码。对于英文字符较多的论坛则用UTF-8节省空间。

GBK包含全部中文字符;UTF-8则包含全世界所有国家需要用到的字符。

GBK是在国家标准GB2312基础上扩容后兼容GB2312的标准(好像还不是国家标准)

UTF-8编码的文字可以在各国各种支持UTF8字符集的浏览器上显示。

比如,如果是UTF8编码,则在外国人的英文IE上也能显示中文,而无需他们下载IE的中文语言支持包。 所以,对于英文比较多的论坛 ,使用GBK则每个字符占用2个字节,而使用UTF-8英文却只占一个字节。

UTF8是国际编码,它的通用性比较好,外国人也可以浏览论坛,GBK是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大

5G、射频、奥特曼,这仨有联系吗?

手机,作为移动互联网时代的标配,已经走进了我们每个人的生活。有了它,我们可以随心所欲地聊天、购物、追剧,享受美好的人生。

正因为手机如此重要,所以人们对相关技术的发展十分关注。每当有新品发布,媒体会进行长篇累牍的报道,社交网络上也会掀起热烈的讨论。

然而,人们对手机的关注,往往集中在CPU、GPU、基带、屏幕、摄像头上。有那么一个特殊的部件,对手机来说*为重要,却很少有人留意。

是哪个部件呢?没错,它就是我们今天文章的主角——射频。

%title插图%num

%title插图%num

什么是射频

射频,英文名是Radio Frequency,也就是大家熟悉的RF。从字面上来说,Radio Frequency是无线电频率的意思。射频信号,则特指频率范围在300KHz~300GHz的无线电磁波。

大家都知道,手机之所以能够和基站进行通信,靠的就是互相收发无线电磁波。

%title插图%num

手机里专门负责收发无线电磁波的一系列电路、芯片、元器件等,被统称为射频系统,简称“射频”(下同)。

射频和基带,是手机实现通信功能的基石。如果我们把手机与外界的通信看作是一项“快递服务”,那么,基带的职责是对数据进行“打包/拆包”。而射频的职责,则是将“包裹”通过指定的无线电频段发射出去/接收下来。

%title插图%num

示意图:左边是基带,右边是射频

射频到底长什么样?下面这张,是某品牌手机的主电路板正反面布局图。

%title插图%num%title插图%num

(图片来自ABI Research)

图中,黄色圈出的部分,全部属于射频。可以看出,射频元件在手机构造中,占据了不小的比例。

从架构上来看,一套完整的射频系统包括射频收发器、射频前端、天线三个部分。射频前端又包括功率放大器、包络追踪器、低噪声放大器、滤波器、天线开关、天线调谐器等多个组件。

%title插图%num

射频的架构

射频前端各个组件的作用并不复杂。例如,放大器,就是把信号放大,让信号传得更远;滤波器,是把杂波去掉,让信号更 “纯净”;天线开关,用于控制天线的启用与关闭;天线调谐器,主要作用是“摆弄”天线,获得*好的收发效果……

%title插图%num

数量众多的射频组件,相互配合,分工协作,就是为了完成“临门一脚”,把基带打包好的数据,“biu~biu~biu~”地发射出去。

%title插图%num

如果射频设计不合理,元器件性能落后,那么,将直接影响手机的无线信号收发能力,进而影响手机的通信能力。具体表现出来,就是无线信号差,通信距离短,网络速率慢,等等。

换言之,手机的射频能力不行,就好比汽车的动力不足,就算其它功能再花哨,也无法被用户所接受。

所以,手机厂商在研发设计手机时,通常都会在射频方面下足功夫,反复推敲并进行测试验证,才敢推出*终产品。

%title插图%num

5G射频的挑战

如今,我们昂首迈入了5G时代。相比传统4G,5G的射频系统有变化吗?

答案是肯定的。不仅有变化,而且是巨变。

5G相比4G,在性能指标上有了大幅的提升。5G的eMBB(增强型移动宽带)场景,将手机速率提升至千兆级甚至万兆级,分别是早期LTE速率(100Mbps)的10倍/100倍。

2G/3G/4G,加上5G,加上MIMO(多天线技术),加上双卡双待,手机的天线数量和支持频段翻倍增加。4G早期只有不到20个频段组合。相比之下,5G有超过10000个频段组合,复杂性堪称恐怖。

与此同时,为了确保用户愿意升(tāo)级(qián),5G手机的厚度和重量不能增加,功耗不能增加,待机时长不能减少。

换做你是手机厂商,你会不会抓狂?

%title插图%num

所以说,5G手机的射频,必须重塑自我,大力出奇迹搞创新

到底该如何解决射频系统的设计难题呢?高通提出了一个宏观的思路,直接提供“完整的调制解调器及射频系统”。通俗理解,就是把基带、射频收发器、射频前端、天线模组、软件框架等,全部都做好,给厂商一套完整的方案。

也就是说,5G手机等终端元器件设计的理念,必须摒弃以往“东市买骏马,西市买鞍鞯,南市买辔头,北市买长鞭”专注于单个元件的思路,转而采用“打包设计”的一体化系统级解决方案。

例如,以前是A厂造基带,B厂造射频,C厂造天线,然后手机D厂自己捣鼓如何整合和对接。现在,改成有实力的大厂直接把基带、射频和天线等一起打包设计好,然后交给手机厂商,拿了就能快速使用。

%title插图%num

系统级集成,是5G基带和射频复杂度大幅提升的必然结果。

这就好比是火车。以前绿皮车的车速慢,车厢和车头可以分开设计、制造,然后拼在一起运行。但是,到了高铁时代,速度指标翻倍,如果继续分开设计、制造,车厢和车头不能深度协同,不仅速度指标难以实现,还可能出现安全问题。

所以,高铁的动车组,通常都是统一设计和制造的。

也就是说,面对前面提及的苛刻5G指标,需要站在系统级集成的角度,对基带和射频进行整体设计。这样一来,才能让两者实现完美的软硬件协同,发挥*佳性能(吞吐率、覆盖范围等)。

除了达成指标之外,整合设计也有利于缩减系统的*终尺寸,减少对手机空间的占用。对于系统功耗和散热控制来说,整合设计也有明显优势。

*后一点,也是很重要的一点,提供系统级整合方案,可以降低手机厂商的设计难度,方便他们以更快的速度推出产品,抢占市场。

%title插图%num

%title插图%num

5G射频的黑科技

我们来具体看看,系统级集成的5G射频,到底有哪些有趣的黑科技。

首先,*个黑科技,就是宽带包络追踪。

前面介绍射频架构的时候,里面就有一个功率追踪器。功率追踪器是配合功率放大器使用的。

功率放大器是射频的核心元件,它就像一个喇叭,把小声音(信号)变成大声音(信号)。

%title插图%num

想要把喇叭吹响,肯定需要鼓足力气(电源供电)。功率追踪器的作用,就是控制吹喇叭的力度(功率)。

%title插图%num

传统的吹法,是APT法,也就是平均功率追踪。某一时间段内,吹的力量保持不变。

而宽带包络追踪(ET)技术,可以精确地控制力量。也就是说,基带(调制解调器)可以根据信号的变化,控制射频里的包络追踪器,进而精准控制无线信号的发射功率。

%title插图%num

包络追踪的虚耗电量明显小于传统平均功率追踪

(图片来自高通)

这样一来,体力(能量)大幅节约了,射频的功耗也就下降了,手机的待机时间得以增加。

精准的发射功率控制,帮助手机获得*佳的信号发射效率,从而获得更好的信道质量。在手机与基站“双向沟通”过程中,当手机获得更好的信道质量时,基站就能支持手机实现更优的上下行业务,例如支持2×2 MIMO,网速更加丝滑。此外,更好的信道质量,也为基站侧给手机分配更高阶的调制方式(例如256QAM)创造了条件,可以提升手机吞吐率,支持更快更优的数据传输业务。

高通此前发布的几代骁龙5G调制解调器及射频系统集成的宽带包络追踪器,就已经采用了上述技术。而其*新的宽带包络追踪器QET7100,与目前市场上其它厂商提供的*先进产品相比,能效提升了30%。

我们介绍的第二个黑科技,就是AI辅助信号增强技术。

这个技术是2月份刚推出的骁龙X65 5G调制解调器及射频系统中*新发布的新技术,也是行业里首次将大热的AI技术引入手机射频系统,用于增强信号。

AI辅助信号增强技术的核心,就是将AI技术引入天线调谐系统。天线调谐分为两种方式,一个是阻抗匹配,另一个是孔径调谐。

%title插图%num

我们先看看阻抗匹配。

所谓阻抗匹配,我们可以理解为是一种“接水管”的工作。

射频系统元件与天线之间对接,就像两根水管对接。当阻抗一致时,就是位置完美对应,这时水流*大,信号的效率*高。如果元件的阻抗发生偏移,那么水管就歪了,水流就小了,一部分水流也浪费了。

%title插图%num

导致阻抗变化的原因很多,例如手的触碰,还有插接数据线、安装手机壳等。即便是不同的持握手势(左手、右手、单手、双手),也会带来不同的阻抗。

%title插图%num

传统的阻抗匹配做法,就是在实验室对各种造成阻抗变化的原因进行测试,找到天线特征值,然后通过调制解调器控制射频元件进行阻抗调节,让接水管尽可能对准送水管。

而AI辅助信号增强技术,就是引入AI算法,对各种阻抗变化原因的天线特征值进行大数据分析和机器学习,实现对阻抗的智能调节,达到*完美的匹配效果。

说白了,就有点像在送水管和接水管之间,安装了一根对接软管,让水流尽可能不浪费。

%title插图%num

AI辅助信号增强,相当于射频和天线间的对接软管

孔径调谐相对来说较为简单,就是调节天线的电长度。

从辐射学的角度来说,天线的完美长度应该是波长的四分之一。现在的手机,因为全网通、双卡双待等原因,移动通信系统的工作频率是动态变化的。例如,有时候工作在2.6GHz,有时候工作在3.5GHz。

工作频率如果变化,意味着*佳波长也变化了。所以,需要对天线进行孔径调谐,调节天线的长度,拉长波峰,以此达到*佳效果。

总而言之,以阻抗匹配和孔径调谐为基础的天线调谐技术,主要作用是克服外部环境对天线信号的影响,对信号进行动态调节,改善用户体验。

%title插图%num

根据实际验证,凭借着AI辅助信号增强技术,系统的情境感知准确性可以提升30%,能够明显降低通话掉线率,提升速率、覆盖和续航。

%title插图%num

结语

5G射频系统的创新黑科技还有很多,例如多载波优化、去耦调谐、多SIM卡增强并发等。这些黑科技全部都是技术创新的成果。它们凝结了工程师们的智慧,也为5G终端的顺利推出奠定了基础。

如今的5G射频,已不再是基带的辅助,而是能够和基带平起平坐、相辅相成的重要手机组件。

随着5G网络建设的不断深入,除了手机通信之外,越来越多的5G垂直行业应用场景也开始落地开花。5G终端的形态将会变得五花八门,更大的考验将会摆在5G射频前端的面前。

届时5G射频又会玩出什么新花样?让我们拭目以待!

创建数据库时,设置数据库的编码方式

-- 创建数据库时,设置数据库的编码方式 

-- CHARACTER SET:指定数据库采用的字符集,utf8不能写成utf-8

-- COLLATE:指定数据库字符集的排序规则,utf8的默认排序规则为utf8_general_ci(通过show character set查看)

drop database if EXISTS dbtest;

create database dbtest CHARACTER SET utf8 COLLATE utf8_general_ci;

 

1

2

3

-- 修改数据库编码

alter database dbtest CHARACTER SET GBK COLLATE gbk_chinese_ci;

alter database dbtest CHARACTER SET utf8 COLLATE utf8_general_ci;

 

1

2

3

4

5

6

7

8

9

-- 创建表时,设置表、字段编码

use dbtest;

drop table if exists tbtest;

create table tbtest(

id int(10) auto_increment,

user_name varchar(60) CHARACTER SET GBK COLLATE gbk_chinese_ci,

email varchar(60),

PRIMARY key(id)

)CHARACTER SET utf8 COLLATE utf8_general_ci;

 

1

2

3

4

-- 修改表编码

alter table tbtest character set utf8 COLLATE utf8_general_ci;

-- 修改字段编码

ALTER TABLE tbtest MODIFY email VARCHAR(60) CHARACTER SET utf8 COLLATE utf8_general_ci;

 

1

2

3

4

-- 查看所有的字符编码

SHOW CHARACTER SET;

-- 查看创建数据库的指令并查看数据库使用的编码

show create database dbtest;

 

1

2

-- 查看数据库编码:

show variables like '%char%';