0%

Docker容器云计算革命

在讲解Docker之前,我想请问大家,我们是如何给手机安装App的?

我想大家一定会说,这也需要考吗?这太简单了,直接在AppStore搜索你想要的软件,然后点击安装呀。

没错 !接着我想请大家回忆下,尤其是80后、70后,在PC时代,尤其是360软件管家出现之前大家是如何安装软件的?那时候可不像现在这样“一站式”的,那时候很费劲,大概会有这么几个步骤:

  1. 百度搜索: 在百度搜索我们需要的软件名。
  2. 跳转软件网站: 接着跳转到中关村在线或者是下载天空等软件下载类网站。
  3. 迅雷下载: 为了加速下载,往往会弹出迅雷,几分钟后,软件下载到本地了。
  4. 手动安装: 在迅雷的下载目录,找到要刚才的软件,点击安装。然后输入若干信息,比如安装目录和数据目录等。如果遇到写注册表的,还需要输入机器密码等。
  5. 开始菜单: 最后在“开始”里找到刚安装的软件,点击运行。当然有的软件安装后,会提示是否直接运行。
  6. 更糟糕的: 如果这个软件是.Net或Java开发的,运行的时候,还会报错,提示我们得先下载.Net Framework或JRE,否则无法运行。

辛亏如今一切变得那么顺畅。然而,如果我们把视线从客户端App,转移到服务端看一看呢?程序员们安装服务器软件,多数公司还处在PC时代安装软件一样。研发人员需要把软件打包给运维人员,复杂点的,还可能需要附带一个安装手册,里面可能包含如何链接到数据库、如何做数据初始化、如何设置对其他服务的依赖等。我们就会想,难道服务端程序的部署就不能像AppStore那样吗?搜索,点安装,两步完成了。甚至更简单,程序写完了就自动部署?

的确可以,这就是今天要说的Docker。在Docker的世界里,要求程序员把程序打包成一个叫做镜像的东西,英文叫Docker Image。接着把这个镜像,上传到镜像的AppStore,也就是术语里说的Docker Hub镜像中心。最后其他人员(包括运维,甚至其他网友)都可以一键“点击部署/运行”。

什么是镜像?我们可以形象的理解为,它是一种软件虚拟化技术,它能够把开发人员开发那个软件时,使用的笔记本,完整的刻录下来,相当于硬件的笔记本也能被复制。或者更确切的说是,相当于把那个笔记本里的操作系统、开发的软件及其依赖的软件,全部复制了。

实践镜像的运行

