Docker从基础到高级

前言 本人曾在简书上写过三篇关于Docker、Docker集群部署、Docker镜像打包的文章。由于后来写的很多博客有大量公式,简书对公式的支持并不好,因此转移到CSDN了。现将之前写的三篇关于Docker的文章整合为一篇,集中发布到CSDN上。

Docker类似于一个裁剪的虚拟操作系统,可以定制各种镜像,这些镜像针对不同的应用需求已经做了裁剪,比如原始的ubuntu镜像、nginx镜像、python镜像,django镜像、postgresql镜像等等。ubuntu镜像包含了ubuntu最基本的功能,连最基本的vi文本编辑器都被裁剪掉了(想在docker容器里修改配置文件,可以在宿主机上修改好后再拷贝进容器里进行覆盖),当然也可以在这些已发布镜像的基础上自己扩展出专用镜像,比如扩展出nginx服务器等等。一个docker容器上一般只跑一个应用,理论上也可以像配置ubuntu服务器一样跑很多应用,但这就违背了docker部署的初衷。可以将docker容器理解为一个单一应用的虚拟服务器,而一台宿主机上可以很方便地创建多个docker容器,相当于有多个轻量级的虚拟服务器。这里出现了镜像、容器的概念,镜像就相当于类,容器是类派生出的对象,比如一个nginx镜像可以生成多个容器,每个容器上都跑一个nginx服务器。

一、Docker基本使用

本节首先介绍了使用Docker前的一些安装和准备工作,然后再单步跟踪一个docker项目的全生命周期,从拉取镜像、运行容器、编辑容器到打包镜像和部署生产。

1.1 Docker安装和镜像源配置

实践出真知,千里之行始于安装。ubuntu下安装Docker最简单的方法是使用官方脚本,一条指令搞定,也可以使用国内daocloud的脚本:

curl -sSL https://get.daocloud.io/docker | sh

安装好docker后就需要拉取镜像。首先是为docker更换国内镜像源,否则拉取镜像的速度很慢。在/etc/docker目录下新建daemon.json文件,gedit /etc/docker/daemon.json,将下面这段包含了中科大、网易和阿里镜像源的配置信息粘贴到daemon.json中保存,然后重启docker,重启docker的命令是systemctl restart docker,重启后的docker再拉取镜像时速度就很快。修改源重启docker后,使用docker info查看一下docker当前的状态,最后一段Registry Mirrors中是否列出了刚才修改的hub源?如果这条指令报如下错误“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?”,这说明docker没有启动,可以使用sudo service docker start试一下,再用sudo service docker status看一下是否启动成功。

{
	"registry-mirrors" : [
		"https://registry.docker-cn.com",
		"https://docker.mirrors.ustc.edu.cn",
		"http://hub-mirror.c.163.com",
		"https://cr.console.aliyun.com/"
  ]
}

1.2 准备工作

相关指令

  • 将当前用户添加到docker用户组:sudo adduser $USER docker
  • 查看当前用户所属用户组:id $USER
  • 启动|关停|重启docker:sudo service docker start|stop|restart
  • 查看docker服务的状态:sudo service docker status
  • 查看docker状态:docker info

docker安装好后,运行docker需要使用sudo提升权限,如果想让当前用户直接就能使用docker,需要将当前用户添加到docker用户组。可以cat /etc/group | grep docker查看一下,返回docker: x :999:的用户组信息。其中第一个docker是组名,x是密码的掩码,999是这个组创建时给的编号,后面跟的是用户名,因为现在还没有为这个组添加任何用户,所以冒号后面是空的。输入groupmod三连击tab,可以输出系统里的所有组,查看一下当前用户所属的组有哪些,输入id $USER,($USER是环境变量,你可以echo $USER看一下,就是你登录的用户名,你也可以直接输入用户名取代这个环境变量)。id指令输出了所查询用户的所有组名,目前还没加入docker组。输入sudo adduser $USER docker,再用id指令查看一下,当前用户已经加入到docker组了。以后再运行docker指令就无需使用sudo提权。

1.3 拉取镜像

相关指令

  • 查看本地镜像列表:docker image ls
  • 搜索docker hub上的镜像:docker search [–filter “xxx=xx”] XXX
  • 拉取镜像:docker image pull XXX:tag
  • 删除本地镜像:docker rmi XXX:tag|IMAGE_ID

