docker常用知识

ooowl
  • 容器技术
  • docker
  • docker
  • 容器技术
About 16 min

docker常用知识

先放一波资料,我目标是能日常使用,排查一些常见错误,自己打包dockerfile编排compose,大致知道imspect各个项是什么含义,管理容器镜像网络仓库转移。

前置工作

不同的系统架构,dockerimages打包出来的镜像是不一样的,所以打包之前要注意。可以跨平台dan但我自己平时用不需要所以直接跳过这个
安装dockeropen in new window安装docker-composeopen in new window
安装完成之后运行一个demo的容器测试行不行

sudo docker run hello-world

镜像不包含动态数据,镜像以基础镜像构建时,不会改变先前的底层基础镜像,即使是删除操作也是标记,所以体积不会减小。容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用。仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。

⚡️Danger

** docker的网络是越过防火墙的**
docker的网络是越过防火墙的,使用firewall或者iptables是无法限制docker打开的端口的。

列一下自己常用的命令,已经嵌入潜意识那种

docker -v # 查看版本
docker run # 基础的运行容器
docker stop f0b935ae9277
docker rm f0b935ae9277
docker ps 
docker images 
docker port demo # 查看demo的所有端口映射
docker rmi f0b935ae9277
docker exec -it f0b935ae9277 /bin/bash # 进入某个容器,ctrl+p+q or ctrl+d 退出而不关闭
docker inspect f0b935ae9277
docker logs f0b935ae9277
docker-compose stop 
docker-compose up -d
docker-compose down # down会把容器停止并删除

几个命令常用参数:

docker run -d -p 8085:8000 -v ./dicom_sets:/app/dicom_sets --name dicom_viewer  dicom_viewer
# -d 表示运行完不退出容器
# -p 外部端口:内部端口
# -v 外部目录:内部目录
# --name 容器名字

docker ps # 默认情况下只显示正在运行的容器
docker ps -a # 显示所有的容器
docker ps -aq # 显示所有的容器的id

docker stop `docker ps -aq` # 使用id停止所有的容器,同理也可以start

docker ps --filter "name=web" 
# 显示名称包含 "web" 的容器
docker ps --filter "status=exited"
# 显示状态为 "exited" 的容器 

docker是引擎和client结构,你的client可以连接任意的引擎。

镜像

docker pull ubuntu:18.04 默认的Registry是library也就是docker官方的镜像库docker.ioopen in new window,完整的URI名字应该是docker.io/library/ubuntu:18.04

镜像拉取的时候并不是单文件而是多层的文件系统构成的,所以多个镜像可以共享相同的底层文件,中间的依赖回收也是引用计数,docker image获取的是顶层可以使用的镜像,中间的依赖镜像和文件并不会显示。删除行为分为两类,一类是 Untagged,另一类是 Deleted,镜像是多层依赖构成的,先把所有的tag都取消掉,然后引用计数回收。因为有时候是多images同时引用一个,所以有可能不会全删除

docker system df # 查看镜像、容器、数据卷所占用的空间
docker images -f dangling=true # 查看所有name&tags是none的镜像
docker image prune # 清除所有none的镜像
docker diff 容器 # 对照容器做了哪些更改
docker commit --author "nobody" --message "修改了什么" container_name image_name:tag
# 修改了哪些东西,搞成一个新镜像
docker tag 5e50574fb696 ooowl/nginx:1.0 # 有些镜像导入之后没名字,记得手动修改

📌Tip

docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit 定制镜像,应使用 Dockerfile 来完成。 由于各种对镜像的命令执行,还有很多文件被改动或添加了。如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,将会导致镜像极为臃肿。

离线导出导入

开导!
命令很固定,可以一次性保存多个

docker save redis:7 mongo:6 | 7za a -si images.tar.7z  # 导出镜像
7za x -so images.tar.7z   | docker load # 导入镜像
# 不使用7z
docker save -o nginx.tar nginx:latest
docker load -i nginx.tar
# 直接一条命令转移镜像
docker save <镜像名> | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load'

