前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >容器 & 服务:Docker 应用的 Jenkins 构建 (二)

容器 & 服务:Docker 应用的 Jenkins 构建 (二)

原创
作者头像
程序员架构进阶
修改2021-03-02 18:07:27
8680
修改2021-03-02 18:07:27
举报
文章被收录于专栏:架构进阶架构进阶

系列文章:

容器 & 服务:开篇,压力与资源

容器 & 服务:Jenkins 本地及 docker 安装部署

容器 & 服务:Jenkins 构建实例

容器 & 服务:一个 Java 应用的 Docker 构建实战

容器 & 服务:Docker 应用的 Jenkins 构建

一 概述

容器 & 服务:Docker 应用的 Jenkins 构建 中,通过shell编写的部署(deploy)脚本,初步把Docker纳入持续集成平台。但这个demo依然是玩具。因为我们只是做了一个简单的衔接,并没有完全实现上线的全部过程。而且,我们的demo应用只是一个空接口,没有涉及任何服务的部分。即使是持续集成本身,也没有做多机发布、回滚、平滑升级等等。本篇将列举这些问题,并逐个解构并在后续系列文章中逐个落实。

二 生产环境

实际的持续继承过程,在构建时会涉及代码版本校验,静态代码检查(可选),代码/产物(打包结果)上传到服务器,旧进程关闭&新进程发布【这里也会涉及到平滑重启】。另外,在发布过程中也可能出现中断,导致只有部分机器人发布了新包,而其他机器保留旧包的情况,这时需要完善的回滚策略;

还有很多存在小流量测试/AB测试,需要金丝雀发布、滚动发布、蓝绿发布等等。

另外,我们目前是直接使用docker run启动容器,但没有使用任何容器编排工具。实际的生产环境中,多达上百甚至数千的服务管理,x10甚至x100以上数量级的容器规模,显然不可能使用这样原始的方式去管理,接下来就介绍一下容器编排的相关内容。

三 容器编排

开源的容器编排工具有k8s,swarm,Marathon等等,下面会简单介绍一下这三种工具。

3.1 kubernetes

即k8s,资料应该是最多的,也是目前使用最为广泛的。K8s还可以作为托管解决方案提供,对逻辑单元pods进行调度——pods是一组部署到一起的容器,用于完成特定的任务。K8s对Docker没有任何依赖,是Cloud Native Computing Foundation(CNCF)项目的一部分。

本篇先不做详细描述,后面文章中会做专题讲解。

3.2 docker+swarm+compose

Docker Swarm是Docker的原生编排工具,从Docker 1.12开始新增了swarm模式,用于跨多个主机进行编排,可以通过Docker API访问,也可以用它调用类似docker compose这样的工具,对服务和容器进行声明式编排。Docker Swarm是Docker Datacenter的一部分,后者针对企业级容器部署。

3.3 mesos+marathon

编排框架Marathon基于Apache Mesos项目,通过API提供了跨数据中心的资源管理和调度抽象,而这些数据中心在物理上可能是分散的。Mesos上的系统可以用底层的计算、网络和存储资源,就想虚拟机通过虚拟机管理程序使用底层资源一样。Marathon支持Mesos容器运行时,也支持Docker容器运行时。

本篇将会使用compose和swarm对构建示例进行改造。

四 compose&swarm介绍

4.1 Compose

Compose 是用于定义和运行多容器 Docker 应用程序的工具(也就是容器编排)。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

Compose 使用的三个步骤:

  • 使用 Dockerfile 定义应用程序的环境。
  • 使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
  • 最后,执行 docker-compose up 命令来启动并运行整个应用程序。

由于本地使用的是macos,Mac 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Mac 用户不需要单独安装 Compose。Docker 安装说明可以参阅 MacOS Docker 安装

4.2 YAML

YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

YAML 的配置文件后缀为 .yml,如:runoob.yml 。

基本语法:

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释

4.3 Dockerfile与yml

Dockerfile 是拿来构建自定义镜像的,并没有直接生成容器。只是可以在运行镜像时运行容器而已。

做容器编排以部署环境,是使用 docker-compose.yml 文件进行的,里面可能会需要用到 Dockerfile 。

Dockerfile把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像。

docker-compose 是官方开源项目,负责实现对 Docker 容器集群的快速编排,部署分布式应用。通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。

