Docker
简介
概述
打包源码+配置+环境+版本等信息,解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。
Docker和 虚拟机的 区别
虚拟机
将整套Linux操作系统移植到Windows操作系统上
虚拟机的缺点:1 资源占用多 2 冗余步骤多 3 启动慢
Docker
提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有可移植性和一致性。将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。
比较
比较了 Docker 和传统虚拟化方式的不同之处:*传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;*容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。
基本组成
镜像
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。它也相当于是一个root文件系统。相当于容器的“源代码”,docker镜像文件类似于Java的类模板,而docker容器实例类似于java中new出来的实例对象。
容器
容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台 2 从镜像容器角度可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
仓库
仓库(Repository)是集中存放镜像文件的场所。 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等
常见命令
帮助启动类命令
- 启动/停止/重启/状态docker: systemctl start/stop/restart/status docker
- 开机启动: systemctl enable docker
- 查看docker概要信息: docker info
- 查看docker总体帮助文档: docker --help
- 查看docker命令帮助文档: docker 具体命令 --help
镜像命令
- 列出本地主机上的镜像: docker images
-a :列出本地所有的镜像(含历史映像层)
-q :只显示镜像ID。
- 下载镜像: docker search 镜像名
–limit : 只列出N个镜像,默认25个(docker search --limit isoName)
- 拉取镜像:docker pull 某个XXX镜像名字[:TAG]
- 查看镜像/容器/数据卷所占的空间: docker system df
- 删除镜像: docker rmi 某个XXX镜像名字ID
删除多个:docker rmi -f 镜像名1:TAG 镜像名2:TAG
全部删除: docker rmi -f $(docker images -qa)
虚悬镜像
仓库名、标签都是的镜像,构建错误所产生。俗称虚悬镜像dangling image
- 删除所有的虚悬镜像: docker image prune
容器命令
- 新建/启动容器: docker run [OPTIONS] IMAGE [COMMAND] [ARG…]
–name=“容器新名字” 为容器指定一个名称;
-d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
-i:以交互模式运行容器,通常与 -t 同时使用;
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;也即启动交互式容器(前台有伪终端,等待交互);
-P: 随机端口映射
-p: 指定端口映射(格式:-p 主机端口:容器端口)
-d: 后台启动
–restart=always 虚拟机重启该容器也会重启
#使用镜像centos:latest以后台模式启动一个容器docker run -d centos 问题:然后docker ps -a 进行查看, 会发现容器已经退出很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程.容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
docker run -d ubuntu
启动的容器因为没有持续运行的进程,会立即停止,所以在docker ps
中看不到。
docker run -d redis
启动的容器中有一个持续运行的 Redis 服务进程,容器会持续运行,所以在docker ps
中可以看到。
example
#使用镜像centos:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it centos /bin/bash
参数说明:-i: 交互式操作。-t: 终端。centos : centos 镜像。/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。要退出终端,直接输入 exit:
- 列出当前运行的容器:docker ps [OPTIONS]
-a :列出当前所有正在运行的容器+历史上运行过的
-n 参数: 列出前n个容器(停止非停止都会列出)
-l :显示最近创建的容器。
-n:显示最近n个创建的容器。
-q :静默模式,只显示容器编号。
- 退出容器:
exit run进去容器,exit退出,容器停止
ctrl+p+q run进去容器,ctrl+p+q退出,容器不停止
- 启动已停止运行的容器: docker start 容器ID或者容器名
- 重启/停止容器: docker restart/stop 容器ID或者容器名
- 强制停止容器: docker kill 容器ID或容器名
- 删除已停止的容器: docker rm 容器ID
若在运行,可添加-f强制删除
- 一次性 删除所有容器示例
docker rm -f $(docker ps -a -q)
- 批量删除已停止的容器
docker ps -a -q | xargs docker rm
-
查看容器日志: docker logs 容器ID
-
查看容器内运行的进程: docker top 容器ID
-
查看容器内部细节: docker inspect 容器ID
-
进入正在运行的容器并以命令行交互
- docker attach 容器id (attach 直接进入容器启动命令的终端,不会启动新的进程
用exit退出,会导致容器的停止。) - docker exec -it 容器ID bashShell (exec 是在容器中打开新的终端,并且可以启动新的进程
用exit退出,不会导致容器的停止。)
- docker attach 容器id (attach 直接进入容器启动命令的终端,不会启动新的进程
-
将容器内的文件拷贝到当前主机: docker cp 容器ID:容器内路径 目的主机路径
-
导出容器的内容留作为一个tar归档文件到当前主机目录下:docker export 容器ID > 文件名.tar
-
从tar包中的内容创建一个新的文件系统再导入为镜像: cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
其他容器常见命令
图片正下方还有命令 attach Attach to a running container
# 当前 shell 下 attach 连接指定运行镜像build Build an image from a Dockerfile # 通过 Dockerfile 定制镜像commit Create a new image from a container changes
# 提交当前容器为新的镜像cp Copy files/folders from the containers filesystem to the host path
#从容器中拷贝指定文件或者目录到宿主机中create Create a new container # 创建一个新的容器,同 run,但不启动容器diff Inspect changes on a container's filesystem
# 查看 docker 容器变化events Get real time events from the server
# 从 docker 服务获取容器实时事件exec Run a command in an existing container
# 在已存在的容器上运行命令export Stream the contents of a container as a tar archive # 导出容器的内容流作为一个 tar 归档文件[对应 import ]history Show the history of an image # 展示一个镜像形成历史images List images
# 列出系统当前镜像import Create a new filesystem image from the contents of a tarball # 从tar包中的内容创建一个新的文件系统映像[对应export]info Display system-wide information
# 显示系统相关信息inspect Return low-level information on a container
# 查看容器详细信息kill Kill a running container
# kill 指定 docker 容器load Load an image from a tar archive
# 从一个 tar 包中加载一个镜像[对应 save]login Register or Login to the docker registry server
# 注册或者登陆一个 docker 源服务器logout Log out from a Docker registry server # 从当前 Docker registry 退出logs Fetch the logs of a container
# 输出当前容器日志信息port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
# 查看映射端口对应的容器内部源端口pause Pause all processes within a container
# 暂停容器ps List containers
# 列出容器列表pull Pull an image or a repository from the docker registry server # 从docker镜像源服务器拉取指定镜像或者库镜像push Push an image or a repository to the docker registry server
# 推送指定镜像或者库镜像至docker源服务器restart Restart a running container # 重启运行的容器rm Remove one or more containers
# 移除一个或者多个容器rmi Remove one or more images
# 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]run Run a command in a new container
# 创建一个新的容器并运行一个命令save Save an image to a tar archive
# 保存一个镜像为一个 tar 包[对应 load]search Search for an image on the Docker Hub # 在 docker hub 中搜索镜像start Start a stopped containers
# 启动容器stop Stop a running containers
# 停止容器tag Tag an image into a repository
# 给源中镜像打标签top Lookup the running processes of a container
# 查看容器中运行的进程信息unpause Unpause a paused container
# 取消暂停容器version Show the docker version information
# 查看 docker 版本号wait Block until a container stops, then print its exit code # 截取容器停止时的退出状态值
Docker镜像
commit
commit提交容器副本使之成为一个新的镜像,复用原镜像的基础内容
- docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]
Docker容器数据卷(必须加–privileged=true)
将docker容器内的数据保存进宿主机的磁盘中,运行一个带有容器卷存储功能的容器实例,有点类似我们Redis里面的rdb和aof文件
特点:1:数据卷可在容器之间共享或重用数据2:卷中的更改可以直接实时生效,爽3:数据卷中的更改不会包含在镜像的更新中4:数据卷的生命周期一直持续到没有容器使用它为止
- docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
容器卷和主机互联
互联后,宿主机目录下和容器内目录下的文件双向同步,即使docker关闭了,也会在下一次重启docker时同步信息
以下读写都是对容器进行限制
直接命令添加(默认读写)
- docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
:rw 读写
:ro 只读
ps: 没有标注的话默认为读写(-v /宿主机绝对路径目录:/容器内目录:rw)
卷的继承和共享
- docker run -it --privileged=true --volumes-from 父类名 --name 名字 镜像名
实现继承后,子类和父类一样会和主机互联。
如果父类宕机了,子类依旧和主机互联。
如果父类宕机恢复,在宕机这段时间子类和主机互联的数据在父类依然存在。
基本软件安装
tomcat
注意: 最新版Tomcat需要把webapps删除,将webapps.dist重命名为webapps
mysql
需要添加容器卷,解决乱码,备份等问题,
docker run -d -p 3306:3306 --privileged=true
-v /cqx/mysql/log:/var/log/mysql
-v /cqx/mysql/data:/var/lib/mysql
-v /cqx/mysql/conf:/etc/mysql/conf.d
-v /cqx/mysql/mysql-files:/var/lib/mysql-files
-e MYSQL_ROOT_PASSWORD=123456 --name mysql
mysql:5.7
注意要加入mysql-files,MySQL服务器配置了--secure-file-priv
参数用于限制可以从哪个目录导入/导出文件。默认情况下,MySQL服务器设置了--secure-file-priv
参数指向/var/lib/mysql-files/
目录,这个目录是MySQL服务器可以读写的目录。
方法1: 新建一个mysql-files目录用于存储文件
-v /cqx/mysql/mysql-files:/var/lib/mysql-files
方法2: 宿主机自定义mysql配置文件my.cnf,并将自定义的配置文件挂载到容器中的MySQL配置目录
redis
现在主机修改配置文件,指定redis配置
docker run -p 6379:6379 --name myr3 --privileged=true
-v /app/redis/redis.conf:/etc/redis/redis.conf
-v /app/redis/data:/data -d
redis:6.0.8 redis-server /etc/redis/redis.conf
1~2亿条数据需要缓存,请问如何设计这个存储
方案1:哈希取余分区
直接用hash(key) % N个机器台数
简单粗暴,但是原来规划好的节点,进行扩容或者缩容就比较麻烦
方案2: 一致性哈希算法分区
构建一致性哈希环
对2^32取模,简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环
服务器ip节点映射
通过ip地址的哈希函数计算服务器在哈希环上的位置
key落到服务器的落键
当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
方案3: 哈希槽
edis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。如下代码,key之A 、B在Node2, key之C落在Node3上
slot = CRC16(key) % 16384。
mysql主从配置
新建主机实例
docker run -p 3307:3306 --name mysql-master \-v /mydata/mysql-master/log:/var/log/mysql \-v /mydata/mysql-master/data:/var/lib/mysql \-v /mydata/mysql-master/conf:/etc/mysql \-e MYSQL_ROOT_PASSWORD=root \-d mysql:5.7
新建主机配置文件
[mysqld]## 设置server_id,同一局域网中需要唯一server_id=101 ## 指定不需要同步的数据库名称binlog-ignore-db=mysql ## 开启二进制日志功能log-bin=mall-mysql-bin ## 设置二进制日志使用内存大小(事务)binlog_cache_size=1M ## 设置使用的二进制日志格式(mixed,statement,row)binlog_format=mixed ## 二进制日志过期清理时间。默认值为0,表示不自动清理。expire_logs_days=7 ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致slave_skip_errors=1062
主机容器实例内创建数据同步用户
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
新建从机实例
docker run -p 3308:3306 --name mysql-slave \-v /mydata/mysql-slave/log:/var/log/mysql \-v /mydata/mysql-slave/data:/var/lib/mysql \-v /mydata/mysql-slave/conf:/etc/mysql \-e MYSQL_ROOT_PASSWORD=root \-d mysql:5.7
新建从机配置文件
[mysqld]## 设置server_id,同一局域网中需要唯一server_id=102## 指定不需要同步的数据库名称binlog-ignore-db=mysql ## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用log-bin=mall-mysql-slave1-bin ## 设置二进制日志使用内存大小(事务)binlog_cache_size=1M ## 设置使用的二进制日志格式(mixed,statement,row)binlog_format=mixed ## 二进制日志过期清理时间。默认值为0,表示不自动清理。expire_logs_days=7 ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致slave_skip_errors=1062 ## relay_log配置中继日志relay_log=mall-mysql-relay-bin ## log_slave_updates表示slave将复制事件写进自己的二进制日志log_slave_updates=1 ## slave设置为只读(具有super权限的用户除外)read_only=1
- 在主数据库中查看主从同步状态: show master status;
主机容器实例内创建数据同步用户
change master to master_host='宿主机ip', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;
master_host:主数据库的IP地址;master_port:主数据库的运行端口;master_user:在主数据库创建的用于同步数据的用户账号;master_password:在主数据库创建的用于同步数据的用户密码;master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;master_connect_retry:连接失败重试的时间间隔,单位为秒。
在从数据库中查看主从同步状态
show slave status \G;
从数据库容器实例中开启主从同步
start slave
3主3从redis配置
新建六台redis容器实例
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
随便进入一台容器,配置主从关系
redis-cli --cluster create 192.168.100.149:6381 192.168.100.149:6382 192.168.100.149:6383 192.168.100.149:6384 192.168.100.149:6385 192.168.100.149:6386 --cluster-replicas 1
// --cluster-replicas 1 表示为每个master创建一个slave节点 ip地址填linux主机地址
- 查看集群信息: cluster info
- 查看集群状态: cluster nodes
ps: 由于使用插槽机制,会为每个key使用Crc算法计算出特定值,将特定key放到特定槽上。如果以单机形式启动redis容器,可能会有某些key所对应的Crc值不属于这台redis容器的槽。
也就是需要以集群方式启动redis容器,在命令后加入参数-c
例:redis-cli -p 6381 -c
- 查看集群信息: redis-cli --cluster check ip:集群内中任一端口
在前面的redis 3主3从前提下 进行扩容(添加主6387 从6388)
新建主6387和从6388两个节点
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388docker ps
将主机加入到集群(未分配槽)
将新增的6387作为master节点加入集群
redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:63816387
就是将要作为master新增节点6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群
为6387主机分配槽
重新分派槽号命令:
redis-cli --cluster reshard IP地址:端口号
需要给该主机移动多少单位的槽
接收分配槽的主机id是什么
分配方式:
all 每个主机均匀分配一部分给该主机
done 输入要匀出槽位的主机的id
将从机6388设为主机6387的从节点
命令:
redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID
在前面的redis 4主4从前提下 进行缩容(删除主6387 从6388)
先删除从机6388
因为从机为读操作,先删主机可能部分写操作会丢失
命令:
redis-cli --cluster del-node ip:从机端口 从机6388节点ID
清除主机槽位
redis-cli --cluster reshard 192.168.111.147:6381
删除主机6388
命令:redis-cli --cluster del-node ip:端口 6387节点ID
DockerFile
Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
DockerFile,镜像,容器三者之间的关系
构建步骤
- 编写DockerFile文件
- dokcer build命令构建镜像
- docker run允许镜像实例
Dockerfile内容基础知识
1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2:指令按照从上到下,顺序执行
3:#表示注释
4:每条指令都会创建一个新的镜像层并对镜像进行提交
DockerFile常用保留字指令
FROM
基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from
MAINTAINER
镜像维护者的姓名和邮箱地址
RUN
容器构建时需要运行的命令,RUN是在 docker build 构建(DockerFile -> 镜像)时运行
格式1: shell
RUN <命令行命令>
命令行命令等同于在终端的shell命令
例:
RUN yum -y install vim
格式2:exec
RUN [“可执行文件”,“para1”,“para2”]
例: RUN [“./a.php”,“dev”,“offline”] 等价于 RUN ./a.php dev offline
RUN yum -y install vim 等价于 RUN ["yum", "-y", "install", "vim"]
EXPOSE
当前容器对外暴露出的端口
WORKDIR
指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
USER
指定该镜像以什么样的用户去执行,如果都不指定,默认是root
ENV
用来在构建镜像过程中设置环境变量
例:
设置环境变量
ENV MY_PATH /usr/mytest
使用环境变量
WORKDIR $MY_PATH
COPY
拷贝宿主机的文件和目录到镜像中。
将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
写法1:COPY src dest
写法2:COPY ["src", "dest"]
<src源路径>:源文件或者源目录
<dest目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
ADD
将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包,可视为有解压功能的COPY
ADD 文件 文件在容器实例的路径/文件名
VOLUME
容器数据卷,用于数据保存和持久化工作
VOLUME /PATH
VOLUME /data
容器的/data会和宿主机的/host/data同步,也就是/host/PATH所写的地址
CMD
指定容器启动后的要干的事情,CMD是在docker run 时运行。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
CMD[“<para1>”,"<para2>"]
例:
CMD ["nginx", "-g", "daemon off;"]
在linux终端对应的命令
nginx -g "daemon off;"
CMD ["java", "-jar", "myapp.jar"]
在linux终端对应的命令
java -jar myapp.jar
如果在docker run上加入相关参数的话,CMD的内容会被docker run的参数覆盖
tomcat最后一个CMD是 "CMD["catalina.sh",run]"
输入启动命令: docker run -it -p 8080:8080 tomcat /bin/bash
因为加入了/bin/bash操作,可视为加了CMD["/bin/bash","run"]
此时运行localhost:8080,无法正常访问
ENTRYPOINT
也是用来指定一个容器启动时要运行的命令,类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序
格式:
ENTRYPOINT ["<exec>","<para1>","<para2>"]
ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成
假设有nginx:test镜像
ENTRYPOINT ["nginx","-c"] # 定参
CMD ["/nginx/nginx.conf"]# 变参
对应到linux终端则是
nginx -c /nginx/nginx.conf
但如果在命令上传入了和ENTRYPOINT的相关操作,则会覆盖ENTRYPOINT
例:
docker run -it nginx:test .etc/nginx/my.conf
对应到linux终端则是
nginx -c /nginx/my.conf
自定义ubuntu镜像
创建Dockerfile文件
注意名字必须是Dockerfile
FROM ubuntu
MAINTAINER cqx<mnixqc@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get -y install vim
RUN apt-get install net-tools
RUN apt-get install -y iproute2
RUN apt-get install -y inetutils-ping
EXPOSE 80
CMD echo $MYPATH
CMD echo "install inconfig cmd into ubuntu success--------------ok"
CMD /bin/bash
构建Dockerfile
- docker build -t 新镜像名字:TAG .
例:docker build -t myubuntu:777 .
运行镜像容器
- docker run -it 新镜像名字:TAG
Docker微服务实战案例
1.用maven将项目打包成.jar文件发送到linux
2.编写Dockerfile
例:
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
#在容器中执行命令,创建一个空文件 /zzyy_docker.jar 运行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]
#暴露6001端口作为微服务
EXPOSE 6001
3.构建镜像
4.运行容器并测试
Dokcer网络
docker启动后,会产生一个名为docker0的虚拟网桥,用于宿主机和各个容器内的通信
docker容器内部的ip是有可能会发生改变的,所以需要配置网络服务
虚拟网桥 docker0 是 Docker 在安装时创建的一个默认的网络桥接口,它具有以下作用:
连接 Docker 容器和宿主机网络:docker0 充当了 Docker 容器和宿主机之间的网桥,使得容器可以与宿主机进行通信。当 Docker 容器启动时,它们会连接到 docker0 网桥,从而实现容器和宿主机之间的网络通信。
为容器分配 IP 地址:docker0 网桥会为连接到它上的容器分配 IP 地址。Docker 在启动容器时,会自动为容器分配一个 IP 地址,这个 IP 地址是在 docker0 网桥的子网范围内的。
实现容器间通信:通过 docker0 网桥,不同容器之间也可以进行网络通信。Docker 在创建容器时会为每个容器分配一个虚拟网卡,并将这些虚拟网卡连接到 docker0 网桥上,从而实现容器间的通信。
连接到外部网络:docker0 网桥还允许 Docker 容器访问宿主机所在的网络以及外部网络,实现容器与外部世界的通信。
Docker网络命令
- 查看命令使用方法: docker network --help
- 查看网络: docker network ls
- 查看网络数据源: docker network inspect XXX网络名字
- 删除网络: docker network rm XXX网络名字
Docker网络作用
容器间的互联和通信以及端口映射
容器IP变动时候可以通过p服务名直接网络通信而不受到影响
Docker网络模式
bridge模式:使用–network bridge指定,默认使用docker0
host模式:使用–network host指定
none模式:使用–network none指定
container模式:使用–network container:NAME或者容器ID指定
bridge
Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。 # 查看 bridge 网络的详细信息,并通过 grep 获取名称项docker network inspect bridge | grep name ifconfig
1 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。 2 docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址 3 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
3.1 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
3.2 每个容器实例内部也有一块网卡,每个接口叫eth0;
3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
两两匹配验证
host
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。无需宿主机和容器端口映射(无意义)
启动加: --network host
none
禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
启动加: --network none
container
container⽹络模式 新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
启动加: --network container:容器标识
alpine2共享alpine1的网络配置
docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
此时关闭alpine1
自定义网络模式
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
bridege存在的问题:
docker容器内部的ip是有可能会发生改变的,如果ip变动会造成无法连通的问题。
使用bridge模式容器间用ip可以ping通,但通过服务名不能ping通
- 创建新的网络模式: docker network create networkname
例:
docker run -d -p 8081:8080 --network zzyy_network --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network zzyy_network --name tomcat82 billygoo/tomcat8-jdk8
此时无论ip还是服务名都能互相ping通
Docker-compose容器编排
Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
常见Docker Compose命令
Compose常用命令:
- docker compose -h # 查看帮助
- docker compose up # 启动所有docker compose服务
- docker compose up -d # 启动所有docker compose服务并后台运行
- docker compose down # 停止并删除容器、网络、卷、镜像
- docker compose exec <service_id> # 进入容器实例内部
- docker compose exec <service_id> /bin/bash # 进入容器实例内部执行命令
- docker compose ps # 展示当前docker compose编排过的所有容器
- docker compose top # 展示当前docker compose编排过的容器进程
- docker compose logs <service_id> # 查看容器输出日志
- docker compose config # 检查配置
- docker compose config -q # 检查配置,有问题才有输出
- docker compose restart # 重启服务
- docker compose start # 启动服务
- docker compose stop # 停止服务
1.编写docker-compose.yml文件
由于配置了自定义网络模式,所以微服务项目中不用写死mysql,redis的ip。可通过服务名:端口方式访问
version: "3"
services:
microService:
image: zzyy_docker:1.6
container_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- atguigu_net
depends_on:
- redis
- mysql
# 相当于 docker run -d --name ms01 -p 6001:6001 -v /app/microService:/data --network=atguigu_net --depends-on redis --depends-on mysql zzyy_docker:1.6
redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- atguigu_net
command: redis-server /etc/redis/redis.conf
# 相当于 docker run -d --name redis -p 6379:6379 -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data --network=atguigu_net redis:6.0.8 redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2021'
MYSQL_USER: 'zzyy'
MYSQL_PASSWORD: 'zzyy123'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- atguigu_net
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
# 相当于: docker run -d --name mysql -e MYSQL_ROOT_PASSWORD='123456' -e MYSQL_ALLOW_EMPTY_PASSWORD='no' -e MYSQL_DATABASE='db2021' -e MYSQL_USER='zzyy' -e MYSQL_PASSWORD='zzyy123' -p 3306:3306 -v /app/mysql/db:/var/lib/mysql -v /app/mysql/conf/my.cnf:/etc/my.cnf -v /app/mysql/init:/docker-entrypoint-initdb.d --network=atguigu_net mysql:5.7 --default-authentication-plugin=mysql_native_password
networks:
atguigu_net:
指定启动顺序
用depend_on 指定,指定的服务会比当前服务先启动
version: '3.8'
services:
db:
image: mysql
restart: always
backend:
image: backend_image
depends_on:
- db
restart: always
frontend:
image: frontend_image
depends_on:
- backend
restart: always
在上面的示例中,db 服务会在 backend 服务之前启动,而 backend 服务会在 frontend 服务之前启动。这样,当您使用 docker-compose up 启动这些服务时,Docker Compose 将会按照正确的顺序启动它们。
2.检查并启动容器
- 检查docker compose文件: - docker compose config -q
- 启动所有docker compose服务并后台运行 :docker compose up -d
上面docker compose例子启动其中微服务microservice指定了容器名字ms01
3.停止容器
- 停止服务: docker compose stop
Portainer
Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。
安装
docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
–restart
具体来说,--restart=always
参数告诉Docker引擎,无论在何种情况下,只要Docker守护进程在运行,都会自动重启指定的容器。这意味着,如果容器意外终止或Docker引擎重启,Docker将自动重新启动该容器。
访问
第一次登录需创建admin,访问地址:ip:9000