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

posted @ 2022-04-29 10:54  My_blog_s  阅读(146)  评论(0编辑  收藏  举报