《Docker Deep Dive》阅读笔记(一)
安装
Linux系统下安装
wget -qO- https://get.docker.com/ | sh
如果需要使用非root
用户管理docker(最佳实践),需要添加用户到docker
组:
sudo usermod -aG docker your-user
# 查看用户组:
# cat /etc/group | grep docker
添加后注意先注销再登录。
- 查看版本信息:
docker --version
- 升级Docker引擎(Ubuntu系统下操作)
# 1.停止Docker daemon,更新安装包列表 apt-get update # 2. 卸载Docker(docker可能有不同的名称,尽量列出可能的名称) apt-get remove docker docker-engine docker-ce docker.io -y # 3. 安装新的版本 wget -qO- https://get.docker.com/ | sh # 4. 设置开机自启 systemctl enable docker # 5. 检查Docker状态 systemctl is-enabled docker # 6. 确保容器和服务已经重启 docker container ls docker service ls
- Docker和存储驱动
Linux系统下的Docker存储驱动有以下四种:
一个Docker主机只能有一种存储驱动(不能每个容器一种驱动),可以通过• aufs (the original and oldest) • overlay2 (probably the best choice for the future) • devicemapper • btrfs • zfs # 备注:windows下只支持 windowsfilter 驱动
docker info
查看当前存储驱动。
Windows10下安装
设置>程序和功能>启用或关闭Windows功能
窗口中开启Containers
,Hyper-V
- 到 https://docs.docker.com/docker-for-windows/install/ 下载Docker For Windows进行安装
- 注意到安装完之后会让你选择原生Windows容器还是Linux容器,即容器的运行主机环境,一般选Linux容器(可自由切换)。在Windows下运行Linux容器,是通过
Hyper-V
虚拟技术来实现的。(目前还没有原生Mac容器) - Docker for Windows 包含了: Docker Engine (client 和 daemon),Docker Compose,Docker Machine 和 the Docker Notary command line
镜像(Image)
将镜像类比于编程语言中的类,那么容器就相当于类的实例。
- 镜像的基本操作
- 拉取:
docker image pull <repository>/<name>:<tag>
,默认为镜像的latest
tag, 加-a
选项将会拉取镜像的所有tags。对于官方镜像,不用声明repository
- 查看:
docker image ls
,加--digests
输出摘要信息,加--filter
过滤输出结果 - 输出详细信息:
docker image inspect xxx
- 删除:
docker image rm xxx
- 删除所有:
docker image rm $(docker image ls -q) -f
,其中image ls -q
返回所有镜像ID - 搜索:
docker search xxx
- 拉取:
- 镜像由一组松散的只读层(layer)组成,上层可以覆盖下层,多个镜像可以共享一个层
- 多架构镜像(Multi-architecture images):一个镜像可能需要支持多种架构,比如:Windows,ARM,这时就可以使用多架构镜像( manifest list中记录支持的架构信息)。
容器
容器是镜像的运行时实例。
- Docker版本信息:
docker version
,如果用户账号没有权限,记得将其添加到Docker组 - 检查Docker状态:
service docker status
或者systemctl is-active docker
- 容器基本操作
- 开启一个容器:
docker container run
-it
:表示把当前的终端连接到容器的shell,例如:docker container run -it ubuntu /bin/bash
,将启动一个Ubuntu容器,然后运行它的Bash Shell。Window容器例子:docker container run -it microsoft/powershell:nanoserver pwsh.exe
- 启动并连接到bash后,可以
ps -elf
查看进程详情。输入exit
退出bash,容器运行时不能没有进程,如果这时容器没有进程,将会自动退出 - 按
Ctrl+PQ
键退出bash而不会终止容器。 --name
:设置容器名称,如果没有指定,会自动生成一个
- 查看:
docker container ls
,加-a
同时输出所有的容器 - 按
Ctrl+PQ
键退出后重新回到容器的bash:docker container exec -it 3027eb644874 bash
,其中容器ID值可以先通过docker container ls
得到 - 停止:
docker container stop xxx
- 重启:
docker container start xxx
- 删除:
docker container rm xxx
- 开启一个容器:
- 优雅地删除容器:先停止再删除
- 容器重启策略
always
总是重启停止了的容器,除非显式地停止容器(比如通过docker container stop
),例子:docker container run --name neversaydie -it --restart always alpine sh
unless-stopped
显式地停止后,重启Docker服务不会再重启on-failed
当容器以非0退出码退出,或者Docker服务重启
- 在 Docker Compose 或 Docker Stacks中指定重启策略,例子:
version: "3" services: myservice: <Snip> restart_policy: condition: always | unless-stopped | on-failure
- Web server 例子
- 运行
docker container run -d --name webserver -p 80:8080 nigelpoulton/pluralsight-docker-ci
-d
表示后台运行,前台运行(进入容器交互环境)使用-it
-p 80:8080
将宿主机80端口映射到容器的8080端口
- 然后浏览器访问宿主机IP或者curl ip即可打开网页
- 查看镜像详情:
docker image inspect nigelpoulton/pluralsight-docker-ci
- 运行
- 清理所有容器:
docker container rm $(docker container ls -aq) -f
容器化应用
容器化一个单容器应用
- 下载应用代码:
git clone https://github.com/nigelpoulton/psweb.git
- 查看其中的Dockerfile文件,代码及注释如下:
以上代码可以生成的镜像示意图:# All Dockerfiles start with the FROM instruction. This will be the base layer of the image FROM alpine # 元数据,不构成Layer LABEL maintainer="nigelpoulton@hotmail.com" # Alpine apk package manager to install nodejs and nodejs-npm into the image. RUN apk add --update nodejs nodejs-npm # copies in the app files from the build context COPY . /src # 元数据,不构成Layer WORKDIR /src RUN npm install # exposes a web service on TCP port 8080 # 元数据,不构成Layer EXPOSE 8080 # set the main application that the image(container) should run ENTRYPOINT ["node", "./app.js"]
- 构建镜像:
docker image build -t web:latest .
(最后的(.)号表示构建上下文为当前目录) - 查看:
docker image ls
- 推送到Docker Hub
- 登录:
docker login
(需先注册账号) - 给镜像打标签:
docker image tag web:latest nigelpoulton/web:latest
,后面的参数形式为:<current-tag> <new-tag>
- 推送:
docker image push nigelpoulton/web:latest
- 登录:
- 运行应用:
docker container run -d --name c1 \ -p 80:8080 \ web:latest
- 查看应用:
docker container ls
- 测试应用:访问对应的地址
- 查看镜像的构建历史:
docker image history web:latest
使用多阶段构建部署到生产环境
将构建过程分成多个镜像,后面的镜像可以从前面的镜像中拷贝必要的文件,避免镜像体积过大。
-
参考资料
-
构建镜像:
docker image build
-t
:添加标签-f
:指定Dockfile文件--no-cache=true
不使用缓存--squash
压成一个layer,减少镜像体积apt-get install
添加no-install-recommends
-
FROM
为新的镜像指定基础镜像 -
COPY
复制程序源代码到镜像 -
EXPOSE
指定应用的网络端口 -
ENTRYPOINT
镜像启动时,默认要运行的程序
Compose
Compose 用于单引擎模式(单个节点)下部署和管理多容器应用。
- Linux系统下安装:参考https://github.com/docker/compose/releases
- 查看版本:
docker-compose --version
- 使用yaml格式配置文件,默认文件名为
docker-compose.yml
,-f
可以指定自定义文件 - 配置文件例子
以上配置可分为四个部分: version、services、networks、volumes# 版本,参考:https://docs.docker.com/compose/compose-file/compose-versioning/ version: "3.5" # 服务,以下配置包含服务(容器):web-fe,redis services: web-fe: # 使用当前目录的Dockerfile构建镜像 build: . # 容器启动时运行(这句可省略,因为Dockerfile已经定义) command: python app.py # 将容器里面的5000端口映射到宿主主机5000端口 ports: - target: 5000 published: 5000 # 指明添加到哪个网络 networks: - counter-net # 将卷counter-vol挂载到容器的/code目录 volumes: - type: volume source: counter-vol target: /code redis: # 从 redis:alpine 构建镜像 image: "redis:alpine" networks: counter-net: # 网络 networks: counter-net: # 卷 volumes: counter-vol:
- 使用Compose进行部署
- 下载代码:git clone https://github.com/nigelpoulton/counter-app.git
- 切换到counter-app目录,启动应用:
docker-compose up &
(加&
输出日志)- Compose 配置文件默认名称为
dockercompose.yml
或docker-compose.yaml
- 如果不是这两个名称,可以
-f
指定 -d
后台运行
- Compose 配置文件默认名称为
- 使用Compose管理容器
- 查看服务(容器)
docker-compose ps
- 列出每个服务的进程:
docker-compose top
- 停止应用:
docker-compose stop
- 停止并删除:
docker-compose down
- 重启:
docker-compose restart
- 删除:
docker-compose rm
- 查看服务(容器)
Docker Swarm
由两部分组成:
- 安全集群:组织Docker节点成为集群
- 编排引擎:提供部署和管理的API
组成示意图:
创建一个安全Swarm集群
- 开放端口
- 2377/tcp: for secure client-to-swarm communication
- 7946/tcp and 7946/udp: for control plane gossip
- 4789/udp: for VXLAN-based overlay networks
- Swarm mode 和 single-engine mode
- 在一个single-engine mode Docker宿主主机运行:
docker swarm init
,节点将转为swarm mode,并创建一个新的swarm,该节点则成为第一个manager - 其他节点可以加入成为worker或manager
- 例子(创建一个Swarm,manager:mgr1-mgr3,worker:wrk1-wrk3)
-
登录 mgr1主机,初始化一个Swarm:
docker swarm init \ --advertise-addr 10.0.0.1:2377 \ #其他节点连接该管理者节点的IP和端口 --listen-addr 10.0.0.1:2377 # 需要监听的IP和端口,一般和上面的一样
-
查看节点:
docker node ls
-
在
mgr1
运行:docker swarm join-token manager
生成添加manager节点的命令(包含token) 输出结果大概是这样的:To add a manager to this swarm, run the following command: docker swarm join \ --token SWMTKN-1-0uahebax...ue4hv6ps3p \ 10.0.0.1:2377
同理,将命令中的
manager
替换成worker
,则生成添加worker节点的命令 -
添加一个worker节点:登录到wkr1,运行:
docker swarm join \ --token SWMTKN-1-0uahebax...c87tu8dx2c \ 10.0.0.1:2377 \ --advertise-addr 10.0.0.4:2377 \ --listen-addr 10.0.0.4:2377
该节点将被添加到Swarm作为worker
-
同理可添加wrk2、wrk3节点
-
添加一个manager节点:登录到mgr2,运行:
docker swarm join \ --token SWMTKN-1-0uahebax...ue4hv6ps3p \ 10.0.0.1:2377 \ --advertise-addr 10.0.0.2:2377 \ --listen-addr 10.0.0.1:2377
同理可添加mgr3管理节点
-
再次查看
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS 0g4rl...babl8 * mgr2 Ready Active Reachable 2xlti...l0nyp mgr3 Ready Active Reachable 8yv0b...wmr67 wrk1 Ready Active 9mzwf...e4m4n wrk3 Ready Active d21ly...9qzkx mgr1 Ready Active Leader e62gf...l5wt6 wrk2 Ready Active
- (*)号代表当前运行命令的节点
-
Swarm manager高可用
- Swarm manager原生支持高可用
- active状态的manager称为Leader,它负责分发任务,转发其他manager的任务,示意图:
- 管理节点集群之间的内部管理协调使用了Raft共识算法,这样就保证了管理节点高可用(HA
- 最佳实践:
- 部署奇数数量的manager(减少split-brain)
- 不要部署太多manager(3-5个为佳)
- Swarm内置的安全防护:CA settings, join tokens, mutual TLS, encrypted cluster store,encrypted networks, cryptographic node ID’s等
- 锁定Swarm
- 重启旧的manager,恢复旧的备份等操作可能会引起安全问题或者擦除当前的配置
- 初始化的时候添加
--autolock=true
- 或者对于已有的Swarm更新:
docker swarm update --autolock=true
,注意保存生成的key,manager重启时候解锁用 - 重启其中一个Docker节点:
service docker restart
,看是否能自动添加到集群 - 查看集群中的节点:
docker node ls
(会有报错,提示需要解锁) docker swarm unlock
解锁,输入KEY
Swarm services
- 创建,例子:
docker service create --name web-fe -p 8080:8080 --replicas 5 nigelpoulton/pluralsight-docker-ci
- 声明一个服务,名称为
web-fe
- 每个Swarm节点的8080端口映射到服务副本的8080端口
--replicas 5
表示创建5个服务副本- 按回车后,manager作为leader初始化5个副本,每个manager和worker拉取镜像并启动一个容器,跑在8080端口上。leader还要确保服务的要求状态保存在集群上并复制到Swarm的每一个manager。
- 所有服务都会被Swarm监控着,如果跟要求的状态不符,Swarm将会采取行动,恢复要求的状态。
- 比如,有一个副本挂掉了,Docker会重新启动一个副本,使其恢复到要求的状态
- 声明一个服务,名称为
- 查看服务:
docker service ls
(如果在创建服务后马上查看,可能会查看不到,需要等到部署完成) - 查看服务副本列表:
docker service ps <service-name or serviceid>
- 详情:
docker service inspect xxx
- 服务的两种模式:
- replicated(默认):指定副本数量,并尽可能平均分布到集群上
- global:在每个符合条件的节点上启动一个副本,创建是加
--mode global
- 伸缩一个服务
- 比如,流量暴增,需要增加服务副本,可以:
docker service scale web-fe=10
,增加到10个副本 docker service ls
查看服务,docker service ps
查看所有副本情况docker service scale web-fe=5
将副本数改回5个
- 比如,流量暴增,需要增加服务副本,可以:
- 移除服务:
docker service rm web-fe
- 滚动更新
- 假设镜像需要从v1更新到v2,例子:
docker service update \ --image nigelpoulton/tu-demo:v2 \ --update-parallelism 2 \ # 每次并发更新两个副本 --update-delay 20s uber-svc # 20秒延迟
- 假设镜像需要从v1更新到v2,例子:
- 问题排除
- 查看Swarm service日志:
docker service logs <service-name>
,默认日志驱动为json-file
,json-file
和journald
均支持该命令查看
- 查看Swarm service日志: