前言:
有一段時間沒有更新博客了,今天給大家分享一下如何將一個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環境