4.4 Docker Compose与Docker Stack

docker在1.12的时候引入了swarm mode,其中有个stack命令,看起来两者的功能差不多,但还有一点差异的:

docker compose:

    compose是fig演变而来,python脚本,需要单独安装,compose可以build image,compose需要单独安装,compose更多是dev环境使用。

docker stack:

    stack被集成进docker原生CLI,go编写,不支持build image。stack更适合docker cloud环境,用来管理集群。

    一个stack是一组services的集合,它可以使你的app运行在指定的环境,一个stack文件是一个YAML文件,YAML文件中定义了一个或者多个services,和docker-compose.yml文件很相似,但是和compose又有一点小扩展。两者虽然都使用compose.yml文件,但是里面的命令有一丢丢的差别,stack只支持swarm模式下使用,只支持compose V3格式。

五 部署脚本改造

5.1 docker-compose.yml

docker-compose是通过识别工作目录下的docker-compose.yml文件,并根据文件内容进行构建的。一个简单的示例如下:

代码语言:javascript
复制
version: '3.4'services:  dockerdemoapplication1:    image: dockerdemoapplication1    deploy:      restart_policy:        condition: on-failure     expose:       - "80"    ports:      - 18012:8080

在命令行执行docker-compose up命令的输出如下:

代码语言:javascript
复制
192:dockerdemo qingclass$ docker-compose upWARNING: Some services (dockerdemoapplication1) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.Recreating dockerdemo_dockerdemoapplication1_1 ... doneAttaching to dockerdemo_dockerdemoapplication1_1dockerdemoapplication1_1  | dockerdemoapplication1_1  |   .   ____          _            __ _ _dockerdemoapplication1_1  |  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \dockerdemoapplication1_1  | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \dockerdemoapplication1_1  |  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )dockerdemoapplication1_1  |   '  |____| .__|_| |_|_| |_\__, | / / / /dockerdemoapplication1_1  |  =========|_|==============|___/=/_/_/_/dockerdemoapplication1_1  |  :: Spring Boot ::        (v2.1.7.RELEASE)dockerdemoapplication1_1  | dockerdemoapplication1_1  | 2021-02-25 04:14:41.436  INFO 1 --- [           main] c.f.docker.DockerDemoApplication         : Starting DockerDemoApplication v1.0.0-SNAPSHOT on 62cc2e9dc61b with PID 1 (/dockerdemo.jar started by root in /)dockerdemoapplication1_1  | 2021-02-25 04:14:41.442  INFO 1 --- [           main] c.f.docker.DockerDemoApplication         : No active profile set, falling back to default profiles: defaultdockerdemoapplication1_1  | 2021-02-25 04:14:43.136  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)dockerdemoapplication1_1  | 2021-02-25 04:14:43.184  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]dockerdemoapplication1_1  | 2021-02-25 04:14:43.185  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.22]dockerdemoapplication1_1  | 2021-02-25 04:14:43.373  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContextdockerdemoapplication1_1  | 2021-02-25 04:14:43.373  INFO 1 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1857 msdockerdemoapplication1_1  | 2021-02-25 04:14:43.809  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'dockerdemoapplication1_1  | 2021-02-25 04:14:44.497  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''dockerdemoapplication1_1  | 2021-02-25 04:14:44.505  INFO 1 --- [           main] c.f.docker.DockerDemoApplication         : Started DockerDemoApplication in 3.733 seconds (JVM running for 4.566)

在docker容器中,可以看到新创建和启动的容器:

后台执行,增加-d参数即可,docker-compose up -d。

5.2 docker-compose up命令

格式为docker-compose up [options] [SERVICE...],该命令可以自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

默认情况下,docker-compose up启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。当通过Ctrl+c停止命令时,所有容器将会停止。如果希望在后台启动并运行所有的容器,使用docker-compose up -d

如果服务容器已经存在,并且在创建容器后更改了服务的配置(即docker-compose.yml文件)或者镜像,那么docker-compose会停止容器,然后重新创建容器。

注意: 这里的镜像修改指的是已经拉取到本地的镜像更改。当你的镜像仓库内容有变化,不会影响到本地的服务容器。如果你想更新本地的镜像,可以使用docker-compose pull [serviceName]

