https://yq.aliyun.com/articles/32071
摘要: 本文主要从现有持续交付过程中遇到的问题,引发出Docker技术为软件交付方式带来的革命,重点详细介绍几种常用开发语言的持续交付实践, 最后会说明为什么Docker是Devops概念的最佳诠释。
因为我非常啰嗦,所以写的分享也太长,全部内容被内部同学review后的反馈是: 像看小说一样……
所以为了防止大家看了开头就去逛别的小网站了,开篇我先点个题, 这篇文件最终的目的是讲清楚下面这张图:

就是一个完整的,应用Docker化持续交付需要做的事情。
并且,这篇文章不是硬广, 图中涉及到的服务也是基础服务,提供便捷的配置方式,最佳实践的推荐。
我们并不去定义标准和规范,会兼容业内所有的规范和标准的玩法。
下面开始正文:
本文涉及到的平台有:
- 阿里云持续交付平台 https://crp.aliyun.com
- 阿里云容器服务
- 阿里云镜像仓库服务 https://dev.aliyun.com
- 阿里云Code https://code.aliyun.com
- 其他会举例引用等都来自docker.com , github 和 travis-ci 等业内标准。
目录:
- 传统CD过程中遇到的问题
- 变革软件交付方式的技术: Docker
- 应用Docker化交付的过程实践
研发过程中的困境
-
在干货之前,先要引导一下为什么要做持续交付,任何一家互联网或者软件公司,随着产品规模的扩大,市场需求的变化,都会逐步的发现产品版本管理混乱,运维人员总是在兜底, 不知道开发/测试/集成/预发布/生产等等环境到底经历过几代运维人员之手,所以环境压根没人敢动。
-
因为市场永远在变化, 需求一定在变化,人员也在变化,导致了研发过程中遇到的这样那样的问题。 因此,大多数企业都用CI/CD 这个解决方案来应对 , 如下图:
-
额外说一下,我们认为的持续交付概念总结如下:
在一起
就是集成,每次集成都应该有反馈
。- 只有不停的集成才是持续集成。越少持续,每次反馈
代价越大
。 多次集成
产生一次交付。
-
CI/CD 是无法提升你的代码质量的,是无法解决你代码中的Bug的,但能够提升效率和质量的原因是: 他能把问题发现在前面, 让小问题提前暴露出来.
-
我们说做持续集成最重要的是
有效反馈
和持续
,因为CI就像体检服务一样,好比我这个胖子要减肥,体检服务不能让我吃的更少动的更多,但我如果每天都称一下体重,就能随时知道自己身体的状态,随时知道我每天该干什么, 这就是持续的重要性。 -
如果我不做这个事儿,很可能等到我年度体检的时候才发现,TMD脂肪肝又加重了。。 同理如果每次代码提交都能自动和其他代码集成,和测试环境集成, 就不会出现最终发布的时候出现各种各样的问题, 也就是刚才说的运维总在兜底的问题。
-
CI过程的
有效反馈
也很重要,每次集成都应该给出准确的问题定位和建议, 谁的代码merge出现冲突,谁提交的commit导致UT失败, 谁应该立刻去解决什么样的问题, 这都是有效的反馈。 就好比我中午没吃饭,去称一下体重,体重秤告诉我:还凑合。 那这个反馈让我晚饭是吃。。还是不吃呢? 。。 这就是无效反馈。
简单来说,持续交付的pipeline就像下面的管道图一样:
当然这个图里的每个节点(stage)的定义并不适用于所有应用,每个stage 是不同角色,运行需要耗费不同的成本,那么只要保证每个Stage 是一个独立有效的反馈就是正确的持续交付pipeline 。
那么,构建出能够运行这样pipeline的一个环境,都需要什么东西:
- 如上图, 你需要有代码托管服务(存储),运行CI中的单元测试,编译打包服务(环境), 如果你的应用已经托管在公共云上,还要涉及到网络问题。 也就是你核心要解决的除了需要服务本身,关键是解决“存储,环境和网络”这三个问题。
现在,当你辛辛苦苦做好了这些过程之后,仍然会遇到一些问题:
-
每次build,是需要不同的build环境的
- 编译环境维护困难
-
每次集成 Test,是需要依赖其他环境,被依赖的环境不受提交者的控制
- 依赖环境维护困难
-
每个package, 在不同的环境, run的结果是不一样的
- 切换环境调试困难
-
每个package ,是无法回溯的
- 运行包的版本维护困难
-
每个环境,是不同的维护者(开发环境,测试环境,生产/产品环境)
- 统一环境标准困难
-
每个环境,除了维护者,是无法清楚知道环境的搭建过程的
- 环境回溯,更是难上加难
Why ? 为什么会遇到这样那样的问题? 为什么开发人员经常抱怨: “明明我的程序在测试环境已经调试好了,为什么一上生产环境就运行不了 ? ”
-
归根结底的原因是:
-
开发人员交付的只是软件代码本身, 而运维人员需要维护的是一整套运行环境,以及运行环境之间的依赖关系
变革软件交付方式的技术: Docker
- 有人说:“交付方式的变革,改变了全球的经济格局”
- 那么,在软件开发领域,Docker ( An open platform for distributed applications for developers and sysadmins) , 就是
变革软件交付方式的技术。
回到第一章节的问题, 我们找到了开发和运维之间问题的关键,找到了写代码和维护生产环境之间的核心差别, 那么我们YY一下。
如果我们能像描述代码依赖关系一样,描述代码运行所需的环境依赖呢? 如果又能像描述应用之间的依赖关系一样,描述环境之间的依赖呢?
-
假定,我们的代码中有一个文件,定义了运行需要的环境依赖栈(就像pom.xml文件中定义了java应用的jar包依赖一样)
-
构建时,我们能根据整个文件,将所有软件依赖栈安装到一个镜像中,镜像是只读的。任何变更都会新产生一个新的镜像而不会更改原先的镜像。
-
并且只要这个镜像不变,镜像起来的容器之内的环境也不变。
-
那我们是不是可以像把代码,依赖,测试脚本,环境依赖,环境描述等等这些东西装到集装箱中一样, 集装箱作为一个整体来传递, 作为一个整体在不同的平台上运行, 集装箱不变,任何平台上运行的结果都不变。 YY思路如下图:
如果我们能轻松的交付整个软件依赖栈, 是不是刚才说到的在不同环境调试的问题就能大大减少或者不复存在了?
这个YY过程正好被Docker 技术所覆盖, 我们看一下Docker 提供什么样的能力,能满足刚才的YY:
-
描述环境的能力
- 提供了描述运行栈,并且自定义Build 过程的能力。Code 中的描述文件就是Dockerfile
-
分层文件系统
- Image可以像Git一样进行管理,并且每一层都是只读的,对环境的每个操作都会被记录,并且可回溯
-
Docker Registry
- 提供了管理Image 存储系统,可以存储,传递,并且对Image进行版本管理
- 提供了管理Image 存储系统,可以存储,传递,并且对Image进行版本管理
-
屏蔽Host OS 差异
- 解决了环境差异,保证在任何环境下的运行都是一致的(只要满足运行docker的linux 内核)
- 解决了环境差异,保证在任何环境下的运行都是一致的(只要满足运行docker的linux 内核)
这几种能力天然的帮助我们解决环境描述和传递的问题, 因此docker 能够做到Build Once, Run EveryWhere !
- 因此,软件的交付方式,变成了最简单的 Build -- Ship -- Run, 如下图:
应用Docker化交付的过程实践
这是本文最核心的一章, 首先先看个例子,用docker做持续交付能带来的好处, 避免广告嫌疑,我用docker官方网站上的案例: BBC News
-
简单来说,一个全球新闻中心,内容的变化是最快的, BBC 公司内部的第一个问题是涉及10几种CI环境,26000 Jobs,500Dev人员
-
第二个核心问题是,CI任务需要等待,无法并行
最明显的改变,开发可以自己定义自己的开发语言,自己所需的build,集成测试环境,以及应用运行所需的依赖环境。
既然效果这么明显, 该怎么做呢?
基本思路如下:
-
安装好Docker环境
-
Docker 化你的应用运行环境
-
Docker 化你的应用编译,UT环境
-
Docker 化你的应用运行的依赖环境
第一步, 如何安装运行一个Docker环境
这个现在已经非常非常的简便了:
- 配置安装
- 安装Docker Toolkit
- 安装云驱动
- ECS driver for Docker Machine
- AWS, GCE, etc.
- 创建Docker运行环境
docker-machine create --driver aliyunecs mytest eval "$(docker-machine env mytest)"
docker run -d nginx
第二步, 如何将自己的应用运行在Docker容器中?
这句话可以翻译为: 如何将我的应用环境通过Dockerfile描述出来?
假如我的应用是一个Java Web 应用,需要Java运行环境和Tomcat 容器 ,那么大概我的环境所需下面这些东西:
- 某Linux发行版操作系统
- 基础软件(起码有个能解压缩包的吧)
- openjdk 7 && 配置 Java Home 等环境变量
- Tomcat 7 && 配置 环境变量
- 应用包 target.war
- 应用包 启动参数 JVM
- Web Server 指定端口 8080
- 启动tomcat
转化为成Dockerfile 的语言大致如下:
FROM buildpack-deps:jessie-curl
RUN apt-get update && apt-get install -y unzip openjdk-7-jre-headless=“$JAVA_DEBIAN_VERSION”
ENV LANG C.UTF-8
ENV JAVA_VERSION 7u91
ENV JAVA_DEBIAN_VERSION 7u91-2.6.3-1~deb8u1
ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
RUN mkdir -p "$CATALINA_HOME"
WORKDIR $CATALINA_HOMEENV TOMCAT_VERSION 7.0.68
ENV TOMCAT_TGZ_URL https://xxxx/apache-tomcat-$TOMCAT_VERSION.tar.gz
RUN set -x \
&& curl -fSL "$TOMCAT_TGZ_URL" -o tomcat.tar.gz \
&& curl -fSL "$TOMCAT_TGZ_URL.asc" -o tomcat.tar.gz.asc \
&& gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz \
&& tar -xvf tomcat.tar.gz --strip-components=1 \
&& rm bin/*.bat \
&& rm tomcat.tar.gz*
EXPOSE 8080
CMD ["catalina.sh", "run"]
- 可以看出 ,Dockerfile 第一步永远是From 某个镜像, 开始安装了一些基础包(这里是Jre7), 又设置了java的环境变量, 之后安装tomat(这里是7.0),再声明启动8080端口,最后运行tomcat的启动脚本结束,在最后结束之前将我的Web 应用.war包COPY或者ADD进去即可。
我们再看一个Nodejs的环境:
FROM ubuntu:14.04
COPY sources.list /etc/apt/sources.list
COPY .npmrc /root/.npmrc
RUN apt-get update && apt-get -y install curl automake tar libtool make wget xz-utils supervisor
ENV NODE_VERSION 0.12.5
ENV NPM_VERSION 2.11.3
RUN curl -SLO "https://npm.taobao.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" \
&& tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
&& npm install -g npm@"$NPM_VERSION" \
&& npm cache clear
RUN rm -rf ~/.node-gyp \
&& mkdir ~/.node-gyp \
&& tar zxf node-v$NODE_VERSION-linux-x64.tar.gz -C ~/.node-gyp \
&& rm "node-v$NODE_VERSION-linux-x64.tar.gz" \
&& mv ~/.node-gyp/node-v$NODE_VERSION-linux-x64 ~/.node-gyp/$NODE_VERSION \
&& printf "9\n">~/.node-gyp/$NODE_VERSION/installVersion
CMD ["node"]
- 关于这个环境,COPY了本地的sources.list和.npmrc 到容器中,是更换了安装源为mirrors.aliyun.com 和 NPM源为npm.taobao.org , 国内源更快。 其他就是安装了基本的Nodejs 运行环境
那么通过这两个例子,我们发现Dockerfile 还是写起来很麻烦的(其实也不麻烦,就是刚刚说的装要装的东西,配置,运行这三步)。 那么,刚刚说到每一个Dockerfile的第一行都是FROM另一个镜像, 那么思考一下:
-
如果有一个安装好Java的环境 ?
-
如果有一个安装好Java和Tomcat的环境 ?
-
如果是微服务,对环境只依赖Java/Node基础环境,是不是所有应用都可以共用1个环境?
通过这些思考,得到如下寻找docker镜像的过程:
-
寻找java镜像 ,选择镜像版本, 检查 Dockerfile
-
寻找tomcat镜像,选择 Tomcat & Java 版本, 检查 Dockerfile
-
测试运行 :
docker run -ti —rm -v /home/app.war:/canhin/webapp/ tomcat:7-jre7
说句题外话,这个思路同样适用于公司内部,因为Dockerfile 明确划分出了开发和运维的边界, 如果公司有统一的运维标准,比如某个操作系统的某个版本, 某种确定的Web Server, 这样开发只需要From 运维提供的镜像来描述自己的应用环境特殊的部分就好了。 如果大家的环境都一样,调试和测试的过程中,只需要把应用代码通过-v 的参数挂载进去运行就好了, 这样世界就变的很简单和清楚了。
- 那么当我需要一个Java 7, Tomcat 7的环境的时候, 直接选择一个官方的tomcat 7 - jre7 镜像即可 , 比如https://dev.aliyun.com/detail.html?spm=5176.1972343.2.2.U5HdFI&repoId=1270 这个, 点击查看他的dockerfile
- 跟我所需的环境基本一致, 那就可以基于这个镜像再写自己的Dockerfile ,这样就会简单很多了。
第三步, 用Docker 描述我的编译环境
编译/CI环境往往在公司规模越来越大的时候, 变得越来越麻烦, 因为不同语言,不同类型的应用对编译环境的要求都不一样。 就像刚才说到的BBC News的例子,一个大公司几十种编译环境的存在是很正常的。
那么,编译环境Docker化最大的好处是: 自定义,可扩展,可复制
-
试想一下, 假如你的应用编译只需要依赖标准的Jdk 1.7 和 Maven 2, 或者你是python应用编译过程其实只是需要安装依赖, 那么你可以跟很多人共用编译镜像。
-
但假如你的应用是Nodejs ,编译依赖特定的C库, 或者是C++之类的编译环境一定要和运行环境一致等等,那就需要定制自己的编译环境了。
这里我做一个最简单的用于编译java的镜像示例:
- 编译镜像的Docker file 示例:
FROM registry.aliyuncs.com/acs-sample/centos:7
RUN yum update yum install -y open-jdk-1.7.0_65-49
COPY build.sh /build.sh
COPY settings.xml /home/apache-maven-2.2.1/conf/
ENTRYPOINT [“./build.sh"]
- 上述Dockerfile的build.sh示例:
cd /ws ; mvn -e -U clean package -Dmaven.test.skip=true $@
cp target/*.war docker/ || exit 0
- 运行方式示例:
git clone git@code.aliyun.com:tangrong.lx/myproject.git ~/myprj ; cd ~/myprj
docker run --rm -v `pwd`:/ws -v ~/.m2/repo:/buf build_maven:1.0
- 解释一下这个过程:
- 我的编译环境需要CentOs7 系统, 安装JDK1.7 , 然后把maven 的setting (这里主要配置指向其他私有nexus) 和 编译脚本拷贝进去。
- 编译脚本也很简单,就是maven编译打包命令,并且把最终生成的war拷贝到一个定义好的docker目录下, 这个目录随便定义。
- 最后是运行方式, 即把源代码挂载到容器里进行编译, 同时可以选择把本地的.m2 缓存到镜像内加快编译速度
这里提两个小提示,都是经验之谈:
建议: build app 和 build docker image 建议分开进行, 即先进行应用本身的编译,再将输出物拷贝到镜像内(但脚本语言可以例外) 因为:
-
镜像分层概念导致源码可能泄露:因为DockerImage 每一层都会保存一个版本, 即便是ADD代码进去,编译后再rm掉,也可以通过获取ADD这一层镜像拿到源码,因为镜像是运行在各个环境中,是不应该包含源代码信息的。
-
镜像最小化原则:编译环境可能需要和运行环境不一样的东西,比如Maven的配置,Nodejs的一些C库的依赖, 都不需要在运行环境中体现,所以本着镜像应该最小化原则,不需要的东西最好都不要放进去,也应该分开进行这个步骤。
-
所以,整个过程还是分为build app和build docker image 两个过程,类似下面这个简单流程 :
建议: Docker file 不要放到代码根目录下
- 避免大量文件传给docker deamon : docker build会先加载Dockerfile同级目录下所有文件进去,如果有不需要ADD/COPY到镜像里的文件不应该放到Dockerfile目录下, 可以试一下把Dockerfile放到系统/根目录下,这时build 十有八九就会让docker deamon挂掉。
第四步,用docker 描述UT环境
简单思路: 运行Docker 镜像环境,安装测试所需依赖 , 运行Docker容器, 运行测试命令/脚本
用一个travis-ci 官方的例子来说明容器测试这件事,先看下面一个ruby的镜像:
FROM ubuntu:14.04
MAINTAINER carlad "https://github.com/carlad"
# Install packages for building ruby
RUN apt-get update
RUN apt-get install -y --force-yes build-essential wget git
RUN apt-get install -y --force-yes zlib1g-dev libssl-dev libreadline-dev libyaml-dev libxml2-dev libxslt-dev
RUN apt-get clean
RUN wget -P /root/src http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.2.tar.gz
RUN cd /root/src; tar xvf ruby-2.2.2.tar.gz
RUN cd /root/src/ruby-2.2.2; ./configure; make install
RUN gem update --system
RUN gem install bundler
RUN git clone https://github.com/travis-ci/docker-sinatra /root/sinatra
RUN cd /root/sinatra; bundle install
EXPOSE 4567
- 简单来说就是标准的一个ruby 镜像, 启动4567端口 。 那么通过这个镜像进行的测试过程如下:
sudo: required
language: ruby
services:
- docker
before_install:
- docker build -t carlad/sinatra .
- docker run -d -p 127.0.0.1:80:4567 carlad/sinatra /bin/sh -c "cd /root/sinatra; bundle exec foreman start;"
- docker ps -a
- docker run carlad/sinatra /bin/sh -c "cd /root/sinatra; bundle exec rake test"
script:
- bundle exec rake test
-
这个其实就是大家可以在本地进行的一个过程,在before install部分内可以看到过程是:
- 先build出运行环境的镜像
- 运行这个镜像,看看服务能否正常启动
- 查看容器是否存活(保证容器不是运行一下就挂了退出)
- 运行测试
再来看一个python的例子,也很好理解:
language: python
python:
- 2.7
services:
- docker
install:
- docker build -t blog .
- docker run -d -p 127.0.0.1:80:80 --name blog blog
before_script:
- pip install -r requirements.txt
- pip install mock
- pip install requests
- pip install feedparser
script:
- docker ps | grep -q blog
- python tests.py
简单来说就是运行容器, 安装依赖, 运行测试脚本 。 或者 直接通过下面一行命令进行
docker run -v mycode:/ws mytestimage:master /bin/sh -c "python3 djanus/manage.py test djanus mobilerpc "
tips: 这里不是说推荐大家用travis-ci ,但travis-ci 制定了一种语法标准, 非常清楚的能够看到整个过程。 同时:
阿里云持续交付平台未来将会完全支持兼容travis-ci定义的yml语法结构
第五步:用Docker-Compose 描述依赖环境
刚刚说了单独一个容器运行测试的情况, 但实际情况可能是即便是运行测试,也需要依赖proxy,依赖db,依赖redis等。 简单来说一般web应用会需要下面的结构:
这个结构很简单也很常见, 那在传统思想里,要运行UT或者集成测试,需要依赖的组件,都是去搭建。 搭一个mysql,配置mysql ,运行mysql 这种思路。
-
但是在docker的思想里,是声明的概念,就是说我需要一个mysql 去存一些数据进行测试, 这个mysql运行在哪里我根本不care 。 同样的思路告诉docker:
- I need 负载均衡(haproxy,Nginx)
- I need 数据库(mysql)
- I need 文件存储(通过-v , ossfs)
- I need 缓存服务(redis,kv-store)
- I need ...
-
这时,用于编排多个Docker Image 的服务,docker-compose 就出现了,官方文档里用三张最简单的图表明了compose是怎么用的:
- 就是说,我运行一次测试, 需要mysql, 那我就启动一个mysql容器就行,通过link 的方式将我的app链接上,配置一个密码即可,至于其他的信息,我根本不需要,或者说不关心。
再举一个例子,假设一个php的Wordpress 应用, 除了应用本身还需要一个db ,他的编排文件(docker-compose.yml)如下:
web:
image: registry.aliyuncs.com/acs-sample/wordpress:4.3
ports:
- '80'
volumes:
- 'wp_upload:/var/www/html/wp-content/uploads'
environment:
WORDPRESS_AUTH_KEY: changeme
WORDPRESS_SECURE_AUTH_KEY: changeme
WORDPRESS_LOGGED_IN_KEY: changeme
WORDPRESS_NONCE_KEY: changeme
WORDPRESS_AUTH_SALT: changeme
WORDPRESS_SECURE_AUTH_SALT: changeme
WORDPRESS_LOGGED_IN_SALT: changeme
WORDPRESS_NONCE_SALT: changeme
WORDPRESS_NONCE_AA: changeme
command: run test script
links:
- 'db:mysql'
labels:
aliyun.logs: /var/log
aliyun.probe.url: http://container/license.txt
aliyun.probe.initial_delay_seconds: '10'
aliyun.routing.port_80: http://wordpress
aliyun.scale: '3'
db:
image: registry.aliyuncs.com/acs-sample/mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: password
restart: always
labels:
aliyun.logs: /var/log/mysql
- 除了自身的配置,文件挂载之外,声明的mysql 就是官方5.7的版本,只需要设置一个密码即可, 这样直接运行起来无论是提供服务, 还是运行测试, 都非常的方便。
tips1: compose的好处还在于将配置从Dockerfile中提取出来,比如在测试/生产环境所需要的配置差别, 就可以放到compose里,在不同环境运行的时候换不同的compose文件即可,不用重复的编出不同环境用的docker image
tips2: 上面的wordpress示例里,启动多个应用容器之上,并没有用nginx做代理,因为阿里云容器服务提供了routing,省去了这部分, 如果是在企业内部,当启动三个应用,还是需要在compose里再声明一个nginx 或者 haproxy 在前面做应用代理和负载均衡的。
完整拼接
- 现在回到文章一开始那张图 :
对一个公司/企业来说,将自身应用docker化,编译服务,测试集群docker化之后, 要跑通整个的过程,达到BBC News 这样的效果, 整个流程就如图中所示:
-
代码,测试脚本,配置,Dockerfile/Compose 等从开发本地push到代码仓库中
-
代码仓库能够hook 这个信息,通过事件trigger build 服务,通过容器进行app build,运行test, 通过后对应用进行docker image 的build
-
build 好的docker image push到远程docker registry 用于存储和传递
-
当build test 都pass之后, 通过deploy service 告诉应用集群进行更新,从docker registry 上pull 下来新的image进行应用更新,或更新集群配置
当然,如果你拥抱阿里云的服务, 并且自己的业务都在阿里云上。 你可以省去搭建这些基础设施, 使用云Code(https://code.aliyun.com) 来托管代码, 使用阿里云持续交付平台(https://crp.aliyun.com ) 来做CI/CD, 使用镜像仓库服务( https://dev.aliyun.com ),作为远程docker registry来管理镜像, 使用阿里云容器服务搭建和管理自己的运行环境 。 让一切变的更简单,让你更专注于自己应用的开发和docker化。
用docker 为开发/运维人员带来的好处
Docker技术是 DevOps 的最好诠释, DevOps不是开发去做运维的事情, 而是:
- 将编程的思想应用到运维领域
举例来说: Immutable,Copy on Write 这些思想在研发领域是耳熟能详的,好处大家秒懂。而在运维领域的Immutable,传统是怎么做的? 靠组织架构,权限管理。各种人为订制的机制,规范。 而docker 是用技术来解决了这个问题, 官方文档的介绍docker是 An open platform for distributed applications for developers and sysadmins, 很明显看到了DevOps有木有?
-
由于应用的软件依赖栈完全由应用自己在Dockerfile中定义和维护 ,因此开发人员能够更清楚,更灵活的掌控自己的软件运行环境。 运维人员也不用为应用软件依赖栈的变更碎片化自己的时间。
-
最最重要的一点,Dockerfile的存在,非常清晰地将研发和PE的责任和界限划分清楚了。 开发人员可以FROM 运维人员提供的基础镜像,配置自己应用的依赖栈; 运维人员可以FROM 更底层的系统工程师的基础镜像, 配置环境依赖栈; 系统工程师则定义了一个公司的基础Linux系统所需的版本和配置。
另外,从资源的角度上讲, docker化能够大大减少开发/测试环境的成本,测试或者调试的场景是当发起测试的时候才需要, 其他时候测试环境并不承担业务, 如果用虚拟机则白白的在那里空跑。 Docker 化之后可以在需要的时候随时拉起来整个环境,很快,并且不会出错, 因此**阿里云持续交付平台CRP在会在将来提供集成测试环境,作为一项基础服务, 如果没有容器化,那提供整个服务的成本和可行性都是无法想象的**
- 片尾:希望大家都能运用docker技术做到被说了很久但无法落地的:DevOps
花絮....
- 不是都片尾了怎么还有花絮? 因为我感觉刚才通篇说的好像把docker神话了, 为了防止大家出现过度崇拜和追捧的情况,还是要回过头来考虑一下, 到底什么样的应用适合Docker化,换句话说到底什么样的应用适合容器化?
-
如上图: 我们认为Web应用,微服务,这种即无状态(是指好比一个web应用,通过10台服务器提供服务,当挂掉1台的时候流量自动被其他九台分摊,不会影响到用户,这样就叫无状态应用),又生命周期很短的业务适合docker化, 反过来,每个应用都是有状态,有存储的这种情况,不太容易docker化,或者说docker化的好处不明显 。
-
但我们认为的也不一定是对的,今天docker技术,容器技术发展的速度太快, 所以花絮里这个问题 Let’s Think out together ……
本文基本上表达了我在Qcon2016北京站上的分享,和阿云其他同事在各种运维,容器大会上分享里关于容器化持续交付的思想, 表达方式不同,但思想基本上是一致的。
本来想写成step by step的教程, 但考虑到docker 技术还是要了解它的特性和思想, 才能熟练运用,否则会出现把容器当vm来用等形式, 并且既然说了是完整版,所以分别用了java,ruby, nodejs, pyhton 和php 举例,当然都涉及的很浅,都是很简单的示例。
还是希望大家能对docker的特性有更深的理解, 讲其长处运用在自己的公司,自己的项目和自己的应用中。