Python社区  »  docker

[Docker] - 入门

昱仔 • 1 周前 • 24 次点击  
阅读 2

[Docker] - 入门

入门理解docker的一些概念时,可以把docker和虚拟机进行类比(当然无论从底层还是从应用来说它们都很不一样)

  • Docker 可以认为是 vmware或者virtualbox
  • 镜像 可以认为是 xxx.iso
  • 容器 可以认为是 virtualbox运行xxx.iso后的系统
  • 镜像 是容器的基础

  • $ docker run --name webserver -d -p 80:80 nginx
    这条命令会用nginx镜像启动一个容器,命名为webserver,并且映射到80 端口,这样我们可以用浏览器去访问这个nginx服务器。

  • $ docker exec -it webserver bash
    使用docker exec命令进入容器,修改其内容。

  • 做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个docker commit命令,可以将容器的存储层保存下来成为镜像 docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

    docker commit的坏处:黑箱操作,docker diff只能提供一些线索,除了制作者本人外其他人都不知道这个镜像的详细情况

  • Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

  • dockerfile所在文件夹和镜像容器的关系?


Dockerfile详解

FROM 指定基础镜像

服务类镜像:nginx、redis、mongo、mysql、httpd、php、tomcat  语言应用镜像:node、openjdk、python、ruby、golang  空白镜像:scratch 指定版本时自动安装latest

RUN 执行命令

每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。 正确的写法应该是这样:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
  && apt-get update \
  && apt-get install -y $buildDeps \
  && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
  && mkdir -p /usr/src/redis \
  && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
  && make -C /usr/src/redis \
  && make -C /usr/src/redis install \
  && rm -rf /var/lib/apt/lists/* \
  && rm redis.tar.gz \
  && rm -r /usr/src/redis \
  && apt-get purge -y --auto-remove $buildDeps
复制代码

rm用来清理无关的东西

COPY 复制文件

COPY [--chown=:] <源路径>... <目标路径>

目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。

VOLUME 定义匿名卷

容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
VOLUME /data 这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。

EXPOSE 声明端口

  • 在容器中使用端口不需要特殊声明
  • 若需要这个端口暴露出来,需要在Dockerfile中声明
  • 若需要容器的端口指定映射到宿主的端口,需要在启动一个镜像为容器时,用 -p 参数来进行端口映射

构建镜像

  • docker build [选项] <上下文路径/URL/->
    $ docker build -t nginx:v3 .
    这里的 . ,指的是上下文的目录,docker build命令会将该目录下的内容打包交给Docker引擎以构建镜像,一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore

  • $ docker build - < context.tar.gz
    如果发现标准输入的文件格式是 gzip、bzip2 以及 xz 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。

进入容器

-i -t参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

$ docker exec -it 69d1 bash
root@69d137adef7a:/#
复制代码

数据管理

  • 数据卷

    数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

    • 数据卷 可以在容器之间共享和重用
    • 对 数据卷 的修改会立马生效
    • 对 数据卷 的更新,不会影响镜像
    • 数据卷 默认会一直存在,即使容器被删除

Dockerfile最佳实践

  • 容器只运行单个应用

    若在一个容器中运行多个进程

    • 非常长的构建时间(修改前端之后,整个后端也需要重新构建)
    • 非常大的镜像大小
    • 多个应用的日志难以处理(不能直接使用stdout,否则多个应用的日志会混合到一起)
    • 横向扩展时非常浪费资源(不同的应用需要运行的容器数并不相同)
    • 僵尸进程问题 - 你需要选择合适的init进程

实际代码阅读

  • 需要准备的内容:Dockerfile,start.sh,node.tar安装包,dynamodb_local_latest.tar安装包,这些东西放在docker的上下文中,最后和dockerfile一起打包上传

  • Dockerfile:

    • FROM 官方的java镜像,

    • ADD 把node安装包、dynamodb的安装包放在镜像的/data/,start.sh放在镜像的/opt/

    • RUN 修改start.sh的执行权限

    • ENV 配置环境变量

      • ENV PATH /data/node/bin:$PATH

        PATH 是由冒号分隔开的,所以这条指令的意思是该镜像中的环境变量为node/bin下的node,npm,npx加上本机的PATH(PATH)

      • ENV DYNAMO_ENDPOINT http://localhost:8000

          定义dynamo的端口
        复制代码
    • RUN 安装pm2, dynamodb-admin, redis-server, net-tools, vim

    • EXPOSE 暴露端口,顺序分别为dynamo-admin, gx, boss

    • ENTRYPOINT 指定入口点,类似于RUN指令,在容器建立好,运行前的准备工作,可以把准备工作写在一个脚本,如start.sh,然后指定为ENTRYPOINT

  • start.sh

    • 用nohup的方法运行dynamodb和redis
    • 运行gx代码
    • 运行boss
  • cgx指令:写python脚本,建立 /usr/bin/cgx -> /opt/run.py 的软链接

    • #!/usr/bin/python

      指定用什么解释器运行脚本以及解释器所在的位置 #!/usr/bin/python相当于写死了python路径; #!/usr/bin/env python会去环境设置寻找python目录,推荐这种写法

    • @python中的修饰器

      接收被修时的函数作为参数

      def test(f):
      print "before ..."
      f()
      print "after ..."
      @test
      def func():
      print "func was called"
      ---
      before ...
      func was called
      after ..
      复制代码
    • commands.getstatusoutput('shell command')

      返回两个元素的元组tuple(status, result),status为int类型,result为string类型。

      commands.getstatusoutput('pwd')
      (0, '/home/oracle')
      复制代码

      类似的方法还有os.system() 只能执行,但无法返回输出和返回值, os.popen() 返回的是file read的对象

    • if ____name____ == '____main____'的意思是:

      • 当.py文件被直接运行时,if ____name____ == '____main____'之下的代码块将被运行;
      • 当.py文件以模块形式被导入时,if ____name____ == '____main____'之下的代码块不被运行;
    • ____name____是内置变量,

      • 当在其他py文件中导入时,print(____name____)打印出的是包的结构
      • 当直接执行该文件时,print(____name____ )打印的是____main____
    • argparse 命令行解析模块

      • 定义命令行需要的参数,并且定义如何解析它们,当用户给程序提供无效参数时,该模块会自动生成帮助和使用消息并发出错误

      • add_mutually_exclusive_group() 用来创建一个互斥组,确保命令行使用的参数只有互斥组中的一个

        group = parser.add_mutually_exclusive_group()

        group.add_argument("-r","--random",help="mapping list random",action="store_true")

        group.add_argument("-p","--ports",help="the port list of dynamo-admin, gx, boss, you want to appoint, example -p 8001,8007,10001")

      1. 启动docker镜像为容器
      2. 检查需要使用的端口是否空闲
      3. git clone 代码
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/49235
 
24 次点击  
分享到微博