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

  • Post author:
  • Post category:其他


先理解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: [""]
    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 <file>..." 以包含要提交的内容)
#
#       file1
#       file2
#       file3
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

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

在这里插入图片描述

将其中一个文件上传至暂存区并查看效果
[root@node1 data_git]# git add file1
[root@node1 data_git]# git status
# 位于分支 master
#
# 初始提交
#
# 要提交的变更:
#   (使用 "git rm --cached <file>..." 撤出暂存区)
#
#       新文件:    file1
#
# 未跟踪的文件:
#   (使用 "git add <file>..." 以包含要提交的内容)
#
#       file2
#       file3

上传所有文件到暂存区
[root@node1 data_git]# git add .
[root@node1 data_git]# git status
# 位于分支 master
#
# 初始提交
#
# 要提交的变更:
#   (使用 "git rm --cached <file>..." 撤出暂存区)
#
#       新文件:    file1
#       新文件:    file2
#       新文件:    file3
#


[root@node1 data_git]# git rm --cache .
fatal: 未提供 -r 选项不会递归删除 '.'
[root@node1 data_git]# git rm --cache file1
rm 'file1'
[root@node1 data_git]# git status
# 位于分支 master
#
# 初始提交
#
# 要提交的变更:
#   (使用 "git rm --cached <file>..." 撤出暂存区)
#
#       新文件:    file2
#       新文件:    file3
#
# 未跟踪的文件:
#   (使用 "git add <file>..." 以包含要提交的内容)
#
#       file1

[root@node1 data_git]# git rm --cache file{2,3}
rm 'file2'
rm 'file3'
[root@node1 data_git]# git status
# 位于分支 master
#
# 初始提交
#
# 未跟踪的文件:
#   (使用 "git add <file>..." 以包含要提交的内容)
#
#       file1
#       file2
#       file3
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)


暂存区内容:

在这里插入图片描述

[root@node1 data_git]# git add .
[root@node1 data_git]# git status
# 位于分支 master
#
# 初始提交
#
# 要提交的变更:
#   (使用 "git rm --cached <file>..." 撤出暂存区)
#
#       新文件:    file1
#       新文件:    file2
#       新文件:    file3
#

这时候只剩暂存区了,文件全部被跟踪了

.将暂存区文件提交至本地仓库:

-m:信息描述
[root@node1 data_git]# git commit -m "file1,file2,file3"
[master(根提交) 2d15fa4] file1,file2,file3
 3 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file1
 create mode 100644 file2
 create mode 100644 file3

没有任何文件,已经全部上传到本地仓库
[root@node1 data_git]# git status
# 位于分支 master
无文件要提交,干净的工作区
[root@node1 data_git]# ls
file1  file2  file3



对比各个区域文件内容之间的差异



git diff对比工作区和暂存区

#改变下本地文件(工作区)
[root@node1 data_git]# echo "cjq" > file1
[root@node1 data_git]# cat file1
cjq

#工作区和暂存区对比
[root@node1 data_git]# git diff file1
diff --git a/file1 b/file1
index e69de29..8ed477a 100644
--- a/file1   #变动前的版本
+++ b/file1   #变动后的版本
@@ -0,0 +1 @@
+cjq   #+和-,+表示添加的内容

#查看下,可以看到变更了但未暂存的内容,处于工作区中
[root@node1 data_git]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#       修改:      file1
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@node1 data_git]# git add .

#变更了也上传到暂存区的内容
[root@node1 data_git]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#       修改:      file1
#

#这时候工作区和暂存区file1没有区别了
[root@node1 data_git]# git diff file1


[root@node1 data_git]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#       修改:      file1
#
[root@node1 data_git]# git reset file1
重置后撤出暂存区的变更:
M       file1
[root@node1 data_git]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#       修改:      file1
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@node1 data_git]# git checkout file1
[root@node1 data_git]# git status
# 位于分支 master
无文件要提交,干净的工作区

#此时的文件没有内容
[root@node1 data_git]# cat file1



git diff –cached可以比对暂存区和本地仓库的文件差异

[root@node1 data_git]# git status
# 位于分支 master
无文件要提交,干净的工作区
[root@node1 data_git]# echo "cjq" > file1
[root@node1 data_git]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#       修改:      file1
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@node1 data_git]# git add .
[root@node1 data_git]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#       修改:      file1
#


[root@node1 data_git]# git diff --cached file1
diff --git a/file1 b/file1
index e69de29..8ed477a 100644
--- a/file1   #改动前的版本
+++ b/file1   #改动后的版本
@@ -0,0 +1 @@
+cjq

将暂存区内容提交至本地仓库再对比
[root@node1 data_git]# git commit -m "file1--cjq"
[master 911a111] file1--cjq
 1 file changed, 1 insertion(+)
[root@node1 data_git]# git diff --cached file1
[root@node1 data_git]# git status
# 位于分支 master
无文件要提交,干净的工作区
[root@node1 data_git]# cat file1
cjq



git commit流程与使用git log

我们可以将git commit操作与虚拟机的快照对比,简单来说就是每次commit都相当于对文件做了次快照,或者说是标记。

我们知道commit相对于文件快照,那我们如何得知该文件快照修改了多少次,可以通过git log命令进行查看

#详细的输出
[root@node1 data_git]# git log
commit 911a111bc770f84baeda4a81cdc3cf609a6af828
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:04:11 2022 +0800

    file1--cjq

commit 2d15fa40fb369b221c04703f15f1653ebaf4abfa
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 19:38:13 2022 +0800

    file1,file2,file3


只查看哈希值和版本名称
[root@node1 data_git]# git log --oneline
911a111 file1--cjq
2d15fa4 file1,file2,file3


-n:显示最近的几次记录,在git log排序中,越新的就越在上面
[root@node1 data_git]# git log -1
commit 911a111bc770f84baeda4a81cdc3cf609a6af828
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:04:11 2022 +0800

    file1--cjq
[root@node1 data_git]# git log -2
commit 911a111bc770f84baeda4a81cdc3cf609a6af828
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:04:11 2022 +0800

    file1--cjq

commit 2d15fa40fb369b221c04703f15f1653ebaf4abfa
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 19:38:13 2022 +0800

    file1,file2,file3



git版本控制系统回退

在这里插入图片描述



工作区回退(git checkout)

在file1文件中随便写点内容
[root@node1 data_git]# echo "aaa" > file1
[root@node1 data_git]# cat file1
aaa

[root@node1 data_git]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#       修改:      file1
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@node1 data_git]# echo "aaa" > file2
[root@node1 data_git]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#       修改:      file1
#       修改:      file2
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@node1 data_git]#

回退
[root@node1 data_git]# git checkout .
[root@node1 data_git]# git status
# 位于分支 master
无文件要提交,干净的工作区
[root@node1 data_git]# cat file1
cjq
[root@node1 data_git]# cat file2



暂存区退回到本地文件修改(git reset HEAD)

很多时候修改完代码并提交至暂存区后才发现代码写的有问题,这时需要回退,回退流程:先将暂存区的内容回退到工作区,在从工作区退回到本地文件

git reset HEAD:将所有此次暂存到暂存区的修改过的内容回退到工作区

git reset HEAD file:针对某个文件,也可以多个

[root@node1 data_git]# echo "aaa" > file1
[root@node1 data_git]# cat file1
aaa
[root@node1 data_git]# git add .
[root@node1 data_git]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#       修改:      file1

撤回暂存区,回到工作区
[root@node1 data_git]# git reset HEAD file1
重置后撤出暂存区的变更:
M       file1
[root@node1 data_git]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#       修改:      file1
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@node1 data_git]# cat file1
aaa

工作区回退
[root@node1 data_git]# git checkout file1
[root@node1 data_git]# cat file1
cjq



上传至本地仓库回退(git reset –hard)

第一次修改文件
[root@node1 data_git]# echo "aaa" > file1
[root@node1 data_git]# cat file1
aaa
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m "first"
[master a96ed11] first
 1 file changed, 1 insertion(+), 1 deletion(-)
[root@node1 data_git]# cat file1
aaa


第二次修改文件
[root@node1 data_git]# echo "bbb" >> file1
[root@node1 data_git]# cat file1
aaa
bbb
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m "second"
[master f777940] second
 1 file changed, 1 insertion(+)


[root@node1 data_git]# git log
commit f7779408fc2eaee5ecdb3f5edb2e632695a78b39
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:46:25 2022 +0800

    second

commit a96ed1171d2929f0cfba9dc2d97a73468f41326e
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:44:45 2022 +0800

    first

commit 911a111bc770f84baeda4a81cdc3cf609a6af828
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:04:11 2022 +0800

    file1--cjq

commit 2d15fa40fb369b221c04703f15f1653ebaf4abfa
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 19:38:13 2022 +0800

    file1,file2,file3


回退至file1--cjq版本的file文件
[root@node1 data_git]# git reset --hard 911a111bc770f84baeda4a81cdc3cf609a6af828
HEAD 现在位于 911a111 file1--cjq
[root@node1 data_git]# cat file1
cjq

按时间轴来看,这个版本的"未来的版本"已经不见了
[root@node1 data_git]# git log
commit 911a111bc770f84baeda4a81cdc3cf609a6af828
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:04:11 2022 +0800

    file1--cjq

commit 2d15fa40fb369b221c04703f15f1653ebaf4abfa
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 19:38:13 2022 +0800

    file1,file2,file3


reflog记录所有版本
[root@node1 data_git]# git reflog
911a111 HEAD@{0}: reset: moving to 911a111bc770f84baeda4a81cdc3cf609a6af828
f777940 HEAD@{1}: commit: second
a96ed11 HEAD@{2}: commit: first
911a111 HEAD@{3}: commit: file1--cjq
2d15fa4 HEAD@{4}: commit (initial): file1,file2,file3

回退到second
[root@node1 data_git]# git reset --hard f777940
HEAD 现在位于 f777940 second
[root@node1 data_git]# git log
commit f7779408fc2eaee5ecdb3f5edb2e632695a78b39
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:46:25 2022 +0800

    second

commit a96ed1171d2929f0cfba9dc2d97a73468f41326e
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:44:45 2022 +0800

    first

commit 911a111bc770f84baeda4a81cdc3cf609a6af828
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 20:04:11 2022 +0800

    file1--cjq

commit 2d15fa40fb369b221c04703f15f1653ebaf4abfa
Author: cjq <3472189068@qq.com>
Date:   Wed May 18 19:38:13 2022 +0800

    file1,file2,file3
[root@node1 data_git]# git reflog
f777940 HEAD@{0}: reset: moving to f777940
911a111 HEAD@{1}: reset: moving to 911a111bc770f84baeda4a81cdc3cf609a6af828
f777940 HEAD@{2}: commit: second
a96ed11 HEAD@{3}: commit: first
911a111 HEAD@{4}: commit: file1--cjq
2d15fa4 HEAD@{5}: commit (initial): file1,file2,file3
git reset:

--mixed:
默认方式,相当于不带任何参数的git reset

--soft:
回退到某个版本,只回退commit的信息,如果要提交到本地仓库,直接commit即可,暂存区和工作区的文件目录等内容不变

--hard:
彻底回退到某个版本,本地的源码文件内容会直接编程上一版本,所有修改的内容丢失



git分支和操作

首先项目建立,会有主干master,

每一个开发人员从主干上拉取代码到本地,形成一个分支

(就是git branch创建个分支,该分支会包含所有的master主干的文件信息),一个开发可以使用无数个分支,首先在自己的分支上修改代码,当代码写的没问题后,要先将master上的代码拉到分支上,与分支进行测试后没问题,在将分支的代码与maste代码进行合并。

在这里插入图片描述



创建删除分支

git branch 分支名

git branch -d 分支名

[root@node1 data_git]# git branch
* master

创建分支
[root@node1 data_git]# git branch bug
[root@node1 data_git]# git branch
  bug
* master   #带*号表示当前在哪一个分支
[root@node1 data_git]# git branch -d bug
已删除分支 bug(曾为 f777940)。



切换分支

[root@node1 data_git]# git branch bug
[root@node1 data_git]# ls
file1  file2  file3
[root@node1 data_git]# git checkout bug   #切换分支也是git checkout,所以注意分支名和文件名不要一样
切换到分支 'bug'
[root@node1 data_git]# git branch
* bug
  master

[root@node1 data_git]# ls
file1  file2  file3
bug分支会记录所有master的所有文件的内容



在分支上创建新功能并提交至本地仓库

[root@node1 data_git]# git branch
* bug
  master
[root@node1 data_git]# cat file1
aaa
bbb
[root@node1 data_git]# echo "bug" > file1
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m "bug分支修改file1"
[bug 662e609] bug分支修改file1
 1 file changed, 1 insertion(+), 2 deletions(-)



分支代码合并

合并分支首先要将master分支拉取到本地,测试没问题后再将matser代码与分支进行合并

合并分支,基于dev分支,将master分支的内容合并至dev分支,在dev上进行测试功能

要时刻保持与master上的代码是一致的


为了准确理解应该说a分支合并b分支,理解成将b的内容拿到a分支中来

合并代码语法:

master合并bug就需要在master分支执行合并命令

bug合并master就需要在bug分支执行命令

在bug上拉取下master的代码并进行合并master:
[root@node1 data_git]# git branch
* bug
  master
[root@node1 data_git]# git merge master
Already up-to-date.   #此输出表示master无最新代码,就是相对上次在master上拉取代码创建bug分支,master分支没变化

在master分支合并bug分支,首先切回到master,在使用merge命令进行合并:
[root@node1 data_git]# git checkout master
切换到分支 'master'
[root@node1 data_git]# git merge bug
更新 f777940..662e609
Fast-forward
 file1 | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)   #一个文件改动,一行增加,一行删除



分支合并时出现冲突

分支合并冲突会出现在两个开发同时修改

同一个代码文件的同一行

(只要存在任意一行同样的修改),提交时会提示合并冲突(任意两个分支合并发生的冲突)

在这里插入图片描述

首先进入bug分支新建一个file10并提交到本地仓库
[root@node1 data_git]# git checkout bug
切换到分支 'bug'
[root@node1 data_git]# vim file10
[root@node1 data_git]# cat file10
cjq
cjq
cjq
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m "file10--cjq*10"
[bug 1123e86] file10--cjq*10
 1 file changed, 3 insertions(+)
 create mode 100644 file10

切换到master也建一个file10并提交
[root@node1 data_git]# git checkout master
切换到分支 'master'
[root@node1 data_git]# vim file10
[root@node1 data_git]# cat file10
aaa
aaa
aaa
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m "file10"
[master 0da4acc] file10
 1 file changed, 3 insertions(+)
 create mode 100644 file10

切换到bug分支在进行合并会报错冲突(测试用,为了影响小,在bug分支合并master分支)
[root@node1 data_git]# git checkout bug
切换到分支 'bug'
[root@node1 data_git]# git merge master
自动合并 file10
冲突(添加/添加):合并冲突于 file10
自动合并失败,修正冲突然后提交修正的结果。

解决冲突,与对应的开发协商好保留哪些代码,如果都保留则把1/5/9行删掉
  1 <<<<<<< HEAD   #当前分支的
  2 cjq
  3 cjq
  4 cjq
  5 =======
  6 aaa
  7 aaa
  8 aaa
  9 >>>>>>> master   #master分支
修改完文件重新提交至本地仓库
[root@node1 data_git]# cat file10
cjq
cjq
cjq
aaa
aaa
aaa
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit "merge解决"
[bug 1d962f4] merge解决
[root@node1 data_git]# git checkout master   #这里如果不切换master,直接在bug再次合并master,会显示Already up-to-date,合并(这里拿合并mster分支举例)和创建分支其实都暗含了从master主干拉取代码,这里的合并,因为和上次合并时拉取master分支代码一样,master分支在这里没有发生更改,即使更改了,修改的也会只是bug分支的代码文件,master分支的文件并没有合并到bug分支的内容,没有价值
切换到分支 'master'
[root@node1 data_git]# cat file10
aaa
aaa
aaa
[root@node1 data_git]# git merge bug
更新 0da4acc..1d962f4
Fast-forward
 file10 | 3 +++
 1 file changed, 3 insertions(+)
[root@node1 data_git]# cat file10
cjq
cjq
cjq
aaa
aaa
aaa

一般会从master主干拉取代码创建分支,分支开发功能,拉取master分支的代码合并进行功能测试,检测完成后,master分支在合并其余的功能分支



附加些概念


github,gitlab,gitee的区别



Gitee、Gitlab、Github如何选择



Git基础-本地仓库和远程仓库



Git版本控制系统标签操作

