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 docker
sudo gpasswd -a <user> docker
- 重启
docker
进程sudo service docker restart
并重启shell
Dock Platform
Docker 提供了一个开发,打包,运行 app 的平台,并把 app 和底层 infrastructure
隔离,按顺序分为以下几层
Application
Docker 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
,uts
Control 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 container
docker 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
是什么,尽量使用官方的image
FROM scratch
:制作base image
FROM 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
或wget
ENV
:用于设定环境变量,在下面的命令里可以使用,可以增加可维护性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
:列出所有的namespace
ip netns delete <namespace name>
:删除某个namespace
-
docker network ls
:可列出网络情况,包括networkid
、name
、driver
、scope
bridge
:
-
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
,aws
Volume
有两种类型 -
受管理的
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
:查看启动的所有service
docker-compose stop
:会停止所有service
docker-compose down
:会停止所有service
并删除定义的service
,network
,volume
,但不会删除image
docker-compose images
:列举出container
以及使用的image
docker-compose exec <Service> <command>
:和docker exec
类似
Swarm Mode
这个容器编排工具已经集成在 docker 里了,只不过正常不是运行在这个模式下
Swarm
是一个集群的架构,集群就会有节点,节点就会有角色,有两种角色
Manager
:集群的大脑,如果有多个Manager
就需要进行同步,这里就会用到一个分布式的数据库Raft consensus group
Worker
:也就是干活的节点,有的会通过网络同步信息 有两个重要的概念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>
:加入某个cluster
docker 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 <netowrk>
:设置所在网络
-
docker service ls
:列出所有service
-
docker service ps <service>
:查看服务状态 -
docker service scale <service=num>
:用于扩展多个servcie
,num
为扩展的数量,扩展之后的服务会均衡分布在各个节点中,同时这个scale
会保持replicas
的数量不变,如果有replicas
被shutdown
,会在service
内重新创建一个相同的replicas
来进行修补 -
docker service rm <service>
:删除service
service
是在不同的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
响应