VPGAME 是集赛事运营、媒体资讯、大数据剖析、玩家社群、游戏周边等为一体的归纳电竞服务渠道。总部坐落我国杭州,在上海和美国西雅图别离设立了电竞大数据研制中心和 AI 研制中心。本文将叙述 VPGAME 将服务器搬迁至 Kubernetes 的进程。

布景

跟着容器技能的日趋老练,公司近期方案将服务搬迁至容器环境,经过 Kubernetes 对容器进行调度、编列和办理。并借此机会,对服务进行标准化,优化整个 CI/CD 的流程,进步服务布置的功率。

CI/CD 东西的挑选

CI/CD 东西上,咱们挑选了 GitLab-CI。GitLab-CI 便是一套合作 GitLab 运用的持续集成体系,以完结代码提交之后的装置依托、编译、单元测验、lint、镜像构建以及发布等作业。

GitLab-CI 完美地和 GitLab 进行集成,在运用的时分只需求装置装备 gitlab-runner 即可。GitLab-Runner 在向 GitLab 完结注册后能够供给进行 CI/CD 操作的环境,担任从 GitLab 中拉取代码,根据代码库房中装备的 gitlab-ci.yml ,履行相应的指令进行 CI/CD 作业。

比较于 Jenkins,GitLab-CI 装备简略,只需在工程中装备 gitlab-ci.yml 文件完结 CI/CD 流程的编写,不需求像在 Jenkins 里相同装备 webhook 回调地址,也不需求 Jenkins 新建这个项意图编译装备。而且个人认为 GitLab 的 CI/CD 进程显现比 Jenkins 愈加漂亮。当然 Jenkins 依托它丰厚的插件,能够装备许多 GitLab-CI 不存在的功用。依照现在咱们的需求, GitLab-CI 简略易用,在功用也满意咱们的需求。

服务运转环境

容器环境长处

传统的服务布置办法是在操作体系中装置好相应的运用依托,然后进行运用服务的装置,这种布置办法的缺陷是将服务的程序、装备、依托库以及生命周期与宿主机操作体系严密地耦合在一起,对服务的晋级、扩缩容、搬迁等操作不是非常便当。

容器的布置办法则是以镜像为中心,在代码进行编译构建时,将运用程序与运用程序运转所需求的依托打包成一个镜像,在布置阶段,经过镜像创立容器实例完结服务的布置和运转。然后完结以运用为中心的办理,容器的阻隔性完结了资源的阻隔,由于容器不需求依托宿主机的操作体系环境,所以能够很好地确保开发、测验和出产环境的共同性。此外,由于构建好的镜像是不可变的,而且能够经过 tag 进行版别操控,所以能够供给牢靠、频频的容器镜像构建和布置,亦可便利及快速进行回滚操作。

Kubernetes 渠道功用

Kubernetes(简称 k8s),作为一个容器调度、编列和办理渠道,能够在物理或虚拟机集群上调度和运转运用程序容器,供给了一个以容器为中心的根底架构。经过 Kubernetes,对容器进行编列和办理,能够:

  • 快速、可猜测地布置服务
  • 具有即时扩展服务的才能
  • 翻滚晋级,完结新功用发布
  • 优化硬件资源,降低成本

阿里云容器服务优势

咱们在服务搬迁中选用了阿里云的容器服务,它根据原生 Kubernetes 进行适配和增强,简化集群的搭建和扩容等作业,整合阿里云虚拟化、存储、网络和安全才能,打造云端最佳的 Kubernetes 容器化运用运转环境。在快捷性上,能够经过 Web 界面一键完结 Kubernetes 集群的创立、晋级以及节点的扩缩容。功用上,在网络、存储、负载均衡和监控方面与阿里云资源集成,在搬迁进程中能够最小化削减搬迁带来的影响。

此外,在挑选集群创立时,咱们挑选了保管版 Kubernetes,只需创立 Worker 节点,Master 节点由容器服务创立并保管。如此一来,咱们在 Worker 节点的规划与资源阻隔上仍是具有自主性和灵敏性的一起不需求运维办理 Kubernetes 集群 Master 节点,能够将更多的精力重视在运用服务上。

GitLab Runner 布置

GitLab CI 作业流程

GitLab CI 根本概念

在介绍 GitLab CI 之前,首要简略介绍一下 GitLab CI 里的一些根本概念,具体如下:

  • Pipeline:Gitlab CI 里的流水线,每一次代码的提交触发 GitLab CI 都会发生一个 Pipeline。
  • Stage:每个 Pipeline 由多个 Stage 组成,而且每个 Stage 是有先后次序的。
  • Job:GitLab CI 里的最小使命单元,担任完结具有一件作业,例如编译、测验、构建镜像等。每个 Job 都需求指定 Stage ,所以 Job 的履行次序能够经过拟定不同的 Stage 来完结。
  • GitLab Runner:是具体履行 Job 的环境,每个 Runner 在同一时刻只能履行一个 Job。
  • Executor:每个 Runner 在向 GitLab 注册的时分需求指定 Executor,来决议经过何种类型的履行器来完结 Job。