万丈高楼平地起,首先要把地基选好,这个地基就是镜像。在准备工作中,我们已经切换了docker hub为国内源,这个源就是提供地基的镜像仓库,得益于docker社区的人丁兴旺,镜像仓库中已经储备了非常丰富的镜像,能几乎满足各行业的地基需求。我们只需在仓库中搜索自己需要的镜像,再以此为基础做进一步的修改和配置。为何不从头构建自己的镜像呢?原因有二,一是效率不多说,二是镜像仓库中提供的公共源是经过安全验证和精简化的,简而言之就是如果我们自己动手构建出相同目的的镜像,在体积和性能上往往都比不过镜像仓库中经过考验的公共镜像。
根据自己的需求,首先到docker hub上搜索需要的镜像,比如我们要搭建一个nginx反向代理服务,输入docker search nginx指令,搜索公共镜像库,得到一堆返回信息。如果想增加一些过滤信息,可以添加–filter参数,如–filter "is-official=true"则仅仅返回官方版,–filter "stars=500"则返回获星数大于500的镜像。有人要问,如果这个镜像不错,我是不是也可以去点个星?当然可以了,移步docker hub官网,如果要点星,首先得注册一个帐号,然后再在左上角的搜索框中搜索想要的镜像,这个是可视化的搜索方式,可以查看镜像的说明、查看镜像各版本的tag号,可以给心怡的镜像点★。如果仅仅是查看版本和tag,不注册帐号也是可以的。
这里提到了tag,tag是镜像的版本标签,同一个镜像会不停迭代版本,通过tag来标记版本,如果在拉取镜像时不指定tag,则默认拉取tag为latest的版本,通过刚才的官网查询,你也可以选择自己需要的版本,比如stable也是不错的选择。拉取镜像的指令是docker image pull nginx:stable,国内镜像速度还是可以的,很快就拉取完毕,通过docker image ls再查看一下,本地的镜像仓库已经增加了一条nginx镜像。
如果想删除本地的镜像,可以通过docker rmi IMAGE_ID|REPOSITORY:TAG指令。rmi是移除镜像的意思,后面跟的参数可以是镜像的ID号,也可以是镜像的名称:tag,但移除镜像前先要确保基于该镜像的容器已经全部关闭,否则会报错。这里推荐通过镜像名:tag的组合参数方式来移除镜像,一是便于人类操作,二是存在两个镜像ID号重复的情况,比如有两个nginx镜像,一个tag为stable,一个tag为1.19,这两个镜像其实是相同的,具有相同的IMAGE_ID,此时如果通过IMAGE_ID来删除,则会报错。

1.4 运行容器

利用本地镜像库中的镜像启动一个容器,docker run --name mynginx -d -p 81:80 nginx:stable,启动成功后会返回一串ID号,这串号码的前12位就是这个容器的ID号。这段命令中,通过–name指定了容器别名,通过-d指明属于后台进程,不会随终端关闭而关闭,-p 81:80指定了将本地81端口映射给容器的80端口,最后指明镜像的名字和tag。可以通过docker ps查看活动的容器,也可以再打开一个终端,输入docker stats,结果将以列表形式不断刷新当前所有活动容器的状态,类似于打开了一个实时监控窗口。
通过docker stop mynginx可以关闭这个容器。通过docker rm mynginx可以删除这个容器,删除之前先要保证容器处于关闭状态。只有与镜像关联的所有容器都被删除后,镜像才能被删除。

1.5 编辑容器

获取容器内系统的bash,可通过docker exec -it mynginx bash指令切换为容器内系统的bash。
在宿主机上通过docker cp指令,可以实现宿主机与docker容器的文件互传,在宿主机桌面上打开终端,输入docker cp default.conf mynginx:/etc/nginx/conf.d/default.conf,则实现了用宿主机桌面上的default.conf配置文件覆盖容器中nginx配置文件的目的。当然这个桌面上的配置文件是提前准备好的,比如为了实现浏览器地址栏URL不改变的隐式跳转,可以这样配置nginx:

server{
	listen		80;
	server_name	192.168.31.111;
	location / {
		proxy_pass https://www.baidu.com;
	}
}

其中那串IP地址是我虚拟机上ubuntu的局域网地址,配置文件覆盖后,通过前文介绍的获取容器bash的方法进入容器,执行nginx -s reload重启nginx,到ubuntu或者windows上用浏览器访问192.168.31.111:81,可以看到打开的内容是百度的首页,这就实现了nginx的反向代理服务。为什么这里是81端口?还记得前面run这个容器时所做的端口映射吗。

