准备阶段
开始CICD之前你需要提前准备这些东西:
- 需要CICD的项目
- 写好Dockerfile
- 将项目提交到代码仓库
- 开通阿里云效工作流
- 开通阿里容器镜像仓库个人版,可参照此文章
- 准备一台服务器
添加流水线
本文以.NetCore项目部署至K8S为例
点击新建流水线可以看到如下图页面:

选择【其他-镜像构建,发布到k8s集群】的模板。
配置代码源
创建后选择添加代码源,根据你使用的代码仓库来选择代码来源,本文使用github:

注意此处你可以开启代码源触发,当你push代码时就可以触发工作流,如需此设置,需要将云效提供的webhook连接添加到代码仓库内:


企业公钥就是ssh公钥,填写到github或者你代码仓库的个人setting里就可以,比如:

构建镜像
接下来点击镜像构建,添加步骤,选择【镜像构建并推送至阿里云镜像仓库个人版】,然后选择授权,如果没有就点击新建服务授权,地域选择你个人容器仓库的所属地域,选错了你的镜像会构建到别的区域里,之后选择镜像要上传的仓库,如果没有就点新建来创建一个镜像仓库,都填写完后,信息如下:

继续往下拉,还有个标签和更多标签,这里推荐使用你git push的id作为镜像的tag,同时多打一个latest标签,这里的CI_COMMIT_ID是一个自带的全局变量,具体描述请参照这里:环境变量

最后如果有特殊需要(比如你dockerfile不在项目根目录,或执行命令的目录不和dockerfile在同一个context中),那么你需要指定dockerfile的位置,和ContextPath:

- Dockerfile路径为Dockerfile文件相对于代码库根目录所在路径,如META/config/Dockerfile或Dockerfile
- ContextPath为docker build命令执行上下文路径。填写相对于代码根目录的路径,如target,如果不填则为Dockerfile文件所在目录
这里给出我项目的dockerfile和路径结构,方便你对照进行修改这两个选项:
# HuLuProject/HuLuProject/HuLuProject.Web.Entry/Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["HuLuProject.Web.Entry/HuLuProject.Web.Api.csproj", "HuLuProject.Web.Entry/"]
COPY ["HuLuProject.Web.Core/HuLuProject.Web.Core.csproj", "HuLuProject.Web.Core/"]
COPY ["Furion.Extras.Logging.Serilog/Furion.Extras.Logging.Serilog.csproj", "Furion.Extras.Logging.Serilog/"]
COPY ["HuLuProject.Application/HuLuProject.Application.csproj", "HuLuProject.Application/"]
COPY ["HuLuProject.Core/HuLuProject.Core.csproj", "HuLuProject.Core/"]
COPY ["Furion.Extras.DatabaseAccessor.MongoDB/Furion.Extras.DatabaseAccessor.MongoDB.csproj", "Furion.Extras.DatabaseAccessor.MongoDB/"]
COPY ["Furion.Extras.DatabaseAccessor.FreeSql/Furion.Extras.DatabaseAccessor.FreeSql.csproj", "Furion.Extras.DatabaseAccessor.FreeSql/"]
COPY ["Furion.Extras.DependencyModel.CodeAnalysis/Furion.Extras.DependencyModel.CodeAnalysis.csproj", "Furion.Extras.DependencyModel.CodeAnalysis/"]
COPY ["Furion/Furion.csproj", "Furion/"]
COPY ["Furion.Extras.Authentication.JwtBearer/Furion.Extras.Authentication.JwtBearer.csproj", "Furion.Extras.Authentication.JwtBearer/"]
COPY ["Furion.Extras.ObjectMapper.Mapster/Furion.Extras.ObjectMapper.Mapster.csproj", "Furion.Extras.ObjectMapper.Mapster/"]
RUN dotnet restore "HuLuProject.Web.Entry/HuLuProject.Web.Api.csproj"
COPY . .
WORKDIR "/src/HuLuProject.Web.Entry"
RUN dotnet build "HuLuProject.Web.Api.csproj" -c Release -o /app/build -s https://api.nuget.org/v3/index.json
FROM build AS publish
RUN dotnet publish "HuLuProject.Web.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "HuLuProject.Web.Api.dll"]
最后你可以添加任务插件,比如我这里增加了邮件通知:

这样,镜像构建的步骤就完成了,但是先不要急,你可以往下再拉一下可以看到【任务输出】:

这里指的是该步骤输出了哪些变量名可以让你在之后的步骤使用,接下来在部署阶段我们会用得到
部署到K8S
最后一个步骤自然就是CD了,将构建好的镜像拉取至你的远程服务器上,然后跑起来,对于k8s来讲这个过程其实非常简单,你只要提供给Kubeconfig配置就可以了,它会自动使用你源代码中的.yaml文件进行kubectl apply -f,来实现部署资源到集群,废话不多说,我们来看看具体如何配置,首先先编写你项目的yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hulu-project-api
namespace: default
labels:
app: project-api
spec:
replicas: 1
selector:
matchLabels:
app: project-api
template:
metadata:
creationTimestamp: null
labels:
app: project-api
spec:
volumes:
- name: hulu-api-settings
configMap:
name: hulu-project-api
defaultMode: 420
containers:
- name: hulu-project-api
image: ${IMAGE} # 注意这里使用了一个变量名,用这个变量名替换了image的镜像名称,这样在实际部署时是使用了变量的值
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: hulu-api-settings
readOnly: true
mountPath: /app/appsettings.json
subPath: appsettings.json
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
之后添加步骤,选择【Kubectl发布】,其他的先不管,往下拉,找到yaml路径和变量:

yaml路径就是你仓库里的k8s资源文件yaml的位置,变量新建一个,key就是你在yaml里填写的名称,值选择镜像VPC地址,这里简单说下,上一部构建镜像时输出了三个变量,第一个就是CI_COMMIT_ID,实际上也就是你构建的镜像的tag,而下面两个就是包含这个tag的完整镜像地址,因为我们的服务器也是阿里云服务器,所以选择vpc内网地址,这样下载速度更快也不会走公网流量浪费钱。
配置好yaml后,我们需要让阿里云效有权限链接我们的k8s集群,首先我们要到集群master节点的/root/.kube/文件夹,拿到config文件,打开后,将 server 改为你集群master节点所在的服务器公网ip:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS
server: https://172.26.65.240:6443 # 此处改为公网ip 端口是你api server的端口
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1
client-key-data: LS0tLS1CRUd
之后新建集群链接,将内容拷贝到集群配置文件内:

kubectl版本选择与你k8s集群最接近的版本,不一定需要保持一致,因为k8s保证了前后六个大版本的兼容,最后输入命名空间,这个指定了资源部署在你集群内的哪个namespace下。
最后点击保存并运行,来验证一下流程是否运转正常。
部署到Docker
如果你没有自建K8S,也没有任何容器编排工具,也可以只使用docker来进行部署
删除部署步骤的k8s发布,然后新建一个部署类型-Docker部署,点选后看到右边列表如下:

先说主机组,这里点击新建主机组,如果是阿里云服务器就选阿里云ECS,不然就选自有主机,阿里云ECS没什么好说的,如果你是百度云或者腾讯云,你需要选择右边的自有主机,然后按照提示进行操作:

总之,将你需要进行部署的服务器添加到同一个主机组内,然后选择该主机组。
执行用户默认填写root,最重要的就是最后的部署配置,这里实际上就是执行一段部署脚本,如无其他必要,这里可以直接填:
docker run $image
就跟正常在服务器上使用docker run一样,你可以挂载数据卷,指定端口等,这里的$image就是前面构建步骤输出的变量,此处已经添加到服务器环境变量了(注意不能有特殊字符),所以直接run就可以了。
附录
如果你不喜欢使用过容器,或者您的编程语言无法使用容器,亦或者您的编程语言可以不用构建,那么你可以选择使用对应语言的构建步骤,比如PHP应用可以使用【PHP构建】,或者可以完全忽略构建步骤,直接在部署步骤使用命令行git clone,总之道路千千万,条条大路通罗马,ci/cd最终的目的就是方便,自然是你怎么用着简单,成本低就怎么来。
可能发生的错误
代码源使用github而不是通用git时,出现无法拉取代码,或触发不了自动构建的情况,可以使用通用git来替代github:

镜像构建过程中,发生找不到目录的情况,需要你仔细检查dockerfile的位置和dockerfile内文件的路径是否填写正确。
镜像构建过程中,Copy都是对的,但涉及到类似dotnet restore、npm install命令时,执行该命令时所在的工作目录不对导致构建失败,需要检查context的位置是否正确,默认context的位置是dockerfile所在目录。