GitLab CI 的作业流程

当有代码 push 到 GitLab 时,就会触发一个 Pipeline。然后进行编译,测验和镜像构建等操作等操作,其间每一步操作都为一个 Job。在 CD 阶段,会将 CI 阶段构建出来的成果根据状况布置到测验环境或出产环境。

GitLab Runner 介绍

Gitlab Runner 分类

GitLab 中有三种类型的 Runner ,别离为:

  • shared:一切项目运用
  • group:group下项目运用
  • specific:指定项目运用

咱们能够根据需求向 GitLab 注册不同类型的 Runner,注册的办法是相同的。

Gitlab Runner 作业进程

Runner 首要会向 GitLab 建议注册恳求,恳求内容中包含 token、tag 等信息,注册成功后 GitLab 会向 Runner 回来一个 token,后续的恳求,Runner 都会带着这个恳求。

注册成功后,Runner 就会不断的向 GitLab 恳求 Job,时刻距离是 3s。若没有恳求到 Job,GitLab 回来 204 No Content。假如恳求到 Job,GitLab 会把 Job 信息回来回来,Runner 在接纳到 Job 之后,会向 GitLab 发送一个承认恳求,一起更新使命的状况。之后,Runner 开端 Job 的履行, 而且会守时地将中心数据,以 Patch 恳求的办法发送给 GitLab。

GitLab Runner 的 Executor

Runner 在实践履行 Job 时,是经过调用 Executor 来完结的。Runner 在注册时供给了 SSH、Shell、Docker、docker-ssh、VirtualBox、Kubernetes 等不同类型的 Executor 来满意不同的场景和需求。

其间咱们常用的有 Shell 和 Docker 等 Executor,Shell 类型首要是运用 Runner 地点主机的环境进行 Job的履行。而 Docker 类型的 Executor 在每个 Job 开端时,拉取镜像出产一个容器,在容器里完结 Job,在 Job 完结后,对应的容器就会被毁掉。由于 Docker 阻隔性强、轻量且收回,咱们在运用时选用 Docker 类型的 Executor 去履行 Job,咱们只需提早做好 Job 所需环境的 Docker 镜像,在每个 Job 界说好 image 即可运用对应的环境,操作快捷。

GitLab Runner 装置与装备

Docker 装置

由于咱们需求运用 Docker 类型的 Executor,所以需求在运转 Runnner 的服务器上先装置 Docker,具体步骤如下(CentOS 环境):

装置需求的软件包,yum-util 供给 yum-config-manager 功用,别的两个是 DeviceMapper 驱动依托:

yum install -y yum-utils device-mapper-persistent-data lvm2

设置 yum 源:

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

装置 Docker:

yum install docker-ce -y

发动并参加开机发动:

systemctl start docker

systemctl enable docker

Gitlab runner 装置与发动

履行下面的指令进行 GitLab Runner 的装置和发动:

`curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/.rpm.sh | sudo bash

sudo yum install gitlab-runner -y

gitlab-runner start`

GitLab Runner 注册与装备更新

发动 GitLab Runner 后还需求向 GitLab 进行注册,在注册前需求从 GitLab 里查询 token。不同类型的 Runner 对应的 token 获取的途径不同。shared Runner 需求 admin 权限的账号,按如下办法能够获取对应的 token。

其他两种类型在对应的页面下( group 或 project 主页)的 setting—>CI/CD—>Runner 能够获取到 token。

Runner 的注册办法分为交互式和非交互式两种。其间交互式注册办法,在输入 gitlab-runner register 指令后,依照提示输入注册所需求的信息,包含 gitlab url、token 和 Runner 姓名等。这边个人比较引荐非交互式指令,能够事前准备好指令,完结一键注册,而且非交互式注册办法供给了更多的注册选项,能够满意更多样化的需求。

按如下示例即可完结一个 Runner 的注册:

  1. gitlab-runner register --non-interactive \
  2. --url "http://git.xxxx.cn" \
  3. --registration-token "xxxxxxxxxxx" \
  4. --executor "docker" \
  5. --docker-image alpine:latest \
  6. --deion "base-runner-docker" \
  7. --tag-list "base-runner" \
  8. --run-untagged="true" \
  9. --docker-privileged="true" \
  10. --docker-pull-policy "if-not-present" \
  11. --docker-volumes /etc/docker/daemon.json:/etc/docker/daemon.json \
  12. --docker-volumes /etc/gitlab-runner/key/docker-config.json:/root/.docker/config.json \
  13. --docker-volumes /etc/gitlab-runner/find_diff_files:/usr/bin/find_diff_files \
  14. --docker-volumes /etc/gitlab-runner/key/id_rsa:/root/.ssh/id_rsa \
  15. --docker-volumes /etc/gitlab-runner/key/test-kube-config:/root/.kube/config

