Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

Docker使用

2022/04/25 自动化运维
Word count: 10,193 | Reading time: 44min

Docker学习

轻部署,省成本,易迁移

docker和虚拟机VM的区别

一、本质上的区别:

VM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;

Container(Docker容器),在宿主机器、宿主机器操作系统上创建Docker引擎,在引擎的基础上再安装应用。

那么问题来了,没有操作系统,怎么运行程序?

可以在Docker中创建一个ubuntu的镜像文件,这样就能将ubuntu系统集成到Docker中,运行的应用就都是ubuntu的应用。

二、使用上的区别:

Size:

  • 虚拟机ubuntu大小为:24.1GB
  • Docker中镜像文件占用内存: 81.2MB

Startup:

  • Docker在宿主机器的操作系统上创建Docker引擎,直接在宿主主机的操作系统上调用硬件资源,而不是虚拟化操作系统和硬件资源,所以操作速度快。
  • 这个其实安装一个ubuntu的虚拟机和拉取一个Docker的ubuntu镜像文件,运行一下就知道了,区别很明显,虚拟机开一下大概得2分多钟,而Docker只需要2秒钟。

宿主机、镜像与容器

宿主机器: 运行docker的机器

镜像:不可以修改内容

容器:可以修改能内容,相当于虚拟机,默认情况下彼此相互独立(容器之间可以通信)

△.可以依据镜像来创建容器,也可以封装容器为一个镜像,即容器<===>镜像

Docker命令

搜索镜像: docker search python

下载镜像: docker pull NAME

▲注:latest是默认的标签,字面上理解为最新版本的镜像,实质上latest只是镜像的标签名称,跟具体某个版本号地位一样,有latest标签的镜像不一定是最新版本,镜像作者可以任意指定标签。
一般来说,我们会使用latest作为最新镜像的标识,这是约定。但,一旦该镜像被拉取到本地,版本号就已经确定,不会自动更新。(2021年10月10日 webapp链接mysql时镜像一直用的是latest版本的镜像,导致浪费了很长时间捣鼓)===>所以在每次都要以最新镜像构建容器时都应该把原有的latest镜像删除后,再拉取标签为latest的最新镜像。

发布者如果docker build -t <username>/<repositoryName> .中不带tag标签,则默认为latest,但是需要注意的是,这样意味着latest其实也是发布者指定的,如果发布v5时将latest绑定为v5,但发布了新的v6后发布者并没有同步对当前的镜像发布latest,那么latest标签绑定的还是v5。这就于我们所理解的latest有偏差了,实际上也反应了latest其实也就是一个标签名字而已,他并没特殊的含义

通过Dockerfile构建新镜像: docker build . --build-arg FRP_VERSION=v0.37.0 -t frp

  • -t, --tag=[]: 指定镜像tag标签

根据镜像创新容器: docker run -tid (--name xxx) IMAGE ID

  • ti表示以交互的形式创建容器,d表示不进入
    • -d, --detach=false, 指定容器运行于前台还是后台,默认为false
    • -i, --interactive=false, 打开STDIN,用于控制台交互
    • -t, --tty=false, 分配tty设备,该可以支持终端登录,默认为false
    • -v, --volume=[host-src:]container-dest[:<options>]
    • -p, --port=<host_port>:<container_port> 指定端口映射,格式为:主机(宿主)端口:容器端口

Flag参数查询: https://appapi.w3cschool.cn/doc_docker_1_11/docker_1_11-engine-reference-commandline-run-index.html?lang=en

启动容器: docker start CONTAINER_ID

进入容器: docker attach CONTAINER_ID (已被弃用) ==> docker exec -ti CONTAINER_id /bin/bash

  • 进入容器前得前运行容器

退出容器: ctrl + p + q , 以ctrl + zexit将会终止容器运行

在一个已运行的容器中执行命令: •docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

封装容器为镜像: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

1
2
3
4
-a :提交的镜像作者;
-c :使用Dockerfile指令来创建镜像;
-m :提交时的说明文字;
-p :在commit时,将容器暂停。

容器通信-创建子节点链接中心节点: docker run -tid (--name xxx) (--link CONTAINER_NAMES) IMAGE ID

查看容器ip: cat /ect/hosts

1
2
3
4
5
6
7
8
9
root@b68943564e76:/# cat /etc/hosts 
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 test 4e02c004539e
172.17.0.3 b68943564e76

▲初次使用, ifconfig使用无效,ping也无效,原因如下

我创建的容器是拉取的Base镜像,而因为用Docker拉取的Base镜像如Centos和Ubuntu的话都是最简版本,不包含Ping工具,而对Docker进行Docker network和Docker链接操作时往往要用到Ping工具测试两个容器间的网络,此时就要用到Ping工具了,下面是Ubuntu下安装Ping工具的命令:apt-get update && apt-get install iputils-ping、安装ifconfig工具:apt install net-tools

从容器里面拷文件到宿主机

docker cp 容器名:要拷贝的文件在容器里面的路径 要拷贝到宿主机的相应路径

宿主机上执行:$ docker cp testtomcat:/usr/local/tomcat/webapps/test/js/test.js /opt

从宿主机拷文件到docker容器里面

docker cp 要拷贝的文件路径 容器名:要拷贝到容器里面对应的路径

宿主机上执行$ docker cp requirements.txt apptest:/app

▲注:容器NAME可以通过docker ps命令查看

Dockerfile指令

Dockerfile文件D一定要大写

$ docker build -t="dormanctpress/df_test2" .

  • .为Dockerfile的路径

1.FROM <image>:<tag>: 指定拉取base镜像

2.MAINTAINER: 作者信息

3.RUN

  • 镜像构建中运行的命令,主要用于安装库

