锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

kubernetes的DevOps业务(一):Jenkins,GitLab,Harbor,Tekton,GitOps

时间:2023-06-10 17:07:00 集成电路lm2575hvn55v2a3b变送器2u1s9电力变送器q24j6a连接器

文章目录

  • CI/CD
  • JenKins
    • 安装
    • 插入问题解决方案(直接跳过没有遇到的问题)
    • 架构
    • 配置
    • 测试
  • Gitlab
    • 一种奇怪的现象
  • Git
    • 部署配置git
    • git使用本地仓库
    • 比较各区域文件内容的差异
      • git diff比较工作区和暂存区
      • git diff --cached可比较临存区与本地仓库的文件差异
      • git commit流程与使用git log
      • git回归版本控制系统
        • 工作区回退(git checkout)
        • 暂存区返回当地文件修改(git reset HEAD)
        • 上传到当地仓库退货(git reset --hard)
      • git分支和操作
        • 创建删除分支
        • 切换分支
        • 在分支上创建新功能,并提交给当地仓库
        • 合并分支代码
        • 分支合并时出现冲突
        • 附加些概念
      • Git版本控制系统标签操作
      • Git远程仓库-码云gitee
        • 远程仓库实操
        • 仓库相关命令
  • Harbor
    • Harbor 认证原理
    • 安装
    • 推送镜像

先理解CI/CD

CI/CD

CI/CD理念
持续集成、交付、部署
CI:continuous intergration 持续集成
CD/CDE:continuous delivery 持续交付
CD:continuoues deployment 持续部署

JenKins

jenkins它是一个开源的连续集成,提供友好的操作界面ci起源于工具hudson,主要用于连续、自动地构建测试软件项目,监控外部任务的运行,jenkins用java语言编写,可在tomcat等流行的servlet容器中运行,也可独立运行,通常与版本管理工具(SCM)、结合使用施工工具,常用的版本控制工具有SVN、GIT,构建工具有maven、ant、gradle

提到基于 Kubernete 的 CI/CD,有很多工具可以使用,比如 Jenkins、Gitlab CI 以及新兴的 drone 等等,我们将在这里使用最熟悉的 Jenkins 来做 CI/CD 的工具。

安装

既然要基于 Kubernetes 来做 CI/CD,我们这里最好还是将军 Jenkins 安装到 Kubernetes 在集群中,安装多安装方法,我们仍然使用手动方法,以便了解更多细节,相应的资源清单文件如下:

apiVersion: v1 kind: PersistentVolume metadata:   name: jenkins-pv spec:   storageClassName: local # Local PV   capacity:     storage: 2Gi   volumeMode: Filesystem   accessModes:     - ReadWriteOnce   local:     path: /var/lib/k8s/jenkins   nodeAffinity:     required:       nodeSelectorTerms:         - matchExpressions:             - key: kubernetes.io/hostname               operator: In               values:                 - node1 --- 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: [""]/span> 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: apps/v1 kind: Deployment metadata: name: jenkins namespace: kube-ops spec: selector: matchLabels: app: jenkins template: metadata: labels: app: jenkins spec: serviceAccount: jenkins 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: jenkins image: jenkins/jenkins:lts imagePullPolicy: IfNotPresent env: - name: JAVA_OPTS value: -Dhudson.model.DownloadService.noSignatureCheck=true ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 name: agent protocol: TCP resources: limits: cpu: 1500m memory: 2048Mi requests: cpu: 1500m memory: 2048Mi 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 --- apiVersion: v1 kind: Service metadata: name: jenkins namespace: kube-ops labels: app: jenkins 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 --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: jenkins namespace: kube-ops spec: entryPoints: - web routes: - kind: Rule match: Host(`jenkins.k8s.local`) services: - name: jenkins port: 8080 

我们这里使用 jenkins/jenkins:lts 镜像,这是 jenkins 官方的 Docker 镜像,然后也有一些环境变量,当然我们也可以根据自己的需求来定制一个镜像,比如我们可以将一些插件打包在自定义的镜像当中,可以参考文档:https://github.com/jenkinsci/docker,我们这里使用默认的官方镜像就行,另外一个还需要注意的数据的持久化,将容器的 /var/jenkins_home 目录持久化即可,同样为了性能考虑,我们这里使用 Local PV,将 Pod 调度到固定的节点上。

