.NetCore 配合 Gitlab CI&CD 实践 - 单体项目
前言
上一篇博文 .NetCore 配合 Gitlab CI&CD 实践 - 开篇,主要简单的介绍了一下 GitLab CI 的持续集成以及持续部署,这篇将通过 GitLab CI 发布一个 .net core 项目,来带小伙伴们感受一下自动化的魅力,从此告别手动发布。
准备工作
创建一个空MVC项目来进行演示:
mkdir hello-world
cd hello-world
dotnet new sln -n HelloWorld
mkdir src
cd src
dotnet new mvc -n GitLabCIDemo
cd ../
dotnet sln add .\src\GitLabCIDemo\GitLabCIDemo.csproj
完成以上创建后,用 vscode 打开应该是下面这个样子:

项目上传至 GitLab
在 gitlab 上新建一个 hello-world 的项目,将本地的项目上传。这个按照如下提示操作即可:

项目上传成功后,切一个 dev 分支出来,我这里的策略是,代码提交到 dev 分支是自动发布到开发环境进行验证的,生产环境是通过 master 分支打 tag 进行发布的。
- 切换到 dev 分支!
- 切换到 dev 分支!
- 切换到 dev 分支!
添加相关脚本
在 hello-world 文件夹内创建 .build/docker 文件夹,并添加如下脚本以及Dockerfile:
- build-image.sh
docker build -f .build/docker/Dockerfile --build-arg PROJECT=$1 --build-arg ASPNETCORE_ENVIRONMENT=$2 -t $3 .
- build-project.sh
set -e
for arg in "$@"
do
target=$(pwd)/src/$arg
echo $target
echo "Starting dotnet restore: $target"
dotnet restore -v n $target
dotnet publish -c Release -o $target/publish $target
done
- push-image.sh
NEW_TAG="registry.cn-hangzhou.aliyuncs.com/xxx/$1"; // 这里得用你自己命名空间哦
docker tag $1 $NEW_TAG
docker push $NEW_TAG
docker rmi $NEW_TAG
- Dockerfile
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine
ARG PROJECT
ARG ASPNETCORE_ENVIRONMENT
ENV ASPNETCORE_ENVIRONMENT ${ASPNETCORE_ENVIRONMENT}
WORKDIR /app
COPY src/${PROJECT}/publish .
RUN echo "#!/bin/bash \n dotnet ${PROJECT}.dll" > start.sh && chmod +x ./start.sh
ENTRYPOINT ["./start.sh"]
这里构建镜像所使用 3.1-alpine 构建出来的镜像体积只有其他镜像版本构建出来体积的一半。推荐使用。到此目录结构就成了现在这样:

至此准备工作已经差不多了!我们先简单过一下 CI 的流程:
提交代码 --> 编译 --> 测试 --> 构建镜像 --> 发布
编译 -- Build
在 hello-world 目录下添加 .gitlab-ci.yml 文件,添加 build 任务:
stages:
- build
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
这里起一个容器来跑编译任务,具体细节可以看 build-project.sh 脚本,用 artifacts 将发布出来的资源上传,给后面的 构建任务使用,不需要重复 build,节约时间。这里执行会出现如下错误:
/bin/bash: line 89: .build/docker/build-project.sh: Permission denied
这是因为脚本没有执行权限,通过 chmod 命令可以解决:
chmod +x build-image.sh build-project.sh push-image.sh
重新提交,触发 build 任务。打开 GitLab CI 界面,执行成功,是不是很开心呢?

测试 -- Test
现在项目里面没有测试代码,那就新建一个吧。在 hello-world 根目录下:
mkdir test
cd test
dotnet new xunit -n GitLabCIDemo.UnitTests
更新 .gitlab-ci.yml 添加 Test 任务:
stages:
- build
- test
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
helloworld-test:
stage: test
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- dotnet test -c Release
dependencies:
- helloworld-build
等待一会,查看 CI 运行。

构建 Docker 镜像 -- Pack
我这里使用的是阿里云的镜像仓库,需要现在阿里云上创建对应的命名空间以及镜像名称。我这里给镜像名取名为 hello-world,别忘了修改 push-image.sh 中的命名空间哦!