咱们能够经过 --docker-pull-policy 指定 Executor 履行 Job 时 Dokcer 镜像下载战略。--docker-volumes 指定容器与宿主机(即 Runner 运转的服务器)的文件挂载映射联系。上面挂载的文件首要是用于 Runner 在履行 Job 时,运用的一些 key,包含拜访 GitLab、Docker Harbor 和 Kubernetes 集群的 key。当然,假如还有其他文件需求同享给容器,能够经过 --docker-volumes 去指定。

/etc/docker/daemon.json 文件首要为了能够以 http 办法拜访 docker horbor 所做的设置:

{ "insecure-registries" : ["http://docker.vpgame.cn"] }

完结注册后,重启 Runner 即可:

gitlab-runner restart

布置完结后,能够在 GitLab 的 Web 办理界面查看到不同 Runner 的信息。

此外,假如一台服务需求注册多个 Runner ,能够修正 /etc/gitlab-runner/config.toml 中的 concurrent 值添加 Runner 的并发数,修正完之后相同需求重启 Runner。

Docker 根底镜像制造

为了满意不同服务对运转环境的多样化需求,咱们需求为不同言语的服务提早准备不同的根底镜像用于构建镜像阶段运用。此外,CI/CD 所需求的东西镜像也需求制造,作为 Runner 履行 Job 时生成容器所需求的 Docker 镜像。

一切的镜像都以编写 Dockerfile 的方法经过 GitLab 进行办理,而且咱们编写了 .gitlab-ci.yml 文件,使得每次有 Dockerfile 新增或许修正就会触发 Pipeline 进行镜像的构建并上传到 Harbor 上。这种办理办法有以下长处:

  • 依照必定规矩主动构建镜像,能够快速快捷地新建和更新镜像
  • 根据规矩能够找到镜像对应的 Dockerfile,明晰镜像的具体组成
  • 团队成员能够经过提交 Merge Request 自由地构建自己需求的镜像

镜像分类

  • 运转时根底镜像:供给各个言语运转时有必要的东西和相应的 package。
  • CI 镜像:根据运转时根底镜像,添加单元测验、lint、静态剖析等功用,用在 CI/CD 流程中的 test 环节。
  • 打包上线镜像:用在 CI/CD 流程中的 build 和 deploy 环节。

Dockerfile目录结构

每个文件夹都有 Dockerfile 来描绘镜像的根本状况,其间包含了 Java、PHP、Node 和 Go 等不同言语的运转时根底镜像和 CI 镜像,还有 docker-kubectl 这类东西镜像的 Dockerfile。

以 PHP 镜像为例:

