Container Overview

总结了一些容器的知识,回答别人的问题

应用部署

实际上,容器技术流行之后最受益的是开发者,因为开发者可以借助容器技术快速的部署好开发测试环境,也可以将自己的应用打包成一个镜像发放出去,用户可以直接拥有和开发者相同的环境,不需要繁琐的部署

CI/CD(持续集成、持续部署)

容器非常适合敏捷开发模式,开发者将代码通过 svn/git 提交到远程仓库后,通过 jenkins/gitlab-ci 等持续集成工具直接构建包含最新代码的镜像,运行测试,测试成功后自动将新版本的代码的镜像滚动更新到线上环境,完全自动化,解放运维

微服务

将大型应用解耦成若干个服务模块,各服务模块松耦合,独立部署,各服务模块通过 HTTP API/RPC 通信,这种架构使得每个服务皆可以实现横向扩展,故障转移,滚动更新等高级特性,不过对于微服务来说,容器技术只是辅助,最关键的还是服务的设计

疑问

别人如何使用我的容器?

容器不是虚拟机,一般不会直接进入内部操作,打包一个镜像后,用户只需要最简单的 docker run,需要的时候提供一些环境变量用来初始化应用即可,并不需要进入内部!

就比如 mysql 官方的镜像

$ docker pull mysql

$ docker run -d -e MYSQL_ROOT_PASSWORD=passwd mysql # 这样就可以了,直接通过外部去访问 mysql,不需要进入容器内部

相比与在 centos 上安装 mysql 需要做的步骤…

$ yum install mysql mysql-server -y

$ systemctl start mysqld

$ mysql_secure_installation

$ mysql

GRANT ALL ....

当然,这只是最简单的一个数据库安装,如果你的应用 前端用的是 nginx,后端是 mysql,还要部署 php 环境呢?假设你的应用叫做 summer-blog

$ docker pull summer-blog

$ docker run -tid -p 80:80 summer-blog

$ curl localhost:80

将复杂的环境直接打包成一个镜像,用户直接通过 docker run 就可以得到和你一样的环境,直接运行你的应用,再也不需要 *** install… 操作了

不过一般不推荐这么做,因为将那么多组件打包在一个镜像里面,无法追踪其状态

推荐分别将 nginx, mysql, php 环境独立为一个容器,再通过一些编排工具 例如 docker-compose 将每个容器连接起来

就可以使用一些很高级的功能,比如 health check,检测到 nginx 异常退出了,自动将 nginx 容器重启,提高整个架构的可用性

RUN、CMD、ENTRYPOINT 的区别

Dockerfile 中有三个用来执行命令的方式,分别是 RUN、CMD、ENTRYPOINT

它们分别有什么区别呢?

  • RUN 是用来指定构建镜像(build image) 过程中执行的命令

  • CMD 是指定容器运行时(docker run) 默认命令和参数,可以被 docker run *** command 的命令所覆盖

  • ENTRYPOINT 也是容器运行时执行的命令,但是可以接受 docker run *** args或者 CMD 提供的参数,一般用于容器配置初始化

主要是 CMD 和 ENTRYPOINT 之间的差异,一个 Dockerfile 中,如果 CMD 和 ENTRYPOINT 同时存在,那么 CMD 的会传给 ENTRYPOINT,我们来看一个例子

通过 CMD 的方式输出字符串

$ cat Dockerfile

from centos:7

CMD ["echo", "hello world"]

$ docker build . -t test

$ docker run test

hello world

如果我们想自定义输出的字符串的,可以修改 Dockerfile 重新 build 镜像,或者手动传递执行的命令,像下面这样

$ docker run test echo hello docker

hello docker

因为 CMD 是容器启动时候的默认命令,是会被 docker run image 后面的内容给覆盖的

通过 ENTRYPOINT 输出字符串

$ cat Dockerfile

from centos:7

ENTRYPOINT ["echo", "hello"]

$ docker build . -t test

$ docker run test

hello

$ docker run test world
hello world

$ docker run test echo hello world

hello echo hello world

可以看出,使用 ENTRYPOINT 之后,docker run image 后面的内容就作为位置参数传递进去了,并没有覆盖原有的命令

CMD 和 ENTRYPOINT 同时存在

$ cat Dockerfile

from centos:7

ENTRYPOINT ["echo"]

CMD ["hello world"]

$ docker build -t test

$ docker run test
hello world

会看到 CMD 也作为参数传递到 ENTRYPOINT 后面了,因为 Dockerfile 中的 CMD 其实和命令行 docker run image 后面跟的内容是等价的,但是命令行的优先级更高

$ docker run test hello python

hello python # 覆盖了 CMD

实际使用

现在比较流行的做法是 ENTRYPOINT 和 CMD 混用,ENTRYPOINT 指定一个初始化容器的脚本,CMD 指定启动前台应用的命令,我们来看看 redis 官方镜像是怎么做的

MySQL 5.7 Dockerfile

# 省略前面
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306
CMD ["mysqld"]

docker-entrypoint.sh

MySQL 的 docker-entrypoint 大概的意思就是,可以手动传递一些选项,或者通过 -e 设置类似 MYSQL_ROOT_PASSWORD 这样的环境变量来初始化 MySQL