linux笔记 — docker 集合
docker的使用给我带来了极大的便利,很久就想记录一下docker基础的内容,苦于健忘,今天才补上…/(ㄒoㄒ)/~~…
资料来源:
https://yeasy.gitbooks.io/docker_practice/introduction/why.html
https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html更新
1
2
3
4
5
6
7
8
9
10
11
12
13
1419.01.01 初始汇总
19.01.10 补充 docker-compose
19.01.25 补充proxy设置
19.07.22 补充 vscode-docker
19.09.23 补充ipv6-nat,更新内容
19.09.25 重新整理内容
19.10.10 补充空间清理,重新整理内容
19.10.29 补充文件权限相关问题
20.02.05 补充 iptables
20.10.12 补充清理的一点问题
21.01.07 更新 ipv6-nat 部分
21.03.14 更新 ipv6-nat 部分
22.03.10 更新一部分网络内容
22.12.11 容器内抓包
导语
- 这是一篇大杂烩,仅作为自己的遇到docker问题的记录.
Docker
- docker起源与对linux容器的封装,提供了一个软件虚拟化环境,实质上是linux系统底层的一个进程,所需要资源比虚拟机低了一个数量级.
- docker在各类支持的宿主机内随便迁移,保证一致的运行环境。
- 基于层次的镜像构建,官方维护了一批高质量的基础镜像,定制非常简单。
- 真正的一次配置,到处运行.
基础
- 镜像
Image
- 镜像本质上就是容器运行的最小文件系统,包含必要的配置、程序、资源等等。镜像中不包含任何动态数据,镜像构建后其内容不会有任何变化.(可以说是面向对象中的类)
- 官方镜像 ubuntu:18.04 是完整的一套 Ubuntu 18.04 最小系统的 root 文件系统.(所有类中,由系统提供的基类)
- 镜像本身为分层构建,透过基础镜像,个性化定制边的非常容易.
- 容器
Container
- 容器就行类的运行实例,可以创建、启动、停止、删除等.
- 容器停止时,其对应文件系统实体,并不会删除.但docker的最佳实践要求,不要使用容器本身作为持久化数据的载体,容器本身不应该写入任何持久化数据.
- 使用数据卷、绑定宿主机目录来保存持久化数据.
- 仓库
Repository
- 分发镜像的地方,类比GitHub.
- 安装docker后默认的是官方镜像仓库Docker Hub,但是速度可想而知.
- 国内有各类的加速镜像,可替换官方镜像.
- 用户还可以自建各类私有的镜像仓库.
安装
- docker有两个版本.
CE
和EE
,前者是社区免费版,后者是企业版,一般的个人使用的以CE
为主. - 下面以
ubunt16.04
、win10
和Raspberry Pi 3B
为例.以官方源安装docker.
ubuntu16.04
一键脚本,docker官方提供了一键安装脚本,适配14,16,18
1
2curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.shApt安装
添加CA证书
1
2
3
4
5
6sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common添加 GPG 密钥
1
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
添加官方源(这里是stable,需要测试/每日构建版本,改为test/nightly)
1
2
3
4sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"安装 Docker CE
1
2sudo apt-get update
sudo apt-get install docker-ce
其他
启动docker, 执行完成,docker就加入了开机启动.
1
2sudo systemctl enable docker
sudo systemctl start docker验证docker
1
docker run hello-world
有输出,没有提示错误即docker正常安装.
1
2
3
4Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
...建立dokcer用户组.
docker命令会使用socket与docker引擎通信,但这个操作通常只有root/docker才会使用,为确保安全,需要使用 docker 的用户加入 docker 用户组.
问题
docker for windows 要求64位系统且必须开启Hyper-V.(前期版本是直接使用了VirtualBox)
stable版本
之后与正常windows程序安装一致.
验证方式一致.
Raspberry Pi 3B
- 与win或者ubuntu不同,Raspberry Pi是arm架构,docker同样支持,但两者镜像不能通用.
- 整体过程与ubuntu相同.
- 树莓派支持镜像需要访问arm32v7
仓库
与
git
一样,你可以在仓库中搜索、发布和拉取镜像.大体流程与git相似,下面只是罗列几个常见命令.
1
2
3
4
5docker login #登陆默认仓库
docker logout #登出
docker search #搜索镜像,类似 ubuntu 不带前缀的时docker官方维护,username/imagename 为用户镜像.
docker pull #拉取镜像
docker push #推送镜像
镜像
拉取镜像
1
docker pull ubuntu:18.04
运行镜像
1
2
3
4docker run -it \ # -it 启动交护操作 启动终端
--rm \ # --rm 容器停止后,自动删除.
ubuntu:18.04 \ # 启动ubuntu:18.04
bash # 容器启动后执行 启动bash命令exit
退出容器.列出镜像
1
docker image ls
结果类似
1
2
3REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 0584b3d2cf6d 3 weeks ago 196.5 MB
redis alpine 501ad78535f0 3 weeks ago 21.03 MB删除镜像
1
docker image rm [选项] <镜像1> [<镜像2> ...]
可以是
ID
、镜像名
或者摘要
.
docker pull proxy
docker pull 操作在全局设置 http_proxy/https_proxy 情况下依旧不能下载.
参考
https://docs.docker.com/config/daemon/systemd/
docker.service(官方推荐),在我的测试中最终生效的.
创建目录
1
sudo mkdir -p /etc/systemd/system/docker.service.d
创建并写入http_proxy.
1
2
3
4
5vim /etc/systemd/system/docker.service.d/http-proxy.conf
# 写入
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80/"创建并写入https_proxy.
1
2
3
4
5vim /etc/systemd/system/docker.service.d/https-proxy.conf
#写入
[Service]
Environment="HTTPS_PROXY=https://proxy.example.com:443/"如果存在不需要通过proxy访问的,可以在对应的文件,增加
NO_PROXY
字段,以 http_proxy.conf 为例.1
2[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80/" "NO_PROXY=localhost,127.0.0.1,docker-registry.somecorporation.com"应用更改并重启
1
2sudo systemctl daemon-reload #应用更改
sudo systemctl restart docker #重启docker查看结果
1
systemctl show --property=Environment docker
也有直接写入
/lib/systemd/system/docker.service
的,但是不推荐.且以第一种方式会覆盖docker.service
的对应字段.
定制镜像(Dockerfile 指令)
示例
1
2
3
4
5FROM node:8.4
COPY . /app
WORKDIR /app
RUN npm install --registry=https://registry.npm.taobao.org
EXPOSE 3000说明
- FROM: docker镜像是分层构建,FROM就是构建镜像的上一层.
- COPY: 拷贝当前目录下所有文件到 镜像的/app文件夹
- WORKDIR: 将接下来的路径设为/app文件夹.
- RUN: 执行 以下命令,注意 ,一个RUN命令就是一个镜像层,一般使用
\
将命令链接起来,避免过多层级. - EXPOSE: 暴露容器3000端口.
其他详细指令参考Dockerfile 指令详解
容器
运行
1
docker run -t -i ubuntu:18.04 /bin/bash
-i -t 启动交互式终端,容器启动后执行 bash.
当image不存在时,docker会搜索默认的docker仓库.1
docker run -d ubuntu:18.04
-d参数,容器在后台运行,所有容器的输出需要log命令获取
1
docker container logs [id / name]
进入
进入正在运行的容器有attach
和exec
两个命令,前者会导致容器终止,推荐使用exec
1
docker exec -i -t [id/name] bash #示例
停止
docker container stop [id/name]
,终止后可通过docker container start
重新启动.删除
docker container rm [id/name]
来删除已停止运行容器.
对于正在运行容器需要添加-f
参数.
如已停止容器较多,docker container prune
可一次性删除所有处于终止状态的容器.
日常使用
Docker Compose
Docker Compose 是 Docker 三剑客之一,负责快速的部署分布式应用.本质上是通过python调用docker的API,实现快速的部署和管理.
Docker Compose 有两个重要概念
- service: 默认是一个应用的容器.实际上也可以是若干组相同的镜像.
- project: 由一组关联的服务(容器)组成的完整业务单元.在docker-compose.yml文件中定义.
通过编写 docker-compose.yml 可一次性部署同一业务下所属的N个容器,进一步,通过python脚本,实现全自动的部署和管理.
安装Docker Compose
docker for windows 和 docker for mac ,已经自带了 Docker Compose.
ubuntu 建议直接由官方二进制文件安装.
1
2sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-composeRaspberry Pi等 arm架构的计算机,建议使用 pip方式安装.
1
sudo pip install -U docker-compose
当然Docker Compose 是一个python应用,也可以直接跑在容器内.
1
2curl -L https://github.com/docker/compose/releases/download/1.8.0/run.sh > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
使用(以caddy镜像使用为例)
caddy是一个go语言编写的web服务端.使用户可以非常方便的搭建一个网站,
- HTTP/2 全自动支持HTTP/2协议.
- HTTPS Caddy 使用 Let’s Encrypt 全自动申请+续签.
- IPv6 完整支持
- WebSockets 对WebSockets有很好的支持.
- go语言编写 没有依赖,非常方便部署。
这里我们使用 dockerhub 上最流行的 caddy镜像
abiosoft/caddy
.使用caddy 需要提供 Caddyfile 的配置文件,
abiosoft/caddy
的默认位置在 /etc/Caddyfile.我们需要将宿主机的Caddyfile 挂载到镜像的/etc/Caddyfile .
这里我提供的 Caddyfile 文件示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20http://www.xxxx.com:80 {
timeouts none
redir https://www.xxxx.com
}
https://www.xxxx.com {
root /www
timeouts none
gzip
tls [email protected]
}
http://xxxx.com:80 {
timeouts none
redir https://xxxx.com
}
https://xxxx.com {
root /www
timeouts none
gzip
tls [email protected]
}同时我们需要将镜像 /root/.caddy(证书存放地址) ,挂载到宿主机目录.
链接镜像80 443端口到宿主机 80 443端口.
使用docker run 命令
1
2
3
4
5
6docker run -i -t \
-v /root/Caddyfile:/etc/Caddyfile \
-v /root/www:/www \
-v /root/.caddy:/root/.caddy \
-p 80:80 -p 443:443 \
abiosoft/caddy在交护终端选择 y .同意 Let’s Encrypt 的协议.
访问https://www.xxxx.com
即可.Docker Compose
编写docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16version: '2'
services:
caddy:
container_name: caddy
image: abiosoft/caddy
volumes:
- "/root/Caddyfile:/etc/Caddyfile"
- "/root/www:/www"
- "/root/.caddy:/root/.caddy"
environment:
ACME_AGREE: "true"
ports:
- 80:80
- 443:443
network_mode: "host"
restart: always`这里 特别注意 环境变量要附加
ACME_AGREE: "true"
,等同于 同意Let’s Encrypt 的协议.docker-compose up
启动项目.
访问https://www.xxxx.com
即可其他命令
docker-compose ps
列出运行的服务docker-compose stop
停止某个服务docker-compose start
启动某个暂停的服务
ps:
restart: always
对应的service即开机自启
Compose 命令说明
Compose 模板文件
管理插件
还是推荐 vscode-docker.
微软官方出品,值得信赖.日常的镜像/容器/数据卷/网络栈管理,支持接入dockerhub,非常方便.
话说现在 vscode 已经支持直接ssh登录镜像进行开发了,一种语言一个docker,顶多挂载几个文件夹.(待补充)
直接上图了
网络
- 提及 docker 的网络又是一个深坑.至今还在里面.这里暂时只提及ipv6.
ipv6
参考 Enabling IPv6 Functionality for Docker & Docker Compose , IPv6 with Docker
docker 对ipv6 的支持是在2017年以后.
终究还是不习惯 ipv6 的方式.
启用ipv6
首先你的宿主机要有至少相对固定的 ipv6 地址.每次ipv6 地址变化改配置文件很麻烦…
编辑
/etc/docker/daemon.json
添加1
2"ipv6": true,
"fixed-cidr-v6" : "2001:db8:1::/64"2001:db8:1::/64
要分配给docker的子网ip段.
重启docker .
1
systemctl restart docker
同时也需要添加路由表和转发.
1
2
3ip -6 route add 2001:db8:1::/64 dev docker0
sysctl net.ipv6.conf.default.forwarding=1
sysctl net.ipv6.conf.all.forwarding=1ifconfig
查看 docker0 网卡的ipv6 地址.
ipv6-nat
ipv6 问题
- 国内的 ipv6 地址经常变化,该配置文件太麻烦了.
- 容器独立的ipv6地址意味着完全没有nat,主机的v6防火墙形同虚设.
- 虽然ipv6下nat没有什么意义了,但就安全而言,容器暂时没有较好的防火墙设置.v6端口全开终究不是什么好事.
docker 官方暂时不会支持ipv6-nat了,这里来自 robbertkl/docker-ipv6nat
启用一个-network = host 的本地容器处理ipv6 的nat.但是只支持 bridge 类型的网桥.包括 docker 0(默认bridge类型) 和用户自定义的 bridge 网桥.
最方便的是在 docker-compose 中随文件自定义网桥.
配置nat
前置条件
能够转发的ip地址范围:
fc00::/7
,用户自定义网桥需要注意.宿主机需要加载
ip6_tables
模块.如果lsmod
没有发现,echo "ip6_tables" >> /etc/modules
加载并重启一下.启用转发等
1
2sysctl net.ipv6.conf.default.forwarding=1
sysctl net.ipv6.conf.all.forwarding=1
启动 docker-ipv6nat
1
docker run -d --restart=always -v /var/run/docker.sock:/var/run/docker.sock:ro --privileged --net=host robbertkl/ipv6nat
容器使用
docker 0 网桥: 将上文中
fixed-cidr-v6
的ip地址范围修改为fc00::/7
内,例如fd00:dead:beef::/48
.这样 docker 0 网桥可以正常访问ipv6了.用户自行创建一个网桥,也非常简单.
1
docker network create --ipv6 --subnet=fd00:dead:beef::/48 mynetwork
容器启动时使用
--net=mynetwork
链接到mynetwork
.
托管到 docker-compose
前置条件相同
在 docker-compose 中直接新建一个受支持的网桥,容器分配到这个网桥下即可.
因为需要定义 enable_ipv6
所以创建网桥的 docker-compose 的版本只能限制为 2.1.
docker-compose version 3.x 只能曲线救国,即由 2.x 创建网桥,其他 3.x 通过 external 引用到此网桥下.
为了方便管理,这里将创建网桥和启动 robbertkl/ipv6nat
放在一起了.
- 为了使网桥正常创建,还需要将网桥分配给那个凑数容器…
- 与直接命令参数相同,需要
privileged: true
提权. - 系统创建的网桥名称是
文件夹名_nat
1 | version: '2.1' |
容器内引用网桥(假设是 ipv6_nat
)
1 | version: '3.8' |
这样启动容器后,ifconfig
就可以看到分配的 v6 地址.ping -6 也能正常ping通.
其他
有一个奇葩需求: 多个容器共享同一个网络栈.
- 这个需求来源大概是一些软件只能监听 127.0.0.1,需要其映射到局域网
docker-compose:
- network_mode: service:[service name]
空间清理
参考如何清理Docker占用的磁盘空间?
docker system df
类似linux的df
命令,查看docker的磁盘空间占用.1
2
3
4
5
6# Docker 镜像占用了7.2GB磁盘 Docker 容器占用了104.8MB磁盘 Docker 数据卷占用了1.4GB磁盘
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 147 36 7.204GB 3.887GB (53%)
Containers 37 10 104.8MB 102.6MB (97%)
Local Volumes 3 3 1.421GB 0B (0%)
Build Cache 0B 0Bdocker system prune
- 删除关闭的容器
- 没有使用的数据卷
- 没有使用网络,
- 无 tag 的镜像
docker system prune -a
(慎用)- 同上
- 没有使用的docker镜像
有时docker的日志会浪费大量空间. Debian系上 Docker 的所有相关文件(镜像 容器等)都保存在/var/lib/docker/目录下.
sudo du -hs /var/lib/docker/
排查过大的文件夹.以上治标不治本.需要限制容器日志的文件大小.幸好在docker-compose中非常简单.在需要限制的 services 下添加即可.(需要指明 2.1 版本)
1
2
3
4logging:
driver: "json-file"
options:
max-size: "50m"类似命令
1
2
3
4docker container prune # 清理所有 stop 容器
docker image prune # 清理无用中间镜像
docker volume prune # 清理未使用 volume
docker network prune # 清理未使用网络
清理问题
有时会有无法清理的容器.
直接到 /var/lib/docker/container
rm -rf…
文件/文件夹权限问题
宿主机挂载文件夹到容器,非常方便容器的数据管理.
不过容器创建的文件都是root权限,一直以来都未太在意,直到折腾 aria2 docker.
下载的文件除了读取连删除都没法搞.
容器一般默认运行在 root 用户下,创建文件自然是 root 权限.
为了解决文件权限文件,我们需要指定容器运行在宿主机系统的普通用户下.
背景
- linux 目前通过 Linux user namespace 对进程进行安全隔离,一个普通用户在系统层面是通过 用户和用户组 来管理权限.而在内核,内核是通过 uid 和 gid 来管理权限,而不是用户名/用户组.
- 容器与宿主机共用内核,我们无法直接指定容器运行用户.但是可以指定 uid 和 gid.这样解决方案就来了.
解决
- 宿主机直接使用root用户,一切权限都是浮云,但是很不安全.
- 或者 通过获取宿主机普通用户的 uid 和 gid ,指定容器以相同的 uid 和 gid 运行,虽然在容器内用
id
命令可能无法提示正确的用户名,但是实际在宿主机的文件已经可以正常读取了.
方法
- 使用
id
获取宿主机普通用户的 uid 和 gid ,一般默认的普通用户是1000:1000
. - 在docker run 时
-u
指定运行时的 uid 和 gid . 例:docker run -u=1000:1000
- docker-compose 文件中也非常简单,在 services 下增加
user: 1000:1000
- 使用
如此 aria2 下载的文件正常了,web UI 还在折腾中.
docker 内使用 iptables
docker 内使用 iptables 有两个问题
- docker 基础镜像一般都是最小镜像,不带 iptables.
- 权限不足.
第一个问题自编译镜像时加上 iptables 即可,为了保险把 ip6tables 也一并安装.以 alpine 为例:
apk add --no-cache iptables ip6tables
权限问题解释有点麻烦, docker 内默认运行的 root 用户,但这个 root 用户只相当与宿主机的一个普通用户.但是docker 可以指定 privileged 参数,赋予容器真正的 root 权限.
docker 启动时
docker run --privileged xxx
docker-compose 在 server 中指定
privileged: true
严重警告: 赋予容器 root 权限后,必须非常小心.
容器内抓包
参考
- https://nutao.gitee.io/2020/04/06/Docker-容器抓包/
- https://mozillazg.com/2020/04/use-tcpdump-for-a-container-but-outside-container.html
tcpdump 在 host 调试时候相当好用,但对容器无能为力. -> nsenter
- 详细原理就不细究了,粗略来说 容器在与 host 不同的网络命名空间,nsenter 能临时将 shell 的网络命名空间切换到容器所在的地方.
获取容器 id ,切换 ,tcpdump 搞定
1 | docker inspect --format "{{.State.Pid}}" <container id/name> |
其他
docker compose 突然提示: network.external.name is deprecated in favor of network.name
1 | default: |
要换成下面的形式
1 | default: |