Docker 学习手册

Docker 是什么,能做什么?

  • Docker 有点像传统的虚拟机,最大的区别是不需要虚拟出一个内核,直接基于宿主内核。
  • 使用 Docker 的目的是让程序有一个一致的运行环境,方便迁移、部署。官网表示解决的最大痛点是「这段代码在我机器上没问题啊」这个问题。

三个基本概念,镜像、容器与仓库是什么?

  • 镜像:Image,就是很多层==只读==的 layers,后面会写到 Dockerfile,Dockerfile 的一个命令就是一层,所有这些层合起来编程一个 unioned file system。当然,镜像作为这些只读的 layers 合成的文件系统也是只读的。
  • 容器:Container,容器和镜像的区别就是在镜像的外面多了一层可读写的 layer。但容器未必是要在运行状态的。

基本命令有哪些?

  • create、start、stop 和 run:

    • docker run 其实等于 docker create + docker start。

    • docker create 是在 image 上加一层可读写的 layer,变成一个 container。

    • docker start 则是把这个 container 变成 running container。

    • docker stop 把 running container 停下来变成 container。

    • 常见用法:开一个交互窗口:

      docker run --rm -it -p 5000:80 -v /host:/local #image_id /bin/bash

      • --rm :退出之后自动删除这个 container。
      • -it :把 container 连接到 terminal。
      • -p 5000:80:把 container 的 80 端口 map 到 host 的 5000 端口。
      • -v /host:/local:把 host 的路径/host map 到 container 的 /local
      • /bin/bash :开启 container 之后运行这个命令。
  • exec:

    • 和 run 有点像,但 exec 是针对 running container 的,是在 running container里再跑一个进程。

    • 常见用法:再开一个交互窗口:

      docker exec -it #container_id /bin/bash

  • ps 和 images:

    • ps 列出所有 running containers,加上参数 -a,能列出所有 containers.
    • images 列出所有 images,加上参数 -a,列出所有可读层。
  • stop 和 kill:

    • 区别在于对进程是发出了 SIGTERM 还是 SIGKILL。前者可以被 block,后者强制。
  • rm 和 rmi:

    • 前者移除容器的可读写层,只能针对非运行状态的容器。
    • 后者可以移除镜像的只读层,但只能移除最顶层镜像,用 -f 可以移除中间层。
  • commit:

    • 把一个可读写层变成一个只读层,也就是把一个 container 变成 image。
  • build:

    • 输入一个镜像和 dockerfile,输出一个镜像
    • build 的本质其实是 FORM image -> docker run -> RUN command-> docker commit.
  • inspect:

    • 查看一个镜像或容器的元数据。
  • save 和 export:

    • save 只对镜像有效,生成的 tar 文件有所有镜像的层。
    • export 则会生成合并完后的一层镜像,会移除元数据和不必要的层。
  • history:

    • 显示一个镜像的 build 历史。
  • tag:

    • 给一个 image 打上 tag:docker tag ba90d13a384b updated_ubuntu:20170803

Dockerfile 的一些知识

  • FORM:

    • 指定基础镜像,FORM 必须是 Dockerfile 的第一行。有个特殊的空白 FORM 叫 scratch,这个 form 是空白的,也就是不以任何系统为基础,直接将可执行文件复制进镜像。
  • RUN,CMD,ENTRYPOINT:

    • RUN <命令>:<命令>有两种格式,一种是直接写 shell 命令CMD echo $HOME,另一种是 exec 格式RUN ["sh", "-c", "echo $HOME"]。每一个 RUN 行为都会 commit,也就是创建一层新的镜像。但是 Union FS 是有最大层数限制的,目前是不超过 127 层,因此,尽量把所有的 RUN 放到一条命令里面,用 && 把命令串起来。记得每次 RUN 最后要加 apt-get purge -y --auto-remove 清除不必要的中间文件。
    • CMD <命令>:跟 RUN 一样,有两种格式,都是跑一个命令,区别是 RUN 之后的结果是镜像,CMD 是开启容器之后的启动命令,也就是 CMD 执行完之后并不会做 commit。CMD 就一条,就算写了多条,前面的 CMD 都会被忽略,而且在 docker run 的启动命令后加上命令,Dockerfile 里的这一句会被忽略。
    • ENTRYPOINT<命令>:跟 CMD 一样,也是开启容器之后的启动命令,区别是 ENTRYPOINT 的命令可以在启动 docker 的时候补加命令行参数,相当于把整个镜像当做一个命令行工具来使用。
  • COPY,ADD:

    • COPY ./source /target:source 要相对路径,并且必须是./ 而不能是../ 或绝对路径/,COPY 不是真的 copy 文件,而是相当于把这个文件挂载到 docker 里,让 docker 能读取这些文件。ADD 除了 COPY 本身的命令之外,还有解压缩和下载 URL 调整权限的功能,功能比较复杂,但 Docker 官方的最佳实践提示:用 COPY 尽量不用 ADD.
  • ENV,ARG:

    • ENV <key> = <val>:设置环境变量,在其他命令当中可以使用。
    • ARG<key>[=<deafult val>]:设置环境变量名,可以在 docker run 命令中通过 --build-arg 来传进去。
  • EXPOSE:

    • 运行容器时,暴露出来的端口,但其实 EXPOSE 只是一个容器端口的声明,真正映射出去的,是在运行 docker 的时候 -p <宿主端口>:<容器端口> 开的。
  • WORKDIR:

    • 改变工作目录,因为在 Dockerfile 里面写下面这样的命令是没法在/app 下面找到 world.txt 的。

      RUN cd /app
      RUN echo "hello" > world.txt
      
    • 因为每个 RUN 都会构造一层镜像,第一个 RUN 只发生在内存中,对文件系统不做任何修改,第二个 RUN 也就跟第一个 RUN 没有关系了。所以要用WORKDIR 才能真的切换路径。

  • VOLUME:

    • volume 可以将容器以及容器产生的数据分离开来,即使当 rm 了 container 之后,volume 也会保留下来。
    • volume 定义的路径是在 docker container 里的路径,而不是 host 宿主的路径,如果要指定 map 到宿主路径,需要在docker run 的时候用-v /host:/local 来指定。不然 volume 定义的路径会生成一个随机的 host 宿主地址去存储。实际 mount 的地址可以通过docker container inspect --format {{.Mounts}} 07c3fe7802df 命令来获得。修改对应 mount 在 host 地址内的文件夹,能直接影响 container 里面访问的文件夹内容。
    • 但在 mac 里,因为 docker 本身就是放在 VM 里面的,因此,这个路径是 docker 本身 VM 内的地址。可以先开一个 screen:screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty 在里面可以访问 inspect 到的地址。​

