• 【Devops】 发布一个Python项目(Flask服务后端)到K8S环境


    前言:

    有一段时间没有更新博客了,今天给大家分享一下如何将一个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环境

  • 相关阅读:
    [转]laravel 4之视图及Responses
    Laravel 安装指南
    [转]CodeIgniter与Zend Acl结合实现轻量级权限控制
    OSCHina技术导向:Java电子商务平台OFBiz
    OSCHina技术导向:Java开源QQ工具iQQ
    OSCHina技术导向:web内容管理系统Magnolia
    OSCHina技术导向:Java轻量web开发框架——JFinal
    OSCHina技术导向:Java模板引擎velocity
    OSCHina技术导向:Java全文搜索框架Lucene
    OSChina技术导向:Java图表框架JFreeChart
  • 原文地址:https://www.cnblogs.com/Ronaldo-HD/p/14345904.html
Copyright © 2020-2023  润新知