一、基本概念
1.docker架构
2.docker隔离原理
利用linux内部的隔离原理
- namespace 6项隔离
| namespace | 系统调用参数 | 隔离内容 |
|---|---|---|
| UTS | CLONE_NEWUTS | 主机和域名 |
| IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 |
| PID | CLONE_NEWPID | 进程编号 |
| Network | CLONE_NEWUTS | 网络设备、网络栈、端口等 |
| UTS | CLONE_NEWUTS | 挂载点(文件系统) |
| User | CLONE_NEWUSER | 用户和用户组 |
- cgroups资源限制 (资源限制)
cgroup提供的主要功能如下:
资源限制:限制任务使用的资源总额,并在超过这个 配额 时发出提示
优先级分配:分配CPU时间片数量及磁盘IO带宽大小、控制任务运行的优先级
资源统计:统计系统资源使用量,如CPU使用时长、内存用量等
任务控制:对任务执行挂起、恢复等操作
3.docker安装
参考官网:docs.docker.com/engine/inst…
1.移除旧版本
sudo yum remove docker*
2、设置docker yum源
sudo yum install -y yum-utils
# 官方源地址(慢)
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 阿里云
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
3、安装最新docker engine
sudo yum install docker-ce docker-ce-cli containerd.io
4、安装指定版本docker engine
1,在线安装
#找到所有可用docker版本列表
yum list docker-ce --showduplicates | sort -r
# 安装指定版本,用上面的版本号替换<VERSION_STRING>
sudo yum install docker-ce-<VERSION_STRING>.x86_64 docker-ce-cli-
<VERSION_STRING>.x86_64 containerd.io
#例如:
#yum install docker-ce-3:20.10.5-3.el7.x86_64 docker-ce-cli-3:20.10.5-
# 3.el7.x86_64 containerd.io
#注意加上 .x86_64 大版本号
2,离线安装
download.docker.com/linux/cento…
rpm -ivh xxx.rpm
可以下载 tar
解压启动即可
5,启动服务
systemctl start docker
systemctl enable docker
6,镜像加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://82m9ar63.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
#以后docker下载直接从阿里云拉取相关镜像
/etc/docker/daemon.json 是Docker的核心配置文件。
二,命令复习
1.常见命令
所有Docker命令手册 docs.docker.com/engine/refe…
| 命令 | 作用 |
|---|---|
| attach | 绑定到运行中容器的 标准输入, 输出,以及错误流(这样似乎也能进入容器内容,但是一定小心,他们操作的就是控制台,控制台的退出命令会生效,比如redis,nginx...) |
| build | 从一个 Dockerfile 文件构建镜像 |
| commit | 把容器的改变 提交创建一个新的镜像 |
| cp | 容器和本地文件系统间 复制 文件/文件夹 |
| create | 创建新容器,但并不启动(注意与docker run 的区分)需要手动启动。start\stop |
| diff | 检查容器里文件系统结构的更改【A:添加文件或目录 D:文件或者目录删除 C:文件或者目录更改】 |
| events | 获取服务器的实时事件 |
| exec | 在运行时的容器内运行命令 |
| export | 导出容器的文件系统为一个tar文件。commit是直接提交成镜像,export是导出成文件方便传输 |
| history | 显示镜像的历史 |
| images | 列出所有镜像 |
| import | 导入tar的内容创建一个镜像,再导入进来的镜像直接启动不了容器。 |
| /docker-entrypoint.sh nginx -g 'daemon ow ;'docker ps --no-trunc 看下之前的完整启动命令再用他 | |
| info | 显示系统信息 |
| inspect | 获取docker对象的底层信息 |
| kill | 杀死一个或者多个容器 |
| load | 从 tar 文件加载镜像 |
| login | 登录Docker registry |
| logout | 退出Docker registry |
| logs | 获取容器日志;容器以前在前台控制台能输出的所有内容,都可以看到 |
| pause | 暂停一个或者多个容器 |
| port | 列出容器的端口映射 |
| ps | 列出所有容器 |
| pull | 从registry下载一个image 或者repository |
| push | 从一个 Dockerfile 文件构建镜像 |
| rename | 从一个 Dockerfile 文件构建镜像 |
| restart | 重启一个或者多个容器 |
| rm | 从一个 Dockerfile 文件构建镜像 |
| rmi | 移除一个或者多个镜像 |
| run | 创建并启动容器 |
| save | 把一个或者多个镜像保存为tar文件 |
| search | 去docker hub寻找镜像 |
| start | 启动一个或者多个容器 |
| stats | 显示容器资源的实时使用状态 |
| tag | 给源镜像创建一个新的标签,变成新的镜像 |
| top | 显示正在运行容器的进程 |
| pause | pause的反操作 |
| update | 更新一个或者多个docker容器配置 |
| version | Show the Docker version information |
| container | 管理容器 |
| image | 管理镜像 |
| network | 管理网络 |
| volume | 管理卷 |
# 镜像是怎么做成的。基础环境+软件
redis的完整镜像应该是: linux系统+redis软件
# 以后自己选择下载镜像的时候尽量使用
alpine: slim:
docker rmi -f $(docker images -aq) #删除全部镜像
docker image prune #删除游离镜像 dangling:游离镜像(没有镜像名称)
docker tag 原镜像:标签 新镜像名称:标签 #重命名
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
docker create [设置项] 镜像名 [启动] [启动参数...]
docker create redis: 按照redis:latest镜像启动一个容器
# docker run 镜像名 默认在前台启动, 一般加 -d 在后台移动 docker run -d
# docker create 只是创建容器 docker start 容器启动
# docker run -d = docker create + docker start
docker kill是强制kill -9(直接拔电源);
docker stop可以允许优雅停机(当前正在运行中的程序处理完所有事情后再停止)
进容器:
docker attach 绑定的是控制台. 可能导致容器停止。
docker exec -it -u 0:0 --privileged mynginx4 /bin/bash: 0用户,以特权方式进入容器
查看详情:docker container inspect 容器名 = docker inspect 容器
查看容器更改:docker diff 容器名
# 一般运行中的容器会常年修改,我们要使用最终的新镜像
docker commit -a leifengyang -m "first commit" mynginx4 mynginx:v4
---------------export/import 操作容器--------------
docker export -o nginx.tar 容器id
# export 会将容器导出为文件
docker import nginx.tar mynginx:v4
# import 会将文件导入为镜像,但镜像并不能直接启动,需要容器的command,查看命令(docker ps --no-trunc)
----save/load--操作镜像--
docker save -o busybox.tar busybox:latest 把busybox镜像保存成tar文件
docker load -i busybox.tar 把压缩包里面的内容直接导成镜像
----镜像为什么能长久运行-----------
镜像启动一定得有一个阻塞的进程,一直干活,在这里代理。
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
镜像启动以后做镜像里面默认规定的活。通过inspect查看镜像的Cmd和Entrypoint
---构建镜像-------
1.准备Dockerfile文件
2.编写Dockerfile
3.构建镜像
# docker build -t mybusy66:v6 -f Dockerfile
-
容器状态
created(新建),up(运行中),pause(暂停),exited(退出)
-
推送镜像
- 注册docker hub并登录
- 可以创建一个仓库,选为public
- docker push leifengyang/mynginx:tagname
- docker hub一个完整镜像的全路径是 docker.io/library/redis:alpine3.13 我们的docker.io/leifengyang/mynginx:tagname
- docker images的时候镜像缩略了全名 默认官方镜像没有docker.io/library/
- docker.io/ rediscommander / redis-commander:latest docker.io/leifengyang/mynginx:v4 我的镜像的全称
- 登录远程docker仓库
- 当前会话登录以后 docker login 。所有的东西都会push到这个人的仓库 docker push leifengyang/mynginx:tagname
- 上面命令的完整版 docker push docker.io/leifengyang/mynginx:v4
- 怎么知道是否登录了 cat ~/.docker/config.json 有没有 auth的值,没有就是没有登录
2.典型命令
1.docker run
常用关键参数 OPTIONS 说明:
- -d: 后台运行容器,并返回容器ID;
- -i: 以交互模式运行容器,通常与 -t 同时使用;
- -P: 随机端口映射,容器内部端口随机映射到主机的端口
- -p:指定端口映射,格式为:主机(宿主)端口:容器端口
- -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用
- --name="nginx-lb":为容器指定一个名称;
- --dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
- --dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
- -h "mars": 指定容器的hostname;
- -e username="ritchie": 设置环境变量;
- --env-file=[]: 从指定文件读入环境变量;
- --cpuset="0-2" or --cpuset="0,1,2": 绑定容器到指定CPU运行;
- -m :设置容器使用内存最大值;
- --net="bridge": 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
- --link=[]: 添加链接到另一个容器;
- --expose=[]: 开放一个端口或一组端口;
- --restart , 指定重启策略,可以写--restart=awlays 总是故障重启
- --volume , -v: 绑定一个卷。一般格式 主机文件或文件夹:虚拟机文件或文件夹
如何在docker中部署组件
1.部署nginx
# 注意 外部的/nginx/conf下面的内容必须存在,否则挂载会覆盖
docker run --name nginx-app \
-v /app/nginx/html:/usr/share/nginx/html:ro \
-v /app/nginx/conf:/etc/nginx
-d nginx
2.部署mysql
# 5.7版本
docker run -p 3306:3306 --name mysql57-app \
-v /app/mysql/log:/var/log/mysql \
-v /app/mysql/data:/var/lib/mysql \
-v /app/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
#8.x版本,引入了 secure-file-priv 机制,磁盘挂载将没有权限读写data数据,所以需要将权限透传,或者chmod -R 777 /app/mysql/data
# --privileged 特权容器,容器内使用真正的root用户
docker run -p 3306:3306 --name mysql8-app \
-v /app/mysql/conf:/etc/mysql/conf.d \
-v /app/mysql/log:/var/log/mysql \
-v /app/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--privileged \
-d mysql
3.部署redis
# 提前准备好redis.conf文件,创建好相应的文件夹。如:
port 6379
appendonly yes
#更多配置参照 https://raw.githubusercontent.com/redis/redis/6.0/redis.conf
docker run -p 6379:6379 --name redis \
-v /app/redis/redis.conf:/etc/redis/redis.conf \
-v /app/redis/data:/data \
-d redis:6.2.1-alpine3.13 \
redis-server /etc/redis/redis.conf --appendonly yes
4.部署elasticsearch
#准备文件和文件夹,并chmod -R 777 xxx
#配置文件内容,参照
https://www.elastic.co/guide/en/elasticsearch/reference/7.5/node.name.html 搜索相关配置
# 考虑为什么挂载使用esconfig ...
docker run --name=elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m" \
-v /app/es/data:/usr/share/elasticsearch/data \
-v /app/es/plugins:/usr/shrae/elasticsearch/plugins \
-v esconfig:/usr/share/elasticsearch/config \
-d elasticsearch:7.12.0
5.部署tomcat
# 考虑,如果我们每次 -v 都是指定磁盘路径,是不是很麻烦?
docker run --name tomcat-app -p 8080:8080 \
-v tomcatconf:/usr/local/tomcat/conf \
-v tomcatwebapp:/usr/local/tomcat/webapps \
-d tomcat:jdk8-openjdk-slim-buster
6.重启策略
no,默认策略,在容器退出时不重启容器
on-failure,在容器非正常退出时(退出状态非0),才会重启容器
on-failure:3,在容器非正常退出时重启容器,最多重启3次
always,在容器退出时总是重启容器
unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
三,网络和存储原理
1.docker存储
1.Images and layers
Docker镜像由一系列层组成,每层代表镜像的Dockerfile中的一条指令。除最后一层外的每一层都是可读的。
2.Container and layers
-
容器和镜像之间的主要区别是可写顶层。 -
在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。 -
删除容器后,可写层也会被删除。 基础图像保持不变。 因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础映像的访问,但具有自己的数据状态。
下图显示了共享同一Ubuntu 15.04镜像的多个容器。
3.磁盘容量预估
docker ps -s
size:用于每个容器的可写层的数据量(在磁盘上)。
virtual size:容器使用的用于只读图像数据的数据量加上容器的可写图层大小。
多个容器可以共享部分或全部只读图像数据。
从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层。 因此,不能只对虚拟大小进行总计。这高估了总磁盘使用量,可能是一笔不小的数目
4.镜像如何挑选
- busybox:是一个集成了一百多个最常用Linux命令和工具的软件。
- alpine:Alpine操作系统是一个面向安全的轻型Linux发行版经典最小镜像,基于busybox,功能比Busybox完善。
- slim:docker hub中有些镜像有slim标识,都是瘦身了的镜像。也要优先选择
无论是制作镜像还是下载镜像,优先选择alpine类型.
5. copy on write (写时复制)
需要对容器中文件进行修改时,先复制镜像中的一份,在进行修改,修改完成后保存在工作层,下次读取时,会优先读取该层,当没有文件时,在读取镜像层。
2.容器如何挂载
-
Volumes(卷) :存储在主机文件系统的一部分中,该文件系统由Docker管理(在Linux上是“ / var / lib / docker / volumes /”)。 非Docker进程不应修改文件系统的这一部分。 卷是在Docker中持久存 储数据的最佳方法。
-
Bind mounts(绑定挂载) 可以在任何地方 存储在主机系统上。 它们甚至可能是重要的系统文件或目录。 Docker主机或Docker容器上的非Docker进程可以随时对其进行修改。
-
tmpfs mounts(临时挂载) 仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统
1.volume(卷)
- 匿名卷: -v 容器绝对路径
-v /usr/share/nginx/html
- 具名卷: -v 不以/开头的路径:docker容器内部绝对路径
-v html:/usr/share/nginx/html
# 注意空挂载问题。 -v 绝对路径:容器绝对路径 会导致空挂载问题
2.docker网络
1.网络模式
| 网络模式 | 配置 | 说明 |
|---|---|---|
| bridge模式 | --net=bridge | 默认值,在Docker网桥docker0上为容器创建新的网络栈 |
| none模式 | --net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
| container模式 | --net=container:name/id | 容器和另外一个容器共享Networknamespace。kubernetes中的pod就是多个容器共享一个Networknamespace。 |
| host模式 | --net=host | 容器和宿主机共享Network namespace; |
| 用户自定义 | --net=mynet | 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
2.桥接模式
3.自定义网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
所有东西实时维护好,直接域名ping通
使用:docker run -d -P --network 自定义网络名(提前创建)
四 如何构建Dockerfile
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。一般而言,Dockerfile可以分为四部分 基础镜像信息 维护者信息 镜像操作指令 启动时执行指令
镜像构建命令:docker build -t mybusy66:v6 -f Dockerfile .
例:
| 指令 | 说明 |
|---|---|
| FROM | 指定基础镜像 |
| LABEL | 指定生成镜像的元数据标签信息 |
| RUN | 运行命令 |
| CMD | 指定启动容器时默认的命令 |
| ENTRYPOINT | 指定镜像的默认入口.运行命令 |
| EXPOSE | 声明镜像内服务监听的端口 |
| ENV | 指定环境变量,可以在docker run的时候使用-e改变 |
| ADD | 复制指定的src路径下的内容到容器中的dest路径下,src可以为url会自动下载,可以为tar文件,会自动解压 |
| COPY | 复制本地主机的src路径下的内容到镜像中的dest路径下,但不会自动解压等 |
| VOLUME | 创建数据卷挂载点 |
| USER | 指定运行容器时的用户名或UID |
| WORKDIR | 配置工作目录,为后续的RUN、CMD、ENTRYPOINT指令配置工作目录 |
| ARG | 指定镜像内使用的参数(如版本号信息等),可以在build的时候,使用--build-args改变 |
| OBBUILD | 配置当创建的镜像作为其他镜像的基础镜像是,所指定的创建操作指令 |
| STOPSIGNAL | 容器退出的信号值 |
| HEALTHCHECK | 健康检查 |
| SHELL | 指定使用shell时的默认shell类型 |
1.FROM
FROM 指定基础镜像,最好挑一些apline,slim之类的基础小镜像.
scratch镜像是一个空镜像,常用于多阶段构建
如何确定我需要什么要的基础镜像?
- Java应用当然是java基础镜像(SpringBoot应用)或者Tomcat基础镜像(War应用)
- JS模块化应用一般用nodejs基础镜像
- 其他各种语言用自己的服务器或者基础环境镜像,如python、golang、java、php等
2.LABEL
标注一些镜像的说明信息。 kv形式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
3.RUN
-
RUN指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。
RUN <command> ( shell 形式, /bin/sh -c 的方式运行,避免破坏shell字符串) RUN ["executable", "param1", "param2"] ( exec 形式) # RUN 的上下文没有关系,每个指令都是从头开始
4.CMD
CMD 的三种写法:
CMD ["executable","param1","param2"] ( exec 方式, 首选方式)
CMD ["param1","param2"] (为ENTRYPOINT提供默认参数)
CMD command param1 param2 ( shell 形式)
#docker run启动参数会覆盖CMD内容
5.ARG和ENV
1.ARG
-
ARG指令定义了一个变量,用户可以在构建时使用--build-arg = 传递,docker build命令会将其传递给构建器。
-
--build-arg 指定参数会覆盖Dockerfile 中指定的同名参数
-
如果用户指定了 未在Dockerfile中定义的构建参数 ,则构建会输出 警告 。
-
ARG只在构建期有效,运行期无效
2.ENV
- 在构建阶段中所有后续指令的环境中使用
- 容器运行时ENV值可以生效 docker run -env 可修改这些值
- 构建期和运行期都有效,但构建期不能修改,运行期可以修改
6.ADD 和 COPY
1.ADD
将上下文context指定的内容添加到镜像中。如果是压缩包,会自动解压,如果是远程文件,会自动下载
# 注: docker build 后面的 . 指的就是上下文环境
2.COPY
将上下文context指定的内容复制到镜像中
7.WORKDIR和VOLUME
1.WORKDIR
- 为Dockerfile指定工作目录,其后所有的镜像构建,都是默认在该工作目录上进行的。
- 多次使用WORKDIR,工作目录会被嵌套。 如 /app/aaa
- 当进入镜像构建的容器时,会默认进入到工作目录
2.VOLUME
作用:将容器的文件挂载到容器外部。
#注:假设指定了挂载目录,在之后对挂载目录的所有操作,都会被弃用。挂职之前对目录的操作不会弃用。
挂载后的修改,在将容器提交为镜像,会导致挂载修改的内容丢失。
8.multi-stage builds
多阶段构建
1.使用
FROM maven #下载maven
WORKDIR /app # 创建镜像的工作目录
COPY . . # 将当前宿主机下所有文件复制到 /app 目录下
RUN mvn clean package # 打包
RUN mv target/*.jar springbootdocker.jar # 将打好的jar包 移动到 /app 下
CMD java -jar springbootdocker.jar # 运行java程序
2.生产示例
#以下所有前提 保证Dockerfile和项目在同一个文件夹
# 第一阶段:环境构建; 用这个也可以
FROM maven:3.5.0-jdk-8-alpine AS builder # 打包工具maven
WORKDIR /app # 创建工作目录
ADD ./ /app # 将当前目录所有文件添加到 /app 下
RUN mvn clean package -Dmaven.test.skip=true # maven打包,跳过测试
# 第二阶段,最小运行时环境,只需要jre;第二阶段并不会有第一阶段那些没用的层
#基础镜像没有 jmap; jdk springboot-actutor(jdk)
FROM openjdk:8-jre-alpine # 加载jre环境
LABEL maintainer="534096094@qq.com" # 作者信息
# 从上一个阶段复制内容
COPY --from=builder /app/target/*.jar /app.jar
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo'Asia/Shanghai' >/etc/timezone && touch /app.jar
# 环境变量
ENV JAVA_OPTS=""
ENV PARAMS=""
# 运行jar包
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom$JAVA_OPTS -jar /app.jar $PARAMS" ]
3。案例
自己 写一个多阶段构建
1、自动从git下载指定的项目
2、把项目自动打包生成镜像
3、我们只需要运行镜像即可
FROM bitnami/git:latest AS gitclone
WORKDIR /app
RUN git clone https://gitee.com/xinyunbian/spring-dockerfile.git
RUN ls
FROM maven AS mavenpackage
COPY --from=gitclone /app/* /app/
WORKDIR /app
RUN ls
RUN mvn clean package -Dmaven.test.skip=true
RUN ls
RUN mv target/*.jar app.jar
RUN ls
FROM openjdk:8-jre-alpine
LABEL maintainer="534096094@qq.com"
COPY --from=mavenpackage /app/app.jar /app/
WORKDIR /app
RUN ls
ENTRYPOINT ["sh" "-c" "java -jar app.jar" ]
4.springboot最终写法
FROM openjdk:8-jre-alpine
LABEL maintainer="534096094@qq.com"
COPY target/*.jar /app.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo'Asia/Shanghai' >/etc/timezone && touch /app.jar
ENV JAVA_OPTS=""
ENV PARAMS=""
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom
$JAVA_OPTS -jar /app.jar $PARAMS" ]
# 运行命令 docker run -e JAVA_OPTS="-Xmx512m -Xms33 -" -e PARAMS="--spring.profiles=dev --server.port=8080" -jar /app/app.jar