做者 | 伍冲斌 VPGAME 运维开发工程师php
导读:VPGAME 是集赛事运营、媒体资讯、大数据分析、玩家社群、游戏周边等为一体的综合电竞服务平台。总部位于中国杭州,在上海和美国西雅图分别设立了电竞大数据研发中心和 AI 研发中心。本文将讲述 VPGAME 将服务器迁移至 Kubernetes 的过程。
随着容器技术的日趋成熟,公司近期计划将服务迁移至容器环境,经过 Kubernetes 对容器进行调度、编排和管理。并借此机会,对服务进行标准化,优化整个 CI/CD 的流程,提升服务部署的效率。html
CI/CD 工具上,咱们选择了 GitLab-CI。GitLab-CI 就是一套配合 GitLab 使用的持续集成系统,以完成代码提交以后的安装依赖、编译、单元测试、lint、镜像构建以及发布等工做。node
GitLab-CI 完美地和 GitLab 进行集成,在使用的时候只须要安装配置 gitlab-runner 便可。GitLab-Runner 在向 GitLab 完成注册后能够提供进行 CI/CD 操做的环境,负责从 GitLab 中拉取代码,根据代码仓库中配置的 gitlab-ci.yml ,执行相应的命令进行 CI/CD 工做。mysql
相比于 Jenkins,GitLab-CI 配置简单,只需在工程中配置 gitlab-ci.yml 文件完成 CI/CD 流程的编写,不须要像在 Jenkins 里同样配置 webhook 回调地址,也不须要 Jenkins 新建这个项目的编译配置。而且我的认为 GitLab 的 CI/CD 过程显示比 Jenkins 更加美观。固然 Jenkins 依靠它丰富的插件,能够配置不少 GitLab-CI 不存在的功能。按照如今咱们的需求, GitLab-CI 简单易用,在功能也知足咱们的需求。linux
传统的服务部署方式是在操做系统中安装好相应的应用依赖,而后进行应用服务的安装,这种部署方式的缺点是将服务的程序、配置、依赖库以及生命周期与宿主机操做系统紧密地耦合在一块儿,对服务的升级、扩缩容、迁移等操做不是十分便利。nginx
容器的部署方式则是以镜像为核心,在代码进行编译构建时,将应用程序与应用程序运行所须要的依赖打包成一个镜像,在部署阶段,经过镜像建立容器实例完成服务的部署和运行。从而实现以应用为中心的管理,容器的隔离性实现了资源的隔离,因为容器不须要依赖宿主机的操做系统环境,因此能够很好地保证开发、测试和生产环境的一致性。此外,因为构建好的镜像是不可变的,而且能够经过 tag 进行版本控制,因此能够提供可靠、频繁的容器镜像构建和部署,亦可方便及快速进行回滚操做。laravel
Kubernetes(简称 k8s),做为一个容器调度、编排和管理平台,能够在物理或虚拟机集群上调度和运行应用程序容器,提供了一个以容器为核心的基础架构。经过 Kubernetes,对容器进行编排和管理,能够:git
咱们在服务迁移中选用了阿里云的容器服务,它基于原生 Kubernetes 进行适配和加强,简化集群的搭建和扩容等工做,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳的 Kubernetes 容器化应用运行环境。在便捷性上,能够经过 Web 界面一键完成 Kubernetes 集群的建立、升级以及节点的扩缩容。功能上,在网络、存储、负载均衡和监控方面与阿里云资源集成,在迁移过程当中能够最小化减小迁移带来的影响。web
此外,在选择集群建立时,咱们选择了托管版 Kubernetes,只需建立 Worker 节点,Master 节点由容器服务建立并托管。如此一来,咱们在 Worker 节点的规划与资源隔离上仍是具有自主性和灵活性的同时不须要运维管理 Kubernetes 集群 Master 节点,能够将更多的精力关注在应用服务上。正则表达式
在介绍 GitLab CI 以前,首先简单介绍一下 GitLab CI 里的一些基本概念,具体以下:
当有代码 push 到 GitLab 时,就会触发一个 Pipeline。而后进行编译,测试和镜像构建等操做等操做,其中每一步操做都为一个 Job。在 CD 阶段,会将 CI 阶段构建出来的结果根据状况部署到测试环境或生产环境。
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。
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 便可使用对应的环境,操做便捷。
因为咱们须要使用 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 的安装和启动:
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash sudo yum install gitlab-runner -y gitlab-runner start
启动 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 的注册:
gitlab-runner register --non-interactive \ --url "http://git.xxxx.cn" \ --registration-token "xxxxxxxxxxx" \ --executor "docker" \ --docker-image alpine:latest \ --description "base-runner-docker" \ --tag-list "base-runner" \ --run-untagged="true" \ --docker-privileged="true" \ --docker-pull-policy "if-not-present" \ --docker-volumes /etc/docker/daemon.json:/etc/docker/daemon.json \ --docker-volumes /etc/gitlab-runner/key/docker-config.json:/root/.docker/config.json \ --docker-volumes /etc/gitlab-runner/find_diff_files:/usr/bin/find_diff_files \ --docker-volumes /etc/gitlab-runner/key/id_rsa:/root/.ssh/id_rsa \ --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。
为了知足不一样服务对运行环境的多样化需求,咱们须要为不一样语言的服务提早准备不一样的基础镜像用于构建镜像阶段使用。此外,CI/CD 所须要的工具镜像也须要制做,做为 Runner 执行 Job 时生成容器所须要的 Docker 镜像。
全部的镜像都以编写 Dockerfile 的形式经过 GitLab 进行管理,而且咱们编写了 .gitlab-ci.yml 文件,使得每次有 Dockerfile 新增或者修改就会触发 Pipeline 进行镜像的构建并上传到 Harbor 上。这种管理方式有如下优势:
每一个文件夹都有 Dockerfile 来描述镜像的基本状况,其中包含了 Java、PHP、Node 和 Go 等不一样语言的运行时基础镜像和 CI 镜像,还有 docker-kubectl 这类工具镜像的 Dockerfile。
以 PHP 镜像为例:
php/ ├── 1.0 │ ├── Dockerfile │ ├── ci-1.0 │ │ └── Dockerfile │ ├── php.ini │ ├── read-zk-config │ ├── start_service.sh │ └── www.conf └── nginx ├── Dockerfile ├── api.vpgame.com.conf └── nginx.conf
该目录下有一个名为 1.0 的文件夹,里面有一个 Dockerfile 用来构建 php fpm 运行时基础进行镜像。主要是在 php:7.1.16-fpm-alpine3.4 加了咱们本身定制化的文件,并指定工做目录和容器初始命令。
FROM php:7.1.16-fpm-alpine3.4 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories\ && apk upgrade --update && apk add --no-cache --virtual build-dependencies $PHPIZE_DEPS \ tzdata postgresql-dev libxml2-dev libmcrypt libmcrypt-dev libmemcached-dev cyrus-sasl-dev autoconf \ && apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev libmemcached-dev \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone \ && docker-php-ext-configure gd \ --with-gd \ --with-freetype-dir=/usr/include/ \ --with-png-dir=/usr/include/ \ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-install gd pdo pdo_mysql bcmath opcache \ && pecl install memcached apcu redis \ && docker-php-ext-enable memcached apcu redis \ && apk del build-dependencies \ && apk del tzdata \ && rm -rf /var/cache/apk/* \ && rm -rf /tmp/* \ && rm -rf /working/* \ && rm -rf /usr/local/etc/php-fpm.d/* COPY start_service.sh /usr/local/bin/start_service.sh COPY read-zk-config /usr/local/bin/read-zk-config COPY php.ini /usr/local/etc/php/php.ini COPY www.conf /usr/local/etc/php-fpm.d/www.conf WORKDIR /work CMD ["start_service.sh"]
在 1.0/ci-1.0 还有一个 Dockerfile,是用来构建 PHP 在进行单元测试和 lint 操做时所使用的 CI 镜像。能够看到它基于上面的基础运行时镜像增长其余工具来进行构建的。
FROM docker.vpgame.cn/infra/php-1.0 ENV PATH="/root/.composer/vendor/bin:${PATH}" ENV COMPOSER_ALLOW_SUPERUSER=1 RUN mkdir -p /etc/ssh && echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config RUN apk --update add --no-cache make libc-dev autoconf gcc openssh-client git bash &&\ echo "apc.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini RUN pecl install xdebug && docker-php-ext-enable xdebug &&\ echo -e "\nzend_extension=xdebug.so" >> /usr/local/etc/php/php.ini RUN wget https://vp-infra.oss-cn-beijing.aliyuncs.com/gitlab-ci/software/download/1.6.5/composer.phar -O /bin/composer && \ chmod +x /bin/composer && \ composer config -g -q repo.packagist composer https://packagist.laravel-china.org RUN composer global require -q phpunit/phpunit:~5.0 squizlabs/php_codesniffer:~3.0 WORKDIR / CMD ["/bin/bash"]
另外 Nginx 目录下一样有 Dockerfile,来定制化咱们 PHP 项目所须要的 Nginx 镜像。
在 GitLab 里第一次增长新的 Dockerfile 或者更改 Dockerfile 时,会触动 Pipeline 自动进行镜像的构建并上传的咱们私有的 Docker Harbor 上。
因为各个镜像经过 Dockerfile 进行管理, Master 分支有新的合并,能够经过 git diff 命令找出合并先后新增或更新的 Dockerfile,而后根据这些 Dockerfile 依据必定的命名规则构建镜像,并上传到 Docker Harbor 上。
for FILE in `bash ./find_diff_files|grep Dockerfile|sort`; do DIR=`dirname "$FILE"`; IMAGE_NAME=`echo $DIR | sed -e 's/\//-/g'`; echo $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME; docker build -t $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME -f $FILE $DIR; docker push $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME; done
上面命令中 finddifffiles 基于 git diff 命令找出合并先后有差别的文件。
在完成 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 是一个很是重要的概念, 在 .gitlab-ci.yml 中进行全局定义, 在定义 Job 时指定其中的值来代表 Job 所处的 stage。而在 stages 里元素的顺序定义了 Job 的执行顺序:全部在相同 stage 的 Job 会并行执行,只有当前 stage 的全部成功完成后,后面 stage 的 Job 才会去执行。
例如,定义以下 stages:
stages: - build - test - deploy
Job 是 .gitlab-ci.yml 文件中最重要的组成部分,全部的 CI/CD 流程中所执行的任务都可以须要经过定义 Job 来实现。具体来讲,咱们能够经过关键字来对每个 Job 进行描述。因为 Job 中的关键字众多,而且用法比较丰富,这边针对咱们本身实战中的一个 Job 来进行说明。
unittest: stage: test image: docker.vpgame.cn/infra/php-1.0-ci-1.1 services: - name: docker.vpgame.cn/infra/mysql-5.6-multi alias: mysql - name: redis:4.0 alias: redis_default script: - mv .env.tp .env - composer install --no-dev - phpunit -v --coverage-text --colors=never --coverage-html=coverage --stderr artifacts: when: on_success paths: - vendor/ - coverage/ expire_in: 1 hour coverage: '/^\s*Lines:\s*\d+.\d+\%/' only: - branches - tags tags: - base-runner
上面的 Job 主要完成了单元测试的功能,在起始行定义了 Job 的名称。下面咱们来解释 Job 每一个关键字的具体含义。
dependencies: - unittest
job: only: - /^issue-.*$/ except: - branches
这个例子中,只有以 issue- 开头 tag 标记才会触发 Job。若是不加 except 参数,以 issue- 开头的分支或者 tag 标记会触发 Job。
因此,咱们在 Job 定义中,经过 tags 指定 Runner 的值,来指定所须要的 Runner。
咱们能够看到 Job 的定义很是的清晰和灵活,关于 Job 的使用远不止这些功能,更详细的用法能够参考 GitLab 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。
在 .gitlab-ci.yml 文件中,test 阶段主要完成 lint 和 unitest 两个 Job,不一样的语言在进行 Job 定义时会各有不一样。咱们重点来看一下 build 和 deploy 两个 stage 的 Job 描述。build stage:
# Build stage .build-op: stage: build dependencies: - unittest image: docker.vpgame.cn/infra/docker-kubectl-1.0 services: - name: docker:dind entrypoint: ["dockerd-entrypoint.sh"] script: - echo "Image name:" ${DOCKER_IMAGE_NAME} - docker build -t ${DOCKER_IMAGE_NAME} . - docker push ${DOCKER_IMAGE_NAME} tags: - base-runner build-test: extends: .build-op variables: DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA} only: - /^testing/ - master build-prod: extends: .build-op variables: DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_TAG} only: - 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:
# Deploy stage .deploy-op: stage: deploy image: docker.vpgame.cn/infra/docker-kubectl-1.0 script: - echo "Image name:" ${DOCKER_IMAGE_NAME} - echo ${APP_NAME} - sed -i "s~__NAMESPACE__~${NAMESPACE}~g" deployment.yml service.yml - sed -i "s~__APP_NAME__~${APP_NAME}~g" deployment.yml service.yml - sed -i "s~__PROJECT_NAME__~${CI_PROJECT_NAME}~g" deployment.yml - sed -i "s~__PROJECT_NAMESPACE__~${CI_PROJECT_NAMESPACE}~g" deployment.yml - sed -i "s~__GROUP_NAME__~${GROUP_NAME}~g" deployment.yml - sed -i "s~__VERSION__~${VERSION}~g" deployment.yml - sed -i "s~__REPLICAS__~${REPLICAS}~g" deployment.yml - kubectl apply -f deployment.yml - kubectl apply -f service.yml - kubectl rollout status -f deployment.yml - kubectl get all,ing -l app=${APP_NAME} -n $NAMESPACE # Deploy test environment deploy-test: variables: REPLICAS: 2 VERSION: ${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA} extends: .deploy-op environment: name: test url: http://example.com only: - /^testing/ - master tags: - base-runner # Deploy prod environment deploy-prod: variables: REPLICAS: 3 VERSION: ${CI_COMMIT_TAG} extends: .deploy-op environment: name: prod url: http://example.com only: - tags tags: - 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 流程,很是好地知足多样化的场景。
在 CI/CD 流程中完成 Docker 镜像的打包任务以后须要将服务所对应的镜像部署到 Kubernetes 集群中。Kubernetes 提供了多种能够编排调度的资源对象。首先,咱们简单了解一下 Kubernetes 中的一些基本资源。
Pod 做为无状态应用的运行实体是其中最经常使用的一种资源对象,Kubernetes 中资源调度最小的基本单元,它包含一个或多个紧密联系的容器。这些容器共享存储、网络和命名空间,以及如何运行的规范。
在 Kubernetes 中,Pod 是非持久的,会由于节点故障或者网络不通等状况而被销毁和重建。因此咱们在 Kubernetes 中通常不会直接建立一个独立的 Pod,而是经过多个 Pod 对外提供服务。
ReplicaSet 是 Kubernetes 中的一种副本控制器,控制由其管理的 Pod,使 Pod 副本的数量维持在预设的个数。ReplicaSets 能够独立使用,可是在大多数场景下被 Deployments 做为协调 Pod 建立,删除和更新的机制。
Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义方法。经过在 Deployment 中进行目标状态的描述,Deployment controller 会将 Pod 和 ReplicaSet 的实际状态改变为所设定的目标状态。Deployment 典型的应用场景包括:
在 Kubernetes 中,Pod 会被随时建立或销毁,每一个 Pod 都有本身的 IP,这些 IP 也没法持久存在,因此须要 Service 来提供服务发现和负载均衡能力。
Service 是一个定义了一组 Pod 的策略的抽象,经过 Label Selector 来肯定后端访问的 Pod,从而为客户端访问服务提供了一个入口。每一个 Service 会对应一个集群内部的 ClusterIP,集群内部能够经过 ClusterIP 访问一个服务。若是须要对集群外部提供服务,能够经过 NodePort 或 LoadBalancer 方式。
deployment.yml 文件用来定义 Deployment。首先经过一个简单的 deployment.yml 配置文件熟悉 Deployment 的配置格式。
上图中 deployment.yml 分为 8 个部分,分别以下:
在实际应用中,还有更多灵活个性化的配置。咱们在 Kubernetes 的部署实践中制定了相关的规范,在以上基础结构上进行配置,获得知足咱们实际需求的 deployment.yml 配置文件。
在 Kubernetes 的迁移实践中,咱们主要在如下方面对 Deployment 的配置进行了规范的约定:
首先咱们的 deployment.yml 配置文件是带有变量的模版文件,以下所示:
apiVersion: apps/v1beta2 kind: Deployment metadata: labels: app: __APP_NAME__ group: __GROUP_NAME__ name: __APP_NAME__ namespace: __NAMESPACE__
APPNAME__、__GROUPNAME 和 __NAMESPACE__ 这种形式的变量都是在 CI/CD 流程中会被替换成 GitLab 每一个 project 所对应的变量,目的是为了多了 project 用相同的 deployment.yml 文件,以便在进行 Kubernetes 迁移时能够快速复制,提升效率。
节点配置策略,以项目组做为各项目 Pod 运行在哪些 Node 节点的依据,属于同一项目组的项目的 Pod 运行在同一批 Node 节点。具体操做方式为给每一个 Node 节点打上形如 group:__GROUP_NAME__ 的标签,在 deployment.yml 文件中作以下设置进行 Pod 的 Node 节点选择:
... spec: ... template: ... spec: ... affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: group operator: In values: - __GROUP_NAME__ ...
资源请求大小,对于一些重要的线上应用,limit 和 request 设置一致,资源不足时 Kubernetes 会优先保证这些 Pod 正常运行。为了提升资源利用率。对一些非核心,而且资源不长期占用的应用,能够适当减小 Pod 的 request,这样 Pod 在调度时能够被分配到资源不是十分充裕的节点,提升使用率。可是当节点的资源不足时,也会优先被驱逐或被 oom kill。
Liveness 主要用于探测容器是否存活,若监控检查失败会对容器进行重启操做。Readiness 则是经过监控检测容器是否正常提供服务来决定是否加入到 Service 的转发列表接收请求流量。Readiness 在升级过程能够发挥重要的做用,防止升级时异常的新版本 Pod 替换旧版本 Pod 致使整个应用将没法对外提供服务的状况。
每一个服务必须提供能够正常访问的接口,在 deployment.yml 文件配置好相应的监控检测策略。
... spec: ... template: ... spec: ... containers: - name: fpm livenessProbe: httpGet: path: /__PROJECT_NAME__ port: 80 initialDelaySeconds: 3 periodSeconds: 5 readinessProbe: httpGet: path: /__PROJECT_NAME__ port: 80 initialDelaySeconds: 3 periodSeconds: 5 ... ...
升级策略咱们选择 RollingUpdate 的方式,即在升级过程当中滚动式地逐步新建新版本的 Pod,待新建 Pod 正常启动后逐步 kill 掉老版本的 Pod,最终所有新版本的 Pod 替换为旧版本的 Pod。
咱们还能够设置 maxSurge 和 maxUnavailable 的值分别控制升级过程当中最多能够比原先设置多出的 Pod 比例以及升级过程当中最多有多少比例 Pod 处于没法提供服务的状态。
... spec: ... strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate ...
采用 log-tail 对容器日志进行采集,全部服务的日志都上报到阿里云日志服务的一个 log-store中。在 deployment.yml 文件里配置以下:
... spec: ... template: ... spec: ... containers: - name: fpm env: - name: aliyun_logs_vpgame value: stdout - name: aliyun_logs_vpgame_tags value: topic=__APP_NAME__ ... ...
经过设置环境变量的方式来指定上传的 Logstore 和对应的 tag,其中 name 表示 Logstore 的名称。经过 topic 字段区分不一样服务的日志。
经过在 Deployment 中增长 annotations 的方式,令 Prometheus 能够获取每一个 Pod 的业务监控数据。配置示例以下:
... spec: ... template: metadata: annotations: prometheus.io/scrape: "true" prometheus.io/port: "80" prometheus.io/path: /{{ project_name }}/metrics ...
其中 prometheus.io/scrape: "true" 表示能够被 Prometheus 获取,prometheus.io/port 表示监控数据的端口,prometheus.io/path 表示获取监控数据的路径。
service.yml 文件主要对 Service 进行了描述。
apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet labels: app: __APP_NAME__ name: __APP_NAME__ namespace: __NAMESPACE__ spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: __APP_NAME__ type: LoadBalancer
对 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 中的服务的稳定性、性能、自动伸缩等方面还须要更深刻地探索和研究。
iPhone 11 Pro、卫衣、T恤等你来抽,立刻来试试手气 https://www.aliyun.com/1111/2019/m-lottery?utm_content=g_1000083877
本文为云栖社区原创内容,未经容许不得转载。