从 Docker 内连接 Host 网络

  • 这部分的需求在于,比如我在 Docker 外起了一个服务,我需要从一个 app 的 Docker 内部去访问这个服务 。而因为 Docker 内部是一个虚拟环境,直接访问 localhost 肯定是没法转到宿主的,所以一个方法是知道宿主的 IP,然后通过 IP 去访问。

  • 为了最简化,直接在 Host 用 python 起一个最简单的 server.

    >$python -m SimpleHTTPServer 8000

  • 先试试本地能不能访问,确保 Host 可以访问:

    >$curl 127.0.0.1:8000
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
    <title>Directory listing for /</title>
    <body>
    <h2>Directory listing for /</h2>
    <hr>
    ...
    
  • 然后打开一个 docker:

    >$docker run --rm -it --net=host ubuntu:latest /bin/bash
    
  • 理论上用了--net=host 之后就可以用 localhost 访问 Host 主机了,然而:

    >$curl 127.0.0.1:8000
    curl: (7) Failed to connect to 127.0.0.1 port 8000: Connection refused
    
  • 查了半天,终于发现问题原因是,我的操作系统是 OSX,docker 本身就在沙盒里。所以要用迂回的方法再获得 host ip.

  • 获得 host ip:

    >$ip route|awk '/default/ {print $3}'
    192.168.65.1
    
  • 访问 host 服务:

    >$curl 192.168.65.1:8000
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
    <title>Directory listing for /</title>
    <body>
    <h2>Directory listing for /</h2>
    <hr>
    ...
    
  • 事实上,这个时候就算不用--net=host 一样可以通过 192.168.65.1:8000 访问到host 主机了。不过用ip route|awk '/default/ {print $3}' 这个命令获得不到这个 IP。

  • 使用--net=host 会导致 port mapping 失效,因此如果需要从 host 用 localhost 访问 docker 内部暴露的端口,一方面要在 Dockerfile 里加入 Expose,另一方面不能使用--net=host,所以在需要 container 和 host 双向沟通的地方,还是使用局域网 ip 吧。


参考

  1. Docker Docs
  2. Docker Cheat Sheet
  3. 10张图带你深入理解Docker容器和镜像
  4. 探索本地Docker桥接网络
  5. Should docker run –net=host work
  6. Docker for mac document
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 转载自 http://blog.opskumu.com/docker.html 一、Docker 简介 Docke...
    极客圈阅读 10,468评论 0 120
  • 五、Docker 端口映射 无论如何,这些 ip 是基于本地系统的并且容器的端口非本地主机是访问不到的。此外,除了...
    R_X阅读 1,734评论 0 7
  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 15,505评论 15 147
  • 赵建铜 北洋军阀统治时期的西北部华原,各种势力为一己之私,大肆搜刮民脂民膏,强令农民种植大烟,弄得民不聊生,生灵涂...
    赵建铜阅读 428评论 0 3
  • 上一篇文章介绍了RxJava基础,这一篇介绍一下RxJava2.0中操作符和调度器的使用。 一、操作符 map()...
    MarvinGuo阅读 1,209评论 0 48