注意:如果导出的文件同名则会覆盖(没有警告)

docker save和docker export的区别,一般都是导入导出镜像,然后再做部署工作

  • 对于Docker Save方法,会保存该镜像的所有历史记录
  • 对于Docker Export 方法,不会保留历史记录,即没有commit历史
  • docker save保存的是镜像(image),docker export保存的是容器(container);
  • docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像;
  • docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称。

dockerfile

这是菜鸟列出来的所有命令open in new window
dockerfile构建的一些命令open in new window 当手册可以查

Dockerfile 中每一个指令都会建立一层,每一个 RUN 的行为就会新建立一层,在其上执行这些命令结束后commit 这一层的修改,构成新的镜像。所以为了减少冗余,最好是RUN个大的而不是多次RUN(指令也是)。

📌Tip

Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层,也就是说dockerfile过程产生的数量就不能超过此层数。
而且每层构建都会永久的跟着镜像,所以打包编译之类的工作最后应该clean并删除不需要的文件保持干净。

Dockerfile 文件所在目录执行docker build -t images:tag . 注意最后的目录,此目录是表明构建时候的工作目录,dockerfile中COPY命令等涉及文件路径操作的,都是基于此目录操作。因为有可能是连接的其他的engine,会把client进行build的时候的目录全都传送过去,所以不要随便找个地方build,最好整个空的干净目录,把需要的都放在这个目录里面。一般来说大部分情况都是本地的build,所以找个新目录用 . 就可以了。
默认读取指定的工作目录名为的Dockerfile的文件作为构建文件,可以使用-f ../Dockerfile.php 指定dockerfile。
给出一个dockerfile的示例

FROM library/nginx:latest  

LABEL maintainer="none <none@gmail.com>"  

# stdout 无缓冲,直接输出  
ENV PYTHONUNBUFFERED 1  

WORKDIR /app  

COPY app/static/comparison_worker.py /app  

ENV PYTHONPATH=/app  

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

一般来说,步骤大致包括指定基础镜像和维护人, 配置环境变量,复制需要的文件进去,指定工作目录,使用compose最后运行启动命令

远程仓库

可以使用docker search centos进行搜索镜像,查找的时候通过 --filter=stars=N 参数可以指定仅显示收藏数量为 N 以上的镜像
docker logindocker logout登入登出,如果要推送镜像的话。
docker tag 5e50574fb696 ooowl/textmodel:1.0 注意先修改你的用户名才能推送到你的账户底下。
docker push ooowl/textmodel:1.0 推送到了ooowl的用户名下

如果你使用了私有的hub 那就把路径前加上hub的地址就可以了(假设hub的地址是5000)

  • 先用tag修改名字tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
  • push上去docker push 127.0.0.1:5000/ubuntu:latest

容器

常用的参数 -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。 docker run -it nginx:1.0 /bin/echo 'Hello world' 也可以直接运行bash来操作,不过你detect的时候容器就结束了,可以用-d来后台运行 当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从registry下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

docker attach 和docker exec是一样的功能,但是退出终端后容器就会退出

数据卷

一般映射

通过 -v 外部目录:内部目录 来映射目录,映射的时候要注意权限问题,比如在pg数据库映射data的时候自动生成的文件权限就是1001:1001这个权限会被带到里面,不对的话数据库启动不了。
映射的时候目录最好使用绝对路径
如果使用路径了,比如带点或者带杠的,那就会尝试使用目录,挂不上就是报错。
如果只是单纯打个名字,那就会自动创建一个卷。
映射的时候如果外部或者内部没有这个目录那就会被创建,前提是目录为绝对目录
-v详细的写法可以写成如下,可以配置是否为只读
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly

拷贝文件(夹)进去或者出来,文件夹也行
docker cp source destnation,其中源和目的地,本机上的都是绝对路径,镜像内的路径表达是container:/root/abspath

使用数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UnionFS,可提供很多有用的特性:

  • 可以在容器之间共享和重用
  • 修改会立马生效
  • 更新,不会影响镜像
  • 默认会一直存在,即使容器被删除
docker volume create my-vol # 创建一个新的卷
docker volume ls # 查看所有的卷
docker volume inspect v_name # 查看卷详细信息
docker volume prune # 批量清除没在用的
docker volume rm # 删除

数据卷相当于一个docker管理起来的文件夹,可以让多个docker共用,如果使用目录就不能共享
数据卷的迁移,比较麻烦。 Docker并未提供官方的简单方案,所以用到了再去查

网络

基本使用

容器要想访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开。

$sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

如果为 0,说明没有开启转发,则需要手动打开。

$sysctl -w net.ipv4.ip_forward=1

如果在启动 Docker 服务的时候设定 --ip-forward=true, Docker 就会自动设定系统的 ip_forward 参数为 1。

docker network create network_name  # 一般默认的就是bridge
docker network ls
docker network connect(disconnect) 网络 容器 # 将运行中的容器加入某一个网络
docker network inspect network_name # 探查某个网络

#创建网络的时候指定网络的参数
docker network create \
  --driver bridge \              # 指定网络驱动程序为 bridge
  --subnet 192.168.1.0/24 \     # 指定子网
  --gateway 192.168.1.1 \       # 指定网关
  --ip-range 192.168.1.128/25 \ # 指定 IP 范围
  --dns 8.8.8.8 \               # 指定 DNS 服务器
  --dns 8.8.4.4 \               # 可以指定多个 DNS 服务器
  my-custom-network             # 指定网络名称


# run的时候指定静态ip地址和容器名,如果加入了网络可以使用容器名通信,如果是默认那可以使用ip地址通信
docker run --network=my-custom-network\ # 这里空格等号都行
           --ip 192.168.1.10 \
           --name=container1 \
           -d my-image

如果在同一个Docker Compose 文件或 Docker Swarm 集群中Docker 会自动设置 DNS 记录镜像可以使用容器名进行通信。一般都是划分到共同的网络里容器名通信就完事了。

原理与配置

以后遇到网络问题可以慢慢积累高级的网络知识
网络映射使用-p的话除了指定之外可以把外部随机一个端口映射。
创建network之后,让容器共同加入一个网络才能互相访问,否则只能过一遍宿主机网络访问。
docker network create -d bridge my-net-d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于swarm,所以直接不看 网络的基本结构