另外,如果你想防止在配置文件改动后服务容器进行更改,那么可以使用--no-recreate参数。

有关该命令的其他参数,可以使用docker-compose up --help查看。

更多docker-compose 命令的详细描述,可以查看docker命令行的官方文档

5.3 docker stack

除了docker-compose up命令,我们也可以使用docker stack deploy 来启动容器,执行命令和控制台输出如下:

代码语言:javascript
复制
192:dockerdemo xxx$ docker stack deploy -c docker-compose.yml  dockerdemoapplication1Ignoring deprecated options:expose: Exposing ports is unnecessary - services on the same network can access each other's containers on any port.service "dockerdemoapplication1": expose is deprecatedWaiting for the stack to be stable and running...dockerdemo: Failed		[pod status: 0/0 ready, 0/0 pending, 0/0 failed]dockerdemoapplication1: Ready		[pod status: 1/1 ready, 0/1 pending, 0/1 failed]

5.4 Jenkins部署脚本调整

回到Jenkins构建配置,把shell脚本内容调整如下:

代码语言:javascript
复制
#!/bin/sh. /etc/profilecd $WORKSPACEdocker build -t dockerdemoapplication1 .#    docker run -d -t -p 18081:8080 --name dockerdemoapplication1 dockerdemoapplication1# docker-compose 启动docker-compose up -d# docker stack deploy 启动#docker stack deploy -c docker-compose.yml  dockerdemoapplication1sleep 2

然后在Jenkins中再次构建项目,确认容器启动无误。

相关文件和脚本已更新到项目代码中,地址:https://github.com/flamingstar/dockerdemo

注:有一点需要注意,在github上新创建的项目,代码默认主分支命名变成了main,这导致jenkins构建使用master分支的配置构建出错。如果发现是这种情况,那么需要在git中修改分支名称为master。

六 容器资源与k8s初探

6.1 Container中的异类

在构建这个demo之前,曾经也胡乱尝试过一些docker和k8s的示例,导致上述一系列操作后,发现Containers内的容器列表如下:

而这些容器与docker run操作 和 docker-compose up操作启动的容器不同,在命令行试图用docker stop停止或docker rm删除时,发现消失一段时间后又会自动启动。这就是k8s干的"坏事"了。从命名方式也能看出,都带有/k8s_的前缀。

那么就顺便了解一下k8s,并清理掉这些“坏”容器(实际上是pods)。

在Kubernetes群集中,只能运行pods。Pods在kubernetes中是部署的原子单位。一个Pod是一个或者多个共存的容器,它们共享着相同的内核命名空间,比如网络命名空间。

关于docker container和k8s的pod之间的相关性和差异,可以看下kubernetes之七–Pods这篇文章。

6.2 k8s几个命令及pod操作

6.2.1 获取pods列表

代码语言:javascript
复制
192:dockerdemo xxx$ kubectl get podsNAME                                      READY   STATUS    RESTARTS   AGEdockerdemoapplication1-5d75d9499c-94wmn   1/1     Running   2          34mjava-demo-6fb4b85b5-49wst                 1/1     Running   22         160djava-demo-6fb4b85b5-nwf92                 1/1     Running   20         160djava-demo-6fb4b85b5-wmx7c                 1/1     Running   19         160d

从这里我们可以看出各个pod的名字,READY,状态(STATUS),重启次数(RESTARTS),年龄(AGE)存活时间。

6.2.2 删除pod

使用kubectl delete命令:

代码语言:javascript
复制
192:dockerdemo xxx$ kubectl delete pod dockerdemoapplication1-5d75d9499c-94wmnpod "dockerdemoapplication1-5d75d9499c-94wmn" deleted

再次kubectl get pods,发现pod还在,只是重启次数和AGE发生了变化:

why... 因为不懂啊!查了一下资料,有说正确删除pod的方法如下:

1、先删除pod

2、再删除对应的deployment

否则只是删除pod是不管用的,还会看到pod,因为deploy使用的yml文件中定义了副本数量。ok,按照说明尝试一下:

6.2.3 查询并删除deployments

代码语言:javascript
复制
192:dockerdemo xxx$ kubectl get deploymentsNAME                     READY   UP-TO-DATE   AVAILABLE   AGEdockerdemoapplication1   1/1     1            1           53mjava-demo                3/3     3            3           160d

