Gitlab Docker Jenkins K8s

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


一、Gitlab 搭建

1.使用ssh连接ec2 instance

1.1 将.pem的权限设置为当前用户

  • 有且仅有当前用户可以使用且需要给当前用户完全空值权限
    在这里插入图片描述
    #在这里插入图片描述

1.2 使用windows的Terminal直接连接

  1. 使用windows的terminal连接,直接将复制ec2连接里的example即可,注意.pem的路径需要和只想当前语句的路径在一个文件夹下
    在这里插入图片描述

1.3 使用vscode里的ssh连接器

  1. 下载Remote-SSH插件
  2. 使用ctrl+shift+p然后>ssh找到配置文件open ssh configuration file
Host 54.252.193.163
    HostName 54.252.193.163
    User ec2-user
    IdentityFile "F:\Downloads\lg101.pem"
  1. 其中Host可以自定义,HostName就是ec2的Public IP,user是固定写法,File是 .pem文件在本机的地址

2.给EC2的Linux服务器安装docker

  1. 查看当前用户和服务器版本,服务器必须是Amazon Linux 22服务器
$ uname -r
$ lsb_release -a
  1. 更新yum
sudo yum update -y
  1. 安装amazon-linux-extras
sudo yum install -y amazon-linux-extras
  1. 安装docker
sudo amazon-linux-extras install docker
  1. 开启docker服务
sudo service docker start
  1. 给ec2-user账号授权,这样在docker使用的时候都不需要一直用sudo
sudo usermod -a -G docker ec2-user
  1. 安装docker-compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  1. 授权给docker-compose文件
sudo chmod +x /usr/bin/docker-compose

3. 安装gitlab

1.进入到用户目录下

cd /usr/local

2.创建docker 目录

sudo mkdir docker

3.在该文件下创建一个文件夹

sudo mkdir gitlab_docker
  1. 进入文件夹,创建docker-compose.yml文件
sudo vi docker-compose.yml

5.拉去gitlab 镜像

sudo docker pull gitlab/gitlab-ce:latest

6.编辑docker-compose.yml文件,目前只能通过80端口访问该服务

version: '3.8'

services:
  web:
    image: 'gitlab/gitlab-ce:latest'
    restart: always
    hostname: '54.206.92.244'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://54.206.92.244'
    ports:
      - '80:80'
    volumes:
      - './config:/etc/gitlab'
      - './logs:/var/log/gitlab'
      - './data:/var/opt/gitlab'
    networks:
      - gitlab

networks:
  gitlab:
    name: gitlab-network
  • 启动成功
    -在这里插入图片描述
  1. 获取root密码
docker exec -it gitlab_docker-web-1 grep 'Password:' /etc/gitlab/initial_root_password
  1. 用root用户登录
om5PpM6DRsb8ntOBTZ8HQ16EjcbajZ7sHOntZf/+Js0=

9.成功进入

  • 如果想用别的端口运行gitlab,那么需要将docker-compose.yml改为
version: '3.8'

services:
  web:
    image: 'gitlab/gitlab-ce:latest'
    restart: always
    hostname: '54.206.92.244:8929'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://54.206.92.244:8929'
    ports:
      - '8929:8929'
    volumes:
      - './config:/etc/gitlab'
      - './logs:/var/log/gitlab'
      - './data:/var/opt/gitlab'
    networks:
      - gitlab

networks:
  gitlab:
    name: gitlab-network
  • EC2的inbound rules一定要添加我们的端口
    在这里插入图片描述

4. 使用gitlab

pass

二、Jenkins搭建

搭建之前需要安装Jdk

sudo apt update
sudo apt install -y openjdk-11-jdk

1. jenkins 安装

  1. update package
sudo apt update
  1. add jenkins key to the system
curl -fsSL https://pkg.jenkins.io/debian/jenkins.io.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
  1. add jenkins repository
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null

  1. update package again
sudo apt update

  1. Install Jenkins
sudo apt install jenkins

2. 开启jenkins

  1. 开启服务
sudo systemctl status jenkins
  1. 打开aws 8080端口

  2. 找到初始密码

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

