• 如何在容器场景中进行 DevOps 实践?


    背景知识

    什么是 DevOps?

    DevOps 是依靠云原生、工作流程、人员组织的整合,以协作、自动化、精益、度量、共享、文化为指引,旨在建立一种可以快速交付价值,并且具有持续改进能力的现代化 IT 组织。

    不同的容器用法,DevOps 的实践方式是不一样的。

    胖容器 VS 瘦容器

    容器有很多用法:

    把容器的 1 号进程当作你的业务进程,容器和业务进程的生命周期保持一致,显得轻量和“瘦小”,这种用法叫“瘦容器”。

    把容器的 1 号进程当作是 systemd 或者 supervisord,容器和业务进程的生命周期不一致,各种运维能力(系统管理、命令执行、系统安全等)要逐步“下层”,封装成对应的 agent 部署在容器里,容器里塞满了各种运维工具,显得庞大,这种用法叫“胖容器”。

    在不同的容器用法下,DevOps 实践过程面临的挑战完全不一样。

    瘦容器偏向微服务的用法,可以便利地使用原生 Kubernetes 的弹性能力,比如滚动更新(RollingUpdate)、水平自动扩展(Horizontal Pod Autoscaler)。该场景实践 DevOps 比较方便,使用 CICD 工具集成 Kubernetes 后就可以实现蓝绿部署、不停机发布。

    胖容器更偏向传统虚拟机器的用法,失去了原生 Kubernetes 的弹性能力,要自己提供各种“静态化”能力,比如支持容器销毁重建后 IP 保持不变、在容器里停止业务进程、更新覆盖代码、启动业务进程。该场景实践 DevOps 稍微复杂,使用 CICD 工具集成 Kubernetes 后,要自己开发工具实现应用的 AB 发布、不停机发布。

    那么关于“胖/瘦容器”我们到底该如何选择呢?

    “瘦容器”的实现更多是把挑战丢给了业务代码层去解决,而“胖容器”的实现往往是为了兼容用户的使用习惯,降低用户的接入成本,把挑战丢给了运维层去解决。

    但不管怎么样,“胖/瘦容器”本质都是为了提升业务的稳定性和敏捷能力,如果业务改造成微服务的成本小于“胖容器”所带来的维护成本,我建议你考虑“瘦容器”的方案来实现(这一讲就选择了“瘦容器”方案)。

    gitlab-ci 语法说明

    它使用 yaml 语言进行描述,默认文件名为.gitlab-ci.yml,该文件默认放在仓库的根目录

    其中,gitlab-ci 中最常见的三个概念是 pipeline(管道)、stage(阶段)、job(任务)。从图中我们可以看到:

    pipeline 代表一次流水线实例;

    stage 代表流水线的某个节点;

    job 代表流水线节点下的某个任务。

    DevOps 实践流程

    环境准备

    这里你需要准备至少 1 台 Debian9 的服务器(为了演示方便我准备了 3 台服务器),服务器配置至少为 4core-8G 内存 -50G 硬盘。

    当你已经准备好相应的服务器,并配置好了网络、APT源、主机名等基本环境,就可以按照下面的步骤开始实践了。

    第一步:参考官网安装 Gitlab 服务端,这里我们选择二进制包部署,选择二进制的原因主要是方便连接后面部署的 Kubernetes 集群。

    1 # 安装 Gitlab 服务
    2 sudo apt-get update
    3 sudo apt-get install -y curl openssh-server ca-certificates perl
    4 sudo apt-get install -y postfix
    5 curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
    6 sudo EXTERNAL_URL="http://{你的服务器IP地址}:8000" apt-get install gitlab-ee
    7 
    8 # 获取管理员密码(24h后自动销毁)
    9 cat /etc/gitlab/initial_root_password

    访问 http://{你服务器的IP地址}:8000,使用 username 为 root 以及获取到的管理员密码,成功登录如下。

     第二步:参考官网安装好 gitlab-runner 服务,建议保持和 Gitlab 同个大版本。

     1 # 安装基本软件
     2 apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common -y
     3 
     4 # 安装docker官方apt源
     5 curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
     6 sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
     7 
     8 # 安装docker-ce
     9 apt-get update
    10 apt-get install docker-ce -y
    11 
    12 # 安装gitlab-runner
    13 docker run -d --name gitlab-runner --restart always \
    14      -v /home/gitlab-runner/config:/etc/gitlab-runner \
    15      -v /var/run/docker.sock:/var/run/docker.sock \
    16      gitlab/gitlab-runner:latest

    第三步:参考官网(register a new runner)注册好 gitlab-runner 服务。

    1 docker run --rm -it -v /home/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register

    在管理员界面获取到注册的 token。

     注册成功如下。

     第四步:部署 Kubernetes 环境,为了模拟生产环境来测试,建议选择 K3s 工具

     1 # 部署 k3s-master 节点(1 台 master 节点上执行)
     2 curl -sfL https://get.k3s.io | sh -
     3 mkdir ~/.kubecp && cp /etc/rancher/k3s/k3s.yaml .kube/config
     4 systemctl status k3s
     5 
     6 # 部署 k3s-agent 节点(另外 2 台 worker 节点上执行,如果只有一台测试机,此步骤不需要执行。)
     7 curl -sfL https://get.k3s.io | K3S_URL=https://master_server_ip:6443 K3S_TOKEN=mynodetoken sh -
     8 systemctl status k3s-agent
     9 
    10 # 参数说明
    11 K3S_URL为master节点的IP地址。
    12 K3S_TOKEN为master节点产生的token,token内容在master节点的/var/lib/rancher/k3s/server/node-token

    看到下面的内容,代表 K3s 集群安装成功。

    1 # kubectl get nodes
    2 
    3 NAME        STATUS   ROLES                  AGE   VERSION
    4 liuyong01   Ready    control-plane,master   86m   v1.21.3+k3s1
    5 liuyong02   Ready    <none>                 47m   v1.21.3+k3s1
    6 liuyong03   Ready    <none>                 65m   v1.21.3+k3s1

    第五步:集成 Gitlab 和 Kubernetes 服务,依次点击 Infrastructure——Kubernetes clusters——Add Kubernetes cluster——Add existing cluster 来到如下界面。

    注意:设置 Gitlab 允许请求本地局域网服务(点击 Admin->Settings->Network->Outbound requests,勾选 Allow requests to the local network from web hooks and services ),否则会出现上图中的红色提示。

    结合下面的命令和官网说明,获取相关的配置信息,点击 Add Kubernetes cluster。

     1 # 获取apiserver地址(填写在API URL位置)
     2 kubectl cluster-info | grep -E 'Kubernetes master|Kubernetes control plane' | awk '/http/ {print $NF}'
     3 
     4 # 获取连接apiserver的ca证书(填写在CA Certificate位置)
     5 kubectl get secrets
     6 kubectl get secret default-token-xxxxx -o jsonpath="{['data']['ca\.crt']}" | base64 --decode
     7 
     8 # 创建一个serviceaccout(gitlab)并赋予cluster-admin权限
     9 kubectl apply -f gitlab-admin-service-account.yaml
    10 
    11 # 获取serviceaccout(gitlab)所对应token(填写在Service Token位置)
    12 kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab | awk '{print $1}')

    最后看到如下信息代表成功。

     第六步:最后我们验收一下 Gitlab、gitlab-runner 和 Kubernetes 的环境是否正常,如下所示在 runner 中认证 Kubernetes 成功。

     在准备好环境以后,假设我们的目标是使用Nginx部署一个拉勾教育的 Web 应用,要求使用 DevOps 的方式来实现应用的蓝绿部署( AB 切换)。那么首先就要准备好蓝绿部署所需要的容器镜像。

    准备 CI 流程

    一个完整的 DevOps 流水线比较复杂,比如拉取源代码——编译——单元测试——打包——上传打包制品——集成测试——应用部署——应用观测。

    为了可以让你在实验环境中体验到DevOps 流水线的基本过程,所以我设计了一个简单通用的 DevOps 流水线,让你明白其中的原理。这样一来,当你面临具体的业务场景时,你就可以在这个流水线的基础上继续完善。

    准备 Dockerfile 文件:

    1 FROM nginx:alpine
    2 
    3 COPY default.conf /etc/nginx/conf.d/default.conf
    4 COPY index.html /usr/share/nginx/html
    5 COPY health /usr/share/nginx/html
    6 COPY entrypoint.sh /root/entrypoint.sh
    7 
    8 ENTRYPOINT ["/bin/sh","/root/entrypoint.sh"]

    准备 .gitlab-ci.yml 文件:

     1 image: dtzar/helm-kubectl
     2 
     3 variables:
     4   DOCKER_REGISTRY: docker.io
     5 
     6 before_script:
     7   - env
     8 
     9 after_script:
    10   - echo "job is done"
    11 
    12 build:
    13   stage: build
    14   image: docker:19.03.12
    15   variables:
    16     DOCKER_HOST: tcp://docker:2376  # 指定docker daemon的tls地址
    17     DOCKER_TLS_CERTDIR: "/certs"
    18     DOCKER_TLS_VERIFY: 1 # 启用TLS认证
    19     DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client" # docker clinet认证需要的证书
    20   services:
    21     - docker:19.03.12-dind # 启用dind模式
    22   before_script:
    23     - env
    24     - docker info
    25   script:
    26     # login dockerhub
    27     - echo "docker login $DOCKER_REGISTRY"
    28     - docker login $DOCKER_REGISTRY -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWD}
    29 
    30     # get image url
    31     - REPO=`echo $CI_PROJECT_DIR | awk -F '/' '{print $NF}'`
    32     - DOCKER_REPO="${DOCKER_HUB_USER}/$REPO"
    33     - IMAGE_TAG=${DOCKER_REGISTRY}/$DOCKER_REPO:${CI_COMMIT_REF_NAME}
    34     - IMAGE_LATEST=${DOCKER_REGISTRY}/$DOCKER_REPO:latest
    35 
    36     # build docker image
    37     - echo "docker build -t $IMAGE_TAG"
    38     - cd nginx-hello-world
    39     - docker build --no-cache -t $IMAGE_TAG .
    40 
    41     # tag docker image
    42     - echo "docker tag  $IMAGE_TAG $IMAGE_LATEST"
    43     - docker tag  $IMAGE_TAG $IMAGE_LATEST
    44 
    45     # push docker image
    46     - echo "$ docker push $IMAGE_TAG"
    47     - docker push $IMAGE_TAG
    48     - echo "$ docker push $IMAGE_LATEST"
    49     - docker push $IMAGE_LATEST
    50 
    51     # logout dockerhub
    52     - docker logout $DOCKER_REGISTRY
    53 
    54 test:
    55   stage: test
    56   script:
    57     - echo "Do a unitest here"

    使用 Gitlab的 pipeline 执行 CI 过程后,容器镜像会成功上传到 Docker Hub。

    准备 CD 流程

    CI 过程准备好以后,我们需要准备应用的 deployments.yaml、services.yaml、gitlab-ci.yml文件,最后将应用部署在 Kubernetes 集群中(相关配置我已经传到 Github仓库,方便你直接使用)。

    准备 deployments 文件 hello-blue.yaml:

     1 apiVersion: apps/v1
     2 kind: Deployment
     3 metadata:
     4   name: hello-blue
     5 spec:
     6   replicas: 3
     7   selector:
     8     matchLabels:
     9       app: hello
    10       track: stable
    11   template:
    12     metadata:
    13       labels:
    14         app: hello
    15         track: stable
    16         version: v1.0.0
    17     spec:
    18       containers:
    19         - name: hello
    20           image: lyonger/cicd_test:v1.0.0
    21           imagePullPolicy: Always
    22           ports:
    23             - name: http
    24               containerPort: 8888
    25           readinessProbe:
    26             httpGet:
    27               path: /
    28               port: 8888
    29               scheme: HTTP

    接下来,准备 service 文件 hello-blue.yaml:

     1 kind: Service
     2 apiVersion: v1
     3 metadata:
     4   name: "hello"
     5 spec:
     6   selector:
     7     app: "hello"
     8     version: v1.0.0
     9   ports:
    10     - protocol: "TCP"
    11       port: 8888
    12       targetPort: 8888
    13       nodePort: 30007
    14   type: NodePort

    然后,准备 .gitlab-ci.yml 文件:

    image: dtzar/helm-kubectl
    
    variables:
      DOCKER_REGISTRY: docker.io
    
    before_script:
      - env
    
    after_script:
      - echo "job is done"
    
    deploy_blue_app:
      stage: deploy
      environment:
        name: production
        url: https://devops.learning.lagou.com
      when: manual
      allow_failure: false
      script:
        - kubectl apply -f deployments/hello-blue.yaml
    
    deploy_blue_svc:
      stage: deploy
      environment:
        name: production
        url: https://devops.learning.lagou.com
      when: manual
      allow_failure: false
      script:
        - kubectl apply -f services/hello-blue.yaml
    
    deploy_green_app:
      stage: deploy
      environment:
        name: production
        url: https://devops.learning.lagou.com
      when: manual
      allow_failure: false
      script:
        - kubectl apply -f deployments/hello-green.yaml

    接着部署 blue 环境,此时应用的版本是 v1.0.0:

     请求http://NodeIP:NodePort ,看到返回如下内容,是一个简单的负载均衡:

     1 curl http://10.202.6.101:30007/
     2 Title: Welcome to LaGou Education WebSite!
     3 Version: v1.0.0-blue
     4 hostname: hello-f6449d7cc-x5c9w
     5 
     6 curl http://10.202.6.101:30007/
     7 Title: Welcome to LaGou Education WebSite!
     8 Version: v1.0.0-blue
     9 hostname: hello-f6449d7cc-c2ctd
    10 
    11 curl http://10.202.6.101:30007/
    12 Title: Welcome to LaGou Education WebSite!
    13 Version: v1.0.0-blue
    14 hostname: hello-f6449d7cc-kxjgt

    蓝绿发布

    为了线上服务的稳定,要尽可能降低应用的不可用时间,一般采用蓝绿发布方式来实现,在切换到 v2.0.0 之前,我们先来了解一下蓝绿发布背后的实现原理。

     如上图,切换之前 Service 将所有流量代理到 v1.0.0,切换之后 Service 将所有流量代理到v2.0.0,虚线处已经不存在任何流量,接下来我们来尝试一下切换到 v2.0.0。

     先点击 deploy_green_app 将应用部署到新版本 v2.0.0,此时线上无法访问到 v2.0.0,最后点击 deploy_green_svc 切换所有流量到 v2.0.0,尝试请求http://NodeIP:NodePort ,看到只返回 v2.0.0的内容。

     1 curl http://10.202.6.101:30007/
     2 Title: Welcome to LaGou Education WebSite!
     3 Version: v2.0.0-green
     4 hostname: hello-green-58bf975df-k94j9
     5 
     6 curl http://10.202.6.101:30007/
     7 Title: Welcome to LaGou Education WebSite!
     8 Version: v2.0.0-green
     9 hostname: hello-green-58bf975df-qnxsj
    10 
    11 curl http://10.202.6.101:30007/
    12 Title: Welcome to LaGou Education WebSite!
    13 Version: v2.0.0-green
    14 hostname: hello-green-58bf975df-q546j

    最后完整的 DevOps 实践效果如下:

     你要注意,线上在执行完成 deploy_green_app 之后需要经过充分测试,测试符合预期后再执行 deploy_green_svc 切换所有流量到新版本。

  • 相关阅读:
    计算机网络-TCP的三次握手与四次挥手
    计算机网络-XSS及CSRF攻击防御
    计算机网络-HTTP与HTTPS的区别
    装饰器模式和代理模式的区别
    23种设计模式总结
    单例模式详解
    常用设计模式总结
    PG-用户|角色管理
    PG-表空间管理
    TiDB-性能测试
  • 原文地址:https://www.cnblogs.com/dashujuzhilu/p/15700004.html
Copyright © 2020-2023  润新知