docker在安装完之后会生成一个docker0网络,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络,使用ifconfig查看网络可得网络其中有一个,172网段内部网络

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:b7ff:fe9a:598c  prefixlen 64  scopeid 0x20<link>
        ether 02:42:b7:9a:59:8c  txqueuelen 0  (Ethernet)
        RX packets 3927  bytes 920130 (920.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5476  bytes 29518426 (29.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker在自己的内部会创建三个网络分别是bridge,host,none,这三个网络是不能删除的,创建容器的时候网络类型都是依据这三个来创建的,默认的网络就是bridge

Docker 创建一个容器的时候,会执行如下操作:

  1. 创建一对虚拟接口,分别放到本地主机和新容器中;
  2. 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 veth65f9;
  3. 容器端放到新容器中,并修改名字作为 eth0,这个接口只在容器的命名空间可见;
  4. 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth65f9。
  5. 桥接不能使用name解析bridge中的ip地址

可以在 docker run 的时候通过 --network 参数来指定容器的网络配置,有4个可选值:

  • --network bridge 这个是默认值,不配置就是他,连接到默认的网桥,实际上是NAT会消耗一点CPU。
  • --network my-net 这需要你提前创建一个bridge网络,自建的brigge可以使用容器名通信,而且和其他的网络栈隔离
  • --network host 容器直接接入host的网络,拥有完全的host接口访问权限,甚至可以重启。如果进一步的使用 --privileged=true,容器会被允许直接配置主机的网络堆栈。
  • --network none 让 Docker 不设置任何网络,之后自己配置不然上不了网,用的少除非容器只本地用
  • --network container:NAME_or_ID 俩容器用一个网卡,此时他俩不过是一个ip下的连体婴儿罢了。

不管哪种方式,端口访问控制其实也是在本地的 iptable 的 nat 表中添加相应的规则

docker-compose

docker stack是docker原生支持的编排工具,被定义为适用于生产环境的编排工具,强化了( 复制集、 容器重启策略、回滚策略、服务更新策略 )等生产特性(虽然我都用不着
在portainer中可以看到有哪些stack,一般来说一个compose文件就会创建一个stack,并且创建一个bridge网络,并把编排的容器放进去。

常用的命令,一般的命令就不提了,天天用。:

docker-compose up -d
docker-compose down # 注意和stop的区别
docker-compose stop/start
docker-compose build # 重新构建服务
docker-compose config # 检测是不是有语法错误
docker-compose images # 查看stack需要的镜像
docker-compose logs | less +G # 从底下开始查看日志,不同的容器会给你用不同颜色混杂显示

一个compose内的容器的名称默认是这样规定的:

  1. 项目名称:默认情况下,这是你的 docker-compose.yml 文件所在文件夹的名称。
  2. 服务名称:这是在 docker-compose.yml 文件中定义的服务名称。
  3. 实例编号:这是一个数字,表示这是该服务的第几个实例。
  4. 如果你不写名字,那每一个都会自动编号为project-container-1
  5. docker-compose -p custom_name up 可以自定义前缀而不是使用项目名称

常用的命令open in new window,可以浏览几遍。
几个需要注意的:

  • 虽然depends_on确保了依赖关系,但它并不会等待依赖的服务完全就绪,而是只等待依赖的服务启动。这意味着,在某些情况下,即使依赖的服务已经启动,它们可能还没有完全初始化或准备好接受连接,比如db的容器已经启动,但是容器内的数据库还没起来,这样你就得自己想办法延时或者重试。
  • 当您尝试创建一个网络并指定已经被使用的子网时,网络创建请求会失败,因为Docker会检测到子网冲突并阻止网络的创建。
  • 指定network的时候external: true会尝试找已经存在的网络加入,如果没有就创建

尝试自己写一个elk:

回去看看无聊的时候给搭起来,现在内存不是很多。
[Docker Compose部署ELK – 爱拼才会赢](https://xuyongsheng.cn/docker-composebushuelk/)

日志收集

#TODO 挖坑docker的日志收集 纵览当前容器日志收集的场景,无非就是两种方式:一是直接采集Docker标准输出,容器内的服务将日志信息写到标准输出,这样通过Docker的log driver可以发送到相应的收集程序中;二是延续传统的日志写入方式,容器内的服务将日志直接写到普通文件中,通过Docker volume将日志文件映射到Host上,日志采集程序就可以收集它。

第一种方式足够简单,直接配置相关的Log Driver就可以,但是这种方式也有些劣势:

  1. 当主机的容器密度比较高的时候,对Docker Engine的压力比较大,毕竟容器标准输出都要通过Docker Engine来处理。
  2. 尽管原则上,我们希望遵循一容器部署一个服务的原则,但是有时候特殊情况不可避免容器内有多个业务服务,这时候很难做到所有服务都向标准输出写日志,这就需要用到前面所说的第二种场景模式。
  3. 虽然我们可以先选择很多种Log Driver,但是有些Log Driver会破坏Docker原生的体验,比如docker logs无法直接看到容器日志。

Docker处理日志的方法&日志收集工具比较 https://blog.51cto.com/u_12890843/5347071open in new window

踩坑记录

pass

Loading...