3. 安装插件

  1. 将推荐的插件全部安装
  2. 安装ssh agent

三、 Ansible安装

  1. 安装脚本ansible.sh
sudo apt-add-repository ppa:ansible/ansible -y

sudo apt update -y

sudo apt install ansible -y

四、 使用脚本安装minikube和docker

1. 安装docker

  1. 使用docker.sh文件安装
#!/bin/bash
sudo apt update -y

sudo apt install apt-transport-https ca-certificates curl software-properties-common -y

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable" -y

sudo apt update -y

apt-cache policy docker-ce -y

sudo apt install docker-ce -y

#sudo systemctl status docker

sudo chmod 777 /var/run/docker.sock
  1. 开启docker 服务
sudo service docker start

2 . 安装minikube

2.1 安装Minikube环境并运行

  1. 先决条件,安装docker,并且启动服务
  2. 更新pakage
  sudo apt update
  1. install minikube
  curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x ./kubectl && sudo mv ./kubectl /usr/local/bin/kubectl
  curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
  1. install contrack
sudu   apt install conntrack
  1. 使用完root 用户后退出
exit
  1. 将当前用户添加到 docker 组并使用更新的组成员身份启动一个新 shell。
  sudo usermod -aG docker $USER && newgrp docker
  1. 使用docker 开启minikube
 minikube start --driver=docker

2.2 运行一个测试服务

  1. 使用一个docker image 开启一个pod
  kubectl run todolistapp --image=kubekode/react-todo-list-app
  1. 为该pod部署一个Nodeport 服务,端口为80
  kubectl expose pod todolistapp --type=NodePort --port=80 --name=todolistapp-service
  1. 查看该web服务是否正常运行,成功返回一个ip
  minikube service todolistapp-service --url
  1. 使用curl查看web的网页返回
curl <上面的ip>
  1. 测试都正常,将对外的3000端口,转发到我们开启的80端口
  kubectl port-forward svc/todolistapp-service 3000:80 --address 0.0.0.0 &
  1. 使用<ec2 ip:3000>,访问服务

在这里插入图片描述

2.3 停止一个minikube服务

  1. 查看目前运行的Pod
kubectl get svc
  1. 删除服务
kubectl delete svc todolistapp-service
  1. 查看端口是否占用
sudo lsof -i :<port>
  1. 删除占用端口
sudo kill  <PID>
  1. 看出Pods
kubectl get pods --all-namespaces
  1. 删除pods
kubectl delete pod <pod-name> -n <namespace>

五、Jenkins配置github

1. 手动触发pipeline

  1. 需要先安装插件ssh-agent:这样服务器之间就可以通过.pem的私钥进行通讯,安装完成后,重启服务器
  2. 新建一个pipeline
    在这里插入图片描述
  3. 编写第一个jenkins pipeline脚本

在这里插入图片描述

  1. 生成git的pipeline连接
    在这里插入图片描述

  2. 生成crendential
    在这里插入图片描述

  3. 在github里的setting里,生成token
    在这里插入图片描述

  4. 配置好之后,复制crendential到scripts里
    在这里插入图片描述

  5. 更该scripts

node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: '4a51e2fc-41b4-4c24-b523-d18b0bc3c887', url: 'https://github.com/CXTV/devDemo.git'
    }
}

  1. 点击applysave,手动build now,成功之后,我们进入到jenkins 所在的服务器的工作文件夹里,就可以看到我们项目所有的文件已经
    在这里插入图片描述

2. 自动触发pipeline添加githubhook

  1. 勾选github trigger
    在这里插入图片描述
  2. 到github里的项目页的setting里面设置jenkins的webhook
    在这里插入图片描述
  3. 在jenkins的个人页面里获取token,复制到github里的secret
    在这里插入图片描述
  4. 设置好之后,只要main分支进行了更新,jenkins会自动更关心代码到自己的workflow的文件夹下
    在这里插入图片描述
  5. 添加一个test_demo.py 文件,并且合并到main分支下,到jenkins服务器里的workflow里查看,新添加的文件已经自动到jenkins服务器
    在这里插入图片描述

