我们先来看看Kubernetes最基础的知识吧。最初接触Kubernetes时候,一般初学者都会被一大堆单词所迷惑(例如:Deployment、Pod、ReplicationController、ReplicaSet、Service等)。笔者刚开始学习时也遭遇到这种困境。但是,任何复杂的系统都是发源于最基本的公式或定理,Kubernetes虽然庞大且复杂,不过只要抓住一些基本的脉络(一些最基本的组件的定义及使用),入门便也是毫不费劲。
重点:曾经的笔者,幻想着这产品在业界这么成熟了,是不是可以借助于某些成熟的工具(如cubeAdmin、之流)而不用掌握yml以及命令行语法就可以轻松入门?如果你也有这样的想法,建议你及早抛弃掉。
Kubernetes架构图
Kubernetes 最常用的组件介绍
Node(节点)是 kubernetes 集群中的计算机,可以是虚拟机或物理机。每个 Node(节点)都由 master 管理。一个 Node(节点)可以有多个Pod(容器组),kubernetes master 会根据每个 Node(节点)上可用资源的情况,自动调度 Pod(容器组)到最佳的 Node(节点)上。
Pod 是一个Kubernetes中一个抽象的概念,用于存放一组 container(可包含一个或多个 container 容器),以及这些 container (容器)的一些共享资源,是Kubernetes 的最小单位。
译名为 部署。通过发布 Deployment,可以创建应用程序 (docker image) 的实例 (docker container),在旧版本的Kubernetes里,还有着ReplicationController,你可以认为Deployment属于ReplicationController的升级版。
Kubernetes 中的 Service 提供了负载的抽象层,它选择具备某些特征的 Pod(容器组)并为它们定义一个访问方式。Service(服务)使 Pod(容器组)之间的相互依赖解耦(原本从一个 Pod 中访问另外一个 Pod,需要知道对方的 IP 地址)。一个 Service(服务)选定哪些 Pod(容器组) 通常由 LabelSelector(标签选择器) 来决定。
一种全局的、为了代理不同后端 Service 而设置的负载均衡服务。Ingress由两部分组成:Ingress controller和Ingress服务。
如果按照上述的架构图+ 一堆概念的文字来读,是很难搞明白这些组件会起到怎样的作用。 如果我拿个“公司”来做个比喻的话,假设我把Kubenetes 当作一家正在运作的企业,Node 代表这家企业会拥有多少个办公场地。某一天业务做大有钱了以及业务需要扩建了,企业就可以通过买更多办公场地(Node)来扩大业务。
某一天,企业接到了新项目,当然要组建团队来服务这个项目,这个所组建的团队名字就叫做Pod(容器组)了,团队成员就是容器了,它可以是Java、可以是PHP或者是NodeJS等。
有了Pod ,那得有场所空间给他们干活,也得有人指挥调度。假设项目太大,一个团队忙不过来怎么办,得多弄几个团队一起做大买卖。那谁来负责给Pod(容器组) 去向Node申请空间,以及动态扩容团队数量 这个调度呢?这就是Deployment 的工作(兼负着公司内政的角色)。
Service ,主要做分发的工作。从业务上可以理解成一名客服吧。就像某云、某信,用户不能直接触达内部的技术人员(Pod), 只能打客服热线或者是发起工单,把请求抛给Service ,再由Service 分配给Pod 来处理。像极了nginx 里面的upstream。
如果把Service 比作成客服, Ingress 就是用户要拨打过去的热线电话。 它主要管理着域名与service ip地址的映射关系,以及做高并发的限流、负载均衡工作。 顾名思义Ingress Nginx 。
注:Deployment 、Service、Ingress 均部署在Master节点,所有流量的入口都在Master机器上(k3s与k8s最大的区别,在于Master节点能否集群)。 而Pod 则由Deployment调配 到不同的Node节点。
使用Pod 创建一个内部可访问的容器
- 测试准备工作
理解好上面的概念后,让我们尝试在 Kubernetes 中跑我们的应用。在 Kubernetes 中,作为最小单元的pod其实是由容器组成的。所以我们需要有容器相关的开发环境,目前在本地开发环境方,比较常用是的 Docker 公司开发的 Docker Desktop和 Rancher 公司开始的 Rancher Desktop,这两个软件都包含了 docker 工具,并且都同时支持 Windows 和 macOS。目前我在Mac机上使用的是Docker Desktop。在Mac机的最上面会出现一个小船图标。
安装完成后,测试Docker 是启动成功,执行命令
docker version
- 建立一个测试使用的NodeJS Demo
使用阿里云的EggJS 框架 快速建立一个Hello Demo。
#k3s-test 是项目目录
$ mkdir k3s-test && cd k3s-test
$ npm init egg --type=simple
$ npm i
编写一段简单的代码(如果嫌麻烦,也可以直接下载写好的Demo)
#1、添加app/controller/home.js
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
this.ctx.body="Hello K8s!";
}
}
module.exports = HomeController;
#2、添加app/router.js
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.home.index);
router.get('/index', controller.home.index);
};
#3、添加config/config.default.js以及config/config.prod.js
#相同代码
/* eslint valid-jsdoc: "off" */
'use strict';
/**
* @param {Egg.EggAppInfo} appInfo app info
*/
module.exports = appInfo => {
/**
* built-in config
* @type {Egg.EggAppConfig}
**/
const config = exports = {
bodyParser: {
jsonLimit: '10mb',
formLimit: '10mb'
}
};
// 添加自定义 中间件配置
config.middleware = [
'util', // 参数解析中间件
];
config.multipart = {
mode: 'file',
whitelist: ['.png', '.csv', '.jpg', '.jpeg', '.xlsx','.xls']
};
config.proxy = true;
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1620702942813_3365';
//公共存储目录
config.bashSavePath = './private/';
// add your user config here
const userConfig = {
// myAppName: 'egg',
};
config.security = {
csrf: {
enable: false,
}
};
config.cluster = {
listen: {
port: 7001,
hostname: '0.0.0.0'
//0.0.0.0 很关键,否则外网访问没法通进。
}
};
return {
...config,
...userConfig,
};
};
这个简单的Demo,会基于7001端口进行监听,首先要确保代码在本机运行的正确性
#编译
npm run dev
#测试
curl http://localhost:7001
#输出内容
Hello K8s!%
运行成功后,执行打包命令
npm install --production
- 生成Dockfile容器
创建一个Dockerfile 文件,与k3s-test 目录同级,如下图:
录入以下内容
#建于Node最新的镜像
FROM node:latest
#先在根目录下k3s-test目录迁移过去
COPY k3s-test/ /k3s-test/
#对目录进行授权
RUN chmod -R 777 /k3s-test
#设置为工作目录
WORKDIR /k3s-test
#输出7001端口
EXPOSE 7001
#启动执行npm run start
ENTRYPOINT ["npm", "run"]
CMD ["start"]
然后然后命令生成镜像。
#例如
docker build -t k3s-test:latest .
#成功后,应该先试着运行容器,检查是否成功
docker run -d -p 8080:7001 --name k3s-test k3s-test:latest
#测试
curl http://localhost:8080
#输出内容
Hello K8s!%
为了方便从其它地方能拉取镜像,我们必须有一个镜像仓库,最著名的就是 Docker Hub,你可以注册账号,上传自己的镜像。当然国内也有很多类似的服务,比如:阿里云。如果你还没有自己账号的镜像仓库,建议使用阿里云的 容器镜像服务,目前个人账号有 300 个免费的镜像额度,足够使用。 (注:阿里云镜像怎么使用,这里不具体说明了)
如果嫌创建镜像麻烦,也可以使用我创建好的Demo镜像地址(复制即可):。
- 部署到 k3s
与k8s的命令是一致的,部署到 k3s 使用 kubectl 命令完成。部署时支持使用纯命令的方式,也支持 yml 描述文件的方式。这里我们使用 yml 描述文件的方式。使用Multipass 进入server节点(请参照上一篇文章:Kubernetes(k3s)学习(一) -- 使用集群部署)
#进入server节点容器的命令
multipass shell server
#进入容器后,先创建文件:k3s-test.yml
sudo vi k3s-test.yml
#k3s-test.yml 的内容:
apiVersion: v1 #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本
kind: Pod #指定要创建的类型
metadata: #译名为元数据,即 Pod 的一些基本属性和信息
name: k3s-test #这个pod 的名称
labels: #标签,可以灵活定位一个或多个资源,其中key和value均可自定义,可以定义多组,目前不需要理解
app: k3s-test #app 为key ,k3s-test 为value,还可以定义多个
spec: #关于这个pod 的描述
containers: #要加载的docker 的容器
- name: k3s-test #容器名称
#容器要挂载的镜像
image: registry.cn-hangzhou.aliyuncs.com/dawson-project/hello-node:latest
我们在server 节点的根目录下已经创建好k3s-test.yml 文件,接下来执行创建命令即可。
#create 命令也可以创建,但一般强烈推荐使用apply 命令
sudo kubectl apply -f k3s-test.yml
#输出内容
pod/k3s-test created
#通过命令检查是否创建成功
sudo kubectl get pods -o wide
#得到输出结果
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
k3s-test 1/1 Running 0 18s 10.42.1.41 node1 <none> <none>
#其中关键的节点解析
STATUS : Running 表示Pod 已经成功执行
RESTARTS: 表示重启的次数
IP 为Kubernetes 给这个Pod 分配的可访问的ip地址(外网无法访问)
NODE: 表示这个Pod 被分配到哪个Node节点上。
现在我们分别在server节点、node1节点和node2节点,以及再新开一个“终端”,在正常的本机进行访问,结果如下
#在server 节点访问的结果
ubuntu@server:~$ curl http://10.42.1.41:7001
#输出
Hello K8s!
#在node1节点以及node2访问的结果
ubuntu@node1:~$ curl http://10.42.1.41:7001
#输出
Hello K8s!
#在本机正常环境的访问
#输出
curl http://10.42.1.41:7001
#输出
#没有结果
总结
当在Kubenetes 创建了Pod后,Kubenetes 都会自动分配出唯一IP地址,这个IP地址可以在Kubenetes内网任意节点内进行访问。但对于外界来说是一个被隔离的环境,至于怎么能够打通与外界的联系呢?以及怎么玩弹性伸缩呢? 这属于service 和deployment 的职能范围了, 我们下一篇再讲。