镜像
是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像。
镜像分层
当我们执行 docker pull tomcat时,下载时分好多层下载,上面的层下载好后,再下载下一层
UnionFS(联合文件系统)
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统。它支持对文件系统的修改作为一次提交来一层一层的叠加。同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像)可以制作各种具体的镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各种文件叠加起来,这样最终文件系统会包含所有底层的文件和目录。
Docker镜像加载原理
dokcer的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,linux刚启动时会加载bootfs文件系统,在dokcer镜像的最底层是引导文件系统个bootfs。这一层与典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已有bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中/dev 、 /proc 、 /bin 、/etc等标准目录和文件。rootfs就是各种不同操作系统发行版,比如Ubuntu、Centos等等。
为什么docker镜像要采用这种分层结构呢?
镜像分层最大的一个好处就是共享资源,方面复制迁移,就是为了复用。
比如说有多个镜像都从相同的base镜像构建而来,那么Docker Host只需要在磁盘上保存一份base镜像。 同时内存中也只需要加载一份base镜像,就可以为所有的容器服务了。而且镜像的每一层都可以被共享。
重点理解
Docker镜像层都是只读的,容器层是可写的。
当容器运行时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作"容器层","容器层"之下的层都叫镜像层。
多有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
Docker镜像commit操作案例
docker commit 提交容器副本使之成为一个新的镜像
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
案例演示 ubuntu安装vim
从hub上下载ubuntu镜像到本地
docker pull ubuntu
运行ubuntu容器
docker run -it ubuntu /bin/bash
原始的ubuntu镜像是不带vim命令的
外网联通的情况下安装vim
# 先更新包管理工具
apt-get update
# 安装vim
apt-get -y install vim
# 用vim命令编辑一下a.txt文件
vim a.txt
写入 this is docker
wq!退出编辑
# 打印出a.txt内容
cat a.txt
将带有vim命令的ubuntu容器通过commit命令提交成一个新的镜像
docker commit -m="add vim cmd" -a="tom" db33f73a242f tom/ubuntu:1.0
查看新生成的镜像
本地镜像发布到阿里云
先登录自己的阿里云,左侧菜单-容器和镜像服务-实例列表-进入个人实例(需要创建);
1、创建镜像仓库,设置仓库密码
2、创建命名空间,先把命名空间公开
3、返回到镜像仓库,选择一个命名空间
4、创建镜像仓库
下一步(选择本地仓库)
创建仓库成功后,会看到很多命令
最后操作命令,将镜像推送到阿里云仓库
登录(需要输入镜像仓库密码)
docker login --username=fengqinghai registry.cn-hangzhou.aliyuncs.com
把需要的镜像打个标签
docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/xxxxxx/myubuntu1.3:[镜像版本号]
最后push到阿里云仓库
docker push registry.cn-hangzhou.aliyuncs.com/xxxxxx/myubuntu1.3:[镜像版本号]
最后验证一下
将阿里云中的镜像下载到本地使用
登录到阿里云仓库(和上面的登录一样)
从阿里云拉取想要的镜像
docker pull registry.cn-hangzhou.aliyuncs.com/xxxxxx/myubuntu1.3:[镜像版本号]
将本地镜像发布到私有库
假如有一些比较重要的东西,不想放在阿里云,那么可以放在公司内部的私有库\
私有库是什么?
1、官方Docker hub地址:https//hub.docker.com ,中国大陆访问太慢了且准备被阿里云取代的趋势,不太主流。(放在docker hub相当于将代码放在github是类似的)\
2、Docker hub和阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私有仓库供团队使用,基于公司内部项目构建镜像。\
Docker Registry是官方提供的工具,可以用于构建私有镜像仓库。
1、下载镜像Docker Registry
docker pull registry
2、运行私有库registry,相当于本地有个私有docker hub
docker run -d -p 5000:5000 -v /fqh/myregistry/:/tmp/registry --privileged=true registry
默认情况下,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器全映射,方便与宿主机联调
参数说明:
-d:后台运行
-p:端口映射 -p 主机端口:容器内部端口(-p 8080:80 代表通过访问主机的8080端口来访问容器内部的80端口)
-v /fqh/myregistry/:/tmp/registry --privileged=true 后面将的 数据券知识
3、案例演示创建一个新的镜像,ubuntu安装ifconfig命令
运行ubuntu图容器
docker run -it ubuntu /bin/bash
包的更新
apt-get update
安装ifconfig命令
apt-get install net-tools
验证ifconfig命令(可查看容器的ip)
ifconfig
退出容器,在容器外执行commit命令,将该容器生成本地镜像
docker commit -m='提交的描述信息' -a='作者' 容器id 新镜像名称:tag(版本)
docker commit -m='ifconfig cmd add' -a='fqh' edddb51c310d ifconfigubuntu:1.0
curl验证私服库上有什么镜像
curl -XGET http://192.168.188.100:5000/v2/_catalog
将新镜像ifconfigubuntu:1.0修改符合私服规范的tag
按照公式: docker tag 镜像:tag host:port/repository:tag
docker tag ifconfigubuntu:1.0 192.168.188.100:5000/ifconfigubuntu:1.0
修改配置文件使之支持http
由于dokcer做了安全啊加固,是不支持http的
cat /etc/docker/daemon.json
在daemon.json中加上(别忘记行之间有逗号)
"insecure-registries":["192.168.188.100:5000"]
添加后按esc,输入:wq保存退出编辑模式,可以用cat查看一下是否修改成功;如果设置好不生效,建议从新启动docker。
重新启动docker
systemctl restart docker
查看docker状态
systemctl status docker
把docker的私服库重启一下
docker run -d -p 5000:5000 -v /fqh/myregistry/:/tmp/registry --privileged=true registry
push推送到私服库
docker push 192.168.188.100:5000/ifconfigubuntu:1.0
curl验证私服库上有什么镜像
curl -XGET http://192.168.188.100:5000/v2/_catalog
一不做二不休,把本地的镜像先删掉,再从本地仓库拉取镜像
docker rmi -f 192.168.188.100:5000/ifconfigubuntu:1.0
docker rmi -f ifconfigubuntu:1.0
pull到本地并运行
docker pull 192.168.188.100:5000/ifconfigubuntu:1.0
运行一下拉下来的容器
docker run -it 192.168.188.100:5000/ifconfigubuntu:1.0 /bin/bash
验证是有有ifconfig命令
ifconfig
容器卷
Docker挂载主机目录访问,如果出现cant open directory.:Permission denied。
解决办法:在挂载目录后多加一个 --privileged=true 参数即可。
如果是CentOS7安全模块会比之前版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为, 在SELinux里面挂载目录被精致掉了,如果要开启,我们一般使用 --privileged=true的命令,扩大容器权限解决挂载目录没有权限的问题,也即使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户的权限。
什么叫挂载?相当于一个u盘插在电脑主机上了。
简洁点说 --privileged=true 是权限扩容,保证有root用户权限的这样一个东东。
我们在启动启动docker私服库时,运行过这样一段代码:
-v : 代表要添加自定义的容器卷
冒号左边的是 宿主机的路径,右边的是容器内的路径
--privileged=true 放开权限。
以上红框内的代码完成了 启动容器以后完成了容器内部和宿主机的一个绝对路径实现了信息的共享和互通
如果不加,默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便与宿主机联调。
容器卷是什么?
一句话,有点类似redis里的rdb和aof文件,
将docker容器内的数据保存进宿主机的磁盘中
如何运行一个带有容器卷存储功能的容器实例
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
数据券能干嘛?
Docker容器产生数据,如果不备份,当容器删除后,容器内的数据自然也就没有了。
为了能保存数据在docker中我们使用券。
特点:
1、数据券可在容器之间共享或重用数据
2、卷中的修改可以直接实时生效,爽
3、数据券中的更改不会包含在镜像的更新中
4、数据券的生命周期一直持续到没有容器使用它为止
宿主机vs容器之间映射添加容器卷
命令模板: docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data --name=u1 ubuntu
执行成功就以进入容器内部
cd /tmp/docker_data
ls 发现什么都没有
touch dockerin.txt 在此目录下创建一个文件
回到主机内,进入/tmp/host_data目录下
cd /tmp/host_data
ls 发现已经有dockerin.txt文件
在主机此文件下创建一个文件
touch hostin.txt
在容器的/tmp/docker_data中也实时的出现了这个新文件
查看数据券是否挂载成功???
docker inspect 容器id
在 Mounts信息中可看到数据券的绑定路径和其它信息
容器停了之后,在宿主机中创建文件,然后再启动容器,容器内会有新创建的文件吗???
docker stop 容器id
cd /tmp/host_data
touch c.txt
docker start 容器id
docker exec -it 容器id /bin/bash
cd /tmp/docker_data
ls 发现有新的文件
上述答案是肯定的,即使容器停了,在宿主机创建的文件也会实时同步到容器内
读写规则映射添加说明
默认就是上面的规则:主机和容器读写同步
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
rw:就是默认规则(意思是容器内的目录可读可写)
只读:我们有时希望 容器内只能读不能写
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
ro:read only
如何还之一饿的在容器内写文件,会提示read-only file system
docker rm -f u1|容器id 删除刚才的容器
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data:ro --name=u1 ubuntu
cd /tmp/docker_data
touch d.txt 会报错,写入权限被限制
卷的继承和共享
1、容器1完成完成和宿主机的映射
docker run -it --privileged=true -v /mydocker/u:/tmp/u --name u1 ubuntu [/bin/bash(ubuntu自动带shell,所以/bin/bash可省略)]
cd /temp/u
touch u1data.txt
exit
2、容器2继承容器1的券规则
语法:docker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu
docker run -it --privileged=true --volumes-from u1 --name u2 ubuntu
因为u1和主机的/mydocker/u联通,u2继承u1,所以u2和主机的/mydocker/u也联通
cd /tmp/u
touch u2data.txt
docker ps -n 2 查看最近的两个容器(u1已经死了)
重启u1
docker start u1
进去u1
docker exec -it u1 /bin/bash
cd /tmp/u
ls 发现能看到u2data.txt
docker上安装常用软件说明
总体步骤
1、搜索镜像 (去docker hub上搜一下看有没有这个镜像)
2、拉取镜像
3、查看镜像
4、启动镜像-服务端口映射
5、停止容器
6、移除容器
安装tomcat
1、docker hub上查找tomcat镜像 https://hub.docker.com/ 因为我们配了阿里云服务器,所以是从阿里云下载的
docker search tomcat
2、从docker hub上拉取tomcat镜像到本地
docker pull tomcat
3、docker images 查看是否拉取到tomcat
docker images tomcat
4、运行镜像生成容器实例
docker run -d -p 8080:8080 --name t1 tomcat (-P:随机分配端口 -p 主机端口:容器端口)
5、发现hostip:8080是404,因为最新版的tomcat内的webapps目录是空的,而webapps.dist不是空的,所以需要把webapps删掉,把webapps.dist改成webapps
进入tomcat容器内 docker exec -it t1 /bin/bash
直接在/usr/local/tomcat
ls -l 发现有webapps 和webapps.dist两个文件
把控的webapps删掉
rm -r webapps
把webapps.dist改名成webapps
mv webapps.dist webapps
然后再访问hostip:8080就可以访问到页面了!
exit 退出容器
dokcer stop t1 停掉tomcat容器
docker rm -f t1 删掉容器
免修改版
安装mysql
docker search mysql 搜索mysql
docker pull mysql:5.7 拉取mysql5.7版本
查看linux下有没有mysql运行,防止端口被占用
ps -ef|grep mysql
运行msql镜像生成实例(-e是运行的环境 设置root密码 端口映射是3306:3306)
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
进入容器
dokcer exec -it 容器id /bin/bash
登录数据库
mysql -uroot -p
123456
show databases;
create database db01;
use db01;
create table t1 (id int, name varchar(20));
insert into t1 values(1, 'z3');
select * from t1;
exit 退回到linux
ifconfig 查看本机ip ens33
用Navicat for mysql可以连接(输入ip 和密码 测试可连接)
查询和插入没问题,但是插入中文时报错了,原因是docker上默认字符集编码没有修正;
SHOW VARIABLES LIKE 'character%'; 查询字符集编码(全是 拉丁)
删除容器后,里面的数据怎么办?跑路?
docker rm -f 容器id (把mysql容器误删了,完了数据也丢了,只有跑路)
查询字符集编码(看到全是 拉丁)
实战版装mysql
1、解决dokcer字符编码乱的问题;
2、解决误删容器导致数据丢失的问题;
新建mysql容器实例
docker run -d -p 3306:3306 --privileged=true \
--restart unless-stopped \
-v /fqh/mysql/log:/var/log/mysql \
-v /fqh/mysql/data:/var/lib/mysql \
-v /fqh/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql \
mysql:5.7
挂了3个数据券,开放了权限,设置了密码,给容器取了名字(加容器卷可以解决误删容器数据丢失问题)
–restart unless-stopped : 在docker重启时重启当前容器。但不包含docker重启时已停止的容器。
新建my.cnf-通过容器卷同步给mysql容器实例(解决中文乱码问题)
cd /fqh/mysql/conf
ls
vim mysql.cnf
插入这些配置(保存退出, linux命令 i-进入插入模式 esc-退出插入模式或命令行模式 :-进入命令行模式)
[client]
default_character_set=utf8
[mysqld]
character_set_server=utf8
cat mysql.cnf 查看此文件
重启mysql
docker restart mysql
进来mysql容器
docker exec -it mysql /bin/bash
查看docker内的mysql字符编码
SHOW VARIABLES LIKE 'character%';
插入汉字(成功)
insert into ti values (3, '王五');
有无聊的人手欠,直接把正在运行的mysql容器给删了
docker rm -f mysql
再次运行mysql镜像生成容器
docker run -d -p 3306:3306 --privileged=true \
--restart unless-stopped \
-v /fqh/mysql/log:/var/log/mysql \
-v /fqh/mysql/data:/var/lib/mysql \
-v /fqh/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql \
mysql:5.7
进入mysql容器
docker exec -it mysql /bin/bash
mysql -uroot -p
123456
use db01;
show tables;
select * from t1; 看到数据还在!!!数据已经备份好了(误删容器也能保住数据)
安装redis
docker search redis
docker pull redis
dokcer run -d -p 6379:6379 redis
docker exec -it 36bc3b5f6122 /bin/bash
redis-cli
set k1 v1
get k1
exit
docker rm -f 36bc3b5f6122
redis同样需要有容器卷,防止数据丢失,有配置文件,有数据券就需要开放权限 --privileged=true
=================实战版安装redis=======================
新建/app/redis目录
mkdir -p /app/redis
cd /app/redis
// 把redis.conf文件拷贝到/app/redis目录下
cp /fqh/myredis/redis.conf /app/redis (根据自己下载的redis版本下载或复制redis.conf文件)
在/app/redis目录下修改自定义的redis.conf文件
vim redis.conf
1、开启redis验证 可选(这里图省事设置成不需要密码)
# requirepass 123456 (如果开启密码验证,就是123456) 命令行模式下 /关键字可搜索位置
2、允许redis外地连接 必须
# bind 127.0.0.1 -::1 (注释掉这一行)
3、将daemonize yes注释起来或者设置成 daemonize no设置成no
4、redis持久化 appendonly yes 这个自己决定(可开可不开)
5、如果外部连接redis不放心,可以把 protected-mode no 设置为no(关闭保护模式)
:wq 保存退出
// 运行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 \
redis-server /etc/redis/redis.conf
参数解释:redis-server /etc/redis/redis.conf 让redis服务去读指定的配置文件
如果没成功启动 查看日志(大多是版本错了)
docker logs 容器id|名称
docker exec -it myr3 /bin/bash
redis-cli
set k2 v2
get k2
redis.conf (6.26版本去网上搜索一下,注意汉字都要被注释)
安装nginx
dokcer search nginx
docker pull nginx
// 在/app/nginx下创建一个web目录,用来存放web项目文件(web内包含一个index.html文件可以传到主机上)
mkdir -p /app/nginx/web
// 找一个nginx.conf文件上传到主机的/app/nginx目录下(配置见下文)
启动容器(设置好配置文件映射和项目文件映射)
docker run -d \
-p 3001:80 \
--privileged=true \
-v /app/nginx/web:/usr/local/web \
-v /app/nginx/nginx.conf:/etc/nginx/nginx.conf \
--name nginx1 \
nginx
通过主机ip:3001 访问,可以看到我们上传的index.html页面
nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include 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 logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /usr/local/web;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
# server {
# listen 8011;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root D:\xxx;
# index index.html index.htm;
# }
# location /api {
# proxy_pass https://xxx;# 代理接口地址
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}