docker学习
docker是什么
我们写程序时可能依赖于各种运行环境比如mysql,redis等等,如果我们需要程序在不同的电脑上运行就要分别为电脑安装运行环境还很容易出现版本不一致的问题,使用docker我们可以将运行环境上传到docker容器中,其他电脑只需要使用容器。
docker容器是相互隔离的,每个容器都可以理解成一个极小的操作系统,里边运行着我们镜像。
安装与启动docker
这里仅提供linux下安装方式
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh --mirror Aliyun
启动docker服务
systemctl start docker
默认情况下docker命令使用Unix socket与docker引擎通信,而只有root用户和docker组用户才能访问docker引擎的Unix socket。因此我们最好把需要使用docker的用户加入到docker用户组中
sudo groupadd docker
sudo usermod -aG docker $USER
docker核心概念
镜像(image):一个镜像代表一个软件。比如mysql镜像,redis镜像...
容器(container):基于某个镜像启动的一个服务称之为容器
仓库(repository):存储docker中所有镜像
- 远程仓库:docker提供的一个仓库,可以拉取镜像到本地仓库。docker hub
- 本地仓库:本地存储下载好的镜像的位置
远程仓库加速
1.登录阿里云账号
2.点击控制台
3.产品与服务-容器镜像服务
4.镜像工具-镜像加速器
5.输入对应的指令即可
6.使用docker info
查看设置是否成功
相关命令
引擎
1.查看docker的版本信息
docker info
2.查看docker所有命令
docker
镜像
1.查看本地仓库中存在的镜像
docker images
或者
docker image ls
2.下载镜像到本地仓库
docker pull 镜像名称:版本号
先到docker hub中查看有没有对应的镜像
或者使用
docker search 镜像名
查看有没有这个名字的镜像
3.删除镜像
docker image rm [-f] 镜像名:tag
或
docker image rm [-f] 镜像id
-f:表示强制删除
4.删除全部镜像
docker image rm -f ${docker images -q}
${}表示把括号里的命令输出结果作为输入
5.将本地jar包导入到本地仓库中
docker load -i jar包
容器
1.建立并运行容器
docker run 镜像名:tag
或者
docker run 镜像id
需要注意的是由于docker是进程隔离的,所以这种方式运行的容器,docker外是访问不到的!
需要进行宿主机端口和容器端口的映射(双向映射):
docker run -p 宿主机端口:容器端口 镜像名:tag
为生成的容器指定名称:
docker run --name 容器名称 镜像名:tag
指定容器后台运行(不输出日志):
docker run -d 镜像名:tag
需要注意run这个命令总是创建新的容器,即使在同一个端口基于同一个镜像创建出的也是不同的容器,是相互隔离的
2.查看容器
查看正在运行的容器:
docker ps
查看所有容器:
docker ps -a
返回正在运行的容器id
docker ps -q
3.停止和启动
停止容器:
docker stop 容器名或者容器id
启动容器:
docker start 容器名或者容器id
重启容器:
docker restart 容器名或者容器id
容器id不用全写,写头几位能够唯一表示一个容器即可
4.删除容器
docker rm [-f] 容器名称或者容器id
-f:表示强制删除,用来删除正在运行的容器
5.查看容器运行日志
docker logs [-f] 容器名称或者容器id
-f:实时输出日志
6.查看容器内运行的进程
docker top 容器名称或者容器id
7.与容器内部进行交互
进入容器的命令行
docker exec -it 容器名称或者容器id bash
8.主机与容器的文件操作
把容器内的文件复制到主机:
docker cp 容器名或容器id:资源路径 主机路径
把主机内的文件复制到容器:
docker cp 主机路径 容器名或容器id:资源路径
9.查看容器细节
docker inspect 容器名或容器id
10.数据卷 volume
实现主机与docker容器之间的文件共享(与cp命令不同,cp是复制,数据卷相当于主机和容器指向的是同一个文件,修改一个另一个也会改变)
创建数据卷:
docker volume create 数据卷名
查看所有数据卷:
docker volume ls
查看数据卷所有信息
docker volume inspect 数据卷名
挂载
docker run --name 容器名字 -p 宿主机端口:docker端口 -v 宿主机目录:docker目录 (必须为绝对路径) 镜像id
清理
docker volume rm 数据卷名
应用容器化
步骤
1.编写应用代码
2.创建一个Dockerfile,其中包括当前应用的描述,依赖以及该如何运行这个应用
3.对该Dockerfile执行 docker image build
命令
4.等待Docker将应用程序构建到Docker镜像中
一旦应用容器化完成(即应用被打包成了一个镜像),就能以镜像的形式交付并以容器的方式运行
Dockerfile
镜像的构建文件
上下文目录:Dockerfile所在的目录
构建镜像
docker build -t 镜像名:版本号 Dockerfile所在的目录
镜像名和版本号都是自己起的。
需要注意的是,docker会把Dockerfile所在的目录下所有的文件都打包到镜像中,因此我们往往单独创建一个目录,存储我们镜像所需要的文件
Dockerfile相关命令
FROM 镜像
LABEL maintainer="镜像维护者邮箱"
RUN 命令行命令
WORKDIR 目录
COPY src dst
WORKDIR
FROM:当前镜像基于哪个镜像,必须是Dockerfile的第一句
RUN:构建镜像时需要运行的指令
EXPOSE:镜像暴露哪个端口给宿主机
WORKDIR:创建容器后,终端进来的默认工作目录。若目录不存在会自动创建。WORKDIR可以执行多次,但是上一次的相对路径。
ENV:在构建镜像过程中设置环境变量
ADD:将宿主机目录下的文件拷贝进镜像,且ADD命令会自动处理URL(会下载URL中的文件)和tar包
COPY:类似于ADD,将构建上下文 <原路径>的文件/目录 复制到新的一层镜像的<目的路径>位置
VOLUME:容器数据卷,用于数据保存和持久化工作
CMD:指定一个容器启动时要运行的命令,Dockerfile中可以指定多个命令,但只有最后一个会生效。且会被docker run命令行参数中指定要运行的程序所覆盖
ENTRYPOINT:指定一个容器启动时要运行的命令
ENTRYPOINT与CMD
一般用ENTRYPOINT 后接变参,CMD后接定参
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
实质就是nginx -c /etc/nginx/nginx.conf
缓存
docker镜像时分层的,Dockerfile的除了个别指令,其余的指令每行都会创建出一个新的镜像层
docker镜像的构建过程是存在缓存的,第一次构建后会把相关的镜像放在缓存中,当再次构建该镜像时会直接链接到这个已经存在的镜像中。
需要注意,一旦有指令没有命中缓存,后面整个构建过程中将不再使用缓存机制!也就是说我们应该尽可能把能用到缓存的构建命令放在前边。
示例
使用Dockerfile构建springboot应用
1.开发springboot应用
2.打包
- war包:基于tomcat镜像
- jar包:基于jdk镜像
3.在服务器中创建dockerfile的上下文目录,在这个目录中创建Dockerfile文件
4.上传jar包(demo-0.0.1-SNAPSHOT.jar)到dockerfile的上下文目录
5.编写dockerfile
FROM openjdk:8-jdk
WORKDIR /app
ADD demo-0.0.1-SNAPSHOT.jar app.jar #把jar包拷贝到镜像中,并且重命名
EXPOSE 8081
ENTRYPOINT ["java","-jar"]
CMD ["app.jar"]
6.执行构建 docker build -t demo:01
7.运行容器 docker run -d -p 8081:8081 --name demo demo:01
Docker Compose
微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知。
使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具
安装
Linux:
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
compose文件
compose文件使用的是yaml格式
version: "3.5"
services:
web-fe:
build: .
command: python app.py
ports:
- "9000:8081"
networks:
counter-net:
columes:
counter-vol:
-
verson:指定compose文件的版本
-
services:定义不同的服务,示例中web-fe就是一个服务
- build: Dockerfile文件所在的目录
- command: build时执行的命令
- port:端口映射
-
networks:Docker创建的网络,默认情况下会创建bridge网络
-
volums:Docker创建的卷
启动与停止
使用docker-compose up
来启动一个Compose应用
默认情况下回查找名为docker-compose.yml或docker-compose.yaml的Compose文件。如果Compose文件是其他文件名,则需要通过-f参数来指定
使用docker-compose stop
来停止一个Compose应用
使用docker-compose down
来停止并删除一个Compose应用,它会删除容器和网络,但不会删除卷和镜像
Docker 网络
Docker网络主要有三个部分组成:CNM,Libnetwork和驱动
其中CNM是设计标准,Libnetwork是CNM的具体实现,驱动则实现了特定的网络拓扑
CNM
CNM基本要素为:
- 沙盒:一个独立的网络栈。包括以太网接口,端口,路由表以及DNS配置
- 终端:虚拟网络接口。同普通网络接口一样
- 网络:802.1d网桥(类似大家熟悉的交换机)实现的,网络就是需要交互的终端的集合
如图所示,容器A与容器B因为有终端接入同一个网络,因此是可以相互通信的。而容器B的两个终端不能相互通信。
驱动
Docker内封装了若干内置驱动,Linux上的Bridge,Overlay,Macvlan,Windows上的NAT,Overlay,Transport以及L2 Bridge
none模式
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过--network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
Bridge模式
从名称中可以看到两点,单机意味着该网络只能在单个 Docker 主机上运行,并且只能与所在 Docker 主机上的容器进行连接,桥接意味着这是 802.1.d 桥接的一种实现(二层交换机)。
Linux Docker 创建单机桥接网络采用内置的桥接驱动,而 Windows Docker 创建时使用内置的 NAT 驱动。实际上,这两种驱动工作起来毫无差异。
下图展示了两个均包含相同本地桥接网络 mynet 的 Docker 主机。虽然网络是相同的,但却是两个独立的网络。这意味着图中容器无法直接进行通信,因为并不在一个网络当中。
每个 Docker 主机都有一个默认的单机桥接网络。
在 Linux 上网络名称为 bridge,在 Windows 上叫作 nat。除非读者通过命令行创建容器时指定参数--network,否则默认情况下,新创建的容器都会连接到该网络。
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
我们可以看到每个容器的网卡的ip地址是和虚拟网桥的ip地址是同一个地址段的。
container模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
host模式
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。
Host模式如下图所示:
实践
在实际使用中我们一般使用bridge模式,但是我们不希望所有的容器都连接到同一个网桥中,而是一些容器连接到指定的网桥中。同一网桥之内的容器可以相互通信
创建网桥
docker network create 网桥名字
启动容器时指定网桥
docker run -d -p 8080:8080 --network:网桥名 --name:mycontainer01 linux
--network参数指定我们的网桥
之后每个容器都被分配了一个ip地址,我们可以通过ip地址或者容器名访问同一网桥内的容器
删除网桥
docker netword rm 网桥名
查看网桥细节
docker network inspect 网桥名