更新 .gitlab-ci.yml 添加 Pack 任务:
stages:
- build
- test
- pack
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
helloworld-test:
stage: test
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- dotnet test -c Release
dependencies:
- helloworld-build
helloworld-pack-staging:
stage: pack
tags:
- shell
script:
- .build/docker/build-image.sh GitLabCIDemo Staging hello-world:beta
- .build/docker/push-image.sh hello-world:beta
dependencies:
- helloworld-build
only:
- dev
Staging: 用来设置环境变量ASPNETCORE_ENVIRONMENT,让.net core读取对应的配置文件,可以设置Development,Staging,Production三种,可以在对应的环境设置对应环境变量,使用不同配置。beta: 因为是开发环境,就一直使用同一个 tag 去覆盖之前的镜像,每次 pull 最新的镜像就好了,也可以使用
$CI_COMMIT_SHORT_SHA使用当前 git 提交的 hash 值作为版本号
CI 应该执行的差不多,再去瞧瞧呗! 又是全绿,真是开心!额,全绿,怪怪的...

打开阿里云镜像容器服务,查看一下刚刚上传的容器镜像吧!

部署 -- Deploy
这里我图方便就使用
docker-compose来做演示了,通常部署环境都是使用集群来部署的,当然单机部署也不是不无可能的,我内网用 k3s 搭建的一个集群,为啥不用 k8s,因为要求资源配置比较高,k3s 刚好够用。但是部署到 k3s 需要写挺多的配置的文件,可以单独写一篇博文介绍。
作为自动化最后一步,无论你是用 k8s,k3s,docker swarm......,都是拉取对应的镜像,进行部署。思路是一样的,只是部署方式略有不同。这里通过 docker-compose 来发布 hello-world 应用咯。
在部署的 Linux 服务器上创建文件夹 /deploy ,在目录下添加 docker-compose.yml 文件,添加如下内容:
version: "3.8"
services:
hello-world:
image: registry.cn-hangzhou.aliyuncs.com/jd-rd/hello-world:beta
container_name: hello-world
restart: always
ports:
- 5013:80
networks:
- basic_service
networks:
basic_service:
更新 .gitlab-ci.yml,添加部署任务:
stages:
- build
- test
- pack
- deploy
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
helloworld-test:
stage: test
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- dotnet test -c Release
dependencies:
- helloworld-build
helloworld-pack-staging:
stage: pack
tags:
- shell
script:
- .build/docker/build-image.sh GitLabCIDemo Staging hello-world:beta
- .build/docker/push-image.sh hello-world:beta
dependencies:
- helloworld-build
only:
- dev
deploy-staging:
stage: deploy
tags:
- staging
script:
- cd /deploy && docker-compose pull && docker-compose up --force-recreate -d && docker ps
only:
- dev
等待整个CI跑完,查看 CI 运行结果

通过服务器IP + 5013 访问应用了,就可以看到服务更新

修改一下 Index.cshtml 将 Welcome 修改为 Hello Devops ! ,重新提交一下,稍等片刻,重新访问即可看到变化了。