git有commit,为什么还要引入tag?由于commit id号不容易记住,用tag可以打一个易懂的名称

命令格式:

​ git tag -a “标签名” -m “描述信息” //增加一个标签

​ git tag -a “标签名” commit id -m “描述信息”

​ git tag -d 要删除的标签名 //删除一个标签

​ git show 标签名 //查看属性

给当前版本代码打标签
[root@node1 data_git]# git tag -a "v1.1" -m "重大升级--"

查看当前所有标签
[root@node1 data_git]# git tag
v1.1

查看该标签的版本的代码的详细信息
[root@node1 data_git]# git show v1.1
tag v1.1
Tagger: cjq <3472189068@qq.com>
Date:   Thu May 19 09:40:57 2022 +0800

重大升级--

commit 16c7bb04bb725081aa05bef7d5f0d4b35b52be22
Merge: 8bdb698 f84bda4
Author: cjq <3472189068@qq.com>
Date:   Thu May 19 00:26:23 2022 +0800

     filetest-ok

diff --cc filetest
index c25a932,c9aa2c4..a423c5f
--- a/filetest
+++ b/filetest
@@@ -1,4 -1,3 +1,7 @@@
 +qqq
 +qqq
 +qqq
 +qqq
+ aaa
+ aaa
+ aaa

对某个commit提交打标签
[root@node1 data_git]# git reflog
16c7bb0 HEAD@{0}: merge bug: Fast-forward
f84bda4 HEAD@{1}: checkout: moving from bug to master
16c7bb0 HEAD@{2}: commit (merge): filetest-ok
8bdb698 HEAD@{3}: checkout: moving from master to bug
f84bda4 HEAD@{4}: commit: filetest-master
1d962f4 HEAD@{5}: checkout: moving from bug to master
8bdb698 HEAD@{6}: commit: filetest-bug
1d962f4 HEAD@{7}: checkout: moving from master to bug
1d962f4 HEAD@{8}: merge bug: Fast-forward
0da4acc HEAD@{9}: checkout: moving from bug to master
1d962f4 HEAD@{10}: checkout: moving from master to bug
0da4acc HEAD@{11}: checkout: moving from bug to master
1d962f4 HEAD@{12}: commit (merge): merge解决
1123e86 HEAD@{13}: checkout: moving from master to bug
0da4acc HEAD@{14}: commit: file10
662e609 HEAD@{15}: checkout: moving from bug to master
1123e86 HEAD@{16}: commit: file10--cjq*10
662e609 HEAD@{17}: checkout: moving from master to bug
662e609 HEAD@{18}: merge bug: Fast-forward
f777940 HEAD@{19}: checkout: moving from bug to master
662e609 HEAD@{20}: commit: bug分支修改file1
f777940 HEAD@{21}: checkout: moving from master to bug
f777940 HEAD@{22}: reset: moving to f777940
911a111 HEAD@{23}: reset: moving to 911a111bc770f84baeda4a81cdc3cf609a6af828
f777940 HEAD@{24}: commit: second
a96ed11 HEAD@{25}: commit: first
911a111 HEAD@{26}: commit: file1--cjq
2d15fa4 HEAD@{27}: commit (initial): file1,file2,file3
[root@node1 data_git]# git tag -a "v1.0" 16c7bb0 -m "v1.0"
[root@node1 data_git]# git tag
v1.0
v1.1
[root@node1 data_git]# git v1.0
git:'v1.0' 不是一个 git 命令。参见 'git --help'[root@node1 data_git]# git show v1.0
tag v1.0
Tagger: cjq <3472189068@qq.com>
Date:   Thu May 19 09:46:21 2022 +0800

v1.0

commit 16c7bb04bb725081aa05bef7d5f0d4b35b52be22
Merge: 8bdb698 f84bda4
Author: cjq <3472189068@qq.com>
Date:   Thu May 19 00:26:23 2022 +0800

     filetest-ok

diff --cc filetest
index c25a932,c9aa2c4..a423c5f
--- a/filetest
+++ b/filetest
@@@ -1,4 -1,3 +1,7 @@@
 +qqq
 +qqq
 +qqq
 +qqq
+ aaa
+ aaa
+ aaa

可以看到,tag和log都是git风格,新的在上

在这里插入图片描述



Git远程仓库—码云gitee

每次提交代码都是提交到本地仓库,我们现在将本地仓库的代码推送至远程仓库,供所有人下载

远程仓库:一个远程仓库对应一个本地仓库,不会混乱

在这里插入图片描述

注册并登录你的gitee账户

在这里插入图片描述

在这里插入图片描述

[root@node1 data_git]# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
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:hlH10BNvLrdJiVJay0258h5Q2lFIVTuY+bwp57FM37k root@node1
The key's randomart image is:
+---[RSA 2048]----+
|        ..o..o.o=|
|       .   oo.=o.|
|      .     +=Bo |
|       o   = @o+.|
|      . S o O Oo |
|       .   . B oo|
|             .=* |
|             .*.*|
|              .E+|
+----[SHA256]-----+
[root@node1 data_git]# cd ~/.ssh
[root@node1 .ssh]# ls
id_rsa  id_rsa.pub
[root@node1 .ssh]# cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEsLI9XsnZfPK3TWURmTFxjHDYsjzLpQi/89hisbHMozIPGqCCOgYJYATU7ADpp6YI8vZSGsy9eAXjajtlza6c2VuAIqWNg+0dHCJrUMKO5iPzJD0wGTTUXY11gizYs7fBKQPeOepO/doSpT6EqnVb9lgGN0lsTsbfAPAf2X4bQvp8bcBwwMAQ71nZuiJqF8BddIJeZkQBSdzmw7c/TGvVImtPkrEXPcc3MrbXxhoRQskXfDamQO8cmrlmdHcQQqcMnlkywXCMKfECrMwLZ5SLPQViAjlAwM9MJpjNZNgbRMr2WEa0Amhdz75HCZqWL7jOsxn1IKblaYUFmdCxcdhZ root@node1

在这里插入图片描述

在这里插入图片描述

配置本地仓库连接远程仓库

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

[root@node1 data_git]# cd /data_git/

添加gitee远程仓库
[root@node1 data_git]# git remote add origin git@gitee.com:a-bais/test.git

将本地的master分支推送到远程仓库的master分支,-u表示远程用户,origin是远程仓库的用户名
[root@node1 data_git]# git push -u origin master
The authenticity of host 'gitee.com (180.97.125.228)' can't be established.
ECDSA key fingerprint is SHA256:FQGC9Kn/eye1W8icdBgrQp+KkGYoFgbVr17bmjey0Wc.
ECDSA key fingerprint is MD5:27:e5:d3:f7:2a:9e:eb:6c:93:cd:1f:c1:47:a3:54:b1.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitee.com,180.97.125.228' (ECDSA) to the list of known hosts.
Counting objects: 32, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (22/22), done.
Writing objects: 100% (32/32), 2.50 KiB | 0 bytes/s, done.
Total 32 (delta 4), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.3]
To git@gitee.com:a-bais/test.git
 * [new branch]      master -> master
分支 master 设置为跟踪来自 origin 的远程分支 master。

在这里插入图片描述

将bug分支推送至远程仓库
[root@node1 data_git]# git checkout bug
切换到分支 'bug'
[root@node1 data_git]# git push -u origin bug
Total 0 (delta 0), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.3]
remote: Create a pull request for 'bug' on Gitee by visiting:
remote:     https://gitee.com/a-bais/test/pull/new/a-bais:bug...a-bais:master
To git@gitee.com:a-bais/test.git
 * [new branch]      bug -> bug
分支 bug 设置为跟踪来自 origin 的远程分支 bug。

在这里插入图片描述



远程仓库实操

一般公司新来的开发人员是没有远程仓库的代码,因此我们一般先将仓库所有代码克隆一份到本地,然后让新员工进行改代码,提交—上传到远程仓库


注意第一次获取仓库代码时必须使用克隆,只将master的代码克隆到本地


克隆地址从下图获取

在这里插入图片描述

换个工作目录
cd /opt

克隆下来
[root@node1 opt]# git clone git@gitee.com:a-bais/test.git
正克隆到 'test'...
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 32 (delta 4), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (32/32), done.
处理 delta 中: 100% (4/4), done.
[root@node1 opt]# ls
cni  containerd  rh  test
[root@node1 opt]# cd test
[root@node1 test]# ls
file1  file10  file2  file3  filetest
[root@node1 test]# cat file1
bug
[root@node1 test]# echo "new" >> file1
[root@node1 test]# git add .
[root@node1 test]# git commit -m "new"
[master db3d74e] new
 1 file changed, 1 insertion(+)