由于我们这里使用的镜像内部运行的用户 uid=1000,所以我们这里挂载出来后会出现权限问题,为解决这个问题,我们同样还是用一个简单的 initContainer 来修改下我们挂载的数据目录。

另外由于 jenkens 会对 update-center.json 做签名校验安全检查,这里我们需要先提前关闭,否则下面更改插件源可能会失败,通过配置环境变量 JAVA_OPTS=-Dhudson.model.DownloadService.noSignatureCheck=true 即可。

另外我们这里还需要使用到一个拥有相关权限的 serviceAccount:jenkins,我们这里只是给 jenkins 赋予了一些必要的权限,当然如果你对 serviceAccount 的权限不是很熟悉的话,我们给这个 sa 绑定一个 cluster-admin 的集群角色权限也是可以的,当然这样具有一定的安全风险。最后就是通过 IngressRoute 来暴露我们的服务,这个比较简单。

kubectl create ns kube-ops
在node节点,准备/var/lib/k8s/jenkins
直接apply即可


[root@master1 ~]# kubectl get pod -n kube-ops
NAME                      READY   STATUS    RESTARTS   AGE
jenkins-d59b57f9d-rxlgk   1/1     Running   0          16m
[root@master1 ~]# kubectl logs jenkins-d59b57f9d-rxlgk -n kube-ops

在这里插入图片描述

看到上面的 run: Jenkins is fully up and running 信息就证明我们的 Jenkins 应用以前启动起来了。
然后我们可以通过 IngressRoute 中定义的域名 jenkins.k8s.local(需要做 DNS 解析或者在本地 /etc/hosts 中添加映射)来访问 jenkins 服务:
(比如我这里是master_ip jenkins.k8s.local)

然后可以执行下面的命令获取解的管理员密码:

[root@master1 ~]# kubectl exec -it jenkins-d59b57f9d-rxlgk -n kube-ops -- cat /var/jenkins_home/secrets/initialAdminPassword
7936fd85b0c1464281444d6c9b3de790
这个是持久化目录下的,所以就算是pod重启了,只要使用这个目录,密码就一样不变
要想彻底换个jenkins,就要删除这个目录的内容或者换个目录挂载

如果显示该实例离线,解决方法:
找到jenkins对应的节点上的持久化目录,比如我这里是node1,/var/lib/k8s/jenkins
将update.jenkins.io这个url换成这个

当然还有其他选择:

http://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
http://updates.jenkins-ci.org/update-center.json
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
http://mirror.esuni.jp/jenkins/updates/update-center.json
http://mirror.xmission.com/jenkins/updates/update-center.json

http和https都行

然后重启该pod或者直接jenkins.k8s.local/restart也行的,该开始装建议删除该pod进行重启好点

然后跳过插件安装,选择默认安装插件过程会非常慢(也可以选择安装推荐的插件),点击右上角关闭选择插件,等配置好插件中心国内镜像源后再选择安装一些插件。

check now

重启下,可以看到是中文的了
在首页


在新的页面最下面配置升级站点 URL 地址为 https://updates.jenkins-zh.cn/update-center.json(可能因为版本的问题会出现错误,可以尝试使用地址:https://cdn.jsdelivr.net/gh/jenkins-zh/update-center-mirror/tsinghua/dynamic-stable-2.277.1/update-center.json 进行测试),然后点击提交,最后点击立即获取:(这步可选的,看你想用哪个)
建议还是使用清华源的

比如我们可以搜索安装 Pipeline 插件,配置完成后正常下载插件就应该更快了

拉到页面下边,点击下载安装重启,check now

重启下jenkins即可

插入一个问题解决(没遇到这个问题的直接跳过)

有个网友跟我私信说,他遇到了个问题,在走完上面的插件安装步骤之后,点击系统管理,会弹出报错信息,大致意思就是插件版本和jenkins版本不匹配,说要求jenkins要多少杜少版本以上才行,解决方法呀么换插件版本要么你就是升级jenkins,这里直接升级jenkins会方便很多,其实这个问题我在之前版本的jenkins使用中也遇到过。

有可能因为jenkins版本与插件版本不对应导致插件启动失败,一般是因为jenkins版本低的问题,根据jenkins的web页面提示下载war包下来