至此一个简单的 CI 流程已经走完了,各位看官可以根据自己的需求,继续探索。
题外话
如果将脚本都放在项目里面的话,将来涉及到脚本变更,需要每个项目都给改过去,这是十分痛苦的事情。这里推荐小伙伴可以通过 git submodule 子模块的方式进行引用,将公共的脚本都给提取出来,项目通过子模块来加载脚本项目,将来脚本变更,每个项目只需要更新一下子模块就好。
总结
通过以上一个小案例已经带小伙伴了解了一圈 GitLab-CI 如何来发布一个 .net core 应用,感受了一下 GitLab-ci 的魅力。但是以上方案还是有瑕疵的,对于单体应用来说,没有太大问题,但并不适合微服务项目。在微服务项目中,如果多个服务散落在多个仓库中,需要多个项目代码改动其实是很不方便的,所以现在很多的微服务项目都采用 Mono 仓库风格及将所有的服务都放在一个仓库里面,仓库体积虽然大,但是改动起来更方便。本篇就先到这里了,有关于 GitLab-CI 对于 Mono 仓库风格项目 CI&CD 探索实践,且听下回分解。
.NetCore 配合 Gitlab CI&CD 实践 - 单体项目的更多相关文章
- .NetCore 配合 Gitlab CI&CD 实践 - 开篇
引言 这是一个系列的文章,讲述的是一个中小型开发团队如何从零开始使用搭建基建 GitLab 代码托管平台,以及使用 GitLab Runner 实现 CI/CD 的故事.本系列通过部署一个完整的 .n ...
- .Net Core自动化部署系列(三):使用GitLab CI/CD 自动部署Api到Docker
之前写过使用Jenkins实现自动化部署,最近正好没事研究了下GitLab的自动化部署,顺便记录一下. 使用GitLab部署我们需要准备两件事,第一个起码你得有个GitLab,自己搭建或者使用官方的都 ...
- GitLab CI/CD的官译【原】
CI / CD方法简介 软件开发的持续集成基于自动执行脚本,以最大限度地减少在开发应用程序时引入错误的可能性.从新代码的开发到部署,它们需要较少的人为干预甚至根本不需要干预. 它涉及在每次小迭代中不断 ...
- spring-boot 2.5.4,nacos 作为配置、服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD
spring-boot 2.5.4,nacos 作为配置.服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD 本文主要介绍 Java 通过 Cloud N ...
- 实践分享!GitLab CI/CD 快速入门
用过 GitLab 的同学肯定也对 GitLab CI/CD 不陌生,GitLab CI/CD 是一个内置在 GitLab 中的工具,它可以帮助我们在每次代码推送时运行一系列脚本来构建.测试和验证代码 ...
- CI Weekly #6 | 再谈 Docker / CI / CD 实践经验
CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...
- 使用 Gitlab CI/CD 实现自动化发布站点到 IIS
说明 这里先介绍下两个东西 CI/CD.GitLab Runner,当然在此之前你需要对 git 有所了解,关于 git 这里不做说明,可以自行百度. 首先介绍 CI/CD :随着我们开发方式的转变, ...
- Docker Gitlab CI 部署 Spring Boot 项目
目前在学习这一块的内容,但是可能每个人环境都不同,导致找不到一篇博客能够完全操作下来没有错误的,所以自己也写一下,记录一下整个搭建的过程. Docker 的安装这里就不赘述了,基本上几行命令都可以了, ...
- 前端初探 Gitlab CI/CD
前言 纵观人类历史的发展以及三次工业革命,你会发现利用机器来替代部分人力劳动,将重复的工作自动化从而解放生产力都是发展的必然趋势,在软件工程领域也不例外,其中 CI/CD 就是其中一项,那么什么是 C ...
随机推荐
- Mysql基础(八):MySQL 表的一对一、一对多、多对多问题
将实体与实体的关系,反应到最终数据库表的设计上,将关系分为三种:一对一,一对多(多对一)和多对多,所有的关系都是表与表之间的关系; 一对一 一对一:一张表的一条记录只能与另外一条记录进行对应,反之亦然 ...
- Ethical Hacking - Web Penetration Testing(10)
SQL INJECTION SQLMAP Tool designed to exploit SQL injections. Works with many DB types, MySQL, MSSQL ...
- GPO - AppLocker
AppLocker can help you: Define rules based on file attributes that persist across app updates, such ...
- Ethical Hacking - GAINING ACCESS(10)
CLIENT SIDE ATTACKS Use if server-side attacks fail. If IP is probably useless. Require user interac ...
- OSCP Learning Notes - WebApp Exploitation(2)
Cross-Site Scripting(XSS) 1. Using the tool - netdiscover to find the IP of target server. netdiscov ...
- 事件循环 event loop 究竟是什么
事件循环 event loop 究竟是什么 一些概念 浏览器运行时是多进程,从任务管理器或者活动监视器上可以验证. 打开新标签页和增加一个插件都会增加一个进程,如下图:  浏览器渲染进程是多线程,包 ...
- jmeter 及测试(转载)
负载测试:在一定的工作负荷下,给系统造成的负荷及系统响应的时间. 压力测试:在一定的负荷条件下,长时间连续运行系统给系统性能造成的影响. 1.性能测试(Performance Test):通常收集 ...
- C++语法小记---同名覆盖
同名覆盖 子类中的同名成员会覆盖父类中的同名成员,但是在内存中仍然存在,只是无法直接访问,需要加上域名才能访问 子类中的同名函数会覆盖父类中的函数,复写是同名覆盖的一种特殊情况,只要不是多态场景,复写 ...
- Java中static、final和static final(final static)的区别(转)
大佬的总结(大赞!) final可以修饰:属性,方法,类,局部变量(方法中的变量) final修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变. final修饰的属性跟具体对象有关, ...
- RACTF-web C0llide?(js弱类型)
源码: const bodyParser = require("body-parser") const express = require("express") ...