Docker要解决什么问题

这段代码在我的机器上没问题啊φ( ̄∇ ̄o),相信大部分程序员都遇到过这种场景,运营反馈了生产环境的bug给我们,我们在自己的电脑运行了代码,然后对运营说出了开头的那句话。

  • 开发过程中一个常见的问题就是环境不一致。由于开发环境、测试环境、生产环境不一致,导致有些bug并未在开发过程被发现。
  • 环境的配置也是一件麻烦事,开发环境、测试环境、生产环境都要配置一遍,开发环境多是在Windows和Mac系统配置,测试环境和生产环境在Linux系统配置,系统的不同,导致系统的设置,依赖库和组件的安装方法都有可能不同。
  • 环境配置如此麻烦,换一台机器,就要重新配置一遍环境,费事费力。

Docker就是为了解决上面的问题而开发的。Docker的出现,使得开发环境、测试环境和生成环境的环境配置可以保持完全一致,应用的迁移也更加方便,无论是本地主机,物理机,还是各种云主机,都可以很轻易地将应用的迁移过去。

虚拟机

在Docker出现之前,虚拟机是解决上面问题的一种方案,但Docker相比虚拟机,有以下的优点

  • 更高效的利用系统资源
    虚拟机需要进行硬件模拟和运行完整的操作系统,在该系统上再运行所需要的应用。虚拟机会独占一部分的内存和硬盘空间。即使虚拟机里真正使用的内存只有1MB,依然需要几百MB的内存才能运行。

    而容器内的应用直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

8IeTGF.png
8Ie724.png

  • 更快速的启动时间
    虚拟机需要启动这个操作系统,可能要几分钟。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查看本机的所有容器,可以看到刚刚生成的容器实例STATUSExited

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。

其他容器命令

  1. docker container start <CONTAINER ID>, run是每次都会生成一个新的容器文件。start是启动已停止运行的容器。
  2. docker container stop <CONTAINER ID>,终止容器运行。
  3. docker container rm <CONTAINER ID>,删除容器。
  4. docker container exec -it <CONTAINER ID> /bin/bash用来进入一个正在运行的容器。
  5. 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镜像加速配置:

8TkNzd.jpg

制作自己的镜像

了解了镜像和容器后,在开发过程中,我们可以将代码和依赖库打包,制作自己的镜像文件,上传到仓库,测试环境和生成环境可以直接从仓库抓取我们的镜像文件,这样我们的测试环境和生成环境就可以在完全相同的环境配置下,运行我们的代码。

下面是制作镜像文件的流程。

  1. 新建一个简单的项目。我这里建了一个python flask项目。

    8T90lq.jpg

    同时在项目的根目录下,新建一个文件.dockerignore。将venv目录排除,不要打包进镜像文件。

    8o9rKf.jpg

  2. 编写Dockerfile文件。

    在项目的根目录,新建一个文件Dockerfile

    8oQBkR.jpg

    上面一共五行代码

    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命令

  3. 创建镜像文件
    使用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
  4. 运行容器

    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/

    8T9wpn.jpg

    新建一个shell文件runserver.sh,每次更新代码后,删除之前的镜像,重新构造镜像,并运行容器。

    8TlDw4.jpg

  5. 发布镜像文件
    容器运行成功后,就确认了镜像文件的有效性。这时候,就可以将镜像发布到网上,供其他人使用。

    除了官方的docker hub,国内的阿里云也提供了镜像仓库。

    阿里云容器镜像服务

参考资料

Docker 入门教程

Docker — 从入门到实践

What is the difference between a Docker image and a container?

Last modification:March 26th, 2020 at 11:39 am
如果觉得我的文章对你有用,请尽情赞赏 🐶