Docker 学习手册
时间:2022-06-01
本文章向大家介绍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
- 给一个 image 打上 tag:
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 的时候补加命令行参数,相当于把整个镜像当做一个命令行工具来使用。
- RUN <命令>:<命令>有两种格式,一种是直接写 shell 命令
- COPY,ADD:
- COPY ./source /target:source 要相对路径,并且必须是
./
而不能是../
或绝对路径/
,COPY 不是真的 copy 文件,而是相当于把这个文件挂载到 docker 里,让 docker 能读取这些文件。ADD 除了 COPY 本身的命令之外,还有解压缩和下载 URL 调整权限的功能,功能比较复杂,但 Docker 官方的最佳实践提示:用 COPY 尽量不用 ADD.
- COPY ./source /target:source 要相对路径,并且必须是
- ENV,ARG:
- ENV
<key> = <val>
:设置环境变量,在其他命令当中可以使用。 - ARG
<key>[=<deafult val>]
:设置环境变量名,可以在 docker run 命令中通过--build-arg
来传进去。
- ENV
- EXPOSE:
- 运行容器时,暴露出来的端口,但其实 EXPOSE 只是一个容器端口的声明,真正映射出去的,是在运行 docker 的时候
-p <宿主端口>:<容器端口>
开的。
- 运行容器时,暴露出来的端口,但其实 EXPOSE 只是一个容器端口的声明,真正映射出去的,是在运行 docker 的时候
- WORKDIR:
- 改变工作目录,因为在 Dockerfile 里面写下面这样的命令是没法在
/app
下面找到world.txt
的。 RUN cd /app RUN echo "hello" > world.txt - 因为每个 RUN 都会构造一层镜像,第一个 RUN 只发生在内存中,对文件系统不做任何修改,第二个 RUN 也就跟第一个 RUN 没有关系了。所以要用
WORKDIR
才能真的切换路径。
- 改变工作目录,因为在 Dockerfile 里面写下面这样的命令是没法在
- 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 吧。
- 赋值运算符函数__from <剑指Offer>
- 从static变量导出问题解析 __declspec(dllexport) 和 __declspec(dllimport)的作用
- php实现SESSION跨域
- 使用cJSON解析JSON字符串
- 逻辑回归 | TensorFlow深度学习笔记
- MakeSureDirectoryPathExists与CreateDirectory的区别
- 粗略的物体碰撞预测及检测
- 讨厌算法的程序员 1 | 插入排序
- FFmpeg菜鸡互啄#第6篇#音频帧格式转换(重采样)
- TF.Learn 手写文字识别
- PHP中的防御性编程
- Ray-AABB交叉检测算法
- javascript里的sleep()方法
- 编程求取直线一般式表达式,两直线交点
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如果用java swing编写一个五子棋(人人对战)
- 【mysql系列】细谈explain执行计划之“谜”
- 洛谷 P1352 没有上司的舞会(树形 DP)
- CF思维联系– CodeForces - 991C Candies(二分)
- 洛谷P1122 最大子树和 树形DP初步
- JAVA_WEB--jsp语法
- 图论--树的直径--DFS+树形DP模板
- js逐步教你实现原生电影院系统
- JAVA_WEB--jsp概述
- js逐步教你实现原生古诗匹配系统
- c++从入门到进阶--引用与常量
- 图论树的直径
- 白话k8s-Pod的组成
- Jaba_Web--JDBC 修改记录操作模板
- h5逐步实现 <<canvas系统>>