浏览器下载的war包一般在浏览器所处的节点的这个目录下


这里的devops2就是我的node2节点

可以看到版本包是使用jenkins这个pod的/usr/share/jenkins/jenkins.war,将下载的war包替换掉它即可

我这边的jenkins是运行在node1节点,准备在node1节点那个目录来对pod的/usr/share/jekins.war做持久化,将新下载的war包放进去,或者以后想升级直接拿新的war来替换即可

修改下jenkins.yaml

现在前面加上这两个pv和pvc:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: jenkins-pv-war
spec:
  storageClassName: local-war # Local PV
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  local:
    path: /var/lib/war
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - node1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pvc-war
  namespace: kube-ops
spec:
  storageClassName: local-war
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
---

修改目录属主和数组,不能是root,要用容器用的用户和用户组,加个初始化容器
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
#下面这块就是加的
      initContainers:
        - name: fix-permissions2
          image: busybox
          command: ["sh", "-c", "chown -R 1000:1000 /usr/share/jenkins"]
          securityContext:
            privileged: true
          volumeMounts:
            - name: jenkinswar
              mountPath: /usr/share/jenkins


最后持久化该目录:
就是加个jenkinswar的volume和volumemount
          volumeMounts:
            - name: jenkinshome
              mountPath: /var/jenkins_home
            - name: jenkinswar
              mountPath: /usr/share/jenkins
      volumes:
        - name: jenkinshome
          persistentVolumeClaim:
            claimName: jenkins-pvc
        - name: jenkinswar
          persistentVolumeClaim:
            claimName: jenkins-pvc-war

在node1节点
mkdir -p /var/lib/war/ref
(按道理来说应该创建/var/lib/war就够了,但是实操发现不创建ref会有相应的报错,于是也一块创建ref,至于愿意懒得去理解)

然后将下载的新版本的war拷贝到/var/lib/war

直接apply
重启访问jenkins.k8s.local,点系统管理,问题解决

对于获取容器运行时的用户uid等信息,可以通过环境变量获取,也可以直接进入容器看
除了可以改pv属主,还可以在yaml指定容器运行的用户为root(节点创建的目录用root创建,有效组也是root,自然一开始属主和数组都是root),或者修改容器或者定制镜像中使用USER都行,不过这样不够安全。

[root@master1 ~]# kubectl exec -it jenkins-86f6848b45-tq9hx -n kube-ops -- /bin/bash
jenkins@jenkins-86f6848b45-tq9hx:/$ id
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)
这是比较笨但比较方便的方法

Tips:整个内核制管理一套uid和gid,也就比如容器中root其实和linux的root是同一个,只不过用capability将容器中的root权限减少了很多,上面可以估计除jenkins镜像创建是使用USER 1000,当然是以uid为主,就是说1000在容器中对应的名称可能是叫a,1000在linux中可能叫b了,者之间存在映射关系,主要还是看1000

至于官网镜像等详细信息可以在官网或者官方的github上查看

架构

Jenkins 安装完成了,我们要了解下在 Kubernetes 环境下面使用 Jenkins 有什么好处。

我们知道持续构建与发布是我们日常工作中必不可少的一个步骤,目前大多公司都采用 Jenkins 集群来搭建符合需求的 CI/CD 流程,然而传统的 Jenkins Slave 一主多从方式会存在一些痛点,比如:

主 Master 发生单点故障时,整个流程都不可用了
每个 Slave 的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲
资源分配不均衡,有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态
资源有浪费,每台 Slave 可能是物理机或者虚拟机,当 Slave 处于空闲状态时,也不会完全释放掉资源。

正因为上面的这些种种痛点,我们渴望一种更高效更可靠的方式来完成这个 CI/CD 流程,而 Docker 虚拟化容器技术能很好的解决这个痛点,又特别是在 Kubernetes 集群环境下面能够更好来解决上面的问题,下图是基于 Kubernetes 搭建 Jenkins 集群的简单示意图:

从图上可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master 运行在其中一个节点,并且将其配置数据存储到一个 Volume 上去,Slave 运行在各个节点上,并且它不是一直处于运行状态,它会按照需求动态的创建并自动删除。

这种方式的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。

那么我们使用这种方式带来了哪些好处呢?

服务高可用,当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。

动态伸缩,合理使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。

