容器化, 每个容器隔离. 不是模拟一个完整的操作系统. 容器没有自己的内核
Docker 将环境打包在一起
Client
Client
Client
Client
Docker Daemon
Docker Daemon
Container
Container
Container
ContainerViewer does not support full SVG 1.1
Docker 为什么比虚拟机更快: 更少的抽象层
docker version
docker info # docker 系统信息
docker 命令 --help
docker images
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hagb/docker-easyconnect cli f3c06cd4a55c 4 weeks ago 117MB
hagb/docker-easyconnect vncless-7.6.3 e9d950d9c373 4 weeks ago 373MB
hello-world latest d1165f221234 5 months ago 13.3kB
# 可选项
-a, --all # 显示所有的镜像
-q, --quiet # 只显示 id
docker search 搜索镜像
--filter # 过滤
docker pull 下载镜像
# docker pull 镜像名[:tag] 默认下载最新版
❯ docker pull mysql:5.7
5.7: Pulling from library/mysql
e1acddbe380c: Pull complete # 分层次下载, 如果已经存在则不需要重新下载, 节省内存
bed879327370: Pull complete
03285f80bafd: Pull complete
ccc17412a00a: Pull complete
1f556ecc09d1: Pull complete
adc5528e468d: Pull complete
1afc286d5d53: Pull complete
4d2d9261e3ad: Pull complete
ac609d7b31f8: Pull complete
53ee1339bc3a: Pull complete
b0c0a831a707: Pull complete
Digest: sha256:7cf2e7d7ff876f93c8601406a5aa17484e6623875e64e7acc71432ad8e0a3d7e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7 # 真实地址
docker rmi 删除
docker rmi -f $(docker images -aq) # 删除所有镜像
docker commit 提交容器
docker commit -m="message" -a="author" CONTAINER NAME[:TAG] # 将容器保存为新的副本
以 centos 为例
docker pull centos
新建容器并启动
docker run [可选] image
# 参数说明
--name="Name" # 容器名字
-d # 后台方式运行. 如果没有前台进程, docker 会自动停止后台应用
-it # 使用交互方式运行, 进入容器查看内容
-p # 指定容器的端口
-p ip 主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-P # 随机指定端口
举例:
列出所有运行的容器
docker ps [OPTIONS]
Options:
-a: 列出所有容器(默认是正在运行的)
-n=?: 显示最近创建的容器
-q: 只显示容器的编号
退出容器
exit # 停止运行并退出容器
Ctrl+P+Q # 不停止退出容器
删除容器
docker rm 容器id
docker rm -f $(docker ps -aq)
docker ps -aq | xargs docker rm
启动和停止容器
docker start id
docker restart id
docker stop id
docker kill id
其他
docker exec -it ID /bin/bash # 进入正在运行的容器, 开启新的终端
docker attach ID # 进入正在执行的终端, 不开启新的
docker logs ID # 查看日志
docker top ID # 查看容器内进程
docker inspect ID # 查看容器元数据
docker cp ID:SRC_PATH DEST_PATH # 拷贝文件
部署 nginx
docker pull nginx
docker run -d --name nginx01 -p 3344:80 nginx # 后台运行, 80端口映射到宿主机 3344 端口
docker exec -it nginx01 /bin/bash # 进入容器内部
每次改配置文件, 都要进如容器很麻烦——使用数据卷
部署 tomcat
docker run -it --name tomcat01 -p 3355:8080 tomcat
部署 elasticsearch + kibana
docker run -it --name tomcat01 -p 3355:8080 tomcat
从宿主机的 3355 端口访问可以看到
部署 elasticsearch + kibana
docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.types=single-node" -e ES_JAVA_OPS="-Xms64m -Xmx512m" elasticsearch
Unable to find image 'elasticsearch:latest' locally # 限制最小最大内存
docker stats # 查看 CPU 和内存占用
两个服务部署在两个容器, 如何对接?
portainer (无用)
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
镜像是一种轻量级的、可以独立执行的独立软件包, 用来打包软件运行环境和运行环境开发的软件, 它包含某个软件运行需要的全部内容, 包括环境变量、代码、运行时库和配置文件
典型的 Linux 文件系统由 bootfs (boot file system) 和 rootfs(root file system) 组成.
bootfs: 包含 bootloader 和 kernel. bootloader 负责引导加载 kernel. 内核加载进内存后 bootfs 被卸载
rootfs: Linux 系统中的 /dev, /proc, /etc 等标准目录和文件
Docker 镜像建立在 AUFS (一种 UnionFS) 之上, UnionFS 支持对文件系统的修改作为一次提交来一层层叠加, 同时可以将不同目录挂载到同一个虚拟文件系统下.UnionFS 一次加载多层只读 (read only)的 rootfs, 但从外面看来只能看到一个 rootfs. 联合加载 (union mount) 的这些叠加起来的 roofts 就是 Docker 镜像. 镜像实例化后会分配一层空的 read-write 的 roofts==>容器层.
以下部分内容节选并翻译自 https://docs.docker.com/storage/volumes/
卷 (volumes)是持久化由 Docker 容器产生和使用的数据的首选机制. 绑定挂载 (bind mounts) 依赖于目录结构以及宿主机的操作系统, 而卷完全由 Docker 管理. 相比绑定挂载, 卷有以下几个优点:
绑定挂载 (bind mounts) 在命令上与卷 (volumes)很相似, -v 三个字段为: 宿主机目录, 容器内挂载目录和读写权限(可选).
如果容器生成非持久状态数据, 请考虑使用 tmpfs mount, 以避免永久地将数据存储在什么地方, 并通过避免对容器的可写层 (writable layer) 写入来提升容器性能.
一般来讲, --mount
更明确详细, 最大的区别是 -v
将所有的选项合并在一个字段, --mount
则是分开的. 下面是两种语法的比较.
如果你想具体指定卷驱动的选项, 必须使用 --mount
.
-v
或者 --volume
: 由三个字段组成, 由冒号 ( : ) 分隔.字段中的顺序必须正确, 并且每个字段的含义并不是显而易见的.ro
rw
--mount
: 由多个键值对组成, 由逗号分隔, 每个键值对由 <key>=<value>
的元组组成. --mount
语法更加详细, 但是键的顺序不重要, 这个标志的值也更容易理解.type
, 可以是 bind
, volume
或者 tmpfs
. 此处讨论卷, 所以 type 永远为 volume
.source
. 对于具名卷来说是卷名, 对于匿名卷来说该字段被省略. 可以指定为 source
或者 src
.destination
, 是容器内被挂载文件或者目录的路径. 可以被指定为 destnation
, dest
, target
readonly
, 这个选项会使卷以只读的方式挂载到容器中.volume-opt
选项, 可以被多次指定, 采用由选项名和值组成的键值对.-v
和 --mount
的不同和绑定挂载不同, 卷中的所有选项都可以用于 --mount
和 -v
标志.
当卷与服务一起使用时, 支持 --mount
.
与绑定挂载不同, 在容器外可以创建和管理卷.
创建一个卷:
docker volume create my-vol
列出所有的卷:
docker volume ls
local my-vol
查看某个卷的元数据:
docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
删除一个卷:
docker volume rm my-vol
启动容器时如果指定的卷不存在, Docker 会自动创建这个卷. 下面的例子将卷 myvol2
挂载到了容器内的 /app/
目录.
-v
和 --mount
在示例中的结果相同, 选择其中一种运行.
docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
docker run -d \
--name devtest \
-v myvol2:/app \
nginx:latest
使用 docker inspect devtest
来确认卷被正确地创建和挂载了, 看下面的 Mounts
部分:
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
这表明挂载的是一个卷, 显示了正确的源和目标, 并且挂载是可读可写的.
停止容器并移除卷. 移除卷是单独的步骤:
docker container stop devtest
docker container rm devtest
docker volume rm myvol2
对于一些开发应用程序, 容器需要写入卷, 以便将变化传回宿主机. 其他时候, 容器只需要读取数据. 记住多个容器可以挂载同一个卷, 并单独设置每个容器的读写权限.
docker run -d \
--name=nginxtest \
--mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
nginx:latest
docker run -d \
--name=nginxtest \
-v nginx-vol:/usr/share/nginx/html:ro \
nginx:latest
查看元数据 docker inspect nginxtes
的 Mounts
部分:
"Mounts": [
{
"Type": "volume",
"Name": "nginx-vol",
"Source": "/var/lib/docker/volumes/nginx-vol/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": false,
"Propagation": ""
}
],
停止容器并移除卷:
docker container stop nginxtest
docker container rm nginxtest
docker volume rm nginx-vol
通过数据卷容器来共享数据. --volumes-from
可以从其他已经挂载卷的容器挂载数据卷, 但不可对挂载位置, 读写权限进行修改. 同一个容器可以指定多个 --volumes-from
.
docker run -d -v /app --name test nginx # 创建数据卷容器
docker run -d --volumes-from test --name test01 nginx
使用 --volumes-from
. 略
Dockerfile 是用来构建 docker 镜像的构建文件, 里面写脚本. 每个命令都是镜像的一层
例子:
FROM centos
VOLUME ["/volume01","/volume02"] # 创建两个匿名的数据卷挂载到 volume01 和 volume0
CMD echo "---end---" # 打印一些信息
FROM ImageName # 指定基础镜像
MAINTAINER <name> # 维护者,已过时,应使用 LABEL
RUN <command> # 镜像构建的时候运行的命令
COPY [--chown=<user>:<group>] <src>... <dest> # 官方推荐使用,类似于 ADD
ADD # COPY 文件,自动解压 tar
WORKDIR /path/to/workdir # 制定当前的工作目录
VOLUME ["/data"] # 设置卷,挂载到容器目录,可以用 -v 修改挂载点
EXPOSE <port> [<port>/<protocol>...] # 暴露端口,随机映射 -P 会用到此处指定的端口
CMD <command> # 容器启动时运行的命令
ENTRYPOINT <command> # 容器启动时运行的命令
ONBUILD <command> # 本次不执行.当该镜像被 FROM 时执行
ENV <key> <value>
ENV <key>=<value1> <key2>=<value2> # 指定环境变量
CMD
与 ENTRYPOINT
的不同:
CMD
的具体用法:
CMD <command> # 执行 shell 命令
CMD ["<command>","<param1>","<param2>",...] # 推荐写法
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
dockerfile 中存在多个 CMD
时,只会执行最后一个.可以被 docker run
的命令行参数覆盖.
ENTRYPOINT
的具体用法:
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
与 CMD
类似,但是不会被 docker run
的命令行参数覆盖,而是会将 docker run
的命令行参数传给 ENTRYPOINT
.可以与 CMD
搭配使用.
举个例子:
FROM nginx
ENTRYPOINT ["nginx", "-c"]
CMD ["/etc/nginx/nginx.conf"]
不传参运行
docker run nginx:test
容器内执行
nginx -c /etc/nginx/nginx.conf
传参执行
docker run nginx:test -c /etc/nginx/new.conf
容器内执行
nginx -c /etc/nginx/n.conf
写好 dockerfile 后,用 docker build
构建一个叫做 my-centos
的镜像:
docker build -f Dockerfile -t my-centos:0.1 .
一个具体的 Dockerfile
FROM centos
MAINTAINER rikka@rikka.com
COPY README.md /usr/local/README.md
ADD ./jdk-8u301-linux-x64.tar.gz /usr/local/
ADD ./apache-tomcat-9.0.52.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_301
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.52
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.52
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
WORKDIR $MYPATH
EXPOSE 8080
CMD $MYPATH/apache-tomcat-9.0.52/bin/startup.sh && tail -F $MYPATH/apache-tomcat-9.0.52/logs/catalina.out
docker build -t mytomcat:0.1 # 构建镜像
docker run -d -p 8080:8080 --name tomcat -v test:/usr/local/ apache-tomcat-9.0.52/webapps/test mytomcat:0.1 # 运行容器
Docker 自动创建一个叫做 test
的 volume 挂载到容器内的 test
目录, 在 test
内放入 web 项目即可通过 ip:8080 访问.
问题:宿主机是否可以 ping 通容器内 eth0 的 ip?容器之间能 ping 通吗
答:都可以, 试一下就知道
每启动一个容器, docker 就会给容器分配一个 ip, 只要安装了 docker, 就会有一个网卡 docker0 桥接模式, 使用 veth-pair 技术.
veth-pair 是成对出现的一种虚拟网络设备接口, 一端连着网络协议栈, 一端彼此相连. 它充当一个桥梁, 连接各种虚拟设备
我知道这里说得很简略, 但现在不是深入了解 docker 网络的时候, 仅仅是做个了解
过时的: 在 docker run
的时候指定 --link
, 即可将指定容器的容器名及其 ip 写入新容器的 hosts, 这样 ping 容器名
就可以 ping 通.
现在应该使用 自定义网络
查看所有的 docker 网络
dockr network ls
网络模式 | |
---|---|
bridge | 桥接模式(默认) |
none | 不配置网络 |
host | 和主机共享网络 |
container | 容器内网络联通(不常用) |
创建一个网络, 桥接模式, 用 16 位表示主机号, 默认网关 192.168.0.1, 名称 mynet
docker network create --driver bridge --subnet 192.168.0.0/24 --gateway 192.168.0.1 mynet
inpect
docker network inspect mynet
将服务放在自己的网络中
docker run -d -P --name tomcat-mynet01 --net mynet tomcat
好处:
网络连通
刚才创建了一个 mynet 网络, 使用 mynet 的容器是 ping 不通使用 docker0 的容器的. 所以需要用到 connet.
如, 将 docker0 上的容器 tomcat01连接到 mynet:
docker network connect mynet tomcat01
此后, 容器 tomcat01 同时有两个 ip.