4.EXPOSE

  • 如果是端口的话,只是声明会使用xx端口,但是实际使用时还是需要run -p xx $ docker build -t="dormanctpress/df_test2" .
  • 设置环境变量
  1. CMD
  • 在容器运行时运行的默认命令(命令行中添加的命令可以代替掉Dokcerfile中写的CMD指令)
  • 每个Dockerfile只能够包含一个CMD,多个CMD 只有最后一个能有效
  • 定义的三种方式
    • CMD <cmd>这个会当做/bin/sh-c“cmd”来执行
    • CMD[“executable",“arg1”,“arg2”]
      • 一定要双引号
    • CMD[“arg1",“arg2”],这个时候CMD作为ENTRYPOINT的参数
  1. ENTRYPOINT
    • 每个Dockerfile只能够包含一个ENTRYPOINI,多个ENTRYPOINT只有最后一个能有效
    • 不会像CMD命令一样被覆盖,除非使用
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
         - 当定义了ENTRYPOINT后,CMD将被作为参数使用。如果定义了CMD,那么`docker run -t test/osf2 xxxx`后面即xxxx的内容全会被当做参数传递ENTRYPOINT

      组合使用ENTRYPOINT和CMD: ENTRYPOINT指定命令,CMD指定默认参数

      7. ADD&COPY

      共同点

      - ADD&COPY的源必须在context路径下
      - 当在源代码构建的方式下,可以通过ADD和COPY的方式,把Host上的文件或者目录复制到Image

      不同点:

      ADD

      - 包含类似tar**解压**的功能

      - 当遇到`<scr>`参数为URL时候,可以通过URL**下载**文件并且复制到`<dest>`,这个在任何build的方式下都可以Work

      COPY

      - COPY是ADD的一种简化版本,目的在于满足大多数人“复制文件到容器”的需求。
      - Docker 团队的建议是在大多数情况下使用COPY。拷贝文件的原则:使用COPY(除非你明确你需要ADD)

      9. VOLUME['/data']
      - 设置数据卷,暴露给外部使用者映射
      10. WORKDIR /path/to/workdir
      - 一般使用绝对路径
      11. EXPOSE
      - 暴露端口给外部使用者映射
      12. `ENV <key> <value>`
      - 用来设置环境变量,后续的RUN可以使用它所创建的环境变量。
      - 当创建基于该镜像的Container的时候,会自动拥有设置的环境变量
      13. USER daemon

      - 指定UID或者username,来决定运行RUN指令的用户
      - 如果不指定,则默认root

      13. ONBUILD
      - 触发器

      14. ARG
      - `ARG` 指令定义了一个变量,用户可以在构建时使用 `docker build` 命令使用`--build-arg <varname>=<value>` 标志将其传递给构建器。如果用户指定了未在 Dockerfile 中定义的构建参数,则构建会输出警告。

      ### dockerfile构建过程

      1. 从基础镜像运行一个容器
      2. 执行一条指令,对容器做出修改
      3. 执行类似docker commit的操作,提交一个新的镜像层
      4. 再基于刚提交的镜像运行一个新容器
      5. 执行Dockerfile中的下一条指令,直至所有指令执行完毕



      ### 使用中间层镜像进行调试

      查找错误

      ### Dockerfile 镜像缓存

      构建缓存:将之前的镜像缓存

      不使用缓存`docker build --no-cache`

      ### 查看镜像构建的过程

      查看构建过程:`docker history xxx/yyy`



      ## 数据卷

      `sudo docker run -v ~/container_data:/data -it ubuntu /bin/bash`

      参数说明:使用-v选项,第一个参数为宿主机目录,:后的目录为在容器中使用的目录(挂载);ubuntu为镜像

      ### 查看容器是否挂载了数据卷

      `docker inspect CONTAINER_ID`可以查看容器是否挂载了数据卷

      ### 给数据卷增加权限

      `sudo docker run -v ~/datavolume:/data:ro -it ubuntu/bin/bash`

      ro是read-only

      ## 数据卷容器

      > 命名的容器挂载数据卷,其他容器通过挂载这个容器实现数据共享,挂载数据卷的容器,就叫做数据卷容器

      挂载数据卷容器的方法

      `docker run--volumes-from [CONTAINER NAME]`



      ## MySQL通信

      ```shell
      >>> can't connect to MySQL server on '172.17.0.8'(111)
      $ vim /etc/mvsal/my.cnf
      # 将bind-address=127.0.0.1注释掉
      >>> Host '2eaf92ef2ff6'is not allowed to connect to this MySQL server
      $ create user "weiwei"@"%"identified by"weijc7789"
      # 创建用户
      $ grant create, select, update, delete, insert on *.* to weiwei;
      # 增添权限
      $ mysq1-h 172.17.0.8-u weiwei-p
      # 链接

redis通信

1
2
3
4
5
6
7
$ redis-cli -h 172.17.0.8
>>> Could not connect to Redis at 172.17.0.8:6379:Connection refused
$ vim /etc/redis /redis.conf
#bind-address=127.0.0.1注释掉

# 如果修改后/ect/init.d/redis-server restart后还是链接不了,重启进入容器
$ redis-cli-h 172.17.0.8-p 6379

一条命令实现停用并删除容器:

1
docker stop $(docker ps -q) & docker rm $(docker ps -aq)

一条命令删除所有镜像:

1
docker rmi `docker images -q`

一条命令删除创建失败的镜像:

docker images | sed -n '2p' | awk '{print$3}' | xargs docker rmi

https://www.jianshu.com/p/658911a8cff3)

坑点记录

docker容器启动后马上退出解决方案

dokcerfile中的最后一个命令不能在后台执行,不然会启动后马上退出

原因

Docker容器同时只能管理一个进程,如果这个进程退出那么容器也就退出了,但这不表示容器只能运行一个进程(其他进程可在后台运行),但是要使容器不退出必须有一个前台执行的进程。

解决方法

脚本中最后一个进程一定要用前台运行方式即在进程最后不加&(&表示后台运行),否则容器会退出。

如何正确的使用docker attach

Q:由于执行着uwsgi --ini uwsig.ini命令,用户就无法直接进入到容器中去,docker attach CONTAINER_id 就会一直卡着。

A: attach早已过时了,可用: docker exec -it containerID /bin/bash,一开始使用的是/bin/sh然后还是一直卡住.但是/bin/bash是可以的

附Uuntu版本代号:

版本号 代号 发布时间
18.04 Bionic Beaver(仿生海狸) 即将发布2018年4月(LTS)
17.10 Artful Aardvark(机灵的土豚) 2017年10月
16.04 LTS Xenial Xerus 好客的非洲地松鼠 即将发布 2016/4
15.10 Wily Werewolf 狡诈的狼人 2015/10/22
15.04 Vivid Vervet 活泼的小猴 2015/04/23
14.10 Utopic Unicorn 乌托邦独角兽 2014/10/23
14.04 LTS Trusty Tahr 值得信赖的塔尔羊 2014/04/18
13.10 Saucy Salamander 活泼的蝾螈 2013/10/17
13.04 Raring Ringtail 铆劲浣熊 2013/04/25
12.10 Quantal Quetzal 缤纷的绿咬鹃 2012/10/18
12.04 LTS Precise Pangolin 精准的穿山甲 2012/04/26
11.10 Oneiric Ocelot 有梦的虎猫 2011/10/13
11.04 Unity成为默认桌面环境 Natty Narwhal 敏捷的独角鲸 2011/04/28
10.10 Maverick Meerkat 标新立异的的狐獴 2010/10/10
10.04 LTS Lucid Lynx 清醒的猞猁 2010/04/29
9.10 Karmic Koala 幸运的无尾熊 2009/10/29
9.04 Jaunty Jackalope 活泼的兔子 2009/04/23
8.10 Intrepid Ibex 无畏的高地山羊 2008/10/30
8.06 官方查不到此版本发布信息 Haughty Husky 骄傲的哈士奇 2008/06/07
8.04 LTS Hardy Heron 坚强的苍鹭 2008/04/24
7.10 Gutsy Gibbon 勇敢的长臂猿 2007/10/18
7.04 Feisty Fawn 烦躁不安的小鹿 2007/04/19
6.10 Edgy Eft 尖利的小蜥蜴 2006/10/26
6.06 LTS Dapper Drake 整洁的公鸭 2006/06/01
5.10 Breezy Badger 活泼的獾 2005/10/13
5.04 Hoary Hedgehog 白发得刺猬 2005/04/08
**4.10 **初始发布版本 Warty Warthog 多疣的疣猪 2004/10/20

docker stop containerID,以后再重新启动时报错

1
Error response from daemon: driver failed programming external connectivity on endpoint app (3c0c01ac6b42b7a420644fc2b4debfbbcc0e1d2e74e6294155c28aca82f794c4):  (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 12345 -j DNAT --to-destination 172.17.0.2:80 ! -i docker0: iptables: No chain/target/match by that name.

A: 重启docker即可:systemctl restart docker

再开启你的容器: docker run xxxxdocker start xxx

查看docker容器的运行日志

docker logs containerID

docker端口映射到宿主机后外网仍无法访问容器的web

A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 解决办法:
$ sudo vi /etc/sysctl.conf
或者
$ sudo vi /usr/lib/sysctl.d/00-system.conf
# 添加如下代码:
net.ipv4.ip_forward=1

重启network服务
$ sudo systemctl restart network

查看是否修改成功
$ sudo sysctl net.ipv4.ip_forward

#如果返回为“net.ipv4.ip_forward = 1”则表示成功了

附录

安装docker

由于ubuntu维护的docker版本比较老,所以建议使用docker自己维护的版本

法一:

较为麻烦

1
2
3
4
5
6
7
8
9
sudo apt-get update
sudo apt-get install -y apt-transport-https
echo deb https://get.docker.com/ubuntu docker main>/etc/apt/
sources.list.d/docker.list
# 添加仓库的key
sudo apt-key adv--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
#安装
sudo apt-get update
sudo apt-get install -y lxc-docker

法二:

docker简易安装方法->脚本

1
2
3
4
5
6
# 如果没有安装curl,先安装curl
$ sudo apt-get install -y curl
# 淘汰!
$ curl -sSL https://get.docker.com/ubuntu/ | sudo sh
# wget ==> 2021年10月25日 淘汰!
$ wget -qO- https://get.docker.com/ | sh

法三:https://docs.docker.com/engine/install/ubuntu/ 推荐:2021年10月25日

Docker容器互访——创建bridge网络尝试

  1. 创建容器
1
2
3
docker run -itd --name network_test_mysql -p 11111:3306 --network testnet --network-alias mysqlhost -e MYSQL_ROOT_PASSWORD="aaa123123" daocloud.io/library/mysql:5.7.5-m15

docker run -it --name network_test_app6 --network testnet --network-alias mysql1 -e MYSQL_ROOT_PASSWORD="123456" daocloud.io/library/mysql:5.7.5-m15
  1. 在第二个容器中测试是否能连接到第一个容器的mysql服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql -uroot -p123456
# ✌: 会默认进入localhost:3306
mysql -hmysql -uroot -p123456
# ERROR 2005 (HY000): Unknown MySQL server host 'mysql' (110)
mysql -hmysqlhost -uroot -p123456
# ERROR 1045 (28000): Access denied for user 'root'@'network_test_app6.testnet' (using password: YES)
# ▲通过网络别名进入
mysql -hmysqlhost -uroot -paaa123123
# ✌: 会进入第一个容器的mysql服务, 且访问的直接是容器中的3306端口, 所以可以直接进入(区分宿主机暴露的11111端口)

# 通过容器名进入
mysql -hnetwork_test_mysql -uroot -p123456

# 注: 暴露的11111是非容器间访问时暴露的端口,如我的PC上想要连接这个容器的话,需要如下连接
mysql -h49.235.118.244 -uroot -paaa123123 -P11111
# ✌

docker网络官方文档:https://docs.docker.com/network/network-tutorial-standalone/——在用户定义的网络上,如alpine-net,容器不仅可以通过 IP 地址进行通信,还可以将容器名称解析为 IP 地址。此功能称为自动服务发现。让我们连接alpine1并测试一下。alpine1应该能够解析 alpine2alpine4(和alpine1,本身)到 IP 地址。

上传自己的镜像

  1. 先编写自己的镜像文件:Dockerfile,可以如下
1
2
3
4
5
6
7
FROM python:3.7.4-alpine
# MAINTAINER Mrli 1063052964@qq.com
LABEL maintainer="Mrli 1063052964@qq.com"
ADD ./app /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT [ "python", "test.py" ]
  1. 然后运行docker build -t userName/repositoryName:tagName构建镜像,userName/repositoryName指定镜像名,tagName指定版本

  2. 登陆docker镜像仓库:dockerhub或者ali的,

    • dockerhub:docker login
    • ali:docker login --username=南邮果粒橙 registry.cn-hangzhou.aliyuncs.com
  3. 推送镜像到镜像仓库

    • dockerhub: docker push nymrli/python:v1,由于dockerhub我的用户名是nymrli,加上创建的仓库名叫python,所以dockerhub给我的链接nymrli/python(仓库名可能重名,但加上用户名的选项后只要用户名不重名,那么这个镜像名就不会重名),push的参数为也为这个

    • Ali:

      1
      2
      3
      4
      $ docker login --username=南邮果粒橙 registry.cn-hangzhou.aliyuncs.com
      $ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/mrli_docker_images/backend:[镜像版本号]
      # mrli_docker_images/backend为<命名空间>/<仓库名>
      $ docker push registry.cn-hangzhou.aliyuncs.com/mrli_docker_images/backend:[镜像版本号]

推送

注:

  • FROM的镜像名是python:<版本号>-<镜像特性名>
  • 镜像最初的工作路径为/根目录,根目录下任何东西都没有,需要自己拷贝过去

Travis+Docker[+阿里云容器镜像、dockerhub]

travis主要是关注在CI持续集成上,同时能完成持续交付(持续集成的基础上,增加打包构建形成产物)的工作。因此本次就以travis+docker来完成这么一个持续集成与持续交付的步骤:

步骤如下:

  1. travis官网绑定travis和github

  2. Dockerhub创建镜像仓库 or 阿里云开通容器镜像服务

  3. 项目中添加.travis.yml和Dockerfile文件

    1
    2
    3
    4
    5
    6
    7
    FROM python:3.9.1-alpine
    MAINTAINER Mrli 1063052964qq.com
    RUN tar -zcf app.tar.gz .
    ADD app.tar.gz /app
    WORKDIR /app
    RUN pip install -r local_requiements.txt
    ENTRYPOINT python app/main.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    language: python
    # sudo: enabled or required
    python:
    - "3.9.1"

    branches:
    only:
    - master

    install:
    - pip install -r local_requirements.txt

    before_script:
    - flake8 app tests
    - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD # dockerfile中需要拉取 python-3.9.1:alipine的镜像, 所以需要先docker login

    script:
    - python app/main.py
    - docker build -t nymrli/python:v2 .

    after_success:
    - docker login -u $username -p $password # 这里我们使用环境变量来控制,避免写死。环境变量,在travis的后台settings里面添加
    - docker push
    - echo "success"

    services: # 需要提供docker才能在里面使用docker命令
    - docker
  4. git push提交代码,travis会侦听github仓库变化触发githook从而执行任务

  5. 登录要运行app的服务器,docker-compose up将镜像启动

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    version: '2'
    services:
    mysql:
    container_name: mysql1
    image: mysql:5.7
    environment:
    MYSQL_ROOT_PASSWORD: 数据库密码
    ports:
    - "3306:3306"
    volumes:
    - /usr/local/docker/mysql/data:/var/lib/mysql
    - /usr/local/docker/mysql/conf:/etc/mysql
    - /usr/local/docker/mysql/logs:/var/log/mysql
    web:
    container_name: jafir_nginx1
    image: registry.cn-hangzhou.aliyuncs.com/jafir_docker_images/web:latest
    ports:
    - 8080:80
    restart: always
    depends_on:
    - java
    volumes:
    - /mydockerdata/nginx/etc/nginx.conf:/etc/nginx/nginx.conf
    - /mydockerdata/nginx/log/:/var/log/nginx
    java:
    container_name: jafir_gps1
    image: registry.cn-hangzhou.aliyuncs.com/jafir_docker_images/jafir-images:latest
    restart: always
    depends_on:
    - mysql
    ports:
    - 9090:9090
    volumes:
    - /mydockerdata/java/gps/upload:/Users/jafir/Downloads/upload
    - /mydockerdata/arme/out/*.nofoo.cn/*.nofoo.cn.pfx:/Users/jafir/Downloads/upload/cert/*.nofoo.cn.pfx

注:如果需要travis的构建过程中在服务器执行一些命令(如将代码部署发到自己的服务器上),还需要配置生成公钥和私匙,参看:

docker-compose使用[转]

docker-compose常见命令

  • ps:列出所有运行容器
1
docker-compose ps
  • logs:查看服务日志输出
1
docker-compose logs
  • port:打印绑定的公共端口,下面命令可以输出 eureka 服务 8761 端口所绑定的公共端口
1
docker-compose port eureka 8761
  • build:构建或者重新构建服务
1
docker-compose build
  • start:启动指定服务已存在的容器
1
docker-compose start eureka
  • stop:停止已运行的服务的容器
1
docker-compose stop eureka
  • rm:删除指定服务的容器
1
docker-compose rm eureka
  • up:构建、启动容器
1
docker-compose up
  • kill:通过发送 SIGKILL 信号来停止指定服务的容器
1
docker-compose kill eureka
  • pull:下载服务镜像
  • scale:设置指定服务运气容器的个数,以 service=num 形式指定
1
docker-compose scale user=3 movie=3
  • run:在一个服务上执行一个命令
1
docker-compose run web bash

docker-compose.yml 字段含义

  • version:指定 docker-compose.yml 文件的写法格式
  • services:多个容器集合
  • build:配置构建时,Compose 会利用它自动构建镜像,该值可以是一个路径,也可以是一个对象,用于指定 Dockerfile 参数
1
2
3
4
5
6
7
build: ./dir
---------------
build:
context: ./dir
dockerfile: Dockerfile
args:
buildno: 1
  • command:覆盖容器启动后默认执行的命令
1
2
3
command: bundle exec thin -p 3000
----------------------------------
command: [bundle,exec,thin,-p,3000]
  • dns:配置 dns 服务器,可以是一个值或列表
1
2
3
4
5
dns: 8.8.8.8
------------
dns:
- 8.8.8.8
- 9.9.9.9
  • dns_search:配置 DNS 搜索域,可以是一个值或列表
1
2
3
4
5
dns_search: example.com
------------------------
dns_search:
- dc1.example.com
- dc2.example.com
  • environment:环境变量配置,可以用数组或字典两种方式
1
2
3
4
5
6
7
environment:
RACK_ENV: development
SHOW: 'ture'
-------------------------
environment:
- RACK_ENV=development
- SHOW=ture
  • env_file:从文件中获取环境变量,可以指定一个文件路径或路径列表,其优先级低于 environment 指定的环境变量
1
2
3
4
env_file: .env
---------------
env_file:
- ./common.env
  • expose:暴露端口,只将端口暴露给连接的服务,而不暴露给主机
1
2
3
expose:
- "3000"
- "8000"
  • image:指定服务所使用的镜像
1
image: java
  • network_mode:设置网络模式
1
2
3
4
5
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
  • ports:对外暴露的端口定义,和 expose 对应
1
2
3
ports:   # 暴露端口信息  - "宿主机端口:容器暴露端口"
- "8763:8763"
- "8763:8763"
  • links:将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况
1
2
links:    # 指定服务名称:别名 
- docker-compose-eureka-server:compose-eureka
  • volumes:卷挂载路径
1
2
3
volumes:
- /lib
- /var
  • logs:日志输出信息
1
2
3
4
--no-color          单色输出,不显示其他颜.
-f, --follow 跟踪日志输出,就是可以实时查看日志
-t, --timestamps 显示时间戳
--tail 从日志的结尾显示,--tail=200

更新容器

  • 当服务的配置发生更改时,可使用 docker-compose up 命令更新配置
  • 此时,Compose 会删除旧容器并创建新容器,新容器会以不同的 IP 地址加入网络,名称保持不变,任何指向旧容起的连接都会被关闭,重新找到新容器并连接上去
  • 服务之间可以使用服务名称相互访问,links 允许定义一个别名,从而使用该别名访问其它服务
1
2
3
4
5
6
7
8
version: '2'
services:
web:
build: .
links:
- "db:database"
db:
image: postgres
  • 这样 Web 服务就可以使用 db 或 database 作为 hostname 访问 db 服务了

Dockerfile RUN,CMD,ENTRYPOINT命令区别

RUN命令执行命令并创建新的镜像层,通常用于安装软件包

CMD命令设置容器启动后默认执行的命令及其参数,但CMD设置的命令能够被docker run命令后面的命令行参数替换

ENTRYPOINT配置容器启动时的执行命令(不会被忽略,一定会被执行,即使运行 docker run时指定了其他命令)

Shell格式和Exec格式运行命令

我们可用两种方式指定 RUN、CMD 和 ENTRYPOINT 要运行的命令:Shell 格式和 Exec 格式:

  • Shell格式:<instruction> <command>。例如:apt-get install python3
    • 多条命令用&&链接
  • Exec格式:<instruction> ["executable", "param1", "param2", ...]。例如: ["apt-get", "install", "python3"]

CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。

总结

  • 使用 RUN 指令安装应用和软件包,构建镜像。
  • 如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命令行替换默认参数。
  • 如果想为容器设置默认的启动命令,可使用 CMD 指令。用户可在 docker run 命令行中替换此默认命令。

Docker 运行python flask的web程序

1创建镜像

1.1 ubuntu16.04+python3.6

18.04卡在了PPA环节,并且git安装也没安装上,后来使用了dockerHub上搜素到的github仓库中的16.04 Xenial就解决了。

注:镜像TAG版本需要到dockerHub上才能查看,最初下载成18.04就是因为这个原因被坑了

18.04PPA问题:

aptsources.distro.NoDistroTemplateException: Error: could not find a distribution template for Ubuntu/bionic意思是18.04该PPA没有资源.bionic是版本代号,如16.04的 Xenial

⑴使用下载好的Xenial的Dockerfile进行创建镜像docker run 1604ubuntu .

为了使用国内源用阿里云,先编辑一个sources.list,放在dokcerfile同目录下,作为docker创建镜像时的上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse

⑵根据官方的镜像来编写自己的Dockerfile创建具有工具的Ubuntu1604

涉及交互式选择项(如下),docker build的时候会报错。设置 DEBIAN_FRONTEND=noninteractive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
FROM 1604ubuntu
MAINTAINER mrli
#用ubuntu国内源替换默认源
RUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/sources.list

#安装python3.6必要的包。源镜像太精简了,ip ifconfig之类的都没有。后续安装python pip也需要一些。但是build_essential似乎不必须,先去了。如果后面安装numpy之类需要gcc了,再加上
RUN apt update
#RUN apt upgrade

RUN apt install -y apt-utils apt-transport-https vim iproute2 net-tools ca-certificates curl build-essential wget python-software-properties software-properties-common psmisc

#安装python3.6 来自第三方
RUN add-apt-repository ppa:jonathonf/python-3.6
RUN apt update
RUN apt install -y python3.6
RUN apt install -y python3.6-dev
RUN apt install -y python3.6-venv

#为3.6安装pip
RUN wget https://bootstrap.pypa.io/get-pip.py
RUN python3.6 get-pip.py

#设置默认python为python3
RUN update-alternatives --install /usr/bin/python python /usr/bin/python2 100
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 150

#和自带的3.5共存,设置python3默认为3.6
#RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2

# 更新配置
RUN update-alternatives --config python3
#print()时在控制台正常显示中文
ENV PYTHONIOENCODING=utf-8

在dockerfile所在路径下执行,建立image

1
docker build -t uos:1604 .

因为开头几步用了国内源,所以非常快。

1
2
3
# 设置apk源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# 设置时区

1.2 开发环境

再建一个dockerfile,开头使用刚才建立的镜像uos1604

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
FROM uos:1604
MAINTAINER mrli

#代码复制过来后的路径
RUN mkdir /app
# 指定容器启动时执行的命令都在app目录下执行
WORKDIR /app

# 将本地app目录下的内容拷贝到容器的app目录下
COPY ./app/ /app/

# 安装nginx
RUN apt -y install nginx mysql-server

RUN /etc/init.d/nginx start
# 替换nginx的配置
RUN rm /etc/nginx/sites-enabled/default
RUN cp nginx.conf /etc/nginx/sites-enabled/nginx.conf

RUN pip3 install uwsgi

#安装需要的python库
# 启动nginx和uwsgi
#ENTRYPOINT pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple some-package --no-cache-dir && service nginx restart && uwsgi --ini uwsgi.ini

# 为了保证能之后进入所以最后一个命令为/bin/sh
ENTRYPOINT pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple some-package --no-cache-dir && service nginx restart && uwsgi --ini uwsgi.ini & && /bin/sh

创建uflask镜像:docker build -t uflask .

根据镜像创建运行容器:docker run -tid -p 12345:80 flaskdemo IMAGE_ID

此时就可以通过VPS的IP地址:宿主机端口访问这个应用程序

查看日志:docker logs 应用名(NAMES)docker logs flaskdemo

关于mysql的建议

mysql建议作为单独容器来跑数据库,然后远程连接数据库.或是使用数据卷

1
2
3
4
5
6
# 搜索
# docker search mysql
# 拉取
# docker pull mysql:5.7
#运行
# docker run --name mysql5.7 -e MYSQL_ROOT_PASSWORD=123456 -p 3307:3306 -d mysql:5.7

2启动容器(转)

  • Docker

    一种开源容器应用,供开发者打包自己的开发环境,可以任意移植

  • docker-compose
    一种管理多个Docker容器的工具,可以简化我们启动容器的各种命令

2.1手动敲docker命令

先试试用docker命令行启动容器:

1
docker run --name quotation_api -itd -p 5000:5000 -v /home/quotation:/code quotation_dev:latest

用到的参数分别是

–name为容器命名;

-itd 输入输出终端,后台运行

-p host端口:容器端口

  • 将宿主机5000端口的请求转发到容器5000端口,用5000是flask默认

-v host路径:容器内路径(挂载数据卷)

quotation_dev:latest 最后是使用的镜像名(前面刚用dockerfile build出来的)

然后进入容器

1
docker attach quotation_api

用python3 main.py启动flask,OK。

这样flask运行在docker里了。

在host改代码,可以看见docker的控制台在更新,和在host一样了。(使用数据卷)

2.2使用dock-compose

如果没有安装先进行安装apt install docker-compose

dock-compose用来管理多个container的运行,特别适合1个host上跑多个container的情况。

得天独厚,docker官网上dock-compose的gettingstarted文档就是flask的(说明flask+docker代表了先进生产力的前进方向O Yeah!),看完了基本就能用了。

dock-compose采用yaml作为配置文件。查了一下,yaml参考了xml和json,以及python的语法,采用了python之缩进,无XML之标记,无json之括号,无字符串之引号。特别适合作为配置文件用。

建立docker-compose.yaml文件(无镜像,但有dockerfile):

1
2
3
4
5
6
version: '2' # 表示该 Docker-Compose 文件使用的是 Version 2 file
services:
docker-demo: # 指定服务名称
build: . # 指定 Dockerfile 所在路径
ports: # 指定端口映射
- "9000:8761"

建立docker-compose.yaml文件(已有镜像):

1
2
3
4
5
6
7
8
9
10
version: "3"

services:
quotation_api: # 指定服务名称
image: quotation_dev:latest # 指定镜像
volumes: # 选择数据卷
- /home/quotation:/code
ports: # 端口映射
- "5000:5000"
command: python3 main.py # 执行命令

基本对应手动敲的docker命令,最后还省了敲python3 main.py

当然如果是部署,这句可以用CMD 写进Dockfile。但是开发过程,文件名之类的会改变,比如最终部署运行用可能是gunicorn+wsgi.py,所以还是写在dockerfile外面比较方便

运行,在控制台执行:docker-compose updocker-compose up -d // 后台启动并运行容器

docker-compose更多介绍

关于Dockerfile中docker-entrypoint.sh 文件的用处

很多著名库的 Dockerfile 文件中,通常都是 ENTRYPOINT 字段会是这样:ENTRYPOINT ["docker-entrypoint.sh]

https://blog.csdn.net/u013272009/article/details/84073136: 在启动容器时,可以通过docker-entrypoint.sh的 shell 脚本执行些预处理逻辑(如运行多个脚本), 然后再exec $@把启动容器入口正式交给使用者,即,需要容器启动预处理的,都可以使用 docker-entrypoint.sh 机制

★===> 采坑记录:原因是dockerfile中虽然RUN命令可以有多个,但那个主要是在构建过程中的,因此如果构建完成后容器运行时需要执行脚本,RUN命令是不行,只能用ENTRYPOINT,但ENTRYPOINT命令为了简洁命令看来,就只执行docker-entrypoint.sh ,然后将要运行脚本的命令写在docker-entrypoint.sh 里就行了

shell脚本变量宏:

  • $@所有参数列表。如$@用「"」括起来的情况、以"$1""$2"…."$n"的形式输出所有参数。
    $
  • $#: 添加到shell的参数个数
  • $0: shell本身的文件名

用户权限

在执行mvn package docker:build出现com.spotify.docker.client.shaded.javax.ws.rs.ProcessingException: java.io.IOException: Permission denied需要查看下docker是否安装正常,docker是否打开,当前用户是否具有执行docker命令的权限(我的个人账号mrli就没有权限,需要将其加入docker用户组,否则得加sudo)

1
2
3
sudo groupadd docker #添加docker用户组
sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
newgrp docker #更新用户组

参考:http://www.ityouknow.com/springboot/2018/03/19/spring-boot-docker.html

踩坑记录

Alpine镜像

Alpine Linux: 当你想为你的Python开发环境选择一个基础镜像时,大多数人都会选择Alpine,为什么?因为它太小了,仅仅只有 5 MB 左右(对比 Ubuntu 系列镜像接近 100 MB)

然而问题是,假设我们的python应用需要做一些科学计算,无法直接pip安装,因为大多数Linux发行版都使用GNU版本的标准C库(glibc),而Alpine Linux使用的是musl,那些二进制安装包是针对glibc编译的,因此Alpine禁用了Linux安装包支持。从而导致了Alpine的致命问题:标准的Linux安装包在Alpine Linux上根本无法使用。这也就意味着你需要自己弄清楚每一个系统库的依赖性。事先编译好需要的依赖,重新改写Dockerfile.alpine:

1
2
3
# alpine 中 pip 安装Scipy 等科学相关的包时报错, 需要预先安装的库
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk --update-cache --no-cache add tzdata gcc make freetype-dev gfortran musl-dev g++ libgcc libquadmath musl libgfortran lapack-dev

然而经过测试,这样安装会耗时非常久,并且安装过后体积也并不小了,因此,这种情况下不太适合使用alpine做为镜像。因为alpine系统对很多库支持都不够友好

注:python-alpine是一个极简的镜像,只有6.5M左右,但在选择使用时会产生比较多的问题,所以如果系统存储够用或者公司对镜像大小没有限制的话,还是建议使用比较完整的镜像来跑程序,这样会省事很多。

RUN命令

RUN命令只会在构建时候执行,踩坑记录RUN sh repeat.sh

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
echo "Start Record"

ENV_DIR=$(cd $(dirname $0);pwd)
# source $ENV_DIR/env/bin/activate
# pyenv activate py38
nohup python -u checkRepeatSituation.py 1> r1.log 2>err.log &
nohup python -u checkRepeatSituation1.py 1> r2.log 2>err.log &
nohup python -u checkRepeatSituation3.py 1> r3.log 2>err.log &
nohup python -u checkRepeatSituation4.py 1> r4.log 2>err.log &
nohup python -m visdom.server &

docker exec -it xxx bash进入时ps aux并没有这些python进程,是因为RUN后面的repeat.sh脚本只在构建过程中执行

VOLUMN特性

Volume, on the other hand, just lets a container run from your image have access to some path on whatever local machine the container is being run on. You cannot use files from your VOLUME directory in your Dockerfile. Anything in your volume directory will not be accessible at build-time but will be accessible at run-time.

翻译:另一方面,"卷 "只是让从你的镜像中运行的容器能够访问该容器所运行的任何本地机器上的一些路径。你不能在你的Docker文件中使用VOLUME目录中的文件。在你的volume目录中的任何东西在构建时不能访问,但在运行时可以访问。

★===>在构建时只要指明了 VOLUMN /app/logs,就不能再往里面放数据COPY draw.sh /app/logs,如果还是要放入,则可以在entrypoint.sh中使用mv在运行时移动文件到/app/logs目录中

作用:

  • 存放运行时生成的持久化数据
  • 存放配置文件==>容器关闭后仍然存在,继续运行时能够读取
  • 跨容器共享文件

注:与VOLUMN的区别是,ADD的内容作为镜像一部分,是构建时的内容;而VOLUMN的更多是运行时的数据

From:https://stackoverflow.com/questions/27735706/docker-add-vs-volume

ADD使用说明:

  • 如果源路径是个文件,且目标路径是以 / 结尾, 则docker会把目标路径当作一个目录,会把源文件拷贝到该目录下。
    如果目标路径不存在,则会自动创建目标路径。
  • 如果源路径是个文件,且目标路径是不是以 / 结尾,则docker会把目标路径当作一个文件。
    如果目标路径不存在,会以目标路径为名创建一个文件,内容同源文件;
    如果目标文件是个存在的文件,会用源文件覆盖它,当然只是内容覆盖,文件名还是目标文件名。
    如果目标文件实际是个存在的目录,则会源文件拷贝到该目录下。 注意,这种情况下,最好显示的以 / 结尾,以避免混淆。
  • 如果源路径是个目录,且目标路径没有/结尾
    • 如果目标路径不存在,则docker会自动以目标路径创建一个目录,把源路径目录下的文件拷贝进来
    • 如果目标路径是个已经存在的目录,则docker会把源路径目录下的文件拷贝到该目录下。如ADD log_utils/ /app就没有/app/log_utils文件夹,但log_utils中的内容全在/app目录下
  • 如果源路径是个目录,且目标路径以/结尾,那么将会把原路径目录整个文件夹放在目标路径下来,如ADD log_utils/ /app/就会有/app/log_utils文件夹
  • 如果源文件是个归档文件(压缩文件),则docker会自动帮解压。

除了最后的归档解压一点以外,在其他拷贝特性上,add和copy一致

测试

1
2
3
4
5
6
7
8
9
10
11
FROM python:3.9
MAINTAINER Mrli 1063052964@qq.com

WORKDIR /app # 会生成/app 目录
VOLUME /cl_logs # 会生成/cl_logs目录

COPY test.txt /abc # 当src为文件时,abc会当做是根目录下的文件
COPY test.txt /abcd/ # 当dest指明为文件夹时, 会创建/abcd目录,并复制拷贝到dest目录
ADD test.txt /ddd # 当src为文件时,abc会当做是根目录下的文件
ADD log_utils/ /app # 到src为文件夹时, 会把log_utils中的内容拷贝到app中
ADD log_utils/ /app/ # 到src为文件夹时, 会把log_utils文件夹拷贝到app中

Dockfile构建过程中路径问题

路径如下:

1
2
3
4
projectPath/
|-requirements.txt
|-log_utils
|-requirements.txt

其中,因为报过requirements.txt找不到的错误,因此对文件存在的时机和路径进行了思考。得出结论:dockerfile文件在构建过程中,不能够访问到至少同级目录的文件。宿主机里项目中的sourceFile只有ADD命令能接触到。dockfile构建的过程中是访问不到dockerfile目录下的文件的,要用的话,只能add、copy复制进来后再在容器中正常使用。

因此对于整个工程文件都要导入的话,比较主流的做法是,在build构建过程种git clone或者entry-poind.sh里进行clone

entrypoint.sh

由于脚本都是在Linux下运行的,因此entrypoint.sh脚本最好是在Linux下编写,否则\r\n问题属实让人头疼

至于bash和sh的使用,更推荐bash,用户更友好

Dockerfiles记录

批量删除shell脚本

  • 删除所有None镜像docker rmi $(docker images | grep "^<none>" | awk '{print $3}')
  • 删除所有app容器docker rm $(docker ps -a | grep "app" | awk '{print $1}' )
  • 删除6-8行的容器:docker rm $(docker ps -a | grep "Exited" | awk '{print $1}' | sed -n '6, 8p')

修改已创建容器的启动

1
2
3
4
# 修改成总是尝试重启
docker container update --restart=always mysql57
# 停止
docker [container] update --restart=no <container-id>

Author: Mrli

Link: https://nymrli.top/2019/07/25/Docker使用/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
Vim记忆
NextPost >
Python中的正则匹配
CATALOG
  1. 1. Docker学习
    1. 1.1. docker和虚拟机VM的区别
      1. 1.1.1. 一、本质上的区别:
      2. 1.1.2. 二、使用上的区别:
    2. 1.2. 宿主机、镜像与容器
    3. 1.3. Docker命令
    4. 1.4. Dockerfile指令
    5. 1.5. redis通信
  2. 2. 坑点记录
    1. 2.1. docker容器启动后马上退出解决方案
      1. 2.1.1. 如何正确的使用docker attach
      2. 2.1.2. 附Uuntu版本代号:
      3. 2.1.3. docker stop containerID,以后再重新启动时报错
      4. 2.1.4. docker端口映射到宿主机后外网仍无法访问容器的web
      5. 2.1.5.
  3. 3. 附录
    1. 3.1. 安装docker
    2. 3.2. Docker容器互访——创建bridge网络尝试
    3. 3.3. 上传自己的镜像
    4. 3.4. Travis+Docker[+阿里云容器镜像、dockerhub]
      1. 3.4.1. 步骤如下:
    5. 3.5. docker-compose使用[转]
      1. 3.5.0.1. docker-compose常见命令
      2. 3.5.0.2. docker-compose.yml 字段含义
      3. 3.5.0.3. 更新容器
      4. 3.5.0.4. links
  4. 3.6. Dockerfile RUN,CMD,ENTRYPOINT命令区别
  5. 3.7. Docker 运行python flask的web程序
  6. 3.8. 1创建镜像
    1. 3.8.1. 1.1 ubuntu16.04+python3.6
    2. 3.8.2. 1.2 开发环境
    3. 3.8.3. 关于mysql的建议
  7. 3.9. 2启动容器(转)
    1. 3.9.1. 2.1手动敲docker命令
    2. 3.9.2. 2.2使用dock-compose
  8. 3.10. 关于Dockerfile中docker-entrypoint.sh 文件的用处
    1. 3.10.1. 用户权限
  • 4. 踩坑记录
    1. 4.1. Alpine镜像
    2. 4.2. RUN命令
    3. 4.3. VOLUMN特性
    4. 4.4. ADD使用说明:
    5. 4.5. Dockfile构建过程中路径问题
    6. 4.6. entrypoint.sh
  • 5. Dockerfiles记录
  • 6. 批量删除shell脚本
    1. 6.1. 修改已创建容器的启动