扩展性好,当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个 Kubernetes Node 到集群中,从而实现扩展。 是不是以前我们面临的种种问题在 Kubernetes 集群环境下面是不是都没有了啊?看上去非常完美。

配置

接下来我们就需要来配置 Jenkins,让他能够动态的生成 Slave 的 Pod。

第 1 步. 我们需要安装 kubernetes 插件, 点击 Manage Jenkins -> Manage Plugins -> Available -> Kubernetes 勾选安装即可。

勾选重启生效即可,可以在已安装那里查看

第 2 步. 安装完毕后,进入 http://jenkins.k8s.local/configureClouds/ 页面(Dashboard->系统管理->节点管理->Configure Clouds:



首先配置连接 Kubernetes APIServer 的地址,由于我们的 Jenkins 运行在 Kubernetes 集群中,所以可以使用 Service 的 DNS 形式进行连接 https://kubernetes.default.svc.cluster.local:

注意 namespace,我们这里填 kube-ops,然后点击 Test Connection,如果出现 Connected to Kubernetes… 的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信了。

然后下方的 Jenkins URL 地址:http://jenkins.kube-ops.svc.cluster.local:8080,这里的格式为:服务名.namespace.svc.cluster.local:8080,根据上面创建的 jenkins 的服务名填写,包括下面的 Jenkins 通道,默认是 50000 端口(要注意是 TCP,所以不要填写 http):

第 3 步. 点击最下方的 Pod Templates 按钮用于配置 Jenkins Slave 运行的 Pod 模板,命名空间我们同样是用 kube-ops,Labels 这里也非常重要,对于后面执行 Job 的时候需要用到该值。



然后配置下面的容器模板,我们这里使用的是 cnych/jenkins:jnlp6 这个镜像,这个镜像是在官方的 jnlp 镜像基础上定制的,加入了 docker、kubectl 等一些实用的工具

想看镜像的每一层内容或者看运行容器用的用户身份可以之际点进去看

