我一个写Java的,怎么就开始玩K8s和Jenkins了?!
前几天接到一个新任务,要求把以前部署在私有服务器上的项目,全都搬到云端去部署。之前的发布流程其实挺简单的,都是在本地打包好,然后通过文件传输把打好的jar包或者前端编译好的文件夹,直接替换到服务器上。挺传统也挺直接的。
但这次不一样了,老板希望上线流程能更自动化,得用Jenkins实现一键部署,减少人工操作,省时省力。说实话,对于我这开发来说,哪干过这活啊。都是已经做好的流水线直接用就行了,奈何人手不够用,直接让我上。
接下来,我们就一步步来摸索、学习一下,看看从私有服务器迁移到云端部署,到实现Jenkins一键发布,这其中会遇到哪些坑,哪些需要特别注意的地方。
另外要提醒的是,每家公司的基础架构环境和要求都不太一样,下面说的内容更多是给新手做个参考,帮助理解和入门,具体操作还是得结合自己公司的实际情况来调整。
名词解释
基础问题
首先我们先来搞清楚一些最基础的概念。
ACR,其实说白了就是一个私有的 Docker 镜像仓库。这个是某里云那边的叫法(Alibaba Cloud Registry),但其实每个云厂商都有类似的服务,比如腾讯云都有,只是名字不同而已。核心作用都是一样的,就是用来存储你打好的 Docker 镜像。
为什么要用私有仓库呢?因为你不可能把你们公司的服务包发布到 Docker Hub 这种公共仓库上去,那样既不安全,也不符合公司规范。所以我们会把自己的镜像推送到 ACR 这种私有仓库里,之后部署服务时就从这里拉取镜像。
再来说说 ACK,这也是阿里云的一个产品,全称是 Alibaba Cloud Kubernetes。它其实就是一个托管化的容器服务,换句话说,它帮你把 Kubernetes 集群搭建、管理那一整套繁琐的操作都封装好了。你只需要关注怎么把你的服务跑起来,而不用操心集群、节点、控制面这些底层的东西。
接下来我给大家讲一下 ACK 里面的一些基本概念和内容,帮你更好地理解它是怎么工作的。
首先,命名空间,你可以把它简单理解成“文件夹”这种东西。它的作用就是帮你更方便地管理你运行的 Docker 镜像和服务。就好比你电脑里的文件夹,把不同的东西分类放好,查找和维护起来也会更清晰明了。
然后是无状态节点和有状态节点,这两者的区别主要在于存储方面。无状态节点就是说它们不保存数据,节点被销毁了,数据也没了,重新启动就像全新的一样。反过来,有状态节点是支持数据持久化的,可以绑定存储,数据不会丢失。像我们平时做的微服务,大多数都是无状态的,因为它们可以随时销毁重建,不需要担心数据丢失。你也可以通过它们查看访问方式,比如服务的暴露和路由规则这些。
再说说容器组,你可以把它想象成一个服务的“集合”。一个服务通常会有多个副本节点,也就是多个相同的容器同时运行。容器组就是把这些副本集中管理的地方,你可以在这里看到每个副本节点的运行状态,还有它们的日志信息,方便你排查问题或者监控运行情况。
网络
服务(service)
他有好多种类型。他的目的是直接管理某个微服务的所有节点副本。
ClusterIP:只在当前集群的“虚拟网络”里生效,任何外部实体(不管是另一个集群、还是你办公室的笔记本)都访问不到。→ 相当于“房门只在屋里开”。
NodePort:把服务端口映射到“节点所在的那一层网络”。如果节点是私有网段(10.x/172.x/192.168.x),且没做额外打通 → 外部/另一个集群照样访问不到。如果节点是公网 IP,或两个集群的 VPC/局域网路由/对等连接已打通 → 外部/另一个集群就能通过 <节点IP>:NodePort 访问。→ 相当于“房门开到走廊,走廊能不能走到,取决于你俩在没在同一条走廊里”。
LoadBalancer:云厂商帮你额外申请一个公网负载均衡 IP(或内网 SLB + 公网绑定),无论节点本身有没有公网 IP,都会给你一个可直接在公网访问的地址。→ 相当于“直接在大楼门口给你挂一个独立门牌号,任何人都能按门铃”。
路由(Ingress)
同一个公网入口 IP + 端口,根据域名/路径把流量分到不同的后端 Service,这个没啥讲的,你就完全可以把他当成nginx来看就行。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nacos3-ingress
namespace: common
labels:
app: nacos3
component: ingress
ingress-controller: nginx
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /nacos-demo(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: nacos3
port:
number: 8080
当然并不是所有的路径分配都要写到一个Ingress里面,就像nginx我们也会把各个项目的配置文件分开一样,分开声明就可以。
配置管理
配置项
这个你是你Java项目的application配置文件。只不过可以单独配置,不过我们基本上都是使用Nacos进行管理,不用这个,用它的都是公共组件,比如xxljob这种的。
# 配置文件
apiVersion: v1
kind: ConfigMap
metadata:
name: xxl-job-config
namespace: common
labels:
app: xxl-job
version: "3.2.0"
data:
application.properties: |
### web
server.port=8080
server.servlet.context-path=/xxl-job-admin
界面查看查看效果如图所示:
保密字典
我们一般会把数据库的密码啊,或者一些加密文件什么的,都放在这个地方。最常见的用途,比如你要从 ACR 拉取镜像,这时候就需要提前把账号和密码配置好。不太一样的是,这里的 Secret 类型不能用普通的 Opaque(如图)。
而是要用 kubernetes.io/dockerconfigjson 这个类型,专门用来存储镜像仓库的认证信息。
apiVersion: v1
data:
.dockerconfigjson: >-
eyJhdXRocyI6eyJiaG1jLWFjci1zY3JtLXJlZ2lzdHCSE1DLVNDUk0tVCIsInBhc3N3b3JkIjoiQmhtY3Njcm0jMDEiLCJhdXRoIjoiWVdOeVFFSklUVU10VTBOU1RTMVVPa0pvYldOelkzSnRJekF4In19fQ==
kind: Secret
metadata:
name: acr
namespace: wechat-apps
type: kubernetes.io/dockerconfigjson
dockerconfigjson全是base64生成的,如果不知道初始数据是啥,可以自己在web界面弄完之后,复制出来看下。
存储
这块主要是说给每个服务分配存储空间的事情。之前用 Docker 的时候,操作特别简单,直接挂载一个目录就能用,没啥特别的,服务启动的时候指定个目录,数据就能存到那里。但到了云上环境或者 Kubernetes 里,就不太一样了,这里没法像 Docker 那样直接挂载目录。它们都要求我们先去申请存储资源,再把存储绑定到对应的服务上,不能直接用本地目录的方式。
你可以把它想象成写 Java 代码的过程:先写个类,然后 new 出一个对象,接着才给这个对象设置各种属性值。
存储类
这步就是先得有类。你具体要使用什么类型的磁盘,某里云上你都可以看到。你要是使用的话,必须声明才可以。
storageClassName: alicloud-disk-ssd
这个是已经存在的资源,不用我们创建。当然我就不使用命令了,因为我也得先查,web界面操作如下:
点击后,你就能看到各个类型了。
存储卷
这时候你要开始new对象了,先写明你要什么多大容量的磁盘。但具体给谁用,存储卷不知道。这只是一个属性值而已。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nacos3-data-dev
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: alicloud-disk-ssd
hostPath:
path: /mnt/nacos3-data/demo
申请好后,会显示在界面上。
存储声明
这个就是设置属性值的时候了。直接给存储卷绑定上,这样整个流程就算完活了。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nacos3-pvc
namespace: common
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
volumeName: pv-nacos3-data-dev
storageClassName: alicloud-disk-ssd
这个存储就可以给你的服务使用了。
volumes:
- name: nacos3-storage
persistentVolumeClaim:
claimName: nacos3-pvc
- name: nacos3-logs
persistentVolumeClaim:
claimName: nacos3-logs-pvc
环境配置
首先,云上环境的开通会需要一点时间,这一块主要是由甲方来负责操作的。你不用太担心,后续他们会把所有相关的信息都整理好并提供给你。比如账号的登录信息、ACR(容器镜像服务)的账号和密码、ACK(容器服务)的秘钥信息等等。
另外,如果项目中用到了云上的数据库服务,也会一并提供数据库的连接信息。这些内容我们会统一整理到一个表格文档里,方便你后续查阅或复制粘贴使用,避免信息遗漏或重复沟通。
跳板机
接下来说说服务的部署流程,通常情况下,咱们的服务都会部署在内网环境里,也就是说不会直接给你一个公网地址让你访问。所以,你这边必须先申请开通一些防火墙规则,确保必要的网络能通畅。具体来说,主要涉及到以下几个关键点:
首先,要准备一台跳板机。跳板机的作用很简单,就是帮你直接操作云上的ACR(容器镜像仓库)和ACK(容器服务)。如果你对这些名词还不太熟悉,建议赶紧补一下相关知识,这样后续操作会顺利很多。除此之外,这台跳板机还得部署Jenkins,用来做自动化构建和部署的工作。
为了让跳板机能正常工作,必须提前把跳板机所在的网络和GitLab、ACR、ACK这些服务的网络打通。如果条件允许的话,还可以把Maven拉取的国内镜像仓库域名也一起放开,这样构建速度会快很多。如果没办法打通Maven镜像,那你就得手动把jar包上传到Maven私服,操作会麻烦一些。
至于为啥一定要申请跳板机,主要是因为云上的ACR和ACK管理界面操作起来比较繁琐,直接通过界面做一些批量操作或者复杂配置很不方便。有了跳板机,你可以直接通过命令行来执行各种操作,效率会高不少。当然,如果你比较喜欢折腾,也可以选择直接在云服务的Web界面用yaml文件格式来创建和管理资源,这两种方式都可以。
kubectl环境
跳板机需要的预置环境必须要有docker以及kubectl命令,如果没有必须临时开通外网权限自己安装,如果源代码编译你会遇到很多莫名其名的问题。远远超出开发的能力了。
紧接着你还需要配置下kubectl的环境,目的就是可以使用命令操作ack环境。如果你还没有就去下载一下。
然后跟着教程走就行。这部分没啥难度。
docker环境
docker需要提前登录到acr环境才可以正常打包项目并推送过去,不然ack是拉取不到镜像的。命令都会提示给你。acr的命名空间我用来隔离生产和测试环境了。需要提前创建好,不然直接推送到acr是会报错的。
所有的登录信息都会到镜像指南中提供给你,acr的登录账密开通人员也会提供给你。直接按照教程走即可。
Jenkins
接着就是部署jenkins了,因为我们网络基本的打通了。为了方便启动,我直接使用的docker容器启动了jenkins,compose文件如下:
# 定义Compose文件版本
version: '3'
# 定义服务
services:
# 定义名为jenkins的服务
jenkins:
# 指定服务使用的镜像
image: jenkins/jenkins:lts
# 自定义容器名称
container_name: jenkins-2.481
# 设置容器重启策略为始终重启
restart: always
# 给予容器特权权限,允许进行Docker-in-Docker操作
privileged: true
# 定义网络配置
networks:
- jenkins
# 设置容器环境变量
environment:
DOCKER_TLS_CERTDIR: /certs/client
# 定义数据卷映射
volumes:
- /data/jenkins/jenkins-data/certs:/certs/client:ro
# jenkins 数据目录
- /data/jenkins/jenkins-data:/var/jenkins_home
- /data/maven:/root/.m2
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- ~/.docker:/root/.docker:ro
- /usr/local/bin/kubectl:/usr/local/bin/kubectl
- /data/npm-cache:/root/.npm
# 定义端口映射
ports:
- "8080:8080"
# 设置容器运行用户为root,以便有足够权限操作Docker
user: root
# 定义网络
networks:
jenkins:
# 指定网络驱动为桥接模式
driver: bridge
这里我提前把mvn的仓库目录挂载出来了,还有前端编译的缓存包,以及kubectl命令,这样Jenkins就可以操作ack了。好的,接下来就是启动,启动后直接安装推荐的插件。能多安就别少安,毕竟我们只是开发。先确保可以正常跑流水线再说。这部分缺少的插件遇到报错我就安装了,忘记记录了,只要流水线在报错,你就去插件商店安装即可。或者自己手动上传。
账密配置
因为Jenkins需要操作git、acr、ack这些服务,所以账密及秘钥信息都需要提前在jenkins中配置好。如图所示:
进入后,然后点击页面的system,如图所示:
接着你就点击新增就好,如果是账密类型的,你就选择,Username with password,比如ack是配置信息,我直接创建的secret file。
这样基本就可以了。
项目相关
因为每个项目结构都不会有关于dockerfile或者deployment这种k8s文件在结构中,所以这种都是单独在一个项目中,然后复制出来给其他项目使用,gtilab创建好项目后,我这里直接创建一个自由风格项目。如图所示:
然后配置下git仓库地址即可,如图所示:
然后在配置下构建后的操作,如图所示:
这里我将uat目录下的所有文件都归档到工作空间了。这样,你保存进行构建任务时,就会生成一个供其他所有任务使用的文件。如图所示:
剩下的项目就都是流水线项目了。我们直接创建一下。这里你可以使用git管理流水线也可以直接在脚本中写流水线都可以,为了快速验证我这里先用的脚本。
一个项目的脚本如下:
pipeline {
agent any
tools {
maven 'Maven-3.9' // 如果 Global Tool 里叫 Maven-3.9 就用这个
}
environment {
// 所有配置直接硬编码为测试环境
ACR_REGISTRY = '**cr.aliyuncs.com'
APP_NAME = '**'
ACR_NAMESPACE = '**' // 固定使用开发命名空间
// 凭证ID固定
GIT_CRED_ID = 'git'
ACR_CRED_ID = 'acr-credentials' // 需要您创建ACR登录凭证
KUBECONFIG_CRED_ID = 'ack-dev' // 固定使用测试环境的kubeconfig
// 代码库信息固定
GIT_URL = '**.git'
GIT_BRANCH = 'uat-cloud'
// Maven命令
MAVEN_PACKAGE_CMD = 'mvn clean package -Dmaven.test.skip=true'
// 最终镜像名称 (固定模式)
FULL_IMAGE_NAME = "${ACR_REGISTRY}/${ACR_NAMESPACE}/${APP_NAME}:latest"
}
stages {
stage('Checkout Code') {
steps {
echo "开始从 ${GIT_URL} 拉取分支 ${GIT_BRANCH}..."
git credentialsId: GIT_CRED_ID, url: GIT_URL, branch: GIT_BRANCH
}
}
stage('Maven Build') {
steps {
/* 2. 把私服 settings.xml 挂进来 */
withCredentials([file(credentialsId: 'maven-settings-nexus',
variable: 'MVN_SETTINGS')]) {
sh '''
mvn -s $MVN_SETTINGS clean package \
-Dmaven.test.skip=true
'''
}
}
}
stage('Copy Dockerfile') {
steps {
// 把共享 Job 产生的 Dockerfile 复制到当前工作区
copyArtifacts(
projectName: '**',
selector: lastSuccessful(),
target: 'docker-tmp'
)
sh 'cp docker-tmp/uat/docker/Dockerfile .'
}
}
stage('Build & Push Image') {
steps {
script {
// 构建并推送镜像
sh """
docker build -t ${FULL_IMAGE_NAME} \
--build-arg JAR_FILE=target/*.jar .
docker push ${FULL_IMAGE_NAME}
"""
}
}
}
stage('Deploy to ACK') {
steps {
script {
// 把共享 Job 产生的 Dockerfile 复制到当前工作区
copyArtifacts(
projectName: '**',
selector: lastSuccessful(),
target: 'docker-tmp'
)
sh 'cp docker-tmp/uat/k8s/**.yaml .'
// 使用测试环境的kubeconfig进行部署
withCredentials([file(
credentialsId: KUBECONFIG_CRED_ID,
variable: 'KUBECONFIG_FILE'
)]) {
// 更新镜像标签并部署
sh "kubectl --kubeconfig=${KUBECONFIG_FILE} apply -f **.yaml"
// 查看部署状态
sh "kubectl --kubeconfig=${KUBECONFIG_FILE} get pods -l app=${APP_NAME}"
}
}
}
}
}
post {
always {
cleanWs()
}
}
}
小结
总的来说,这次从私有服务器迁移到云端部署,再到 Jenkins 一键发布,确实算是硬着头皮上阵,一路踩坑一路摸索。对我这种写 Java 的来说,平时很少接触这些云原生的东西,什么 ACK、ACR、跳板机、kubeconfig,一开始真的一脸懵。但一步步下来,其实逻辑都不复杂,关键是流程要理清、配置要记牢。希望这篇记录对和我一样“被抓壮丁”的开发同学有点帮助,别像我一样每次都从零开始瞎撞。环境不同、细节不同,但大方向差不多,照着搞肯定能跑起来。愿我们都能少踩点坑,早点下班!
我一个写Java的,怎么就开始玩K8s和Jenkins了?!的更多相关文章
- 写Java也得了解CPU--CPU缓存
CPU,一般认为写C/C++的才需要了解,写高级语言的(Java/C#/pathon...)并不需要了解那么底层的东西.我一开始也是这么想的,但直到碰到LMAX的Disruptor,以及马丁的博文,才 ...
- 我是怎么开发一个小型java在线学习网站的
2016/1/27 11:55:14 我是怎么开发一个小型java在线学习网站的 一直想做一个自己的网站(非博客),但是又不知道做什么内容的好,又一次看到了w3schools,就萌发了开发一个在线ja ...
- 一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库 RxJava,相当好
https://github.com/ReactiveX/RxJava https://github.com/ReactiveX/RxAndroid RX (Reactive Extensions,响 ...
- 读《架构探险——从零开始写Java Web框架》
内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...
- 如何用Maven创建一个普通Java项目
一下内容包括:用Maven创建一个普通Java项目,并把该项目转成IDEA项目,导入到IDEA,最后把这个项目打包成一个jar文件. 有时候运行mvn命令失败,重复运行几次就OK了,无解(可能因为网络 ...
- myeclipse 写java代码提示 dead code 原因
经常使用MyEclipse要么Eclipse编辑写java程序猿代码.您可能经常会遇到一个黄色警戒线:dead code:一般程序猿遇到这些问题都会置之不理,反正也不影响程序的编译运行.对,这不是bu ...
- 手写JAVA虚拟机(二)——实现java命令行
查看手写JAVA虚拟机系列可以进我的博客园主页查看. 我们知道,我们编译.java并运行.class文件时,需要一些java命令,如最简单的helloworld程序. 这里的程序最好不要加包名,因为加 ...
- 手写JAVA虚拟机(三)——搜索class文件并读出内容
查看手写JAVA虚拟机系列可以进我的博客园主页查看. 前面我们介绍了准备工作以及命令行的编写.既然我们的任务实现命令行中的java命令,同时我们知道java命令是将class文件(字节码)转换成机器码 ...
- 一个完整Java Web项目背后的密码
前言 最近自己做了几个Java Web项目,有公司的商业项目,也有个人做着玩的小项目,写篇文章记录总结一下收获,列举出在做项目的整个过程中,所需要用到的技能和知识点,带给还没有真正接触过完整Java ...
- [转]恕我直言,在座的各位根本不会写 Java!
导语 自 2013 年毕业后,今年已经是我工作的第 4 个年头了,总在做 Java 相关的工作,终于有时间坐下来,写一篇关于 Java 写法的一篇文章,来探讨一下如果你真的是一个 Java 程序员,那 ...
随机推荐
- DRF之频率组件源码分析
DRF之频率组件源码分析 [一]频率组件介绍 Django Rest Framework(DRF)中的频率组件是用于限制API端点的访问频率的一种机制. 频率组件可以帮助你控制用户对API的请求频率, ...
- 二、Linux基本应用工具
1.系统文件共享(网络) 通过网络文件共享协议(例如 SMB 或 NFS)来完成Ubuntu下的文件夹共享给 Windows 1.Samba 实现共享 安装samaba sudo apt update ...
- 记一次ADL导致的C++代码编译错误
这篇文章主要讲讲c++的ADL,顺便说说为什么很多c++的IDE都会让你尽量不要include用不上的头文件. 和其他c++文章一样,这篇也会有基础回顾环节,所以不用担心看不懂,但读者最好还是得有c+ ...
- 实现对C语言类学生管理系统文件存储的两种方法
学习javascript的时候曾经想做一个留言板的应用,但是却由于不知道如何存储失败了,由于做这个留言板的思路类似于C语言的学生管理系统,故此这次经历让我重新审视自己去学懂C语言的文件操作. 我重新用 ...
- SQL Server 配置管理器打不开提示错误
---------------------------SQL Server 配置管理器--------------------------- 无法连接到 WMI 提供程序.您没有权限或者该服务器无法访 ...
- C# vs c#判断程序是否调试模式
https://blog.csdn.net/qq_37664403/article/details/118747195 1.Debug模式,Release模式#if DEBUGConsole.Writ ...
- layui的layer.open弹出层高度自适应的解决
https://blog.csdn.net/yzw675628706/article/details/115347180 其他没有试,这个是对我目前的使用有效 layui.layer.open({ ...
- nginx反向代理,负载均衡和yeauty集成的websocket的使用
被要求一个这样的需求:要求项目和websocket使用一个端口.经过一周激烈争论,领导终于同意可以可以开通一个端口,一个月了,端口还没有开. 正式环境已经通过此方法进行部署,没有问题. 前言 因涉及到 ...
- java combobox 多选框
简介 简单 code package calcu; import java.awt.*; import javax.swing.*; public class ComboBoxFrame extend ...
- sciTech-BigDataAIML-AI 发展的"四个阶段“: "看懂世界”到“改造世界”:Perception AI → Generative AI → Agentic AI → Physical AI
sciTech-BigDataAIML: Abbreviation(英文缩写词): CV( Computer Vision ) = 计算机视觉, 是 AI (人工智能) 领域的一个重要分支, 研究如何 ...