非专业运维,不会复杂配置,踩了很多坑,不断实验调整,并非最佳实践,最终目的是实现项目(vue&node)的打包部署或迁移,涉及技术:
- Docker 容器
- Jenkins 项目管理及自动化
- Nginx 代理
- Mysql 数据库
- Nodejs 服务端
- yarn/npm 前端包管理
- git 附赠,无需安装
- pm2 静态/动态服务 + 进程守护
Docker是一个在称为“容器”的孤立环境中可运行应用程序的平台。
容器分为三个:
- 一个是放Mysql的,单独迁移数据库。
- 一个则集成Node环境的容器,其中由Jenkins统一管理项目。
- 独立一个Nginx容器,只用于代理。
原本计划是在服务器A做好容器后直接将容器迁移到服务器B,实际操作后发现只有nginx成功迁移了,其它两个容器启动失败。不过好在docker能帮我省去很多环境安装的步骤,mysql和nginx在服务器B手动安装也不算费力,所以实际迁移则只迁移了sql文件和nginx配置文件,而Jenkins我则简单粗暴地将整个jenkins_home打包。
首先安装Docker
系统centos7.6
curl -fsSL https://get.docker.com | bash -s docker --mirror aliyun
其它可参考这里
安装完运行
sudo systemctl start docker
常用操作
docker ps 查看正在运行的容器
docker ps -a 列出所有容器
docker stop <name> | docker start <name> | docker restart <name> 字面意思
docker rm <id> 删除容器
docker rmi <id> 删除镜像
常见参数
-d 进程守护
-uroot 使用 root 身份进入容器
-p xx:xx 主机 xx 端口映射容器的 xx 端口
-v 目录映射
安装Jenkins
update: 这一步主要将Jenkins_home打包,后续迁移往服务器的Jenkins映射目录解压
docker pull jenkinsci/blueocean
实际运行时我这里加了 --net=host
,因为我的服务都将在这个容器中运行
docker run -d --name back-place -u root -p 9090:8080 -v /var/jenkins_home:/var/jenkins_home jenkinsci/blueocean
这样一个Jenkins就安装好了,少了一堆工作,Jenkins的一些配置可以参考我这篇或者上谷歌百度一下,这里不多展开,解释下上面的命令,--name
后面带的是容器名,创建后都可随意修改,-p
这行代表Jenkins将运行在9090端口,而且占用了Docker中的8080端口。
查看Docker目前的进程,可以看到 back-place 这个容器已经跑起来:
docker ps
此时的back-place就可以看做一个虚拟机,我们可以进入到这个容器的bash环境中:
docker exec -it back-place /bin/bash
经过一阵折腾,初始化Jenkins、Github生成新令牌、SSH、配置Git(似乎docker实例Jenkins的时候自带了git,查找路径命令为 which git
)、一个个项目的构建配置复制等。
(注:如果嫌github太慢,可以使用generic-webhook-trigge
插件hook码云,不过通常来讲github只要不用https连接就行,这算是一个坑,https时灵时不灵的,要用ssh项目连接)
离开bash环境:
exit
安装Mysql,迁移数据库
注:这一步实际为备份旧服务器上的mysql文件。
登录mysql,输入密码
mysql -u root -p
输入 status
可以看到版本号:
或者在linux中mysql -V
也可以直接看到版本,注意大写V。
我是将版本号复制出来直接安装,不过还是建议官网查找相关版本的镜像来安装稳一点:
docker pull mysql:5.7.34
登录旧的mysql,导完自己的sql文件,把linux的mysql关掉,释放3306端口
service mysqld stop 看不同的版本操作不同
-- 分割线: 以下在目标服务器再操作,mysql容器导入运行没成功 --
运行 docker images
看下刚才安装是否成功
修改密码并启动运行:
docker run -itd --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.34
123456为root密码,5.7.34改成自己的版本
在bash中或者外部工具连接导入数据库文件,搞定。
安装Node (Alpine)
docker exec -it back-place /bin/bash
进容器,一顿操作(这里看看就好,踩坑了,前面这些步骤是错的):
wget https://nodejs.org/dist/v14.17.1/node-v14.17.1.tar.gz
......
出师未捷身先死,nodejs.org在容器内部能ping通,但却怎么也下载不了,查了一些资料似乎说是容器网络模式设置的问题,默认是桥接模式,要让容器直接使用宿主的网络命名空间就得重新安装容器了,算了,采取折中方法,先退出bash,回到宿主linux中:
docker inspect -f '{{.Id}}' back-place
然后通过局域网拷贝文件:docker cp 本地文件路径 ID全称:容器路径
docker cp node-v14.17.1.tar.gz afe6f75d24f72b48826a5396c62e8efeb35c2cba30b5460bf20378817a8881f1:/usr/local/src/node.tar.gz
重新回到docker容器back-place中一顿操作:
docker exec -it back-place /bin/bash
cd /usr/local/src
tar zxvf node.tar.gz
cd node-v14.17.1
./configure --prefix=/usr/local/node/14.17.1
很好,又崩了,python找不到,原来我之前一直安装node的方法是基于CentOS的,已经集成了python环境,突然发觉事情不对劲了,查了一下目前docker容器的linux版本:
好家伙,又赶紧查了一下各个系统版本对应的包管理工具:
- centos:yum
- ubuntu:apt-get
- alpine:apk
我是一条无奈的分割线
以下需要按照顺序操作,否则npm与node可能对不上先设置apk为国内镜像源:
推荐使用中科大镜像
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
apk update && apk upgrade
-----------
以下为清华镜像,node版本有问题
echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.8/main/" > /etc/apk/repositories
apk update && apk upgrade
安装node执行:
apk add nodejs
需要手动安装npm
apk add --update npm
npm config set registry https://registry.npm.taobao.org
node 安装完毕
检查node版本,如果是8(中科大镜像无问题),手动升级一下node最新稳定版
npm install -g n
n stable
上一步如果遇到报错,尝试运行以下命令(坑真的多)
npm config set unsafe-perm true
根据提示运行以下命令,不然全局 node 命令还是旧版
PATH="$PATH"
安装PM2
用来在Jenkins容器中守护各个服务的进程。
npm install pm2 -g
常用命令
pm2 list
pm2 start ./xxx
pm2 restart all
pm2 flush # 清空所有日志文件
pm2 delete 0 # 删除指定应用 id 0
跑静态服务
pm2 serve <path> <port> --name <name>
如果你的Vue项目是history路由,需要这么跑
pm2 serve --spa <path> <port> --name <name>
跑动态服务
pm2 start <path>
eg: pm2 start /home/service.js
安装 Nginx (Alpine 失败)
就在我安装到一半的时候,linux突然崩溃,尝试了很久,依然是不行,最终决定把Nginx独立出来一个容器。
就这样搞废了几个容器,虽然很不服气,但最后还是选择放弃 TvT
安装 Nginx(Docker)
先停掉宿主的Nginx服务,Docker拉一份镜像
docker pull nginx
实例化镜像,绑定80端口(桥接模式)
docker run --name nginx -p 80:80 -d nginx
桥接模式需要指定端口,考虑到以后可能会配置更多的端口,所以实例还是直接使用net模式方便些
docker run --name nginx -d --net=host nginx
进入Nginx容器,查看配置目录
docker exec -it nginx /bin/bash
nginx -t
可以看到配置文件在 /etc/nginx
目录下,按需要进行修改,或利用docker cp将原Nginx的配置迁移过来。
docker inspect -f '{{.Id}}' nginx
docker cp <本地文件路径> <ID全称>:<容器路径>
docker cp /conf/nginx.conf xxxxxxxxxxxx:/etc/nginx
导入导出(放弃)
导出前需要查看复制出容器的COMMAND,这在导入时需要用到
docker ps -a --no-trunc
导出镜像 docker export <id> > <path>
docker export 7691a814370e > /home/test.tar
这样就有了镜像文件:test.tar,将镜像文件放到静态服务中,从服务器B远程拉取服务器A的镜像安装(理想很美好,实际上最终只有nginx容器移花接木成功,故放弃)。
导入镜像 docker import <url> <name>
docker import http://xxx.com/nginx.tar test/nginx
由于该镜像为容器实例,所以导入运行时需要最后面加上容器对应的COMMAND命令:
docker run --name=xxx -d xxx /docker-entrypoint.sh nginx -g 'daemon off;'
大文件传输
第一次打包后的文件大概是这样的:
然后放到静态服务上,根本下载不了:
这里比较占空间的是Jenkins容器,后面因为容器迁移跑不起来,实际我只迁移了jenkins_home目录(可以看做是Jenkins的所有配置,我压缩完大概200M),记得把工作空间清空一下,这块是比较占用容量的,然后用gzip压缩,效果是非常的Amazing啊:
压缩
gzip -c mysql.tar > mysql.tar.gz
解压
gunzip mysql.tar.gz
服务器之间的大文件传输我们可以使用scp来实现:
scp mysql.tar.gz root@服务器B的IP:/data/
gzip只能压缩文件,打包目录要用 tar :
tar -zcvf jk.tar.gz /var/jenkins_home
上面这个-zcvf
的缩写就代表了打包完顺便使用gzip压缩,解压:
tar -xzvf
结尾
一开始想的是把所有线上的服务,统统放到docker独立容器中,通过克隆镜像来一次性迁移,目的是为了减少再次部署安装以及各种配置的时间,最后虽然不如理想中美好,但也算达成了目的:以最低成本搬家,并且在最低改动下保持我原有的生态——项目统一管理、自动化部署等等,在新服务器中使用docker安装环境并不复杂,只有少量配置需要重新弄(如SSH-key这种),这样下次服务器需要变更的时候也轻松许多了。