注意
容器的名称必须是 jnlp,这是默认拉起的容器,另外需要将 运行的命令 和 命令参数 的值都删除掉,否则会失败。(和这个镜像的定制有关的


然后我们这里需要在下面挂载两个主机目录,一个是 /var/run/docker.sock,该文件是用于 Pod 中的容器能够共享宿主机的 Docker,这就是大家说的 docker in docker 的方式,Docker 二进制文件已经打包到上面的镜像中了,另外一个目录下 /root/.kube 目录,我们将这个目录挂载到容器的 /root/.kube 目录下面这是为了让我们能够在 Pod 的容器中能够使用 kubectl 工具来访问我们的 Kubernetes 集群,方便我们后面在 Slave Pod 部署 Kubernetes 应用。



另外如果在配置了后运行 Slave Pod 的时候出现了权限问题,这是因为 Jenkins Slave Pod 中没有配置权限,所以需要配置上 ServiceAccount,在 Slave Pod 配置的地方点击下面的高级,添加上对应的 ServiceAccount 即可:


到这里我们的 Kubernetes 插件就算配置完成了。

测试

Kubernetes 插件的配置工作完成了,接下来我们就来添加一个 Job 任务,看是否能够在 Slave Pod 中执行,任务执行完成后看 Pod 是否会被销毁。

在 Jenkins 首页点击 新建任务,创建一个测试的任务,输入任务名称,然后我们选择 构建一个自由风格的软件项目 类型的任务,注意在下面的 Label Expression 这里要填入 ydzs-jnlp,就是前面我们配置的 Slave Pod 中的 Label,这两个地方必须保持一致:


然后往下拉,在 构建 区域选择 执行 shell:

然后输入我们测试命令

echo "测试 Kubernetes 动态生成 jenkins slave"
echo "==============docker in docker==========="
docker info

echo "=============kubectl============="
kubectl get pods


点击保存

现在我们直接在页面点击左侧的 立即构建 触发构建即可,然后观察 Kubernetes 集群中 Pod 的变化:

我们可以看到在我们点击立刻构建的时候可以看到一个新的 Pod:jenkins-agent-q51pb被创建了,这就是我们的 Jenkins Slave。任务执行完成后我们可以看到任务信息:

到这里证明我们的任务已经构建完成,然后这个时候我们再去集群查看我们的 Pod 列表,发现 kube-ops 这个 namespace 下面已经没有之前的 Slave 这个 Pod 了。

到这里我们就完成了使用 Kubernetes 动态生成 Jenkins Slave 的方法。

Gitlab

Gitlab 官方提供了 Helm 的方式在 Kubernetes 集群中来快速安装,但是在使用的过程中发现 Helm 提供的 Chart 包中有很多其他额外的配置,所以我们这使用自定义的方式来安装,也就是自己来定义一些资源清单文件。

Gitlab 主要涉及到3个应用:Redis、Postgresql、Gitlab 核心程序,实际上我们只要将这3个应用分别启动起来,然后加上对应的配置就可以很方便的安装 Gitlab 了,我们这里选择使用的镜像不是官方的,而是 Gitlab 容器化中使用非常多的一个第三方镜像:sameersbn/gitlab,基本上和官方保持同步更新,地址:http://www.damagehead.com/docker-gitlab/

https://github.com/sameersbn/docker-gitlab/tree/master/kubernetes

如果我们已经有可使用的 Redis 或 Postgresql 服务的话,那么直接配置在 Gitlab 环境变量中即可,如果没有的话就单独部署,我们这里为了展示 gitlab 部署的完整性,还是分开部署。

首先部署需要的 Redis 服务,对应的资源清单文件如下:

# gitlab-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: kube-ops
  labels:
    name: redis
spec:
  selector:
    matchLabels:
      name: redis
  template:
    metadata:
      name: redis
      labels:
        name: redis
    spec:
      containers:
      - name: redis
        image: sameersbn/redis:4.0.9-2
        imagePullPolicy: IfNotPresent
        ports:
        - name: redis
          containerPort: 6379   #redis的常规端口
        volumeMounts:
        - mountPath: /var/lib/redis
          name: data
        livenessProbe:
          exec:
            command:
            - redis-cli
            - ping
          initialDelaySeconds: 30
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command:
            - redis-cli
            - ping
          initialDelaySeconds: 30
          timeoutSeconds: 1
      volumes:
      - name: data
        emptyDir: { 
        }   #redis一般用来存放缓存数据
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: kube-ops
  labels:
    name: redis
spec:
  ports:
    - name: redis
      port: 6379
      targetPort: redis
  selector:
    name: redis

然后是数据库 Postgresql,对应的资源清单文件如下,为了提高数据库的性能,我们这里也没有使用共享存储之类的,而是直接用的 Local PV 将应用固定到一个节点上:

# gitlab-postgresql.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgresql-pv
spec:
  storageClassName: local  # Local PV
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  local:
    path: /var/lib/k8s/gitlab/postgresql/
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node2
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-pvc
  namespace: kube-ops
spec:
  storageClassName: local
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
  namespace: kube-ops
  labels:
    name: postgresql
spec:
  selector:
    matchLabels:
      name: postgresql
  template:
    metadata:
      name: postgresql
      labels:
        name: postgresql
    spec:
      containers:
      - name: postgresql
        image: sameersbn/postgresql:12-20200524
        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,btree_gist
        - name: USERMAP_UID
          value: "999"
        - name: USERMAP_GID
          value: "999"
        ports:
        - name: postgres
          containerPort: 5432
        volumeMounts:
        - mountPath: /var/lib/postgresql
          name: data
        readinessProbe:
          exec:
            command:
            - pg_isready
            - -h
            - localhost
            - -U
            - postgres
          initialDelaySeconds: 30
          timeoutSeconds: 1
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: postgresql-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: postgresql
  namespace: kube-ops
  labels:
    name: postgresql
spec:
  ports:
    - name: postgres
      port: 5432
      targetPort: postgres
  selector:
    name: postgresql

然后就是我们最核心的 Gitlab 的应用,对应的资源清单文件如下:(gitlab.yaml)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: gitlab-pv
spec:
  storageClassName: local  # Local PV
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  local:
    path: /var/lib/k8s/gitlab/data/
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node2
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gitlab-pvc
  namespace: kube-ops
spec:
  storageClassName: local
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitlab
  namespace: kube-ops
  labels:
    name: gitlab
spec:
  selector:
    matchLabels:
      name: gitlab
  template:
    metadata:
      name: gitlab
      labels:
        name: gitlab
    spec:
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /home/git/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /home/git/data
      containers:
      - name: gitlab
        image: sameersbn/gitlab:13.12.1
        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
          value: admin321
        - name: GITLAB_ROOT_EMAIL
          value: 517554016@qq.com
        - name: GITLAB_HOST
          value: git.k8s.local
        - name: GITLAB_PORT
          value: "80"
        - name: GITLAB_SSH_PORT
          value: "22"
        - 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
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 60
          timeoutSeconds: 1
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: gitlab-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: gitlab
  namespace: kube-ops
  labels:
    name: gitlab
spec:
  ports:
    - name: http
      port: 80
      targetPort: http
    - name: ssh
      port: 22
      targetPort: ssh
  selector:
    name: gitlab
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: gitlab
  namespace: kube-ops
spec:
  entryPoints:
  - web
  routes:
  - kind: Rule
    match: Host(`git.k8s.local`)
    services:
    - name: gitlab
      port: 80

同样因为我们这里的 gitlab 镜像内部是一个 git 的用户(id=1000)(git是容器运行时的用户的,1000在我这里是对应的宿主机的devops,所以如果开启了PID等的话,可以用ps看到该容器的进程,但进程的用户是devops2),所以我们这里为了持久化数据通过一个 initContainers 将我们的数据目录权限进行更改:

initContainers:
- name: fix-permissions
  image: busybox
  command: ["sh", "-c", "chown -R 1000:1000 /home/git/data"]
  securityContext:
    privileged: true
  volumeMounts:
  - name: data
    mountPath: /home/git/data

由于 gitlab 启动非常慢,也非常消耗资源,我们同样还是用的 Local PV,为了不让应用重启,我们这里也直接去掉了 livenessProbe,这样可以防止 gitlab 自动重启,要注意的是其中 Redis 和 Postgresql 相关的环境变量配置,另外,我们这里添加了一个 IngressRoute 对象,来为我们的 Gitlab 配置一个域名 git.k8s.local,这样应用部署完成后,我们就可以通过该域名来访问了,然后直接部署即可:

kubectl apply -f gitlab-redis.yaml -f gitlab-postgresql.yaml -f gitlab.yaml
(或者-f某个目录)

创建完成后,查看 Pod 的部署状态:

[root@master1 ~]# kubectl get pod -n kube-ops
NAME                          READY   STATUS    RESTARTS   AGE
gitlab-68d5dd6bf6-lsbbt       1/1     Running   0          36m
jenkins-86f6848b45-tq9hx      1/1     Running   0          31h
postgresql-566846fd86-vbhbd   1/1     Running   0          36m
redis-8cc6f6d9d-xqztk         1/1     Running   0          36m

可以看到都已经部署成功了,然后我们可以通过 Ingress 中定义的域名 git.k8s.local(需要做 DNS 解析或者在本地 /etc/hosts 中添加映射)来访问:

使用用户名 root,和部署的时候指定的超级用户密码 GITLAB_ROOT_PASSWORD=admin321 即可登录进入到首页:

Gitlab 运行后,我们可以注册为新用户并创建一个项目,还可以做很多的其他系统设置,比如设置语言、设置应用风格样式等等。

点击 New project 创建一个新的项目,和 Github 使用上没有多大的差别:


创建完成后,我们可以添加本地用户的一个 SSH-KEY,这样我们就可以通过 SSH 来拉取或者推送代码了。SSH 公钥通常包含在 ~/.ssh/id_rsa.pub 文件中,并以 ssh-rsa 开头。如果没有的话可以使用 ssh-keygen 命令来生成,id_rsa.pub 里面的内容就是我们需要的 SSH 公钥,然后添加到 Gitlab 中。(想要在那个节点使用ssh操作,就在那个节点上生成SSH的密钥对,然后添加公钥到目标,就类似于建立ssh连接的信任关系)

[root@master1 ~]# cd ~/.ssh
[root@master1 .ssh]# ls
known_hosts

#直接回车使用默认的配置即可,有一个令牌可以说一下,如果设置了令牌,那使用私钥前还得用输入令牌,就是双重保险,好比给锁头上了个带锁的壳,先用令牌开了锁的壳,在用密钥开锁
[root@master1 .ssh]# ssh-keygen -t rsa (最好加上-C "xxx@qq.com" 这个是你在gitlab的账号绑定的邮箱)
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:jR0/0cKYPz55IJvtujVLOEi6gL1FyRA8JtULkL3nxY8 root@master1
The key's randomart image is:
+---[RSA 2048]----+
|  .*o.           |
|  o *..    + .   |
|   o.+ o  + + .  |
|    .oo.o+ + o   |
|     o+.So+ *    |
|   o ..oE..B =   |
|  . o o . = O .  |
|     + .   = =   |
|    . .   ooo    |
+----[SHA256]-----+
[root@master1 .ssh]# ls
id_rsa  id_rsa.pub  known_hosts






不像http方式,想用ssh方式克隆项目,还得登录到gitlab的账号
这里有个坑,比如我在a机器上解析了域名,也设置了密钥对,上传公钥到服务器上,但ssh方式git clone或者用ssh连接测试(只是测试不是登录)时老是显示permission denied,解决方法在当前使用的主机~/.ssh创建config,写入

Host git.k8s.local   #或者Hostname也行
Port 30022


#测试
[root@node2 .ssh]# ssh -T git@git.k8s.local
Welcome to GitLab, @root!

[root@node2 .ssh]# git clone git@git.k8s.local:root/gitlab.demo.git
正克隆到 'gitlab.demo'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (3/3), done.
[root@node2 .ssh]# ls
config  gitlab.demo  id_ed25519  id_ed25519.pub  known_hosts  test2
[root@node2 .ssh]# cd gitlab.demo/
[root@node2 gitlab.demo]# ls
good

由于平时使用的 ssh 默认是 22 端口,现在如果用默认的 22 端口去连接,是没办法和 Gitlab 容器中的 22 端口进行映射的,因为我们只是通过 Service 的 22 端口进行了映射,要想通过节点去进行 ssh 链接就需要在节点上一个端口和容器内部的 22 端口进行绑定(访问主机本地的22端口,默认连接到主机的ssh服务),所以这里我们可以通过 NodePort 去映射 Gitlab 容器内部的 22 端口(就是连接到容器的22端口),我们可以将环境变量设置为 GITLAB_SSH_PORT=30022(环境变量,向容器注入信息,这里是注入service的信息给容器,上面没有怎么调用这些环境变量,所以看得出来这里只是简单的注入写信息),将 Gitlab 的 Service 也设置为 NodePort 类型:

就是edit修改port类型即可
apiVersion: v1
kind: Service
metadata:
  name: gitlab
  namespace: kube-ops
  labels:
    name: gitlab
spec:
  ports:
    - name: http
      port: 80
      targetPort: http
    - name: ssh
      port: 22   #clusterPort
      targetPort: ssh   #其实就是22端口,前面的gitlab的Deployment中定义了容器端口22,名称ssh,这样一来targetPort就和containerPort连上了,实际上targetPort和containerPort存在却省关系,就是定义targetPort就够了,会自动链接到容器同样的端口号上,这里用端口名称会比较清晰
      nodePort: 30022
  type: NodePort
  selector:
    name: gitlab

直接apply,看行不行
不仅仅是service,deployment也要,发现ssh不生效建议直接重启

注意上面 ssh 对应的 nodePort 端口设置为 30022(手动指定nodeport的端口),这样就不会随机生成了,重新更新下 Deployment 和 Service,更新完成后,现在我们在项目上面 Clone 的时候使用 ssh 就会带上端口号了:


现在就可以使用 Clone with SSH 的地址了,由于上面我们配置了 SSH 公钥,所以就可以直接访问上面的仓库了:

$ git clone ssh://git@git.k8s.local:30022/root/gitlab-demo.git
Cloning into 'gitlab-demo'...
Warning: the ECDSA host key for '[git.k8s.local]:30022' differs from the key for the IP address '[123.59.188.11]:300 22'
Offending key for IP in /Users/ych/.ssh/known_hosts:195
Matching host key in /Users/ych/.ssh/known_hosts:204
Are you sure you want to continue connecting (yes/no)? yes
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

然后随便在该项目下面添加一些资源:

$ echo "# hello world" >  README.md
$ git add .
$ git commit -m "change README"
[master 1023f85] change README
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git push origin master
Warning: the ECDSA host key for '[git.k8s.local]:30022' differs from the key for the IP address '[123.59.188.11]:30022'
Offending key for IP in /Users/ych/.ssh/known_hosts:195
Matching host key in /Users/ych/.ssh/known_hosts:204
Are you sure you want to continue connecting (yes/no)? yes
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 259 bytes | 259.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://git.k8s.local:30022/root/gitlab-demo.git
   dfc35a2..1023f85  master -> master

然后刷新浏览器,就可以看到刚刚创建的 Git 仓库中多了一个 README.md 的文件:

到这里就表明我们的 GitLab 就成功部署到了 Kubernetes 集群当中了。
(写项目时建议先下编写好在传送到gitlab上,最好不要在gitlab上在线编辑,会很消耗资源)

有时候显示的不是ssh的网址(实际上使用git@ipxxx已经是使用ssh端口了的),但可以直接用ssh -T git@git.k8s.localxxx来检测

注意:gitlab上述使用的地址是git.k8s.local,而不是公网的地址,所以你想访问时,需要在你的dns服务器或者/etc/hosts做好解析,流程就是git.k8s.local->gitlab:30022

附:
git clone ssh配置流程
gitlab使用的ssh好处
解决git clone ssh需要输入密码
git使用https和git的区别

再推荐个kubernetes部署gitlab的资源清单:
https://github.com/lwolf/kubernetes-gitlab/tree/master/gitlab

一个奇怪的现象

使用时可能会有多个gitlab的pod状态变成evicted,注意不是gitlab-slave的pod,只有一个gitlab的pod在运行这是符合我们预期的,出现驱逐一般是因为节点的磁盘或磁盘io等资源紧张才出现的问题,但检查过这些资源都很充足,将这些gitlab的evicte状态的pod删除掉不影响使用

Git

无论是github,还是gitlab,亦或是gitee,都是基于git分布式控制系统

版本控制系统:
将每一次文件的变化,集中在一个系统中加以版本记录,以便后续查阅特定文件版本的历史记录
常见的版本管理方式
每次提交都会写一个版本号

版本控制解决了什么问题:
1.追溯文件历史变更
2.多人团队协同开发
3.代码集中统一管理

分布式版本控制系统git:
相对于集中版本控制系统,分布式版本控制系统会将远程代码仓库完整镜像下来,进行本地离线版本控制,每一次的提交都不依赖远程服务器,待有网络时再与远程仓库进行版本同步。
也就是说git会将仓库保留在本地,每次代码版本提交都是在本地,当需要推送到服务器时等有网络的情况下提交即可

部署配置git

 wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@node1 ~]# systemctl disable firewalld --now
