第19章 CI/CD
部署gitlab
编写资源配置清单
创建命名空间:
kubectl create namespace devops
创建harbor-secret
cat > harbor-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  namespace: devops
  name: harbor-secret
data:
  .dockerconfigjson: ewoJImF1dGhzIjogewoJCSJsdWZmeS5jb20iOiB7CgkJCSJhdXRoIjogIllXUnRhVzQ2U0dGeVltOXlNVEl6TkRVPSIKCQl9Cgl9Cn0=
type: kubernetes.io/dockerconfigjson
EOF
kubectl apply -f harbor-secret.yaml
编写dp资源
cat > gitlab-dp.yaml << 'EOF'
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitlab
  namespace: devops
  labels:
    app: gitlab
spec:
  selector:
    matchLabels:
      app: gitlab
  template:
    metadata:
      name: gitlab
      labels:
        app: gitlab
    spec:
      nodeName: node-02
      imagePullSecrets:
      - name: harbor-secret
      initContainers:
      - name: fix-permissions
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "chown -R 1000:1000 /var/opt/gitlab"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: gitlab
          mountPath: /var/opt/gitlab
      containers:
      - name: gitlab
        # image: gitlab/gitlab-ce:latest
        image: luffy.com/devops/gitlab-ce:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: GITLAB_OMNIBUS_CONFIG
          value: |
            external_url 'http://gitlab-svc'
        #    alertmanager['enable'] = false
        #    grafana['enable'] = false
        #    prometheus['enable'] = false
        #    node_exporter['enable'] = false
        #    redis_exporter['enable'] = false
        #    postgres_exporter['enable'] = false
        #    pgbouncer_exporter['enable'] = false
        #    gitlab_exporter['enable'] = false
        ports:
        - name: http
          containerPort: 80
        - name: ssh
          containerPort: 22
        volumeMounts:
        - mountPath: /var/opt/gitlab
          name: gitlab
      volumes:
        - name: gitlab
          hostPath:
            path: /data/gitlab/
            type: DirectoryOrCreate
EOF
gitlab-svc
cat > gitlab-svc.yaml << 'EOF'
---
apiVersion: v1
kind: Service
metadata:
  name: gitlab-svc
  namespace: devops
  labels:
    app: gitlab
spec:
  selector:
    app: gitlab
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: ssh
      port: 22
      targetPort: 22
  type: ClusterIP
EOF
gitlab-ingress
cat > gitlab-ingress.yaml << 'EOF'
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitlab
  namespace: devops
spec:
  ingressClassName: nginx
  rules:
  - host: gitlab.local
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: gitlab-svc
            port:
              number: 80
EOF
查看密码:
kubectl -n devops exec -it $(kubectl -n devops get pod|awk '/gitlab/{print $1}') -- grep "^Password" /etc/gitlab/initial_root_password
实验代码
[root@master-01 ~/xzs]# tree -L 2
.
├── docker-compose.yaml
├── Dockerfile
├── pom.xml
├── README.md
├── settings.xml
├── src
│   └── main
└── start.sh
阿里云的settings
nexus的settings
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <pluginGroups>
  </pluginGroups>
  <proxies>
  </proxies>
  <servers>
    <server>
      <id>snapshots</id>
      <username>admin</username>
      <password>admin</password>
    </server>
    <server>
      <id>releases</id>
      <username>admin</username>
      <password>admin</password>
    </server>
    <server>
      <id>public</id>
      <username>admin</username>
      <password>admin</password>
    </server>
  </servers>
  <mirrors>
    <mirror>
      <id>public</id>
      <mirrorOf>*</mirrorOf>
      <url>http://10.0.0.51:8081/repository/maven-public/</url>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
      <id>public</id>
      <repositories>
        <repository>
          <id>public</id>
          <url>http://10.0.0.51:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>public</id>
          <url>http://10.0.0.51:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>public</activeProfile>
  </activeProfiles>
</settings>
部署jenkins
编写资源配置清单
创建命名空间
kubectl create namespace devops
创建RBAC
cat > jenkins-rbac.yaml << 'EOF'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: jenkins-admin
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins-admin
  namespace: devops
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins-admin
subjects:
- kind: ServiceAccount
  name: jenkins-admin
  namespace: devops
EOF
创建pv和pvc
cat > jenkins-pv.yaml << 'EOF'
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: jenkins-pv-volume
  labels:
    type: local
spec:
  storageClassName: local-storage
  claimRef:
    name: jenkins-pv-claim
    namespace: devops
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  local:
    path: /data/jenkins
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node-01
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pv-claim
  namespace: devops
spec:
  storageClassName: local-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