六、 Ansible服务器设置

里,我们已经将我们github的所有文件发到了jenkins里,现在需要将jenkins里的项目文件,发送到ansible的服务器里,然后构建dockerfile生成docker image

1. 将jenkins里的项目文件传给Ansible服务器

  1. 添加一个新的stage,用来将dockerfile传递给ansible的服务器
node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: '4a51e2fc-41b4-4c24-b523-d18b0bc3c887', url: 'https://github.com/CXTV/devDemo.git'
    }
    
    stage('sending docker file to Ansible server over ssh'){
        
      
    }
}
  1. 添加stage里的内容,选择ssh-agent
    在这里插入图片描述
  2. 添加crendential

在这里插入图片描述

  1. 将生成的crendential添加到stage里
node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: '4a51e2fc-41b4-4c24-b523-d18b0bc3c887', url: 'https://github.com/CXTV/devDemo.git'
    }
    
    stage('sending docker file to Ansible server over ssh'){
       sshagent(['ansible_demo']) {
           
        }
    }
}
  1. 添加ssh 命令,让jenkins执行,创建文件夹,并且复制jenkins里的所有项目文件到ansible服务器的指定文件夹里
node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: '4a51e2fc-41b4-4c24-b523-d18b0bc3c887', url: 'https://github.com/CXTV/devDemo.git'
    }
    
    stage('sending docker file to Ansible server over ssh'){
        sshagent(['ansible_demo']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@54.66.ansible_server mkdir -p /home/ubuntu/pipeline_review'
            sh 'scp -r /var/lib/jenkins/workspace/pipeline-demo/* ubuntu@54.66.ansible_server :/home/ubuntu/pipeline_review/'
        }    
    }
}

2. 在ansible服务器里构建docker image

2.1 docker image build

  1. 创建stage,其中$JOB_NAME系统变量,pipeline的名称,$BUILD_ID是构建的次数=ID
node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: 'github_jenkins', url: 'https://github.com/CXTV/devDemo.git'
    }
    
    stage('sending docker file to Ansible server over ssh'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.107.70.208 mkdir -p /home/ubuntu/pipeline_review/'
            sh 'scp -r /var/lib/jenkins/workspace/pipeline-review/* ubuntu@3.107.70.208:/home/ubuntu/pipeline_review/'
        }
    }
    
    stage('Build docker images'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 cd /home/ubuntu/pipeline_review/'
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker build -f /home/ubuntu/pipeline_review/Dockerfile -t $JOB_NAME:v1.$BUILD_ID  /home/ubuntu/pipeline_review/'
        }
    }


}

注意:

  1. 这里的stage应该写的是ansible服务器的
  2. sh里所有文件夹结尾一定要写/,不然会报错
  3. 在使用docker的时候,一定要确保服务器docker已经服务开启
  4. 如何需要在指定的文件夹里docker build一定要-f指定文件夹

2.2 给image进行tag

  • tag只是为了更好的区分我们的docker image的版本,为下一步上传到docker Hub里做准备
node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: 'github_jenkins', url: 'https://github.com/CXTV/devDemo.git'
    }
    
    stage('sending docker file to Ansible server over ssh'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 cd /home/ubuntu/'
            sh 'scp -r /var/lib/jenkins/workspace/pipeline-review/* ubuntu@3.27.188.111:/home/ubuntu/pipeline_review/'
        }
    }
    
    stage('Build docker images'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  cd /home/ubuntu/pipeline_review/'
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker build -f /home/ubuntu/pipeline_review/Dockerfile -t $JOB_NAME:v1.$BUILD_ID /home/ubuntu/pipeline_review/'
        }
    }
    
    stage('Build image tagging'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  cd /home/ubuntu/'
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image tag $JOB_NAME:v1.$BUILD_ID babypig521/$JOB_NAME:v1.$BUILD_ID '
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image tag $JOB_NAME:v1.$BUILD_ID babypig521/$JOB_NAME:latest'
        }
    }   
}

