引言
背景介绍
这篇文章最早大约是在2019年中旬写的,当时是为了给某项目组搭建一套规范化的发布流程,同时方便运维工作。如今已经2024年,又参与了一个项目,场景与当时相似,所以完善这篇文章,供参考。
作者的公司最早打包发布全靠Svn同步和拷贝依赖,在Eclipse中手动配置依赖等信息,这样其实很容易造成Jar包不一致和依赖配置难登问题,时间久了,因为没有做依赖的版本管理,当时的构建环境不容易还原,新同事入职配置开发环境全靠口口相传。
所以在16年以后Jar包依赖全部使用maven管理,代码使用git管理,代码发布通过jenkins流水线构建来迭代Jar包版本,作者也是在这个时候接触了相关的东西,刚开始是自己搭建Jenkins,构建项目工程,然后发布到docker,后来的项目中因为服务比较多,采用了mesos+marathon来做容器编排,再就到了这篇文章初做的2019年,因为当时项目比较小,所以考虑到不适用于mesos+marathon这种复杂的架构,就搭建了这套比较简单的集群环境。
目的阐述
本文的场景是搭建一个适用于小团队(3-4人左右)的Devops环境。
安装Docker
安装docker的方法有很多,我习惯到官网查看官方文档安装,介绍的比较全一些
官网安装
在该选项中有几种不同的安装方法,可根据自己需要进行安装,我这里常用的安装方式是下面这个:
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh
Ubuntu
- 更新软件包索引:
sudo apt update
- 安装Docker依赖项:
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
- 添加Docker GPG密钥并设置Docker的APT仓库:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- 安装Docker CE:
sudo apt install docker-ce docker-ce-cli containerd.io
CentOS
- 安装必要的软件包管理工具(EPEL):
sudo yum install -y epel-release
- 安装Docker CE:
sudo yum install -y docker-ce docker-ce-cli containerd.io
docker配置
docker私有仓库配置
docker默认使用https下载镜像,我们搭建的私库需要添加到下面配置中才能正常推送和拉取镜像
添加/etc/docker/daemon.json文件
{
"insecure-registries": ["s01.ycrh.jsyt:5000","10.83.3.120:5000"]
}
配置docker代理
因为部分网络环境下是无法访问外网下载镜像的,所以需要配置代理进行镜像的下载
vi /lib/systemd/system/docker.service
在 Service 部分下 增加 Environment 变量,配置成你自己的代理地址,如下
[Service]
Environment="HTTP_PROXY=http://[proxy-addr]:[proxy-port]/" "HTTPS_PROXY=https://[proxy-addr]:[proxy-port]/"
启动并启用Docker服务
sudo systemctl start docker
sudo systemctl enable docker
重启docker服务
service docker restart
启动docker registry
这里采用最简单的方式搭建docker仓库,不过推荐采用nexus或harbor,他们有可视化管理界面,我觉得比较方便的是可以自定义镜像的清理策略,比如可以设置保留最近10个版本的镜像
如果你想最简单的搭建这套环境,nexus应该是个不错的选择,因为nexus可以同时作为maven、npm、docker仓库,搭配gitlab的流水线,甚至不要jenkins
新建目录:mkdir /home/registry
docker run -d -p 5000:5000 -v /home/registry:/var/lib/registry -u 0 --restart=always --name registry registry
安装Docker Swarm
docker swarm是docker自带的集群管理组件,不过要确保你的docker版本至少为17.06,创建Docker Swarm集群涉及初始化一个管理节点(Manager node),随后将工作节点(Worker nodes)加入到集群中。
初始化Swarm
sudo docker swarm init --advertise-addr <MANAGER_IP>
例如:
[root@manager-node ~]# docker swarm init --advertise-addr 10.83.3.120
Swarm initialized: current node (39efft8yudtdpwmnenxewlt4r) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-5mci820jcs7dard6jrp19gjduj562kimptqmbfgd6omurkxa36-92jgt38tfowrsmo1wdzojtnde \
10.83.3.120:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
[root@manager-node ~]
加入Worker节点
使用Manager节点提供的命令进行加入。
docker swarm join \
--token SWMTKN-1-5mci820jcs7dard6jrp19gjduj562kimptqmbfgd6omurkxa36-92jgt38tfowrsmo1wdzojtnde \
10.83.3.120:2377
即可加入到该集群,此处需要注意防火墙的配置。
查看当前集群节点信息
[root@swarm-master ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kpcxiz0pnby0if3co2jx9kr30 Down Active
vojwoufr5bdfl5edat8m1c3qc * swarm-master Ready Active Leader 24.0.7
kjoitys5lmf3ka3lthjthjcrf swarm-node1 Ready Active 24.0.7
kj4og3ldcdeyvhymk8ptrg72r swarm-node2 Ready Active 24.0.7
o1i1x3xlra5stpgh9n39gqmxs swarm-node3 Ready Active Reachable 24.0.7
c9s4bkzykq390prblj4708wyl swarm-node4 Ready Active 24.0.7
u2gjy4xfiex0r7e9ngqr2a8oa swarm-node5 Ready Active 24.0.7
到了这里,其实就可以使用docker swarm发布docker服务了,前提是你习惯使用命令去管理docker集群。
下面列举用命令行创建docker服务
创建网络
docker network create --driver overlay test
创建服务
docker service create --replicas 3 --network test -p xx:xx --name 服务名 镜像名
新增副本,根据你服务器的压力进行伸缩
docker service scale 服务名称=3
其余命令根据下面提示使用:
docker swarm默认采用的是 vip 负载均衡模式,vip模式,就是docker swarm为每一个启动的service分配一个vip,并在DNS中将service name解析为该vip,发往该vip的请求将被自动分发到service下面的诸多active task上(down掉的task将被自动从vip均衡列表中删除)。
下面推荐一个docker图形管理工具:portainer,效果还是很好的。
安装Portainer
Portainer是Docker的图形化管理工具,提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作)、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控制等功能。功能十分全面,基本能满足中小型单位对容器管理的全部需求。
创建Portainer数据卷
sudo docker volume create portainer_data
启动Portainer容器
sudo docker run -d -p 9000:9000 --name=portainer --restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce
访问Portainer
在Web浏览器中访问 http://<YOUR_SERVER_IP>:9000
来开始使用Portainer。
添加docker集群
直接将docker swarm的管理节点添加上即可。
连接失败的时候,可以检查下是否远程docker 没有开启 2375端口。
以下是 docker 端口配置方法:
# 1. 编辑docker.service
vim /usr/lib/systemd/system/docker.service
# 找到 ExecStart字段修改如下
ExecStart=/usr/bin/dockerd-current -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
# 2. 重启docker重新读取配置文件,重新启动docker服务
systemctl daemon-reload
systemctl restart docker
# 3. 开放防火墙端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
# 4.刷新防火墙
firewall-cmd --reload
# 5.再次配置连接远程docker就可以了
添加成功后,就可以看到docker swarm集群的情况了,如下图
可以看到集群中一共有7个节点,3个stacks,21个服务
发布应用
应用发布可以使用下面三种方式:
-
直接根据docker镜像启动一个container
-
发布一个service,好处是支持横向扩展和收缩,比如发布多个服务实例
-
发布一个stack,当你的服务比较多的时候,定义一个docker-compose配置文件,可以很方便的发布
启动一个container
发布一个service
这里的配置和上面的container类似,只是多了服scale等参数可以实现发布多个实例来负载
发布一个stack
发布stack有4种方式,我使用比较多的是web editor和repository
其实刚开始使用的是web editor方式发布,但是后来发现还需要单独去维护docker-compose文件,而且从jenkins的流水线也不好直接去修改web editor中的配置,如果直接修改docker swarm的配置,portainer中的配置其实是不会更新的,所以最后选择了repository方式,这种方式完美的解决了我的诉求:
一、我的配置通过gitlab来维护,也不怕丢失了,并且有版本管理;
二、在jenkins中可以通过发起portainer的webhook请求来触发portainer自动拉取最新的gitlab上的配置,然后更新服务;
web editor方式
repository方式,通过配置一个git仓库地址来发布
配置完后,界面上会提供一个webhook地址来触发更新
配合jenkins来实现自动发布
下面的jenkins流水线配置是一个标准的springboot打包和发布流程,你可以通过少许改动来直接复用,主要过程就是
-
maven打包
-
docker镜像打包
-
docker镜像推送
-
调用portainer webhook请求,触发stack更新
pipeline {
agent any
tools {
maven 'Maven3' // 替换为你在全局配置中配置的Maven名称
}
environment {
// 全局变量,用于存储时间戳
TIMESTAMP = getCurrentTimestamp()
MAVEN_HUB_URL = '192.168.100.10:6000'
APP_NAME = 'demo-mf-main'
ARCHITECTURE = 'amd64'
DOCKER_COMPOSE_FILE = 'app-dev.yml'
STACK_NAME = 'app-dev'
}
stages {
stage('拉取主体功能代码') {
steps {
script {
git credentialsId: 'git-lab',
url: 'http://192.168.100.9:9980/rdcenter/demo-mf-main.git',
branch: 'develop'
}
}
}
stage('Maven构建') {
steps {
// 使用Maven构建项目
script {
sh ' mvn clean package'
}
}
}
stage('构建Docker镜像') {
steps {
script {
// 使用Docker Pipeline插件构建镜像,并使用年月日时分秒作为tag
def customImage = docker.build("${env.MAVEN_HUB_URL}/${env.APP_NAME}:${env.TIMESTAMP}-${env.ARCHITECTURE}", '-f Dockerfile .')
// 推送镜像到私有仓库
docker.withRegistry("http://${env.MAVEN_HUB_URL}", 'Maven_Hub') {
customImage.push()
// 添加 latest 标签并推送
customImage.push()
}
// 删除本地镜像
//customImage.remove()
sh "docker rmi ${env.MAVEN_HUB_URL}/${env.APP_NAME}:${env.TIMESTAMP}-${env.ARCHITECTURE}"
}
}
}
stage('发布应用') {
steps {
script {
dir('devops') {
git credentialsId: 'gaojunxin-git',
url: 'http://192.168.100.9:9980/rdcenter/devops.git',
branch: 'main'
// 使用双引号包围sed命令,并使用单引号包裹正则表达式内的特殊字符,同时正确插入环境变量
def imageName = "${env.MAVEN_HUB_URL}\\/${env.APP_NAME}:${env.TIMESTAMP}-${env.ARCHITECTURE}"
sh """
sed -i " \"s/image.*${env.APP_NAME}:[^\\":]*\\\$/'image: ${imageName}'/\" " ${env.DOCKER_COMPOSE_FILE}
"""
sh "cat ${env.DOCKER_COMPOSE_FILE}"
// 先不做docker swarm发布,直接通过portainer触发
// sh "docker stack deploy --compose-file '${env.DOCKER_COMPOSE_FILE}' '${env.STACK_NAME}'"
// 添加、提交并推送更改到Git仓库
sh """
git config --global user.email "gjx.xin@qq.com"
git config --global user.name "gaojunxin"
git add .
git commit -m "更新${APP_NAME}镜像版本到${env.TIMESTAMP}-${ARCHITECTURE}"
"""
withCredentials([usernamePassword(credentialsId: 'gaojunxin-git', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) {
sh 'git push http://${GIT_USERNAME}:${GIT_PASSWORD}@192.168.100.9:9980/rdcenter/devops.git HEAD:refs/heads/main'
}
sh '''
curl -X POST \
-H "Content-Type: application/json" \
-d '{}' \
'http://192.168.100.11:9000/api/stacks/webhooks/adf41602-71cb-4b36-8fd7-c71f669ad1ab'
'''
}
}
}
}
}
}
//获取时间
def getCurrentTimestamp() {
def calendar = Calendar.getInstance()
def timestamp = calendar.format("yyyyMMdd-HHmmss")
return timestamp
}