1.6 打包容器

相关指令

  • 导出容器:docker export 容器名 > 本地路径/xxx.tar
  • 对应的导入镜像: docker import 本地路径/xxx.tar 镜像名:tag
  • 将容器保存到镜像库:docker commit -a 作者 -m 描述 镜像名:tag
  • 将镜像保存到本地:docker save 镜像名:tag > 本地路径/xxx.tar
  • 对应的导入镜像:docker load < 本地路径/xxx.tar
  • 查看镜像历史信息:docker history 镜像名:tag
  • 查看镜像层文件:docker inspect 镜像名:tag

打包容器有两种方法,一种是直接直接将容器导出到本地磁盘,一种是将容器提交到本地镜像库,然后再从镜像库中导出镜像到本地磁盘。这两者初看似乎结果一样,都是实现了容器的本地持久化保存,实际还是有区别的。先来练手实践,后面再来分析它们的区别。
先是第一种,直接将容器导出到本地磁盘,docker export 容器名 > /home/xxx.tar,这里就将指定容器的快照持久化保存到本地/home/xxx1.tar文件中。从该文件还原镜像的指令是docker import /home/xxx1.tar 镜像名:tag
第二种将容器持久化到本地镜像库,docker commit -a 作者 -m 描述 镜像名:tag,再将镜像库中的这个镜像导出到本地磁盘,docker save 镜像名:tag > /home/xxx.tar,这样也同样实现了容器转镜像再保存到本地的目的。从该文件还原镜像就不能用import了,import是与export对应的,与save对应的还原镜像指令是docker load < /home/xxx2.tar,load时不用指定镜像名和tag了,应为save保存时已经将原镜像的相关信息保存到xxx2.tar中了。
在导入镜像时可能会碰到错误,请查看一下是不是import和load用错了,不同的导出指令对应不同的导入指令。
这里再来细究一下两种打包容器的方法的区别。首先可以去看看刚才导出的两个压缩文件xxx1.tar和xxx2.tar,它们的文件大小是不一样的,export方式的xxx1.tar体积小于save方式的xxx2.tar,原因是export将当前容器的快照作为一层保存,而通过commit再save的方式保存了镜像的历史层信息。你可以试着将xxx1.tar和xxx2.tar再导入到镜像库,用指令docker history 镜像名:tar来查看下它们各自的历史信息,前者只有一条历史信息,后者保存有从基础镜像逐步构建的历史信息。查看镜像层信息的指令是docker inspect 镜像名:tag,从输出信息中RootFS:Layers属性中可以看到。镜像分层相当于镜像的开发里程碑,当从镜像生成容器后,会增加一层容器层,容器的所有操作都限制在这一层,下面各历史镜像层是不会被改动的,即使删除下层的文件,也只是在容器层增加了一个删除标记,所以越层删除反而会增加镜像的体积,而不会缩小体积。
理解了这两种打包方式的原理,就要审时度势的使用。如果确认当前容器已经最优,用于发布,则可以用export打包,所有层都将压缩到一起,丢失分层信息。如果使用敏捷开发和迭代部署,保留镜像分层信息可用于回滚。
导出、导入也已经实现了,docker的基本使用和部署也就掌握了。

1.7 部署生产

docker非常适合于分布式快速部署,与之配合的有Swarm,docker集群管理平台。过去搭建大规模分布式系统,需要考虑很多细节问题,部署复杂,对运维的要求也很高。现在有了docker技术,配合Swarm,可以屏蔽很多分布式系统的底层细节问题,将重点集中到业务逻辑的拆分上,大大降低了运维的难度。这里有一篇文章,并发分布式架构演进之路,是淘宝从小规模发展到如今这样规模的架构演进过程,写得挺好,收藏于此。此生可能无缘部署如此规模的架构了,但其演进路上的某些里程碑是可以借鉴的,且如今有了docker这类容器化技术,比前人们的起点更高了。如何部署,需要的只是想象力和现实的业务需求量。当一切技术问题都不再是问题时,一张纸一支笔就能在架构设计的战场上运筹帷幄。
##7. 2021.1.6补充:docker容器开机自启动
在使用docker run从镜像启动容器时,使用–restart参数来设置,–restart具体参数值详细信息:

  • no:
    容器退出时,不重启容器。
  • on-failure:
    只有在非0状态退出时才从新启动容器;在使用on - failure策略时,指定Docker将尝试重新启动容器的最大次数sudo docker run --restart=on-failure:10。默认情况下,Docker将尝试永远重新启动容器。
  • always:
    无论退出状态是如何,都重启容器。

