前言:

有一段时间没有更新博客了,今天给大家分享一下如何将一个python项目成功部署并运行到K8S环境,特做一个记录

 

准备工作

1. 编写一个python项目,我这边提供的一个Flask服务,提供接口的mock能力。(项目里面编写如下文件)

  1. dockerfile
  2. jenkinsfile
  3. 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环境