注意:

  1. 在进行tag的时候,用户名必须和dockerhub里的用户名一直

2.3 将tag好的image上传到docker hub里

  1. 首先,在进行上传的server里,检查docker hub是否可以正常登录
docker login
  1. 将dockerhub的密码存放在crendential里,防止密码泄露
    在这里插入图片描述
  2. 创建stage
node {
    stage('Git checkout'){
        git branch: 'main', credentialsId: 'github_jenkins', url: 'https://github.com/CXTV/devDemo.git'
    }
    
    stage('sending docker file to Ansible server over ssh'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 cd /home/ubuntu/'
            sh 'scp -r /var/lib/jenkins/workspace/pipeline-review/* ubuntu@3.27.188.111:/home/ubuntu/pipeline_review/'
        }
    }
    
    stage('Build docker images'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  cd /home/ubuntu/pipeline_review/'
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker build -f /home/ubuntu/pipeline_review/Dockerfile -t $JOB_NAME:v1.$BUILD_ID /home/ubuntu/pipeline_review/'
        }
    }
    
    stage('Build image tagging'){
        sshagent(['ansible_server']) {
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  cd /home/ubuntu/'
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image tag $JOB_NAME:v1.$BUILD_ID pjj521/$JOB_NAME:v1.$BUILD_ID '
            sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image tag $JOB_NAME:v1.$BUILD_ID pjj521/$JOB_NAME:latest'
        }
    }
    
    stage('push docker images to dockerhub'){
        sshagent(['ansible_server']) {
            withCredentials([string(credentialsId: 'dockerPWD', variable: 'dockerhub_password')]) {
                sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 docker login -u pjj521 -p $dockerhub_password'
                sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image push pjj521/$JOB_NAME:v1.$BUILD_ID '
                sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image push pjj521/$JOB_NAME:latest'
            }
        }
    }
    
}
  1. 上传完毕后,可以看到docker hub里的repository
    在这里插入图片描述

七、部署argo CD

1. 安装argo CD

  1. 进入到k8s的服务器,执行
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
  1. 使用cli安装argo cd
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm argocd-linux-amd64
  1. 匹配节点
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
  1. 查看argo cd是否启动
kubectl get all -n argocd
  1. 暴露节点.
kubectl port-forward svc/argocd-server -n argocd --address 0.0.0.0 8080:443
  1. 一定使用https+ip+8080进行访问
https://3.107.22.174:8080/
  1. 新开一个连接,因为这个服务不是后台运行,获取admind的密码
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

额外补充

1.课程

2. 使用vscode开发jenkins

2.1 配置vscode环境

  1. vscode里的项目根目录添加.vscode/settings.json文件将jenkins的配置文件添加
  2. 安装jenkins runner插件
  3. 将配置写入
{
    "jenkins-runner.jobs": {
        "gitops-argocd_CI": {
            "isDefault": true,
            "runWith": "jenkins-master",
            "name": "gitops-argocd_CI"
        }
    },
    "jenkins-runner.hostConfigs": {
        "jenkins-master": {
            "url": "http://54.206.212.1:8080",
            "user": "babypig521",
            "password": "shangxi123",
            "useCrumbIssuer": true,
        }
    }
}

2.2 将github的代码提交到jenkins里

  1. 创建Jenkinsfile文件在项目的根目录
  2. 编写
pipeline{

    agent any

    environment{

        DOCKERHUB_USERNAME = 'pjj521'
        DOCKERHUB_CREDENTIALS = 'dockerPWD'
        APP_NAME = 'gitops-argocd_CI'
        IMAGE_NAME = "${BUILD_NUMBER}"
        IMAGE_TAG = "${DOCKERHUB_USERNAME}"+"/"+ "${APP_NAME}" 

    } 
    stages{
        stage('git check out'){
            steps{
                script{
                    git branch: 'main',
                    url: 'https://github.com/CXTV/devDemo.git',
                    credentialsId: 'github_jenkins'
                }
            }
        }
    }
}

2.3 将jenkins服务器里的项目文件复制到argoCD的服务器上

  1. 注意Jenkins上项目的所在位置,在进行复制