推动到远程仓库的origin用户下
[root@node1 test]# git push -u origin master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 259 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.3]
To git@gitee.com:a-bais/test.git
   16c7bb0..db3d74e  master -> master
分支 master 设置为跟踪来自 origin 的远程分支 master。



仓库相关命令

[root@node1 data_git]# git remote
origin

# origin描述某个仓库,仓库名
[root@node1 data_git]# git remote -v
origin  git@gitee.com:a-bais/test.git (fetch)
origin  git@gitee.com:a-bais/test.git (push)

添加远程仓库
git remote add name url
name可以自定义,方便我们区分仓库

推送到某个仓库的某个分支
git push name branch
有的时候git remote添加远程仓库后不能直接使用,就是push会失败
git会提示我们先pull,

git pull name branch
可能拉取也会报错,非关联的仓库不能拉取
使用如下命令,添加--allow-unrelated-histories参数,git pull name branch --allow-unrelated-histories
[root@node1 data_git]# git branch
* bug
  master
[root@node1 data_git]# vim file1
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m "tag"
[bug ebeac46] tag
 1 file changed, 1 insertion(+)
[root@node1 data_git]# git push origin master
To git@gitee.com:a-bais/test.git
 ! [rejected]        master -> master (non-fast-forward)
error: 无法推送一些引用到 'git@gitee.com:a-bais/test.git'
提示:更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。
提示:检出该分支并与远程变更合并(如 'git pull'),然后再推送。详见
提示:'git push --help' 中的 'Note about fast-forwards' 小节。
[root@node1 data_git]# git pull origin bug
来自 gitee.com:a-bais/test
 * branch            bug        -> FETCH_HEAD
Already up-to-date.
[root@node1 data_git]# git branch
* bug
  master
[root@node1 data_git]# cat gile1
cat: gile1: 没有那个文件或目录

#为修改之前,bug这个远程分支的file1没有qqq
[root@node1 data_git]# cat file1
bug
new
qqq

以上不只是分支本地和远程的差异,还有就是bug分支不应该上传到master,要上传到master分支,先切换到master本地分支然后合并bug分支功能,在上传到master远程分支

给某次提交添加标签,这样这次提交上传远程仓库时自然也会有提交的标签的信息

[root@node1 data_git]# git branch
* bug
  master
[root@node1 data_git]# cat file1
bug
new
qqq
[root@node1 data_git]# ecaho "www" >> file1
bash: ecaho: 未找到命令...
[root@node1 data_git]# echo "www" >> file1
[root@node1 data_git]# cat file1
bug
new
qqq
www
[root@node1 data_git]# git add .
[root@node1 data_git]# git commit -m " tagv1.10"
[bug 8e1069b]  tagv1.10
 1 file changed, 1 insertion(+)
[root@node1 data_git]# git reflog
8e1069b HEAD@{0}: commit: tagv1.10
ebeac46 HEAD@{1}: commit: tag
db3d74e HEAD@{2}: pull origin master: Fast-forward
16c7bb0 HEAD@{3}: checkout: moving from master to bug
16c7bb0 HEAD@{4}: merge bug: Fast-forward
f84bda4 HEAD@{5}: checkout: moving from bug to master
16c7bb0 HEAD@{6}: commit (merge): filetest-ok
8bdb698 HEAD@{7}: checkout: moving from master to bug
f84bda4 HEAD@{8}: commit: filetest-master
1d962f4 HEAD@{9}: checkout: moving from bug to master
8bdb698 HEAD@{10}: commit: filetest-bug
1d962f4 HEAD@{11}: checkout: moving from master to bug
1d962f4 HEAD@{12}: merge bug: Fast-forward
0da4acc HEAD@{13}: checkout: moving from bug to master
1d962f4 HEAD@{14}: checkout: moving from master to bug
0da4acc HEAD@{15}: checkout: moving from bug to master
1d962f4 HEAD@{16}: commit (merge): merge解决
1123e86 HEAD@{17}: checkout: moving from master to bug
0da4acc HEAD@{18}: commit: file10
662e609 HEAD@{19}: checkout: moving from bug to master
1123e86 HEAD@{20}: commit: file10--cjq*10
662e609 HEAD@{21}: checkout: moving from master to bug
662e609 HEAD@{22}: merge bug: Fast-forward
f777940 HEAD@{23}: checkout: moving from bug to master
662e609 HEAD@{24}: commit: bug分支修改file1
f777940 HEAD@{25}: checkout: moving from master to bug
f777940 HEAD@{26}: reset: moving to f777940
911a111 HEAD@{27}: reset: moving to 911a111bc770f84baeda4a81cdc3cf609a6af828
f777940 HEAD@{28}: commit: second
a96ed11 HEAD@{29}: commit: first
911a111 HEAD@{30}: commit: file1--cjq
2d15fa4 HEAD@{31}: commit (initial): file1,file2,file3
[root@node1 data_git]# git tag -a "v1.10" 8e1069b -m "tag---now"
[root@node1 data_git]# git push origin bug
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 273 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.3]
To git@gitee.com:a-bais/test.git
   ebeac46..8e1069b  bug -> bug

在这里插入图片描述

建立橡项目的版本标签,基于某个分支创建,便于区分不同版本,甚至可以设置为发行版本:

在这里插入图片描述

在这里插入图片描述



Harbor

Harbor 是一个 CNCF 基金会托管的开源的可信的云原生 docker registry 项目,可以用于存储、签名、扫描镜像内容,Harbor 通过添加一些常用的功能如安全性、身份权限管理等来扩展 docker registry 项目,此外还支持在 registry 之间复制镜像,还提供更加高级的安全功能,如用户管理、访问控制和活动审计等,在新版本中还添加了 Helm 仓库托管的支持。

Harbor 最核心的功能就是给 docker registry 添加上一层权限保护的功能,要实现这个功能,就需要我们在使用 docker login、pull、push 等命令的时候进行拦截,先进行一些权限相关的校验,再进行操作,其实这一系列的操作 docker registry v2 就已经为我们提供了支持,v2 集成了一个安全认证的功能,将安全认证暴露给外部服务,让外部服务去实现。(由外部认证服务器完成)



Harbor 认证原理

上面我们说了 docker registry v2 将安全认证暴露给了外部服务使用,那么是怎样暴露的呢?我们在命令行中输入 docker login https://registry.qikqiak.com 为例来为大家说明下认证流程:

1.docker client 接收到用户输入的 docker login 命令,将命令转化为调用 engine api 的 RegistryLogin 方法

2.在 RegistryLogin 方法中通过 http 调用 registry 服务中的 auth 方法

3.因为我们这里使用的是 v2 版本的服务,所以会调用 loginV2 方法,在 loginV2 方法中会进行 /v2/ 接口调用,该接口会对请求进行认证

4.此时的请求中并没有包含 token 信息,认证会失败,返回 401 错误,同时会在 header 中返回去哪里请求认证的服务器地址

5.registry client 端收到上面的返回结果后,便会去返回的认证服务器那里进行认证请求,向认证服务器发送的请求的 header 中包含有加密的用户名和密码

6.认证服务器从 header 中获取到加密的用户名和密码,这个时候就可以结合实际的认证系统进行认证了,比如从数据库中查询用户认证信息或者对接 ldap 服务进行认证校验

7.认证成功后,会返回一个 token 信息,client 端会拿着返回的 token 再次向 registry 服务发送请求,这次需要带上得到的 token,请求验证成功,返回状态码就是200了

8.docker client 端接收到返回的200状态码,说明操作成功,在控制台上打印 Login Succeeded 的信息 至此,整个登录过程完成,整个过程可以用下面的流程图来说明:

在这里插入图片描述

要完成上面的登录认证过程有两个关键点需要注意:怎样让 registry 服务知道服务认证地址?我们自己提供的认证服务生成的 token 为什么 registry 就能够识别?

对于第一个问题,比较好解决,registry 服务本身就提供了一个

配置文件

,可以在启动 registry 服务(Harbor)的配置文件中指定上认证服务地址即可,其中有如下这样的一段配置信息:

......
auth:
  token:
    realm: token-realm
    service: token-service
    issuer: registry-token-issuer
    rootcertbundle: /root/certs/bundle
......

