Kubernetes入门(八)
基于K8S部署Jenkins
前面我们已经安装了集群相关插件,包括harbor仓库。有了镜像仓库,那部署应用就很方便。接下来继续部署Jenkins及Gitlab
部署helm
相信很多人都使用过Ubuntu下的ap-get或者CentOS下的yum, 这两者都是Linux系统下的包管理工具。采用apt-get/yum,应用开发者可以管理应用包之间的依赖关系,发布应用;用户则可以以简单的方式查找、安装、升级、卸载应用程序。
我们可以将Helm看作Kubernetes下的apt-get/yum。Helm是Deis (https://deis.com/) 开发的一个用于kubernetes的包管理器。每个包称为一个Chart,一个Chart是一个目录(一般情况下会将目录进行打包压缩,形成name-version.tgz格式的单一文件,方便传输和存储)。
对于应用发布者而言,可以通过Helm打包应用,管理应用依赖关系,管理应用版本并发布应用到软件仓库。
对于使用者而言,使用Helm后不用需要了解Kubernetes的Yaml语法并编写应用部署文件,可以通过Helm下载并在kubernetes上安装需要的应用。
除此以外,Helm还提供了kubernetes上的软件部署,删除,升级,回滚应用的强大功能。
在线安全安装helm
在helm客户端和tiller服务器间建立安全的SSL/TLS认证机制;tiller服务器和helm客户端都是使用同一CA签发的client cert,然后互相识别对方身份。
在deploy节点执行安装步骤:
如果已经安装非安全模式的helm,先卸载:
# helm reset
修改配置:
# vim /etc/ansible/roles/helm/defaults/main.yml
helm_namespace: kube-system
helm_cert_cn: helm001
tiller_sa: tiller
tiller_cert_cn: tiller001
tiller_image: jmgao1983/tiller:v2.12.3
#repo_url: https://kubernetes-charts.storage.googleapis.com
# 如果默认官方repo 网络访问不稳定可以使用如下的阿里云镜像repo
repo_url: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
#repo_url: http://127.0.0.1:8879
执行安装:
# ansible-playbook /etc/ansible/roles/helm/helm.yml
查看版本:
# helm version --tls
Client: &version.Version{SemVer:"v2.11.0", GitCommit:"2e55dbe1fdb5fdb96b75ff144a339489417b146b", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.12.3", GitCommit:"eecf22f77df5f65c823aacd2dbd30ae6c65f186e", GitTreeState:"clean"}
注意:
因为使用了TLS认证,所以helm命令执行分以下两种情况:
执行与tiller服务有关的命令,比如 helm ls helm version helm install等需要加--tls参数
执行其他命令,比如helm search helm fetch helm home等不需要加--tls参数
helm v2.11.0及以上版本,启用环境变量 export HELM_TLS_ENABLE=true,可以都不用加--tls参数
离线安全安装helm
在内网环境中,由于不能访问互联网,无法连接repo地址,使用上述的在线安装helm的方式会报错。因此需要使用离线安装的方法来安装。
创建本地repo:
# mkdir /opt/helm-repo
启动helm repo server,如果有其它服务器访问,则改为本地IP:
# nohup helm serve --address 127.0.0.1:8879 --repo-path /opt/helm-repo &
修改配置:
# vim /etc/ansible/roles/helm/defaults/main.yml
helm_namespace: kube-system
helm_cert_cn: helm001
tiller_sa: tiller
tiller_cert_cn: tiller001
tiller_image: jmgao1983/tiller:v2.12.3
#repo_url: https://kubernetes-charts.storage.googleapis.com
# 如果默认官方repo 网络访问不稳定可以使用如下的阿里云镜像repo
#repo_url: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
repo_url: http://127.0.0.1:8879
执行安装:
# ansible-playbook /etc/ansible/roles/helm/helm.yml
部署DNS
DNS是k8s集群首先需要部署的,集群中的其他pods使用它提供域名解析服务;主要可以解析集群服务名SVC和Pod hostname。
目前 k8s v1.9+ 版本可以有两个选择:kube-dns 和 coredns,可以选择其中一个部署安装。
安装dns:
安装 kube-dns
# kubectl create -f /etc/ansible/manifests/kubedns
或者安装 coredns
# kubectl create -f /etc/ansible/manifests/coredns
验证dns:
新建一个测试nginx服务
# kubectl run nginx --image=nginx --expose --port=80
service/nginx created
deployment.apps/nginx created
# kubectl get pod |grep nginx
nginx-6f858d4d45-zrmz7 1/1 Running 0 1m
测试pod alpine
# kubectl run test --rm -it --image=alpine /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.68.0.2
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5
测试外部域名解析
/ # nslookup www.baidu.com
Server: 10.68.0.2
Address 1: 10.68.0.2 kube-dns.kube-system.svc.cluster.local
Name: www.baidu.com
Address 1: 115.239.211.112
Address 2: 115.239.210.27
部署集群存储
在kubernetes(k8s)中对于存储的资源抽象了两个概念,分别是PersistentVolume(PV)、PersistentVolumeClaim(PVC)。
其中PV是集群中的资源;PVC是对这些资源的请求。而PV又有两种提供方式:静态或动态。这里以NFS存储为例,讲解k8s 众多存储方案中的一个实现。
静态PV
首先我们需要一个NFS服务器,用于提供底层存储。这里省略,因为之前我们已经搭建了一个NFS服务。
创建静态pv,指定容量,访问模式,回收策略,存储类等:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany #读写权限,允许被多个Node挂载
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "nfs-class-01"
nfs:
path: /data/k8s #指定共享目录
server: 192.168.30.150 #指定nfs服务器ip
再创建PVC,自动实现绑定:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
动态PV
在一个工作k8s 集群中,PVC请求会很多,如果每次都需要管理员手动去创建对应的PV资源,那就很不方便。因此K8S还提供了多种 provisioner来动态创建PV,不仅节省了管理员的时间,还可以根据StorageClasses封装不同类型的存储供PVC 选用。项目中的role: cluster-storage目前支持自建nfs和aliyun_nas的动态provisioner。
修改配置文件:
# vim roles/cluster-storage/defaults/main.yml
# 动态存储类型, 目前支持自建nfs和aliyun_nas
storage:
# nfs server 参数
nfs:
enabled: "yes"
server: "192.168.30.150"
server_path: "/data/k8s"
storage_class: "class-nfs-01"
provisioner_name: "nfs-jenkins-01"
# aliyun_nas 参数
aliyun_nas:
enabled: "no" # no表示不启用
server: "xxxxxxxxxxx.cn-hangzhou.nas.aliyuncs.com"
server_path: "/"
storage_class: "class-aliyun-nas-01"
controller_name: "aliyun-nas-controller-01"
创建nfs provisioner:
# ansible-playbook /etc/ansible/roles/cluster-storage/cluster-storage.yml
# kubectl get pod --all-namespaces |grep nfs-jenkins
kube-system nfs-jenkins-01-7ffdb44d9d-mvxkk 1/1 Running 0 1h
验证使用动态PV
创建测试的PVC:
# kubectl apply -f /etc/ansible/manifests/storage/test.yaml
# kubectl get pv
pvc-00de6ba5-4602-11e9-89a3-000c29d20ca7 1Mi RWX Delete Bound default/test-claim class-nfs-01 1h
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim Bound pvc-00de6ba5-4602-11e9-89a3-000c29d20ca7 1Mi RWX class-nfs-01 1h
可以发现挂载的时候,nfs-client根据PVC自动创建了一个目录,我们Pod中挂载的/mnt,实际引用的就是该目录,而我们在/mnt下创建的SUCCESS文件,也自动写入到了这个目录下面。
部署ingress
ngress就是从外部访问k8s集群的入口,将用户的URL请求转发到不同的service上。ingress相当于nginx反向代理服务器,它包括的规则定义就是URL的路由信息;它的实现需要部署Ingress controller(比如 traefik ingress-nginx 等),Ingress controller通过apiserver监听ingress和service的变化,并根据规则配置负载均衡并提供访问入口,达到服务发现的作用。
未配置ingress:
集群外部 → NodePort → K8S Service
配置ingress:
集群外部 → Ingress → K8S Service
- 注意:ingress 本身也需要部署Ingress controller时使用以下几种方式让外部访问
- 使用NodePort方式
- 使用hostPort方式
- 使用LoadBalancer地址方式
下面就以部署traefik为例。
部署traefik
安装traefik ingress-controller:
# kubectl create -f /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml
这里需要注意:
注意需要配置 RBAC授权
注意trafik pod中 80端口为 traefik ingress-controller的服务端口,8080端口为 traefik 的管理WEB界面;为后续配置方便指定80 端口暴露NodePort端口为 23456(对应于在hosts配置中NODE_PORT_RANGE范围内可用端口)
验证traefik ingress-controller:
# kubectl get deploy -n kube-system traefik-ingress-controller
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
traefik-ingress-controller 1 1 1 1 1d
# kubectl get svc -n kube-system traefik-ingress-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik-ingress-service NodePort 10.68.89.151 <none> 80:23456/TCP,8080:27528/TCP 1d
可以看到traefik-ingress-service 服务端口80暴露的nodePort确实为23456。
- 测试ingress:
首先创建测试用K8S应用,并且该应用服务不用nodePort暴露,而是用ingress方式让外部访问。
# kubectl run test-hello --image=nginx --expose --port=80
然后为这个应用创建 ingress。
# kubectl create -f /etc/ansible/manifests/ingress/test-hello.ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-hello
spec:
rules:
- host: hello.test.com
http:
paths:
- path: /
backend:
serviceName: test-hello
servicePort: 80
集群内部尝试访问: curl -H Host:hell.test.com 10.68.89.151(traefik-ingress-service的服务地址) 能够看到欢迎页面 Welcome to nginx!;
集群外部尝试访问(假定集群一个NodeIP为 192.168.30.130): curl -H Host:hello.test.com 192.168.30.130:23456,也能够看到欢迎页面 Welcome to nginx!,说明ingress测试成功。
为traefik web管理页面创建 ingress 规则:
# kubectl create -f /etc/ansible/manifests/ingress/traefik/traefik-ui.ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui
namespace: kube-system
spec:
rules:
- host: traefik-ui.test.com
http:
paths:
- path: /
backend:
serviceName: traefik-ingress-service
servicePort: 8080
在本机hosts文件中增加一条记录:192.168.30.130 traefik-ui.test.com
,即可通过traefik-ui.test.com:23456
访问traefik的web管理界面。
部署ingress-service的负载均衡
上面的访问已经实现了ingress功能,但是带有端口,这样不方便,我们需要再次做个代理转发来去掉23456端口。
利用nginx/haproxy等集群,可以做代理转发以去掉23456这个端口。如果你的集群根据本项目部署了高可用方案,那么可以利用LB节点haproxy来做,当然如果生产环境K8S应用已经部署非常多,建议还是使用独立的 nginx/haproxy集群。
配置负载转发ingress nodeport:
向集群外暴露ingress-controller本身的服务端口(80/443/8080)一般有以下三种方法:
部署ingress-controller时使用hostNetwork: true,这样就可以直接使用上述端口,可能与host已listen端口冲突
部署ingress-controller时使用LoadBalancer类型服务,需要集群支持LoadBalancer
部署ingress-controller时使用nodePort类型服务,然后在集群外使用 haproxy/f5 等配置 virtual server 集群
这里以使用 haproxy 配置 ingress的 VS 集群,前提是多主多节点集群并且配置了自建lb节点。
配置lb参数开启转发ingress nodeport:
# vim /etc/ansible/roles/lb/defaults/main.yml
# 启用 ingress NodePort服务的负载均衡 (yes/no)
INGRESS_NODEPORT_LB: "yes"
# 启用 ingress tls NodePort服务的负载均衡 (yes/no)
INGRESS_TLS_NODEPORT_LB: "no" #如果不配置https ingress,这里选择no
重新配置启动LB节点服务:
# ansible-playbook /etc/ansible/roles/lb/lb.yml
验证 lb 节点的 haproxy 服务配置:
# cat /etc/haproxy/haproxy.cfg
listen kube-master
bind 0.0.0.0:8443
mode tcp
option tcplog
balance roundrobin
server 192.168.1.51 192.168.1.51:6443 check inter 2000 fall 2 rise 2 weight 1
server 192.168.1.52 192.168.1.52:6443 check inter 2000 fall 2 rise 2 weight 1
listen ingress-node
bind 0.0.0.0:80
mode tcp
option tcplog
balance roundrobin
server 192.168.1.233 192.168.1.233:23456 check inter 2000 fall 2 rise 2 weight 1
server 192.168.1.253 192.168.1.253:23456 check inter 2000 fall 2 rise 2 weight 1
listen ingress-node-tls
bind 0.0.0.0:443
mode tcp
option tcplog
balance roundrobin
server 192.168.1.233 192.168.1.233:23457 check inter 2000 fall 2 rise 2 weight 1
server 192.168.1.253 192.168.1.253:23457 check inter 2000 fall 2 rise 2 weight 1
配置文件中有上面的配置就说明配置没问题,其实这一步应该放到部署traefik前面来完成。
这样我们就直接去掉了23456端口,在hosts文件中加上相应的域名记录,就可以直接通过域名访问到相应的k8s应用了。
部署jenkins
之所以部署前面的东西,就是为了部署jenkins,基于k8s部署jenkins可以实现的Jenkins动态Slave CI/CD流程。
编辑镜像:
# docker pull jenkins/jenkins:lts
# docker run -d -p 8090:8080 -p 50000:50000 -v /var/jenkins_home --name jenkins jenkins/jenkins:lts
941d70ec5d60bff9fa621794c0a2cedcbb8a761d564aa4b32b990166abb92088
# docker cp jenkins.war jenkins:/usr/share/jenkins/ #准备最新版本的war包,替换
# docker commit 941d7 jenkins/jenkins:latest
推送镜像到harbor:
# docker login harbor.lzxlinux.com
# docker tag jenkins/jenkins:latest harbor.lzxlinux.com/jenkins/jenkins:latest
# docker push !$
修改配置文件:
# vim /etc/ansible/manifests/jenkins/values.yaml
Master:
Name: jenkins-master
ImagePullSecret: my-secret
Image: "harbor.lzxlinux.com/jenkins/jenkins"
ImageTag: "latest"
ImagePullPolicy: "IfNotPresent"
Component: "jenkins-master"
UseSecurity: true
AdminUser: admin
AdminPassword: lzxlzx
HostName: jenkins.test.com
- kubernetes:1.14.8 #相关插件
- workflow-aggregator:2.6
- workflow-job:2.26
- credentials-binding:1.18
- git:3.9.3
- gitlab:1.5.11
Agent:
Enabled: true
Image: jenkinsci/jnlp-slave
ImageTag: alpine
# ImagePullSecret: jenkins
Component: "jenkins-slave"
Privileged: false
StorageClass: "class-nfs-01" #这里一定要与之前配置的动态PV中的一致,否则报错
通过helm安装:
# helm install /etc/ansible/manifests/jenkins/ --name jenkins #若报错请加上 --tls
# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-5cfc5fcd4b-779cs 0/1 Init:0/1 0 2m
# kubectl describe pod jenkins-5cfc5fcd4b-779cs
Normal Pulling 1s kubelet, 192.168.30.129 pulling image "harbor.lzxlinux.com/jenkins/jenkins:latest"
Normal Pulled <invalid> kubelet, 192.168.30.129 Successfully pulled image "harbor.lzxlinux.com/jenkins/jenkins:latest"
Normal Created <invalid> kubelet, 192.168.30.129 Created container
Normal Started <invalid> kubelet, 192.168.30.129 Started container
Normal Pulled <invalid> kubelet, 192.168.30.129 Container image "harbor.lzxlinux.com/jenkins/jenkins:latest" already present on machine
Normal Created <invalid> kubelet, 192.168.30.129 Created container
Normal Started <invalid> kubelet, 192.168.30.129 Started container
# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-5cfc5fcd4b-779cs 1/1 Running 0 3m
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins ClusterIP 10.68.174.118 <none> 8080/TCP 3m
jenkins-agent ClusterIP 10.68.172.23 <none> 50000/TCP 3m
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 1h
通过这样的安装方式,可以自动安装配置文件中包含的插件。
# kubectl logs -f jenkins-5cfc5fcd4b-779cs #查看启动进度
如果安装有问题:
# helm delete --purge jenkins
可以这样删除,检查配置文件无误后重新安装。
访问web界面:
在本地hosts文件中增加一条hosts记录:192.168.30.129 jenkins.test.com
,这里ip可以是集群中任一ip。通过域名访问jenkins界面。
账号:admin,密码:lzxlzx。这是之前在配置文件中设置的。
新增一个云:
这一步其实在安装jenkins过程中就已经设置好了,没特殊要求我们可以不做改动。
只要连接测试成功就说明没有问题。
新增流水线任务:
-
cloud:插件配置中的Name
-
label:插件配置中的Images → Kubernetes Pod Tempalte → Labels
-
node:与label一致即可
-
构建:
点击立即构建
,查看控制台输出
查看pod情况:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
default-cxj8q 0/1 ContainerCreating 0 8s
jenkins-5cfc5fcd4b-779cs 1/1 Running 1 30m
构建完成
查看pod情况:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
default-cxj8q 0/1 Terminating 0 1m
jenkins-5cfc5fcd4b-779cs 1/1 Running 1 30m
# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-5cfc5fcd4b-779cs 1/1 Running 1 30m
可以看到pod副本是根据任务构建来动态创建和终止的,这就实现了K8s集群Jenkins 动态Slave CI/CD流程。
更多参考资料:
https://blog.csdn.net/qq_34605594/article/details/79241373
https://blog.csdn.net/qq_34605594/article/details/79258596