pipeline{

    agent any

    environment{

        DOCKERHUB_USERNAME = 'pjj521'
        DOCKERHUB_CREDENTIALS = 'dockerPWD'
        APP_NAME = 'gitops-argocd_CI'
        IMAGE_NAME = "${BUILD_NUMBER}"
        IMAGE_TAG = "${DOCKERHUB_USERNAME}"+"/"+ "${APP_NAME}" 

    } 
    stages{
        stage('git check out'){
            steps{
                script{
                    git branch: 'main',
                    url: 'https://github.com/CXTV/devDemo.git',
                    credentialsId: 'github_jenkins'
                }
            }
        }
        stage('sending code to ansible server'){
            steps{
                script{
                    sshagent(['ansible_server']) {
                        sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 cd /home/ubuntu/'
                        sh 'scp -r /var/lib/jenkins/workspace/gitops-argocd_CI/* ubuntu@3.27.188.111:/home/ubuntu/gitops-argocd_CI/'
                    }
                }
            }
        }
    }
}

2.4 对image进行build/tagging/push

  • 这些步骤和手动版本的一致,只是需要注意的有几点:
pipeline{

    agent any

    environment{

        DOCKERHUB_USERNAME = 'pjj521'
        DOCKERHUB_CREDENTIALS = 'dockerPWD'
        APP_NAME = 'gitops-argocd_app'
        IMAGE_NAME = "${BUILD_NUMBER}"
        IMAGE_TAG = "${DOCKERHUB_USERNAME}"+"/"+ "${APP_NAME}" 
        IMAGE_VERSION = "v1.$BUILD_ID"
    } 

    stages{
        stage('git check out'){
            steps{
                script{
                    git branch: 'main',
                    url: 'https://github.com/CXTV/devDemo.git',
                    credentialsId: 'github_jenkins'
                }
            }
        }

        stage('sending code to ansible server'){
            steps{
                script{
                
                    sshagent(['ansible_server']) {
                        sh 'ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 cd /home/ubuntu/'
                        sh 'scp -r /var/lib/jenkins/workspace/gitops-argocd_CI/* ubuntu@3.27.188.111:/home/ubuntu/gitops-argocd_CI/'
                    }
                }
            }
        }

        stage('build docker image'){
            steps{
                script{
                    sshagent(['ansible_server']) {
                        sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  cd /home/ubuntu/"
                        sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker build -f /home/ubuntu/gitops-argocd_CI/Dockerfile -t ${APP_NAME}:${IMAGE_VERSION}  /home/ubuntu/gitops-argocd_CI/"
                    }
                }
            }
        }

        stage('Build image tagging'){
            steps{
                sshagent(['ansible_server']) {
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  cd /home/ubuntu/"
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image tag ${APP_NAME}:v1.$BUILD_ID pjj521/${APP_NAME}:${IMAGE_VERSION} "
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image tag ${APP_NAME}:v1.$BUILD_ID pjj521/${APP_NAME}:latest"
                }
            }
        }        

        stage('push image to dockerhub'){
            steps{
                script{
                    sshagent(['ansible_server']) {
                        withCredentials([string(credentialsId: 'dockerPWD', variable: 'dockerhub_password')]) {
                            sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111 docker login -u ${DOCKERHUB_USERNAME} -p $dockerhub_password"
                            sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image push  ${DOCKERHUB_USERNAME}/${APP_NAME}:${IMAGE_VERSION} "
                            sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker image push  ${DOCKERHUB_USERNAME}/${APP_NAME}:latest"
                        }
                    }
                }
            }
        }
    }

    post {
        always{
            script{
                sshagent(['ansible_server']) {
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@3.27.188.111  docker rmi -f ${APP_NAME}:${IMAGE_VERSION} "
                }
            }
        }
    }
}

注意:

  1. docker image build的时候必须都是小写
  2. post 最终处理,需要在stages外面写,这样全部stages运行完毕,可以进行处理
  3. 删除/home/ubuntu里的文件一定要小心,里面有隐藏文件
  4. 经常重复的信息,可以参数化
  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值