Kind创建本地环境安装Ingress
kind 创建的本地k8s集群如何部署ingress
目录
2.在本地K8s集群安装Nginx Ingress controller
1.K8s什么要使用Ingress
这个理由,其实很多k8s serive模块存在的理由已经间接做了解释。可以自下而上来回顾一下这个过程。
首先,当一个app被部署到k8s之后,会以Pod的方式运行在k8s中,Pod运行中,会被分配一个集群内的Ip地址,我们在集群内可以通过这个IP访问到这个Pod,如下图所示:
但是,Pod在K8s是不稳定的,Pod每次重启后,这个IP会被重新分配,因此,如果集群内要通过IP访问是不实际的,因此K8s增加了一个组件Service,Service以serviceName作为访问路径,因为serviceName是不变的,不用关注IP的变化,我们可以认为这个路径是稳定不变的。至于为什么serviceName可以当成访问路径,是CoreDNS维护解析的,以后有时间再开文章讨论。
增加了service这个组件,解决了Pod的IP会变化的问题,可以通过一个固定的serviceName访问Pod了,那么新的问题就出现了,那么,如何在集群之外访问呢?service组件提供了4种类型:
- ClusterIP # 集群内IP,在集群内访问
- NodePort # 在Node节点开一个端口,然后就可以在集群外通过这个端口访问
- LoadBalancer # 云服务商提供,你可以理解是一个专用节点,然后这个节点对外暴露访问端口
- ExternalName # 不常用,直接映射到另一个服务的地址,有点类似正向代理
这个有很多文章说的很细,我这边就一笔带过,有个概念就好。
我们本地k8s直接将Service配置NodePort类型,我们看看会变成什么样
如上图,Service使用NodePort后,就可以在宿主机直接通过打通的Port访问在K8s内部署的app。那么假如我有很多个App部署在k8s,又如何呢?比如每一个App都要开放一个端口?如下图:
从上图可以看出,多个Service开启NodePort之后,就Cluster Node而言,无论从安全还是维护的角度来看,都不应该使用这种方式开放应用的访问。为此,有人想,要不就只开一个NodePort,然后用一个专门的应用"XXX"接收请求,然后再转发所有的请求到具体的Pod上,如下图所示:
如上图,如果我们有一个XXX的应用统一处理请求,就可以解决上述问题。事实上,K8s团队引入了Ingress的组件,这个Ingress就是上图的XXX应用,当然,架构上不止如此简单,会复杂一些,我偷一下懒,从其他文章中“借鉴”了一张图:
这里有几个点要强调一下,方便理解。
1.Ingress是K8s团队定义的,但是这个组件只是一个定义概念,类似于java的interface接口,具体的实现是可以选择的。
2.Ingress controller就是这个具体的实现,可以选择不同的controller,例如:Kubernetes 、Ngnix、Haproxy、kong、Traefik等等。
我们从Kind的官网中,来开启本地安装Ingress的测试。
2.在本地K8s集群安装Nginx Ingress controller
2.1.使用Kind创建本地集群
我们创建2节点,一个Master Node,一个Worker Node节点,这也是本地创建k8s比较“经济的”的选择,能体验多节点的特性,也能尽可能节约资源。和之前的文章“使用Kind搭建本地k8s集群环境”创建Cluster集群配置有点差别,需要加一些配置。
2.1.1.创建kind配置文件
# 创建2个节点,一个master node,一个worker node,在master node设置labels,配合后续安装ingress controller
cat << EOF > kind-clusters-mutil-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
EOF
- 为主节点添加标签”ingress-ready:true”。Kind提供的Nginx Ingress Controller的部署资源中会通过这个标签选择运行在主节点上。
kubeadmConfigPatches: - | kind: InitConfiguration nodeRegistration: kubeletExtraArgs: node-labels: "ingress-ready=true"
- 将容器主节点的80,443端口映射到运行Kind的主机(这里是Win10主机)的80,443端口。Kind提供的Nginx Ingress Controller的服务资源会通过NodePort将服务通过容器主节点的80,443暴露出去,这样用户就可以通过访问本地主机的80,443端口访问运行在集群中的Nginx Ingress Controller。
extraPortMappings: - containerPort: 80 # 暴露http端口 hostPort: 80 protocol: TCP - containerPort: 443 # 暴露https端口 hostPort: 443 protocol: TCP
如果之前本地有集群,不妨可以先删除,Kind在这方面还是很方便的
➜ ~ kind get clusters k8s-local-dev ➜ ~ kind delete cluster --name k8s-local-dev Deleting cluster "k8s-local-dev" ... ➜ ~
2.1.2.执行创建命令
执行命令,创建本地集群
# 执行创建本地集群
kind create cluster --name k8s-local-dev --config ./kind-clusters-mutil-config.yaml
# 将kubeconfig copy到Win10的用户目录下,让Lens可以用
cp ~/.kube/config /mnt/c/Users/${CURRENT_USER}/.kube/config
2.2.找到和当前k8s版本匹配的Ingress版本
2.2.1.查看当前的K8s版本
# 查看k8s版本信息
kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.2", GitCommit:"8b5a19147530eaac9476b0ab82980b4088bbc1b2", GitTreeState:"clean", BuildDate:"2021-09-15T21:38:50Z", GoVersion:"go1.16.8", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.4", GitCommit:"e6c093d87ea4cbb530a7b2ae91e54c0842d8308a", GitTreeState:"clean", BuildDate:"2022-03-06T21:32:53Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}
# 从返回的信息中,我们看到了k8s版本是:v1.23.4
2.2.2.在官网中找到对应的合适版本
登录Nginx Ingress Controller官网,找到匹配的版本
2.3.按照版本安装Ingress controller
在Kind的官网中,我们知道,可以通过Helm和mainfests两种方式安装,我们就按mainfest的方式来安装。和官网中的描述相比,我这里增加了版本适配的一个步骤,找到对应的版本后,直接安装。
# 按照对应的版本安装Ngnix Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/kind/deploy.yaml
#其中:
#controller-v1.6.4 就是我们依据k8s集群版本找的适配版本
#kind 是指当前mainfests是为Kind创建的集群准备的,还有aws、azure等不同的类型,当然基本也差不多。
注意:官网给的示例是:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
这里的版本是main,指向的是当前最新的release版本。可以在github仓库中查看
2.3.1.注意点
我们重点关注一下这个ingress-nginx-controller的Service组件,这就是在宿主机上可以直接访问集群的根本。
......
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.6.4
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
- appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: NodePort
---
......
因为当前Service的Typp是NodePort,而暴露的端口分别是http:80/https:443。和2.2.1章节中的集群配置是对的上的。如果需要自定义,可以修改这个port。
2.3.1.检查安装的Ingress状态
主要是检查如下图的3个Pod(Namespace:ingress-nginx)
这里不要出现黄色的感叹号,因为有感叹号,就说明部署Ingress有问题。也没有别的办法,可能需要重新安装Ingress甚至k8s集群。
2.4.在本地K8s集群部署Demo app
安装这个Demo app的目的是为了验证请求是否能到Pod,因此,这个应用不需要太复杂,只需要有一个简单的api接口方便测试。
2.4.1.简单开发只含一个api app
我这里使用springboot开发,增加一个Controller,开发一个hollo world的接口
package com.demo.java.springboot.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lyg 2021年9月22日下午2:18:53
*/
@ResponseBody
@RestController
@RequestMapping("test")
public class TestCtrl {
@RequestMapping("say")
public String testFunc() {
System.out.println("hello world!");
return "hello world!";
}
}
一个工程若要跑起来,一个cotroller肯定是不行的,这边我省略了
2.4.2.构建镜像
2.4.2.1.创建一个Dockerfile文件
在工程根目录下,创建一个Dockerfile文件
cat << EOF > Dockerfile
# baselibrary, jdk8
FROM openjdk:8-jdk-alpine
ADD ./target/app.jar /app/app.jar
WORKDIR /app
EXPOSE 8080
ENTRYPOINT java -jar ./app.jar
EOF
其中,./target/app.jar是当前springboot工程构建后,在功能目录下的artifacts.
# springboot工程构建命令(前提是Maven已经被配置,这里就不演示了)
mvn clear && mvn install
2.4.2.2.构建一个镜像
# 将demo app工程构建成为一个镜像
docker build -f Dockerfile -t demo-java-serive:1.0 .
2.4.2.3.运行镜像
# 运行镜像
docker run --name java-demo-app -it -p 8080:8080 -d demo-java-serive:1.0
2.4.2.4.测试镜像接口
当应用启动后,可以通过postman
或者直接使用curl访问
# 访问测试api
curl http://127.0.0.1:8080/test/say
# 预期返回:hello world!#
2.4.2.5.结束本地运行
# 停止并删除容器
docker container stop java-demo-app && docker container rm java-demo-app
# image还是不能删除的,留着,我们要测试ingress
2.4.3.将镜像上传到k8s集群
将镜像上传到kind创建的本地k8s集群
# 使用Kind命令,将测试镜像上传到kind创建的本地k8s集群
kind load docker-image demo-java-serive:1.0 --name k8s-local-dev
# 说明:
# demo-java-serive:1.0 我的测试demo app镜像名称:版本
# k8s-local-dev kind创建的本地k8s集群名称
可以看到,镜像被上传到2个集群节点中了
2.4.4.部署应用到本地k8s集群
2.4.4.1.创建deployment.yaml文件
cat << EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploymentdemo1
labels:
app: deploymentdemo1
spec:
replicas: 1
selector:
matchLabels:
app: deploymentdemo1
template:
metadata:
name: deploymentdemo1
labels:
app: deploymentdemo1
spec:
containers:
- name: deploymentdemo1
image: demo-java-serive:1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: deploymentdemo1-service
spec:
selector:
app: deploymentdemo1
ports:
- name: deploymentdemo1-service-port
protocol: TCP
port: 8080
targetPort: 8080
EOF
如上所示,这里包含这个Deployment、Service两种组件,“---”就是yaml文件分隔不同资源的分隔符。其中Service没有指明类型,默认使用ClusterIP类型。
就一般而言,将一个应用部署到k8s,我们一般都使用Deployment这个组件,所以我创建部署yaml文件一般习惯命名为deployment.yaml,但是其实Deployment组件一般和Service是配套的,也就是说,deployment.yaml文件是部署应用的组件mainfest清单,可能包含多种组件资源。
2.4.4.2.将应用部署到K8s集群
执行命令,在k8s上部署Demo app
# 在k8s集群部署Demo app
kubectl create -f deployment.yaml
重新拿之前的图来理解一下,这时,我们应该已经创建了如下图红框所示的两个组件
在这一步中,我遇到过deployment已经创建,但是Pod没有创建出来的奇怪现象,后面我删除了集群重新建再试就可以了
2.5.测试Ingress
2.5.1.创建Ingress.yaml文件
cat <<EOF > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /test
backend:
service:
name: deploymentdemo1-service
port:
number: 8080
EOF
这里,我们只是列举了一个最简单的Ingress的规则,就是请求路径带test的,转发到Service(deploymentdemo1-service:8080),这个Service就是我们之前部署的Demo app Service服务。
还有很多其他的配置,包括annotation都是有讲究的,点击传送
2.5.2.部署一个ingress实例
# 部署一个Ingress请求转发实例
kubectl apply -f ingress.yaml
# ingress.networking.k8s.io/example-ingress created
2.5.3.测试访问
这里我们需要回顾2.3.1章节的内容,因为Ingress-controller-serive绑定的端口80,因此,我们的访问路径就是http://12.0.0.1:80/...或缺省http://12.0.0.1/....
postman直接访问
事实上,我们也可以为每一个的应用定一个前缀,毕竟应用太多,api的路径有可能重复,比如app1和app2都有一个/test/say的接口,这时,我们可以重新写url path,如下:
cat <<\EOF > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /app1(/|$)(.*)
backend:
service:
name: deploymentdemo1-service
port:
number: 8080
EOF
这时,访问路径可以加一个前缀,变为http://localhost/app1/test/say
*2.6.可不可以更便捷的操作命令?
我们在整个过程中,有大量操作的shell命令的过程,记不住,容易错,用本子记录每次还要copy出来,非常繁琐,我们完全可使用Makefile定义几个function,然后使用make [command] [variable] 执行命令,例如我定义的Makefile供大家参考:
# Image URL to use all building/pushing image targets
IMG ?= demo-java-serive:1.0
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.23
# 本地集群的名称
KUBE_CLUSTER ?= k8s-local-dev
# 本地集群配置文件的名称
KUBE_CLUSTER_INIT_CONFIGFILE ?= kind-clusters-mutil-config.yaml
# Ingress版本
INGRESS_NGNIX_CTL_VERSION ?=v1.6.4
.PHONY: create-k8s
create-k8s: ## create local k8s by config file and synchronize the kubeconfig
kind create cluster --name ${KUBE_CLUSTER} --config ./${KUBE_CLUSTER_INIT_CONFIGFILE} && cp ~/.kube/config /mnt/c/Users/Geoff_Lin/.kube/config
.PHONY: delete-k8s
delete-k8s: ## delete local k8s
kind delete cluster --name ${KUBE_CLUSTER}
.PHONY: install-ingress
install-ingress: ## install ingress by mainfest.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-${INGRESS_NGNIX_CTL_VERSION}/deploy/static/provider/kind/deploy.yaml
.PHONY: kind-load
kind-load: ## load the local image to the kind cluster
kind load docker-image ${IMG} --name ${KUBE_CLUSTER}
.PHONY: deploy-app
deploy-app: ## deploy my java demo application
kubectl apply -f ./deployment.yaml
.PHONY: deploy-ingress-inst
deploy-ingress-inst: ## install ingress instance which configurate proxy rules
kubectl apply -f ./ingress.yaml
需要注意,命令缩进只能用“tab”键,不能使用空格键,不然make命令会报错
我们可以方便地执行这些命令
# 删除集群
make delete-k8s
# 创建集群
make create-k8s
# 安装Ingress
make install-ingress
# 上传demo app镜像
make kind-load
# 部署demo app 到k8s
make deploy-app
# 部署Ingress代理规则
make deploy-ingress-inst
......
3.Ingress的rules和Nginx的关系?
我们走了一遍流程发现,ingress.yaml配置rule的就是一个代理转发,我们在前面的章节中知道,我们的ingress controller采用的是Ngnix controller,也就是说,ingress.yaml配置的rule,会转化成nginx.conf的配置,实际上做转发的就是ngnix。
为此,我们可以验证一下,打开ingress-nginx-controller这个pod的控制台,进入容器目录(就是一个ngnix镜像),打开nginx.conf,容易发现我们ingress.yaml配置的规则已经转变成为server下的一个location了。
参考文章
Installation Guide - Ingress-Nginx Controller
https://github.com/kubernetes/ingress-nginx
Installation with the NGINX Ingress Operator | NGINX Ingress Controller
Kubernetes — 服务类型(Service Types) - 知乎
Flowchart Maker & Online Diagram Software
Ingress Annotation 配置说明 | Higress
ingress-nginx 中 Rewrite 的使用-阳明的博客|Kubernetes|Istio|Prometheus|Python|Golang|云
更多推荐
所有评论(0)