跳到主要内容

6 篇博文 含有标签「CICD」

查看所有标签

Jenkins Pipeline 构建

· 阅读需 6 分钟

Jenkins pipeline 构建

Jenkins Pipeline 有几个核心概念:

  • Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行环境,比如我们之前动态运行的 Jenkins Slave 就是一个 Node 节点
  • Stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如:Build、Test、Deploy,Stage 是一个逻辑分组的概念,可以跨多个 Node
  • Step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh 'make',就相当于我们平时 shell 终端中执行 make 命令一样。

如何创建 Jenkins Pipline 呢?

  • Pipeline 脚本是由 Groovy 语言实现的,但是我们没必要单独去学习 Groovy,当然你会的话最好
  • Pipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法
  • Pipeline 也有两种创建方法:可以直接在 Jenkins 的 Web UI 界面中输入脚本;也可以通过创建一个 Jenkinsfile 脚本文件放入项目源码库中
  • 一般我们都推荐在 Jenkins 中直接从源代码控制(SCMD)中直接载入 Jenkinsfile Pipeline 这种方法

安装插件

  • Pipeline

  • Kubernetes (能够动态的生成 Slave 的 Pod)

  • Pipeline: Stage View Plugin (可视化构建)

Slave 构建任务

实例:

# 添加 Slave Pod 时,添加的 label 给 node 添加一个 lable 属性
node('salve-jnlp') {
stage('Clone') {
echo "1.Clone Stage"
}
stage('Test') {
echo "2.Test Stage"
}
stage('Build') {
echo "3.Build Stage"
}
stage('Deploy') {
echo "4. Deploy Stage"
}
}

构建前:

$ kubectl get pods -n kube-ops
NAME READY STATUS RESTARTS AGE
jenkins-bb4b795c5-wkpkq 1/1 Running 3 (6h59m ago) 3d23h
......

构建后:

$ kubectl get pods -n kube-ops
NAME READY STATUS RESTARTS AGE
jenkins-agent-8x175 1/1 ContainerCreating 0 0s
jenkins-bb4b795c5-wkpkq 1/1 Running 3 (6h59m ago) 3d23h
......

构建完成后:

这个 agent pod 就不在了

部署 kubernetes 应用

node('slave-jnlp') {
stage('Clone') {
echo "1.Clone Stage"
}
stage('Test') {
echo "2.Test Stage"
}
stage('Build') {
echo "3.Build Docker Image Stage"
}
stage('Push') {
echo "4.Push Docker Image Stage"
}
stage('YAML') {
echo "5.Change YAML File Stage"
}
stage('Deploy') {
echo "6.Deploy Stage"
}
}
使用 podTemplate 定义个阶段容器
Clone 代码 -> 单元测试 -> Golang 编译打包 -> Docker 镜像构建/推送 -> Kubectl 部署服务。

使用 kubectl 需提前将目录 /root/.kube 挂载到容器的 /root/.kube 目录下面,才可以使用 kubectl

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

这里写了一段很简单的定义 podTemplate

def label = "slave-${UUID.randomUUID().toString()}"

podTemplate(label: label, containers: [
containerTemplate(name: 'golang', image: 'golang:1.18.3-alpine3.16', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true)
], serviceAccount: 'jenkins', volumes: [
hostPathVolume(mountPath: '/home/jenkins/.kube', hostPath: '/root/.kube')
], envVars: [
envVar(key: 'DOCKER_HOST', value: 'tcp://docker-dind:2375') // 环境变量
]) {
node(label) {
def myRepo = checkout scm
def gitCommit = myRepo.GIT_COMMIT
def gitBranch = myRepo.GIT_BRANCH

stage('单元测试') {
echo "测试阶段"
}
stage('代码编译打包') {
container('golang') {
echo "代码编译打包阶段"
}
}
stage('构建 Docker 镜像') {
container('docker') {
echo "构建 Docker 镜像阶段"
}
}
stage('运行 Kubectl') {
container('kubectl') {
echo "查看 K8S 集群 Pod 列表"
sh "kubectl get pods"
}
}
}
}

直接贴一个完整的 Jenkinsfile ,有需要直接改动吧
def label = "slave-${UUID.randomUUID().toString()}"

def helmLint(String chartDir) {
println "校验 chart 模板"
sh "helm lint ${chartDir}"
}

def helmDeploy(Map args) {
if (args.debug) {
println "Debug 应用"
sh "helm upgrade --dry-run --debug --install ${args.name} ${args.chartDir} -f ${args.valuePath} --set image.tag=${args.imageTag} --namespace ${args.namespace}"
} else {
println "部署应用"
sh "helm upgrade --install ${args.name} ${args.chartDir} -f ${args.valuePath} --set image.tag=${args.imageTag} --namespace ${args.namespace}"
echo "应用 ${args.name} 部署成功. 可以使用 helm status ${args.name} 查看应用状态"
}
}

