前言:
有一段时间没有更新博客了,今天给大家分享一下如何将一个python项目成功部署并运行到K8S环境,特做一个记录
准备工作
1. 编写一个python项目,我这边提供的一个Flask服务,提供接口的mock能力。(项目里面编写如下文件)
- dockerfile
- jenkinsfile
- deploy文件夹(内含: deploy.yaml service.yaml ingress.yaml)
流程简释:
jenkinsFile 执行流水线语法---打包docker镜像---把镜像发布到K8s环境(这里用到了deploy的三个文件: deploy,servic,ingress)
必备文件介绍
1. jenkinsFile
@Library('devops') 指代的是调用的K8S 第三方库(这个用流水线的公司都会提供,发布是基于这个库进行)
agent 指代发布的机器使用的是集群里面的slave 标签的机器
options 指代jenkins 的一些配置,例如30分钟超时,连接的代码库是(SVN、git、gitlab等等)
env 指代发布的时候使用到的一些环境变量,包括发布分支,镜像路径等信息
stage 指代jenkins发布的各个阶段
@Library('devops') _
String BUILD_RESULT = ""
String RELEASE_BUILD=""
pipeline {
agent {
label 'slave'
}
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
disableConcurrentBuilds()
skipDefaultCheckout()
timeout(time: 30, unit: 'MINUTES')
gitLabConnection('gitlab')
}
environment {
IMAGE_CREDENTIALS = "credential-harbor"
NOTIFY_ACCOUNT= "123456"
DEV_BRANCH="dev"
QA_BRANCH="v.*"
IMAGE_REPOSITORY = "harbor.123.cn/test/mock"
BUILD_CONTEXT="build"
}
stages {
stage('Checkout') {
steps {
script {
container('tools') {
// checkout code
retry(2) { scmVars = checkout scm }
env.RELEASE_BUILD = scmVars.GIT_COMMIT
BUILD_RESULT = devops.updateBuildTasks(BUILD_RESULT,"Checkout OK...√")
echo 'begin checkout...'
echo sh(returnStdout: true, script: "env")
}
}
}
}
stage('build-mock-image') {
steps {
script {
container('tools') {
retry(2) {
sh """
mkdir -p ${BUILD_CONTEXT};
"""
}
devops.dockerBuild(
"Dockfile", //Dockerfile
".", // build context
"${IMAGE_REPOSITORY}", // repo address
env.RELEASE_BUILD, // tag
IMAGE_CREDENTIALS, // credentials for pushing
).start().push()
}
}
}
}
stage('deploy-mock') {
when {
expression { BRANCH_NAME ==~ env.DEV_BRANCH || BRANCH_NAME ==~ env.QA_BRANCH }
}
steps {
script {
container('tools') {
//create configmap and ingress
devops.deploy("", "deploy/ingress.yaml","",false).start()
devops.deploy(
"deploy", //k8s files dir
"deploy/deploy.yaml",
RELEASE_BUILD,
true
).start()
}
}
}
}
}
post {
success {
script {
container('tools') {
devops.notificationSuccess("mock", "流水线完成了", RELEASE_BUILD, "dingTalk")
}
}
}
failure {
script {
container('tools') {
devops.notificationFailed("mock", "流水线失败了", RELEASE_BUILD, "dingTalk")
}
}
}
}
}
2. dockerFile 【制作docker镜像文件,也是一个示范,实际情况实际分析】
基础镜像使用 -silim 能有效缩减镜像大小 (700-900Mb 缩小到200多Mb 的区别)
ADD ./requirements.txt /src/requirements.txt
RUN pip --default-timeout=30 install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt
这里先把依赖文件复制过去,后续会读取这个缓存文件进行依赖安装,如果你的requirements 没有改动,则不会触发下载安装,能有效减少发布时间(很多公司的linux机器网络速度很慢)
后面指定了国内镜像,也是为了加快依赖的下载速度
运行项目可以不执行,因为你发布到K8S, 在deploy.yaml里面一定要执行镜像运行方式
注意:
获取requirements文件的方法: pipreqs . (pip install pipreqs)
# 基于的基础镜像
FROM python:3.7.5-slim
#制作者信息
MAINTAINER XXXX
#设置工作目录
WORKDIR /src
# 利用缓存安装依赖,设置requirements
ADD ./requirements.txt /src/requirements.txt
RUN pip --default-timeout=30 install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt
# 复制项目到镜像工作目录
ADD . /src
# 给镜像下的项目文件赋权
RUN chmod a+x /src/*
# 运行项目
CMD python /src/run.py
3. deploy.yaml 【K8S的入口文件,也是核心发布文件,必需】
namespace 你想发布到K8S的 命名空间
app:mock 你发布的项目名称(在k8s里面显示的deploy名称)
command: ["/bin/sh"]
args: ["-c","python /src/run.py"]
----------程序运行命令, 指的是在镜像里面执行的操作,注意python和java的区别即可。
containerPort 容器暴露的访问端口,注意这是容器内的,随便定义,但是要保证service、ingress 里面保持一致性,程序启动端口也要用这个。否则无法访问
resources 设置了内存和CPU的限制, 这里按机器的实际情况进行设置
image 镜像名称
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: {{NAMESPACE}}
name:mock
labels:
app:mock
spec:
minReadySeconds: 10
progressDeadlineSeconds: 20
strategy:
rollingUpdate:
maxSurge: 1
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app:mock
template:
metadata:
labels:
app:mock
spec:
dnsConfig:
options:
- name: single-request-reopen
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: {{NODE_LABEL_KEY}}
operator: In
values:
- "{{NODE_LABEL_VAL}}"
restartPolicy: Always
volumes:
containers:
- name: mock
image: {{imageUrl}}
imagePullPolicy: IfNotPresent
command: ["/bin/sh"]
args: ["-c","python /src/run.py"]
ports:
- containerPort: 9900
resources:
requests:
memory: "1024Mi"
cpu: "500m"
limits:
memory: "8192Mi"
cpu: "4000m"
4. service.yaml 【K8S提供集群内服务访问的文件,开放服务给其他项目调用,必需】
K8S集群调用方式: mock:9900 即---- serviceName:port
kind: Service
apiVersion: v1
metadata:
name: mock
namespace: {{NAMESPACE}}
spec:
selector:
app: mock
ports:
- protocol: TCP
port: 9900
targetPort: 9900
5. ingress.yaml 【K8S集群提供的外部访问域名文件,用于外部用户通过域名访问服务,非必需】
这里我加入了tls 配置(HTTPS证书访问),secretName为K8S集群里面已存在的证书名称,域名保持一致即可
注意服务名和端口需要和service.yaml对应,即可通过域名访问到服务,如下,通过 mock.test.cn 即可访问mock服务了。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mock
namespace: {{NAMESPACE}}
spec:
tls:
- hosts:
- mock.test.cn
secretName: test-cn
rules:
- host: mock.test.cn
http:
paths:
- backend:
serviceName: mock
servicePort: 9900
path: /
三、可能遇到的问题
1. Nginx 报错 :
解决: python程序、deploy、service、ingress的端口保持一致性
另外: Flask 暴露的HOST=“0.0.0.0”
[error] *97421558 connect() failed (111: Connection refused) while connecting to upstream, client: 1.1.1.1, server:mock.test.cn, request: "GET /api?taskId=1234 HTTP/1.1", upstream: "http://127.0.0.0:9900/api-waf?taskId=1234", host: "mock.test.cn"
2. 发布到不同分支:
确保你在jenkinsFile 设置了不同分支的名称, 例如你从 dev分支打包, tag是v1.0.0 那么用正则表达式: dev|v.* 可同步发布到test和dev环境