执行删除:

代码语言:javascript
复制
192:dockerdemo xxx$ kubectl delete deployment dockerdemoapplication1deployment.extensions "dockerdemoapplication1" deleted192:dockerdemo xxx$ kubectl get deployment -n dockerdemoapplication1No resources found.

再查看deployments 和 pods,额,还在顽强的活着。。

通过k8s-pod管理,了解到“这种删除由于会触发了replicas的确保机制,所以需要删除deployment”,不过上面删除deployment也失败了,这点比较奇怪。

代码语言:javascript
复制
192:dockerdemo xxx$ kubectl get svcNAME                               TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGEdockerdemoapplication1             ClusterIP      10.104.150.239   <none>        80/TCP            80mdockerdemoapplication1-published   LoadBalancer   10.97.129.92     localhost     18012:30735/TCP   80mjava-demo                          ClusterIP      10.96.163.159    <none>        80/TCP            159dkubernetes                         ClusterIP      10.96.0.1        <none>        443/TCP           254d

6.2.4 回顾操作过程

java-demo的几个pod先不去管它,历史遗留问题后面再处理。dockerdemoapplication1是使用docker-compose up 和 docker stack deploy 操作的,那么很可能就是操作方向错了(虽然container中都是k8s_开头的命名)。

通过docker stack services dockerdemoapplication1 查询服务:

果然是这个坑。既然这回找到了位置,那么从堆栈中删除应该就可以了吧?

再看Idea的docker栏(或控制台 docker ps),已经成功删除。

附:一些可能常用的docker操作

1、批量删除镜像

docker rmi docker images|grep none|awk '{print $3}'

代码语言:javascript
复制
bogon:jenkins_demo qingclass$ docker images|grep none<none>                                                           <none>                85bb886c44b2        25 hours ago        122MB<none>                                                           <none>                9562cffbedc7        31 hours ago        122MB<none>                                                           <none>                c65573bfd658        32 hours ago        122MB<none>                                                           <none>                8b06034bf689        32 hours ago        122MB<none>                                                           <none>                a3138ff3a40f        32 hours ago        122MB<none>                                                           <none>                dce15007a3b9        32 hours ago        122MB<none>                                                           <none>                3f0ba799da14        32 hours ago        122MB<none>                                                           <none>                d6d838d7d156        33 hours ago        122MB<none>                                                           <none>                50121beb8fb8        3 days ago          122MB<none>                                                           <none>                7bed4f0cd16e        8 months ago        236MBbogon:jenkins_demo qingclass$ bogon:jenkins_demo qingclass$ docker images|grep none|awk '{print $3}'85bb886c44b29562cffbedc7c65573bfd6588b06034bf689a3138ff3a40fdce15007a3b93f0ba799da14d6d838d7d15650121beb8fb87bed4f0cd16e

删除操作示例:

2、强制删除所有镜像

代码语言:javascript
复制
docker rmi -f $(docker images -q)

3、批量停止容器

代码语言:javascript
复制
docker stop $(docker ps -a -q)

4、批量删除容器

代码语言:javascript
复制
docker rm $(docker ps -a -q)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一 概述
  • 二 生产环境
  • 三 容器编排
    • 3.1 kubernetes
      • 3.2 docker+swarm+compose
        • 3.3 mesos+marathon
        • 四 compose&swarm介绍
          • 4.1 Compose
            • 4.2 YAML
              • 基本语法:
            • 4.3 Dockerfile与yml
              • 4.4 Docker Compose与Docker Stack
              • 五 部署脚本改造
                • 5.1 docker-compose.yml
                  • 5.2 docker-compose up命令
                    • 5.3 docker stack
                      • 5.4 Jenkins部署脚本调整
                      • 六 容器资源与k8s初探
                        • 6.1 Container中的异类
                          • 6.2 k8s几个命令及pod操作
                            • 6.2.1 获取pods列表
                            • 6.2.2 删除pod
                            • 6.2.3 查询并删除deployments
                            • 6.2.4 回顾操作过程
                        • 附:一些可能常用的docker操作
                          • 1、批量删除镜像
                            • 2、强制删除所有镜像
                              • 3、批量停止容器
                                • 4、批量删除容器
                                相关产品与服务
                                容器服务
                                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档