文章目录
docker介绍
虚拟机:虚拟机是通过Hypervisor(虚拟机管理系统,常见的有VMWare workstation、VirtualBox),虚拟出网卡、cpu、内存等虚拟硬件,再在其上建立虚拟机,每个虚拟机是个独立的操作系统,拥有自己的系统内核。
容器:容器是利用namespace将文件系统、进程、网络、设备等资源进行隔离,利用cgroup对权限、cpu资源进行限制,最终让容器之间互不影响,容器无法影响宿主机。
docker的优势
运行在容器上的docker的程序,直接使用的都是宿主机的硬件资源,因此在cpu、内存、利用率上,Docker将会在效率上具有更大的优势。
Docker直接利用宿主机的系统内核,避免了虚拟机启动时所需要的系统引导时间和操作系统运行的资源消耗,利用Docker能够在几秒钟之内启动大量的容器,是虚拟机无法办到的。快速启动低资源消耗的优点,使Docker在弹性云平台自动运维系统方面具有很好的应用场景。
docker的安装
docker的架构
docker相关命令
服务相关命令:
镜像相关命令:
docker search 镜像
docker search --limit 5#显示前5个镜像
docker images#展示所有镜像信息
docker images -a #只展示镜像id信息
docker system df #查看镜像/容器/数据卷所占的空间
容器相关命令:
docker ps [option]
-a:列出当前所有正在运行的容器+历史上运行过的
-l:显示最近创建的容器。
-n:显示最近n个创建的容器。
-q:静默模式,只显示容器编号。
docker rm -f 容器id或名字
docker ps -a -q | xargs docker rm #把第一个获取到的结果作为第二个的参数
docker run [options]
#--name="容器新名字" 为容器指定一个名称;
#-d:后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
#-i:以交互模式运行容器,通常与-t同时使用;
#-t:为容器重新分配一个伪输入终端,通常与-i同时使用;也即启动交互式容器(前台有伪终端,等待交互);
#-P:随机端口映射,大写P
#-p:指定端口映射,小写p
很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程。
容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,
我们配置启动服务只需要启动响应的service即可。例如service nginx start但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀因为他觉得他没事可做了.
所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行,常见就是命令行模式,表示我还有交互操作,别中断,o(n_n)o哈哈~
解决办法:
docker run -it redis:6.0.8 #前台交互式启动
docker run -d redis:6.0.8 #后台守护式启动
docker top 容器名或id #查看容器内运行的进程
docker inspect 容器ID #查看容器内部细节
docker exec -it 容器ID bashShell
docker attach 容器ID
#两个的区别
#attach 直接进入容器启动命令的终端,不会启动新的进程用exit退出,会导致容器的停止。
#exec是在容器中打开新的终端,并且可以启动新的进程用exit退出,不会导致容器的停止。
export导出容器的内容留作为一个tar归档文件[对应import命令]
import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
docker export 容器ID>文件名.tar
cat 文件名.tar | docker import-镜像用户/镜像名:镜像版本号
docker commit 提交容器副本使之成为一个新的镜像
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
docker commit -m="vim cmd add ok" -a="yogen" 容器ID ubantu:1.0
数据卷相关命令:
特点:
1:数据卷可在容器之间共享或重用数据
2:卷中的更改可以直接实时生效
3:数据卷中的更改不会包含在镜像的更新中
4:数据卷的生命周期一直持续到没有容器使用它为止
Docker挂载主机目录访问如果出现cannot open directory .: Permission denied
解决办法:在挂载目录后多加一个–privileged=true参数即可
docker run -it --privileged=true -v /宿主机绝对目录:/容器内目录 镜像名
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data --name=u1 ubuntu
docker run - it -- privileged=true -v /mydocker/u: /tmp:ro ubuntu
#限制容器内部映射的文件readonly,宿主机可以正常写
继承容器卷
docker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu
应用部署
在mysql/conf下创建my.cnf,添加内容如下:
[mysqld]
default_authentication_plugin=mysql_native_password
character-set-server=utf8mb4
bind-address=0.0.0.0
port=3306
default-storage-engine=INNODB
skip-grant-tables
[mysql]
default-character-set=utf8mb4
[client]
default-character-set=utf8mb4
docker run -id -p 3306:3306 --name=myblog -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=****** mysql:5.6
docker exec -it 8b2f34f85319 /bin/bash
mysql -uroot -p
niginx:
docker run -id -p 80:80 -v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf -v $PWD/logs:/var/log/nginx -v $PWD/html:/usr/share/nginx/html nginx
nginx.conf文件
以访问http://192.168.13.130转到其3000端口为例
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server{
listen 80;
server_name 192.168.13.130;
location / {
proxy_pass http://192.168.13.130:3000;
}
}
}
redis的安装
docker pull redis
//将本地下载的redis的redis.conf文件上传到虚拟机的/redis/conf下
docker run -p 6379:6379 --name redis -v /redis/conf/redis.conf:/etc/redis/redis.conf -v /data/redis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
//进入redis命令行
docker exec -it redis redis-cli
在docker上部署运行go项目
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(context *gin.Context) {
context.JSON(200, gin.H{
"message": "hello world!",
})
})
r.Run()
}
Dockerfile
FROM golang:1.18-alpine
#为镜像设置必要的环境变量
ENV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=am64
#工作目录
WORKDIR /project/go-docker/
#下载依赖
COPY go.* ./
RUN go mod download
#编译
COPY . .
RUN go build -o /project/go-docker/build/myapp .
EXPOSE 8080
ENTRYPOINT ["/project/go-docker/build/myapp"]
将文件放到linux下
//启动docker服务
systemctl start docker.service
//部署镜像
docker build -f Dockerfile -t test-go-docker:latest .
//运行镜像
docker run -d -p 8080:8080 test-go-docker:latest
镜像原理
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统。它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual
filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
boots(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统boofs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权己由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs (root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin, /etc等标准目录和文件。roots就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
Dockerfile
Dockerfile基础知识:
1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2:指令按照从上到下,顺序执行
3:#表示注释
4:每条指令都会创建一个新的镜像层并对镜像进行提交
Docker执行Dockerfile的大致流程:
(1)docker从基础镜像运行一个容器
(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层
(4)docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成
基本命令参数:
FROM:基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是
from
MAINTAINER:镜像维护者的姓名和邮箱地址
RUN:容器构建时需要运行的命令,两种格式:shell格式、exec格式。RUN是在docker build时运行
EXPOSE:暴露的端口
WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
USER:指定该镜像以什么样的用户去执行,如果都不指定,默认是root
ENV:用来在构建镜像过程中设置环境变量
VOLUME:容器数据卷,用于数据保存和持久化工作
ADD:将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
COPY:类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中<源路径>的文件/目录复制到新的
一层的镜像内的<目标路径>位置。
COPY src dest
COPY ["src","dest"]
<src源路径>:源文件或者源目录
<dest目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建
CMD:指定容器启动后要干的事情。两种格式:shell格式、exec格式
Dockerfile可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换
RUN是在docker build的时候运行,CMD是在docker run的时候运行
ENTRYPOINT:也是用来指定一个容器启动时要运行的命令,类似于CMD指令,但是ENTRYPOINT不
会被docker run后面的命令覆盖,而且这些命令行参数会被当中参数送给ENTRYPOINT指令指定的程序
ENTRYPOINT可以和CMD一起用,一般是变参才会使用CMD,这里的CMD等于是在给ENTRYPOINT传
参。当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容
作为参数传递给ENTRYPOINT指令,他两个组合会变成“ENTRYPOINT> "<CMD>"
FROM nginx
ENTRYPOINT ["nginx","-c"] #定参
CMD ["/etc/nginx/nginx.conf"] #变参
练习:centos7镜像具备vim+ifconfig+jdk8
需要预先下载jdk压缩包,并在同一文件夹中编写Dockerfile
FROM centos
MAINTAINER yogen<zhangyongjian2021@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装vim编辑器
RUM yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8以及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.har:$JAVA_HOME/lib/tools/jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success-----------ok"
CMD /bin/bash
docker build -t 新镜像名字:TAG .
虚悬镜像
FROM centos
CMD echo 'action is success'
docker build .
docker image prune
#删除虚悬镜像
Network
在CentOS7的安装过程中如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡(virbr0网卡:它还有一个固定的默认IP地址192.168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供NAT访问外网的功能。
我们之前学习Linux安装,勾选安装系统的时候附带了libvirt服务才会生成的一个东西,如果不需要可以直按将libvirtd服务卸载
yum remove libvirt-libs.x86_64
docker network ls
#查看docker的网络
能干嘛?
容器间的互联和通信以及端口映射
容器ip变动时候可以通过服务名直接网络通信而不受到影响
网络模式:
bridge:为每一个容器分配、设置IP等,并将容器连接到一个docker0。虚拟网桥,默认为该模式。
host:容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
none:容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair和网桥
接,IP等。
container:新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范
围等
bridge:
1Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-lP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-lP直接通信。
2docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth……代表网卡一,网卡二,网卡三…… lo代表127.0.0.1,即localhost. inet addr用来表示网卡的IP地址
3网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
1.整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容
器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair) ;
2.每个容器实例内部也有一块网卡,每个接口叫eth0;
3.docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
Docker创建一个容器的时候,会执行如下操作:
1.创建一个虚拟接口/网卡,也就是veth pair,分别放到本地主机和新容器中
2.本地主机一端桥接到默认的docker0或指定网桥上,并具有一个唯一的名字,如vetha596da4
3.容器一端放到新容器中,并修改名字作为eth0,这个网卡/接口只在容器的名字空间可见
4.从网桥可用地址段中(也就是与该bridge对应的network)获取一个空闲地址分配给容器的eth0,并配置默认路由到
桥接网卡vetha596da4
5.会使用docker0的IP地址为容器的默认网关
host:
容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
在创建容器时通过参数–net host或者–network host指定;
采用 host 网络模式的 Docker Container,可以直接使用宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换;
host 网络模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
none:
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo需要我们自己的Docker容器添加网卡、配置IP等。
在创建容器时通过参数 --net none 或者 --network none 指定;
container:
在创建容器时通过参数 --net container:已运行的容器名称|ID 或者 --network container:已运行的容器名称|ID 指定;
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
docker run -d -p 8086:8080 --network container:tomcat85--name tomcat86 billygoo/tomcat8-jdk8
关闭tomcat85,tomcat86也没有了网络
自定义网络:
自定义桥接网络,自定义网络默认使用的是桥接网络bridge
新建自定义网络
新建容器加入上一步新建的自定义网络
互相ping测试
docker network create zzyy_network
# 创建自定义网络
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
这样就能通过服务名ping通
通过 docker network connect 网络名称 容器名称 为容器连接新的网络模式。
通过 docker network disconnect 网络名称 容器名称 命令断开网络。
可以通过 docker network rm 网络名称 命令移除自定义网络模式,网络模式移除成功会返回网络模式名称。
私有仓库
docker和虚拟机比较
docker-compose
Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
通过编写docker-compose.yml,可以快速构建容器
安装:
sudo apt-get update -y
sudo apt-get install docker-compose
两要素:
一个个应用容器实例,比如订单微服务、库存微服务、mysql容器
由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml文件中定义。
三个步骤:
1.编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
2.使用docker-compose.yml定义一个完整业务单元,安排好整体应用中的各个容器服务。
3.最后,执行docker-compose up命令来启动并运行整个应用程序,完成一键部署上线
基本的指令:
version #指定compose的版本
build #指定构建镜像上下文路径
context:上下文路径。
dockerfile:指定构建镜像的 Dockerfile 文件名。
args:添加构建参数,这是只能在构建过程中访问的环境变量。
labels:设置构建镜像的标签。
target:多层构建,可以指定构建哪一层。
command:#覆盖容器启动的默认命令。
container_name#指定自定义容器名称,而不是生成的默认名称。
depends_on#设置依赖关系。被依赖的先启动后停止
deploy#指定与服务的部署和运行有关的配置。只在 swarm 模式下才会有用。
endpoint_mode:#访问集群服务的方式。
labels:#在服务上设置标签。可以用容器上的 labels(跟 deploy 同级的配置) 覆盖 deploy 下的 labels。
mode:#指定服务提供的模式。
replicated:#复制服务,复制指定服务到集群的机器上。
global:#全局服务,服务将部署至集群的每个节点。
environment #添加环境变量。您可以使用数组或字典、任何布尔值,布尔值需要用引号引起来,以确保 YML 解析器不会将其转换为 True 或 False。
expose #暴露端口,但不映射到宿主机,只被连接的服务访问。
extra_hosts #添加主机名映射。类似 docker client --add-host。在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:
image #指定容器运行的镜像
restart#重启策略
no:是默认的重启策略,在任何情况下都不会重启容器。
always:容器总是重新启动。
on-failure:在容器非正常退出时(退出状态非0),才会重启容器。
unless-stopped:在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
volumes #将主机的数据卷或着文件挂载到容器里。
version: '3'
services:
Service:
image: myblog:2
container_name: myblog
ports:
- "3000:3000"
volumes:
- /app/microService:/data
networks:
- my_network
depends_on:
- redis
- mysql
Redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- my_network
command:
- redis-server /etc/redis/redis.conf
Mysql:
image: mysql:5.7
environment: #这是mysql官方提供的针对数据库启动时的配置
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2023'
MYSQL_USER: 'yogen'
MYSQL_PASSWORD: '123456'
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 #这个目录是数据库官方提供的初始目录,以.sql .sh .bat结尾的文件放到这个目录下面,在数据库启动的时候会自动执行。
networks:
- my_network
command:
- --default-authentication-plugin=mysql_native_password #解决外部无法访问
networks:
my_network: #相当于docker network create my_network
这样的话,就可以不用ip地址而使用redis或者mysql作为主机名访问了
docker-compose部署微服务:
# 指定版本号
version: '3'
services:
# 注册中心服务
secondkill-register:
# nacos-1.4.2镜像
image: nacos/nacos-server:1.4.2
# 重启方式:总是
restart: always
# 端口映射
ports:
- 8848:8848
# 容器名
container_name: secondkill-register
# 主机名
hostname: secondkill-register
# 环境变量,设置启动方式为单机启动
environment:
MODE: standalone
# 添加到网路app
networks:
- app
# 数据库服务
secondkill-mysql:
# 数据库对应Dockerfile目录
build:
context: ./db
# 设置数据库密码
environment:
MYSQL_ROOT_PASSWORD: 86598659yu
restart: always
container_name: secondkill-mysql
image: secondkill-mysql
ports:
- 3306:3306
networks:
- app
# redis服务
secondkill-redis:
# redis-3.2镜像
image: redis:3.2
ports:
- 6379:6379
restart: always
container_name: secondkill-redis
hostname: secondkill-redis
networks:
- app
# rabbitmq队列服务
secondkill-rabbitmq:
# rabbitmq-3.8.4镜像
image: rabbitmq:3.8.4
ports:
- 5672:5672
- 15672:15672
restart: always
container_name: secondkill-rabbitmq
hostname: secondkill-rabbitmq
networks:
- app
# 以下是微服务
# 网关服务
secondkill-zuul:
# 网关服务对应Dockerfile路径
build:
context: ./secondkill-zuul
ports:
- 8000:8000
restart: always
container_name: secondkill-zuul
hostname: secondkill-zuul
networks:
- app
# 鉴权服务
secondkill-auth:
# 鉴权服务对应Dockerfile路径
build:
context: ./secondkill-auth
ports:
- 8002:8002
restart: always
container_name: secondkill-auth
hostname: secondkill-auth
networks:
- app
# 商品服务
secondkill-goods:
# 商品服务对应Dockerfile路径
build:
context: ./secondkill-service/secondkill-goods
ports:
- 8021:8021
restart: always
container_name: secondkill-goods
hostname: secondkill-goods
networks:
- app
# 订单服务
secondkill-order:
# 订单服务对应Dockerfile路径
build:
context: ./secondkill-service/secondkill-order
ports:
- 8010:8010
restart: always
container_name: secondkill-order
hostname: secondkill-order
networks:
- app
# 用户服务
secondkill-user:
# 用户服务对应Dockerfile路径
build:
context: ./secondkill-service/secondkill-user
ports:
- 8001:8001
restart: always
container_name: secondkill-user
hostname: secondkill-user
networks:
- app
# 设置网络为app
networks:
app:
利用docker-compose构建镜像
version: "3"
services:
hello:
container_name: hello
build:
context: ./
dockerfile: Dockerfile
docker-compose build
使用docker的注意点
1.不要在容器中存储数据:容器可能会被中断、替换或破坏,在容器中运行的 1.0 版应用程序很容易就会被 1.1 版取代,而不会对数据造成影响或导致数据丢失。所以如果需要存储数据,需要把数据存储在映射卷中。
2.不要从正在运行的容器中创建镜像:不要使用"docker commit"命令来创建镜像。这一镜像创建方法不可复制,因此应完全避免使用。请始终使用 Dockerfile 或其他任何可完全复制的 S21(从源代码到镜像)方法,如此一来,如果存储在源代码控制存储库 (GIT) 中,就可以跟踪 Dockerfile 的变更情况。
3.不要在单个容器中运行一个以上进程:容器只运行一个进程时效果最佳,但如果运行一个以上进程,在管理和检索日志以及单独更新进程时就会遇到很多麻烦。
4.不要在镜像中存储证书及使用环境变量:不要在镜像中对任何用户名/密码进行硬编码操作。请使用环境变量从容器外部检索信息。
5.不要以root权限运行进程:镜像应使用 USER 指令来为容器的运行指定非 root 用户。
6.不要依赖IP地址:每个容器都有自己的内部 IP 地址,如果启动然后停止容器,内部 IP 地址可能会发生变化。如果你的应用程序或微服务需要和另一个容器进行通信,请使用环境变量在容器之间传递相应的主机名和端口。
其它命令
docker top container #查看容器内部的进程信息
docker ps -a #查看容器进程
docker save registry:2.7.1 > registry-2.7.1.tar #打包镜像成.tar文件
docker load < registry-2.7.1.tar #将tar文件导入为镜像
docker history #查看镜像的每一层命令
docker pause && docker unpause #容器暂停和暂停恢复
docker state #查看当前主机下所有容器占用内存和cpu情况
面试题
为什么docker会比vm虚拟机快?
(1 )docker有着比虚拟机更少的抽象层
由于docker不需要Hypervisor(虚拟机)实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的
硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
(2)docker利用的是宿主机的内核,而不需要加载操作系统OS内核
当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。进而避免引寻、加载操作系统内核返回
等比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载OS,返回新建过程是分钟级别的。而docker由于
直接利用宿主机的操作系统,则省略了返回过程,因此新建一个docker容器只需要几秒钟。
谈谈docker虚悬镜像?
仓库名、标签都是<none>的镜像,俗称虚悬镜像dangling image
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M?
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直
接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致
的,rootfs会有差别,因此不同的发行版可以公用bootfs。
Docker镜像层都是只读的,容器层是可写的,当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
docker attach和docker exec进入容器内部的不同?
docker exec相当于进入容器并开启一个新的终端,可以在里面操作。如果使用exit退出,容器也不会停止。
docker attach进入容器正在执行的终端,不会启动新的进程,如果使用exit退出,容器会停止运行。如果想退出容器
但不想容器停止,则按ctril+p+q
卷挂载的两种方式-v和–mount
-v参数:如果本地目录不存在Docker会自动创建一个文件夹
–mount参数:如果挂载卷不存在,Docker就会报错