如果创建容器时未指定 --restart=always ,可通过update命令更新容器:
docker update --restart=always 容器名

二、Docker Swarm集群部署

本节主要介绍创建集群和管理节点、配置私有库、部署服务。

2.1 创建集群和管理节点

  • 初始化集群:docker swarm init --advertise-addr 192.168.31.111
  • 查看集群中节点:docker node ls
  • 查看从节点加入指令:docker swarm join-token worker
  • 查看管理节点加入指令:docker swarm join-token manager
  • 移除一个节点:docker node rm ID|HOSTNAME
  • 主动退出集群:docker swarm leave

执行初始化集群指令时,–advertise-addr参数配置的IP地址就是执行这条指令的主控节点的IP。这条指令会输出一些信息,其中有一行类似这样的内容:
docker swarm join --token SWMTKN-1-20ym67akkte6qhnbo0omhgyhmr75kko2m3jpek2am695dex91l-2myld05a4o7iwt3rjg3gae0h6 192.168.31.111:2377
复制这行,并粘贴到另外两个作为从节点的ubuntu系统中去执行。服务器版ubuntu没法在bash里粘贴,可以用windows cmd ssh连接后操作。
待两个从节点都加入集群后,可在主控节点通过docker node ls指令查看当前集群中的节点,可以看到连同自己和刚加进来的,共有三个节点,自己属于管理节点,且为leader。
swarm的管理节点可以有多个,官方建议是奇数个,推测可能是采用了类似纠删码的技术,只要这些管理节点中有超过一半的节点未宕机,系统仍能正常运行。刚才输入的那段加入集群的指令是给从节点使用,若想以管理节点的身份加入,则需要在现有的管理节点上输入docker swarm join-token manager,将会返回一串加入指令,复制到相应节点执行就可以加入管理层。leader是从管理层中按照一定算法推选出来的。使用docker node ls看一下,目前有一个节点的manager status是leader,刚加进来的那个是reachable,我想储君的英文翻译可能就是reachable吧。
节点退出集群有两种方法,第一种比较常用,是在管理节点上执行docker node rm ID|HOSTNAME,ID或者HOSTNAME可以从节点列表中查到,如果存在多个节点有相同的HOSTNAME,则删除会报歧义错误,只能通过ID删除。第二种是节点主动退出,对于从节点,只能执行swarm的两条指令,即加入指令和退出指令docker swarm leave

2.2 配置私有库

  • 拉取registry镜像:docker pull registry:latest
  • 启动私有库:docker run -d -v /home/registry:/var/lib/registry -p 5000:5000 --restart=always --name registry registry:latest
  • 修改docker配置文件daemon.json: 将私有库IP添加到insecure-registries
  • 修改本地镜像名和tag:docker tag 192.168.31.111:5000/镜像名:标签
  • 向私有库上传镜像:docker push 192.168.31.111:5000/镜像名:标签
  • 从私有库拉取镜像:docker pull 192.168.31.111:5000/镜像名:标签
    *192.168.31.111:5000是我的registry容器部署的网址

swam集群中如果管理节点使用一个本地镜像创建服务,在给子节点分配任务时,子节点就无法从公共docker hub上拉取到这个自建的镜像。此时就需要配置本地私有仓库。我们可以通过registry镜像来构建自己的私有仓库。
首先在管理节点上安装私有库:docker pull registry:latest
启动该私有库:docker run -d -v /home/registry:/var/lib/registry -p 5000:5000 --restart=always --name registry registry:latest
在这段指令中,-d代表以后台进程启动; -v /home/registry:/var/lib/registry表示将本地/home/registry挂载到registry容器的/var/lib/registry上,这样挂载后上传到registry的镜像文件就保存在本地/home/registry目录中;-p 5000:5000是端口映射,registry默认启用5000端口。
私有库启动后就可以向其中push镜像了。但push镜像使用的是ssl连接,由于并没有给registry配置密钥,所以直接push会报错。可以修改docker的配置文件/etc/docker/daemon.json,添加下面的insecure-registries内容,使得可以通过非加密连接该registry服务。修改daemon.json配置文件后记得要sudo service docker restart

{
   "registry-mirrors": [ ...],
   "insecure-registries": ["192.168.31.111:5000"]
}