podTemplate(label: label, containers: [
containerTemplate(name: 'golang', image: 'golang:1.14.2-alpine3.11', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'helm', image: 'cnych/helm', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true)
], serviceAccount: 'jenkins', envVars: [
envVar(key: 'DOCKER_HOST', value: 'tcp://docker-dind:2375') // 环境变量
]) {
node(label) {
def myRepo = checkout scm
// 获取 git commit id 作为镜像标签
def imageTag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
// 仓库地址
def registryUrl = "harbor.k8s.local"
def imageEndpoint = "course/devops-demo"
// 镜像
def image = "${registryUrl}/${imageEndpoint}:${imageTag}"

stage('单元测试') {
echo "测试阶段"
}
stage('代码编译打包') {
try {
container('golang') {
echo "2.代码编译打包阶段"
sh """
export GOPROXY=https://goproxy.cn
GOOS=linux GOARCH=amd64 go build -v -o demo-app
"""
}
} catch (exc) {
println "构建失败 - ${currentBuild.fullDisplayName}"
throw(exc)
}
}
stage('构建 Docker 镜像') {
withCredentials([[$class: 'UsernamePasswordMultiBinding',
credentialsId: 'docker-auth',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASSWORD']]) {
container('docker') {
echo "3. 构建 Docker 镜像阶段"
sh """
cat /etc/resolv.conf
docker login ${registryUrl} -u ${DOCKER_USER} -p ${DOCKER_PASSWORD}
docker build -t ${image} .
docker push ${image}
"""
}
}
}
stage('运行 Helm') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
container('helm') {
sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
echo "4.开始 Helm 部署"
def userInput = input(
id: 'userInput',
message: '选择一个部署环境',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "Dev\nQA\nProd",
name: 'Env'
]
]
)
echo "部署应用到 ${userInput} 环境"
// 选择不同环境下面的 values 文件
if (userInput == "Dev") {
// deploy dev stuff
} else if (userInput == "QA"){
// deploy qa stuff
} else {
// deploy prod stuff
}
helmDeploy(
debug : false,
name : "devops-demo",
chartDir : "./helm",
namespace : "kube-ops",
valuePath : "./helm/my-values.yaml",
imageTag : "${imageTag}"
)
}
}
}
stage('运行 Kubectl') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
container('kubectl') {
sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
echo "5.查看应用"
sh "kubectl get all -n kube-ops -l app=devops-demo"
}
}
}
stage('快速回滚\?') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
container('helm') {
sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
def userInput = input(
id: 'userInput',
message: '是否需要快速回滚?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "Y\nN",
name: '回滚\?'
]
]
)
if (userInput == "Y") {
sh "helm rollback devops-demo --namespace kube-ops"
}
}
}
}
}
}

Harbor 安装部署

· 阅读需 1 分钟

Harbor 安装部署

使用docker-compose安装

docker-compose 安装

下载安装包

wget https://github.com/docker/compose/releases/download/1.28.6/docker-compose-Linux-x86_64

授权并移动

chmod +x docker-compose-Linux-x86_64
mv docker-compose-Linux-x86_64 /usr/local/sbin/docker-compose

查看是否生效

docker-compose version

docker-py version: 4.4.4
CPython version: 3.7.10
OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019

Harbor 安装

下载 解压安装包

wget https://github.com/goharbor/harbor/releases/download/v2.2.2/harbor-offline-installer-v2.2.2.tgz

tar -zxvf harbor-offline-installer-v2.2.2.tgz

修改配置

cp harbor.yml.tmpl harbor.yml

vim harbor.yml

hostname: harbor
harbor_admin_password: harbor12345 # admin的密码
#如果不用443,可以注释掉 https 那个模块
data_volume: /data # 存储路径

安装并验证

# 执行脚本安装
sh install.sh

# 验证
docker ps -a

Gitlab 安装部署

· 阅读需 1 分钟

Gitlab 安装部署

下载 rpm 包 https://mirrors.tuna.tsinghua.edu.cn/

安装Gitlab

rpm -ivh xxx.rpm
# 出现 NOKEY 的问题是因为依赖问题,此处使用强制安装

rpm -ivh xxx.rpm --force --nodeps

修改配置文件

vim /etc/gitlab/gitlab.rb

#修改文件中的
external_url 'http://地址:端口'

初始化

# 初始化gitlab配置信息并启动每一个组件
gitlab-ctl reconfigure

gitlab常用命令

gitlab-ctl start:启动gitlab,
gitlab-ctl stop:停止gitlab。
gitlab-ctl status:查看gitlab状态
gitlab-ctl restart:重启服务