其中 realm 就可以用来指定一个认证服务的地址(realm领域)

第二个问题,就是 registry 怎么能够识别我们返回的 token 文件?如果按照 registry 的要求生成一个 token,是不是 registry 就可以识别了?所以我们需要在我们的

认证服务器中按照 registry 的要求生成 token

,而不是随便乱生成。那么要怎么生成呢?我们可以在 docker registry 的源码中可以看到 token 是通过

JWT(JSON Web Token)

来实现的,所以我们按照要求生成一个 JWT 的 token 就可以了。



安装

Harbor 涉及的组件比较多,我们可以使用 Helm 来安装一个高可用版本的 Harbor,也符合生产环境的部署方式。在安装高可用的版本之前,我们需要如下先决条件:

Kubernetes 集群 1.10+ 版本

Helm 2.8.0+ 版本

高可用的 Ingress 控制器

高可用的 PostgreSQL 9.6+(Harbor 不进行数据库 HA 的部署)

高可用的 Redis 服务(Harbor 不处理)

可以跨节点或外部对象存储共享的 PVC

Harbor 的大部分组件都是无状态的,所以我们可以简单增加 Pod 的副本,保证组件尽量分布到多个节点上即可,在存储层,需要我们自行提供高可用的 PostgreSQL、Redis 集群来存储应用数据,以及存储镜像和 Helm Chart 的 PVC 或对象存储。

在这里插入图片描述

添加git仓库
[root@master1 ~]# helm repo add harbor https://helm.goharbor.io
更新
[root@master1 ~]# helm repo update
拉取1.6.2版本并解压
[root@master1 ~]# helm pull harbor/harbor --untar --version 1.6.2

在安装 Harbor 的时候有很多可以配置的参数,可以在 harbor-helm 项目上进行查看,在安装的时候我们可以通过 –set 指定参数或者 values.yaml 直接编辑 Values 文件即可:

1.Ingress 配置通过 expose.ingress.hosts.core 和 expose.ingress.hosts.notary

2.外部 URL 通过配置 externalURL

3.外部 PostgreSQL 通过配置 database.type 为 external,然后补充上 database.external 的信息。需要我们手动创建3个空的数据:Harbor core、Notary server 以及 Notary signer,Harbor 会在启动时自动创建表结构

4.外部 Redis 通过配置 redis.type 为 external,并填充 redis.external 部分的信息。Harbor 在 2.1.0 版本中引入了 redis 的 Sentinel 模式,你可以通过配置 sentinel_master_set 来开启,host 地址可以设置为 <host_sentinel1>:<port_sentinel1>,<host_sentinel2>:<port_sentinel2>,<host_sentinel3>:<port_sentinel3>。还可以参考文档https://community.pivotal.io/s/article/How-to-setup-HAProxy-and-Redis-Sentinel-for-automatic-failover-between-Redis-Master-and-Slave-servers 在 Redis 前面配置一个 HAProxy 来暴露单个入口点。

5.存储,默认情况下需要一个默认的 StorageClass 在 K8S 集群中来自动生成 PV,用来存储镜像、Charts 和任务日志。如果你想指定 StorageClass,可以通过 persistence.persistentVolumeClaim.registry.storageClass、persistence.persistentVolumeClaim.chartmuseum.storageClass 以及 persistence.persistentVolumeClaim.jobservice.storageClass 进行配置,另外还需要将 accessMode 设置为 ReadWriteMany,确保 PV 可以跨不同节点进行共享存储。此外我们还可以通过指定存在的 PVCs 来存储数据,可以通过 existingClaim 进行配置。如果你没有可以跨节点共享的 PVC,你可以使用外部存储来存储镜像和 Chart(外部存储支持:azure,gcs,s3 swift 和 oss),并将任务日志存储在数据库中。将设置为 persistence.imageChartStorage.type 为你要使用的值并填充相应部分并设置 jobservice.jobLogger 为 database

6.副本:通过设置 portal.replicas,core.replicas,jobservice.replicas,registry.replicas,chartmuseum.replicas,notary.server.replicas 和 notary.signer.replicas 为 n(n> = 2)

比如这里我们将主域名配置为 harbor.k8s.local,通过NFS 的 StorageClass 来提供存储(生产环境不建议使用 NFS),又因为前面我们在安装 GitLab 的时候就已经单独安装了 postgresql 和 reids 两个数据库,所以我们也可以配置 Harbor 使用这两个外置的数据库,这样可以降低资源的使用(我们可以认为这两个数据库都是 HA 模式)。但是使用

外置的数据库我们需要提前手动创建数据库

,比如我们这里使用的 GitLab 提供的数据库,则进入该 Pod 创建

harbor、notary_server、notary_signer

这3个数据库:

(notary公证人,signer签署人)

[root@master1 ~]# kubectl get pod -n kube-ops -l name=postgresql
NAME                          READY   STATUS    RESTARTS   AGE
postgresql-566846fd86-vbhbd   1/1     Running   0          44h

[root@master1 ~]# kubectl exec -it postgresql-566846fd86-vbhbd /bin/bash -n kube-ops
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@postgresql-566846fd86-vbhbd:/var/lib/postgresql# su - postgres
postgres@postgresql-566846fd86-vbhbd:~$ psql
psql (12.3 (Ubuntu 12.3-1.pgdg18.04+1))
Type "help" for help.

postgres=# CREATE DATABASE harbor OWNER postgres;   #创建数据库,数据库的用户是postgres
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE harbor to postgres;   #给postgres和gitlab授权
GRANT
postgres=# GRANT ALL PRIVILEGES ON DATABASE harbor to postgres;
GRANT
postgres=# CREATE DATABASE notary_server OWNER postgres;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE notary_server to postgres;
GRANT
postgres=# GRANT ALL PRIVILEGES ON DATABASE harbor to gitlab;
GRANT
postgres=# GRANT ALL PRIVILEGES ON DATABASE notary_server to gitlab;
GRANT
postgres=# CREATE DATABASE notary_signer OWNER postgres;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE notary_signer to postgres;
GRANT

数据库准备过后,就可以使用我们自己定制的 values 文件来进行安装了,完整的定制的 values 文件如下所示:

# values-prod.yaml
#指定外部URL
externalURL: https://harbor.k8s.local
#这是harbor管理员登录密码
harborAdminPassword: Harbor12345
#日志的级别
logLevel: debug

expose:
  type: ingress
  tls:
    enabled: true
  ingress:
    hosts:
      core: harbor.k8s.local

#认证服务器的地址
      notary: notary.k8s.local
    annotations:

#使用traefik
      kubernetes.io/ingress.class: traefik
      traefik.ingress.kubernetes.io/router.entrypoints: websecure
      traefik.ingress.kubernetes.io/router.tls: "true"

persistence:
  enabled: true
  resourcePolicy: "keep"
  persistentVolumeClaim:
    registry:
      # 如果需要做高可用,多个副本的组件则需要使用支持 ReadWriteMany 的后端
      # 这里我们使用nfs,生产环境不建议使用nfs
      storageClass: "nfs-storage"
      # 如果是高可用的,多个副本组件需要使用 ReadWriteMany,默认为 ReadWriteOnce,nfs本身的理念就是共享,所以设置ReadWriteMany最好
      accessMode: ReadWriteMany
      size: 5Gi
    chartmuseum:
      storageClass: "nfs-storage"
      accessMode: ReadWriteMany
      size: 5Gi
    jobservice:
      storageClass: "nfs-storage"
      accessMode: ReadWriteMany
      size: 1Gi
    trivy:
      storageClass: "nfs-storage"
      accessMode: ReadWriteMany
      size: 2Gi

database:
  type: external

#外部数据库地址设置
  external:
    host: "postgresql.kube-ops.svc.cluster.local"
    port: "5432"
    username: "gitlab"   #会在数据库中创建这个账号
    password: "passw0rd"
    coreDatabase: "harbor"
    notaryServerDatabase: "notary_server"
    notarySignerDatabase: "notary_signer"

redis:
  type: external
  external:
    addr: "redis.kube-ops.svc.cluster.local:6379"

# 默认为一个副本,如果要做高可用,只需要设置为 replicas >= 2 即可
#入口站点
portal:
  replicas: 1
core:
  replicas: 1
jobservice:
  replicas: 1
registry:
  replicas: 1
chartmuseum:
  replicas: 1
trivy:
  replicas: 1
notary:
  server:
    replicas: 1
  signer:
    replicas: 1