其它的所有slave节点也都需要修改该配置文件,并重启docker服务。这样,管理节点将本地镜像上传到该私有registry库中,在启动服务时,slave节点才能从私有registry库中拉取到镜像。
刚才说的第二个弯路就是在这里被卡住的。如果是boot2docker,修改daemon.json文件后就没法重启docker,boot2docker将service、systemctl、apt、yum这类指令都裁剪掉了(难怪只有50M不到),docker没法软重启,只能重启boot2docker系统,但这个内存系统重启后一切又复位了,这是个悖论。只有在不断折腾的过程中才能逐渐找到大方向。

2.3 部署服务

  • 查看集群中已有服务:docker service ls
  • 启动一个服务:docker service create --replicas 3 --name 服务名 -p 81:80 192.168.31.111:5000/镜像名:标签
  • 查看服务状态:docker service ps 服务名
  • 服务更新镜像:docker service update --image 镜像路径 服务名
  • 服务增加|删除端口映射:docker service update --publish-add|rm 80:80 服务名
  • 服务回滚到上一次更新前的状态:docker service update --rollback 服务名
  • 服务弹性伸缩:docker service scale 服务名=10
  • 删除服务:docker service rm 服务名

集群建好了,私有镜像库搭起来了,各节点镜像库源配置好了,万事俱备,就等在上面跑服务了。用任何一个管理节点,使用docker service create指令建立服务,其中参数–replicas 3表示扩展为三个并行服务,–name给服务起名字,-p进行端口隐射,后面跟的镜像名是刚才上传到registry的镜像。
可以用docker service ls看一下,集群中已经跑起了一个服务。想看一看这个服务分布在哪些节点上,可用docker service ps 服务名查一下。如果服务做了修改,修改后的镜像也已经上传到registry上了,则可以通过docker service update --iimage 升级后的镜像名 服务名进行升级。若发现错误,可以通过docker service update --rollback 服务名进行回滚。服务还可以根据需求进一步做弹性伸缩,指令是docker service scale 服务名=数目。最后还可以通过docker service rm 服务名回收集群资源。
对于分布在各子节点的服务,往往需要共享一个公共的存储,可以通过NFS挂载网络磁盘的方式实现,去docker hub官网搜一下NFS,惊喜的发现这个功能也有现成的镜像。再进一步的,随着业务的扩张,可以逐步实施更多的分布式系统架构。比如分布式部署服务还需要通过DNS轮询来实现负载均衡。所幸的是Docker也已经提供了现成的DNS镜像,很容易部署出一个DNS轮询系统。这一切都要感谢容器化部署,减轻了开发的重复工作,也大大降低了运维的复杂度,让一切变得像搭积木一样容易。

三、Dockerfile镜像打包方法

前面介绍的都是从Docker hub上拉取已经发布的公开镜像进行使用。本节将介绍通过Dockerfile配置并打包自己的镜像的方法。
在制作自己的镜像时,首先准备好需要复制到镜像中的文件,包括程序文件和配置文件。再通过Dockerfile中的指令进行文件复制、程序运行和启动配置。实际上,我们通过镜像生成一个容器后,如果还需要对容器进行调整,无非也就是复制一些文件进去,覆盖一些配置文件,运行一些程序。而这些工作,都可以通过Dockerfile脚本的形式完成。这样生成的镜像运行后就无需再调整。这个工作有点类似于制作操作系统安装包,前天还和一个老朋友探讨过操作系统安装包的制作,今天就体会到了,虽然只是容器环境下,复杂度不同,但感觉应该类似。不能如愚见指月,观指不观月。
Dockefile的主体包括如下几个部分:

FROM rackspacedot/python37:latest #指明基础镜像包
COPY xxx /xxx/ #将Dockerfile同目录下的xxx文件或目录拷贝到生成镜像中的/xxx/目录下
RUN xxx #这里相当于在bash里执行指令,每条指令用一个RUN来标记,完成service文件的chmod修改等
MAINTAINER name email #留下你的大名和邮箱
EXPOSE 10000 #暴露端口号
ENTRYPOINT xxx #这个是启动执行命令,只能有一条
CMD xxx #这个也是启动执行命令(或给ENTRYPOINT传递默认参数),若启动容器时附加了参数,则CMD中的命令会被忽略

