写点什么

三万字无坑搭建基于 Docker+K8S+GitLab/SVN+Jenkins+Harbor 持续集成交付环境!!

用户头像
冰河
关注
发布于: 2020 年 12 月 01 日
三万字无坑搭建基于Docker+K8S+GitLab/SVN+Jenkins+Harbor持续集成交付环境!!

写在前面


最近在 K8S 1.18.2 版本的集群上搭建 DevOps 环境,期间遇到了各种坑。目前,搭建环境的过程中出现的各种坑均已被填平,特此记录,并分享给大家!


文章和搭建环境所需要的 yml 文件已收录到:https://github.com/sunshinelyz/technology-binghehttps://gitee.com/binghe001/technology-binghe 。如果文件对你有点帮助,别忘记给个 Star 哦!


服务器规划


安装环境版本



服务器免密码登录


在各服务器执行如下命令。


ssh-keygen -t rsacat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys 
复制代码


将 binghe102 和 binghe103 服务器上的 id_rsa.pub 文件复制到 binghe101 服务器。


[root@binghe102 ~]# scp .ssh/id_rsa.pub binghe101:/root/.ssh/102[root@binghe103 ~]# scp .ssh/id_rsa.pub binghe101:/root/.ssh/103
复制代码


在 binghe101 服务器上执行如下命令。


cat ~/.ssh/102 >> ~/.ssh/authorized_keyscat ~/.ssh/103 >> ~/.ssh/authorized_keys
复制代码


然后将 authorized_keys 文件分别复制到 binghe102、binghe103 服务器。


[root@binghe101 ~]# scp .ssh/authorized_keys binghe102:/root/.ssh/authorized_keys[root@binghe101 ~]# scp .ssh/authorized_keys binghe103:/root/.ssh/authorized_keys
复制代码


删除 binghe101 节点上~/.ssh 下的 102 和 103 文件。


rm ~/.ssh/102rm ~/.ssh/103
复制代码


安装 JDK


需要在每台服务器上安装 JDK 环境。到 Oracle 官方下载 JDK,我这里下的 JDK 版本为 1.8.0_202,下载后解压并配置系统环境变量。


tar -zxvf jdk1.8.0_212.tar.gzmv jdk1.8.0_212 /usr/local
复制代码


接下来,配置系统环境变量。


vim /etc/profile
复制代码


配置项内容如下所示。


JAVA_HOME=/usr/local/jdk1.8.0_212CLASS_PATH=.:$JAVA_HOME/libPATH=$JAVA_HOME/bin:$PATHexport JAVA_HOME CLASS_PATH PATH
复制代码


接下来执行如下命令使系统环境变量生效。


source /etc/profile
复制代码


安装 Maven


到 Apache 官方下载 Maven,我这里下载的 Maven 版本为 3.6.3。下载后直接解压并配置系统环境变量。


tar -zxvf apache-maven-3.6.3-bin.tar.gzmv apache-maven-3.6.3-bin /usr/local
复制代码


接下来,就是配置系统环境变量。


vim /etc/profile
复制代码


配置项内容如下所示。


JAVA_HOME=/usr/local/jdk1.8.0_212MAVEN_HOME=/usr/local/apache-maven-3.6.3-binCLASS_PATH=.:$JAVA_HOME/libPATH=$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATHexport JAVA_HOME CLASS_PATH MAVEN_HOME PATH
复制代码


接下来执行如下命令使系统环境变量生效。


source /etc/profile
复制代码


接下来,修改 Maven 的配置文件,如下所示。


<localRepository>/home/repository</localRepository>
复制代码


将 Maven 下载的 Jar 包存储到/home/repository 目录下。


安装 Docker 环境


本文档基于 Docker 19.03.8 版本搭建 Docker 环境。


在所有服务器上创建 install_docker.sh 脚本,脚本内容如下所示。


export REGISTRY_MIRROR=https://registry.cn-hangzhou.aliyuncs.comdnf install yum*yum install -y yum-utils device-mapper-persistent-data lvm2yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repodnf install https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.13-3.1.el7.x86_64.rpmyum install -y docker-ce-19.03.8 docker-ce-cli-19.03.8systemctl enable docker.servicesystemctl start docker.servicedocker version
复制代码


在每台服务器上为 install_docker.sh 脚本赋予可执行权限,并执行脚本即可。


安装 docker-compose


注意:在每台服务器上安装 docker-compose


1.下载 docker-compose 文件


curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 
复制代码


2.为 docker-compose 文件赋予可执行权限


chmod a+x /usr/local/bin/docker-compose
复制代码


3.查看 docker-compose 版本


[root@binghe ~]# docker-compose versiondocker-compose version 1.25.5, build 8a1c60f6docker-py version: 4.1.0CPython version: 3.7.5OpenSSL version: OpenSSL 1.1.0l  10 Sep 2019
复制代码


安装 K8S 集群环境


本文档基于 K8S 1.8.12 版本来搭建 K8S 集群


安装 K8S 基础环境


在所有服务器上创建 install_k8s.sh 脚本文件,脚本文件的内容如下所示。


#配置阿里云镜像加速器mkdir -p /etc/dockertee /etc/docker/daemon.json <<-'EOF'{  "registry-mirrors": ["https://zz3sblpi.mirror.aliyuncs.com"]}EOFsystemctl daemon-reloadsystemctl restart docker
#安装nfs-utilsyum install -y nfs-utilsyum install -y wget
#启动nfs-serversystemctl start nfs-serversystemctl enable nfs-server
#关闭防火墙systemctl stop firewalldsystemctl disable firewalld
#关闭SeLinuxsetenforce 0sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
# 关闭 swapswapoff -ayes | cp /etc/fstab /etc/fstab_bakcat /etc/fstab_bak |grep -v swap > /etc/fstab
#修改 /etc/sysctl.conf# 如果有配置,则修改sed -i "s#^net.ipv4.ip_forward.*#net.ipv4.ip_forward=1#g" /etc/sysctl.confsed -i "s#^net.bridge.bridge-nf-call-ip6tables.*#net.bridge.bridge-nf-call-ip6tables=1#g" /etc/sysctl.confsed -i "s#^net.bridge.bridge-nf-call-iptables.*#net.bridge.bridge-nf-call-iptables=1#g" /etc/sysctl.confsed -i "s#^net.ipv6.conf.all.disable_ipv6.*#net.ipv6.conf.all.disable_ipv6=1#g" /etc/sysctl.confsed -i "s#^net.ipv6.conf.default.disable_ipv6.*#net.ipv6.conf.default.disable_ipv6=1#g" /etc/sysctl.confsed -i "s#^net.ipv6.conf.lo.disable_ipv6.*#net.ipv6.conf.lo.disable_ipv6=1#g" /etc/sysctl.confsed -i "s#^net.ipv6.conf.all.forwarding.*#net.ipv6.conf.all.forwarding=1#g" /etc/sysctl.conf# 可能没有,追加echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.confecho "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.confecho "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.confecho "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.confecho "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.confecho "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.confecho "net.ipv6.conf.all.forwarding = 1" >> /etc/sysctl.conf# 执行命令以应用sysctl -p
# 配置K8S的yum源cat <<EOF > /etc/yum.repos.d/kubernetes.repo[kubernetes]name=Kubernetesbaseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64enabled=1gpgcheck=0repo_gpgcheck=0gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpgEOF
# 卸载旧版本K8Syum remove -y kubelet kubeadm kubectl
# 安装kubelet、kubeadm、kubectl,这里我安装的是1.18.2版本,你也可以安装1.17.2版本yum install -y kubelet-1.18.2 kubeadm-1.18.2 kubectl-1.18.2
# 修改docker Cgroup Driver为systemd# # 将/usr/lib/systemd/system/docker.service文件中的这一行 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock# # 修改为 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --exec-opt native.cgroupdriver=systemd# 如果不修改,在添加 worker 节点时可能会碰到如下错误# [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". # Please follow the guide at https://kubernetes.io/docs/setup/cri/sed -i "s#^ExecStart=/usr/bin/dockerd.*#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --exec-opt native.cgroupdriver=systemd#g" /usr/lib/systemd/system/docker.service
# 设置 docker 镜像,提高 docker 镜像下载速度和稳定性# 如果访问 https://hub.docker.io 速度非常稳定,亦可以跳过这个步骤# curl -sSL https://kuboard.cn/install-script/set_mirror.sh | sh -s ${REGISTRY_MIRROR}
# 重启 docker,并启动 kubeletsystemctl daemon-reloadsystemctl restart dockersystemctl enable kubelet && systemctl start kubelet
docker version
复制代码


