Docker 基础知识归纳
Docker 能干什么?
- 简化配置,
- 代码流水线管理
- 提高开发效率
- 隔离应用
- 整合服务器
- 调试能力
- 多租户
- 快速部署
k8s 是干什么的?
它是一个容器编排的工具,对容器的创建、管理、调度、运维。
容器是什么?
在很久之前是先有一台服务器,再安装操作系统,再安装 App,缺点有很多
- 部署非常慢
- 成本非常高
- 资源浪费
- 难以迁移和扩展
- 可能会被限定硬件厂商
虚拟化技术出现以后,通过一层 Hypervisor 来进行物理资源的虚拟化,在虚拟化之上安装操作系统,一个物理机可以部署多个 app,每个 app 独立运行在一个 VM 里,它有很多优点
- 资源池——一个物理机的资源分配到不同的虚拟机里
- 很容易扩展——加物理机器 or 加虚拟机
- 很容易云化——亚马逊 AWS,阿里云等
它也有局限性,每一个虚拟机都是一个完整的操作系统,要给其分配资源,当虚拟机数量增多时,操作系统本身消耗的资源势必增多
现在开发和运维面临的挑战,在开发一个项目的时候,我们需要部署数据库,后台,缓存,可能会使用不同的后台语言和数据库,对于开发人员的测试很复杂。对于运维人员的部署也是非常复杂,还需要开发人员和运维人员的沟通,不然环境可能不一样
容器就是对 app 的打包,通过打包可以运行在任何环境中,容器是 APP 层面的隔离,虚拟化是物理资源层面的隔离,当然虚拟化和容器技术可以一起使用
- 对软件和其依赖的标准化打包
- 应用之间相互隔离
- 共享同一个 OS Kernel
- 可以运行在很多主流操作系统上
它解决了很多问题
- 解决了开发和运维之间的矛盾
- 在开发和运维之间搭建了一个桥梁,是实现 devops 的最佳解决方案
一些技巧
docker-machine可以创建一个拥有 docker 的 linux 虚拟机docker playground可以不用安装 docker 来运行 docker- 如果我们想不用
sudo就使用docker命令,可以将用户加入docker用户组sudo groupadd dockersudo gpasswd -a <user> docker- 重启
docker进程sudo service docker restart并重启shell
Dock Platform
Docker 提供了一个开发,打包,运行 app 的平台,并把 app 和底层 infrastructure 隔离,按顺序分为以下几层
ApplicationDocker Engine它有以下几个东西
- 后台进程
dockerd:用于容器和存储的管理,ps -ef | grep docker可以看到进程在/usr/bin/dockerd REST API Server:用于dockerd和docker通信- CLI 接口
docker:
Infrastructure(physical/virtual)
底层技术支持,都是 linux 早已存在的技术
Namespaces:隔离pid,net,ipc,mnt,utsControl group:做资源限制Union file systems:Container和image的分层
Docker Image
image 是什么?
-
它是文件和
meta data的集合root filesystem -
分层的,并且每一层可以添加改变删除文件,成为一个新的
image -
不同的
image可以共享相同的layer -
Image本身是read-only的 命令 -
docker history <image id>:可查看 image 历史 -
docker images或docker image ls:可以列出所有 docker -
docker rmi或docker image rm <image id>:移除image -
docker build或docker image build:根据一个Dockerfile创建image获取Image -
Build from Dockerfile:根据Dockerfile进行构建
# base image
FROM ubuntu:14.04
# 基本标识
LABEL maintainer="Peng Xiao <123@qq.com>
# 在 base image 基础上跑命令
RUN apt-get update && apt-get install -y redis-server
# 暴露端口
EXPOSE 6379
# 通讯入口
ENTRYPOINT ["/usr/bin/redis-server"]之后通过 docker build -t <image name> <dockerfile 路径> 来进行构建
Pull from Registry:从Registry上获取docker image,具体image可从https://hub.docker.com上查阅
Container
Container 是什么?
- 通过
docker创建 - 在
Image layer之上建立一个container layer可读写 - 类比面向对象:类和实例
Image负责 app 的存储和分发,Container负责运行app命令docker run <image name>:<image version>:基于image创建一个container,version不传表示最新-d:让服务在后台执行--name:可以指定名字--link <image name>:可以连接上一个image,生成的image就可直接连接link的image--network <network name>:可以指定创建某种类型的网络容器,有三种bridge:也就是能够联网的容器none:不能联网的容器host:这个容器没有自己的network namespace,它是和主机共享一套network namespace,此时运行ip a和主机的ip a打印结果一致
-p <容器端口>:<本地端口>:端口映射,这样访问主机就能访问这个服务-e <env key=value>:设置环境变量
docker ps -a或docker container ls:列出当前本地正在运行的容器-a:列出所有容器,包括正在运行和退出的-aq:列出所有container id-it:可以进入这个image里输入命令- 可通过
docker rm $(docker ps -aq)将所有正在运行的container移除 docker rm $(docker container ls -f "status-exited" -q)将已经退出的容器移除
docker rm或docker container rm <container id>:移除正在运行的docker containerdocker commit <container id> <container name>或docker container commit:基于某个container,在某个container中做了一些变化,比如安装了某些软件,就能将它再构建为一个image,但这种方式并不提倡docker exec -it <container id> <command>:可以进入正在运行的container中输入命令进行操作,比如/bin/bash命令可以使用命令进行操作docker stop <container id | name>:终止正在运行的容器docker start <name>:可将container启动docker inspect <container id>:可以显示这个container详细的信息docker log <container id>:容器的输出内容
Dockerfile
github 上的 docker-library 有很多官方 image 的 dockerfile ,可以作为参考
FROM:指定了我们定义的base image是什么,尽量使用官方的imageFROM scratch:制作base imageFROM centos:使用base image
LABEL:指定image信息,类似于注释maintainer:作者version:版本description:描述
RUN:运行一些命令,但是要注意每运行一次RUN,image就会生成新的一层,复杂的RUN请用反斜线换行,避免无用分层,合并多条命令成一行。CMD:设置容器启动后默认执行的命令和参数- 如果
docker run指定了其他命令,CMD命令会被忽略 - 如果定义了多个
CMD,只有最后一个会执行 - 当
CMD的值为[]时,可以在使用docker run时后面接需要输入的参数,比如
- 如果
ENTRYPOINT ["/usr/bin/stress"]
CMD []ENTRYPOINT:设置容器启动时运行的命令- 让容器以应用程序或服务的形式运行,比如说启动一个数据库
- 不会被忽略,一定会执行
- 最好写一个 shell 脚本作为
entrypoint,比如
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]使用上面三个操作可以有两种格式
- Shell 格式:
RUN apt-get install -y vim,能够识别变量ENV - Exec 格式:
RUN ["apt-get", "install", "-y", "vim"],它不能识别变量,除非指定shell,比如["/bin/bash", "-c", "echo hello $name"]才会识别这里的$name变量
WORKDIR:当前工作目录,如果后面的值没有则会自动创建目录,用WORKDIR,不要使用RUN cd,同时尽量使用绝对目录ADD:把本地文件添加到image里,它还可以将压缩文件解压缩COPY:类似ADD,但不能解压缩,大部分情况COPY优于ADD,添加远程文件/目录请使用curl或wgetENV:用于设定环境变量,在下面的命令里可以使用,可以增加可维护性VOLUME:可以设定本地持久化存储路径,比如VOLUME ["/var/lib/mysql"],之后通过docker run -v mysql:/var/lib/mysql来进行
每一步命令都会生成一个临时的container id,可以dicker run -it <container id> /bin/bash进行 debug
镜像的发布
- 将
image发布到dockerhub上 - 将
docker和github关联 - 获取
registry镜像,在本地服务器搭建一个本地image仓库
Docker Network
当存在 docker network 时,本机使用 ip a 可以列出网络
-
veth开头的网络用于连接两个network namespace -
docker0就是本地docker的网络,需要使用veth开头网络进行连接,其实就是主机的网络 当创建一个container时,也会创建一个network namespace -
ip netns list:列出所有的namespaceip netns delete <namespace name>:删除某个namespace
-
docker network ls:可列出网络情况,包括networkid、name、driver、scopebridge:
-
docker network inspect <network id>:可列出network详细情况,其中包括Container情况- 如果
network id为bridge可列出network namespace的连接情况 每个容器都会连接docker0,之后docker0会通过NAT连接外网,从而让每个container都能连接外网
- 如果
-
docker network create -d <network type> <network name>:用于创建一个网络,其中network type可以为bridge,当container连接在同一个用户自定义的bridge network,这时连接上同一个network时,可以使用ping <container name>直接连接 -
docker network connect <network name> <container name>:可让容器连接一个网络VXLAN可以使用隧道进行多机通信
Docker 的持久化存储和数据共享
在 Container 中进行数据的存储仅限于容器里面,在容器删除时数据也会被一起删除,现在的持久化数据的方案有两类
-
基于本地文件系统的
Volume,可以在执行Docker create或Docker run时,通过-v参数将主机的目录作为容器的数据卷 -
基于
plugin的Volume,支持第三方的存储方案,比如NAS,awsVolume有两种类型 -
受管理的
data Volume,由 docker 后台自动创建 -
绑定挂载的
Volume,具体挂在位置可以由用户指定Mysql在官方Dockerfile中有个配置项Volume可以将数据存储在它的值/var/lib/mysql中 -
docker volume ls:查看本地的volume -
docker volume inspect <volume id>:查看对应volume的详细情况 -
docker volume rm <volume id>:删除对应volume空间 删除volume后,可以通过docker run的-v选项创建拥有volume别名的container -
docker run -d -v mysql:/var/lib/mysql ...创建对应volume别名的container可在Dockfile文件中自己设置VOLUME配置项来指定存储空间
Bind Mouting
我们可以指定本地的文件存储路径和容器存储路径进行同步,比如 docker run -v /home/aaa:/root/aaa
Docker Compose
多容器的 APP 太恶心
- 要从
Dockerfile build image或Dockerhub拉取image - 要创建多个
container - 要管理这些
container(启动停止删除) 此时可以通过Docker Compose进行批处理,它依赖于一个yml文件,这个文件有三大概念 Service:相当于一个container,类似docker run,可以给其指定network和volume,所以可以给service指定network和Volume的引用Network:可以指定网络Volume:可以指定volume
docker-compose 命令
linux 下安装: https://docs.docker.com/compose/install/#install-compose
docker-compose up:启动compose-f <yml文件>:不加上这个选项相当于-f docker-compose.yml,即当前目录下的docker-compose.yml文件-d:后台启动,但此时不会打印 log--scale <SERVICE=NUM>:将service增加到num个,也就是启动三个同样的服务,加上haproxy可以用于负载均衡
docker-compose ps:查看启动的所有servicedocker-compose stop:会停止所有servicedocker-compose down:会停止所有service并删除定义的service,network,volume,但不会删除imagedocker-compose images:列举出container以及使用的imagedocker-compose exec <Service> <command>:和docker exec类似
Swarm Mode
这个容器编排工具已经集成在 docker 里了,只不过正常不是运行在这个模式下
Swarm 是一个集群的架构,集群就会有节点,节点就会有角色,有两种角色
Manager:集群的大脑,如果有多个Manager就需要进行同步,这里就会用到一个分布式的数据库Raft consensus groupWorker:也就是干活的节点,有的会通过网络同步信息 有两个重要的概念Service:和docker compose里的Service概念基本一致,相当于就是一个Replicas:做扩展,一个服务可能有多个相同的container来进行负载均衡,此时一个container就相当于一个·replicas
docker swarm
docker swarm init:初始化一个swarm cluster,这个命令会生成一个token,需要通过docker swarm join --token <token>来加入cluster--advertise-addr <ip>:用于宣告一个地址让各个节点知道cluster的存在
docker swarm join --token <token>:加入某个clusterdocker node ls:显示当前 docker 的节点
docker service
docker service 可以在整个集群中控制 service
-
docker service create <image> <command>:创建一个service,如果是docker run只能在本地创建service,而service命令可以在整个cluster创建service--name:命名service-e:设置环境变量--mount <type=类型名>:类型名可以为volume,用预设值volume--network <network>:设置所在网络
-
docker service ls:列出所有service -
docker service ps <service>:查看服务状态 -
docker service scale <service=num>:用于扩展多个service,num为扩展的数量,扩展之后的服务会均衡分布在各个节点中,同时这个scale会保持replicas的数量不变,如果有replicas被shutdown,会在service内重新创建一个相同的replicas来进行修补 -
docker service rm <service>:删除serviceservice是在不同的swarm几点中创建的,我们不知道它们的位置,所以我们需要在它们之间进行通信。我们可以通过overlay的方式让所有机器连在同一个网络中 -
docker network create -d overlay <network name>:创建一个overlay网络,如果这个网络下有一个service,而这个service下的主机如果存在节点,就会共享网络 两个Service可以进行通信,连到同一个网络时可以通过服务名进行通信,利用的是DNS服务发现。 如果有service连接到一个overlay网络时,会有一条DNS记录,通过这个DNS记录就可以知道service的 ip 地址。 但是这里获得的 ip 不是每个服务的docker所在的 ip,而是虚拟 ipvip来解决,而同一个service下的replicas的 ip 是一样的,会和实际 ip 有一个map关系 上面说的技术就是Routing Mesh -
internal:Container和Container之间的访问通过overlay网络,也就是虚拟 IP 来做到负载均衡。其实虚拟IP就是LVS(linux virtual Server)用于高可用负载均衡 -
Ingress:如果服务有绑定接口,则此服务可以通过任意swarm节点的相应接口访问,也就是在同一个cluster里可以通过端口访问服务- 外部访问的负载均衡
- 服务端口碑暴露到各个
swarm节点 - 内部通过
IPVS进行负载均衡,每次访问服务的时候让不同的replicas响应