EOF
创建DP
cat > jenkins-dp.yaml << 'EOF'
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins-server
  namespace: devops
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins-server
  template:
    metadata:
      labels:
        app: jenkins-server
    spec:
      nodeName: node-01
      imagePullSecrets:
      - name: harbor-secret
      securityContext:
            fsGroup: 1000
            runAsUser: 1000
      serviceAccountName: jenkins-admin
      initContainers:
      - name: fix-permissions
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "chown -R 1000:1000 /var/jenkins_home"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: jenkins-data
          mountPath: /var/jenkins_home
      containers:
        - name: jenkins-server
          # image: jenkins/jenkins:lts-jdk17
          image: luffy.com/devops/jenkins:lts-jdk17
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              memory: "500Mi"
              cpu: "500m"
            requests:
              memory: "500Mi"
              cpu: "500m"
          ports:
            - name: httpport
              containerPort: 8080
            - name: jnlpport
              containerPort: 50000
          livenessProbe:
            httpGet:
              path: "/login"
              port: 8080
            initialDelaySeconds: 90
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 5
          readinessProbe:
            httpGet:
              path: "/login"
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          volumeMounts:
            - name: jenkins-data
              mountPath: /var/jenkins_home
      #volumes:
      #  - name: jenkins-data
      #    persistentVolumeClaim:
      #        claimName: jenkins-pv-claim
      volumes:
        - name: jenkins-data
          hostPath:
            path: /data/jenkins/
            type: DirectoryOrCreate
EOF
创建svc
cat > jenkins-svc.yaml << 'EOF'
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins-svc
  namespace: devops
  labels:
    app: jenkins-svc
spec:
  selector:
    app: jenkins-server
  ports:
    - name: web
      port: 8080
      targetPort: httpport
    - name: agent
      port: 50000
      targetPort: jnlpport
EOF
创建ingress
cat > jenkins-ingress.yaml << 'EOF'
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins-ingress
  namespace: devops
spec:
  ingressClassName: nginx
  rules:
  - host: jenkins.local
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: jenkins-svc
            port:
              number: 8080
EOF
安装必备插件
kubernetes
git
git parameter
pipeline
pipeline: stage view
active choices
Localization: Chinese (Simplified)

配置kubernetes插件






jenkins/inbound-agent


运行测试



保存kubeconfig到jenkins凭证

保存ssh公钥和私钥到jenkins凭证
这里有坑注意:
这里的公钥和私钥不要使用Centos7系统上生成的,要使用jenkins-agent容器上生成的,因为jenkins-agent容器是Debian系统,Centos7的公钥和私钥似乎在Debian上不太正常。最保险的,还是在jenkins-agent容器上生成,然后保存在jenkins凭证里。
添加私钥

添加公钥

配置信任harbor地址
cat > /etc/docker/daemon.json << 'EOF'
{
      "insecure-registries": ["https://luffy.com"]
}
EOF
配置habor-Secret
cat > harbor-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  namespace: devops
  name: harbor-secret
data:
  .dockerconfigjson: ewoJImF1dGhzIjogewoJCSJsdWZmeS5jb20iOiB7CgkJCSJhdXRoIjogIllXUnRhVzQ2U0dGeVltOXlNVEl6TkRVPSIKCQl9Cgl9Cn0=
