简单说,你部署10个虚拟机,会有10个操作系统;部署10个容器,只需要1个操作系统。容器就是比较特殊的进程。
为啥Docker比虚拟机快?
项目 | docker | Vm |
---|---|---|
虚拟化类型 | OS虚拟化 | 硬件虚拟化 |
性能 | = 物理机性能 | 5%~20% 损耗 |
隔离性 | NS 隔离 | 强 |
QoS | Cgroup弱 | 强 |
安全性 | 中 | 强 |
可迁移性 | 强 | 强 |
1、查旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、设定仓库
sudo yum install -y yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
3、更新yum 索引
yum makecache fast
4、安装(默认是最新的)
sudo yum install docker-ce docker-ce-cli containerd.io
5、启动docker
sudo systemctl start docker
6、测试
docker version
sudo docker run hello-world
会发现:
Unable to find image 'hello-world:latest' locally。
这是因为本地没有hello-world
的镜像,docker会自己从远程仓库拉取镜像。拉取成功后,会出现 Hello from docker
的提示。
那为了启动hello-world
服务,docker 做了什么呢?
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
7、查看镜像
docker images
8、卸载docker
sudo yum remove docker-ce docker-ce-cli containerd.io
sudo rm -rf /var/lib/docker
注意一下:/var/lib/docker
也是docker的默认工作路径
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://uxk0ognt.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
client-server 结构,Docker的守护进程运行在主机上,客户端通过Socket 访问服务端。
docker search KW
--filter
docker search kafka --filter=Stars=1000
【根据 docker hub中对镜像的评分筛选】docker pull ImageName
docker pull mysql:latest
这里 latest 是一个 tag (其实就是版本号),默认情况下docker会拉取最新的镜像。[root@localhost ~]# docker pull mysql:latest
latest: Pulling from library/mysql
a076a628af6f: Already exists 【这里是Linux中的分层下载,联合文件系统】
f6c208f3f991: Pull complete
...
944ba37e1c06: Pull complete
Digest: sha256:feada149cb8ff54eade1336da7c1d080c4a1c7ed82b5e320efb5beebed85ae8c
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest 【这里是真实的下载地址】
docker images
查看docker images -q -a
docker rmi ImageName
docker rmi mysql
删除 MySQL的镜像,默认也是删除 【latest】的镜像docker rmi -f $(docker images -q -a)
删除所有的镜像docker inspect ImageName
查看一个镜像的元数据信息
下面都以安装一个 centos 容器为栗子:
* --name 指定容器名字,比如一个集群中有N个Nginx,每个名字不一样
* -it 以命令行交互的方式启动
* -d 后台启动
* -p 指定端口 【可以和宿主机形成端口映射】
* -P 随机指定端口
* --rm 容器停止后即删除容器,多用于测试
比如:docker run -it centos /bin/bash
启动 centos,并进入 /bin/bash 进程。
[root@localhost ~]# docker run -it centos /bin/bash
[root@d500c59b6e86 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@d500c59b6e86 /]# exit 【从容器退出】
exit
* -a : all
* -q : only show containerID
* exit : 容器停止并退出
* Ctrl P Q : 容器不停止退出
* docker rm -f ContainerID 强制删除
* docker rm $(docker ps -qa) 删除所有的容器
* docker ps -qa |xargs docker rm 效果同上
* docker start ContainerID
* docker restart ContainerID
* docker stop ContainerID
* docker kill ContainerID
docker run -d centos 这样以后台方式启动后,然后docker ps -a 未能发现到正常运行的容器。容器自动退出了!此处有坑:
**docker容器使用后台运行,就必须有一个前台进程,docker发现没有 应用,就会自动停止**!!!
```
[root@localhost ~]# docker run -d centos
03cbee2b8365b4c259948cbe8cb55d3a39f912a1961727cec184342c8397da11
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03cbee2b8365 centos "/bin/bash" 6 seconds ago Exited (0) 5 seconds ago ha
```
* -f follow
* -t 显示时间戳
* -n 显示末尾几行
```
[root@localhost ~]# docker run -it centos /bin/bash
[root@55b522d5d7ae /]# [root@localhost ~]# docker logs -ftn 10 55b522d5d7ae
(这里啥也没有,因为确实没打印日志)
```
为了能够打印出日志方便查,启动容器进入 /bin/bash,启动一个shell 脚本:
```
[root@localhost ~]# docker run -it centos /bin/bash -c "while true;do echo test;sleep 5;done"
test
test
[root@localhost ~]# docker logs -ftn 10 b59e8aebc937
2021-02-19T02:15:05.247176048Z test
2021-02-19T02:15:10.262505079Z test
```
下面是 查看到的进程信息: Pi
[root@localhost ~]# docker top b59e8aebc937
UID PID PPID C STIME TTY TIME CMD
root 2936 2915 0 10:14 pts/0 00:00:00 /bin/bash -c while true;do echo test;sleep 5;done
root 3078 2936 0 10:19 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5
容器的元数据是个JSON:
第一种方式: docker exec
比如我们需要进入容器修改一些配置,
[root@localhost ~]# docker exec -it b59e8aebc937 /bin/bash
[root@b59e8aebc937 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
第二种方式: docker attach ContainerID
[root@localhost ~]# docker attach b59e8aebc937
test
test
两种方式有啥区别?
docker exec
: 进入容器后开启一个新的终端,可在内操作
docker attach
:进入容器正在执行的终端,不会启动新的进程。
docker cp ContainerID:/fromFilePath toFilePath
比如:
docker cp b59e8aebc937:/home/a.txt /home
引申问题:
手动拷贝是一个手动动作,使用 -v
(卷) 能实现实时同步。
docker tag
docker exec -it ContainerID ip addr
这个命令和 docker exec -it ContainerID /bin/bash
有点类似,exec -it
的存在配合最后的ip addr
奏效,ip addr
可以是其他命令
只需要一句命令搞定:
[root@localhost ~]# docker run --name nginx0 -d -p 8080:80 nginx:1.18.0
;
然后本地用浏览器访问出现Nginx 欢迎界面表示安装成功。
1、如果本地没有 1.18.0的镜像,docker会自动pull
2、8080:80 是将docker的80端口映射到 宿主机的 8080,也就是说假如我们访问宿主机的8080 ,实际上请求会被 nginx 的docker 容器从 80 端口接收。
从docker hub上找到安装文档。极简安装也就是一句话:
docker run -it --rm -p 8888:8080 tomcat:9.0
注意: 这里的 --rm
是指这个容器停了就删除,多用于测试。
执行命令后,会出现如下的tomcat 启动日志:
[root@localhost ~]# docker run -it --rm -p 8888:8080 tomcat:9.0
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/local/openjdk-11
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Using CATALINA_OPTS:
NOTE: Picked up JDK_JAVA_OPTIONS: --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
19-Feb-2021 03:21:12.760 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name: Apache Tomcat/9.0.41
从浏览器 访问 ,会出现tomcat 的 404 NotFound页面。
如果ctrl C 退出 console,容器不仅会退出,而且会删除。
现在我们换个姿势 启动:
docker run -it -d --name tomcat0 -p 8888:8080 tomcat:9.0
扎样会手动拉起 docker 容器;进入容器看 tomcat 的目录结构:
[root@localhost ~]# docker exec -it 37a7e7a1e3c1 /bin/bash
root@37a7e7a1e3c1:/usr/local/tomcat# ls -l
total 128
-rw-r--r--. 1 root root 18982 Dec 3 11:48 BUILDING.txt
....
drwxr-xr-x. 2 root root 6 Jan 13 08:25 webapps
drwxr-xr-x. 7 root root 81 Dec 3 11:45 webapps.dist
drwxrwxrwx. 2 root root 6 Dec 3 11:43 work
发现:
1、容器中没有 ll vi
等shell工具
2、 webapps 目录为空,因此浏览器访问没有任何内容
这是因为:
这个镜像为了能够尽量精简,将许多不核心的命令都阉割掉了。
做个测试:
将 webapps.dist 中的内容完整拷贝到 webapps 目录下,然后浏览器访问
http://192.168.**.***:8888/index.jsp
,则可以显示 tomcat
的启动页面。
引申问题:
如果部署容器,还要进入容器岂不是十分麻烦,可以在容器外提供一个映射路径到 webapps。只需要在容器外部署,自动同步到内部岂不妙哉。
ES 有几个特点:
--net somenetwork
我们 以 development 模式启动,从ES的docker hub 看到,这也不过一句命令的事儿:
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:6.8.13
测试一下是不是真的启动成功:
[root@localhost ~]# curl localhost:9200
启动后发现, ES实在是太占资源 了。
使用 docker stats fa09dc0f7672
查看容器状态:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
fa09dc0f7672 elasticsearch 5.54% 1.175GiB / 1.777GiB 66.15% 656B / 0B 695MB / 633kB 44
那如何增加内存限制呢? 【使用 -e
参数修改环境配置】
首先关闭正在运行的 ES 容器,然后添加 JVM参数,再启动
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:6.8.13
重新看下资源:
docker stats ContainerID
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a5a7c21f3224 elasticsearch 1.37% 416.8MiB / 1.777GiB 22.91% 656B / 0B 249MB / 646kB 45
引申问题:
如果我们在同一个宿主机内启动一个Kibana容器 ,连接到这个 ES容器,由于容器之间互相隔离,那两者如何连通?
portainer: Docker的GUI 工具。
在CI/CD 的场景下,Rancher 是更广泛使用的。
从docker hub 找到这个工具的使用方式:
docker volume create portainer_data
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-ce
安装OK后,浏览器打开:
http://192.168.*.*:9000
注意这里 不是 8000 的端口,然后得到这样的界面:
这个工具在大厂用得不是特别多,但是在中小公司倒是十分实用。
DockerImage:本质是个软件包,打包了运行环境和应用软件,准确点说,是把应用运行的所有代码、运行时、环境变量、配置文件统一打包起来了。
怎么获取镜像:
1、什么是UFS
一种分层、轻量级、高性能的文件系统,支持对文件系统的修改作为一次次的提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。 UFS是docker Image 的实现基础。镜像可以通过分层来继承, 基于基础Image,可以制作各种具体应用镜像。
特性:一次加载多个文件系统,但是从外面看起来,只能看到一个文件系统,UFS会把各层文件系统叠加,这样最终的文件系统会包含所有底层的文件和目录。
2、如何理解UFS
我们拿GIt来理解下:
举个栗子:假如现在要制作一个自己的镜像,dockerfile:
From debian
RUN apt-get install emacs
RUN apt-get install apache2
CMD['/bin/bash']
a) 新镜像并不是从Scratch开始,而是直接在 debian 基础上构建
b) 安装 emacs
c) 安装 apache2
d) 容器启动时运行bash
可以看到:
新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层
3、使用UFS的好处
UFS的最大的一个好处就是 - 共享资源
比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
bootfs(boot file system) 主要包含 bootloader和kernel , bootloader主要是引导加载kernel。Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs (root file system),在bootfs之上 (图中的 Debian)。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。【所以有人把 容器称作 小的 虚拟机环境】
Q: 为啥平时我们装的虚拟机都是 几个G,而docker 容器才几百M?
A :对于一个精简版本的OS , rootfs 很小,只需要包含基本的命令、库、工具即可,因为底层直接使用host和kernel,用户只要提供 rootfs 即可。对于不同的Linux发行版本,bootfs 基本一致,rootfs 会有所差别,所以不同的发行版可公用 bootfs
以pull 一个 redis 为例子:
[root@localhost ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a076a628af6f: Already exists 【这一层已经下载过了,不用继续下了】
f40dd07fe7be: Pull complete
ce21c8a3dbee: Pull complete
ee99c35818f8: Pull complete
56b9a72e68ff: Pull complete
3f703e7f380f: Pull complete
Digest: sha256:0f97c1c9daf5b69b93390ccbe8d3e2971617ec4801fd0882c72bf7cad3a13494
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
顺便诊断下 镜像看看:
docker inspect redis
截取其中的rootfs (刚好也是 6 个layers)
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:xxx",
"sha256:xxx",
"sha256:xxx",
"sha256:xxx",
"sha256:xxx",
"sha256:xxx"
]
},
有一点需要重点了解下:
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合。
比如1 : 下面这个镜像包含了两个镜像层的6个文件:(注意这 6 个文件是没有冲突的。)
比如2: 这个复杂一点的三层镜像,在外部看来也是有 6个文件,最上层的文件7 由于是文件5 的更新版本,已经产生了替换。Docker 通过存储引擎(新版本使用快照机制)实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。 Linux上可用的存储引擎有Btrfs 、Zfs 等,每种对应到Linux中对应的文件系统或块存储技术。
在镜像打包的时候,所有镜像层堆叠合并,对外提供统一的视图:
Docker镜像都是只读的,容器启动时,一个新的可写层被加载到了镜像的顶部,这就是所谓的容器层,容器层之下的都叫做镜像层。
解释一下:
我们基于一个Tomcat 镜像,启动一个Tomcat容器,对容器的Tomcat改了点配置,这个修改只是对 容器层,不会改变原先的 镜像。
如果我们希望修改能对所有后来启动的容器都有效果,那么可以基于原镜像再添加一层(再打包并 commit 一次)
docker commit
和 git commit
有异曲同工之妙,和VM的 快照功能有点类似。
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
下面举个栗子:
前面提到,官方的tomcat 镜像是精简过的,webapps 目录下没有内容。直接拉起容器后,通过浏览器访问得到 404 。现在我们搞一个自己的镜像,webapps中有从webapps.dist 中复制过去的内容。然后基于自己制作的镜像,启动新的容器。
[root@localhost ~]# docker run -it -d -p 8888:8080 tomcat:9.0
[root@localhost ~]# docker exec -it /bin/bash 06825c314e50
root@06825c314e50:/usr/local/tomcat# cp webapps.dist/* webapps/ -r
curl localhost:8080 【测试容器是否启动正常】
然后commit 一个自己的镜像:
[root@localhost ~]# docker commit -m="add webapps" -a="author" 06825c314e50 mytomcat:02
看一下镜像是不是打成功了:
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mytomcat 02 2f6f33afc42c 6 seconds ago 654MB
redis latest 621ceef7494a 5 weeks ago 104MB
从新镜像拉起 容器
[root@localhost ~]# docker run --name mytomcat -d -p 8081:8080 mytomcat:02
e10bc76ab632c721edd0f80e501476757af3e7985dd9f918a8125507a8ec6cf8
浏览器访问或者curl一下 (8081 端口),看是否成功