由于我们这里使用的 Ingress 控制器是 traefik2.x 版本,在配置 Ingress 的时候,我们需要重新配置 annotations(如果你使用的是其他 Ingress 控制器,请参考具体的使用方式)

提前准备nfs的pv和storageclass,下面的helm安装应用汇自动生成4个kube-ops下的pvc,

yum install -y rpcbind nfs-utils
systemctl enable rpcbind --now
systemctl enable nfs --now
mkdir -p /data/k8s   #用来做nfs的目录
chmod u+w /data/k8s   #不行就a+w实操发现harbor-harbor-trivy需要使用该持久卷创建目录,有权限不足的情况,这里给w权限可以解决,你也可以改用户等方法解决
vim /etc/exports:
/data/k8s/ *(rw,sync,no_root_squash)

#创建pv和storageclass
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv1
spec:
  storageClassName: "nfs-storage"
  capacity:
    storage: 20Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data/k8s  # 指定nfs的挂载点
    server: 192.168.23.200  # 指定nfs服务地址,这是我node1的主机ip
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage
provisioner: kubernetes.io/no-provisioner


注意由于pv和pvc一一对应,所以这里需要创建四个pv,值需要更改上边的名称的nfs-pv1,分别改2和3和4,每次都创建apply,就可以得到4个pv,这几个pv都是使用nfs-storage这个storageclass,后边创建应用产生pvc会自动绑定。

(pv有namespace,但实际使用,就是与pvc绑定不受namespace约束,就像这里的pv我定义在default下一样能和kube-ops的pvc绑定)

这些配置信息都是根据 Harbor 的 Chart 包默认的 values 值进行覆盖的,现在我们直接安装即可:

[root@master1 ~]# cd harbor/
[root@master1 harbor]# ls
cert  Chart.yaml  conf  LICENSE  README.md  templates  values.yaml
[root@master1 harbor]# vim values-prod.yaml
[root@master1 harbor]# vim values-prod.yaml
[root@master1 harbor]# helm upgrade --install harbor . -f values-prod.yaml -n kube-ops
Release "harbor" does not exist. Installing it now.
NAME: harbor
LAST DEPLOYED: Thu May 19 18:27:20 2022
NAMESPACE: kube-ops
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at https://harbor.k8s.local
For more details, please visit https://github.com/goharbor/harbor
[root@master1 harbor]# helm ls -n kube-ops
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART            APP VERSION
harbor  kube-ops        1               2022-05-19 18:27:20.700375852 +0800 CST deployed        harbor-1.6.2     2.2.2
[root@master1 harbor]# kubectl get pod -n kube-ops -l app=harbor
NAME                                           READY   STATUS    RESTARTS   AGE
harbor-harbor-chartmuseum-5b967fdc48-cs58w     1/1     Running   0          120m
harbor-harbor-core-9fb6f5cfb-2hgx5             1/1     Running   0          120m
harbor-harbor-jobservice-784c6df67f-67ksg      1/1     Running   0          120m
harbor-harbor-notary-server-84446644c7-5dh7n   1/1     Running   0          120m
harbor-harbor-notary-signer-d44bbdd7-q7ssq     1/1     Running   0          120m
harbor-harbor-portal-559c4d4bfd-qq2bb          1/1     Running   0          120m
harbor-harbor-registry-cd8784bdd-plpds         2/2     Running   0          120m
harbor-harbor-trivy-0                          1/1     Running   6          28m

安装完成后,我们就可以将域名 harbor.k8s.local 解析到 Ingress Controller 所在的节点,我们这里使用的仍然是 Traefik,由于我们开启了 KubernetesIngress 支持的,所以我们只需要将域名解析到 Traefik 的 Pod 所在节点即可,然后就可以通过该域名在浏览器中访问了:

[root@master1 harbor]# kubectl get ingress -n kube-ops
NAME                           CLASS    HOSTS              ADDRESS   PORTS     AGE
harbor-harbor-ingress          <none>   harbor.k8s.local             80, 443   125m
harbor-harbor-ingress-notary   <none>   notary.k8s.local             80, 443   125m

用户名使用默认的 admin,密码则是上面配置的默认 Harbor12345,需要注意的是要使用

https

进行访问,否则登录可能提示用户名或密码错误:

在这里插入图片描述

在这里插入图片描述

默认情况下会有一个名叫 library 的项目,该项目默认是公开访问权限的,进入项目可以看到里面还有 Helm Chart 包的管理,可以手动在这里上传,也可以对该项目里面的镜像进行一些其他配置。

但是这里也需要注意的是,由于我们这里使用的 traefik2.x 版本的 Ingress 控制器,所以对于 Ingress 资源的支持不是很友好,由于我们添加了

traefik.ingress.kubernetes.io/router.tls: “true” 这个注解,导致我们的 http 服务又失效了

(traefik开启tls会导致http失效),为了解决这个问题,我们这里手动来创建一个 http 版本的 Ingress 对象:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    meta.helm.sh/release-name: harbor
    meta.helm.sh/release-namespace: kube-ops
    traefik.ingress.kubernetes.io/router.entrypoints: web
    traefik.ingress.kubernetes.io/router.middlewares: kube-system-redirect-https@kubernetescrd
  labels:
    app: harbor
    app.kubernetes.io/managed-by: Helm
    chart: harbor
    heritage: Helm
    release: harbor
  name: harbor-harbor-ingress-http
  namespace: kube-ops
spec:
  rules:
  - host: harbor.k8s.local
    http:
      paths:
      - backend:
          serviceName: harbor-harbor-portal
          servicePort: 80
        path: /
        pathType: Prefix
      - backend:
          serviceName: harbor-harbor-core
          servicePort: 80
        path: /api
        pathType: Prefix
      - backend:
          serviceName: harbor-harbor-core
          servicePort: 80
        path: /service
        pathType: Prefix
      - backend:
          serviceName: harbor-harbor-core
          servicePort: 80
        path: /v2
        pathType: Prefix
      - backend:
          serviceName: harbor-harbor-core
          servicePort: 80
        path: /chartrepo
        pathType: Prefix
      - backend:
          serviceName: harbor-harbor-core
          servicePort: 80
        path: /c
        pathType: Prefix
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: traefik
    meta.helm.sh/release-name: harbor
    meta.helm.sh/release-namespace: kube-ops
    traefik.ingress.kubernetes.io/router.entrypoints: web
    traefik.ingress.kubernetes.io/router.middlewares: kube-system-redirect-https@kubernetescrd
  labels:
    app: harbor
    app.kubernetes.io/managed-by: Helm
    chart: harbor
    heritage: Helm
    release: harbor
  name: harbor-harbor-ingress-notary-http
  namespace: kube-ops
spec:
  rules:
  - host: notary.k8s.local
    http:
      paths:
      - backend:
          serviceName: harbor-harbor-notary-server
          servicePort: 4443
        path: /
        pathType: Prefix

为了让能够跳转到 https,我们还需要创建如下所示的一个 Middleware(如果你使用的是其他 Ingress 控制器,请参考具体的使用方式):

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: redirect-https
  namespace: kube-system
spec:
  redirectScheme:
    scheme: https

需要注意的是在 Ingress 的 annotations 中配置中间件的格式为 -redirect-https@kubernetescrd。

这时候用http方式访问也成功,会强制跳转到https



推送镜像

现在我们来测试下使用 docker cli 来进行 pull/push 镜像,直接使用 docker login 命令登录:

[root@master1 ~]# docker login harbor.k8s.local
Username: admin
Password:
Error response from daemon: Get "https://harbor.k8s.local/v2/": x509: certificate signed by unknown authority

可以看到会登录失败,这是因为在使用 docker login 登录的时候会使用 https 的服务,而我们这里是自签名的证书,所以就报错了,我们可以将使用到的 CA 证书文件复制到 /etc/docker/certs.d/harbor.k8s.local 目录下面来解决这个问题(如果该目录不存在,则创建它)。ca.crt 这个证书文件我们可以通过 Ingress 中使用的 Secret 资源对象来提供:

[root@master1 docker]# mkdir -p /etc/docker/certs.d/harbor.k8s.local
[root@master1 docker]# kubectl get secret -n kube-ops
NAME                           TYPE                                  DATA   AGE
default-token-rswgc            kubernetes.io/service-account-token   3      3d12h
harbor-harbor-chartmuseum      Opaque                                1      5h12m
harbor-harbor-core             Opaque                                8      5h12m
harbor-harbor-ingress          kubernetes.io/tls                     3      5h12m
harbor-harbor-jobservice       Opaque                                2      5h12m
harbor-harbor-notary-server    Opaque                                5      5h12m
harbor-harbor-registry         Opaque                                3      5h12m
harbor-harbor-trivy            Opaque                                2      5h12m
jenkins-token-vwbd6            kubernetes.io/service-account-token   3      3d9h
sh.helm.release.v1.harbor.v1   helm.sh/release.v1                    1      5h12m
[root@master1 docker]# kubectl get secret harbor-harbor-ingress -n kube-ops -o yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5RENDQWR5Z0F3SUJBZ0lRV2tiVURPWEJ4RHlFNnZvZHRGSDUvakFOQmdrcWhraUc5dzBCQVFzRkFEQVUKTVJJd0VBWURWUVFERXdsb1lYSmliM0l0WTJFd0hoY05Nakl3TlRFNU1UQXlOekl4V2hjTk1qTXdOVEU1TVRBeQpOekl4V2pBVU1SSXdFQVlEVlFRREV3bG9ZWEppYjNJdFkyRXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRREQxT2hDcTM0RS9HdGd5dE10UU1PbmJ2OERFR3QyRnFZWVJ3SkJoQ0pJWXVDY0NkdnYKVzBhQXZJY1EySUw2aVdmckhzSjRFdHpSdXI0aTFzZmdWeVhzbXlJYng4M2pRUzN0R1VTK3N4MG9PeWNSaURZegpkdWFreDJIckEveGk2VWRIdzdndGJiSndQaTN1QTVrWFdHV0s0U0RreUtQeHVRQWR2TUVTWGcwK3FBczNpajlSCk4rT2cvNzBwTmZXUFRweUZzWXhGYjhCMHAzUDc0a3NRWXphSkJPc1h2ZTByQ2tqNlZlTlh3aTZsanZOL3d2cFoKWFFFU3pRUHJBY0ZnRUN6YUh2YTRjZEkzZTdwRWZwYklMSUZtY1hadzZwUUlkNjkvTzA3WTY2djJZcTVHSklmVwpMRUFVRjRza29oNDd5K1FNTkVSZmtaNGVBcUozM0ZDME9LdERBZ01CQUFHalFqQkFNQTRHQTFVZER3RUIvd1FFCkF3SUNwREFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXcKQXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFXUEdwQlRkK2VKUWhBQVBtK0ZWZ0JtRnhiK0dqVGdiRwptdHBBMXMzdG1ZenFSWU90YTJ2Ym1UN0UxWG45QWhxNHVmSmRwaGVOak1kY1ZVY0dPTVhqdFJweCtkRVZSMzRyCk05MFFFdVJoTHJuMkFNQkRGQWZZL0RpWDNYdStBb09ESTFWZVNxVi80UUQwd3ZPRkhpVkk2VTh5NWRFd3p2ZXcKWEVMZE12a1RKRjhUUGdzZWhFOFd1OG5rMnU1ZTdKREcweHFvby9KMW1CN0srR0xZODh1ZnV2eUNad0hJZ2JuMwpkM3FzcHFrTVMvU0xtTWpyMXNuK2pNZllXYlZkd2ZpSmVka1NkME45Zm13QksrTXcrMEIweTFVQUxzWS9aOGpoCnFPNzhJSGpyeko2VWtZWjQ0em9sSkp2ay84OUZnd0lRb0V1YWlNQm1PWHVYbUJybUhDeUpJdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lRQ0t3M3JNckd3UFhNbUxtWTdXa0lVakFOQmdrcWhraUc5dzBCQVFzRkFEQVUKTVJJd0VBWURWUVFERXdsb1lYSmliM0l0WTJFd0hoY05Nakl3TlRFNU1UQXlOekl4V2hjTk1qTXdOVEU1TVRBeQpOekl4V2pBYk1Sa3dGd1lEVlFRREV4Qm9ZWEppYjNJdWF6aHpMbXh2WTJGc01JSUJJakFOQmdrcWhraUc5dzBCCkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXpxdlJXSWgyWjBkdjdMbGdBVGQ3cEtuKytaczltcU1OUFpjRnNqMU8KcTdsSlBWUFBnbEd5SDVkbVE2SkQ1QjRDbDhqTDBEd3pRVDRzdXRXd01EdWJ3em9PcEhmWU14bi8veWlWQUxjdwprRlJmM1E4RU9LRmtoNkVmOEdFYzRmdzB0WUhxdkxBb1NISnEvVVBUSVVrazIvR2NMdUJmTkMwQWkwWWpTQ201ClEyWHJIcVZ0Zis2enJJeC9udjZWK1hxd2dTSVdOSnpuZ1pYUFZkcCsvbjZuaElLbzI0SXRYRGlkVEZ1WWU3MFoKRUVPbHJBWHRIb3RCQ1BCUmhFQ1NkUkZhbkFYYzNhb2VpNFVVWnQwWHNkcGpUTFlWaldVSU9obDFmWVhJVVRkTgpTQ09yN0xYeHU2TnhNQkE3d0tiSWIrcjFDaFAySUNiaWtzei9jQ1lzK3J6TkdRSURBUUFCbzI0d2JEQU9CZ05WCkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUF3R0ExVWQKRXdFQi93UUNNQUF3TFFZRFZSMFJCQ1l3SklJUWFHRnlZbTl5TG1zNGN5NXNiMk5oYklJUWJtOTBZWEo1TG1zNApjeTVzYjJOaGJEQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFOa0FQRnAxNEN1bTNzUVpBeU8xUFUzd0F5Zm1KCjNKbkIyWGRsVXJPUW45NzZrbWZDUkxKanBLYTV0NmtNYU5mUi9Ob2xIejJySWFkUXhkemtVSStjR2k3UlplWDgKMml1KytINGE5NVNYSGdncCtVQnF1THpNbGVNbFB4dmY2eU5NMlN4R0tCQXQvbkJhd05aeWJpeEFnTVhkN2JuMwozcENKYVBpZHE1cXBqdG02R2ZzUnVSS2lBbW55Q0VVYjRVdE1Icm00YjJlemN0eW1XQ3E3dWM0NnRmY2pQenkzCnBUZUlkNGtldmUzcVgwL0owTHBvNm9lWWdLYzVZTUtocm9KajhyYWtsMkNhdlptR2dXbEhWTVNkUHFkRTRUdmwKalJNMzlLOE14R0lRcittV1hEUlVkSVVxRlloTWdtU1djMjlhdjFYVVFMWHBSdUVkaXBvbmlpck1XUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBenF2UldJaDJaMGR2N0xsZ0FUZDdwS24rK1pzOW1xTU5QWmNGc2oxT3E3bEpQVlBQCmdsR3lINWRtUTZKRDVCNENsOGpMMER3elFUNHN1dFd3TUR1Ynd6b09wSGZZTXhuLy95aVZBTGN3a0ZSZjNROEUKT0tGa2g2RWY4R0VjNGZ3MHRZSHF2TEFvU0hKcS9VUFRJVWtrMi9HY0x1QmZOQzBBaTBZalNDbTVRMlhySHFWdApmKzZ6ckl4L252NlYrWHF3Z1NJV05Kem5nWlhQVmRwKy9uNm5oSUtvMjRJdFhEaWRURnVZZTcwWkVFT2xyQVh0CkhvdEJDUEJSaEVDU2RSRmFuQVhjM2FvZWk0VVVadDBYc2RwalRMWVZqV1VJT2hsMWZZWElVVGROU0NPcjdMWHgKdTZOeE1CQTd3S2JJYityMUNoUDJJQ2Jpa3N6L2NDWXMrcnpOR1FJREFRQUJBb0lCQVFDK0h4K2hvei9ST00yRQo1dStqanVmbWppQXFmUjVKNEU5dzVqcHVRcW5abmNUN2J0dEpWbUJYWVk3SkQwQUhYWGdqSzZ3Ykx4WXpvYi9DClRJd2RndWlDeXlqbk1meW8zZlU3UkpqMkpJKy9abThkekErbERRdVBIYkZsRCtGakh3RmFMRjdHSUFPZXllc1UKZTgyQkpHbU5FQklPcVlmTFZzN1dVTHBxU3ovTCtCR1FaSjlrR29LOXVHU2VhOG5aOGJlTjJVZ3R4QXFUWFU5bwpUbVljR01mV1o4Z2cvdkUzcWxCNkYyeVdNckwzYTBWaVR1UVI2MytsaVp1V3RZN1dXN1IwdXA4T05GZ29pc3VrClZPSm1hQnE4eTg1ZUpOK2xvZ3lNU2I0N21tRWFlS2piNlhqd2pGcVJzcUNva1EvTDF1NmRKRXZkWUZVdUZnYmMKdGphcTF2OWxBb0dCQVBEdFN4eFlqY1FGbGh5cW1DMlBueWZKQjFZQnFDNTJxTXdiUEROQXc3RjBpV0wwMlpSUAp1SHhqRDJubWI4S2UzY0Z1QUd1WEVEaktVeFJOQnpnR0Y1SGpjd2JFSUswVHNQdDhWSW9aemFKeWsvNEdsSE1uClM1Y2VOM2NoSnE5QnY5QzZWd0lKU2VFZGJvS2MyaVpYVDEybEUzOG5zYXdEZENFOHc4TmhnRENqQW9HQkFOdVoKNFkwbFAxcCtsSlBySjMvQmpuTk9yaURWalJFcG9vc2hkTU1LYUs2ODZrY0RzOCtxVVRscUhIUzA4SDVWN0l0SwpSVXArcmhZV1M1cnBUUmR5RWFQY3JMQXRkRVVqV2F0eHAzbWszTjBwZWNJdnVWRjFka1hSTHhSc1J5SzdocXg5CmYybDRNbHRqeU5lUlFENUtEbHhCMS9LRCtaSEsxZkhXR1ZtOENCc1RBb0dCQUo2U29IQTh2TG83THcwWkgyWkYKZEd5Y1BPN1gwbGtha29rY2wrZm9CYVdEM1hVN1VRWHozcXBJRFBvWHlKRmhQUFcyTUNLZ1gvWHcvUE9NcTJhdApZOUFLMDBldHlSQUJ1T2dkY2dzZlpxQTRKNzMzbUxlZU1udDVVT3BnOEhSRG96dC9tZ3JtRURGUnpNMjg3VHgxCkZQQysyUEZGTUFUV2xSWFhSOFlPSTNCZkFvR0FOdFgxSVphMGZEZHhpSUpHNmJyK2hFS1lickNFdWsxb1BrL0wKWlV5N0dTOHBYVFJLZVVleFlxSGhteTczZ3NTWC9hdzlJejRBNEprT3BnOHQ1ZlhnKzhjR0JEUkZmV0kzYVAxSApjYk8rUnlldE5OVlA5RXhkalUrUDd3eWVzc2lhOTRXYkJFNDBYT2ZNNWk5cUdHUEx0RFBvNWJFWTU5UDdtcGo0CjJaUi9mYXNDZ1lFQTZCekNzM3JyUEVtd2kxSVBYaU9VNE1BcHhya2pzUzhFME54OEphK1R0SkZxMTdXUDYzS2wKeUpFdHhJdXdxYWRKbVZRTU42dXIwUWNhcHNlQW9nWGxERjFobllTK1hYZlR3YlRYbU52eERQN0FIa3pQZUpwbgpkNCsyTmlDQkgvRXhlaHorY1dZU0lBUERGT1ZLQnJic3VTN3pFQ2UrRlZqS2YwUTYvb1h6MitRPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
kind: Secret
metadata:
  annotations:
    meta.helm.sh/release-name: harbor
    meta.helm.sh/release-namespace: kube-ops
  creationTimestamp: "2022-05-19T10:27:22Z"
  labels:
    app: harbor
    app.kubernetes.io/managed-by: Helm
    chart: harbor
    heritage: Helm
    release: harbor
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:ca.crt: {}
        f:tls.crt: {}
        f:tls.key: {}
      f:metadata:
        f:annotations:
          .: {}
          f:meta.helm.sh/release-name: {}
          f:meta.helm.sh/release-namespace: {}
        f:labels:
          .: {}
          f:app: {}
          f:app.kubernetes.io/managed-by: {}
          f:chart: {}
          f:heritage: {}
          f:release: {}
      f:type: {}
    manager: Go-http-client
    operation: Update
    time: "2022-05-19T10:27:22Z"
  name: harbor-harbor-ingress
  namespace: kube-ops
  resourceVersion: "275012"
  uid: 6a6a5bdb-1d24-4834-8a46-953fa2fb6b1e
