手把手教你搭建Gitlab、Docker、Docker-compose CI/CD
作为一个开发人员,怎么提高自己的效率是很关键的,那么CI/CD就是其中很关键的一环,Gitlab作为一个强大的代码托管平台,也拥有强大的CI/CD功能,并且每个月都有一定的免费分钟数供大家使用,本文将基于实际项目来讲解如何搭建一个Gitlab+Docker+Docker-Compose 的完整流水线。无论你是新手还是需要此类功能进行快速交付的团队,都能从中收获不少。
环境准备
准备服务器
这里我们准备一台ubuntu服务器。需要设置SSH访问。所以用putty key generator开生成SSH公私钥。 下载安装Putty 后打开 key generator直接点击生成即可
接下来是将上边步骤生成的公钥,也就是
ssh-ras XXXXX 这一串内容复制,打开服务器远程,执行以下命令:
cd ~/.ssh
vim authorized_keys
打开文件后将上边PUTTY生成的公钥粘贴进去即可。
最后还需要将私钥导出问OpenSSH格式:
新建Gitlab项目组
首先,需要去gitlab官网注册一个账号,关于注册账号的过程就不多赘述了,相信大多数开发者都有账号。账号注册好之后,我们创建一个项目组
示例项目组
创建好项目组之后,我们先得配置环境变量,点击刚刚创建的Group。然后进入Settings-> CICD
打开Variables,配置以下四个环境变量,一个是远程服务器ip,一个SSH端口(默认的话就可以不用配置),SSH用户以及私钥,这个私钥就是前边步骤使用PUTTY生成后导出的OpenSSH私钥文件,直接粘贴私钥内容即可。
这样前置条件就配置好了。接下来就是配置CI/CD模板
配置CICD模板仓库
接下来在上边新建的项目组中新一个代码仓库,命名为ci_cd_template.然后直接在网页上边编辑。
我们这边以dotnet项目cicd为例 此模板仓库文件内容如下
init.yml
这个文件是初始化阶段,用来将以下传入的变量写入环境中,内容如下:
stages:
- init
init-job:
stage: init
script:
# 这里的$ 开头的就是流水线变量,可以自定义,也可以使用平台预定义的
- echo "CI_PROJECT_NAME=$CI_PROJECT_NAME" >> init.env
- echo "CI_PROJECT_PATH=$CI_PROJECT_PATH" >> init.env
- echo "CI_COMMIT_HASH=$CI_COMMIT_SHA" >> init.env
- echo "CI_USER_NAME=$GITLAB_USER_NAME" >> init.env
# 设置工件,过期时间1小时
artifacts:
paths:
- init.env
expire_in: 1 hour
Jobs 具体的执行job
我这里将job分为三个步骤:
- dotnet 构建和发布
- docker镜像构建
- 服务器部署
- dotnet 构建和发布
这里主要是对dotnet项目进行build和publish。前置条件是使用 mcr.microsoft.com/dotnet/sdk:8.0镜像,这个可以根据自己实际项目进行选择,
stages:
- dotnet-build-publish
dotnet-build-publish-job:
stage: dotnet-build-publish
image: mcr.microsoft.com/dotnet/sdk:8.0
needs:
- job: init-job
artifacts: true
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- ~/.nuget/packages/
- ~/.dotnet/
script:
- echo "CI_PROJECT_NAME=$CI_PROJECT_NAME"
- echo "CI_PROJECT_PATH=$CI_PROJECT_PATH"
- echo "CI_COMMIT_HASH=$CI_COMMIT_HASH"
- echo "CI_USER_NAME=$CI_USER_NAME"
- echo "building project: $PROJECT_CSPROJ_PATH"
- dotnet restore $PROJECT_CSPROJ_PATH
# 构建入口csproj
- dotnet build $PROJECT_CSPROJ_PATH -c Release
# 发布项目,输出到./publish目录
- dotnet publish $PROJECT_CSPROJ_PATH -c Release -o ./publish /p:PublishSingleFile=true
- cd ./publish
artifacts:
paths:
- ./publish
expire_in: 1 hour
2. Docker镜像构建
这里先讲一下镜像仓库仓库,Gitlab中可以使用自带的Container Register。方便快捷,直接和流水线集成。每个代码仓库中在Deploy->Container Register下就可以看到。 那么如何构建呢,具体配置如下:
这个步骤使用的基础镜像是docker。而且必须添加服务:docker:dind才能执行docker build命令
stages:
- docker-image-build
variables:
# 容器注册表的登录凭据,GitLab 提供的默认变量
IMAGE_NAME: $CI_PROJECT_PATH/$CD_IMAGE_NAME
# 使用分支名称作为标签
IMAGE_TAG: "${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"
docker-image-build-job:
stage: docker-image-build
image: docker
needs:
- job: dotnet-build-publish-job
artifacts: true
services: # 配置依赖服务
- docker:dind
script:
- echo "CI_DOCKERFILE_PATH=$CI_DOCKERFILE_PATH"
- echo "CI_REGISTRY=$CI_REGISTRY"
- echo "IMAGE_NAME=$IMAGE_NAME"
# 登录docker 这里都是流水线自带的变量
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
# 使用dockerfile构建进项
- docker build -f $CI_DOCKERFILE_PATH -t $CI_REGISTRY/$IMAGE_NAME:$IMAGE_TAG .
# 推送镜像到Container register
- docker push $CI_REGISTRY/$IMAGE_NAME:$IMAGE_TAG
- echo "IMAGE_TAG=$IMAGE_TAG" >> image.env
artifacts:
paths:
- image.env
expire_in: 1 hour
镜像构建完毕后会将镜像放在Container Register中
- 服务器部署
首先,使用docker-compose 部署的话,需要指定镜像,我们每次执行流水线的时候,都会构建一个新版本,所以需要动态更新docker-compose中的image值。那么就需要指定能够进行字符串替换的基础镜像,这里使用ubuntu:latest基础镜像。
还有一个重要的点,就是需要加载SSH私钥,所以在此步骤中有个前置脚本before_script用来加载私钥。
stages:
- digitalocean-ec-docker-deploy
variables:
# 容器注册表的登录凭据
IMAGE_NAME: $CI_PROJECT_PATH/$CD_IMAGE_NAME # GitLab 提供的默认变量
IMAGE_TAG: "${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}" # 使用分支名称作为标签
APP_NAME: $CI_APP_NAME
# SSH 地址
SSH_USER_AND_HOST: ${SSH_HOST_USER}@${SSH_HOST_IP}
# $CI_REGISTRY/$IMAGE_NAME:$IMAGE_TAG
digitalocean-ec-docker-deploy-job:
stage: digitalocean-ec-docker-deploy
image: ubuntu:latest
needs:
- job: docker-image-build-job
artifacts: true
# 前置脚本,加载SSH私钥文件
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/test.pem
- chmod 600 ~/.ssh/test.pem
script:
# 备份
- |-
ssh -p ${SSH_HOST_PORT} -o StrictHostKeyChecking=no -i ~/.ssh/test.pem ${SSH_USER_AND_HOST} << EOF
if [ ! -d /usr/apps/${APP_NAME} ]; then
sudo mkdir /usr/apps/${APP_NAME}
fi
cd /usr/apps/${APP_NAME}
if [ -f docker-compose.yml ]; then
sudo mv docker-compose.yml docker-compose-backup.yml
fi
EOF
# 复制文件到服务器
# sudo mv /home/ubuntu/docker-compose.yml /usr/apps/${APP_NAME}/docker-compose.yml
- scp -P ${SSH_HOST_PORT} -o StrictHostKeyChecking=no -i ~/.ssh/test.pem docker-compose-${APP_NAME}.yml ${SSH_USER_AND_HOST}:/usr/apps/${APP_NAME}/docker-compose.yml
- |-
ssh -p ${SSH_HOST_PORT} -o StrictHostKeyChecking=no -i ~/.ssh/test.pem ${SSH_USER_AND_HOST} << EOF
echo "开始登录"
echo "$CI_JOB_TOKEN" | sudo docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
echo "开始拉去镜像:$CI_REGISTRY/$IMAGE_NAME:$IMAGE_TAG "
sudo docker pull $CI_REGISTRY/$IMAGE_NAME:$IMAGE_TAG
cd /usr/apps/${APP_NAME}
# 这里就是动态替换docker-compose文件中最新的image值
sudo sed -i "s|image: .*|image: $CI_REGISTRY/$IMAGE_NAME:$IMAGE_TAG|" docker-compose.yml
sudo docker compose up -d
EOF
dotnet-deploy.yml 入口文件
这里就是cicd入口配置,定义了阶段以及对应阶段的具体配置文件。并且job可以自定义,比如增加secret扫描、代码扫描、单元测试等等更加丰富的流程。
stages:
- init
- dotnet-build-publish
- docker-image-build
- digitalocean-ec-docker-deploy
include:
- local: 'init.yml'
- local: 'jobs/dotnet-build-publish-job.yml'
- local: 'jobs/docker-image-build-job.yml'
- local: 'jobs/digitalocean-ec-docker-deploy-job.yml'
以上就是整个cicd模板。原理就是,首先使用实际项目中的dockerfile进行镜像构建,然后将项目中docker-compose文件替换image值为最新的tag。在上传到服务器,然后在服务器执行 docker compose up -d命令。
配置项目
在实际项目中新建.gitlab-ci.yml文件、Dockerfile文件、Docker-Compose.yml文件 Dockerfile和Docker-Compose.yml文件根据自己项目的实际情况配置。
.gitlab-ci.yml文件:
我这里设置的是手动出触发。也可以使用自动触发,可以设置Rules来指定不同的触发规则。比如dev分支触发dev cicd,main分支触发生产环境cicd等。
stages:
- manual-cicd
mvc-job:
stage: manual-cicd
# 手动出发
when: manual
variables:
# 指定build和pubish的项目文件
PROJECT_CSPROJ_PATH: "src/PlutoBlog.Mvc/PlutoBlog.Mvc.csproj"
# 指定dockerfile文件
CI_DOCKERFILE_PATH: "src/PlutoBlog.Mvc/Dockerfile"
# 应用名称
CI_APP_NAME: "blogmvc"
# 镜像名称
CD_IMAGE_NAME: "blogmvc"
# dotnet publish的目录
PUBLISH_DIR: "./publish"
# 触发器,用于触发主的cicd模板仓库流水线
trigger:
include:
# 指定cicd模板仓库地址
- project: 'dotnetydd.com/ci_cd_template'
# 指定分支
ref: main
# 指定入口yml文件
file: '/dotnet-deploy.yml'
以上配置增加后,每次提交代码,都可以手动在Build->Pipelines中找到流水线
最终的执行效果如下:
本文使用 mdnice 排版