在每台服务器上为 install_k8s.sh 脚本赋予可执行权限,并执行脚本即可。


初始化 Master 节点


只在 binghe101 服务器上执行的操作。


1.初始化 Master 节点的网络环境


注意:下面的命令需要在命令行手动执行。


# 只在 master 节点执行# export 命令只在当前 shell 会话中有效,开启新的 shell 窗口后,如果要继续安装过程,请重新执行此处的 export 命令export MASTER_IP=192.168.175.101# 替换 k8s.master 为 您想要的 dnsNameexport APISERVER_NAME=k8s.master# Kubernetes 容器组所在的网段,该网段安装完成后,由 kubernetes 创建,事先并不存在于物理网络中export POD_SUBNET=172.18.0.1/16echo "${MASTER_IP}    ${APISERVER_NAME}" >> /etc/hosts
复制代码


2.初始化 Master 节点


在 binghe101 服务器上创建 init_master.sh 脚本文件,文件内容如下所示。


#!/bin/bash# 脚本出错时终止执行set -e
if [ ${#POD_SUBNET} -eq 0 ] || [ ${#APISERVER_NAME} -eq 0 ]; then echo -e "\033[31;1m请确保您已经设置了环境变量 POD_SUBNET 和 APISERVER_NAME \033[0m" echo 当前POD_SUBNET=$POD_SUBNET echo 当前APISERVER_NAME=$APISERVER_NAME exit 1fi

# 查看完整配置选项 https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2rm -f ./kubeadm-config.yamlcat <<EOF > ./kubeadm-config.yamlapiVersion: kubeadm.k8s.io/v1beta2kind: ClusterConfigurationkubernetesVersion: v1.18.2imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containerscontrolPlaneEndpoint: "${APISERVER_NAME}:6443"networking: serviceSubnet: "10.96.0.0/16" podSubnet: "${POD_SUBNET}" dnsDomain: "cluster.local"EOF
# kubeadm init# 根据服务器网速的情况,您需要等候 3 - 10 分钟kubeadm init --config=kubeadm-config.yaml --upload-certs
# 配置 kubectlrm -rf /root/.kube/mkdir /root/.kube/cp -i /etc/kubernetes/admin.conf /root/.kube/config
# 安装 calico 网络插件# 参考文档 https://docs.projectcalico.org/v3.13/getting-started/kubernetes/self-managed-onprem/onpremisesecho "安装calico-3.13.1"rm -f calico-3.13.1.yamlwget https://kuboard.cn/install-script/calico/calico-3.13.1.yamlkubectl apply -f calico-3.13.1.yaml
复制代码


赋予 init_master.sh 脚本文件可执行权限并执行脚本。


3.查看 Master 节点的初始化结果


(1)确保所有容器组处于 Running 状态


# 执行如下命令,等待 3-10 分钟,直到所有的容器组处于 Running 状态watch kubectl get pod -n kube-system -o wide
复制代码


具体执行如下所示。


[root@binghe101 ~]# watch kubectl get pod -n kube-system -o wideEvery 2.0s: kubectl get pod -n kube-system -o wide                                                                                                                          binghe101: Sun May 10 11:01:32 2020
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES calico-kube-controllers-5b8b769fcd-5dtlp 1/1 Running 0 118s 172.18.203.66 binghe101 <none> <none> calico-node-fnv8g 1/1 Running 0 118s 192.168.175.101 binghe101 <none> <none> coredns-546565776c-27t7h 1/1 Running 0 2m1s 172.18.203.67 binghe101 <none> <none> coredns-546565776c-hjb8z 1/1 Running 0 2m1s 172.18.203.65 binghe101 <none> <none> etcd-binghe101 1/1 Running 0 2m7s 192.168.175.101 binghe101 <none> <none> kube-apiserver-binghe101 1/1 Running 0 2m7s 192.168.175.101 binghe101 <none> <none> kube-controller-manager-binghe101 1/1 Running 0 2m7s 192.168.175.101 binghe101 <none> <none> kube-proxy-dvgsr 1/1 Running 0 2m1s 192.168.175.101 binghe101 <none> <none> kube-scheduler-binghe101 1/1 Running 0 2m7s 192.168.175.101 binghe101 <none> <none>
复制代码


(2) 查看 Master 节点初始化结果


kubectl get nodes -o wide
复制代码


具体执行如下所示。


[root@binghe101 ~]# kubectl get nodes -o wideNAME        STATUS   ROLES    AGE     VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION         CONTAINER-RUNTIMEbinghe101   Ready    master   3m28s   v1.18.2   192.168.175.101   <none>        CentOS Linux 8 (Core)   4.18.0-80.el8.x86_64   docker://19.3.8
复制代码


初始化 Worker 节点


1.获取 join 命令参数


在 Master 节点(binghe101 服务器)上执行如下命令获取 join 命令参数。


kubeadm token create --print-join-command
复制代码


具体执行如下所示。


[root@binghe101 ~]# kubeadm token create --print-join-commandW0510 11:04:34.828126   56132 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]kubeadm join k8s.master:6443 --token 8nblts.62xytoqufwsqzko2     --discovery-token-ca-cert-hash sha256:1717cc3e34f6a56b642b5751796530e367aa73f4113d09994ac3455e33047c0d 
复制代码


其中,有如下一行输出。


kubeadm join k8s.master:6443 --token 8nblts.62xytoqufwsqzko2     --discovery-token-ca-cert-hash sha256:1717cc3e34f6a56b642b5751796530e367aa73f4113d09994ac3455e33047c0d 
复制代码


这行代码就是获取到的 join 命令。


注意:join 命令中的 token 的有效时间为 2 个小时,2 小时内,可以使用此 token 初始化任意数量的 worker 节点。


2.初始化 Worker 节点


针对所有的 worker 节点执行,在这里,就是在 binghe102 服务器和 binghe103 服务器上执行。


在命令分别手动执行如下命令。


# 只在 worker 节点执行# 192.168.175.101 为 master 节点的内网 IPexport MASTER_IP=192.168.175.101# 替换 k8s.master 为初始化 master 节点时所使用的 APISERVER_NAMEexport APISERVER_NAME=k8s.masterecho "${MASTER_IP}    ${APISERVER_NAME}" >> /etc/hosts
# 替换为 master 节点上 kubeadm token create 命令输出的joinkubeadm join k8s.master:6443 --token 8nblts.62xytoqufwsqzko2 --discovery-token-ca-cert-hash sha256:1717cc3e34f6a56b642b5751796530e367aa73f4113d09994ac3455e33047c0d
复制代码


具体执行如下所示。


[root@binghe102 ~]# export MASTER_IP=192.168.175.101[root@binghe102 ~]# export APISERVER_NAME=k8s.master[root@binghe102 ~]# echo "${MASTER_IP}    ${APISERVER_NAME}" >> /etc/hosts[root@binghe102 ~]# kubeadm join k8s.master:6443 --token 8nblts.62xytoqufwsqzko2     --discovery-token-ca-cert-hash sha256:1717cc3e34f6a56b642b5751796530e367aa73f4113d09994ac3455e33047c0d W0510 11:08:27.709263   42795 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set.[preflight] Running pre-flight checks        [WARNING FileExisting-tc]: tc not found in system path[preflight] Reading configuration from the cluster...[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.18" ConfigMap in the kube-system namespace[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"[kubelet-start] Starting the kubelet[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:* Certificate signing request was sent to apiserver and a response was received.* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
复制代码


根据输出结果可以看出,Worker 节点加入了 K8S 集群。


注意:kubeadm join…就是 master 节点上 kubeadm token create 命令输出的 join。


3.查看初始化结果


在 Master 节点(binghe101 服务器)执行如下命令查看初始化结果。


kubectl get nodes -o wide
复制代码


具体执行如下所示。


[root@binghe101 ~]# kubectl get nodesNAME        STATUS   ROLES    AGE     VERSIONbinghe101   Ready    master   20m     v1.18.2binghe102   Ready    <none>   2m46s   v1.18.2binghe103   Ready    <none>   2m46s   v1.18.2
复制代码


注意:kubectl get nodes 命令后面加上-o wide 参数可以输出更多的信息。


重启 K8S 集群引起的问题


1.Worker 节点故障不能启动


Master 节点的 IP 地址发生变化,导致 worker 节点不能启动。需要重新安装 K8S 集群,并确保所有节点都有固定的内网 IP 地址。


2.Pod 崩溃或不能正常访问


重启服务器后使用如下命令查看 Pod 的运行状态。


kubectl get pods --all-namespaces
复制代码


发现很多 Pod 不在 Running 状态,此时,需要使用如下命令删除运行不正常的 Pod。


kubectl delete pod <pod-name> -n <pod-namespece>
复制代码


注意:如果 Pod 是使用 Deployment、StatefulSet 等控制器创建的,K8S 将创建新的 Pod 作为替代,重新启动的 Pod 通常能够正常工作。


K8S 安装 ingress-nginx


注意:在 Master 节点(binghe101 服务器上执行)


1.创建 ingress-nginx 命名空间


创建 ingress-nginx-namespace.yaml 文件,文件内容如下所示。


apiVersion: v1kind: Namespacemetadata:  name: ingress-nginx  labels:    name: ingress-nginx
复制代码


执行如下命令创建 ingress-nginx 命名空间。


kubectl apply -f ingress-nginx-namespace.yaml
复制代码


2.安装 ingress controller


创建 ingress-nginx-mandatory.yaml 文件,文件内容如下所示。


apiVersion: v1kind: Namespacemetadata:  name: ingress-nginx
---
apiVersion: apps/v1kind: Deploymentmetadata: name: default-http-backend labels: app.kubernetes.io/name: default-http-backend app.kubernetes.io/part-of: ingress-nginx namespace: ingress-nginxspec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: default-http-backend app.kubernetes.io/part-of: ingress-nginx template: metadata: labels: app.kubernetes.io/name: default-http-backend app.kubernetes.io/part-of: ingress-nginx spec: terminationGracePeriodSeconds: 60 containers: - name: default-http-backend # Any image is permissible as long as: # 1. It serves a 404 page at / # 2. It serves 200 on a /healthz endpoint image: registry.cn-qingdao.aliyuncs.com/kubernetes_xingej/defaultbackend-amd64:1.5 livenessProbe: httpGet: path: /healthz port: 8080 scheme: HTTP initialDelaySeconds: 30 timeoutSeconds: 5 ports: - containerPort: 8080 resources: limits: cpu: 10m memory: 20Mi requests: cpu: 10m memory: 20Mi
---apiVersion: v1kind: Servicemetadata: name: default-http-backend namespace: ingress-nginx labels: app.kubernetes.io/name: default-http-backend app.kubernetes.io/part-of: ingress-nginxspec: ports: - port: 80 targetPort: 8080 selector: app.kubernetes.io/name: default-http-backend app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMapapiVersion: v1metadata: name: nginx-configuration namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMapapiVersion: v1metadata: name: tcp-services namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMapapiVersion: v1metadata: name: udp-services namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: v1kind: ServiceAccountmetadata: name: nginx-ingress-serviceaccount namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
---apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRolemetadata: name: nginx-ingress-clusterrole labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginxrules: - apiGroups: - "" resources: - configmaps - endpoints - nodes - pods - secrets verbs: - list - watch - apiGroups: - "" resources: - nodes verbs: - get - apiGroups: - "" resources: - services verbs: - get - list - watch - apiGroups: - "extensions" resources: - ingresses verbs: - get - list - watch - apiGroups: - "" resources: - events verbs: - create - patch - apiGroups: - "extensions" resources: - ingresses/status verbs: - update
---apiVersion: rbac.authorization.k8s.io/v1beta1kind: Rolemetadata: name: nginx-ingress-role namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginxrules: - apiGroups: - "" resources: - configmaps - pods - secrets - namespaces verbs: - get - apiGroups: - "" resources: - configmaps resourceNames: # Defaults to "<election-id>-<ingress-class>" # Here: "<ingress-controller-leader>-<nginx>" # This has to be adapted if you change either parameter # when launching the nginx-ingress-controller. - "ingress-controller-leader-nginx" verbs: - get - update - apiGroups: - "" resources: - configmaps verbs: - create - apiGroups: - "" resources: - endpoints verbs: - get
---apiVersion: rbac.authorization.k8s.io/v1beta1kind: RoleBindingmetadata: name: nginx-ingress-role-nisa-binding namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginxroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: nginx-ingress-rolesubjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx
---apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: nginx-ingress-clusterrole-nisa-binding labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginxroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: nginx-ingress-clusterrolesubjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx
---
apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-ingress-controller namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginxspec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx template: metadata: labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx annotations: prometheus.io/port: "10254" prometheus.io/scrape: "true" spec: serviceAccountName: nginx-ingress-serviceaccount containers: - name: nginx-ingress-controller image: registry.cn-qingdao.aliyuncs.com/kubernetes_xingej/nginx-ingress-controller:0.20.0 args: - /nginx-ingress-controller - --default-backend-service=$(POD_NAMESPACE)/default-http-backend - --configmap=$(POD_NAMESPACE)/nginx-configuration - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services - --publish-service=$(POD_NAMESPACE)/ingress-nginx - --annotations-prefix=nginx.ingress.kubernetes.io securityContext: capabilities: drop: - ALL add: - NET_BIND_SERVICE # www-data -> 33 runAsUser: 33 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - name: http containerPort: 80 - name: https containerPort: 443 livenessProbe: failureThreshold: 3 httpGet: path: /healthz port: 10254 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 readinessProbe: failureThreshold: 3 httpGet: path: /healthz port: 10254 scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1
---
复制代码


执行如下命令安装 ingress controller。


kubectl apply -f ingress-nginx-mandatory.yaml
复制代码


3.安装 K8S SVC:ingress-nginx


主要是用来用于暴露 pod:nginx-ingress-controller。


创建 service-nodeport.yaml 文件,文件内容如下所示。


apiVersion: v1kind: Servicemetadata:  name: ingress-nginx  namespace: ingress-nginx  labels:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/part-of: ingress-nginxspec:  type: NodePort  ports:    - name: http      port: 80      targetPort: 80      protocol: TCP      nodePort: 30080    - name: https      port: 443      targetPort: 443      protocol: TCP      nodePort: 30443  selector:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/part-of: ingress-nginx
复制代码


执行如下命令安装。


kubectl apply -f service-nodeport.yaml
复制代码


4.访问 K8S SVC:ingress-nginx


查看 ingress-nginx 命名空间的部署情况,如下所示。


[root@binghe101 k8s]# kubectl get pod -n ingress-nginxNAME                                        READY   STATUS    RESTARTS   AGEdefault-http-backend-796ddcd9b-vfmgn        1/1     Running   1          10hnginx-ingress-controller-58985cc996-87754   1/1     Running   2          10h
复制代码


在命令行服务器命令行输入如下命令查看 ingress-nginx 的端口映射情况。


kubectl get svc -n ingress-nginx 
复制代码


具体如下所示。


[root@binghe101 k8s]# kubectl get svc -n ingress-nginx NAME                   TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGEdefault-http-backend   ClusterIP   10.96.247.2   <none>        80/TCP                       7m3singress-nginx          NodePort    10.96.40.6    <none>        80:30080/TCP,443:30443/TCP   4m35s
复制代码


所以,可以通过 Master 节点(binghe101 服务器)的 IP 地址和 30080 端口号来访问 ingress-nginx,如下所示。


[root@binghe101 k8s]# curl 192.168.175.101:30080       default backend - 404
复制代码


也可以在浏览器打开http://192.168.175.101:30080 来访问 ingress-nginx,如下所示。


K8S 安装 gitlab 代码仓库


注意:在 Master 节点(binghe101 服务器上执行)


1.创建 k8s-ops 命名空间


创建 k8s-ops-namespace.yaml 文件,文件内容如下所示。


apiVersion: v1kind: Namespacemetadata:  name: k8s-ops  labels:    name: k8s-ops
复制代码


执行如下命令创建命名空间。


kubectl apply -f k8s-ops-namespace.yaml 
复制代码


2.安装 gitlab-redis


创建 gitlab-redis.yaml 文件,文件的内容如下所示。


apiVersion: apps/v1kind: Deploymentmetadata:  name: redis  namespace: k8s-ops  labels:    name: redisspec:  selector:    matchLabels:      name: redis  template:    metadata:      name: redis      labels:        name: redis    spec:      containers:      - name: redis        image: sameersbn/redis        imagePullPolicy: IfNotPresent        ports:        - name: redis          containerPort: 6379        volumeMounts:        - mountPath: /var/lib/redis          name: data        livenessProbe:          exec:            command:            - redis-cli            - ping          initialDelaySeconds: 30          timeoutSeconds: 5        readinessProbe:          exec:            command:            - redis-cli            - ping          initialDelaySeconds: 10          timeoutSeconds: 5      volumes:      - name: data        hostPath:          path: /data1/docker/xinsrv/redis
---apiVersion: v1kind: Servicemetadata: name: redis namespace: k8s-ops labels: name: redisspec: ports: - name: redis port: 6379 targetPort: redis selector: name: redis
复制代码


首先,在命令行执行如下命令创建/data1/docker/xinsrv/redis 目录。


mkdir -p /data1/docker/xinsrv/redis
复制代码


执行如下命令安装 gitlab-redis。


kubectl apply -f gitlab-redis.yaml 
复制代码


3.安装 gitlab-postgresql


创建 gitlab-postgresql.yaml,文件内容如下所示。


apiVersion: apps/v1kind: Deploymentmetadata:  name: postgresql  namespace: k8s-ops  labels:    name: postgresqlspec:  selector:    matchLabels:      name: postgresql  template:    metadata:      name: postgresql      labels:        name: postgresql    spec:      containers:      - name: postgresql        image: sameersbn/postgresql        imagePullPolicy: IfNotPresent        env:        - name: DB_USER          value: gitlab        - name: DB_PASS          value: passw0rd        - name: DB_NAME          value: gitlab_production        - name: DB_EXTENSION          value: pg_trgm        ports:        - name: postgres          containerPort: 5432        volumeMounts:        - mountPath: /var/lib/postgresql          name: data        livenessProbe:          exec:            command:            - pg_isready            - -h            - localhost            - -U            - postgres          initialDelaySeconds: 30          timeoutSeconds: 5        readinessProbe:          exec:            command:            - pg_isready            - -h            - localhost            - -U            - postgres          initialDelaySeconds: 5          timeoutSeconds: 1      volumes:      - name: data        hostPath:          path: /data1/docker/xinsrv/postgresql---apiVersion: v1kind: Servicemetadata:  name: postgresql  namespace: k8s-ops  labels:    name: postgresqlspec:  ports:    - name: postgres      port: 5432      targetPort: postgres  selector:    name: postgresql
复制代码


首先,执行如下命令创建/data1/docker/xinsrv/postgresql 目录。


mkdir -p /data1/docker/xinsrv/postgresql
复制代码


接下来,安装 gitlab-postgresql,如下所示。


kubectl apply -f gitlab-postgresql.yaml
复制代码


4.安装 gitlab


(1)配置用户名和密码


首先,在命令行使用 base64 编码为用户名和密码进行转码,本示例中,使用的用户名为 admin,密码为 admin.1231


转码情况如下所示。


[root@binghe101 k8s]# echo -n 'admin' | base64 YWRtaW4=[root@binghe101 k8s]# echo -n 'admin.1231' | base64 YWRtaW4uMTIzMQ==
复制代码


转码后的用户名为:YWRtaW4= 密码为:YWRtaW4uMTIzMQ==


也可以对 base64 编码后的字符串解码,例如,对密码字符串解码,如下所示。


[root@binghe101 k8s]# echo 'YWRtaW4uMTIzMQ==' | base64 --decode admin.1231
复制代码


接下来,创建 secret-gitlab.yaml 文件,主要是用户来配置 GitLab 的用户名和密码,文件内容如下所示。


apiVersion: v1kind: Secretmetadata:  namespace: k8s-ops  name: git-user-passtype: Opaquedata:  username: YWRtaW4=  password: YWRtaW4uMTIzMQ==
复制代码


执行配置文件的内容,如下所示。


kubectl create -f ./secret-gitlab.yaml
复制代码


(2)安装 GitLab


创建 gitlab.yaml 文件,文件的内容如下所示。


apiVersion: apps/v1kind: Deploymentmetadata:  name: gitlab  namespace: k8s-ops  labels:    name: gitlabspec:  selector:    matchLabels:      name: gitlab  template:    metadata:      name: gitlab      labels:        name: gitlab    spec:      containers:      - name: gitlab        image: sameersbn/gitlab:12.1.6        imagePullPolicy: IfNotPresent        env:        - name: TZ          value: Asia/Shanghai        - name: GITLAB_TIMEZONE          value: Beijing        - name: GITLAB_SECRETS_DB_KEY_BASE          value: long-and-random-alpha-numeric-string        - name: GITLAB_SECRETS_SECRET_KEY_BASE          value: long-and-random-alpha-numeric-string        - name: GITLAB_SECRETS_OTP_KEY_BASE          value: long-and-random-alpha-numeric-string        - name: GITLAB_ROOT_PASSWORD          valueFrom:            secretKeyRef:              name: git-user-pass              key: password        - name: GITLAB_ROOT_EMAIL          value: 12345678@qq.com        - name: GITLAB_HOST          value: gitlab.binghe.com        - name: GITLAB_PORT          value: "80"        - name: GITLAB_SSH_PORT          value: "30022"        - name: GITLAB_NOTIFY_ON_BROKEN_BUILDS          value: "true"        - name: GITLAB_NOTIFY_PUSHER          value: "false"        - name: GITLAB_BACKUP_SCHEDULE          value: daily        - name: GITLAB_BACKUP_TIME          value: 01:00        - name: DB_TYPE          value: postgres        - name: DB_HOST          value: postgresql        - name: DB_PORT          value: "5432"        - name: DB_USER          value: gitlab        - name: DB_PASS          value: passw0rd        - name: DB_NAME          value: gitlab_production        - name: REDIS_HOST          value: redis        - name: REDIS_PORT          value: "6379"        ports:        - name: http          containerPort: 80        - name: ssh          containerPort: 22        volumeMounts:        - mountPath: /home/git/data          name: data        livenessProbe:          httpGet:            path: /            port: 80          initialDelaySeconds: 180          timeoutSeconds: 5        readinessProbe:          httpGet:            path: /            port: 80          initialDelaySeconds: 5          timeoutSeconds: 1      volumes:      - name: data        hostPath:          path: /data1/docker/xinsrv/gitlab---apiVersion: v1kind: Servicemetadata:  name: gitlab  namespace: k8s-ops  labels:    name: gitlabspec:  ports:    - name: http      port: 80      nodePort: 30088    - name: ssh      port: 22      targetPort: ssh      nodePort: 30022  type: NodePort  selector:    name: gitlab
---apiVersion: extensions/v1beta1kind: Ingressmetadata: name: gitlab namespace: k8s-ops annotations: kubernetes.io/ingress.class: traefikspec: rules: - host: gitlab.binghe.com http: paths: - backend: serviceName: gitlab servicePort: http
复制代码


注意:在配置 GitLab 时,监听主机时,不能使用 IP 地址,需要使用主机名或者域名,上述配置中,我使用的是 gitlab.binghe.com 主机名。


在命令行执行如下命令创建/data1/docker/xinsrv/gitlab 目录。


mkdir -p /data1/docker/xinsrv/gitlab
复制代码


安装 GitLab,如下所示。


kubectl apply -f gitlab.yaml
复制代码


5.安装完成


查看 k8s-ops 命名空间部署情况,如下所示。


[root@binghe101 k8s]# kubectl get pod -n k8s-opsNAME                          READY   STATUS    RESTARTS   AGEgitlab-7b459db47c-5vk6t       0/1     Running   0          11spostgresql-79567459d7-x52vx   1/1     Running   0          30mredis-67f4cdc96c-h5ckz        1/1     Running   1          10h
复制代码


也可以使用如下命令查看。


[root@binghe101 k8s]# kubectl get pod --namespace=k8s-opsNAME                          READY   STATUS    RESTARTS   AGEgitlab-7b459db47c-5vk6t       0/1     Running   0          36spostgresql-79567459d7-x52vx   1/1     Running   0          30mredis-67f4cdc96c-h5ckz        1/1     Running   1          10h
复制代码


二者效果一样。


接下来,查看 GitLab 的端口映射,如下所示。


[root@binghe101 k8s]# kubectl get svc -n k8s-opsNAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                     AGEgitlab       NodePort    10.96.153.100   <none>        80:30088/TCP,22:30022/TCP   2m42spostgresql   ClusterIP   10.96.203.119   <none>        5432/TCP                    32mredis        ClusterIP   10.96.107.150   <none>        6379/TCP                    10h
复制代码


此时,可以看到,可以通过 Master 节点(binghe101)的主机名 gitlab.binghe.com 和端口 30088 就能够访问 GitLab。由于我这里使用的是虚拟机来搭建相关的环境,在本机访问虚拟机映射的 gitlab.binghe.com 时,需要配置本机的 hosts 文件,在本机的 hosts 文件中加入如下配置项。


192.168.175.101 gitlab.binghe.com
复制代码


注意:在 Windows 操作系统中,hosts 文件所在的目录如下。


C:\Windows\System32\drivers\etc
复制代码


接下来,就可以在浏览器中通过链接:http://gitlab.binghe.com:30088 来访问 GitLab 了,如下所示。



此时,可以通过用户名 root 和密码 admin.1231 来登录 GitLab 了。


注意:这里的用户名是 root 而不是 admin,因为 root 是 GitLab 默认的超级用户。



登录后的界面如下所示。



到此,K8S 安装 gitlab 完成。


安装 Harbor 私有仓库


注意:这里将 Harbor 私有仓库安装在 Master 节点(binghe101 服务器)上,实际生产环境中建议安装在其他服务器。


1.下载 Harbor 的离线安装版本


wget https://github.com/goharbor/harbor/releases/download/v1.10.2/harbor-offline-installer-v1.10.2.tgz
复制代码


2.解压 Harbor 的安装包


tar -zxvf harbor-offline-installer-v1.10.2.tgz
复制代码


解压成功后,会在服务器当前目录生成一个 harbor 目录。


3.配置 Harbor


注意:这里,我将 Harbor 的端口修改成了 1180,如果不修改 Harbor 的端口,默认的端口是 80。


(1)修改 harbor.yml 文件


cd harborvim harbor.yml
复制代码


修改的配置项如下所示。


hostname: 192.168.175.101http:  port: 1180harbor_admin_password: binghe123###并把https注释掉,不然在安装的时候会报错:ERROR:root:Error: The protocol is https but attribute ssl_cert is not set#https:  #port: 443  #certificate: /your/certificate/path  #private_key: /your/private/key/path
复制代码


(2)修改 daemon.json 文件


修改/etc/docker/daemon.json 文件,没有的话就创建,在/etc/docker/daemon.json 文件中添加如下内容。


[root@binghe~]# cat /etc/docker/daemon.json{  "registry-mirrors": ["https://zz3sblpi.mirror.aliyuncs.com"],  "insecure-registries":["192.168.175.101:1180"]}
复制代码


也可以在服务器上使用 ip addr 命令查看本机所有的 IP 地址段,将其配置到/etc/docker/daemon.json 文件中。这里,我配置后的文件内容如下所示。


{    "registry-mirrors": ["https://zz3sblpi.mirror.aliyuncs.com"],    "insecure-registries":["192.168.175.0/16","172.17.0.0/16", "172.18.0.0/16", "172.16.29.0/16", "192.168.175.101:1180"]}
复制代码


4.安装并启动 harbor


配置完成后,输入如下命令即可安装并启动 Harbor


[root@binghe harbor]# ./install.sh 
复制代码


5.登录 Harbor 并添加账户


安装成功后,在浏览器地址栏输入http://192.168.175.101:1180打开链接,如下图所示。



输入用户名 admin 和密码 binghe123,登录系统,如下图所示



接下来,我们选择用户管理,添加一个管理员账户,为后续打包 Docker 镜像和上传 Docker 镜像做准备。添加账户的步骤如下所示。




此处填写的密码为 Binghe123。


点击确定后,如下所示。


此时,账户 binghe 还不是管理员,此时选中 binghe 账户,点击“设置为管理员”。




此时,binghe 账户就被设置为管理员了。到此,Harbor 的安装就完成了。


6.修改 Harbor 端口


如果安装 Harbor 后,大家需要修改 Harbor 的端口,可以按照如下步骤修改 Harbor 的端口,这里,我以将 80 端口修改为 1180 端口为例


(1)修改 harbor.yml 文件


cd harborvim harbor.yml
复制代码


修改的配置项如下所示。


hostname: 192.168.175.101http:  port: 1180harbor_admin_password: binghe123###并把https注释掉,不然在安装的时候会报错:ERROR:root:Error: The protocol is https but attribute ssl_cert is not set#https:  #port: 443  #certificate: /your/certificate/path  #private_key: /your/private/key/path
复制代码


(2)修改 docker-compose.yml 文件


vim docker-compose.yml
复制代码


修改的配置项如下所示。


ports:      - 1180:80
复制代码


(3)修改 config.yml 文件


cd common/config/registryvim config.yml
复制代码


修改的配置项如下所示。


realm: http://192.168.175.101:1180/service/token
复制代码


(4)重启 Docker


systemctl daemon-reloadsystemctl restart docker.service
复制代码


(5)重启 Harbor


[root@binghe harbor]# docker-compose downStopping harbor-log ... doneRemoving nginx             ... doneRemoving harbor-portal     ... doneRemoving harbor-jobservice ... doneRemoving harbor-core       ... doneRemoving redis             ... doneRemoving registry          ... doneRemoving registryctl       ... doneRemoving harbor-db         ... doneRemoving harbor-log        ... doneRemoving network harbor_harbor [root@binghe harbor]# ./prepareprepare base dir is set to /mnt/harborClearing the configuration file: /config/log/logrotate.confClearing the configuration file: /config/nginx/nginx.confClearing the configuration file: /config/core/envClearing the configuration file: /config/core/app.confClearing the configuration file: /config/registry/root.crtClearing the configuration file: /config/registry/config.ymlClearing the configuration file: /config/registryctl/envClearing the configuration file: /config/registryctl/config.ymlClearing the configuration file: /config/db/envClearing the configuration file: /config/jobservice/envClearing the configuration file: /config/jobservice/config.ymlGenerated configuration file: /config/log/logrotate.confGenerated configuration file: /config/nginx/nginx.confGenerated configuration file: /config/core/envGenerated configuration file: /config/core/app.confGenerated configuration file: /config/registry/config.ymlGenerated configuration file: /config/registryctl/envGenerated configuration file: /config/db/envGenerated configuration file: /config/jobservice/envGenerated configuration file: /config/jobservice/config.ymlloaded secret from file: /secret/keys/secretkeyGenerated configuration file: /compose_location/docker-compose.ymlClean up the input dir [root@binghe harbor]# docker-compose up -dCreating network "harbor_harbor" with the default driverCreating harbor-log ... doneCreating harbor-db   ... doneCreating redis       ... doneCreating registry    ... doneCreating registryctl ... doneCreating harbor-core ... doneCreating harbor-jobservice ... doneCreating harbor-portal     ... doneCreating nginx             ... done [root@binghe harbor]# docker ps -aCONTAINER ID        IMAGE                                               COMMAND                  CREATED             STATUS                             PORTS
复制代码


安装 Jenkins(一般的做法)


1.安装 nfs(之前安装过的话,可以省略此步)


使用 nfs 最大的问题就是写权限,可以使用 kubernetes 的 securityContext/runAsUser 指定 jenkins 容器中运行 jenkins 的用户 uid,以此来指定 nfs 目录的权限,让 jenkins 容器可写;也可以不限制,让所有用户都可以写。这里为了简单,就让所有用户可写了。


如果之前已经安装过 nfs,则这一步可以省略。找一台主机,安装 nfs,这里,我以在 Master 节点(binghe101 服务器)上安装 nfs 为例。


在命令行输入如下命令安装并启动 nfs。


yum install nfs-utils -ysystemctl start nfs-serversystemctl enable nfs-server
复制代码


2.创建 nfs 共享目录


在 Master 节点(binghe101 服务器)上创建 /opt/nfs/jenkins-data目录作为 nfs 的共享目录,如下所示。


mkdir -p /opt/nfs/jenkins-data
复制代码


接下来,编辑/etc/exports 文件,如下所示。


vim /etc/exports
复制代码


在/etc/exports 文件文件中添加如下一行配置。


/opt/nfs/jenkins-data 192.168.175.0/24(rw,all_squash)
复制代码


这里的 ip 使用 kubernetes node 节点的 ip 范围,后面的 all_squash 选项会将所有访问的用户都映射成 nfsnobody 用户,不管你是什么用户访问,最终都会压缩成 nfsnobody,所以只要将 /opt/nfs/jenkins-data 的属主改为 nfsnobody,那么无论什么用户来访问都具有写权限。


这个选项在很多机器上由于用户 uid 不规范导致启动进程的用户不同,但是同时要对一个共享目录具有写权限时很有效。


接下来,为 /opt/nfs/jenkins-data目录授权,并重新加载 nfs,如下所示。


chown -R 1000 /opt/nfs/jenkins-data/systemctl reload nfs-server
复制代码


在 K8S 集群中任意一个节点上使用如下命令进行验证:


showmount -e NFS_IP
复制代码


如果能够看到 /opt/nfs/jenkins-data 就表示 ok 了。


具体如下所示。


[root@binghe101 ~]# showmount -e 192.168.175.101Export list for 192.168.175.101:/opt/nfs/jenkins-data 192.168.175.0/24
[root@binghe102 ~]# showmount -e 192.168.175.101Export list for 192.168.175.101:/opt/nfs/jenkins-data 192.168.175.0/24
复制代码


3.创建 PV


Jenkins 其实只要加载对应的目录就可以读取之前的数据,但是由于 deployment 无法定义存储卷,因此我们只能使用 StatefulSet。


首先创建 pv,pv 是给 StatefulSet 使用的,每次 StatefulSet 启动都会通过 volumeClaimTemplates 这个模板去创建 pvc,因此必须得有 pv,才能供 pvc 绑定。


创建 jenkins-pv.yaml 文件,文件内容如下所示。


apiVersion: v1kind: PersistentVolumemetadata:  name: jenkinsspec:  nfs:    path: /opt/nfs/jenkins-data    server: 192.168.175.101  accessModes: ["ReadWriteOnce"]  capacity:    storage: 1Ti
复制代码


我这里给了 1T 存储空间,可以根据实际配置。


执行如下命令创建 pv。


kubectl apply -f jenkins-pv.yaml 
复制代码


4.创建 serviceAccount


创建 service account,因为 jenkins 后面需要能够动态创建 slave,因此它必须具备一些权限。


创建 jenkins-service-account.yaml 文件,文件内容如下所示。


apiVersion: v1kind: ServiceAccountmetadata:  name: jenkins
---kind: RoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata: name: jenkinsrules: - 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"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["secrets"] verbs: ["get"]
---apiVersion: rbac.authorization.k8s.io/v1beta1kind: RoleBindingmetadata: name: jenkinsroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: jenkinssubjects: - kind: ServiceAccount name: jenkins
复制代码


上述配置中,创建了一个 RoleBinding 和一个 ServiceAccount,并且将 RoleBinding 的权限绑定到这个用户上。所以,jenkins 容器必须使用这个 ServiceAccount 运行才行,不然 RoleBinding 的权限它将不具备。


RoleBinding 的权限很容易就看懂了,因为 jenkins 需要创建和删除 slave,所以才需要上面这些权限。至于 secrets 权限,则是 https 证书。


执行如下命令创建 serviceAccount。


kubectl apply -f jenkins-service-account.yaml 
复制代码


5.安装 Jenkins


创建 jenkins-statefulset.yaml 文件,文件内容如下所示。


apiVersion: apps/v1kind: StatefulSetmetadata:  name: jenkins  labels:    name: jenkinsspec:  selector:    matchLabels:      name: jenkins  serviceName: jenkins  replicas: 1  updateStrategy:    type: RollingUpdate  template:    metadata:      name: jenkins      labels:        name: jenkins    spec:      terminationGracePeriodSeconds: 10      serviceAccountName: jenkins      containers:        - name: jenkins          image: docker.io/jenkins/jenkins:lts          imagePullPolicy: IfNotPresent          ports:            - containerPort: 8080            - containerPort: 32100          resources:            limits:              cpu: 4              memory: 4Gi            requests:              cpu: 4              memory: 4Gi          env:            - name: LIMITS_MEMORY              valueFrom:                resourceFieldRef:                  resource: limits.memory                  divisor: 1Mi            - name: JAVA_OPTS              # value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85              value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85          volumeMounts:            - name: jenkins-home              mountPath: /var/jenkins_home          livenessProbe:            httpGet:              path: /login              port: 8080            initialDelaySeconds: 60            timeoutSeconds: 5            failureThreshold: 12 # ~2 minutes          readinessProbe:            httpGet:              path: /login              port: 8080            initialDelaySeconds: 60            timeoutSeconds: 5            failureThreshold: 12 # ~2 minutes  # pvc 模板,对应之前的 pv  volumeClaimTemplates:    - metadata:        name: jenkins-home      spec:        accessModes: ["ReadWriteOnce"]        resources:          requests:            storage: 1Ti
复制代码


jenkins 部署时需要注意它的副本数,你的副本数有多少就要有多少个 pv,同样,存储会有多倍消耗。这里我只使用了一个副本,因此前面也只创建了一个 pv。


使用如下命令安装 Jenkins。


kubectl apply -f jenkins-statefulset.yaml 
复制代码


6.创建 Service


创建 jenkins-service.yaml 文件,文件内容如下所示。


apiVersion: v1kind: Servicemetadata:  name: jenkinsspec:  # type: LoadBalancer  selector:    name: jenkins  # ensure the client ip is propagated to avoid the invalid crumb issue when using LoadBalancer (k8s >=1.7)  #externalTrafficPolicy: Local  ports:    - name: http      port: 80      nodePort: 31888      targetPort: 8080      protocol: TCP    - name: jenkins-agent      port: 32100      nodePort: 32100      targetPort: 32100      protocol: TCP  type: NodePort
复制代码


使用如下命令安装 Service。


kubectl apply -f jenkins-service.yaml 
复制代码


7.安装 ingress


jenkins 的 web 界面需要从集群外访问,这里我们选择的是使用 ingress。创建 jenkins-ingress.yaml 文件,文件内容如下所示。


apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: jenkinsspec:  rules:    - http:        paths:          - path: /            backend:              serviceName: jenkins              servicePort: 31888      host: jekins.binghe.com
复制代码


这里,需要注意的是 host 必须配置为域名或者主机名,否则会报错,如下所示。


The Ingress "jenkins" is invalid: spec.rules[0].host: Invalid value: "192.168.175.101": must be a DNS name, not an IP address
复制代码


使用如下命令安装 ingress。


kubectl apply -f jenkins-ingress.yaml 
复制代码


最后,由于我这里使用的是虚拟机来搭建相关的环境,在本机访问虚拟机映射的 jekins.binghe.com 时,需要配置本机的 hosts 文件,在本机的 hosts 文件中加入如下配置项。


192.168.175.101 jekins.binghe.com
复制代码


注意:在 Windows 操作系统中,hosts 文件所在的目录如下。


C:\Windows\System32\drivers\etc
复制代码


接下来,就可以在浏览器中通过链接:http://jekins.binghe.com:31888 来访问 Jekins 了。


物理机安装 SVN


这里,以在 Master 节点(binghe101 服务器)上安装 SVN 为例。


1.使用 yum 安装 SVN


在命令行执行如下命令安装 SVN。


yum -y install subversion 
复制代码


2.创建 SVN 库


依次执行如下命令。


#创建/data/svnmkdir -p /data/svn #初始化svnsvnserve -d -r /data/svn#创建代码仓库svnadmin create /data/svn/test
复制代码


3.配置 SVN


mkdir /data/svn/confcp /data/svn/test/conf/* /data/svn/conf/cd /data/svn/conf/[root@binghe101 conf]# ll总用量 20-rw-r--r-- 1 root root 1080 5月  12 02:17 authz-rw-r--r-- 1 root root  885 5月  12 02:17 hooks-env.tmpl-rw-r--r-- 1 root root  309 5月  12 02:17 passwd-rw-r--r-- 1 root root 4375 5月  12 02:17 svnserve.conf
复制代码


  • 配置 authz 文件,


vim authz
复制代码


配置后的内容如下所示。


[aliases]# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average
[groups]# harry_and_sally = harry,sally# harry_sally_and_joe = harry,sally,&joeSuperAdmin = adminbinghe = admin,binghe
# [/foo/bar]# harry = rw# &joe = r# * =
# [repository:/baz/fuz]# @harry_and_sally = rw# * = r
[test:/]@SuperAdmin=rw@binghe=rw
复制代码


  • 配置 passwd 文件


vim passwd
复制代码


配置后的内容如下所示。


[users]# harry = harryssecret# sally = sallyssecretadmin = admin123binghe = binghe123
复制代码


  • 配置 svnserve.conf


vim svnserve.conf
复制代码


配置后的文件如下所示。


### This file controls the configuration of the svnserve daemon, if you### use it to allow access to this repository.  (If you only allow### access through http: and/or file: URLs, then this file is### irrelevant.)
### Visit http://subversion.apache.org/ for more information.
[general]### The anon-access and auth-access options control access to the### repository for unauthenticated (a.k.a. anonymous) users and### authenticated users, respectively.### Valid values are "write", "read", and "none".### Setting the value to "none" prohibits both reading and writing;### "read" allows read-only access, and "write" allows complete ### read/write access to the repository.### The sample settings below are the defaults and specify that anonymous### users have read-only access to the repository, while authenticated### users have read and write access to the repository.anon-access = noneauth-access = write### The password-db option controls the location of the password### database file. Unless you specify a path starting with a /,### the file's location is relative to the directory containing### this configuration file.### If SASL is enabled (see below), this file will NOT be used.### Uncomment the line below to use the default password file.password-db = /data/svn/conf/passwd### The authz-db option controls the location of the authorization### rules for path-based access control. Unless you specify a path### starting with a /, the file's location is relative to the### directory containing this file. The specified path may be a### repository relative URL (^/) or an absolute file:// URL to a text### file in a Subversion repository. If you don't specify an authz-db,### no path-based access control is done.### Uncomment the line below to use the default authorization file.authz-db = /data/svn/conf/authz### The groups-db option controls the location of the file with the### group definitions and allows maintaining groups separately from the### authorization rules. The groups-db file is of the same format as the### authz-db file and should contain a single [groups] section with the### group definitions. If the option is enabled, the authz-db file cannot### contain a [groups] section. Unless you specify a path starting with### a /, the file's location is relative to the directory containing this### file. The specified path may be a repository relative URL (^/) or an### absolute file:// URL to a text file in a Subversion repository.### This option is not being used by default.# groups-db = groups### This option specifies the authentication realm of the repository.### If two repositories have the same authentication realm, they should### have the same password database, and vice versa. The default realm### is repository's uuid.realm = svn### The force-username-case option causes svnserve to case-normalize### usernames before comparing them against the authorization rules in the### authz-db file configured above. Valid values are "upper" (to upper-### case the usernames), "lower" (to lowercase the usernames), and### "none" (to compare usernames as-is without case conversion, which### is the default behavior).# force-username-case = none### The hooks-env options specifies a path to the hook script environment ### configuration file. This option overrides the per-repository default### and can be used to configure the hook script environment for multiple ### repositories in a single file, if an absolute path is specified.### Unless you specify an absolute path, the file's location is relative### to the directory containing this file.# hooks-env = hooks-env
[sasl]### This option specifies whether you want to use the Cyrus SASL### library for authentication. Default is false.### Enabling this option requires svnserve to have been built with Cyrus### SASL support; to check, run 'svnserve --version' and look for a line### reading 'Cyrus SASL authentication is available.'# use-sasl = true### These options specify the desired strength of the security layer### that you want SASL to provide. 0 means no encryption, 1 means### integrity-checking only, values larger than 1 are correlated### to the effective key length for encryption (e.g. 128 means 128-bit### encryption). The values below are the defaults.# min-encryption = 0# max-encryption = 256
复制代码


接下来,将/data/svn/conf 目录下的 svnserve.conf 文件复制到/data/svn/test/conf/目录下。如下所示。


[root@binghe101 conf]# cp /data/svn/conf/svnserve.conf /data/svn/test/conf/cp:是否覆盖'/data/svn/test/conf/svnserve.conf'? y
复制代码


4.启动 SVN 服务


(1)创建 svnserve.service 服务


创建 svnserve.service 文件


vim /usr/lib/systemd/system/svnserve.service
复制代码


文件的内容如下所示。


[Unit]Description=Subversion protocol daemonAfter=syslog.target network.targetDocumentation=man:svnserve(8)
[Service]Type=forkingEnvironmentFile=/etc/sysconfig/svnserve#ExecStart=/usr/bin/svnserve --daemon --pid-file=/run/svnserve/svnserve.pid $OPTIONSExecStart=/usr/bin/svnserve --daemon $OPTIONSPrivateTmp=yes
[Install]WantedBy=multi-user.target
复制代码


接下来执行如下命令使配置生效。


systemctl daemon-reload
复制代码


命令执行成功后,修改 /etc/sysconfig/svnserve 文件。


vim /etc/sysconfig/svnserve 
复制代码


修改后的文件内容如下所示。


# OPTIONS is used to pass command-line arguments to svnserve.# # Specify the repository location in -r parameter:OPTIONS="-r /data/svn"
复制代码


(2)启动 SVN


首先查看 SVN 状态,如下所示。


[root@itence10 conf]# systemctl status svnserve.service● svnserve.service - Subversion protocol daemon   Loaded: loaded (/usr/lib/systemd/system/svnserve.service; disabled; vendor preset: disabled)   Active: inactive (dead)     Docs: man:svnserve(8)
复制代码


可以看到,此时 SVN 并没有启动,接下来,需要启动 SVN。


systemctl start svnserve.service
复制代码


设置 SVN 服务开机自启动。


systemctl enable svnserve.service
复制代码


接下来,就可以下载安装 TortoiseSVN,输入链接 svn://192.168.0.10/test 并输入用户名 binghe,密码 binghe123 来连接 SVN 了。


物理机安装 Jenkins


注意:安装 Jenkins 之前需要安装 JDK 和 Maven,我这里同样将 Jenkins 安装在 Master 节点(binghe101 服务器)。


1.启用 Jenkins 库


运行以下命令以下载 repo 文件并导入 GPG 密钥:


wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.reporpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
复制代码


2.安装 Jenkins


执行如下命令安装 Jenkis。


yum install jenkins
复制代码


接下来,修改 Jenkins 默认端口,如下所示。


vim /etc/sysconfig/jenkins
复制代码


修改后的两项配置如下所示。


JENKINS_JAVA_CMD="/usr/local/jdk1.8.0_212/bin/java"JENKINS_PORT="18080"
复制代码


此时,已经将 Jenkins 的端口由 8080 修改为 18080


3.启动 Jenkins


在命令行输入如下命令启动 Jenkins。


systemctl start jenkins
复制代码


配置 Jenkins 开机自启动。


systemctl enable jenkins
复制代码


查看 Jenkins 的运行状态。


[root@itence10 ~]# systemctl status jenkins● jenkins.service - LSB: Jenkins Automation Server   Loaded: loaded (/etc/rc.d/init.d/jenkins; generated)   Active: active (running) since Tue 2020-05-12 04:33:40 EDT; 28s ago     Docs: man:systemd-sysv-generator(8)    Tasks: 71 (limit: 26213)   Memory: 550.8M
复制代码


说明,Jenkins 启动成功。


配置 Jenkins 运行环境


1.登录 Jenkins


首次安装后,需要配置 Jenkins 的运行环境。首先,在浏览器地址栏访问链接http://192.168.0.10:18080,打开 Jenkins 界面。



根据提示使用如下命令到服务器上找密码值,如下所示。


[root@binghe101 ~]# cat /var/lib/jenkins/secrets/initialAdminPassword71af861c2ab948a1b6efc9f7dde90776
复制代码


将密码 71af861c2ab948a1b6efc9f7dde90776 复制到文本框,点击继续。会跳转到自定义 Jenkins 页面,如下所示。



这里,可以直接选择“安装推荐的插件”。之后会跳转到一个安装插件的页面,如下所示。



此步骤可能有下载失败的情况,可直接忽略。


2.安装插件


需要安装的插件


  • Kubernetes Cli Plugin:该插件可直接在 Jenkins 中使用 kubernetes 命令行进行操作。


  • Kubernetes plugin: 使用 kubernetes 则需要安装该插件


  • Kubernetes Continuous Deploy Plugin:kubernetes 部署插件,可根据需要使用


还有更多的插件可供选择,可点击 系统管理->管理插件进行管理和添加,安装相应的 Docker 插件、SSH 插件、Maven 插件。其他的插件可以根据需要进行安装。如下图所示。




3.配置 Jenkins


(1)配置 JDK 和 Maven


在 Global Tool Configuration 中配置 JDK 和 Maven,如下所示,打开 Global Tool Configuration 界面。



接下来就开始配置 JDK 和 Maven 了。


由于我在服务器上将 Maven 安装在/usr/local/maven-3.6.3 目录下,所以,需要在“Maven 配置”中进行配置,如下图所示。


接下来,配置 JDK,如下所示。



注意:不要勾选“Install automatically”


接下来,配置 Maven,如下所示。



注意:不要勾选“Install automatically”


(2)配置 SSH


进入 Jenkins 的 Configure System 界面配置 SSH,如下所示。



找到 SSH remote hosts 进行配置。




配置完成后,点击 Check connection 按钮,会显示 Successfull connection。如下所示。



至此,Jenkins 的基本配置就完成了。


Jenkins 发布 Docker 项目到 K8s 集群


1.调整 SpringBoot 项目的配置


实现,SpringBoot 项目中启动类所在的模块的 pom.xml 需要引入打包成 Docker 镜像的配置,如下所示。


  <properties>  	 	<docker.repostory>192.168.0.10:1180</docker.repostory>        <docker.registry.name>test</docker.registry.name>        <docker.image.tag>1.0.0</docker.image.tag>        <docker.maven.plugin.version>1.4.10</docker.maven.plugin.version>  </properties>
<build> <finalName>test-starter</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- docker的maven插件,官网:https://github.com/spotify/docker‐maven‐plugin --> <!-- Dockerfile maven plugin --> <plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>${docker.maven.plugin.version}</version> <executions> <execution> <id>default</id> <goals> <!--如果package时不想用docker打包,就注释掉这个goal--> <goal>build</goal> <goal>push</goal> </goals> </execution> </executions> <configuration> <contextDirectory>${project.basedir}</contextDirectory> <!-- harbor 仓库用户名及密码--> <useMavenSettingsForAuth>useMavenSettingsForAuth>true</useMavenSettingsForAuth> <repository>${docker.repostory}/${docker.registry.name}/${project.artifactId}</repository> <tag>${docker.image.tag}</tag> <buildArgs> <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> </plugin>
</plugins> <resources> <!-- 指定 src/main/resources下所有文件及文件夹为资源文件 --> <resource> <directory>src/main/resources</directory> <targetPath>${project.build.directory}/classes</targetPath> <includes> <include>**/*</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
复制代码


接下来,在 SpringBoot 启动类所在模块的根目录创建 Dockerfile,内容示例如下所示。


#添加依赖环境,前提是将Java8的Docker镜像从官方镜像仓库pull下来,然后上传到自己的Harbor私有仓库中FROM 192.168.0.10:1180/library/java:8#指定镜像制作作者MAINTAINER binghe#运行目录VOLUME /tmp#将本地的文件拷贝到容器ADD target/*jar app.jar#启动容器后自动执行的命令ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar" ]
复制代码


根据实际情况,自行修改。


注意:FROM 192.168.0.10:1180/library/java:8 的前提是执行如下命令。


docker pull java:8docker tag java:8 192.168.0.10:1180/library/java:8docker login 192.168.0.10:1180docker push 192.168.0.10:1180/library/java:8
复制代码


在 SpringBoot 启动类所在模块的根目录创建 yaml 文件,录入叫做 test.yaml 文件,内容如下所示。


apiVersion: apps/v1kind: Deploymentmetadata:  name: test-starter  labels:    app: test-starterspec:  replicas: 1  selector:    matchLabels:      app: test-starter  template:    metadata:      labels:        app: test-starter    spec:      containers:      - name: test-starter        image: 192.168.0.10:1180/test/test-starter:1.0.0        ports:        - containerPort: 8088      nodeSelector:        clustertype: node12
---apiVersion: v1kind: Servicemetadata: name: test-starter labels: app: test-starterspec: ports: - name: http port: 8088 nodePort: 30001 type: NodePort selector: app: test-starter
复制代码


2.Jenkins 配置发布项目


将项目上传到 SVN 代码库,例如地址为 svn://192.168.0.10/test


接下来,在 Jenkins 中配置自动发布。步骤如下所示。


点击新建 Item。


在描述文本框中输入描述信息。


接下来,配置 SVN 信息。


注意:配置 GitLab 的步骤与 SVN 相同,不再赘述。


定位到 Jenkins 的“构建模块”,使用 Execute Shell 来构建发布项目到 K8S 集群。


执行的命令依次如下所示。


#删除本地原有的镜像,不会影响Harbor仓库中的镜像docker rmi 192.168.0.10:1180/test/test-starter:1.0.0#使用Maven编译、构建Docker镜像,执行完成后本地Docker容器中会重新构建镜像文件/usr/local/maven-3.6.3/bin/mvn -f ./pom.xml clean install -Dmaven.test.skip=true#登录 Harbor仓库docker login 192.168.0.10:1180 -u binghe -p Binghe123#上传镜像到Harbor仓库docker push 192.168.0.10:1180/test/test-starter:1.0.0#停止并删除K8S集群中运行的/usr/bin/kubectl delete -f test.yaml#将Docker镜像重新发布到K8S集群/usr/bin/kubectl apply -f test.yaml
复制代码


好了,今天就到这儿吧,我是冰河,我们下期见~~


冰河原创 PDF


关注 冰河技术 微信公众号:


回复 “并发编程” 领取《深入理解高并发编程(第 1 版)》PDF 文档。


回复 “并发源码” 领取《并发编程核心知识(源码分析篇 第 1 版)》PDF 文档。


回复 ”限流“ 领取《亿级流量下的分布式解决方案》PDF 文档。


回复 “设计模式” 领取《深入浅出 Java23 种设计模式》PDF 文档。


回复 “Java8 新特性” 领取 《Java8 新特性教程》PDF 文档。


回复 “分布式存储” 领取《跟冰河学习分布式存储技术》 PDF 文档。


回复 “Nginx” 领取《跟冰河学习 Nginx 技术》PDF 文档。


回复 “互联网工程” 领取《跟冰河学习互联网工程技术》PDF 文档。


重磅福利


微信搜一搜【冰河技术】微信公众号,关注这个有深度的程序员,每天阅读超硬核技术干货,公众号内回复【PDF】有我准备的一线大厂面试资料和我原创的超硬核 PDF 技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作,学习是一条时而郁郁寡欢,时而开怀大笑的路,加油。如果你通过努力成功进入到了心仪的公司,一定不要懈怠放松,职场成长和新技术学习一样,不进则退。如果有幸我们江湖再见!


另外,我开源的各个 PDF,后续我都会持续更新和维护,感谢大家长期以来对冰河的支持!!


写在最后


如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫!



发布于: 2020 年 12 月 01 日阅读数: 207
用户头像

冰河

关注

公众号:冰河技术 2020.05.29 加入

Mykit系列开源框架发起者、核心架构师和开发者,《海量数据处理与大数据技术实战》与《MySQL开发、优化与运维实战》作者。【冰河技术】微信公众号作者。

评论

发布
暂无评论
三万字无坑搭建基于Docker+K8S+GitLab/SVN+Jenkins+Harbor持续集成交付环境!!