侧边栏壁纸
博主头像
Wyatt博主等级

Done is better than perfect!

  • 累计撰写 103 篇文章
  • 累计创建 31 个标签
  • 累计收到 7 条评论

Docker:快速上路

Wyatt
2021-03-24 / 0 评论 / 0 点赞 / 374 阅读 / 5,269 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-03-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一. 简介

Docker核心是由Linux Namespace 的隔离能力、Linux Cgroups 的限制能力,以及基于 rootfs 的文件系统三个组成,

枯燥的指令集毫无乐趣,所以我借鉴了他人的教学方法,采用一个小demo从头到尾运行一边docker镜像的全生命流程。

二. 构建流程

我们今天采用docker进行一个Flask Web项目的全流程构建,部署和推送等。

相关代码在此链接中:flask_web项目地址

2.1 构建App

这个项目将启动一个Flask项目,暴露一个18080的端口,当访问Web根目录时,将会返回向一个'Hello World!' + hostname的字符串,hostname代表当前容器的hostname。
app.py文件如下:

from flask import Flask
import socket

app = Flask(__name__)


@app.route('/')
def hello_world():
    hostname = socket.gethostname()
    return 'Hello World!' + hostname


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=18080)

2.2 编写Dockerfile

Dockerfile 的设计思想,是使用一些标准的原语,描述我们所要构建的 Docker 镜像。并且这些原语,都是按顺序处理的,这也算是一种应用的自描述,是一种很符合DevOps的要求。

关于编写Dockerfile的源码也已将放到GitHub上面了,内容如下:


# 拉取Python3
FROM rackspacedot/python37

# 将工作目录切换为/app
WORKDIR /app

# 将当前目录下的所有内容复制到/app下
ADD . /app

# 使用pip命令安装这个应用所需要的依赖
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 允许外界访问容器的18080端口
EXPOSE 18080

# 设置容器进程为:python app.py,即:这个Python应用的启动命令
CMD ["python", "app.py"]

关于docker file里面的原语内容可以如下理解:

  • FROM:指docker从rackspacedot拉取一个python37的基础镜像
  • WORKDIR:切换容器的工作目录到/app目录下
  • ADD:理解为复制dockerfile所在目录的内容到/app工作目录
  • RUN:容器里执行 shell 命令
  • EXPOSE:声明容器暴露一个端口18080
  • CMD:指定 python app.py 为这个容器的进程,app.py 的实际路径是 /app/app.py。
  • ENTRYPOINT(默认):实际进程是:/bin/sh -c "python app.py",即 CMD 的内容就是 ENTRYPOINT 的参数,所以Docker 容器的启动进程为 ENTRYPOINT,而不是 CMD。

2.3 docker build

docker build 会自动加载当前目录下的 Dockerfile 文件,然后按照顺序,执行文件中的原语。
在编写Dockerfile所在的目录,执行如下的指令:

docker build -t flask_web .

关于指令细节:

  • -t:给这个镜像加一个 Tag
  • flask_web:这个镜像的名称

build success

Dockerfile 中的每个原语执行后,都会生成一个对应的镜像层。即使原语本身并没有明显地修改文件的操作(比如,ENV 原语),它对应的层也会存在。只不过在外界看来,这个层是空的。

当我们构建成功后,可以使用如下指令查看本地镜像内容:

docker image ls

docker image list
不过,上图中构建出的镜像非常大,这说明我选择的原始镜像非常有问题,这也是后续优化的点,这章不做讨论。

2.4 docker run

构建完的镜像都存在于本地,所以我们可以非常简单的使用docker run指令来运行。
指令如下:

docker run -p 18090:18080 flask_web

关于指令细节,作用如下:

  • p:指定映射端口,将宿主机18090端口映射到容器的18080端口
  • flask_web:为本地flask_web的镜像名称

runtime

  • 容器启动之后,我可以使用 docker ps命令看到:
docker ps

ps

  • 我们需要访问该web,可以浏览器访问:http://localhost:18090/
    我们将看到Hello World!2d278afce7a7的内容,代表我们容器的成功运行。

2.5 docker push

当我们完成镜像的编写,我们可以把这个容器的镜像上传到 DockerHub 上分享给更多的人。

  1. 首先需要注册一个 Docker Hub 账号,然后使用docker login命令登录
  2. docker tag给容器镜像取一个完整的名字
