精华内容
下载资源
问答
  • 但是这种简单的发布方式存在两个问题,一方面,在新版本升级过程中,服务是暂时中断的,另一方面,如果新版本有BUG,升级失败,回滚起来也非常麻烦,容易造成更长时间的服务不可用。 为了解决这些问题,人们研究出...

    在一般情况下,升级服务器端应用,需要将应用源码或程序包上传到服务器,然后停止掉老版本服务,再启动新版本。但是这种简单的发布方式存在两个问题,一方面,在新版本升级过程中,服务是暂时中断的,另一方面,如果新版本有BUG,升级失败,回滚起来也非常麻烦,容易造成更长时间的服务不可用。

    为了解决这些问题,人们研究出了多种发布策略,下面我们一一介绍。

    蓝绿部署

    这里写图片描述

    所谓蓝绿部署,是指同时运行两个版本的应用,如上图所示,蓝绿部署的时候,并不停止掉老版本,而是直接部署一套新版本,等新版本运行起来后,再将流量切换到新版本上。但是蓝绿部署要求在升级过程中,同时运行两套程序,对硬件的要求就是日常所需的二倍,比如日常运行时,需要10台服务器支撑业务,那么使用蓝绿部署,你就需要购置二十台服务器。

    滚动发布

    滚动发布能够解决掉蓝绿部署时对硬件要求增倍的问题。

    这里写图片描述

    所谓滚动升级,就是在升级过程中,并不一下子启动所有新版本,是先启动一台新版本,再停止一台老版本,然后再启动一台新版本,再停止一台老版本,直到升级完成,这样的话,如果日常需要10台服务器,那么升级过程中也就只需要11台就行了。

    但是滚动升级有一个问题,在开始滚动升级后,流量会直接流向已经启动起来的新版本,但是这个时候,新版本是不一定可用的,比如需要进一步的测试才能确认。那么在滚动升级期间,整个系统就处于非常不稳定的状态,如果发现了问题,也比较难以确定是新版本还是老版本造成的问题。

    为了解决这个问题,我们需要为滚动升级实现流量控制能力。

    灰度发布

    灰度发布也叫金丝雀发布,起源是,矿井工人发现,金丝雀对瓦斯气体很敏感,矿工会在下井之前,先放一只金丝雀到井中,如果金丝雀不叫了,就代表瓦斯浓度高。
    这里写图片描述

    在灰度发布开始后,先启动一个新版本应用,但是并不直接将流量切过来,而是测试人员对新版本进行线上测试,启动的这个新版本应用,就是我们的金丝雀。如果没有问题,那么可以将少量的用户流量导入到新版本上,然后再对新版本做运行状态观察,收集各种运行时数据,如果此时对新旧版本做各种数据对比,就是所谓的A/B测试。

    当确认新版本运行良好后,再逐步将更多的流量导入到新版本上,在此期间,还可以不断地调整新旧两个版本的运行的服务器副本数量,以使得新版本能够承受越来越大的流量压力。直到将100%的流量都切换到新版本上,最后关闭剩下的老版本服务,完成灰度发布。

    如果在灰度发布过程中(灰度期)发现了新版本有问题,就应该立即将流量切回老版本上,这样,就会将负面影响控制在最小范围内

    使用脉冲云实现灰度发布

    脉冲云的部署管理可以轻松实现带有流量管理功能的灰度发布。正常编辑应用信息后点击保存,然后脉冲云会提示直接升级或灰度发布。

    这里写图片描述

    直接升级就是使用一般的滚动升级,点击灰度发布后可以人工干预升级过程,进行流量控制。

    选择灰度发布后,就会呈现灰度发布控制面板。

    这里写图片描述

    在这个控制面板上,可以拖拉滑块,快速调整新旧版本的运行副本数量,同时也可以按百分比,将流量导入到新版本上。此外,还可以通过匹配HTTP Header,指定个别用户的流量到新版本上。

    这里写图片描述

    除了匹配用户流量的HTTP请求头,还可以直接指定匹配请求头中的Cookie信息,匹配规则支持精确匹配、包含、正则、前缀、后缀等,甚至还允许反向匹配。

    当确认新版本运行无误后,就可以点击 完成升级 按钮,就会将流量全部切换到新版本上,并且销毁掉所有老版本应用。如果新版本出了问题,可以点击 取消升级 按钮,立即将流量切回老版本,并销毁掉新版本应用。

    这里写图片描述

    最佳实践

    在新版本应用发布时,为了服务器不停机升级,使用灰度发布策略,在灰度发布开始时,使用HTTP Header 匹配指定测试人员的流量到新版本上,然后当新版本内部测试通过后,可以再按百分比,将用户流量一点一点导入到新版本中,比如先导入10%观察一下运行情况,然后再导入20%,如此累加,直到将流量全部导入到新版本上,最后完成升级,如果期间发现问题,就立即取消升级,将流量切回到老版本。

    运用灰度发布,就再也不需要加班到深夜进行停机升级了,在白天就可以放心大胆地、安全地发布新版本。

    文章转载自:脉冲云

    展开全文
  • 代码如下:[removed] // 说明:用 Javascript 获取滚动条位置等信息 // 来源 :ThickBox 2.1 function getScroll() { var t, l, w, h; if (document.documentElement && document.documentElement.scrollTop) { t = ...
  • 滚动更新的定义和目标 滚动更新的含义 一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新。 滚动更新的好处 最大好处是零停机,整个更新过程始终有副本在运行,从而保证了业余的连续性。 ...

    滚动更新的定义和目标

    滚动更新的含义

    一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新。

    滚动更新的好处

    最大好处是零停机,整个更新过程始终有副本在运行,从而保证了业务的连续性。

    根据 yaml 创建资源, apply 可以重复执行,create 不行

    kubectl create -f deploy.yml
    kubectl apply -f deploy.yml --record
    

    注意 当deploy.yml 是上次发布的内容没有更改时,使用kubectl apply无法进行更新发布。比如镜像使用的是latest标签时。

    我们在发布deployment时,如果使用了如下的发布方式 或者 参数配置不正确时,会发现 服务会挂掉一会儿,用户会访问不了。

    kubectl  delete  --ignore-not-found=true -f deploy.yml
    kubectl  create -f deploy.yml
    

    原因是
    pod被删除或者 容器启动后,到服务真正工作起来,中间会有一段时间无法正常访问,但 k8s 却认为服务是正常就绪状态。

    本篇文章主要解决这个问题,实现 平滑的发布,发布更新服务过程中保证服务一直可用,用户零感知。

    设置滚动更新

    服务准备

    我们有一个api服务deploy.yaml如下:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: familytree-api
      namespace: default
    spec:
      selector:
        matchLabels:
          app: familytree-api
      replicas: 3
      template:
        metadata:
          labels:
            app: familytree-api
        spec:
          containers:
          - name: familytree-api
            image: 123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:1.0.0
            imagePullPolicy: Always
            resources:
              requests:
                cpu: 1
                memory: 1Gi
              limits:
                cpu: 4
                memory: 4Gi
            env:
              - name: VERSION
                value: 1.0.0
            ports:
              - containerPort: 8080
    
    
    ---
    
    apiVersion: v1
    kind: Service
    metadata:
      name: familytree-api-service
      namespace: default
    spec:
      selector:
        app: familytree-api
      ports:
        - protocol: TCP
          port: 8080
          targetPort: 8080
    ---
    
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: familytree-api-ingress
      namespace: default
      annotations:
        kubernetes.io/ingress.class: traefik
        traefik.frontend.rule.type: PathPrefix
    spec:
      rules:
      -
        host: api.test.com
        http:
          paths:
          - path: /api/nowater
            backend:
              serviceName: familytree-api-service
              servicePort: 8080
    ---
    
    

    服务中包含url路由接口可返回版本号和当前hostname

    访问路由

       app.router.add_get('/api/nowater/version', service.version_print)
    

    实现代码如下:

    async def version_print(request):
        version = config["version"]
        hostname = socket.gethostname()
        try:
            print("打印版本 "+version+" hostname " + hostname)
            return web.json_response({'version': version, "hostname": hostname})
        except Exception as e:
            return web.json_response({'msg': e.value}, status=500)
    
    

    使用命令发布服务

    kubectl create -f deploy.yml
    

    查看部署列表

    kubectl get deployments | grep familytree-api
    

    查看正在运行的pod

    kubectl get pods  | grep familytree-api
    

    查看正在运行的pod使用的镜像

    kubectl get deployment -o wide  | grep familytree-api
    

    输出如下:

    [zzq@host3 ~]$ kubectl get deployment -o wide  | grep familytree-api
    familytree-api              3         3         3            0           4m        familytree-api             123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:1.0.0   app=familytree-api
    

    通过pod描述,查看服务的当前映像版本

    kubectl describe pods  familytree-api-7c6fd4bb75-5qtr5 
    

    访问服务url

    [zzq@host3 ~]$ curl http://api.test.com/api/nowater/version
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    

    进行滚动升级

    方式一 将yaml中的镜像版本修改为升级版本–推荐

     image: 123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:2.0.0
    

    环境变量也修改为2.0.0

            env:
              - name: VERSION
                value: 2.0.0
    

    然后使用apply命令重新发布服务

    kubectl apply -f deploy.yml --record
    

    记得一定需要带有 --record,否则没有版本的信息记录,不利于回滚定位。

    方式二 使用命令

    kubectl  set image deployments/familytree-api familytree-api=123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:2.0.0
    

    查看是否升级成功

    查看状态:

    kubectl rollout status deployment/familytree-api
    Waiting for rollout to finish: 1 out of 3 new replicas have been updated..
    deployment "familytree-api" successfully rolled out
    

    暂停升级

    kubectl rollout pause deployment <deployment>
    

    继续升级

    kubectl rollout resume deployment <deployment>
    

    升级结束后,继续查看rs的状态:

    kubectl get rs
    

    根据AGE我们可以看到离我们最近的当前状态是:3,和我们的yaml文件是一致的,证明升级成功了。用describe命令可以查看升级的全部信息:

    kubectl describe deployment familytree-api
    

    输出为

    [zzq@host3 ~]$ kubectl describe deployment familytree-api
    Name:                   familytree-api
    Namespace:              default
    CreationTimestamp:      Mon, 23 Sep 2019 18:12:43 +0800
    Labels:                 <none>
    Annotations:            deployment.kubernetes.io/revision=2
                            kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"kubernetes.io/change-cause":"kubectl apply --filename=./deploy/deploy-prod.yml ...
                            kubernetes.io/change-cause=kubectl apply --filename=./deploy/deploy-prod.yml --record=true
    Selector:               app=familytree-api
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=familytree-api
      Containers:
       familytree-api:
        Image:     123.dkr.ecr.cn-sourthwest-1.amazonaws.com.cn/module/familytree-api-module:2.0.0
        Port:       8080/TCP
        Host Port:  0/TCP
        Limits:
          cpu:     4
          memory:  4Gi
        Requests:
          cpu:     1
          memory:  1Gi
        Environment:
          VERSION:  2.0.0
        Mounts:     <none>
      Volumes:      <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   familytree-api-c6fbb4499 (3/3 replicas created)
    Events:
      Type    Reason             Age   From                   Message
      ----    ------             ----  ----                   -------
      Normal  ScalingReplicaSet  20m   deployment-controller  Scaled up replica set familytree-api-66f79b747c to 3
      Normal  ScalingReplicaSet  3m    deployment-controller  Scaled up replica set familytree-api-c6fbb4499 to 1
      Normal  ScalingReplicaSet  3m    deployment-controller  Scaled down replica set familytree-api-66f79b747c to 2
      Normal  ScalingReplicaSet  3m    deployment-controller  Scaled up replica set familytree-api-c6fbb4499 to 2
      Normal  ScalingReplicaSet  3m    deployment-controller  Scaled down replica set familytree-api-66f79b747c to 1
      Normal  ScalingReplicaSet  3m    deployment-controller  Scaled up replica set familytree-api-c6fbb4499 to 3
      Normal  ScalingReplicaSet  3m    deployment-controller  Scaled down replica set familytree-api-66f79b747c to 0
    
    

    在这里插入图片描述

    访问服务url

    [zzq@host3 ~]$ curl http://api.test.com/api/nowater/version
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    

    设置服务存活探针和服务健康检查探针以及相应参数

    探针官网介绍

    使用上面的发布步骤并不能保证 服务完全平滑。

    因为k8s不能判断 一个的服务是否已经可用,只要pod正常启动就会判断成 可用。

    但是pod启动后并不代表服务可用,比如java的程序 可能还需要启动spring boot框架,去连接数据库等待。

    这样就会导致 短暂的 服务不可用。

    服务不可用的示例

    比如我们一直访问准备好的version版本查询接口如下:

    while :; do curl  http://api.test.com/api/nowater/version; sleep 1; done
    

    然后进行版本更新发布

    kubectl apply -f deploy.yml --record
    

    输出如下:

    [zzq@host3 ~]$  while :; do curl http://api.test.com/api/nowater/version; sleep 1; done 
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    curl: (7) Failed to connect to  http://api.test.com/api/nowater/version: Connection refused
    curl: (7) Failed to connect to  http://api.test.com/api/nowater/version: Connection refused
    curl: (7) Failed to connect to  http://api.test.com/api/nowater/version: Connection refused
    curl: (7) Failed to connect to  http://api.test.com/api/nowater/version: Connection refused
    curl: (7) Failed to connect to  http://api.test.com/api/nowater/version: Connection refused
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    
    

    我们可以发现镜像版本已经更新到2.0.0
    我们可以在2.0.0镜像中加入延迟启动服务,也就是说,会先sleep 20s,然后才去启动app服务。
    这就模拟了在服务启动过程中,虽然pod已经是存在的状态,但是并没有真正提供服务。
    可以看到,由于延迟启动,api并没有真正做好准备提供服务,此时流量已经发到后端,导致服务不可用的状态

    解决方案–探针检测

    在实际生产环境中会遇到这样那样的问题,比如:容器里面应用挂了或者说新启动的容器里面应用还没有就绪等等,所以说就需要进行探测来检验容器是否满足需求。

    那么一般的检测分为几种,比如:进程检测、业务检测。

    进程检测呢很好理解,也就是说通过检测容器进程来验证容器内应用是否存活。
    Kubelet会定期通过Docker Daemon获取所有Docker进程的运行情况,如果发现某个Docker容器未正常运行,则重新启动该容器进程。目前,进程级的健康检查都是默认启用的。

    业务检测呢也好理解,有些人会问,有了进程检测不就挺好的么,为什么要进行业务检测? 
    因为在很多实际场景下,仅仅使用进程级健康检查还远远不够。有时,从Docker的角度来看,容器进程依旧在运行;但是如果从应用程序的角度来看,假设应用代码处于死锁状态的话,那每次调度到这个容器的时候永远都无法正常响应用户的业务。比如对于使用java web服务的应用来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接连接上等等。

    为了解决以上问题,Kubernetes引人了一个在容器内执行的活性探针(liveness probe)的概念,以支持用户自己实现应用业务级的健康检查。这些检查项由Kubelet代为执行,以确保用户的应用程序正确运转,至于什么样的状态才算“正确”,则由用户自己定义。

    Kubernetes支持3种类型的应用健康检查动作,分别为HTTP Get、Container Exec和TCP Socket。exec的方式比较通用的,因为不是每个服务都有http服务,但每个服务都可以在自己内部定义健康检查的job,定期执行,然后将检查结果保存到一个特定的文件中,外部探针就不断的查看这个健康文件就OK了。

    介绍完存活性探针(liveness probe)之后我们来看看就绪探针(readiness probe),就绪探针是来确定容器是否已经就绪可以接受访问,只有当Pod中的容器都处于就绪状态时kubelet才会认定该Pod处于就绪状态,至于什么样的状态才算 ”就绪”,还是由用户自己定义。该状态的作用就是控制哪些Pod可以作为service的后端,如果Pod处于非就绪状态,那么它们将会被从service的load balancer中移除,防止 流量分发到 异常的pod中。

    存活性探针(liveness probe)和就绪探针(readiness probe)的区别

    readinessProbe检查成功与否,决定这个pod是否会被加入到Service的load balancer列表中,即是否给它分配访问的流量,并不影响Pod本身的生命周期

    如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据Pod的restartPolicy自动执行正确的操作。

    如果您希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy为Always或OnFailure。

    如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是spec中的就绪探针的存在意味着Pod将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。

    两种探测的配置方法完全一样,支持的配置参数也一样,既可单独探测又可结合者一起执行。

    LivenessProbe:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet杀掉该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe探针,则kubelet认为该容器的LivenessProbe探针返回的值永远是“Success”。

    ReadinessProbe:用于判断容器是否启动完成(ready状态),可以接收请求。如果ReadinessProbe探针检测到失败,则Pod的状态被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Endpoint。

    存活性探测

    livenessProbe:存活性探测,最主要是用来探测pod是否需要重启–决定把pod删除重新创建

    在spec的containers中增加
    与image同级
    exec探针

            livenessProbe:
              exec:
                command:
    			- cat
    			- /tmp/healthy
              initialDelaySeconds: 5
              periodSeconds: 10
    		  timeoutSeconds: 2       
              failureThreshold: 3       
    

    http探针

        livenessProbe:
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
    

    tcp探针

      livenessProbe:
          tcpSocket:
            port: 8080
    

    periodSeconds 规定kubelet要每隔10秒执行一次liveness probe 检查。
    initialDelaySeconds 告诉kubelet在第一次执行probe之前要的等待5秒钟。
    timeoutSeconds 超长时长,默认为1s,最小值也为1s
    failureThreshold 处于成功状态时,探测操作至少连续多少次的失败才被视为检测不通过,默认为3,最小为1
    探针检测命令是在容器中执行 cat /tmp/healthy 命令。
    如果命令执行成功,将返回0,kubelet就会认为该容器是活着的并且很健康。如果返回非0值,kubelet就会杀掉这个容器并重启它。

    就绪性探测

    readinessProbe:就绪性探测,用来探测pod是否已经能够提供服务–决定是否参与分配流量

    在spec的containers中增加
    与image同级

            readinessProbe:
              tcpSocket:
                port: 80
              initialDelaySeconds: 5
              periodSeconds: 10
    

    或者

            readinessProbe:
              httpGet:
    		    path: /api/nowater/version
                port: 8080
    			httpHeaders:
    			- name : X-Custom-Header
    			  value: Awesome
              initialDelaySeconds: 5
              periodSeconds: 10
    

    periodSeconds 规定kubelet要每隔10秒执行一次readinessProbe 检查。
    initialDelaySeconds 告诉kubelet在第一次执行probe之前要的等待5秒钟。
    探针检测命令是检查tcp连接 端口80 是否畅通。
    也可以检查某个http 请求是否返回成功码。
    如果返回一个成功的返回码,kubelet就会认定该容器服务是可用的。
    如果返回失败的返回码,kubelet将该pod改为not ready,将会被从service的load balancer中移除。

    注意:任何大于200小于400的返回码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。

    由于设置了readinessProbe,虽然pod已经启动起来了,但是并不会立即投入使用,会出现了 READY: 0/1 的情况
    并且有pod出现了一直持续Terminating状态,因为滚动更新的限制,至少要保证有pod可用

    再查看curl的状态,image的版本平滑更新到2.0.0,没有出现报错的状况

    [zzq@host3 ~]$  while :; do curl http://api.test.com/api/nowater/version; sleep 1; done 
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "1.0.0", "hostname": "familytree-api-66f79b747c-rv69s"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    {"version": "2.0.0", "hostname": "familytree-api-c6fbb4499-vcbt7"}
    ----------
    
    

    调整参数设置

    在spec下面添加滚动升级策略:
    与template同级

    minReadySeconds: 5
    strategy:
      # indicate which strategy we want for rolling update
      type: RollingUpdate
      rollingUpdate:
        maxSurge: 1
        maxUnavailable: 1
    

    minReadySeconds:
    这个值默认是0,Kubernetes会假设该容器启动起来后就可以提供服务了,会导致用户访问异常
    这里需要估一个比较合理的值,从容器启动到应用正常提供服务
    如果没有设置该值,Kubernetes会假设该容器启动起来后就提供服务了
    如果没有设置该值,在某些极端情况下可能会造成服务不正常运行

    maxSurge:
    升级过程中最多可以比原先设置多出的POD数量
    例如:maxSurage=1,replicas=5,则表示Kubernetes会先启动1一个新的Pod后才删掉一个旧的POD,整个升级过程中最多会有5+1个POD。

    maxUnavaible:
    升级过程中最多有多少个POD处于无法提供服务的状态
    当maxSurge不为0时,该值也不能为0
    例如:maxUnavaible=1,则表示Kubernetes整个升级过程中最多会有1个POD处于无法服务的状态。

    格式如下

    在这里插入图片描述

    回滚

    查看Deployment的升级历史:

    kubectl rollout history deployment nginx-deploy
    

    查看指定版本的升级历史

    kubectl rollout history deployment nginx-deploy --revision=3
    

    假如现在要直接回退到当前版本的前一个版本:

    kubectl rollout undo deployment nginx-deploy
    deployment "nginx-deploy" rolled back
    

    当然也可以用revision回退到指定的版本:

    kubectl rollout undo deployment nginx-deploy --to-revision=2
    deployment "nginx-deploy" rolled back
    

    现在可以用命令查看Deployment现在的状态了。

    k8s滚动更新的原理

    部署(Deployment)自动创建副本集(ReplicaSet),副本集根据参数策略可以精确地控制每次替换的Pod数量,从而可以很好的实现滚动更新。具体来说,k8s每次使用一个新的副本控制器(replication controller)来替换已存在的副本控制器,从而始终使用一个新的Pod模板来替换旧的pod模板。

    大致步骤如下:

    1、创建一个新的replication controller。
    2、增加或减少pod副本数量,直到满足当前批次期望的数量。
    3、删除旧的replication controller。

    部署概况

    使用命令查看部署概况

    kubectl get deployment
    

    在这里插入图片描述

    上图包含的几个滚动发布过程标量,说明如下:

    DESIRED 最终期望处于READY状态的副本数
    CURRENT 当前的副本总数
    UP-TO-DATE 当前完成更新的副本数
    AVAILABLE 当前可用的副本数

    部署详情

    使用命令查看部署详情

    kubectl describe deployment familytree-api
    

    在这里插入图片描述

    从上图可以看到,k8s精确地控制着整个发布过程,滚动进行,直到所有副本全部更新。其实,k8s提供了两个参数maxSurge和maxUnavailable来精确地控制每次滚动的pod数量,如下:

    maxSurge 滚动更新过程中运行操作期望副本数的最大pod数,可以为绝对数值(eg:5),但不能为0;也可以为百分数(eg:10%)。默认为25%。
    maxUnavailable 滚动更新过程中不可用的最大pod数,可以为绝对数值(eg:5),但不能为0;也可以为百分数(eg:10%)。默认为25%。
    如果未指定这两个可选参数,则k8s使用默认配置,如下:

    kubectl  get deployment familytree-api -o yaml
    

    在这里插入图片描述

    剖析部署helloworldapi的标准输出:

    当前的副本总数 = 3 + 3 * 25% = 4,所以CURRENT为4。

    当前可用的副本数 = 3 - 3 * 25% = 2,所以AVAILABLE为2。

    整个滚动过程是通过控制两个副本集来完成的,新的副本集:familytree-api-c6fbb4499;旧的副本集: familytree-api-66f79b747c 。

    理想状态下的滚动过程:

    创建了一个新的副本集,并为其分配1个新版本的pod,使副本总数达到4,一切正常。
    通知旧副本集,销毁1个旧版本的pod,使可用副本总数保持到2,一起正常。
    当1个副本销毁成功后,通知新副本集,再新增1个新版本的pod,使副本总数达到4,一切正常。

    只要销毁成功,新副本集,就会创造新的pod,一直循环,直到旧的副本集pod数量为0。
    有时整个滚动过程也是不理想的,不符合上述的规律,会有一些混乱。

    k8s最终都会使服务全部更新到期望状态,始终保持最大的副本总数和可用副本总数的不变性

    deployment的常用命令和参数说明

    查看部署状态

    kubectl rollout status deployment/review-demo  --namespace=scm 
    
    kubectl describe deployment/review-demo --namespace=scm
    

    或者这种写法

    kubectl rollout status deployments review-demo --namespace=scm
    
    kubectl describe deployments review-demo --namespace=scm
    

    升级

    kubectl set image deployment/review-demo review-demo=library/review-demo:0.0.1 --namespace=scm
    

    或者

    kubectl edit deployment/review-demo --namespace=scm
    

    编辑.spec.template.spec.containers[0].image的值

    终止升级

    kubectl rollout pause deployment/review-demo --namespace=scm
    

    继续升级

    kubectl rollout resume deployment/review-demo --namespace=scm
    

    回滚

    kubectl rollout undo deployment/review-demo --namespace=scm
    

    查看deployments版本

    kubectl rollout history deployments --namespace=scm
    

    回滚到指定版本

    kubectl rollout undo deployment/review-demo --to-revision=2 --namespace=scm
    

    升级历史

    kubectl describe deployment/review-demo --namespace=scm
    
    Name: review-demo 
    Namespace: scm 
    CreationTimestamp: Tue, 31 Jan 2017 16:42:01 +0800
    Labels: app=review-demo 
    Selector: app=review-demo 
    Replicas: 3 updated | 3 total | 3 available | 0 unavailable 
    StrategyType: RollingUpdate 
    MinReadySeconds: 0 
    RollingUpdateStrategy: 1 max unavailable, 1 max surge 
    OldReplicaSets: <none> 
    NewReplicaSet: review-demo-2741031620 (3/3 replicas created) 
    Events: FirstSeen LastSeen Count From SubobjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 
    1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set review-demo-2741031620 to 1 
    1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set review-demo-1914295649 to 2 
    1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set review-demo-2741031620 to 2 
    1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set review-demo-1914295649 to 1 
    1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set review-demo-2741031620 to 3
    1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set review-demo-1914295649 to 0
    

    deployment文件

    apiVersion: extensions/v1beta1 
    kind: Deployment 
    metadata:  
    name: review-demo  
    namespace: scm  
    labels:  
    app: review-demo 
    spec:  
    replicas: 3 # 
    minReadySeconds: 60 #滚动升级时60s后认为该pod就绪  
    strategy:  
    rollingUpdate: ##由于replicas为3,则整个升级,pod个数在2-4个之间  
    maxSurge: 1 #滚动升级时会先启动1个pod  
    maxUnavailable: 1 #滚动升级时允许的最大Unavailable的pod个数  
    template:  
    metadata:  
    labels:  
    app: review-demo  
    spec:  terminationGracePeriodSeconds: 60 ##k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒  
    containers:  
    - name: review-demo  
    image: library/review-demo:0.0.1-SNAPSHOT  
    imagePullPolicy: IfNotPresent  
    livenessProbe: #kubernetes认为该pod是存活的,不存活则需要重启  
    httpGet:  path: /health  
    port: 8080  
    scheme: HTTP  
    initialDelaySeconds: 60 ## equals to the maximum startup time of the application + couple of seconds  
    timeoutSeconds: 5 
    successThreshold: 1  
    failureThreshold: 5  
    readinessProbe: #kubernetes认为该pod是启动成功的  
    httpGet:  path: /health  
    port: 8080  
    scheme: HTTP  
    initialDelaySeconds: 30 ## equals to minimum startup time of the application  timeoutSeconds: 5  
    successThreshold: 1  
    failureThreshold: 5  
    resources: # keep request = limit to keep this container in guaranteed class  requests:  
    cpu: 50m  
    memory: 200Mi 
    limits:  
    cpu: 500m  
    memory: 500Mi  
    env:  
    - name: PROFILE  
    value: "test"  
    ports:  
    - name: http  
    containerPort: 8080
    

    几个重要参数说明
    maxSurge与maxUnavailable
    maxSurge: 1 表示滚动升级时会先启动1个pod
    maxUnavailable: 1 表示滚动升级时允许的最大Unavailable的pod个数
    由于replicas为3,则整个升级,pod个数在2-4个之间

    terminationGracePeriodSeconds
    k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒。

    如果需要更优雅地关闭,则可以使用k8s提供的pre-stop lifecycle hook 的配置声明,将会在发送SIGTERM之前执行。

    livenessProbe与readinessProbe
    livenessProbe是kubernetes认为该pod是存活的,不存在则需要kill掉,然后再新启动一个,以达到replicas指定的个数。

    readinessProbe是kubernetes认为该pod是启动成功的,这里根据每个应用的特性,自己去判断,可以执行command,也可以进行httpGet。比如对于使用java web服务的应用来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接连接上等等。对于spring boot应用,默认的actuator带有/health接口,可以用来进行启动成功的判断。

    其中readinessProbe.initialDelaySeconds可以设置为系统完全启动起来所需的最少时间,livenessProbe.initialDelaySeconds可以设置为系统完全启动起来所需的最大时间+若干秒。

    这几个参数配置好了之后,基本就可以实现近乎无缝地平滑升级了。对于使用服务发现的应用来说,readinessProbe可以去执行命令,去查看是否在服务发现里头应该注册成功了,才算成功。

    展开全文
  • 利用kubernetes的滚动更新时,可能经常遇到发布“太快不稳定”或“太慢体验差”的情况。本文将介绍kubernetes滚动更新控制速率的特性。 含义 服务在滚动更新时,deployment控制器的目的是:给旧版本(old_rs)副本...

    Kubernetes滚动更新速率控制解读

    利用kubernetes的滚动更新时,可能经常遇到发布“太快不稳定”或“太慢体验差”的情况。本文将介绍kubernetes滚动更新控制速率的特性。

    金丝雀发布


    金丝雀发布这个术语源自20世纪初期,当时英国的煤矿工人在下井采矿之前,会把笼养的金丝雀携带到矿井中,如果矿井中一氧化碳等有毒气体的浓度过高,在影响矿工之前,金丝雀相比人类表现的更加敏感快速,金丝雀中毒之后,煤矿工人就知道该立刻撤离。金丝雀发布是在将整个软件的新版本发布给所有用户之前,先发布给部分用户,用真实的客户流量来测试,以保证软件不会出现严重问题,降低发布风险。

    在实践中,金丝雀发布一般会先发布到一个小比例的机器,比如 2% 的服务器做流量验证,然后从中快速获得反馈,根据反馈决定是扩大发布还是回滚。金丝雀发布通常会结合监控系统,通过监控指标,观察金丝雀机器的健康状况。如果金丝雀测试通过,则把剩余的机器全部升级成新版本,否则回滚代码。

    图片

     优势:

    1. 对用户体验影响较小,在金丝雀发布过程中,只有少量用户会受影响

    2. 发布安全能够得到保障

    劣势:

    1. 金丝雀的机器数量比较少, 有一些问题并不能够暴露出来

    适用场景:

    1. 监控比较完备且与发布系统集成


    灰度/滚动发布


    灰度发布是金丝雀发布的延伸,是将发布分成不同的阶段/批次,每个阶段/批次的用户数量逐级增加。如果新版本在当前阶段没有发现问题,就再增加用户数量进入下一个阶段,直至扩展到全部用户。

    灰度发布可以减小发布风险,是一种零宕机时间的发布策略。它通过切换线上并存版本之间的路由权重,逐步从一个版本切换为另一个版本。整个发布过程会持续比较长的时间, 在这段时间内,新旧代码共存,所以在开发过程中,需要考虑版本之间的兼容性,新旧代码共存不能影响功能可用性和用户体验。当新版本代码出现问题时,灰度发布能够比较快的回滚到老版本的代码上。

    结合特性开关等技术,灰度发布可以实现更复杂灵活的发布策略。

    图片

    优势:

    1. 用户体验影响比较小, 不需要停机发布

    2. 能够控制发布风险

    劣势:

    1. 发布时间会比较长

    2. 需要复杂的发布系统和负载均衡器

    3. 需要考虑新旧版本共存时的兼容性

    适用场景:

    1. 适合可用性较高的生产环境发布

    含义


    服务在滚动更新时,deployment控制器的目的是:给旧版本(old_rs)副本数减少至0、给新版本(new_rs)副本数量增至期望值(replicas)。大家在使用时,通常容易忽视控制速率的特性,以下是kubernetes提供的两个参数:

    •  maxUnavailable:和期望ready的副本数比,不可用副本数最大比例(或最大值),这个值越小,越能保证服务稳定,更新越平滑;
    •  maxSurge:和期望ready的副本数比,超过期望副本数最大比例(或最大值),这个值调的越大,副本更新速度越快。

     

    取值范围


    数值

    1. maxUnavailable: [0, 副本数]

    2. maxSurge: [0, 副本数]

    注意:两者不能同时为0。

    比例

    1. maxUnavailable: [0%, 100%] 向下取整,比如10个副本,5%的话==0.5个,但计算按照0个;

    2. maxSurge: [0%, 100%] 向上取整,比如10个副本,5%的话==0.5个,但计算按照1个;

    注意:两者不能同时为0。

    建议配置

    1. maxUnavailable == 0

    2. maxSurge == 1

    这是我们生产环境提供给用户的默认配置。即“一上一下,先上后下”最平滑原则:1个新版本pod ready(结合readiness)后,才销毁旧版本pod。此配置适用场景是平滑更新、保证服务平稳,但也有缺点,就是“太慢”了。

    自定义策略

    Deployment controller调整replicaset数量时,严格通过以下公式来控制发布节奏。所以,如需快速发布,可根据实际情况去调整这两个值:

    (目标副本数-maxUnavailable) <= 线上实际Ready副本数 <= (目标副本数+maxSurge)

    举例:如果期望副本数是10,期望能有至少80%数量的副本能稳定工作,所以:maxUnavailable = 2,maxSurge = 2 (可自定义,建议与maxUnavailable保持一致)

    8 <= 线上实际Ready副本数 <= 12

    这样,更新过程中,线上能够正常提供服务的pod数总会保持在这个区间内。

     

    现象(maxUnavailable = 1,maxSurge = 1)


    #我这里跟新了镜像然后应用,也就是滚动更新
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web
      namespace: default
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: web
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxSurge: 1
          maxUnavailable: 1

    可以看到是先删除一个旧版本,待新版本创建并且running再删除一个旧版本

    [root@k8s-master ~]# kubectl get ep -w
    NAME         ENDPOINTS                                             AGE
    kubernetes   192.168.179.102:6443                                  29d
    web          10.244.169.141:80,10.244.169.142:80,10.244.36.78:80   8m8s
    
    
    web          10.244.169.141:80,10.244.36.78:80                     8m58s
    web          10.244.169.141:80,10.244.36.78:80                     10m
    web          10.244.169.141:80,10.244.169.143:80,10.244.36.78:80   10m
    web          10.244.169.143:80,10.244.36.78:80                     10m
    web          10.244.169.143:80,10.244.36.78:80                     10m
    web          10.244.169.143:80,10.244.169.144:80,10.244.36.78:80   10m
    web          10.244.169.143:80,10.244.169.144:80                   10m
    web          10.244.169.143:80,10.244.169.144:80                   11m
    web          10.244.169.143:80,10.244.169.144:80,10.244.36.79:80   11m
    
    
    [root@k8s-master ~]# kubectl get pod -w
    NAME                   READY   STATUS    RESTARTS   AGE
    web-655569c6d8-96kmp   1/1     Running   0          15m
    web-655569c6d8-n8nvk   1/1     Running   0          15m
    web-655569c6d8-vmnj6   1/1     Running   0          15m
    
    
    
    web-7cf4f6bf9f-kktpq   0/1     Pending   0          0s
    web-7cf4f6bf9f-kktpq   0/1     Pending   0          0s
    web-655569c6d8-n8nvk   1/1     Terminating   0          15m
    web-7cf4f6bf9f-bbchz   0/1     Pending       0          0s
    web-7cf4f6bf9f-bbchz   0/1     Pending       0          1s
    web-655569c6d8-n8nvk   1/1     Terminating   0          15m
    web-655569c6d8-n8nvk   0/1     Terminating   0          15m
    web-655569c6d8-n8nvk   0/1     Terminating   0          15m
    web-655569c6d8-n8nvk   0/1     Terminating   0          15m
    web-7cf4f6bf9f-bbchz   0/1     Pending       0          15s
    web-7cf4f6bf9f-bbchz   0/1     ContainerCreating   0          15s
    web-7cf4f6bf9f-bbchz   0/1     ContainerCreating   0          18s
    web-7cf4f6bf9f-bbchz   0/1     Running             0          63s
    web-7cf4f6bf9f-bbchz   1/1     Running             0          75s
    web-655569c6d8-vmnj6   1/1     Terminating         0          16m
    web-7cf4f6bf9f-rgq9w   0/1     Pending             0          0s
    web-7cf4f6bf9f-rgq9w   0/1     Pending             0          0s
    web-655569c6d8-vmnj6   1/1     Terminating         0          16m
    web-655569c6d8-vmnj6   0/1     Terminating         0          16m
    web-655569c6d8-vmnj6   0/1     Terminating         0          16m
    web-655569c6d8-vmnj6   0/1     Terminating         0          16m
    web-7cf4f6bf9f-kktpq   0/1     Pending             0          79s
    web-7cf4f6bf9f-kktpq   0/1     ContainerCreating   0          80s
    web-7cf4f6bf9f-kktpq   0/1     ContainerCreating   0          81s
    web-7cf4f6bf9f-kktpq   0/1     Running             0          82s
    web-7cf4f6bf9f-kktpq   1/1     Running             0          92s
    web-655569c6d8-96kmp   1/1     Terminating         0          17m
    web-655569c6d8-96kmp   1/1     Terminating         0          17m
    web-655569c6d8-96kmp   0/1     Terminating         0          17m
    web-655569c6d8-96kmp   0/1     Terminating         0          17m
    web-655569c6d8-96kmp   0/1     Terminating         0          17m
    web-7cf4f6bf9f-rgq9w   0/1     Pending             0          19s
    web-7cf4f6bf9f-rgq9w   0/1     ContainerCreating   0          19s
    web-7cf4f6bf9f-rgq9w   0/1     ContainerCreating   0          21s
    web-7cf4f6bf9f-rgq9w   0/1     Running             0          59s
    web-7cf4f6bf9f-rgq9w   1/1     Running             0          68s

     

    总结


    本文解释了kubernetes最易忽略的“滚动更新策略中控制更新速率”的特性:maxUnavailable与maxSurge,希望能对你在发布版本时有所帮助。

    Kubectl 滚动更新命令

    ## 查看历史
    kubectl rollout history deployment/anyops-devopsdocker-ui
    
    ## 查看具体某一个历史版本信息
    kubectl rollout history deployment/anyops-devopsdocker-ui --revision=2
    
    ## 回滚上个版本 
    kubectl rollout undo deployment/anyops-devopsdocker-ui -n anyops
    
    ## 回滚指定版本
    kubectl rollout undo deployment/nginx --to-revision=2
    展开全文
  • 成本较高, 播放质量差等不足, 本文介绍一种高清媒体信息发布终端设计方案, 该方案采用嵌入式平台架构及硬件解码技术, 支持多种音视频及图片格式, 能够实现视频、音频、图片和滚动字幕等多媒体信息的组合播放,...
  • 最近需要实现抽奖功能,就需要把中奖的信息,以垂直循环滚动的形式向用户进行展示,这篇主要是使用RecyclerView实现垂直滚动效果,九宫格抽奖功能会在后期写,那现在就来看看实现的过程吧。 实现步骤: 1.效果图展示...

    最近需要实现抽奖功能,就需要把中奖的信息,以垂直循环滚动的形式向用户进行展示,这篇主要是使用RecyclerView实现垂直滚动效果,九宫格抽奖功能会在后期写,那现在就来看看实现的过程吧。

    实现步骤:
    1.效果图展示
    2.自定义实现滚动效果RecyclerView
    3.适配器Adapter实现
    4.适配器布局文件
    5.主程序调用过程
    6.主布局文件
    7.总结

    实现过程:
    1.效果图展示

    在这里插入图片描述

    2.自定义实现滚动效果RecyclerView

    public class AutoPollRecyclerView extends RecyclerView {
        private static final long TIME_AUTO_POLL = 16;
        AutoPollTask autoPollTask;
        private boolean running; //标示是否正在自动轮询
        private boolean canRun;//标示是否可以自动轮询,可在不需要的是否置false
    
        public AutoPollRecyclerView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            autoPollTask = new AutoPollTask(this);
        }
    
        static class AutoPollTask implements Runnable {
            private final WeakReference<AutoPollRecyclerView> mReference;
    
            //使用弱引用持有外部类引用->防止内存泄漏
            public AutoPollTask(AutoPollRecyclerView reference) {
                this.mReference = new WeakReference<AutoPollRecyclerView>(reference);
            }
    
            @Override
            public void run() {
                AutoPollRecyclerView recyclerView = mReference.get();
                if (recyclerView != null && recyclerView.running && recyclerView.canRun) {
                    recyclerView.scrollBy(2, 2);
                    recyclerView.postDelayed(recyclerView.autoPollTask, recyclerView.TIME_AUTO_POLL);
                }
            }
        }
    
        //开启:如果正在运行,先停止->再开启
        public void start() {
            if (running)
                stop();
            canRun = true;
            running = true;
            postDelayed(autoPollTask, TIME_AUTO_POLL);
        }
    
        public void stop() {
            running = false;
            removeCallbacks(autoPollTask);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent e) {
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (running)
                        stop();
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_OUTSIDE:
                    if (canRun)
                        start();
                    break;
            }
            //return  false,注释掉onTouchEvent()方法里面的stop和start方法,则列表自动滚动且不可触摸
            return super.onTouchEvent(e);}
    }
    

    3.适配器Adapter实现

    public class AutoPollAdapter extends RecyclerView.Adapter<AutoPollAdapter.BaseViewHolder> {
        private final Context mContext;
        private final List<AutoScrollLuckyListReq.DatasBean> mData;
    
        public AutoPollAdapter(Context context, List<AutoScrollLuckyListReq.DatasBean> list) {
            this.mContext = context;
            this.mData = list;
        }
    
        @Override
        public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(mContext).inflate(R.layout.auto_list_item, parent, false);
            BaseViewHolder holder = new BaseViewHolder(view);
            return holder;
        }
    
        @Override
        public void onBindViewHolder(BaseViewHolder holder, int position) {
            AutoScrollLuckyListReq.DatasBean datasBean = mData.get(position % mData.size());
            holder.content.setText(datasBean.getPhone() + " 获得 " + datasBean.getGiftName());
        }
    
        @Override
        public int getItemCount() {
            return Integer.MAX_VALUE;
        }
    
        class BaseViewHolder extends RecyclerView.ViewHolder {
            TextView content;
    
            public BaseViewHolder(View itemView) {
                super(itemView);
                content = itemView.findViewById(R.id.content);
            }
        }
    }
    

    4.适配器布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fffcf5"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="135xxxx8888 获得 8积分"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:textColor="#797762"
            android:textSize="14dp" />
    
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#797762"/>
    </LinearLayout>
    

    5.主程序调用过程

    public class MainActivity extends AppCompatActivity {
    
        private AutoPollRecyclerView recyclerView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initView();
            initData();
            initListener();
        }
    
        private void initView() {
            recyclerView = findViewById(R.id.recyclerview);
    
            recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));//设置LinearLayoutManager.HORIZONTAL  则水平滚动
    
        }
    
        private void initData() {
            String reponse = "{\n" +
                    "    \"datas\": [\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"三花便签(20枚)\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"三花便签(20枚)\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"三花便签(20枚)\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"三花便签(20枚)\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"满10减1元\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        },\n" +
                    "        {\n" +
                    "            \"giftName\": \"20个积分\",\n" +
                    "            \"phone\": \"****\"\n" +
                    "        }\n" +
                    "    ],\n" +
                    "    \"msg\": \"success\",\n" +
                    "    \"ret\": 0\n" +
                    "}";
    
    
            AutoScrollLuckyListReq autoScrollLuckyListReq = new Gson().fromJson(reponse, AutoScrollLuckyListReq.class);
            //0为请求成功
            if (autoScrollLuckyListReq.getRet() == 0) {
                AutoPollAdapter autoPollAdapter = new AutoPollAdapter(getApplicationContext(), autoScrollLuckyListReq.getDatas());
                recyclerView.setAdapter(autoPollAdapter);
                //启动滚动
                recyclerView.start();
            }
        }
    
        private void initListener() {
    
        }
    }
    

    6.主布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="15dp"
            android:layout_centerInParent="true"
            android:layout_marginRight="15dp"
            android:background="@drawable/luckyer_bg">
    
            <RelativeLayout
                android:layout_width="40dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="23dp"
                android:layout_marginTop="1dp"
                android:layout_marginBottom="1dp"
                android:background="#fff6ea">
    
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_centerInParent="true"
                    android:padding="10dp"
                    android:src="@drawable/zhongjiangzhemingdan" />
            </RelativeLayout>
    
            <com.showly.autopollrecyclerviewdemo.view.AutoPollRecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="20dp" />
        </LinearLayout>
    </RelativeLayout>
    

    7.总结

    类似中奖信息自动滚动效果到这里就实现了,实现过程还是相对比较简单的,重点在于自定义RecyclerView的实现处理。

    需要Demo源码的童鞋可以在底部公众号回复:“自动滚动效果” 即可获取


    小编整理了一份Android电子书籍,需要的童鞋关注底部公众号(longxuanzhigu)回复:“e_books” 即可获取哦!
    在这里插入图片描述

    以下是个人公众号(longxuanzhigu),之后发布的文章会同步到该公众号,方便交流学习Android知识及分享个人爱好文章:

    在这里插入图片描述

    展开全文
  • 桌面滚动字公告

    热门讨论 2011-12-05 12:03:09
    桌面滚动字公告 一款非常好用的桌面滚动字公告 只在开机后显桌面时自动出现,公告显示完毕时自动关闭。适合小网吧管理。机房管理应用和小企业应用 内有设置说明。
  • 前言这篇文章会带领大家去了解kubernetes中的蓝绿部署、金丝雀发布滚动更新等多种发布方式,我的原创文章内容较多,基本都在5000字以上,看不完先关注收藏哈,满满的都是干货~~ku...
  • 滚动发布能够解决掉蓝绿部署时对硬件要求增倍的问题\color{#FF0000}{滚动发布能够解决掉蓝绿部署时对硬件要求增倍的问题}滚动发布能够解决掉蓝绿部署时对硬件要求增倍的问题 滚动升级。在升级过程中,并不一下子...
  • 2.Android系统信息: 手机品牌,Android版本号 How to use <anylife.scrolltextview.ScrollTextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content...
  • 自己用C# vs2012 sqlserver 2008 R2 开发的滚动的公告通知板 live文件夹为滚动的通知公告板,noticefb文件夹为发布的软件 notices文件为数据库的备份 还原即可
  • 此功能改编自 Steven Lord 发布的演示,可让您向轴添加滑块。 此函数向您展示如何在图形中设置滚动条的滑块。 在您需要绘制大量信息但又不想打开大量图形的情况下,此功能非常有用。 我注意到的唯一不利的事情是,...
  • “星际多媒体信息发布系统(或称数字标牌Digital Signage、电子告示)大众版”是可免费下载试用的数字标牌内容制作软件,用以制作精彩节目在等离子液晶(LCD)平板电视、LED屏、投影设备等多媒体终端播放以及触摸...
  • 实时LED信息发布系统中,数据以表格的形式显示,当单元格中的内容超长时,自动从右向左滚动。费了点时间,做了个雏形,需要的可以下载继续完善。项目文件齐全,仅供分享。
  • Kubernetes(k8s)滚动更新

    千次阅读 2021-01-17 16:33:53
    滚动更新:在金丝雀发布基础上的进一步优化改进,是一种自动化程度较高的发布方式,用户体验比较平滑,是目前成熟型技术组织所采用的主流发布方式。一次滚动发布一般由若干个发布批次组成,每批的数量一般是可以...
  • k8s 应用更新策略:灰度发布和蓝绿发布
  • 相位器视差星 ... 目标是创建视差星星效果。 这是一个旧的演示效果,带有多层星星以创建深度错觉。 前面有较大的恒星移动得更快,而... 有关更多信息,请参阅: 也在 html5gamedevs 论坛上发布了这个: 问题、评论:
  • DOM初探(18)——让滚动滚动

    千次阅读 2019-03-02 20:14:09
    滚动滚动 window上有三个方法 scroll(x,y),scrollTo(x,y) scrollBy(); 三个方法功能类似,用法都是将x,y坐标传入。即实现让滚动滚动到当前位置。 区别:scrollBy()会在之前的数据基础之上做累加。 &...
  • <!-- 消息列表 --> <div class="message-list" ref="message-list"> <div class="message-item" :class="{reverse:item.isMe}" v-for="item in messages" :key="item"> ... slot="i.
  • HDFS的滚动升级: Rolling Upgrade

    千次阅读 2016-07-28 19:46:01
    目前Hadoop版本更新迭代的速度还是比较快的,每次新版本的发布,都是一件令人期待的事情.因为这意味着用户可以使用新的功能特性,又或者说在新版中某某模块性能得到了巨大提升等等.现在问题来了,如果我们想用新的Hadoop...
  • 虽然RC已经是被建议去掉的特性,为了和其他特性进行比较,这篇文章还是将早期最为广泛使用的特性之一进行展开,使用一个具体的示例来介绍在RC中进行滚动升级的方式。
  • swiper文字垂直滚动(公告栏)

    千次阅读 2019-10-02 00:58:18
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8">...文字垂直滚动</title> <meta name="viewport" content="width=device-width, initial-scale...
  • 使用js实现数据流(像日志信息,报警信息)的动态展示(demo实现),包括分页等内容,可兼容。
  • 问题:使用TextView实现文字从右到左滚动显示,属性设置可在布局文件和代码文件中设置,经过多次试验,在布局文件中设置其属性无法实现滚动效果。 Android版本:9 在手机上调试需打开USB调试(仅安卓机) 一、使用...
  • 文章目录前言Docker swarm简介Docker Swarm 优点相关概念实验环境一.Docker swarm...滚动更新(灰度发布) 前言 Docker swarm简介 Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽...
  • Kubernetes滚动更新及回滚

    千次阅读 2020-06-21 13:43:49
    # 用于暂停记录部署版本信息 progressDeadlineSeconds The maximum time inseconds fora deployment to makeprogress before it is considered to be failed. The deployment controller will continue to ...
  • 一个Twitter机器人,旨在发布来自Alberta Health的每日更新的COVID-19案例信息。 怎么运行的?: 该程序使用请求获取Alberta Health的.ASPX信息页的副本,然后将其传递给BeautifulSoup进行解析,并获取COVID-19数据...
  • 这是一款仿新浪微博首页的jQuery文字滚动留言板插件。这款留言板插件使用户发布信息不断的滚动显示,并且无缝衔接,效果非常不错。
  • 滚动 JDialog 示例。 许可证: ---------- 本项目中提供的所有程序/软件均在 APACHE LICENSE, VERSION 2.0 下发布。 可以在此处找到许可证详细信息:https://www.apache.org/licenses/LICENSE-2.0。 本许可的某些...
  • 支持懒加载 默认展开和默认选中 禁用节点 通过多种方式选中节点和获取选中的节点信息 支持自定义节点内容 支持节点过滤 非虚拟滚动下,支持手风琴模式 非懒加载时,支持节点拖拽 支持与element-ui完全相同的主题样式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,994
精华内容 18,397
关键字:

信息滚动发布

友情链接: PowerDesigner_pdm_book.rar