type: kubernetes.io/tls

其中 data 区域中 ca.crt 对应的值就是我们需要证书,不过需要注意还需要做一个 base64 的解码,这样证书配置上以后就可以正常访问了。

不过由于上面的方法较为繁琐,所以一般情况下面我们在使用 docker cli 的时候是在 docker 启动参数后面添加一个 –insecure-registry 参数来忽略证书的校验的(docker login等命令 xxx –insecure-registry),在 docker 启动文件

/usr/lib/systemd/system/docker.service

(systemd管理docker的文件)中修改ExecStart的启动参数:

ExecStart=/usr/bin/dockerd --insecure-registry harbor.k8s.local

还可以在docker的配置文件中修改:

$ cat /etc/docker/daemon.json
{
  "insecure-registries" : [
    "harbor.k8s.local"
  ],
  "registry-mirrors" : [
    "https://ot2k4d59.mirror.aliyuncs.com/"
  ]
}

在这里插入图片描述

然后保存重启 docker,再使用 docker cli 就没有任何问题了:

登录登出
[root@master1 docker]# docker login harbor.k8s.local
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[root@master1 docker]# docker logout
Removing login credentials for https://index.docker.io/v1/


登录进去,然后继续下列的步骤

比如我们本地现在有一个名为 busybox:1.28.4 的镜像,现在我们想要将该镜像推送到我们的私有仓库中去,应该怎样操作呢?首先我们需要给该镜像重新打一个具有 harbor.k8s.local 前缀的镜像,然后推送的时候就可以识别到推送到哪个镜像仓库:

[root@master1 docker]# docker pull busybox:1.28.4
1.28.4: Pulling from library/busybox
07a152489297: Pull complete
Digest: sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
Status: Downloaded newer image for busybox:1.28.4
docker.io/library/busybox:1.28.4

[root@master1 docker]# docker image ls
REPOSITORY                                                        TAG        IMAGE ID       CREATED         SIZE
busybox                                                           1.28.4     8c811b4aec35   3 years ago     1.15MB
[root@master1 docker]# docker push harbor.k8s.local/library/busybox:1.28.4
The push refers to repository [harbor.k8s.local/library/busybox]
432b65032b94: Pushed
1.28.4: digest: sha256:74f634b1bc1bd74535d5209589734efbd44a25f4e2dc96d78784576a3eb5b335 size: 527

推送完成后,我们就可以在 Portal 页面上看到这个镜像的信息了:

在这里插入图片描述

镜像 push 成功,同样可以测试下 pull:

[root@master1 docker]# docker rmi harbor.k8s.local/library/busybox:1.28.4
Untagged: harbor.k8s.local/library/busybox:1.28.4
Untagged: harbor.k8s.local/library/busybox@sha256:74f634b1bc1bd74535d5209589734efbd44a25f4e2dc96d78784576a3eb5b335

[root@master1 docker]# docker rmi busybox:1.28.4
Untagged: busybox:1.28.4
Untagged: busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
Deleted: sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
Deleted: sha256:432b65032b9466b4dadcc5c7b11701e71d21c18400aae946b101ad16be62333a
[root@master1 docker]# docker pull harbor.k8s.local/library/busybox:1.28.4
1.28.4: Pulling from library/busybox
07a152489297: Pull complete
Digest: sha256:74f634b1bc1bd74535d5209589734efbd44a25f4e2dc96d78784576a3eb5b335
Status: Downloaded newer image for harbor.k8s.local/library/busybox:1.28.4
harbor.k8s.local/library/busybox:1.28.4
[root@master1 docker]# docker image ls | grep busybox
harbor.k8s.local/library/busybox                                  1.28.4     8c811b4aec35   3 years ago     1.15MB

到这里证明上面我们的私有 docker 仓库搭建成功了,大家可以尝试去创建一个私有的项目,然后创建一个新的用户,使用这个用户来进行 pull/push 镜像,Harbor还具有其他的一些功能,比如镜像复制,Helm Chart 包托管等等,大家可以自行测试,感受下 Harbor 和官方自带的 registry 仓库的差别。



版权声明:本文为weixin_45843419原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。