docker tag flask_web wy1140174371/flask_web:v1.0.0

具体指令作用如下:

  • flask_web:代表tag的镜像名称
  • wy1140174371:即为个人的dockerhub账户,也叫‘repository’
  • /flask_web:代表当前项目的名称
  • v1.0.0:代表tag的版本,小步增量
  1. 采用docker push推送到docker hub上
    执行如下指令:
docker push wy1140174371/flask_web:v1.0.0

上传成功后,可以在此处查看docker hub的镜像内容,flask_web

flask_web

2.6 docker commit(optional)

docker commit把一个正在运行的容器,直接提交为一个镜像。一般来说,需要这么操作原因是:当这个容器运行起来后,我又在里面做了一些操作,并且要把操作结果保存到镜像里。例如我在容器内新增了一个文件。
具体指令可以使用如下:

docker commit container-id wy1140174371/flask_web:v1.0.1

具体参数含义如下:

  • container-id:此处需要自己替换为正在运行的容器id,通过docker ps获取。
  • v1.0.1:版本注意增量

三. Volume

Volume 机制,允许我们将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作.

3.1 俩种方式

在 Docker 项目里,它支持两种 Volume 声明方式,可以把宿主机目录挂载进容器的 /test 目录当中:

docker run -v /test ...
docker run -v /home:/test ...

当然俩种方式有一定的区别:

  • 不声明式
    由于没有显示声明宿主机目录,那么 Docker 就会默认在宿主机上创建一个临时目录 /var/lib/docker/volumes/[VOLUME_ID]/_data,然后把它挂载到容器的 /test 目录上。
  • 指定宿主机目录
    这种情况下,Docker 就直接把宿主机的 /home 目录挂载到容器的 /test 目录上。

3.2 原理

这个功能利用了Linux 的绑定挂载(bind mount)机制。它的主要作用就是,允许我们将一个目录或者文件,而不是整个设备,挂载到一个指定的目录上。并且,这时我们在该挂载点上进行的任何操作,只是发生在被挂载的目录或者文件上,而原挂载点的内容则会被隐藏起来且不受影响。

3.3 图解

图解如下,mount --bind /home /test,会将 /home 挂载到 /test 上。其实相当于将 /testdentry,重定向到了 /homeinode。这样当我们修改 /test 目录时,实际修改的是 /home 目录的 inode。这也就是为何,一旦执行 umount 命令,/test 目录原先的内容就会恢复:因为修改真正发生在的,是 /home 目录里。

architecture

四. 拓展

4.1 docker exec

docker exec指令可以让我们进入容器内,并在容器内执行相关操作。
如下例子,就是一个进入容器后,并运行/bin/sh.

docker exec -it container-id /bin/sh

4.2 docker inspect

docker inspect指令可以看到当前正在运行的 Docker 容器的进程号(PID),即宿主机上进程号。
具体如下:

docker inspect --format '{{ .State.Pid }}' container-id

4.3 ENTRYPOINT

Docker 容器的启动进程为 ENTRYPOINT,而不是 CMD,即 CMD 的内容就是 ENTRYPOINT 的参数。

4.4 Docker Registry

DockerHub只是官方的镜像上传系统,但是我们也是可以自建其他的镜像系统的,例如Harbor。就比如我们的Java领域的Maven仓库,除了maven,github,sonatype等外,也可以自建nexus仓库等,按照实际需求来即可。

五. 总结

关于docker已经使用很多年了,在最近的时间内重新巩固了一下基础知识。这些东西其实光看光听帮助不大,主要的是我们需要通过实操来巩固记忆。
学习金字塔模型底层的主动式学习是最有效的方式,我一直是一个推崇方法论的实践者。
我经过了很久的工作秘密,确定了走DevOps的方向,在此路上我还是一个新手,大家共勉。

欢迎关注我的博客:https://blog.wyatt.plus

Reference

https://time.geekbang.org/column/article/18119?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title
http://docker-saigon.github.io/post/Docker-Internals/#how:cb6baf67dddd3a71c07abfd705dc7d4b
https://medium.com/@kasunmaduraeng/docker-namespace-and-cgroups-dece27c209c7

0

评论区