[root@node1 ~]# setenforce 0
[root@node1 ~]# sed -ir "/^SELINUX=/c SELINUX=disabled" /etc/selinux/config
[root@node1 ~]# yum install -y git
[root@node1 ~]# git --version
git version 1.8.3.1


#配置git用户,比如是已经在gitlab上注册的用户,或者这里直接用个新用户也行
[root@node1 ~]# git config --global user.name "cjq"
[root@node1 ~]# git config --global user.email "3472189068@qq.com"
[root@node1 ~]# git config --global color.ui true
[root@node1 ~]# cat ~/.gitconfig
[user]
        name = cjq
        email = 3472189068@qq.com
[color]
        ui = true

git本地仓库使用

git提交目录文件至本地仓库
首先创建git版本库,这个目录里面的所有文件都可以被git管理起来,每个文件的修改、删除、git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以还原

创建一个git版本库,其实就是个目录
[root@node1 ~]# mkdir /data_git
[root@node1 ~]# cd /data_git

将普通目录初始成git目录,初始成功后会多一个.git的文件夹,如果删除,则git目录变回普通目录
[root@node1 data_git]# git init
初始化空的 Git 版本库于 /data_git/.git/
[root@node1 data_git]# ls -a
.  ..  .git
[root@node1 data_git]# touch file{1,2,3}
[root@node1 data_git]# git status
# 位于分支 master
#
# 初始提交
#
# 未跟踪的文件:
# (使用 "git add ..." 以包含要提交的内容)
#
# file1
# file2
# file3
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

工作区区的内容(就是未处理过的文件,未被跟踪的文件):

将其中一个文件上传至暂存区并查看效果
[ 

相关文章