之前实习一直听上线的老大说docker,但是一直没让我用过,我就自己学了下,docker是用来做镜像隔离的工具。那么什么是容器与镜像?如何构建容器与镜像?
1、容器与镜像
1.1 什么是容器
一般系统下运行的进程是资源共享的,这样会存在一些问题:
1)进程攻击:因为这些进程能够相互看到并且进行通信,高级权限的进程可以攻击其他进程;
2)共享文件:因为它们使用的是同一个文件系统,因此会带来两个问题:这些进程可以对于已有的数据进行增删改查,具有高级权限的进程可能会将其他进程的数据删除掉,破坏掉其他进程的正常运行;此外,进程与进程之间的依赖可能会存在冲突,如此一来就会给运维带来很大的压力;
3)资源竞争:因为这些进程使用的是同一个宿主机的资源,应用之间可能会存在资源抢占的问题,当一个应用需要消耗大量 CPU 和内存资源的时候,就可能会破坏其他应用的运行,导致其他应用无法正常地提供服务。
所以就有了容器这个东西,让一组进程有一个自己的集合,也就是容器。
容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。所谓“视图隔离”就是能够看到部分进程以及具有独立的主机名等;控制资源使用率则是可以对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。
1.2 什么是镜像
容器具有一个独立的文件系统,因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具,我们只需要提供容器所需的二进制文件、配置文件以及依赖即可。只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。
我们将这些容器运行时所需要的所有的文件集合称之为容器镜像。
1)环境依赖冲突,比如做深度学习有时候你的代码对于tf的版本可能有特定要求,版本过高过低都会出问题,这样不同的程序可能对于系统都有着不同的要求。
2)文件
2、如何构建镜像
2.1 编写DockerFile文件
一般我们新建一个文件夹,在里面存放必要文件和Dockerfile(名字不能改)文件。
vim Dockerfile
例子如下:
# Base Images
## 从天池基础镜像构建
FROM registry.cn-shanghai.aliyuncs.com/tcc-public/python:3
## 把当前文件夹里的文件构建到镜像的根目录下
ADD . /
## 指定默认工作目录为根目录(需要把run.sh和生成的结果文件都放在该文件夹下,提交后才能运行)
WORKDIR /
## 镜像启动后统一执行 sh run.sh
CMD ["sh", "run.sh"]
1、RUN命令:
## 执行指令,可以安装,可以解压文件
RUN yum install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
2、CMD指令
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
CMD 在docker run 时运行。
RUN 是在 docker build。
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
CMD ["sh", "run.sh"]
CMD ["python", "helloworld.py"]
3、COPY
复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
4、ENTRYPOINT
这个类似于CMD,但是这个指令不会被docker run的参数覆盖,一定会被执行。而且,docker run传递的参数将会作为ENTRYPOINT执行命令的参数。
ENTRYPOINT ["/bin/bash","run.sh"]
2.2 build and push
一般我们编写一个镜像文件后,就可以通过 docker build 命令构建出所需要的应用。构建出的结果存储在本地。
docker build -t registry.cn-shenzhen.aliyuncs.com/test_for_tianchi/test_for_tianchi_submit:1.0 .
其中registry.cn-shenzhen.aliyuncs.com/test_for_tianchi/test_for_tianchi_submit是云端地址,我们现在放在阿里云上的公共仓库,(你也可以自己注册一个),随后1.0代表版本号,用来区别你生成的不同版本。最后还有个.代表要操作的目录当前目录。
在实际生产环境中,我们需要将它传到云端仓库上。后期测试或使用是将它再拉下来。
docker push registry.cn-shenzhen.aliyuncs.com/test_for_tianchi/test_for_tianchi_submit:1.0
2.3 pull and run
下拉镜像文件
docker pull box:1.12
通过以下命令查看现存镜像文件
docker images
运行镜像
docker run images_name:version (command)
docker run registry.cn-shanghai.aliyuncs.com/cloudcver_test/test1:1.0 sh run.sh
docker run registry.cn-shanghai.aliyuncs.com/cloudcver_test/test1:1.0
2.4一些常见指令
1、启动一个镜像
我们可以启动一个通用的镜像环境,比如ubuntu,再比如到了别人的电脑上可以快速布局一个python3环境,启动一个镜像即可,这时候加一个参数-i就是可以交互式操作,执行指令/bin/bash就可以进入命令行下
docker run -it ubuntu /bin/bash
-i: 交互式操作。
-t: 终端。
/bin/bash执行的指令,也可以单独就执行一个指令sh run.sh。
输入exit退出
exit
注意:其实也可以后台启动一个镜像,加一个参数d,随后就docker exec id进入镜像即可。
docker run -itd --name ubuntu-test ubuntu /bin/bash
docker exec -it 243c32535da7 /bin/bash
2、查看所有容器
docker ps -a
如下展示我们运行的容器直接执行结束了,所以状态是退出的状态。
3、启动一个容器或者重启
docker start 0f09f77f021e
docker restart 0f09f77f021e
4、停止一个容器
docker stop sss
5、删除容器
docker rm -f 1e560fca3906
下面的命令可以清理掉所有处于终止状态的容器。
docker container prune
6、删除镜像
docker rmi xxx
CPU镜像:docker run your_image sh run.sh
GPU镜像:nvidia-docker run your_image sh run.sh
3 容器的生命周期
容器是一组具有隔离特性的进程集合,在使用 docker run 的时候会选择一个镜像来提供独立的文件系统并指定相应的运行程序。这里指定的运行程序称之为 initial 进程,这个 initial 进程启动的时候,容器也会随之启动,当 initial 进程退出的时候,容器也会随之退出。
因此,可以认为容器的生命周期和 initial 进程的生命周期是一致的。当然,因为容器内不只有这样的一个 initial 进程,initial 进程本身也可以产生其他的子进程或者通过 docker exec 产生出来的运维操作,也属于 initial 进程管理的范围内。当 initial 进程退出的时候,所有的子进程也会随之退出,这样也是为了防止资源的泄漏。
但是这样的做法也会存在一些问题,首先应用里面的程序往往是有状态的,其可能会产生一些重要的数据,当一个容器退出被删除之后,数据也就会丢失了,这对于应用方而言是不能接受的,所以需要将容器所产生出来的重要数据持久化下来。容器能够直接将数据持久化到指定的目录上,这个目录就称之为数据卷。
数据卷有一些特点,其中非常明显的就是数据卷的生命周期是独立于容器的生命周期的,也就是说容器的创建、运行、停止、删除等操作都和数据卷没有任何关系,因为它是一个特殊的目录,是用于帮助容器进行持久化的。简单而言,我们会将数据卷挂载到容器内,这样一来容器就能够将数据写入到相应的目录里面了,而且容器的退出并不会导致数据的丢失。
通常情况下,数据卷管理主要有两种方式:
第一种是通过 bind 的方式,直接将宿主机的目录直接挂载到容器内;这种方式比较简单,但是会带来运维成本,因为其依赖于宿主机的目录,需要对于所有的宿主机进行统一管理。
第二种是将目录管理交给运行引擎。
4 练习
任务:阿里云天池大赛docker练习场
https://tianchi.aliyun.com/competition/entrance/231759/introduction
答案备用:
answer.py
# step 1
print("hello world!")
# step 2
import csv
csv_file=csv.reader(open('/tcdata/num_list.csv','r'))
content=[] #用来存储整个文件的数据,存成一个列表,列表的每一个元素又是一个列表,表示的是文件的某一行
for line in csv_file:
content.append(int(line[0]))
sum_num = sum(content)
content.sort()
content.reverse()
# step 3
import json
print(content[:10])
test_dict = {"Q1":"Hello world","Q2":sum_num,"Q3":content[-10:] }
with open("./result.json","w") as f:
json.dump(test_dict,f)
run.sh
python answer.py
Dockerfile
# Base Images
## 从天池基础镜像构建
FROM registry.cn-shanghai.aliyuncs.com/tcc-public/python:3
## 把当前文件夹里的文件构建到镜像的根目录下
ADD . /
## 指定默认工作目录为根目录(需要把run.sh和生成的结果文件都放在该文件夹下,提交后才能运行)
WORKDIR /
## 镜像启动后统一执行 sh run.sh
CMD ["sh", "run.sh"]