jenkinsfile:用代码部署流水线
groovy语法 pipeline
// We use current date for image/helm versioning.
import java.text.SimpleDateFormat
// Pipeline root
pipeline {
agent any
// Define some project-dependent variables 项目依赖变量
environment {
// OCP endpoint
OCP_API_SERVER_URL =
// OCP login credential
CREDENTIALS_ID_OCP = 'ocp-rikchong'
CREDENTIALS_OCP = credentials("${CREDENTIALS_ID_OCP}")
// Namespace of your app
APP_NAMESPACE = 'boc-app'
// App target environment: test/prod
APP_ENV = 'prod'
// SCM url for your app
GIT_URL =
GIT_BRANCH = 'master'
// SCM credential
CREDENTIALS_ID_GIT =
// Helm package root
HELM_PATH = 'cicd/k8s/helm'
// Helm chart release name
HELM_RELEASE_NAME = 'boc-app'
// Timeout settings
APP_STARTUP_WAIT_TIMEOUT = 20
OCP_BUILD_CONFIG_NAME = 'boc-app'
}
stages {
// OCP login
stage('OCP login') {
steps {
script {
timeout("$APP_STARTUP_WAIT_TIMEOUT") {
waitUntil {
def test = sh (
script: "oc login ${OCP_API_SERVER_URL} -u ${CREDENTIALS_OCP_USR} -p ${CREDENTIALS_OCP_PSW} -n ${APP_NAMESPACE} --insecure-skip-tls-verify",
returnStatus: true)
return test == 0
}}
sh "oc project ${APP_NAMESPACE}"
}}}
// Apply build config && trigger build
stage('SCM Checkout') {
steps {
script {
def scmVars = checkout([
$class: 'GitSCM',
branches: [[name: "$GIT_BRANCH"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
credentialsId: "$CREDENTIALS_ID_GIT",
url: "$GIT_URL" ]] ]) } } }
// Apply build config && trigger build
stage('OCP Build') {
steps {
script {
// Check for running builds
def running = sh(
script: "oc get build -l openshift.io/build-config.name=$OCP_BUILD_CONFIG_NAME | awk '{ print \$4 }' | grep -E 'Pending|Running|New' | wc -l",
returnStdout: true
).trim()
println "current building task num:" + running //IMAGE_TAG
def sdf = new SimpleDateFormat("yyMMddHHmmss") sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")) env.IMAGE_TAG = "r" + sdf.format(new Date()) //build in OCP
def lastBuild = ""
if (running == "0") {
println "====ocp start build ..."
sh "cat cicd/buildconfig/$APP_ENV/buildconfig.yaml" sh "sed 's/app_image_tag/'$IMAGE_TAG'/' cicd/buildconfig/$APP_ENV/buildconfig.yaml"
sh "sed 's/app_image_tag/'$IMAGE_TAG'/' cicd/buildconfig/$APP_ENV/buildconfig.yaml | oc apply -f -"
sh "oc start-build $OCP_BUILD_CONFIG_NAME" }
lastBuild = sh(
script: "oc get bc ${OCP_BUILD_CONFIG_NAME} -o jsonpath={.status.lastVersion}", returnStdout: true
)
lastBuild = "${OCP_BUILD_CONFIG_NAME}-" + lastBuild
println "====> build name: " + lastBuild
//check build result
def buildResult = ""
timeout(20) {
waitUntil(quiet: true) {
buildResult = sh(
script: "oc get build " + lastBuild + " -o jsonpath={.status.phase}", returnStdout: true
).trim()
return buildResult == 'Complete' || buildResult == 'Failed' } }
if (buildResult != 'Complete') {
currentBuild.result = 'ABORTED'
def buildLog = sh(
script: "oc logs " + lastBuild + "-build",
returnStdout: true
)
error("====> build fail OCP buildconfig buildLog:\n" + buildLog) }
//get src version
def gitCommit = sh(
script: "oc get build " + lastBuild + " -o jsonpath={.spec.revision.git.commit} | cut -b 1-8",
returnStdout: true
).trim()
println "====> build completed git commit:" + gitCommit
env.REVISION_AND_DATE = env.IMAGE_TAG + "-" + gitCommit
env.RELEASE_LABEL = "released=" + env.REVISION_AND_DATE
env.RELEASE_LABEL_YAML = "released: " + env.REVISION_AND_DATE env.CHART_VERSION = "1.0.0-" + gitCommit + "." + sdf.format(new Date()) } } }
// Deploy application
stage('App deploy') {
steps {
script {
// Create a 'Chart.yaml' from Chart.template.yaml, by replacing version number. sh "sed 's/version: .*/version: ${CHART_VERSION}/' ${HELM_PATH}/Chart.template.yaml > ${HELM_PATH}/Chart.yaml"
// Create a 'values.yaml' from values-<APP_ENV>.yaml, by replacing image.tag (witch we just built)
// For example, we just pushed image myclusterd.icp:8500/tgxt/tgxt:r190709, now we need to tell helm chart with tag r190709.
sh "sed 's/tag: app-default-tag/tag: ${IMAGE_TAG}/' ${HELM_PATH}/values-${APP_ENV}.yaml > ${HELM_PATH}/values.yaml"
// Test our chart package
sh "helm lint --strict $HELM_PATH"
sh "helm upgrade ${HELM_RELEASE_NAME} ${HELM_PATH} --install" } } }
// Application initialization
stage('Check application') {
steps {
script {
// Find first available pod
def timeoutValue = "$APP_STARTUP_WAIT_TIMEOUT"
def pod
timeout(timeoutValue) {
waitUntil {
sh 'oc get po -l app.kubernetes.io/instance=${HELM_RELEASE_NAME},helm.sh/version=${CHART_VERSION}'
// Find pod id
pod = sh (
script: 'oc get po -l app.kubernetes.io/instance=${HELM_RELEASE_NAME},helm.sh/version=${CHART_VERSION} 2>/dev/null | grep "Running" | awk \'{ print $1 }\'', returnStdout: true
).trim()
return pod != "" } } println 'Found running pod: ' + pod + ', pipeline done!' } } } }}
(复制过来的,缩进有点乱)
values.prod 一些赋值
# Default values for helm.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
env: prod
replicaCount: 1
image:
repository:
tag: app-default-tag
pullPolicy: Always
pullSecrets: ""
nameOverride: ""
fullnameOverride: ""
memcached:
enabled: false
containerName: memcached
port: 11211
image:
repository:
tag: latest
pullPolicy: IfNotPresent
service:
type: NodePort
ports:
- targetPort: 8080
port: 8080
nodePort: ""
protocol: TCP
name:
route:
enabled: true
host:
targetPort: 8080
annotations:
openshift.io/host.generated: true
ingress:
enabled: true
annotations:
ingress.kubernetes.io/proxy-body-size: 200m
paths:
- path: /
servicePort: 8080
hosts:
-
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
persistence:
enabled: true
## A manually managed Persistent Volume and Claim
## Requires persistence.enabled: true
## If defined, PVC must be created manually before volume will be bound
existingClaim: dg-prac-pvc
## jenkins data Persistent Volume Storage Class
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
##
storageClass: gpfs-storage-csi
annotations: {}
accessMode: "ReadWriteMany"
size: "10Gi"
mounts:
- mountPath: /opt/logs
name:
claimName:
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
hostAliases: {}
buildconfig.yaml:
kind: BuildConfig
apiVersion: build.openshift.io/v1
metadata:
name:
spec:
nodeSelector: null
output:
to:
kind: DockerImage
name:
pushSecret:
name: registry
resources:
limits:
cpu: '1'
memory: 2Gi
successfulBuildsHistoryLimit: 5
failedBuildsHistoryLimit: 5
strategy:
type: Source
sourceStrategy:
from:
kind: DockerImage
name:
pullSecret:
name: registry
env:
- name: ANT_ARGS
value:
- name: APP_ENV
value: prod
- name: APP_DIR
value: cicd/k8s/docker/app
postCommit: {}
source:
type: Git
git:
uri:
ref: master
sourceSecret:
name:
#此配置不要 会生成两个节点
# triggers:
# - type: ConfigChange
runPolicy: Serial
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "helm.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "helm.name" . }}
helm.sh/chart: {{ include "helm.chart" . }}
helm.sh/version: "{{ .Chart.Version }}"
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "helm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "helm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
helm.sh/version: "{{ .Chart.Version }}"
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
{{- range .Values.service.ports }}
- containerPort: {{ .targetPort }}
protocol: {{ .protocol }}
{{- end }}
{{- if .Values.persistence.enabled }}
volumeMounts:
{{- range .Values.persistence.mounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
{{- end }}
{{- end }}
env:
- name: STARTUP_CMD
value: /opt/ibm/wlp/bin/server run app
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- if .Values.memcached.enabled }}
- name: {{ .Values.memcached.containerName }}
imagePullPolicy: {{ .Values.memcached.image.pullPolicy }}
image: "{{ .Values.memcached.image.repository }}:{{ .Values.memcached.image.tag }}"
ports:
- containerPort: {{ .Values.memcached.port }}
protocol: TCP
{{- end }}
{{- if .Values.image.pullSecrets }}
imagePullSecrets:
- name: {{ .Values.image.pullSecrets }}
{{- end }}
{{- if .Values.persistence.enabled }}
volumes:
{{- range .Values.persistence.mounts }}
- name: {{ .name }}
persistentVolumeClaim:
claimName: {{ .claimName }}
{{- end }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.hostAliases }}
hostAliases:
{{- toYaml . | nindent 8 }}
{{- end }}
service.yaml:
apiVersion: v1
kind: Service
metadata:
name: {{ include "helm.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "helm.name" . }}
helm.sh/chart: {{ include "helm.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
{{- range .Values.service.ports }}
- port: {{ .port }}
targetPort: {{ .targetPort }}
protocol: {{ .protocol}}
name: {{ .name}}
{{- if .nodePort }}
nodePort: {{ .nodePort }}
{{- end }}
{{- end }}
selector:
app.kubernetes.io/name: {{ include "helm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
ingress.yaml:
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "helm.fullname" . -}}
{{- $ingressPaths := .Values.ingress.paths -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app.kubernetes.io/name: {{ include "helm.name" . }}
helm.sh/chart: {{ include "helm.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . | quote }}
http:
paths:
{{- range $ingressPaths }}
- path: {{ .path }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ .servicePort }}
{{- end }}
{{- end }}
{{- end }}
pvc.yaml:
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) -}}
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
{{- if .Values.persistence.annotations }}
annotations:
{{ toYaml .Values.persistence.annotations | indent 4 }}
{{- end }}
name: {{ template "helm.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "helm.name" . }}
helm.sh/version: "{{ .Chart.Version }}"
helm.sh/chart: {{ include "helm.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- if .Values.persistence.storageClass }}
{{- if (eq "-" .Values.persistence.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistence.storageClass }}"
{{- end }}
{{- end }}
{{- end }}
注:仍需要在ocp上手动新建sourcesecret,
猜测原因:新建sourcesecret不是每次部署都需要进行的操作?sourcesecret里包含了账号密码,不能明文写在代码里?