这里以我们双十一的项目为例子。
总的部署流程如下:
在使用 golang 起一个 https 服务,编写好业务逻辑后,通过 gitlab CI 去自动部署到 k8s 上。
- 使用 golang 起一个 https 服务,编写好业务逻辑。
- 提交代码到 gitlab ,并合并到 master 分支
- 合并到 master 分支后,会自动触发 CI ,执行构建和部署流程。部署项目到测试环境。
- 通过打 tag 的方式,部署到正式环境。
gitlab-ci.yml
配置如下:
image: zacksleo/golang
before_script:
# 预先装 ssh-agent
- 'which ssh-agent || ( apk update && apk add openssh-client)'
# 启动服务
- eval $(ssh-agent -s)
# 将私钥写入deploy.key 文件
- echo "$SSH_PRIVATE_KEY" > ~/deploy.key
- echo "$SSH_PRIVATE_PRIVATE_REPO_KEY" > ~/deploy_private_repo.key
# 配置较低权限
- chmod 0600 ~/deploy.key
- chmod 0600 ~/deploy_private_repo.key
# 注入密钥
- ssh-add ~/deploy.key
- ssh-add ~/deploy_private_repo.key
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
stages:
- install_dep
- build
- deploy
# 打包
build-package:
stage: install_dep
script:
# 项目中用到了私有库
- git config --global url."git@your.gitlab.host.com:".insteadOf "https://your.gitlab.host.com/"
- go mod tidy
# 这里把编译的文件放到子目录避免把 .git 目录也打包进来
- go build -o build/app
only:
- master
- tags
artifacts:
name: "app"
untracked: true # true表示打包没有被git跟踪的文件
expire_in: 60 mins # 在 gitlab 上保存一小时后自动删除
paths:
- ./build
when: on_success
tags:
- hongkong
# 构建镜像
build-webserver-image:
stage: build
image: docker:latest
dependencies:
- build-package
cache: {}
before_script: []
script:
- mv build/app app
- cp deploy/testing/.env .env
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:8} .
- docker push $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:8}
- docker rmi $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:8}
only:
- master
tags:
- mops-temp
build-webserver-image-prd:
stage: build
image: docker:latest
dependencies:
- build-package
cache: {}
before_script: []
script:
- mv build/app app
- cp deploy/production/.env .env
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- docker rmi $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
only:
- tags
tags:
- mops-temp
deploy-testing:
stage: deploy
variables:
SERVER: $DEPLOY_SERVER
SERVER_PATH: "/opt/k8s/apps/gitlab"
dependencies: []
cache: {}
script:
- ssh $SERVER "mkdir -p $SERVER_PATH/$CI_PROJECT_NAME"
- scp -r deploy/testing/* $SERVER:$SERVER_PATH/$CI_PROJECT_NAME
- ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && sed -i 's/__APP_VERSION__/${CI_COMMIT_SHA:0:8}/' deployment.yaml"
- ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && kubectl apply -f deployment.yaml && kubectl apply -f service.yaml && kubectl apply -f ingress.yaml "
only:
- master
tags:
- mops-temp
deploy-prd:
stage: deploy
variables:
SERVER: $DEPLOY_SERVER
SERVER_PATH: "/opt/k8s/apps/business"
dependencies: []
cache: {}
script:
- ssh $SERVER "mkdir -p $SERVER_PATH/$CI_PROJECT_NAME"
- scp -r deploy/production/* $SERVER:$SERVER_PATH/$CI_PROJECT_NAME
- ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && sed -i 's/__APP_VERSION__/$CI_COMMIT_TAG/' deployment.yaml"
- ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && kubectl apply -f deployment.yaml && kubectl apply -f service.yaml && kubectl apply -f ingress.yaml "
only:
- tags
tags:
- mops-temp
在 install_dep 这个 stage 的时候,build 后的 app 使用 artifact 的方式传输到下一个 stage。值得一提的是,使用 artifact 的方式好像只能打包整个目录,如果目录中有 .git 文件夹的话也会打包进来,这显然不太合理。所以我们把构建好的 app 文件放在另一个目录下,就避免了这个问题。
下一个 stage 是 build。
在 build 阶段,runner 会把项目从 git 仓库再拉一遍,也会把 artifact 下载下来,所以当前目录下除了 git 仓库的文件,还有一个 build/app 文件。所以我们要把 app 移动到项目根目录,再把 testing 中的配置文件也复制到项目根目录。就可以执行 docker build 命令制作镜像了。
这里务必要创建 .dockerignore 文件,把 .git 文件加进去,不然会发现构建的镜像体积会很大。
最后一个阶段是 deploy,顾名思义,就是部署阶段。
在这个阶段,我们会把做好的镜像部署到 k8s 上。
先看一下 $SERVER_PATH
这个变量,他的值是 /opt/k8s/apps/gitlab
,前面的 /opt/k8s/apps
是固定的,后面的 gitlab 是命名空间的名字。在 k8s 中,我们创建了一个叫做 gitlab 的命名空间,用于部署测试环境的业务服务(不要问我为什么名字叫做 gitlab,而不叫做 test 等更容易理解的名字,因为不是我创建的)。与之对应的,正式环境的命名空间是 business ,后面会把正式环境的应用部署到这个命名空间中。
在这个阶段,实际执行部署的命令就是最后一句的 kubectl apply -f xxx.yaml
文件,当然,我们现在要一个个去分析 yaml 文件。