无非就是这些,靠它们的组合,可以配置出充满想象力的镜像。
比如,我在开发环境下的django项目根目录的上层目录处新建了Dockerfile(要细品,别被绕晕了,公益活动就不配图了),因为这个项目根目录要整体拷贝进镜像包中,所以需要与Dockerfile在同一目录下。我还想在镜像中增加service文件,并通过service启动uwsgi。那这个service文件也放到此处,通过COPY指令拷贝到镜像中的 /etc/init.d/ 目录下。关于linux的service,在下一节单独做个介绍,这也算是在绕弯路时收获的风景吧。
万事俱备,只需要在ENTRYPOINT处启动服务就可以了。当然,这个地方也只相当于执行了一条bash指令,如果这条指令不能持续运行,那容器运行后也会随着这条指令的结束而结束。要想让容器持续运行,这条ENTRYPOINT指令不能运行为后台服务。也有变通的方法,就是在CMD里执行一条类似于这样的指令tail -f xxx文件,这条tail -f指令是持续刷新显示xxx文件的内容,类似于一个死循环,这样容器也不会自动停止。但这样做不太优雅。最优雅的方式还是在ENTRYPOINT处,既然是用容器提供一个一直运行的服务,那就在ENTRYPOINT的指令里直接运行这个提供服务的不会停止的程序就可以了。

四、Ubuntu的Service机制

说一下ubuntu的service机制。我们在bash里运行service xxx start|stop|reload|status指令,其实都是调用了ubuntu服务文件目录下对应文件中的start|stop|reload|status函数。这个服务文件就是一段sh脚本,你也可以增加一些别致的函数,仅此而已。ubuntu的服务目录有很多级别,代表了系统加载过程中的不同阶段。从rc0.d-rc6.d和rcS.d,以及上文提到的init.d。关于它们之间的区别,网上有资料,我们只需要了解在这些目录下可以编写自己的service文件,service文件是一段sh脚本,里面需要提供start、stop、reload、status等函数,当然也可以只提供部分。这些目录里有了service文件,在bash中就可以通过service xxx start来启动服务,xxx服务名就是放进服务目录的service文件名。一个典型的service文件结构如下:

start() 
{
    uwsgi --ini /home/uwsgi.ini & #这是一段bash指令,后面加上&是后台执行的意思,不输出内容到屏幕
    exit 0;
}

stop() 
{
   uwsgi --stop /home/uwsgi_pid.log #同样是一段bash指令,uwsgi可以通过pid的方式优雅停止,这里也给配置上,前提是在uwsgi启动时的配置文件中要指定生成这个pid文件:pidfile = /home/uwsgi_pid.log
}

case "$1" in #$1是service xxx指令所带的第一个参数,比如start或stop
start)
    start
    ;;

stop)
    stop
    ;;

restart)
    stop
    start
    ;;

*)
    echo "Usage: $0 {start|stop|restart}"
    exit 0
    ;;

esac #shell的语法,case esac
exit 0

在构建镜像时,提前准备好这个service文件,就以uwsgiservice命名吧,通过COPY指令拷贝到镜像中的服务目录下(/etc/init.d/uwsgiservice)。我在这个service程序中调用了uwsgi指令,这就需要在service uwsgiservice start时uwsgi已经安装好,即通过RUN指令pip install -r requirements.txt,uwsgi模块已经在 requirements.txt中列出了。
实际配置我的v12.0镜像时,并没有使用服务。还记得前面说过,要想容器运行后不停止,最优雅的方式是在ENTRYPOINT处执行一个不会停止的程序,这个uwsgi就不会停止,直接ENTRYPOINT uwsgi --ini /home/uwsgi.ini就可以了。
配置好Dockerfile,就可以通过**docker build -t xxx:tag .**指令来制作镜像。这个Dockerfile的名字不要随便取,就叫Dockerfile,位置放到需要拷贝进镜像的文件和目录的同一目录下。-t xxx:tag指定镜像的名字和版本,细心看一下,最后还有一个点,表示当前目录,这意味着执行这条指令时,需要先切换到Dockerfile所在的目录下。当然也可以通过加-f指令来切换目录,但我是本着能少用一个参数就少用一个的原则。
镜像制作好后,docker images看一下,是不是已经列出来了。我打包进镜像的django只有一个功能,就是返回托管django项目的节点的IP地址。因为我想测试集群部署后的负载均衡,当任务被调度到不同的节点后,返回的IP地址是会不同的。python获取主机IP地址的方法有很多,我这里也给出一个最优雅的方式:

import socket
def get_host_ip():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()

    return ip

要想看懂这些内容,得需要有django的基础知识。不过你如果知道web框架那也好办,django就是一个python下的web框架,类似于php的thinkphp,java的struts等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值