type: kubernetes.io/dockerconfigjson
EOF
kubectl apply -f harbor-secret.yaml
运行测试
pipeline模式1:简略配置
pipeline{
    agent {
      kubernetes {
        cloud 'kubernetes'
        inheritFrom 'jenkins-jnlp'
        namespace 'devops'
      }
    }
    stages{
        stage('准备 SSH 密钥') {
            steps {
                withCredentials([
                    sshUserPrivateKey(credentialsId: 'jenkins-ssh-key', keyFileVariable: 'SSH_KEY_FILE'),
                    file(credentialsId: 'jenkins-ssh-pub-key', variable: 'SSH_PUB_KEY_FILE')
                ]) {
                    sh '''
                        mkdir -p ~/.ssh
                        chmod 700 ~/.ssh
                        cp ${SSH_KEY_FILE} ~/.ssh/id_rsa
                        cp ${SSH_PUB_KEY_FILE} ~/.ssh/id_rsa.pub
                        chmod 600 ~/.ssh/id_rsa
                        chmod 644 ~/.ssh/id_rsa.pub
                        ssh-keyscan gitlab-svc >> ~/.ssh/known_hosts
                    '''
                }
            }
        }
        stage("准备docker配置"){
            steps{
                withCredentials([file(credentialsId: 'docker-config', variable: 'DOCKERCONFIG')]) {
                    sh '''
                        echo "登录harbor"
                        mkdir -p ~/.docker && cp ${DOCKERCONFIG} ~/.docker/config.json
                        docker login luffy.com
                        docker pull luffy.com/base/nginx:latest
                    '''
                }
            }
        }
        stage("准备k8s配置"){
            steps{
                withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                    echo "查看 K8S 集群 Pod 列表"
                    sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
                    sh "kubectl get pod"
                }
            }
        }
        stage("拉取代码"){
            steps{
                sh '''
                    rm -rf xzs
                    git clone git@gitlab-svc:root/xzs.git
                '''
            }
        }
        stage('构建镜像'){
            steps{
                sh '''
                    cd xzs
                    docker build -t luffy.com/app/xzs:v1 .
                    docker push luffy.com/app/xzs:v1
                '''
            }
        }
        stage('上传镜像'){
            steps{
                sh '''
                    docker push luffy.com/app/xzs:v1
                '''
            }
        }
    }
}
pipeline模式2:显式yaml配置
pipeline{
    agent {
	    kubernetes{
			cloud 'kubernetes'
			yaml '''
---
apiVersion: "v1"
kind: "Pod"
metadata:
  labels:
    jenkins: "slave"
  namespace: "devops"
spec:
  containers:
  - env:
    - name: "JENKINS_TUNNEL"
      value: "jenkins-svc.devops.svc.cluster.local:50000"
    - name: "JENKINS_AGENT_WORKDIR"
      value: "/home/jenkins/agent"
    - name: "JENKINS_URL"
      value: "http://jenkins-svc.devops.svc.cluster.local:8080/"
    image: "jenkins/inbound-agent"
    imagePullPolicy: "IfNotPresent"
    name: "jnlp"
    volumeMounts:
    - mountPath: "/usr/libexec/docker"
      name: "volume-1"
      readOnly: false
    - mountPath: "/usr/bin/kubectl"
      name: "volume-3"
      readOnly: false
    - mountPath: "/var/run/docker.sock"
      name: "volume-0"
      readOnly: false
    - mountPath: "/etc/docker"
      name: "volume-2"
      readOnly: false
    - mountPath: "/usr/bin/docker"
      name: "volume-4"
      readOnly: false
    - mountPath: "/home/jenkins/agent"
      name: "workspace-volume"
      readOnly: false
    workingDir: "/home/jenkins/agent"
  imagePullSecrets:
  - name: "harbor-secret"
  nodeSelector:
    kubernetes.io/os: "linux"
  securityContext:
    runAsGroup: 0
    runAsUser: 0
  serviceAccountName: "jenkins-admin"
  volumes:
  - hostPath:
      path: "/var/run/docker.sock"
    name: "volume-0"
  - hostPath:
      path: "/etc/docker"
    name: "volume-2"
  - hostPath:
      path: "/usr/libexec/docker"
    name: "volume-1"
  - emptyDir:
      medium: ""
    name: "workspace-volume"
  - hostPath:
      path: "/usr/bin/docker"
    name: "volume-4"
  - hostPath:
      path: "/usr/bin/kubectl"
    name: "volume-3"
'''
		}
	}
    stages{
        stage('准备SSH密钥') {
            steps {
                withCredentials([
                    sshUserPrivateKey(credentialsId: 'jenkins-ssh-key', keyFileVariable: 'SSH_KEY_FILE'),
                    file(credentialsId: 'jenkins-ssh-pub-key', variable: 'SSH_PUB_KEY_FILE')
                ]) {
                    sh '''
                        mkdir -p ~/.ssh
                        chmod 700 ~/.ssh
                        cp ${SSH_KEY_FILE} ~/.ssh/id_rsa
                        cp ${SSH_PUB_KEY_FILE} ~/.ssh/id_rsa.pub
                        chmod 600 ~/.ssh/id_rsa
                        chmod 644 ~/.ssh/id_rsa.pub
                        ssh-keyscan gitlab-svc >> ~/.ssh/known_hosts
                    '''
                }
            }
        }
        stage("准备docker配置"){
            steps{
                withCredentials([file(credentialsId: 'docker-config', variable: 'DOCKERCONFIG')]) {
                    sh '''
                        echo "登录harbor"
                        mkdir -p ~/.docker && cp ${DOCKERCONFIG} ~/.docker/config.json
                        docker login luffy.com
                        docker pull luffy.com/base/nginx:latest
                    '''
                }
            }
        }
        stage("准备k8s配置"){
            steps{
                withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                    echo "查看 K8S 集群 Pod 列表"
                    sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
                    sh "kubectl get pod"
                }
            }
        }
        stage("拉取代码"){
            steps{
                sh '''
                    rm -rf xzs
                    git clone git@gitlab-svc:root/xzs.git
                '''
            }
        }
        stage('构建镜像'){
            steps{
                sh '''
                    cd xzs
                    docker build -t luffy.com/app/xzs:v1 .
                    docker push luffy.com/app/xzs:v1
                '''
            }
        }
        stage('上传镜像'){
            steps{
                sh '''
                    docker push luffy.com/app/xzs:v1
                '''
            }
        }
    }
}
部署harbor
省略
部署nexus
更新: 2024-09-26 11:02:26