为了访问速度,我们找了一个国内的镜像中心`,也就是Docker界的AppStore。地址是:

https://dashboard.daocloud.io/packages/explore

我们搜索下Tomcat,我们能像在AppStore里那样,看到这个镜像的受欢迎程度——被下载了 7825次,被点赞了 152次。

image-20190925144224339

点击进入 Tomcat镜像详情页,可以看到:基本信息、版本迭代、用户评论。还有两个操作:

image-20190925144919475

  • 拉取: 下载到本地,把本地作为Docker集群来运行镜像。
  • 部署: 直接用DaoCloud的在线Docker集群来运行镜像。

部署在线运行

“部署”就是直接利用DaoCloud的Docker集群来运行DockerHub上的镜像。就跟在AppStore里面安装App那么简单。

image-20190925171207178

应用设置三大类:端口映射磁盘挂载环境变量设置

image-20190925171500499

等效的YAML运行文件是:

1
2
3
4
5
6
7

hello-world-tomcat:
image: daocloud.io/library/tomcat:8.0.45-jre7
privileged: false
restart: always
ports:
- 80:8080

运行的结果:

image-20190925171756100

由于我们没有在容器集群外,套一个Load Balancer,因此没有外网地址,无法从公网访问。但是我们可以登录「控制台」,用命令行访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

root@3d9bc7f36bf8:/usr/local/tomcat# curl http://10.23.142.228:80 -I
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 25 Sep 2019 09:20:45 GMT

root@3d9bc7f36bf8:/usr/local/tomcat# curl http://172.17.0.1:80 -I
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 25 Sep 2019 09:20:48 GMT

root@3d9bc7f36bf8:/usr/local/tomcat#

拉取本地运行

  • 下载: 点击“拉取”,页面会提示命令行:

image-20190925170500169

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$ docker login daocloud.io
$ docker pull daocloud.io/library/tomcat:8.0.45-jre7

8.0.45-jre7: Pulling from library/tomcat
21f90b3df721: Downloading [=====================> ] 39.56MB/52.61MB
……
Digest: sha256:08132ab8b2c606cfb78a0ef9e80e1e724
Status: Downloaded newer image for daocloud.io/library/tomcat:8.0.45-jre7
daocloud.io/library/tomcat:8.0.45-jre7

$ docker images # 查看本地镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
daocloud.io/library/tomcat 8.0.45-jre7 fa6ad595ba45 2 years ago 357MB

$ docker inspect daocloud.io/library/tomcat:8.0.45-jre7 # 查看镜像详情
  • 运行: 镜像详情页通常会有如何运行的教程

image-20190925170805607

以前台的方式运行该镜像,并将容器内的8080端口,映射为宿主机的18080端口:

1
2

$ docker run -p 18080:8080 daocloud.io/library/tomcat:8.0.45-jre7

然后在浏览器上输入 http://localhost:18080 ,便会出现Tomcat的首页:

image-20190925173636867

无差别地运行所有软件

可能我们会觉得,安装一个Tomcat,也不会很费劲,直接去Apache网站下载,然后解压,也可以呀。其实不然,直接下载Tomcat,如果要运行,本地还得有JRE。

更重要的是,Docker这种机制它能够让我们「无差别地运行所有软件」。比如我们再运行一个Redis,找到Redis镜像

1
2
3
4
5
6
7
8
9
10
11
12
13

$ docker pull daocloud.io/library/redis:3.2.9

3.2.9: Pulling from library/redis
Status: Downloaded newer image for daocloud.io/library/redis:3.2.9
daocloud.io/library/redis:3.2.9

$ docker images daocloud.io/library/redis:3.2.9
REPOSITORY TAG IMAGE ID CREATED SIZE
daocloud.io/library/redis 3.2.9 3459037fcc3a 2 years ago 98.9MB

$ docker run -p 16379:6379 daocloud.io/library/redis:3.2.9
# Redis容器内的服务端口是6379,映射到宿主机的端口是16379.

然后用telnet作客户端去访问Redis服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

$ telnet localhost 16379
Trying ::1...
Connected to localhost.
Escape character is '^]'.
SADD employee "Alice"
:1
SADD employee "Bob"
:1
SMEMBERS employee
*2
$3
Bob
$5
Alice

这种无差别的性质,可以极大的提高研发工程与运维工程师的沟通效率,更能做到自动化部署。也就是说,在Docker世界里,研发工程师交付的不再是程序了,而是镜像。这正如Docker这个名字,叫「集装箱」,无论你要运输的是手机,还是时装,亦或者是鲜牛肉,不能各是各的包装,而要无差别的放在集装箱里。事实上,如果我们读集装箱改变世界(修订版) [The Box:How the Shipping Container Made the World Smaller and the World Economy Bigger] 书,它会告诉我们物流世界的集装箱同样是依靠无差别性提高了效率,改变了世界,尽管可能集装箱内有空隙,没装满,看似浪费了空间。

容器与宿主机间的关联

前面通过-p 16379:6379 进行了端口映射,如果我们想让Redis开启持久化,并把文件存到宿主机呢?

则可以通过-v参数进行磁盘挂载,比如:

1
2
3
4
5
6
7
8

$ docker run --name order-cache -p 16379:6379 -v ~/tmp/dockerdata:/data -d daocloud.io/library/redis:3.2.9 --appendonly yes

80f25d6252cf5a76edd0a05b8e6ced4d2467d9362db30ab1def5022ac9f44fbd

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80f25d6252cf daocloud.io/library/redis:3.2.9 "docker-entrypoint.s…" 6 seconds ago Up 5 seconds 0.0.0.0:16379->6379/tcp order-cache

参数解释

  • -p 16379:6379: 表示端口映射。把宿主机的16379映射到容器内的6379。注意宿主机在前。
  • -v ~/tmp/dockerdata:/data: 表示磁盘挂载。把宿主机的 ~/tmp/dockerdata 映射到容器内的 /data。我们是如何知道Redis在容器内的数据放在/data目录的呢?一方面看README.md帮助文档,另一方面也可以用docker inspect daocloud.io/library/redis:3.2.9查看 Volumes 节点。
  • -d: 参数-d表示 daemon方式运行。不过官方解释是“-d, –detach Run container in background and print container ID”。相对应的如果前台运行,则-i参数,官方解释是 “-i, –interactive Keep STDIN open even if not attached”。通常用-it,参数的含义“-t, –tty Allocate a pseudo-TTY”。
  • --name order-cache: 给容器ID标注一个名字。这样就可以用该ID删除容器,比如docker kill order-cache
  • --appendonly yes: 最后的“–appendonly yes”,是应用参数,表示Redis开启持久化,持久化文件就存在前面说的容器内的/data,然后被映射到宿主机的~/tmp/dockerdata:/data。

当我们用telnet,并写入 SADD employee "Alice"后,查看宿主机的 ~/tmp/dockerdata 目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat ~/tmp/dockerdata/appendonly.aof
*2
$6
SELECT
$1
0
*3
$4
SADD
$8
employee
$5
Alice

客户端模式与容器间通信

如何在daocloud.io/library/redis:3.2.9 镜像中运行redis客户端模式呢?

1
2
3
4
5

$ docker run --name order-client -it daocloud.io/library/redis:3.2.9 redis-cli

Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected>

这些参数的含义:

  • --name order-client: 对容器ID给了个名字。

image-20190925223003812

  • -it: 以前端交互的形式启动,而不是以后台的模式启动。
  • redis-cli: 在运行docker run daocloud.io/library/redis:3.2.9时,通过程序参数redis-cli表示客户端模式。

连不上127.0.0.1:6379呢?换成 127.0.0.1:16379呢?

1
2
$ docker run --name order-client -it daocloud.io/library/redis:3.2.9 redis-cli -h 127.0.0.1 -p 16379
Could not connect to Redis at 127.0.0.1:16379: Connection refused

依然连不上,原因是redis-cli运行在容器内,并没有运行在宿主机上。如果要从一个容器,访问另一个容器,需要使用--link参数。

顺便说一下,多次启动redis-cli容器,并且打标order-client,容器名会冲突。需要每次删除:

1
2
3
4
5
6
$ docker kill order-client
Error response from daemon: Cannot kill container: order-client: Container f2c4ce6f6441d1086f26c813fc266b996aaf943a655aea0c5554a9763ef0b6ca is not running
$ docker stop order-client
order-client
$ docker rm order-client
order-client

为了不那么麻烦,可以在运行时,加--rm Automatically remove the container when it exits参数,这样会先删除已有的容器,再启动新容器。

1
$ docker run --name order-client -it --rm daocloud.io/library/redis:3.2.9 redis-cli -h 127.0.0.1 -p 16379

回到刚才说的「容器间相互访问的」--link参数。

  • –link: --link order-cache:redisd 把宿主机上的容器ID order-cache,别名到容器内叫redisd
  • redis-cli -h redisd -p 6379: 然后在容器内引用redisd就表示容器order-cache了。

完整的命令:

1
2
3
4
5
$ docker run --name order-client -it --rm --link order-cache:redisd daocloud.io/library/redis:3.2.9 redis-cli -h redisd -p 6379

redisd:6379> SMEMBERS employee
1) "Bob"
2) "Alice"

轻资产云生态

Docker云的核心是什么?两个东西:

  • 云化技术: 能够把机器进行Docker化,并进行集群化管理的技术。
  • 主机资源: 云计算公司需要集中购买大量的主机,而且为了满足各地域用户的需求,还需要在全世界范围部署机房。从这个角度,商业本质上,云计算公司就是一个租赁公司?只不过它利用了高科技手段,将租赁变得更加高效和智能,让用户可以按需计费。租赁通常有个固定资产风险,如果市场需求不足,将会导致大量的机器闲置。

如何轻资产化呢?能不能把经营权与所有权分离呢?这个在很多领域都有:

  • 商业地产领域: 万达广场能迅速在全国快速扩张,原因之一就是有很多广场的所有权并不是万达的,而是万达与其他地产所有权公司合作建设的,万达出建设方案和商业运营管理,合作公司出地和施工费等,最后挂万达广场的品牌。
  • 物流仓储领域: 阿里和京东都有在布局「云仓」,仓本身并不是阿里和京东的,是社会第三方主体的资源,但是加入云仓,由它们提供软件进行电子化管理和现场实操运营经验。
  • 新能源充电桩领域: 比如星星充电,并不是所有的充电桩都是星星的,有不少是个人私家充电桩,加入了星星充电,由星星充电提供充电计费等软件管理,然后各自分成。

同样,如果我们自己有一个主机或者我们购买了阿里的主机,都可以把它加入Docker云,我们除资产,云计算公司出Docker化技术和管理。以DaoCloud为例:

image-20190925233051402

如果我们再把我们的笔记本电脑或台式机,而不是服务器主机也加入云计算公司呢?这就是一种「边缘计算」。