From Office Document
Glance
docker 是一种容器引擎, 基于 Linux 内核的一些功能, 通过构建一个容器, 来给 app 提供一个运行的环境.
因其用到 Linux 的特性, 所以原生支持只能是 Linux.
Mac 和 Windows 要用到的时候, 需要起一个虚拟机相关的东西. 之前, 它用的是 VB, 但现在, 对于这两个平台封装了一个更好用的 app, 会自动起一个更清凉的虚拟引擎来构建docker 需要的环境
通过 docker 提供的容器, 可以和主机的环境分离开来, 而且可以 portable, 只需要将你的环境 制作成 image, 然后拿到另一个地方通过 docker 使用这个 image 构建一个容器, 启动就好了. 自己要保证自己的 app 的依赖环境也一起打包带走, 这个 容器 的制作由一个Dockerfile
的文件确定
Install
Linux
可以使用自带的一些包管理器安装即可
Mac
brew cask install docker
Windows
choco install docer
或者你可以自己下载安装包安装
还有一些在 Windows 上安装的注意事项, 比如开打虚拟化等等.
Dockerfile
使用这个文件来定义你做的这个容器会有什么依赖, 在启动时的一些设置, 就像设定一个机器的硬件/软件/配置等等.
创建一个文件夹 -> 创建一个 DockerFile 文件, 将下面的内容粘贴进去
1 | # Use an official Python runtime as a parent image |
如果你挂了代理, 有可能代理会 block 链接, 这时你还要在文件中加上以下内容, 声明代理的端口, 注意在pip
命令前加上这句话, 才能保证命令跑通
1 |
|
以上算是声明了 image/container 需要的环境, 接下来是需要在这个环境中准备你的 app 了
创建 requirements.txt
和 app.py
, 将他们和 DockerFile 放到同一个目录下, 这样就简单的完成了这个 docker 的项目搭建.
当 DockerFile
被写进 image
时, 这两个文件会被通过ADD
加载进来, 而 app.py
中可以通过http访问则是通过 EXPOSE命令
requirements.txt
1 | Flask |
app.py
1 | from flask import Flask |
From Jike.com’s Video
Docker 简介
一种虚拟化方案, 操作系统级别, 只能运行相同内核的操作系统, 依赖于Linux 内核的特性: NameSpace & Cgoruup(Control Group)
虚拟机的技术是在 hostOS 上运行起一个 GuestOS, 在运行相应的系统服务.
而容器则是直接在 HostOS 上通过 Docker 引擎, 运行对应的虚拟化服务. 不用再起一个 GuestOS, 而是基于当前的 OS 来通过NameSpace 这些特性, 直接使用系统的服务
Docker 的目标
- 提供简单轻量的建模方式
- 职责的逻辑分离
- 快速高效的生命周期
- 鼓励使用面向服务的架构
使用场景
- 使用 Docker 容器开发/测试/部署 服务
- 创建隔离的运行环境
- 搭建测试环境
- 构建多用户的平台即服务(PaaS)基础设施
- 提供软件即服务(SaaS)应用程序
- 高性能, 超大规模宿主机部署
基本组成
- Docker 客户端
- Docker Daemnon 守护进程
- Docker Image 镜像
- Docker Container 容器
- Docker Registy 仓库
image
层叠文件系统, 只读
联合加载
Container
在加载完基础的应用和文件系统后, 再在顶层加载一个读写层, 我们都是在这个层执行我们运行的程序
我们在修改一个文件时, 先由底层的复制到读写层, 该文件的只读版本依然存在, 只是被读写层的副本所隐藏
Registy
公有: Docker 公司提供
私有: 可以构建自己的私有苍鹭
Hub: 可以在这里分享/保存/自己的镜像, 也能查找别人做好的镜像
Docker 的基本组成
安装
参考官方安装文档
容器的基本操作
- 启动容器
1 | docker run IMAGE [COMMAND] [ARG...] |
在新容器中执行命令:
run: 构建一个容器: [通常我们在终端中执行的命令]
[COMMAND]: 在容器启动时执行的命令
直接这样就是一次性的运行就结束了
Docker 还提供了交互式的操作方法, 就像 SSH 连接一个 Linux 操作一样
docker run -i -t IMAGE /bin/bash
-i: –interactive=true 默认是 FALSE
-t: –tty=true | false
- 查看容器
docker ps [-a(全部)] [-l[最新一个]]
, 不加参数就是显示当前正在运行的容器
这个只是查看有哪些容器以及容器引擎上的线程使用, 即运行了哪些容器
查看我们建立起来的容器, 查看某个容器的详细信息, 使用 docker inspect [id]/[name]
后面可以是唯一 id, 也可以是 NAME
在运行run
启动一个容器时, 可以使用--name=xxx
参数来给这个容器自定义 name
- 重新启动已经停止的容器
docker start
使用这个可以使挂起的容器重新运行起来
但是不加命令的话, 还是会马上的结束.
只是重新 start 了一个容器, 需要进行交互式的话还是需要 -i
标签
- 删除容器
docker rm
这个可以删除已经停止的容器, 不能删除正在运行的容器
守护式容器
常用命令:
1 |
|
以上这些容器在结束运行后就会停止, 而我们需要长时间运行的容器, 就是这个守护式容器
特点:
- 能够长期运行
- 没有交互式会话
- 适合运行应用程序和服务
当容器run
之后, 通过Ctrl+P+Q
来退出交互式容器的 bash, 这样容器就会在后台运行.
回到守护式容器:docker attach [id/name]
就能回到容器
通过run
命令启动守护式容器
使用参数: -ddocker run -d IMAGE COMMAND
完了会自动分配给这个容器一个 id, 即 hash 值
查看容器运行 log
之后要查看这个容器的运行情况, 可以使用 logs
命令查看:docker logs [-f] [-t] [--tail] 容器名
-f
–follows=true | false-t
–timestamps=true | false--tail
= ‘all’
+ 查看容器内进程
docker top NAME/HASH
在运行的容器中启动新的进程
虽然 docker 一般一个容器只运行一个服务/进程, 但是有时候维护的时候需要启动一些如监控/监控/管理的时候, 就需要新起一些进程和服务
exec
docker exec [-d][-i][-t] NAME/HASH [COMMAND][ARG...]
停止守护式容器
docker stop
: 只发送一个信号给 container, 等待容器的停止, 容器停止后会返回容器的名字
docker kill
: 直接停止容器
在容器中部署静态网站
映射端口
当外部的网络需要访问 container 的 web 服务时, 需要知道其端口, 机器外部的网络访问本机的端口, 本机需要通过端口的映射来讲访问传给 container.
run [-P]/[-p]
-P
: –publish=all, 会将容器暴露的所有端口进行映射docker run -P -it ubuntu /bin/bash
-p
: –publish=[], 能够指定映射哪些容器的端口
containerPort
: 只指定容器的端口, 宿主机的端口是随机映射的docker run -p 80 -it ubuntu /bin/bash
hostPort:containerPort
: 同时指定了宿主机的端口和容器端口, 成为 一一对应docker run -p 8080:80 -it ubuntu /bin/bash
ip::containerPort
: 指定 ip 和容器的端口docker run -p 0.0.0.0:80 -it ubuntu /bin/bash
ip:hostPort:containerPort
: ip, 宿主机端口, 容器端口同时指定docker run -p 0.0.0.0:8080:80 -it ubuntu /bin/bash
Nginx 部署流程
- 创建映射80端口的 container
docker run --name web -it -p 80 ubuntu /bin/bash
- Install Nginx
- Install Vim
- 安装 Nginx & Vim,
apt-get install nginx vim
, 没有的话需要apt-get update
更新下
- 安装 Nginx & Vim,
- create static page
mkdir -p /var/www/html
->vim index.html
- config Nginx
- 找到 nginx 下 site-enabled 中的
default
文件, 将root
所指修改为刚刚静态网站的位置
- 找到 nginx 下 site-enabled 中的
- run Nginx
nginx
, 可以在容器中使用ps -ef
来查看进程- 在外部可以用
docker ps
来查看 container 状况, 可以看到映射的端口
- verify website
- 可以访问查到的 host 的IP, 也可以直接通过
docker inspect
查看分配给 container 的 IP 地址访问
- 可以访问查到的 host 的IP, 也可以直接通过
注意: 这种 hostPort 随机的情况下, 重新启动 container 会重新分配 IP 和端口
查看和删除镜像
可以使用docker info
来查看 docker 使用的存储驱动和存储的位置
docker的镜像一般存储在/var/lib/docker
目录下(Linux).
列出镜像
docker images
镜像标签和仓库
镜像中有 Tag, 可以标记一个镜像, 同一个镜像可以有多个 tag.仓库名:tag
构成了这个镜像完整的名字.
平时只使用仓库名如ubuntu
会默认是哟给你 tag:latest 的镜像
查看镜像
dokcer inspect
这个命令即可以查看容器, 也可以查看镜像
删除镜像
dokcer rmi
要删除所有镜像时, 可以使用 shell 脚本嵌套进去, 返回所有的 image ID 来删除:
docker rmi $(docker images -a)
获取和推送镜像
查找
docker hub 官方维护镜像仓库来查找
docker search [opton] TERM
拉取
docker pull [option] NAME[:TAG]
换源
因为国内的网络, 下载以及浏览会比较慢, 可以通过修改 docker 配置文件(/etc/default/docker
)来指定源,
使用--registry-mirror=xxxxx
可以指定下载的源, 这个参数是在运行时的一个选项, 要使用的话就需要修改 docker 的配置文件, 将仓库镜像的选项添加到配置文件中
镜像推荐:
- daoclound.io
- https://lug.ustc.edu.cn/wiki/mirrors/help/docker
需要注册 -> 加速器 -> 生成链接, 用来配置仓库镜像地址
在 docker 文件末加入: DOCKER_OPTS='registry-mirror=xxxxxxx'
;
也可以使用一条命令:curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://你的的地址.m.daocloud.io
Mac 的话直接在 GUI 设置就行了
推送
docker push Repository/ID
需要登录
知会增量上传, 即只上传修改的部分
构建镜像
即将容器配置到合适的情况时, 添加了满足的服务, 这时可以将这个 container 构建成一个镜像, 以重复使用和分发
- 保存对容器的修改, 并再次使用
docker commit NAME/ID ImageName
通过容器构建,docker build
通过 Dockerfile 文件配置- 创建
Dockerfile
->
- 创建
- 自定义镜像的能力
- 以软件的形式打包并分发服务及其运行环境
Docker 客户端和守护进程: Docker 的 C/S 模式
Client
cli
最常见最常用的与守护进程(docker server)通信的方式是通过shell(Docker Client), 我们市在这个 Client 中操作, 发送指令给 Server, 由这个守护进程来执行各种操作, 最后把操作结果返回
Remote Api
- Restful api
使用这API 的话意味着可以通过自己编写程序来调用这些 API 来与守护进程通信, 这样就能讲自己的程序与 Docker 进行集成
- 支持
STDIN
,STDOUT
,STDERR
链接方式
socket 链接
有三种 socket 链接方式
- unix:///var/run/docker.sock (默认)
- tcp://host:port
- fd://socketfd
当链接上 socket 后, 就能用 http 指令来查看相关接口
Docker 守护进程的配置和操作
用到Linux 的 service 命令
sudo service docker start
sudo service docker stop
sudo service docker restart
当修改了配置之后, 需要重新启动 Docker 服务来使配置生效
docker 服务的启动有狠毒可以配置的选项, 在配置文件中配置这些, 重启服务就能生效
Docker 远程访问
Note: Client Api 版本要与 ServerApi 版本保持一致
操作练习
- 另起一个 Docker 服务器(大概可以另起一个 Docker Container 就能 满足)
- 设置不同 lable 以示区分
- 修改 Docker 守护进程的启动选项 ( -H 配置服务使用的 socket)
- 修改 docker 文件, 添加socket
- 重启 docker Server
客户端访问远程服务器
- 修改客户端配置: 客户端
docker -H xxxx/info
可以访问配置的远程服务器, 使用的socket 链接就是远程配置的 - 使用环境变量:
export DOCKER_HOST="tcp://xxxx"
, 能够像本机链接一样使用远程 docker 服务 - 然后直接像使用本地 Client 一样
docker info
就能访问到刚刚配置的 HOST 的机器 - 不需要链接远程时, 只需要
export DOCKER_HOST=""
就能恢复
在设置了远程访问的服务器上默认是不能直接使用像本地链接那样使用 docker 命令的, 我们可以配置环境变量将 HOST 指定到本地来解决, 还可以这样:
在 docker 启动文件里
-H
再指定一个 socket, 就能在本地继续访问 docker 服务了
Dockerfile 构建镜像
1 | # comment |
指令
- FROM:
- FROM 镜像
- 已经存在的镜像
- 后续指令都会基于这个镜像, 又叫做基础镜像
- 必须是第一条非注释指令
- MAINTAINER
- 指定作者信息, 包含镜像所有者的联系信息
MAINTAINER 作者名 联系方式
- RUN:
- 指定当前镜像中运行的指令
RUN <command>
Shell 模式- 这个模式以
/bin/sh -c command
来执行指令
- 这个模式以
RUN ["executable", "param1", "param2"]
exec 模式RUN ["/bin/bash", "-c", "echo hellp"]
可以指定其他形式的 shell 来运行指令
- 每一个
RUN
指令, 都会在当前镜像的上层, 创建一个新的镜像, 来运行指定的命令
- EXPOSE
EXPOSE <port>[<port>]
指定运行该镜像的容器使用的端口, 可以指定多个, 也可以使用多个EXPOSE
命令- 这个只是告诉容器内的应用会使用特定的端口, 但是 docker 不会默认打开这个端口, 还需要手动运行
docker run -p 80 -d xxxxx
来添加对端口的映射指令
- CMD
- 不同于
RUN
,RUN
是在镜像构建过程中执行的,CMD
是在Container 运行时运行的 - 和
RUN
的运行模式相似, 但是多了一种运行模式 CMD ["param1", "param2"]
作为ENTRYPOINT
命令的指定参数- 使用
docker run
启动容器时带的参数会覆盖Dockerfile
中CMD
所指定的命令
- 不同于
- ENTRYPOINT
- 用法同上
- 不同: 不会被
docker run
指定的命令所覆盖 - 需要覆盖时, 就需要在
docker run
中指定 –entryoint 选项
- ADD
ADD ["<src>"..."<dest>"]
src
使用构建目录中的相对地址, 或者 curl 从远程获取地址dest
需要指定镜像中的绝对路径- 包含类似
tar
的解压功能
- COPY
COPY ["<src>"..."<dest>"]
- 同上, 只是不会解压, 单纯复制文件, 推荐使用
COPY
- VOLUME
- 用来向基于镜像创建的容器添加卷, 一个卷可以存在一个或多个容器的特定目录
- 提供共享数据和数据持久化的功能, 就可 Linux 的挂载一个卷类似
- WORKDIR
/path/to/workdir
, 从镜像创建一个新容器时, 在容器内部设置一个工作目录,ENTRYPOINT
和CMD
都会在这个目录下执行- 一般使用绝对路径, 使用相对路径的话会基于上一个
WORKDIR
的设置一直传递下去
- ENV
- 设置环境变量
- USER
- 指定用户会以什么身份来运行
- ONBUILD
ONBUILD [INSTRUCTION]
用来为镜像添加触发器- 当一个镜像用作其他镜像的基础镜像时执行
- 当子镜像在构建时, 会插入触发器中的指令
+
Dockerfile 的构建过程
构建一个D DockerFile
里面声明的命令的执行过程
- 从基础镜像运行一个容器
- 执行一条指令, 对容器做出修改
- 执行类似
docker commit
的操纵, 提交一个新的镜像层 - 再基于刚提交的镜像运行一个新容器
- 执行
DockerFile
中的下一条指令,直到所有指令执行完毕
其中, 每个镜像层执行完指令后会删除这个中间层状态的 container, 但是不会删除这个中间层状态的镜像, 也就是说我们可以通过docker run
创建一个中间层镜像的容器, 查看每一步构建镜像的状态, 这样就能对这个构建过程进行调试
好处:排错
构建缓存
由于 docker执行每一条指令都会构建一个镜像, 这样就能把这些镜像看做是一个缓存
在运行同样的 Dockerfile 的情况下, Docker 会使用之前构建的缓存, 这样就能加快构建速度
但是有时候不想使用缓存时:docker build --no-cache
查看构建镜像的过程
docker history [image]
, 就能对这个镜像的构建过程的查看
类似就是查看构建 log
Dicker Container 网络连接
- Docker Container 网络基础
- Container 互联
- Container 与外部网络连接
使用ifconfig
能查看当前设备的网络设备情况, 可以看到有个docker0
的网络设备
这个就是守护进程为 Container 提供网络连接的网络服务
这个实际就是Linux 的虚拟网桥
网桥是在数据链路层上的设备, 通过 MAC 地址来区分网络
Linux虚拟网桥的特点:
- 可以设置 IP 地址, 其把它当做一个可以有 IP 的网络设备, 可以通过 IP 在网络层查找网桥
- 相当一个虚拟网卡
在 Linux 中需要用工具来查看网桥设备bridge-utils
brctl show
查看网桥设备
使用 Linux 的ifconfig
来修改docker0
使用的网段
使用自己设定的虚拟网桥, 不实用 docker 默认的
在本机上先添加一个虚拟网桥
sudo brctl addbr br0
sudo ifconfig bro 192.168.6.1 netmask 255.255.255.0
这时候在运行ifconfig
就能看到新创建的网桥更改
docker
守护进程的启动配置/etc/default/docker
中添加DOCKER_OPS值:b=br0
就能使用这个新建的网桥
容器的互联
- Docker 默认是允许所有容器相互链接的 (
--icc=true
)- 默认同一个宿主机内的容器都是可以互相链接的, 但是直接用 IP 来链接的话不可靠, 重启后 IP 就变了, 所以使用
--link
参数指定容器名字和 alias, 就能避免这种情况 - 这个参数的意义在于, 在启动这个容器的时候, 会在环境变量中设置对于 link 的容器的 IP 地址, 兵器阿紫 host 文件中会自动对这个别名做地址映射
- 默认同一个宿主机内的容器都是可以互相链接的, 但是直接用 IP 来链接的话不可靠, 重启后 IP 就变了, 所以使用
- 拒绝容器间的链接 (
--icc=false
)- 在 docker 配置文件添加这个参数即可
- 允许特定容器间的链接
--icc=false --iptables=true
- 在启动容器时使用
--link
选项 - 利用
iptables
中的机制, 在--icc=false
时阻断所有容器间的访问, 仅仅允许--link
配置的容器进行相互访问
容器与外部网络的链接
ip_forward
- 为 Linux 中的变量, 其值决定系统是否会转发流量,
- 在 docker 守护进程的参数中, 也有这个选项, 默认值=TRUE
- 当使用这个默认值时, docker 会在守护进程启动时, 将系统的
ip_forward
设置为1
, 允许流量转发 - 查看系统
IP_forward
:sudo sysctl net.ipv4.conf.all.forwarding
iptables
- 与 Linux 集成的包过滤防火墙系统
- 查看
iptables
:sudo iptables -t filter -L -n
: 不含有-t
默认就是filter
表
- 允许端口映射访问
- 使用
iptables
配置哪个IP:端口
可以访问(ACCEPT)哪个IP:端口
, 或者阻止(DROP)
- 使用
- 限制 IP 容器访问
- 设置:
sudo iptables -I DOCKER -s 10.xxx.xxx.xxx -d 10.xxx.x.xx -p TCP --dport 80 -j DROP
- 设置:
Docker Container 数据管理
数据卷
用来数据持久化, 独立与 container 存在, 在多个 container 中共享数据
特点
- container 启动时初始化, 如果 container 使用的 image 在挂载点中包含来数据, 也会在启动的时候将数据拷贝到 dataValume 中.
- dataValume 可以在 container之间共享和重用
- 可以直接对 dataValume 中的数据进行修改
- dataValume 的变化不会影响 image 的更新
- dataValume 独立存在, 即使挂载 valume 的 container 已经被删除
操作
- 添加数据卷
sudo docker run -v [host/path/to/valume]:[container/path/to/valume]:[访问权限] -it ubuntu /bin/bash
是哟个 Dockerfile 创建包含 dataValume 的景象
指令: VALUME["/data", "/data1"]
可以指定多个 valume, 但是, 在 Dockerfile 中指定的 dataValume 在 container 创建时, 不能和 host 的文件系统的某个文件映射, 里面只声明了 container 里挂载 dataValume 的位置,
在 host 端, docker 会自动生成一个文件夹来挂载
所以, 这个 dockerfile 的方法使得 container 不能互相共享dataValume
这时候需要 valume container来实现
数据卷容器 DataContainer
命名的容器挂载数据卷, 其他容器通过挂载这个容器实现数据共享, 挂载数据的容器就叫做数据卷容器
操作:
使用: docker run --volumes-from [container name]
来启动一个 container 并指定一个 container 作为 volume container
而[container name]
就是已经挂载了dataValume
的 container name
在成功挂载了 volumeContainer 后, container 就能访问到这个 volumeContainer 挂载的 Volume 信息, 并且通过docker inspect
查看容器时, 可以看到挂载的 Volume 信息, 且当删除了 VolumeContainer 之后, 使用这个 Container 挂载的 Container 还是包邮 Volume 的配置, 即 VolumeContainer 只是当做一个配置信息的桥梁
当删除 VolumeContainer 时加上来
-v
参数的话, 会删除其挂载的 Volume, 但是, 这个 Volume 如果还有其他的 Container 在使用的话, 是不会被删除的
Volume 的备份和还原
备份
docker run --volumes-from [container name] -v [${pwd}保存的目录]:[/backup 指定备份的 container 中的目录] ubantu tar cvf [/backup/backup.tar 容器中生成压缩的文件] [container data volume 压缩来源]
Docker Container 跨主机链接
- 使用网桥实现
- 通过将 host 的网桥也与 docker 的虚拟网桥挂载, 在对外设置同一 IP段, 通过这个网桥走, 就能访问物理的 gateway
- 优点: 配置简单, 不需要第三方软件
- 缺点:
- 与主机在同一个IP段, 需要小心划分IP 地址
- 需要有网段控制权, 生产中不易实现
- host 和 container 在同一地址段, 不易管理
- 兼容性不佳
- 使用 Open vSwitch (虚拟交换机软件)
- 操作
- 建立ovs 网桥:
sudo ovs-vsctl add-br obr0
- 添加 gre 链接:
sudo ovs-vsctl add-port obr0 gre0
->sudo ovs-vsctl set interface gre0 type=gre options:remote_ip=192.xxx.xxx.xx
- 配置 docker 容器虚拟网桥:
sudo brctl addbr br0
->sudo ifconfig br0 192.168.1.1 netmask 255.255.255.0
- 为虚拟网桥添加 ovs 接口: ->
sudo brctl addif br0 obr0
->sudo brctl show
- 添加不同容器网段路由表:
sudo ip route add 192.168.2.4/24 via 192.168.59/104 dev eth0
- 建立ovs 网桥:
- 使用 weave
- 下载 weave, 修改下载文件夹的权限使其可运行,
weave launch
: 实际上这个操作会下载一个 weave docker 镜像, 在启动 weave 时, 会在 docker 中启动一个 weave 的容器- 在另一台机器上:
weave launch [docker host's ip]
, 传入 IP 使两个 host 能链接在一起 - 在 host2 上用 weave 创建一个容器
asd=$(weave run 192.168.1.2 -it ubuntu /bin/bash)
, 填入的 IP 即在启动的 container 中分配的IP, 由 weave 管理, 然后进入这个容器可以查看其网络配置- 在 host1 中也启动一个容器, 指定一个和在 host2中指定 IP 相同网段的 IP
- 这样就能互联来