前言
主题:此次想分享的是一个基于docker+docker-compose+gitlab搭建一套前后端自动化部署业务线流程。 目的:前端开发在一个研发项目组内的地位其实相对尴尬,工作定位仅局限于页面编写及接口调用,对于后端工作、数据库,服务器运维等等几乎0接触。本次分享的目的意在使前端能够对一个项目整体的工作流程有个了解。
技术栈及工具
- 前端项目:react
- 后端项目:koa
- 数据库:mysql
- 打包部署:docker,docker-compose
- cicd:gitlab-runner
- 服务器:linux CentOS
由于涉及到的点太多,一次性分享时间太长,且点太多难吸收。
本文档将只演示前端篇,(前端所涉及的只包含docker,docker-compose会在后端篇中作体现,因为数据库容器和后端服务容器会涉及到容器编排,需要使用到compose)
Docker的使用
基本使用参考:docker官方文档
Docker概述
开发、发布和运行应用程序的开放平台。
给定主机上可同时运行多个容器,容器内打包和运行应用程序的能力
应用程序与基础设施分离
使用流程结构:应用程序(你的项目) -> 容器镜像(经Docker打包生成) -> 容器(镜像的运行实例)
Docker本地安装
不做演示了,官网下载后一直下一步下一步就行
前端项目使用
前端大致结构如下:
以下步骤参考 docker官方文档
1.要构建容器镜像,您需要使用Dockerfile. Dockerfile 只是一个基于文本的文件,没有文件扩展名,但包含指令脚本。Docker 使用此脚本构建容器映像。
在项目中创建Dockerfile文件:
cd /path/to/app
touch Dockerfile
编写Dockerfile
# 指定容器的基础镜像
FROM node:16
# 指定容器内的工作空间
WORKDIR /app
# 将你项目内的所有文件复制到容器内 .表示所有
COPY . .
# 下载依赖
RUN yarn install
# 运行你的前端项目 CMD会在容器启动时自动执行
CMD ["yarn", "start"]
# 暴露端口
EXPOSE 3000
打包镜像
# -t后跟上的是镜像的名称和标签
# .表示当前目录,而docker会查找当前目录的Dockerfile来执行
# -f可不加,默认是Dockerfile,但如果涉及到区分不同环境时可指定,
# 例如:Dockerfile.dev Dockerfile.prod
docker build -t my-frontend-app:v1.0 -f Dockerfile .
启动容器
#-d为后台运行 -p为主机和容器之间的端口映射 HOST:CONTAINER
docker run -dp 3000:3000 --name my-frontend-app my-frontend-app:v1.0
此刻访问localhost:3000就能看到界面了
你现在已经有了一个正在执行的容器了
下面列出一些常用的容器操作指令,就用当前这个容器来试试吧:
docker ps --查看主机上正在所有正在运行的容器
docker ps -a --查看主机上正在所有的容器(包括已停止的)
docker stop <容器ID或名称> --停止容器运行
docker rm <容器ID或名称> --删除容器(需要先停止才能删除)
docker exec -it <容器ID或名称> bash --进入容器的bash界面进行操作
docker logs <容器ID或名称> --查看容器的执行日志
多阶段构建
上面示例中虽然是把前端项目启动构建并启动起来了,但Dockerfile的步骤来看,它只不过是在容器内容启动了开发环境的热更新服务器罢了。但前端的部署的最佳实践肯定不是如此。前端项目最终在服务器端呈现的应该是一个打包后的静态资源文件夹,并且通过Nginx做代理,将文件夹映射到端口上,而此时我们就需要用到Docker的多阶段构建,依然以上述react项目为例,修改Dockerfile文件。
#此From为第一阶段,As起一个别名叫builder。
#docker会采用最后一个FROM,
#所以前面的FROM都是一些中间镜像,在最终构建完成后会被丢弃掉。
FROM node:16 AS builder
# 指定容器内的工作空间
WORKDIR /app
# 这里用到了一个docker的缓存机制,使后续打包会更快
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
# alpine版本更适用于构建容器化应用程序(chatGPT说的)
#这是第二阶段,--from=build将会引用第一阶段的构建结果
#/usr/share/nginx/html为nginx的默认静态文件夹位置,nginx是自动取它
FROM nginx:alpine
#如果你不想用nginx的默认配置,你也可以在项目的nginx.conf中自定义(如下代码块)
COPY ./nginx.conf /etc/nginx/conf.d/default.conf#把根目录中的nginx.conf复制到镜像内(如下代码块)
COPY --from=builder /app/build /usr/share/nginx/html
nginx.conf 把这个文件放在你前端项目的根目录中,上述Dockerfile里会COPY到镜像内
server {
listen 80; #监听80端口
server_name localhost; #主机访问容器,所以地址为localhost
index index.html; # 默认索引文件,当访问目录时尝试返回index.html
root /usr/share/nginx/html; #设置nginx的根目录为/usr/share/nginx/html
location / {
try_files $uri $uri/ /index.html; #先找uri,再找uri为前缀下的文件,最后找不到返回index.html
}
}
删除之前的容器
docker ps #找到前面启动的容器id或名称
docker stop <容器名称或id> && docker rm <容器名称或id> #删除它
重复上述的打包构建和启动再试试
打包镜像
#-t后跟上的是镜像的名称和标签
# .表示当前目录,而docker会查找当前目录的Dockerfile来执行
docker build -t my-frontend-app:v1.0 .
启动容器
这里需要把容器的端口改成80,因为nginx监听的配置为80,不再是上一次的3000
#-d为后台运行 -p为主机和容器之间的端口映射 HOST:CONTAINER
docker run -dp 3000:80 --name my-frontend-app my-frontend-app:v1.0
此刻访问localhost:3000就又能看到界面了
DockerHub
就好比github,它是一个docker容器镜像的仓库,你可以将你的镜像共享至DockerHub上供他人使用。你也可以在上面寻找一些很棒的镜像,并在你的主机上启动它。这一块如果感兴趣的话可以去ducker文档查看,不细讲。
Gitlab-Runner
现在我们已经能在本地的docker上跑项目了,现在,结合gitlab-runner去服务器试试
要使用gitlab的cicd功能,需要通过gitlab-runner来进行项目的打包(GitLab-Runner 是一个与 GitLab CI/CD 配合使用以在管道中运行作业的应用程序。)但gitlab自带的runner为了避免被恶意使用,需要绑定信用卡。所以这里选择自己搭建一个runner。
gitlab-runner安装与运行
在gitlab你的项目仓库中,分别点击设置->CI/CD->新建项目runner
取好标签名称,其他的按需求随意填,点击创建
创建完成后,保留这个界面,后续会用到里面的url、token
通过ssh连接到你的服务器
(阿里云租的服务器可选预装docker,所以安装的步骤就不掩饰了,安装命令网上一搜就有)
通过使用gitlab提供的docker镜像的方式将gitlab-runner运行在docker容器内。
参考:
通过docker运行gitlab-runner 使用本地系统卷挂载启动 Runner 容器
运行gitlab-runner镜像
并将主机的/srv/gitlab-runner/config映射到容器的/etc/gitlab-runner
再将主机的/var/run/docker.sock映射到容器的/var/run/docker.sock(此步也就是说,容器内使用的docker实际是主机的docker)
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
注册gitlab-runner
docker run --rm -it -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register
注册过程中会要求填写url,token等。此处填写上述runner创建页提供的即可
step1:Enter the GitLab instance URL (for example, gitlab.com/)(填写上述的--ur… ): gitlab.com
step2:Enter the registration token(填写上述的--token):你的token
step3:Enter a name for the runner. This is stored only in the local config.toml file:给runner起个名字,不填直接回车也行
step4:Enter an executor: virtualbox, docker+machine, custom, docker, docker-windows, parallels, shell, ssh, kubernetes, docker-autoscaler, instance:这里填写docker
step5:Enter the default Docker image (for example, ruby:2.7):这里填写docker版本,我填的19.03.9
注册完成
下面这一步在gitlab-runner文档中没有体现,但需要
使用vim 编辑上述挂载的配置
vim /srv/gitlab-runner/config/config.toml
添加的两行是优化,如果没有runner镜像才拉取,不然每次cicd构建,runner都会启动新容器
需要再一次的把主机docker映射到容器内
修改前 修改后
此刻你的gitlab-runner就已经部署成功了,回到gitlab页面,你的runner已经在项目中自动使用了。
共享runner是自动打开的,记得关掉,用咱自己的
让我们回到项目去,编写一个gitlab-ci.yml吧 在项目根目录创建一个gitlab-ci.yml文件
这里编写了一个简单的ci,分两个阶段,build里我们使用docker的build,deploy里则直接使用docker run运行(当然,在up之前肯定是需要删除旧的服务的,因为旧的服务还占用着端口和容器名称)
因为这个runner的容器是和服务在同一个服务器下,并且runner使用的docker是主机的docker,所以实际就是在直接操作主机docker,我们的项目build和up也是直接在主机操作的。当这个ci执行完成后。容器则已经在主机上运行了。(如果runner和服务不是在同一主机的话,可能就需要用到dockerhub了,通过将镜像上传至dockerhub,然后在deploy里ssh连接到把你的服务主机,并拉去镜像运行,这种情况不做演示了)
stages:
- build
- deploy
build_prod:
stage: build
tags:
- share-test-runner
only:
- master
script:
- docker build -t my-frontend-app:v1.0 .
deploy_prod:
stage: deploy
when: on_success
tags:
- share-test-runner
only:
- master
script:
#部署前先删除之前的旧容器,这里||true兼容,考虑如果没有旧容器,调用stop和rm会报错,所以||true,让其也能继续运行下去
- docker stop my-frontend-app || true
- docker rm my-frontend-app || true
- docker run -dp 3000:80 --name my-frontend-app my-frontend-app:v1.0
编写好yml后,提交代码,我们会看到流水线已经在构建了
好的,试着使用服务器ip+port访问一下你的项目,可以看到,我们已经可以访问了。
但是我们打包后的容器映射的是主机的3000端口,需要显示的带上3000端口才能访问。http协议默认是80端口,那我们要如何不显示的写端口也能访问呢。
1.直接将主机的80端口映射到前端服务的容器,即 将上述的docker up里的3000:80改为80:80。(多low啊)
2.主机上再安装nginx,将80端口反向代理到3000端口,也就是说,主机有自己的nginx,而容器内也有nginx
服务器Nginx安装与配置
yum update #更新系统软件包列表,以确保安装最新的软件包。
yum install epel-release #安装EPEL存储库,它包含了Nginx的软件包。yum会从epel库里找包并下载
yum install nginx # 安装Nginx软件包。
systemctl start nginx #启动nginx
systemctl enable nginx #设置nginx开机自启
现在试着访问服务器ip,可以看到80端口已经是nginx的欢迎页面了
现在开始配置代理linux下nginx的配置文件一般是在/etc/nginx/nginx.conf(这是nginx默认的配置),你也可以在/etc/nginx/conf.d/下创建一个*.conf来编写你的配置
这里我们采用自己创建default.conf的方式来操作,尽量不去动nginx原有的配置 使用vim打开(没有会自动创建)并编辑
vim /etc/nginx/conf.d/default.conf
server {
listen 80; #监听80端口
server_name 8.137.19.69; #你的域名或ip,目前我们没有域名,只有ip
location / {
proxy_pass http://localhost:3000; #代理到主机的3000端口上
#以下将一些来自客户端必要的header带上去,
#因为经过了代理,如果不写的话,访问方会从客户端变成了主机本机
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
配置完成后记得重新加载nginx配置
sudo nginx -s reload
现在直接访问ip也能看到页面了
域名解析
如果你有域名的话,就可以把域名解析的A记录值指向你的服务器的ip,这样域名绑定就完成了,下方大概给个图
上述的ip均是我自己的服务器ip,请根据实际情况替换成你们的ip
前端部分就此结束,后续会继续编写以nodejs做后端+mysql数据库的docker使用。