Docker要解决什么问题
这段代码在我的机器上没问题啊φ( ̄∇ ̄o)
,相信大部分程序员都遇到过这种场景,运营反馈了生产环境的bug给我们,我们在自己的电脑运行了代码,然后对运营说出了开头的那句话。
- 开发过程中一个常见的问题就是
环境不一致
。由于开发环境、测试环境、生产环境不一致,导致有些bug并未在开发过程被发现。 - 环境的配置也是一件麻烦事,开发环境、测试环境、生产环境都要配置一遍,开发环境多是在Windows和Mac系统配置,测试环境和生产环境在Linux系统配置,系统的不同,导致系统的设置,依赖库和组件的安装方法都有可能不同。
- 环境配置如此麻烦,换一台机器,就要重新配置一遍环境,费事费力。
Docker就是为了解决上面的问题而开发的。Docker的出现,使得开发环境、测试环境和生成环境的环境配置可以保持完全一致,应用的迁移也更加方便,无论是本地主机,物理机,还是各种云主机,都可以很轻易地将应用的迁移过去。
虚拟机
在Docker出现之前,虚拟机是解决上面问题的一种方案,但Docker相比虚拟机,有以下的优点
:
- 更高效的利用系统资源
虚拟机需要进行硬件模拟和运行完整的操作系统,在该系统上再运行所需要的应用。虚拟机会独占一部分的内存和硬盘空间
。即使虚拟机里真正使用的内存只有1MB,依然需要几百MB的内存才能运行。而容器内的应用直接运行于宿主的内核,
容器内没有自己的内核,而且也没有进行硬件虚拟
。因此容器要比传统虚拟机更为轻便。
- 更快速的启动时间
虚拟机需要启动这个操作系统,可能要几分钟。Docker容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级
、甚至毫秒级
的启动时间。大大的节约了开发、测试、部署的时间。
Docker的安装
Docker的安装,可以在官网找到自己系统对应的版本来安装。
Docker的使用
Docker包含三个基本概念镜像(Image)
、容器(Container)
和仓库(Repository)
。
Docker把应用程序及其依赖,打包到镜像文件里面。只有通过这个文件,才能生成容器。image 文件可以看作是容器的模板。Docker根据镜像文件生成容器的实例。同一个镜像文件,可以生成多个同时运行的容器实例。
Docker的仓库,用来存放镜像文件,从仓库里我们可以下载别人制作好的镜像,也可以上传自己制作的镜像文件到仓库,供别人下载。
类比下:
镜像
是食谱
,容器
是蛋糕
,按照同一份食谱(镜像)
可以做出很多相同的蛋糕(容器)
。仓库
是书架
,存放食谱(镜像)
。
镜像
我们可以通过Docker官方的仓库下载镜像。运行下面的命令,将镜像从仓库下载到本地。
vstorm@MacBook-One: docker image pull library/hello-world
Using default tag: latest
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:f9dfddf63636d84ef479d645ab5885156ae030f611a56f3a7ac7f2fdd86d7e4e
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest
查看本机的镜像文件,可以看到我们的镜像文件大小只有1.84k,因为它包含了打印出Hello World的代码。
vstorm@MacBook-One:~$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 14 months ago 1.84kB
可以使用docker image rm <IMAGE ID>
命令,删除镜像文件
容器
现在,使用docker container run <IMAGE ID>
,运行这个镜像文件,生成容器实例,容器实例本身也是一个文件。就好像我们已经拿到了食谱,按照食谱来制作蛋糕。
vstorm@MacBook-One:~$ docker container run fce289e99eb9
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
输出上面的提示后,容器就会自动终止。
docker container ls -a
查看本机的所有容器,可以看到刚刚生成的容器实例STATUS
是Exited
。
vstorm@MacBook-One:~$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3fff14e99941 fce289e99eb9 "/hello" About a minute ago Exited (0) About a minute ago kind_albattani
docker container run
命令,还可以指定一些参数。
例如下面的命令里
$ docker container run -p 8000:3000 -it koa-demo sh
-p 将容器的3000端口映射到本机的8000端口。
-it 容器的shell映射到当前的shell,然后你在本机输入的命令,会传入到容器。
/bin/bash 容器启动以后,内部执行的第一个命令。这里是启动bash,保证用户可以使用shell。
其他容器命令
docker container start <CONTAINER ID>
,run
是每次都会生成一个新的容器文件。start
是启动已停止运行的容器。docker container stop <CONTAINER ID>
,终止容器运行。docker container rm <CONTAINER ID>
,删除容器。docker container exec -it <CONTAINER ID> /bin/bash
用来进入一个正在运行的容器。docker container cp [CONTAINER ID>:[path/to/file] .
从正在运行的容器里,复制文件到当前目录。
仓库
镜像构建完成后,可以很容易的在当前主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。
国内从Docker Hub拉取镜像又是会运到困难,此时可以配置镜像加速器。国内很多云服务商都提供了国内加速器服务。
macOs镜像加速配置:
制作自己的镜像
了解了镜像和容器后,在开发过程中,我们可以将代码和依赖库打包,制作自己的镜像文件,上传到仓库,测试环境和生成环境可以直接从仓库抓取我们的镜像文件,这样我们的测试环境和生成环境就可以在完全相同的环境配置下,运行我们的代码。
下面是制作镜像文件的流程。
- 新建一个简单的项目。我这里建了一个
python flask
项目。同时在项目的根目录下,新建一个文件
.dockerignore
。将venv目录排除,不要打包进镜像文件。 编写Dockerfile文件。
在项目的根目录,新建一个文件
Dockerfile
。上面一共五行代码
FROM python:3.6-alpine 继承官方的python:3.6-alpine镜像。 COPY . /app 将当前目录下的所有文件(除了.dockerignore排除的路径),都复制进入镜像文件的/app目录。 WORKDIR /app 指定接下来的工作目录为/app。 RUN pip install -r requirements.txt 在/app目录下,运行pip install命令安装python依赖库。这些依赖安装成功后,都会打包到镜像文件中。 EXPOSE 5000 将容器端口5000暴露出来,允许外部连接这个端口。 CMD python app.py 容器启动后,运行flask程序。
注意
:RUN
命令是在镜像文件构建阶段执行,执行的结果会打包进镜像文件。CMD
命令则是容器启动后执行。一个Dockerfile
可以有多个RUN
命令,但是只能有一个CMD
命令。
指定了CMD
命令以后,docker container run
命令就不能附加命令了(比如前面的sh),否则它会覆盖CMD
命令创建镜像文件
使用docker image build -t <Image Name> .
命令创建新的镜像文件。vstorm@MacBook-One:~/PycharmProjects/vstorm$ docker image build -t vstorm . Sending build context to Docker daemon 40.96kB Step 1/6 : FROM python:3.6-alpine ---> 781b53c4648c Step 2/6 : COPY . /app ---> 04c19f45b68a Step 3/6 : WORKDIR /app ---> Running in 33a3c405e7e2 Removing intermediate container 33a3c405e7e2 ---> 6deb97e9b45c Step 4/6 : RUN pip install -r requirements.txt ---> Running in 5106acee0727 Collecting click==7.1.1 Downloading click-7.1.1-py2.py3-none-any.whl (82 kB) ... Successfully installed Flask-1.1.1 Jinja2-2.11.1 MarkupSafe-1.1.1 Werkzeug-1.0.0 click-7.1.1 itsdangerous-1.1.0 Removing intermediate container 5106acee0727 ---> 4004a0db35aa Step 5/6 : EXPOSE 5000 ---> Running in 0a624dc47f6a Removing intermediate container 0a624dc47f6a ---> 35c4f376f010 Step 6/6 : CMD python app.py ---> Running in d40aa0005873 Removing intermediate container d40aa0005873 ---> 3f32ba4fc4e1 Successfully built 3f32ba4fc4e1 Successfully tagged vstorm:latest
可以看到
Dockerfile
里的代码被一行一行地执行。使用
docker image ls
可以查看刚创建的镜像文件。vstorm@MacBook-One:~/PycharmProjects/vstorm$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE vstorm latest 3f32ba4fc4e1 12 minutes ago 105MB python 3.6-alpine 781b53c4648c 6 weeks ago 94.6MB hello-world latest fce289e99eb9 14 months ago 1.84kB
运行容器
vstorm@MacBook-One:~/PycharmProjects/vstorm$ docker run --rm -p 50003:5000 -it 3f32ba4fc4e1 *Serving Flask app "app" (lazy loading) *Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. *Debug mode: off *Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
flask
程序运行起来了。使用-rm
参数,容器终止后会自动删除。在浏览器输入
http://127.0.0.1:50003/
新建一个shell文件
runserver.sh
,每次更新代码后,删除之前的镜像,重新构造镜像,并运行容器。- 发布镜像文件
容器运行成功后,就确认了镜像文件的有效性。这时候,就可以将镜像发布到网上,供其他人使用。除了官方的docker hub,国内的阿里云也提供了镜像仓库。
参考资料
What is the difference between a Docker image and a container?