该目录下有一个名为 1.0 的文件夹,里边有一个 Dockerfile 用来构建 php fpm 运转时根底进行镜像。首要是在 php:7.1.16-fpm-alpine3.4 加了咱们自己定制化的文件,并指定作业目录和容器初始指令。

  1. FROM php:7.1.16-fpm-alpine3.4
  2. RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories\
  3. && apk upgrade --update && apk add --no-cache --virtual build-dependencies $PHPIZE_DEPS \
  4. tzdata postgresql-dev libxml2-dev libmcrypt libmcrypt-dev libmemcached-dev cyrus-sasl-dev autoconf \
  5. && apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev libmemcached-dev \
  6. && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
  7. && echo "Asia/Shanghai" > /etc/timezone \
  8. && docker-php-ext-configure gd \
  9. --with-gd \
  10. --with-freetype-dir=/usr/include/ \
  11. --with-png-dir=/usr/include/ \
  12. --with-jpeg-dir=/usr/include/ \
  13. && docker-php-ext-install gd pdo pdo_mysql bcmath opcache \
  14. && pecl install memcached apcu redis \
  15. && docker-php-ext-enable memcached apcu redis \
  16. && apk del build-dependencies \
  17. && apk del tzdata \
  18. && rm -rf /var/cache/apk/* \
  19. && rm -rf /tmp/* \
  20. && rm -rf /working/* \
  21. && rm -rf /usr/local/etc/php-fpm.d/*
  22. COPY start_service.sh /usr/local/bin/start_service.sh
  23. COPY read-zk-config /usr/local/bin/read-zk-config
  24. COPY php.ini /usr/local/etc/php/php.ini
  25. COPY www.conf /usr/local/etc/php-fpm.d/www.conf
  26. WORKDIR /work
  27. CMD ["start_service.sh"]

在 1.0/ci-1.0 还有一个 Dockerfile,是用来构建 PHP 在进行单元测验和 lint 操作时所运用的 CI 镜像。能够看到它根据上面的根底运转时镜像添加其他东西来进行构建的。

  1. FROM docker.vpgame.cn/infra/php-1.0
  2. ENV PATH="/root/.composer/vendor/bin:${PATH}"
  3. ENV COMPOSER_ALLOW_SUPERUSER=1
  4. RUN mkdir -p /etc/ssh && echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config
  5. RUN apk --update add --no-cache make libc-dev autoconf gcc openssh-client git bash &&\
  6. echo "apc.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
  7. RUN pecl install xdebug && docker-php-ext-enable xdebug &&\
  8. echo -e "\nzend_extension=xdebug.so" >> /usr/local/etc/php/php.ini
  9. RUN wget https://vp-infra.oss-cn-beijing.aliyuncs.com/gitlab-ci/software/download/1.6.5/composer.phar -O /bin/composer && \
  10. chmod +x /bin/composer && \
  11. composer config -g -q repo.packagist composer https://packagist.laravel-china.org
  12. RUN composer global require -q phpunit/phpunit:~5.0 squizlabs/php_codesniffer:~3.0
  13. WORKDIR /
  14. CMD ["/bin/bash"]

别的 Nginx 目录下相同有 Dockerfile,来定制化咱们 PHP 项目所需求的 Nginx 镜像。

在 GitLab 里第一次添加新的 Dockerfile 或许更改 Dockerfile 时,会牵动 Pipeline 主动进行镜像的构建并上传的咱们私有的 Docker Harbor 上。

镜像主动构建根本原理

由于各个镜像经过 Dockerfile 进行办理, Master 分支有新的兼并,能够经过 git diff 指令找出兼并前后新增或更新的 Dockerfile,然后根据这些 Dockerfile 根据必定的命名规矩构建镜像,并上传到 Docker Harbor 上。

上面指令中 finddifffiles 根据 git diff 指令找出兼并前后有差异的文件。

加快 tips

  • Alpine Linux Package Management(APK)镜像地址:http://mirrors.aliyun.com
  • 一些海外软件下载会比较慢,能够先下载下来上传至阿里云 OSS 后下载。Dockerfile 运用阿里云 OSS 作为下载源,削减构建镜像时刻。

根据 .gitlab-ci.yml 的 CI/CD 流程

在完结 GitLab Runner 以及 Docker 根底镜像的制造之后,咱们便能够进行 CI/CD 流程来完结代码更新之后的单元测验、lint、编译、镜像打包以及布置等作业。经过 GitLab CI 进行 CI/CD 的操作只需求在代码库房里修改和保护一个 .gitlab-ci.yml 文件,每逢代码有更新,GitLab CI 会读取 .gitlab-ci.yml 里的内容,生成一条 Pipeline 进行 CI/CD 的操作。.gitlab-ci.yml 的语法比较简略,根据 yaml 语法进行 Job 的描绘。咱们把 CI/CD 流程中所需求完结的使命拆分成文件里的 Job,只需对每个 Job 完结明晰的界说,便可构成一套适宜高效并具有普适性的 CI/CD 流程。

界说 stages

stages 是一个非常重要的概念, 在 .gitlab-ci.yml 中进行大局界说, 在界说 Job 时指定其间的值来标明 Job 所在的 stage。而在 stages 里元素的次序界说了 Job 的履行次序:一切在相同 stage 的 Job 会并行履行,只要当时 stage 的一切成功完结后,后边 stage 的 Job 才会去履行。

例如,界说如下 stages:

`stages:

  • build
  • test
  • deploy`
  1. 首要,一切 build 里的 Job 会并行履行;
  2. 当 build 里一切 Job 履行成功, test 里一切 Job 会并行履行;
  3. 假如 test 里一切 Job 履行成功, deploy 里一切 Job 会并行履行;
  4. 假如 deploy 里一切 Job 履行成功, 当时 Pipeline 会被符号为 passed;
  5. 当某个 stage 的 Job 履行失利, Pipeline 会符号为为 failed,其后续stage 的 Job 都不会被履行。

Job 的描绘

Job 是 .gitlab-ci.yml 文件中最重要的组成部分,一切的 CI/CD 流程中所履行的使命均能够需求经过界说 Job 来完结。具体来说,咱们能够经过关键字来对每一个 Job 进行描绘。由于 Job 中的关键字很多,而且用法比较丰厚,这边针对咱们自己实战中的一个 Job 来进行阐明。

  1. unittest:
  2. stage: test
  3. image: docker.vpgame.cn/infra/php-1.0-ci-1.1
  4. services:
  5. - name: docker.vpgame.cn/infra/mysql-5.6-multi
  6. alias: mysql
  7. - name: redis:4.0
  8. alias: redis_default
  9. :
  10. - mv .env.tp .env
  11. - composer install --no-dev
  12. - phpunit -v --coverage-text --colors=never --coverage-html=coverage --stderr
  13. artifacts:
  14. when: on_success
  15. paths:
  16. - vendor/
  17. - coverage/
  18. expire_in: 1 hour
  19. coverage: '/^\s*Lines:\s*\d+.\d+\%/'
  20. only:
  21. - branches
  22. - tags
  23. tags:
  24. - base-runner

上面的 Job 首要完结了单元测验的功用,在起始行界说了 Job 的称号。下面咱们来解说 Job 每个关键字的具体意义。

stage,界说了 Job 所在的 stage,值为界说在大局中 stages 里的值。

image,指定了 Runner 运转所需求的镜像,这个镜像是咱们之前制造的根本镜像。经过该镜像运转的 Docker 便是 Job 运转的环境。

services,Runner 所运转的 Docker 所需求的衔接依托,在这边别离界说了 MySQL 和 Redis,在 Job 运转时会去衔接这两个镜像生成的 Docker。

,Job 运转的具体的指令 ,经过 Shell 来描绘。此 Job 中的 首要完结了代码的编译和单元测验。

artifacts,首要是将此 Job 中完结的成果打包保存下来,能够经过 when 指定何时保存,path 界说了保存的文件途径, expire_in 指定了成果保存的有效期。与之对应的是 dependencies 参数,假如其他 Job 需求此 Job 的 artifacts ,只需求在 Job 依照如下界说即可。

`dependencies:

  • unittest`

only 关键字指定了 Job 触发的机遇,该比方中阐明只要分支兼并或许打 tag 的状况下,该 Job 才会被触发。

与 only 相对还有 except 关键字来扫除触发 Job 某些状况。此外 only 还支撑正则表达式,比方:

  1. job:
  2. only:
  3. - /^issue-.*$/
  4. except:
  5. - branches

这个比方中,只要以 issue- 最初 tag 符号才会触发 Job。假如不加 except 参数,以 issue- 最初的分支 或许 tag 符号会会触发 Job。

tags,tags关键字首要是用来指定运转的 Runner 类型。在咱们实践运用中,布置测验环境和出产环境所选用的 Runner 是不相同的,它们是经过不同的 tag 去标识区别。

所以,咱们在 Job 界说中,经过 tags 指定 Runner 的值,来指定所需求的 Runner。

咱们能够看到 Job 的界说非常的明晰和灵敏,关于 Job 的运用远不止这些功用,更具体的用法能够参阅 GitLab CI/CD 官方文档。

CI/CD 流程编列

在清楚了怎么描绘一个 Job 之后,咱们经过界说一个个 Job,并进行编列构成 Pipelines。由于咱们能够描绘设定 Job 的触发条件,所以经过不同的条件能够触发构成不相同的 Pipelines。

在 PHP 项目 Kubernetes 上线进程中,咱们规则了兼并 Master 分支会进行 lint、unitest、build-test 以及 deploy-test 四个 Job。

在测验环境验证经过之后,咱们再经过打 tag 进行正式环境的上线。此处的 Pipelines 包含了 unittest、build-pro 和 deploy-pro 三个 Job。

build stage:

  1. \# Build stage
  2. .build-op:
  3. stage: build
  4. dependencies:
  5. - unittest
  6. image: docker.vpgame.cn/infra/docker-kubectl-1.0
  7. services:
  8. - name: docker:dind
  9. entrypoint: ["dockerd-entrypoint.sh"]
  10. :
  11. - echo "Image name:" ${DOCKER_IMAGE_NAME}
  12. - docker build -t ${DOCKER_IMAGE_NAME} .
  13. - docker push ${DOCKER_IMAGE_NAME}
  14. tags:
  15. - base-runner
  16. build-test:
  17. extends: .build-op
  18. variables:
  19. DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}
  20. only:
  21. - /^testing/
  22. - master
  23. build-prod:
  24. extends: .build-op
  25. variables:
  26. DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_TAG}
  27. only:
  28. - tags

在这边,由于 build 阶段中测验环境和出产环境进行镜像打包时根本操作时是相同的,都是根据 Dockerfile 进行镜像的 build 和镜像库房的上传。这儿用到了一个 extend 参数,能够削减重复的 Job 描绘,使得描绘愈加地简练明晰。

咱们先界说一个 .build-op 的 Job,然后 build-test 和 build-prod 都经过 extend 进行承继,能够经过界说关键字来新增或掩盖 .build-op 中的装备。比方 build-prod 从头界说了变量( variables)DOCKER_IMAGE_NAME以及触发条件(only)更改为了打 tag 。

这边咱们还需求注意到的是在界说 DOCKER_IMAGE_NAME 时,咱们引用了 GitLab CI 本身的一些变量,比方 CI_COMMIT_TAG 表明项意图 commit 的 tag 称号。咱们在界说 Job 变量时,可能会引用到一些 GitLab CI 本身变量,关于这些变量的阐明能够参阅 GitLab CI/CD Variables 中文文档。

deploy stage:

  1. \# Deploy stage
  2. .deploy-op:
  3. stage: deploy
  4. image: docker.vpgame.cn/infra/docker-kubectl-1.0
  5. :
  6. - echo "Image name:" ${DOCKER_IMAGE_NAME}
  7. - echo ${APP_NAME}
  8. - sed -i "s~__NAMESPACE__~${NAMESPACE}~g" deployment.yml service.yml
  9. - sed -i "s~__APP_NAME__~${APP_NAME}~g" deployment.yml service.yml
  10. - sed -i "s~__PROJECT_NAME__~${CI_PROJECT_NAME}~g" deployment.yml
  11. - sed -i "s~__PROJECT_NAMESPACE__~${CI_PROJECT_NAMESPACE}~g" deployment.yml
  12. - sed -i "s~__GROUP_NAME__~${GROUP_NAME}~g" deployment.yml
  13. - sed -i "s~__VERSION__~${VERSION}~g" deployment.yml
  14. - sed -i "s~__REPLICAS__~${REPLICAS}~g" deployment.yml
  15. - kubectl apply -f deployment.yml
  16. - kubectl apply -f service.yml
  17. - kubectl rollout status -f deployment.yml
  18. - kubectl get all,ing -l app=${APP_NAME} -n $NAMESPACE
  19. \# Deploy test environment
  20. deploy-test:
  21. variables:
  22. REPLICAS: 2
  23. VERSION: ${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}
  24. extends: .deploy-op
  25. environment:
  26. name: test
  27. url: http://example.com
  28. only:
  29. - /^testing/
  30. - master
  31. tags:
  32. - base-runner
  33. \# Deploy prod environment
  34. deploy-prod:
  35. variables:
  36. REPLICAS: 3
  37. VERSION: ${CI_COMMIT_TAG}
  38. extends: .deploy-op
  39. environment:
  40. name: prod
  41. url: http://example.com
  42. only:
  43. - tags
  44. tags:
  45. - pro-deploy

与 build 阶段相似,先先界说一个 .deploy-op 的 Job,然后 deploy-test 和 deploy-prod 都经过 extend 进行承继。

.deploy-op 首要完结了对 Kubernetes Deployment 和 Service 模板文件的一些变量的替换,以及根据生成的 Deployment 和 Service 文件进行 Kubernetes 服务的布置。

deploy-test 和 deploy-prod 两个 Job 界说了不同变量(variables)以及触发条件(only)。除此之外, deploy-prod 经过 tags 关键字来运用不同的 Runner,将布置的方针集群指向给出产环境的 Kubernetes。

这儿还有一个关键字 environment 需求特别阐明,在界说了 environment 之后,咱们能够在 GitLab 中查看每次布置的一些信息。除了查看每次布置的一些信息之外,咱们还能够很便利地进行从头布置和回滚。

能够看到,经过对 Job 的关键字进行装备,咱们能够灵敏地编列出咱们所需求的 CI/CD 流程,非常好地满意多样化的场景。

Deployment 与 Service 装备

在 CI/CD 流程中完结 Docker 镜像的打包使命之后需求将服务所对应的镜像布置到 Kubernetes 集群中。Kubernetes 供给了多种能够编列调度的资源方针。首要,咱们简略了解一下 Kubernetes 中的一些根本资源。

Kubernetes 根本资源方针概览

Pod

Pod 作为无状况运用的运转实体是其间最常用的一种资源方针, Kubernetes 中资源调度最小的根本单元,它包含一个或多个严密联系的容器。这些容器同享存储、网络和命名空间,以及怎么运转的标准。

在 Kubernetes 中, Pod 对错耐久的,会由于节点毛病或许网络不通等状况而被毁掉和重建。所以咱们在 Kubernetes 中一般不会直接创立一个独立的 Pod,而是经过多个 Pod 对外供给服务。

ReplicaSet

ReplicaSet 是 Kubernetes 中的一种副本操控器,操控由其办理的 Pod,使 Pod 副本的数量维持在预设的个数。ReplicaSets 能够独立运用,可是在大多数场景下被 Deployments 作为和谐 Pod 创立,删去和更新的机制。

Deployment

Deployment 为 Pod 和 ReplicaSet 供给了一个声明式界说办法。经过在 Deployment 中进行方针状况的描绘,Deployment controller 会将 Pod 和 ReplicaSet 的实践状况改变为所设定的方针状况。Deployment 典型的运用场景包含:

  • 界说 Deployment 来创立 Pod 和 ReplicaSet
  • 翻滚晋级和回滚运用
  • 扩容和缩容
  • 暂停和持续 Deployment

Service

在 Kubernetes 中,Pod 会被随时创立或毁掉,每个 Pod 都有自己的 IP,这些 IP 也无法耐久存在,所以需求 Service 来供给服务发现和负载均衡才能。Service 是一个界说了一组 Pod 的战略的笼统,经过 Label Selector 来确认后端拜访的 Pod,然后为客户端拜访服务供给了一个进口。每个 Service 会对应一个集群内部的 ClusterIP,集群内部能够经过 ClusterIP 拜访一个服务。假如需求对集群外部供给服务,能够经过 NodePort 或 LoadBalancer 办法。

deployment.yml 装备

deployment.yml 文件用来界说 Deployment。首要经过一个简略的 deployment.yml 装备文件了解 Deployment 的装备格局。

上图中 deployment.yml 分为8个部分,别离如下:

  1. apiVersion 为当时装备格局的版别
  2. kind 指定了资源类型,这边当然是 Deployment
  3. metadata 是该资源的元数据,其间 name 是必需的数据项,还能够指定 label 给资源加上标签
  4. spec 部分是该 Deployment 的标准阐明
  5. spec.replicas 指定了 Pod 的副本数量
  6. spec.template 界说 Pod 的根本信息,经过 spec.template.metadata 和 spec.template.spec 指定
  7. spec.template.metadata 界说 Pod 的元数据。至少需求界说一个 label 用于 Service 辨认转发的 Pod, label 是经过 key-value 方法指定的
  8. spec.template.spec 描绘 Pod 的标准,此部分界说 Pod 中每一个容器的特点,name 和 image 是必需项

在实践运用中,还有更多灵敏个性化的装备。咱们在 Kubernetes 的布置实践中拟定了相关的标准,在以上根底结构上进行装备,得到满意咱们实践需求的 deployment.yml 装备文件。

在 Kubernetes 的搬迁实践中,咱们首要在以下方面临 Deployment 的装备进行了标准的约好:

文件模板化

首要咱们的 deployment.yml 装备文件是带有变量的模版文件,如下所示:

  1. apiVersion: apps/v1beta2
  2. kind: Deployment
  3. metadata:
  4. labels:
  5. app: __APP_NAME__
  6. group: __GROUP_NAME__
  7. name: __APP_NAME__
  8. namespace: __NAMESPACE__

APPNAME__、__GROUPNAMENAMESPACE 这种方法的变量都是在 CI/CD 流程中会被替换成 GitLab 每个 project 所对应的变量,意图是为了多了 project 用相同的 deployment.yml 文件,以便在进行 Kubernetes 搬迁时能够快速仿制,进步功率。

服务称号

  • Kubernetes 中运转的 Service 以及 Deployment 称号由 GitLab 中的 groupname 和 projectname 组成,即 {{groupname}}-{{projectname}},例:microservice-common。此称号记为 app_name,作为每个服务在 Kubernetes 中的仅有标识。这些变量能够经过 GitLab-CI 的内置变量中进行获取,无需对每个 project 进行特别的装备。
  • Lables 中用于辨认服务的标签与 Deployment 称号保持共同,共同设置为 app:{{app_name}}。

资源分配

节点装备战略,以项目组作为各项目 Pod 运转在哪些 Node 节点的根据,归于同一项目组的项意图 Pod 运转在同一批 Node 节点。具体操作办法为给每个 Node 节点打上形如 group:__GROUP_NAME__ 的标签,在 deployment.yml 文件中做如下设置进行 Pod 的 Node 节点挑选:

  1. ...
  2. spec:
  3. ...
  4. template:
  5. ...
  6. spec:
  7. ...
  8. affinity:
  9. nodeAffinity:
  10. requiredDuringSchedulingIgnoredDuringExecution:
  11. nodeSelectorTerms:
  12. - matchExpressions:
  13. - key: group
  14. operator: In
  15. values:
  16. - __GROUP_NAME__
  17. ...

资源恳求巨细,关于一些重要的线上运用,limit 和 request 设置共同,资源缺乏时 Kubernetes 会优先确保这些 Pod 正常运转。为了进步资源运用率。对一些非中心,而且资源不长时间占用的运用,能够恰当削减 Pod 的 request,这样 Pod 在调度时能够被分配到资源不是非常富余的节点,进步运用率。可是当节点的资源缺乏时,也会优先被驱赶或被 oom kill。

健康查看(Liveness/Readiness)装备

Liveness 首要用于勘探容器是否存活,若监控查看失利会对容器进行重启操作。Readiness 则是经过监控检测容器是否正常供给服务来决议是否参加到 Service 的转发列表接纳恳求流量。Readiness 在晋级进程能够发挥重要的效果,避免晋级时反常的新版别 Pod 替换旧版别 Pod 导致整个运用将无法对外供给服务的状况。

每个服务有必要供给能够正常拜访的接口,在 deployment.yml 文件装备好相应的监控检测战略。

  1. ...
  2. spec:
  3. ...
  4. template:
  5. ...
  6. spec:
  7. ...
  8. containers:
  9. - name: fpm
  10. livenessProbe:
  11. httpGet:
  12. path: /__PROJECT_NAME__
  13. port: 80
  14. initialDelaySeconds: 3
  15. periodSeconds: 5
  16. readinessProbe:
  17. httpGet:
  18. path: /__PROJECT_NAME__
  19. port: 80
  20. initialDelaySeconds: 3
  21. periodSeconds: 5
  22. ...
  23. ...

晋级战略装备

晋级战略咱们挑选 RollingUpdate 的办法,即在晋级进程中翻滚式地逐渐新建新版别的 Pod,待新建 Pod 正常发动后逐渐 kill 掉老版别的 Pod,终究悉数新版别的 Pod 替换为旧版别的 Pod。

咱们还能够设置 maxSurge 和 maxUnavailable 的值别离操控晋级进程中最多能够比原先设置多出的 Pod 份额以及晋级进程中最多有多少份额 Pod 处于无法供给服务的状况。

日志装备

选用 log-tail 对容器日志进行收集,一切服务的日志都上签到阿里云日志服务的一个 log-store中。在 deployment.yml 文件里装备如下:

  1. ...
  2. spec:
  3. ...
  4. template:
  5. ...
  6. spec:
  7. ...
  8. containers:
  9. - name: fpm
  10. env:
  11. - name: aliyun_logs_vpgame
  12. value: stdout
  13. - name: aliyun_logs_vpgame_tags
  14. value: topic=__APP_NAME__
  15. ...
  16. ...

经过设置环境变量的办法来指定上传的 Logstore 和对应的 tag,其间 name 表明 Logstore 的称号。经过 topic 字段区别不同服务的日志。

监控装备

经过在 Deployment 中添加 annotations 的办法,令 Prometheus 能够获取每个 Pod 的事务监控数据。装备示例如下:

  1. ...
  2. spec:
  3. ...
  4. template:
  5. metadata:
  6. annotations:
  7. prometheus.io/scrape: "true"
  8. prometheus.io/port: "80"
  9. prometheus.io/path: /{{ project_name }}/metrics
  10. ...

其间 prometheus.io/scrape: "true" 表明能够被 Prometheus 获取,prometheus.io/port 表明监控数据的端口,prometheus.io/path 表明获取监控数据的途径。

service.yml 装备

service.yml 文件首要对 Service 进行了描绘。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. annotations:
  5. service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet
  6. labels:
  7. app: __APP_NAME__
  8. name: __APP_NAME__
  9. namespace: __NAMESPACE__
  10. spec:
  11. ports:
  12. - port: 80
  13. protocol: TCP
  14. targetPort: 80
  15. selector:
  16. app: __APP_NAME__
  17. type: LoadBalancer
  18. 对 Service 的界说比较于 Deoloyment 要简略的多,经过界说 spec.ports 的相关参数能够指定 Service 的对外露出的端口现已转发到后端 Pod 的端口。spec.selector 则是指定了需求转发的 Pod 的 label。

别的,咱们这边是经过负载均衡器类型对外供给服务,这是经过界说 spec.type 为 LoadBalancer 完结的。经过添加 metadata.annotations 为 service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet 能够在对该 Service 进行创立的一起创立一个阿里云内网 SLB 作为对该 Service 恳求流量的进口。

如上图所示,EXTERNAL-IP 即为 SLB 的 IP。

总结

在以上作业的根底上,咱们对各个服务区分为几类(现在根本上依照言语进行区分),然后为每一类中的服务经过 .gitlab-ci.yml 拟定一套共同的 CI/CD 流程,与此相同的,同一类中的服务共用一个 Deployment 和 Service 模板。这样咱们在进行服务搬迁到 Kubernetes 环境时能够完结快速高效地搬迁。

当然,这仅仅搬迁实践路上迈出的第一步,在 Kubernetes 中的服务的稳定性、功能、主动弹性等方面还需求更深化地探究和研讨。

------------------------------------

本文作者: 伍冲斌

原文链接:https://yq.aliyun.com/articles/720109?utm_content=g_1000079876

本文为云栖社区原创内容,未经答应不得转载。

推荐阅读