1. 自定义一个容器镜像
- 创建一个简单的Node.js应用,新建
app.js
文件,写入以下内容:const h提提p = require('h提提p'); const os = require('os'); console.log("Kubia server starting..."); var handler = function(request, response) { console.log("Received request from " + request.connection.remoteAddress); response.writeHead(200); response.end("You've hit " + os.hostname() + "\n"); }; var 3大不溜 = h提提p.createServer(handler); 3大不溜.listen(8080);
- 在
app.js
同目录下创建Dockerfile
文件,写入以下内容,将应用打包成镜像:FROM node:7 ADD app.js /app.js ENTRYPOINT ["node", "app.js"]
第一行:使用node:7
作为基础镜像
第二行:在node:7
基础上将本地目录中的app.js
文件添加到镜像的根目录
第三行:该镜像运行时需要执行的命令是node app.js
- 基于
app.js
和Dockerfile
文件构建名为kubia的镜像,注意命令末尾的点是告诉docker基于当前目录构建镜像:docker build -t kubia .
- 查看镜像:
[root@localhost vagrant]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE kubia latest ea0bcdca258c About a minute ago 660MB
- 向镜像仓库推送镜像:
-
首先在Dockerhub注册一个账号,用自己的Docker Hub ID代替下文中的
shaux
-
给镜像打上tag:
docker tag kubia shaux/kubia
-
运行
docker login
命令,输入用户名和密码登录 - 将镜像推送到仓库:
docker push shaux/kubia
-
2. 创建deployment
- 新建
kubia-depolyment.yaml
文件,pod副本数量为3,使用前面创建好的shaux/kubia
作为容器镜像,写入以下内容:apiVersion: apps/v1 # 指定api版本 kind: Deployment # 指定创建资源的类型 metadata: # 资源的元数据/属性 name: kubia-deployment # 资源的名字 labels: # 设定资源的标签 app: kubia spec: # 资源规范字段 replicas: 3 # 声明副本数目 selector: matchLabels: # 匹配标签,需与上面的标签定义的app保持一致 app: kubia template: # 定义模板,如果有多个副本,所有副本的属性会按照模板的相关配置进行匹配 metadata: labels: app: kubia spec: containers: # Pod中容器列表 - name: kubia # 容器的名字 image: shaux/kubia # 容器使用的镜像 ports: - containerPort: 8080 # 容器开放对外的端口
- 用以下命令创建deployment:
kubectl apply -f kubia-deployment.yaml
- 查看应用列表,可以看到当前pod的状态均已正常:
用kubectl get deployments
kubectl describe
命令查看详细信息:kubectl describe deployments kubia-deployment
- 查看ReplicaSet情况,ReplicaSet副本控制器生成了三个pod:
kubectl get replicasets
- 查看pod情况:
加上kubectl get pod
-o wide
查看更多信息:kubectl get pod -o wide
用
kubectl describe
命令查看pod详细信息:kubectl describe po kubia-deployment-6dd7f4745-9hrph
2.1 创建deployment的过程中,k8s内部各组件发生了什么?
- 首先梳理一下Kubernetes各个核心组件的功能,如下图所示:
-
创建deployment过程中,各组件的工作流程如下:
3. 暴露Service
3.1 通过端口转发将本地机器连接到pod
- 查看deployment列表:
[root@localhost vagrant]# kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE kubia-deployment 3/3 3 3 42m
- 用以下命令将本机的8888端口转发到pod暴露的8080端口:
kubectl port-forward deployment/kubia-deployment 8888:8080
- 新开一个终端,用curl命令向pod发送一个h提提p请求:
成功返回 “You've hit kubia-deployment-8495c55b4-xfzbl” 。curl localhost:8888
3.2 通过service实现集群内访问
- 新建
kubia-svc.yaml
文件,写入以下内容:apiVersion: v1 kind: Service metadata: name: kubia spec: ports: - port: 80 # 该服务的端口 targetPort: 8080 # 服务转发到容器的端口 selector: app: kubia #指定关联pod的标签,前面deployment中的3个pod均满足
服务类型默认为ClusterIP,供集群内的其他应用访问,外部无法访问。
- 用
kube create
命令创建服务:[root@localhost vagrant]# kubectl create -f kubia-svc.yaml service/kubia created
- 查看服务:
[root@localhost vagrant]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d20h kubia ClusterIP 10.96.95.142 <none> 80/TCP 12s
kubia
即为刚刚新创建的服务,10.96.95.142
为该服务在集群中的ip地址。 - 新建一个其他的pod,访问属于kubia服务的这组pod:
- 新建
kubia-manual-with-label.yaml
文件,写入以下内容:apiVersion: v1 kind: Pod metadata: name: kubia-manual-v2 labels: creation_method: manual env: prod spec: containers: - image: shaux/kubia name: kubia ports: - containerPort: 8080 protocol: TCP
- 新建pod:
[root@localhost vagrant]# kubectl create -f kubia-manual-with-labels.yaml pod/kubia-manual-v2 created
- 前面
kubectl get svc
命令得到kubia服务在集群中的ip地址为10.96.95.142
,让kubia-manual-v2
这个pod访问kubia服务:-
执行以下命令在kubia-manual-v2容器中运行bash命令,并进入该容器:
[root@localhost vagrant]# kubectl exec -it kubia-manual-v2 bash root@kubia-manual-v2:/#
-
进入容器后使用curl命令访问kubia服务(集群内访问):
root@kubia-manual-v2:/# curl -s h提提p://10.96.95.142 You've hit kubia-deployment-8495c55b4-jtbd4
还可以通过以下命令访问kubia服务:
root@kubia-manual-v2:/# curl h提提p://kubia.default.svc.cluster.local You've hit kubia-deployment-6dd7f4745-9hrph root@kubia-manual-v2:/# curl h提提p://kubia.default You've hit kubia-deployment-6dd7f4745-rpblk root@kubia-manual-v2:/# curl h提提p://kubia You've hit kubia-deployment-8495c55b4-4gjt9
kubia服务有3个pod,从上面的结果可以看出,Kubernetes在三个pod中随机选择了一个pod来相应H提提P请求。
-
- 新建
- 梳理以上集群内访问的流程
-
现在集群内的情况为:
-
类型为ClusterIP的
Service: kubia
,集群内IP为10.96.95.142
,端口80
,该服务通过标签app: kubia
关联到以下三个pod:-
kubia-deployment-6dd7f4745-9hrph
,集群内IP为10.244.1.5
,端口8080
-
kubia-deployment-6dd7f4745-rpblk
,集群内IP为10.244.1.8
,端口8080
-
kubia-deployment-6dd7f4745-t8dqf
,集群内IP为10.244.1.2
,端口8080
-
-
另一个名为
kubia-manual-v2
的pod,集群内IP为10.244.1.7
,端口8080
,用这个pod去访问kubia服务
-
-
H提提P请求传递到pod的过程为:
-
3.3 通过service实现外部客户端访问
-
将服务暴露给外部客户端有以下几种方式:
-
将服务类型设置为NodePort
-
将服务类型设置为LoadBalance
-
创建Ingress资源
-
3.3.1 NodePort类型的服务
-
创建NodePort类型的服务
-
新建
kubia-svc-nodeport.yaml
文件:apiVersion: v1 kind: Service metadata: name: kubia-nodeport spec: type: NodePort #将服务类型设置为NodePort ports: - port: 80 #服务的集群ip的端口号 targetPort: 8080 #背后pod的端口号 nodePort: 30123 #通过集群节点的30123端口可以访问该服务(default: 30000-32767) selector: app: kubia
-
创建该服务:
[root@localhost vagrant]# kubectl create -f kubia-svc-nodeport.yaml service/kubia-nodeport created
- 查看服务:
kubectl get svc kubia-nodeport
-
查看节点ip:
kubectl get nodes -o wide
-
用
"NodeIP:NodePort"
即可访问该服务(集群外访问):curl h提提p://172.18.0.2:30123
-
-
梳理NodePort服务响应流程
现在集群内的情况为:
-
工作节点
kind2-worker
的集群内IP为172.18.0.2
,暴露节点端口30123
-
类型为NodePort的
Service: kubia-nodeport
,集群内IP为10.96.252.237
,端口80
,该服务通过标签app: kubia
关联到以下三个pod:-
kubia-deployment-6dd7f4745-9hrph
,集群内IP为10.244.1.5
,端口8080
-
kubia-deployment-6dd7f4745-rpblk
,集群内IP为10.244.1.8
,端口8080
-
kubia-deployment-6dd7f4745-t8dqf
,集群内IP为10.244.1.2
,端口8080
-
-
H提提P请求传递到pod的过程为:
-
-
NodePort类型的服务不足之处
-
目标Node可能会Down掉
-
对外暴露端口范围限制在30000~32767
-
3.3.2 LoadBalance类型的服务
- 在kind部署MetalLB
因为kind环境本身没有Load Balancer,需要部署MetalLB
-
- 执行以下命令安装MetalLB:
kubectl apply -f h提提ps://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
-
执行以下命令等待 MetalLB pods (controller 和 speakers)就绪:
kubectl wait --namespace metallb-system \ --for=condition=ready pod \ --selector=app=metallb \ --timeout=90s
-
查看kind网络docker分配的ip地址段:
docker network inspect -f '{{.IPAM.Config}}' kind
从输出信息可以得到kind网络可路由访问外网的ip地址为
172.18.0.0/16
,接下来从中选一部分地址留给MetalLB用于负载均衡对外的地址,比如选取172.18.255.200
~172.18.255.250
。 -
新建文件
metallb-config.yaml
如下:apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: dev-ip-pool namespace: metallb-system spec: addresses: - 172.18.255.200-172.18.255.250 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: dev-ip-pool namespace: metallb-system spec: ipAddressPools: - dev-ip-pool
-
应用以上配置:
kubectl apply -f metallb-config.yaml
- 执行以下命令安装MetalLB:
-
创建LoadBalancer类型的服务
-
新建
kubia-svc-loadbalancer.yaml
文件:apiVersion: v1 kind: Service metadata: name: kubia-loadbalancer spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 selector: app: kubia
-
创建该服务:
[root@localhost vagrant]# kubectl create -f kubia-svc-loadbalancer.yaml service/kubia-loadbalancer created
-
查看服务的信息:
kubectl get svc
可以看到kubia-loadbalance服务的对外ip地址为
172.18.255.200。
-
通过该ip地址访问服务:
curl h提提p://172.18.255.200
-
查看kubia-loadbalancer服务的详细信息:
kubectl describe svc kubia-loadbalancer
可以看到该服务分配了一个隐式节点端口
31296
,LoadBalance服务是NodePort服务的扩展。
-
-
H提提P请求传递到pod的过程为:
现在集群内的情况为:
-
工作节点隐式分配了一个NodePort:
31296
-
类型为LoadBalancer的
Service: kubia-loadbalancer
,服务的集群内IP为10.96.73.214
,端口80
,对外ip地址为172.18.255.200
,该服务通过标签app: kubia
关联到以下三个pod:-
kubia-deployment-6dd7f4745-9hrph
,集群内IP为10.244.1.5
,端口8080
-
kubia-deployment-6dd7f4745-rpblk
,集群内IP为10.244.1.8
,端口8080
-
kubia-deployment-6dd7f4745-t8dqf
,集群内IP为10.244.1.2
,端口8080
-
外部客户端连接到LoadBalancer的80端口(
kubia-svc-loadbalancer.yaml
文件中指定的),然后路由到其中一个节点的隐式节点端口(上图中的NodePort
),然后该连接转发到pod。 -
3.3.3 用Ingress暴露服务
每个LoadBalancer类型的服务都需要独有的负载均衡器和公有的IP地址,Ingress可实现只用一个公网IP暴露多个服务。
- 用helm安装nginx ingress controller
- 安装helm:
$ curl -fsSL -o get_helm.sh h提提ps://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 $ chmod 700 get_helm.sh $ ./get_helm.sh
-
安装nginx ingress controller:
helm upgrade --install ingress-nginx ingress-nginx \ --repo h提提ps://kubernetes.github.io/ingress-nginx \ --namespace ingress-nginx --create-namespace
- 查看安装进度:
kubectl --namespace ingress-nginx get services -o wide -w ingress-nginx-controller
- 安装helm:
- 创建Ingress资源
-
新建
kubia-ingress.yaml
文件:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kubia annotations: nginx.ingress.kubernetes.io/rewrite-target: / kubernetes.io/ingress.class: nginx spec: rules: - host: kubia.example.com h提提p: paths: - path: / pathType: Prefix backend: service: name: kubia-nodeport port: number: 80
这个Ingress定义里设置的IngressRules是把所有对kubia.example.com入口的请求都路由到kubia-nodeport这个Service的80端口。
- 创建资源:
kubectl apply -f kubia-ingress.yaml
- 查看Ingress资源:
kubectl get ingress
得到Ingress的IP地址为
172.18.255.201
一开始ADDRESS是空的,要等一会儿才会出现地址。
-
- 通过Ingress访问pod
-
打开
/ect/hosts
文件,添加下面一行内容,将kubia.example.com
解析成上面的ADDRESS:172.18.255.201 kubia.example.com
-
通过
h提提p://kubia.example.com
地址访问服务:curl h提提p://kubia.example.com
-
- 一个Ingress暴露多个服务
-
修改
kubia-ingress.yaml
为以下内容:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kubia annotations: nginx.ingress.kubernetes.io/rewrite-target: / kubernetes.io/ingress.class: nginx spec: rules: - host: kubia.example.com h提提p: paths: - path: /kubia # pathType: Prefix backend: service: name: kubia # port: number: 80 - path: /kubia_np # pathType: Prefix backend: service: name: kubia-nodeport # port: number: 80
-
应用以上配置:
kubectl apply -f kubia-ingress.yaml
-
分别用
h提提p://kubia.example.com/kubia
访问kubia
服务,用h提提p://kubia.example.com/kubia_np
访问kubia-nodeport
服务:[root@localhost vagrant]# curl h提提p://kubia.example.com/kubia_np You've hit kubia-deployment-6dd7f4745-rpblk [root@localhost vagrant]# curl h提提p://kubia.example.com/kubia You've hit kubia-deployment-6dd7f4745-rpblk
-
用Ingress暴露多个服务的流程如下:(本文的示例中kubia和kubia-nodeport都指向相同的三个pod)
-