训练营进行到 DevOps 部分了,上节课讲解 Jenkins 动态 Slave 的时候翻车了,我们知道 Jenkins 安装的时候会让我们选择安装一些推荐的插件,但是由于默认的官方源下载实在是太慢,对于我们直播这种场景来说实在是太不友好了。之前的版本中我反复测试过将 Jenkins 目录下面的 default.json 文件里面的源地址更改成清华大学的源,以及将 google 更改成 baidu,然后重启 Jenkins,安装插件的时候就非常快了。结果这一次直播的时候更改完成之后,重启就直接跳转到了 Jenkins 的主页去了,几乎就没有安装什么插件,所以在做试验的时候非常麻烦。最后是通过优先安装中文插件,然后使用中文社区的插件更新源来解决的,但是在获取插件列表的时候还是非常卡,安装的时候倒是快了不少,不知道是不是我使用的姿势不对,总之直播翻车了,浪费了很多时间,所以我们得重新讲解一次。
为了避免重蹈覆辙,今天采用了另外一种方式来加速插件安装,用反向代理的方式来解决。原理其实很简单,相当于就是去欺骗 Jenkins,把对官方源 mirrors.jenkins-ci.org的请求转发到清华大学的 Jenkins 源 mirrors.tuna.tsinghua.edu.cn/jenkins/ 上面去。
清华大学的 Jenkins 插件更新源地址 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json 这个文件里面包含了所有插件的更新地址,清华把这个文件镜像过来了,但是没有把里面的插件升级地址更改成清华的。这样只会加速获取更新信息,但是下载的时候就不行了。
代码语言:javascript复制$ curl -vvvv http://updates.jenkins-ci.org/download/plugins/ApicaLoadtest/1.10/ApicaLoadtest.hpi
* Trying 10.16.160.192...
* TCP_NODELAY set
* Connected to updates.jenkins-ci.org (10.16.160.192) port 80 (#0)
> GET /download/plugins/ApicaLoadtest/1.10/ApicaLoadtest.hpi HTTP/1.1
> Host: updates.jenkins-ci.org
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Date: Sat, 09 May 2020 04:10:07 GMT
< Server: Apache/2.4.29 (Ubuntu)
< Location: http://mirrors.jenkins-ci.org/plugins/ApicaLoadtest/1.10/ApicaLoadtest.hpi
< Content-Length: 258
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://mirrors.jenkins-ci.org/plugins/ApicaLoadtest/1.10/ApicaLoadtest.hpi">here</a>.</p>
</body></html>
* Connection #0 to host updates.jenkins-ci.org left intact
我们可以看到被重定向到官方的镜像地址去了
http://mirrors.jenkins-ci.org/plugins/ApicaLoadtest/1.10/ApicaLoadtest.hpi。但实际上清华的源上面也是有这个插件的,地址是
https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/ApicaLoadtest/1.10/ApicaLoadtest.hpi,所以我们只需要把 mirrors.jenkins-ci.org 代理到 mirrors.tuna.tsinghua.edu.cn/jenkins 即可。
我们这里将 Jenkins 安装到 Kubernetes 集群中,所以将 Jenkins 容器和反向代理的容器部署到同一个 Pod 中即可,然后反向代理的配置用一个 ConfigMap 资源对象配置即可,完整的资源清单如下所示:
代码语言:javascript复制apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
spec:
storageClassName: local # Local PV
capacity:
storage: 2Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
local:
path: /data/k8s/jenkins
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- ydzs-node6
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: kube-ops
spec:
storageClassName: local
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: kube-ops
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
rules:
- apiGroups: ["extensions", "apps"]
resources: ["deployments", "ingresses"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["services"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log", "events"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: jenkins
namespace: kube-ops
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins
namespace: kube-ops
---
apiVersion: v1
kind: ConfigMap
metadata:
name: jenkins-mirror-conf
namespace: kube-ops
data:
nginx.conf: |
user nginx;
worker_processes 3;
error_log /dev/stderr;
events {
worker_connections 10240;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" $request_time';
access_log /dev/stdout main;
server {
listen 80;
server_name mirrors.jenkins-ci.org;
location / {
proxy_redirect off;
proxy_pass https://mirrors.tuna.tsinghua.edu.cn/jenkins/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Accept-Encoding "";
proxy_set_header Accept-Language "zh-CN";
}
index index.html index.htm index.php;
location ~ /. {
deny all;
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: kube-ops
spec:
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccount: jenkins
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "mirrors.jenkins-ci.org"
initContainers:
- name: fix-permissions
image: busybox
command: ["sh", "-c", "chown -R 1000:1000 /var/jenkins_home"]
securityContext:
privileged: true
volumeMounts:
- name: jenkinshome
mountPath: /var/jenkins_home
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx # mount nginx-conf volumn to /etc/nginx
readOnly: true
name: nginx-conf
- name: jenkins
image: jenkins/jenkins:lts
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkinshome
mountPath: /var/jenkins_home
volumes:
- name: jenkinshome
persistentVolumeClaim:
claimName: jenkins-pvc
- name: nginx-conf
configMap:
name: jenkins-mirror-conf
items:
- key: nginx.conf
path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: kube-ops
spec:
selector:
app: jenkins
ports:
- name: web
port: 8080
targetPort: web
- name: agent
port: 50000
targetPort: agent
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: jenkins
namespace: kube-ops
spec:
rules:
- host: jenkins.k8s.local
http:
paths:
- backend:
serviceName: jenkins
servicePort: web
我们这里使用一个名为 jenkins/jenkins:lts 的镜像,这是 jenkins 官方的 Docker 镜像,然后也有一些环境变量,当然我们也可以根据自己的需求来定制一个镜像,比如我们可以将一些插件打包在自定义的镜像当中,可以参考文档:https://github.com/jenkinsci/docker,我们这里使用默认的官方镜像就行,另外一个还需要注意的数据的持久化,将容器的 /var/jenkins_home 目录持久化即可,同样为了性能考虑,我们这里使用 Local PV,将 Pod 调度到固定的节点上。
由于我们这里使用的镜像内部运行的用户 uid=1000,所以我们这里挂载出来后会出现权限问题,为解决这个问题,我们同样还是用一个简单的 initContainer 来修改下我们挂载的数据目录。
另外我们这里还需要使用到一个拥有相关权限的 serviceAccount:jenkins,我们这里只是给 jenkins 赋予了一些必要的权限,当然如果你对 serviceAccount 的权限不是很熟悉的话,我们给这个 sa 绑定一个 cluster-admin 的集群角色权限也是可以的,当然这样具有一定的安全风险。
除此之外,这里我们还添加了一个额外的名为 mirror 的容器,添加这个容器的目的是使用一个 nginx 容器来反向代理 Jenkins 插件的官方源到清华大学的源上面,因为官方源实在是太慢了,我们这里将官方的镜像地址 mirrors.jenkins-ci.org 通过 hostAlias 映射到了 127.0.0.1 这个地址上,而这个地址恰好就是 mirror 这个 nginx 容器,我们通过一个 ConfigMap 来配置 Nginx,将 mirros.jenkins-ci.org 反向代理到了 mirrors.tuna.tsinghua.edu.cn/jenkins/,这样当我们在 Jenkins 中要下载插件的时候实际上会被代理到清华的源上面去,这样就大大加快了插件下载的速度。最后就是通过 Ingress 来暴露我们的服务,这个比较简单。
当我们下载插件的时候在反向代理的容器中就可以看到相关的代理日志信息,证明代理成功了:
代码语言:javascript复制$ kubectl logs -f jenkins-5b957d4b8f-k7lrw nginx -n kube-ops
127.0.0.1 - - [09/May/2020:03:45:11 0000] "GET /plugins/jira/3.0.15/jira.h
到这里我们就完成了 Jenkins 插件的加速,当然你也可以直接使用中文社区提供插件源,这里只是提供一种解决的思路。