第一天走进docker世界

走进docker的世界

介绍 + 认识docker

docker容器是什么
docker是通过内核虚拟化技术来提高容器的资源隔离和资源限制。由于docker通过操作系统层的虚拟化试下隔离,(对系统操作的内核有要求),所以docker容器在运行时,无需类似虚拟机额外的操作系统开销,从而比虚拟机更轻量化。docker是一种软件的打包技术。docker引擎版本主要有企业版EE;社区版CE。
为什么选择docker
更高效的利用系统资源
更快的启动时间
一致性的环境
持续交互和部署
更轻松的迁移
docker理念
docker的主要目标是构建,运输,处处运行。
构建:制作docker镜像,打包容器的所有系统文件目录。
运输:下载docker镜像
运行:基于docker镜像提供的rootfs,启动容器
docker的架构组成

在这里插入图片描述

docker daemon:安装使用docker得先运行docker daemon进程,用于管理docker,如:镜像,容器,网络数据集。
rest接口:提供daemon交互的API接口
docker client:客户端使用restapi 和docker daemon进行访问
images:镜像是一个只读模板,用于创建容器,也可以通过dockerfile文件描述镜像的内容
Registry:docker镜像需要进行管理,docker提供了registry(注册表)仓库,其实他是一个容器,可以用于基于该容器运行私有仓库。也可以使用docker hub 联网公有仓库。
container:容器是一个镜像的运行实例。
创建容器的过程
获取镜像,docker pull centos 从镜像仓库拉取
使用镜像创建容器
分配文件系统,挂着读写从,在读写从加载镜像
分配网络/网桥接口,创建一个网络接口,让容器和宿主机通信
容器获取IP
执行容器命令
反馈容器启动结果
小结
Docker是一种CS架构的软件产品,可以把代码及依赖打包成镜像,作为交付介质,并且把镜像启动成为容器,提供容器生命周期的管理

docker组成:docker daemon | restapi接口 | docker client | iamges | registry | container
需要掌握的内容
了解docker理念,熟悉docker架构组成

docker的安装与部署

安装
环境
配置好防火墙及selinux,同步时间,修改hostname,可通外网
配置宿主机网卡转发
# 若没有配置,需执行如下
cat <<EOF >  /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward=1
EOF

# sysctl修改内核运行时参数,-p从配置文件/etc/sysctl.d/加载内核参数设置
sysctl -p /etc/sysctl.d/docker.conf
yum安装docker
2.# 下载阿里源的repo文件
先备份
mkdir /etc/yum.repos.d/bak
mv /etc/yum.repos.d/*.repo  /etc/yum.repos.d/bak

curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum clean all && yum makecache

2.# yum 安装
yum install docker-ce -y

# 查看源中可用版本
yum list docker-ce --showduplicates | sort -r

#也可以指定版本安装
yum install -y docker-ce-18.09.9

3.#配置源加速
mkdir -p /etc/docker
vim /etc/docker/daemom.json
{
  "registry-mirrors": [
    "https://8xpk5wnt.mirror.aliyuncs.com"
  ]
}

4.# 设置开机启动
systemctl enable docker
systemctl daemon-reload
systemctl start docker

5.# docker详细信息
docker info

6.#docker是一个cs架构
# c端:docker-client
~]# which docker
/usr/bin/docker

# s端:docker daemon
 ~]# ps aux | grep docker 
root       6796  0.0  0.8 749652 73012 ?        Ssl  09:47   0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

# 容器container
 ~]# ps aux | grep containerd
root       6784  0.6  0.5 667652 45708 ?        Ssl  09:47   0:04 /usr/bin/containerd
root       6796  0.0  0.9 749652 75396 ?        Ssl  09:47   0:00 /usr/bin/dockerd -H fd:// --containerd=/run/container/containerd.sock

7.# docker 命令帮助信息
docker --help
docker run --help
小结
修改尽量做到备份
配置宿主机网卡转发
同步时间,注意防火墙等

核心要素及常用操作

在这里插入图片描述

三大核心要素镜像images容器container仓库registry

**镜像:**打包了业务代码及运行环境的包,是静态的文件,不能直接对外提供服务

**容器:**镜像的运行时,可以对外提供服务

**仓库:**存放镜像的地方

  • 公有仓库,docker hub ,阿里,网易 …

  • 私有仓库,企业内部搭建

    • docker registry, docker官方提供的镜像仓库存储服务

    • harbor,是docker registry的更高级封装,它除了提供友好的web ui界面,还提供了角色和用户权限管理,用户操作审计等功能

  • 镜像访问地址形式 registry.devops.com/demo/hello:latest。若没有前面的url地址,则默认寻找docker hub中的镜像,若没有tag标签,则使用latest作为标签。比如:docker pull nginx,会被解析成docker.io/library/nginx:latest

  • 公有仓库中,一般存在这么几类镜像

    • 操作系统基础镜像(centos,ubuntu,suse,alpine)
    • 中间件(nginx,redis,mysql,tomcat)
    • 语音编译环境(python,java,golang)
    • 业务镜像(Django-demo…)

容器和仓库不会直接交互,都是以镜像未载体来操作的。

基本操作

1.查看镜像列表

docker images

2.获取镜像

#从远处仓库拉取
docker pull nginx:alpine

#本地构建
docker build . -t my-nginx:ubuntu  -f Dockerfile
#参数解释:
docker build 用于指定dockerfile创建镜像
-t, --tag 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签
-f 指定要使用的dockerfile路径

3.通过镜像启动容器

docker run --name my-nginx-alpine -d nginx:alpine

# 参数解释:
docker run 创建一个新的容器并运行一个命令
--name 为容器指定一个名称
-d 后台运行容器,并返回容器id

4.如何知道容器内部运行了什么程序

# 进入容器内部
$ docker exec -it my-nginx-alpine /bin/sh

# 容器内部执行
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   33 nginx     0:00 nginx: worker process
   34 nginx     0:00 nginx: worker process
   35 nginx     0:00 nginx: worker process
   36 nginx     0:00 nginx: worker process
   43 root      0:00 sh
   49 root      0:00 ps aux

# 参数解释:
docker exec 在运行的容器中执行命令
-t 分配一个伪终端
-i 即使没有附加也保存STDIN(标准输入)打开
-d 分离模式,在后台运行

5.docker如何指定容器启动后该执行什么命令

通过docker build 来模拟构建一个Nginx镜像

1.创建dockerfile
mkdir demo
cd demo
vim Dockerfile

# 告诉docker使用哪个基础镜像作为模板,后续命令都以这个镜像为基础
FROM ubuntu

# RUN命令会在上面指定的镜像里面执行命令
RUN apt-get update && apt install -y nginx

# 告诉docker,启动容器时执行如下命令
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

# 参数解释:
# -g 从配置文件中设置全局指令
# daemon off 后台运行
# docker容器中pid为1的进程结束, 容器也就停止运行,所以要加daemon off

2.构建本地镜像
docker build . -t my-nginx:ubuntu -f Dockerfile

3.使用新镜像启动容器
docker run --name my-nginx-ubuntu -d my-nginx:ubuntu

4.进入容器查看进程
docker exec -ti my-nginx-ubuntu /bin/sh
# ps aux
# apt install -y  curl
# curl localhost:80

6.如何访问容器内的服务

docker exec -ti my-nginx-alpine  curl localhost:80

或进入容器内部访问
docker exec -ti my-nginx-alpine /bin/sh
# ps aux | grep nginx
# curl localhost:80

7.宿主机中如何访问容器服务

## 删除旧服务,重新做做端口映射启动
docker rm -f my-nginx-alpine
docker run --name my-nginx-alpine -d -p 8080:80 nginx:alpine
curl 192.168.178.79:8080

#参数解释:
-p 指定端口映射,格式为:主机端口:容器端口
-P 随机端口映射,容器内部端口随机映射到主机的端口,格式同上
rm 删除容器,-f 删除运行中的容器
操作演示

在这里插入图片描述

1.查看所有镜像

docker images

2.拉取镜像

docker pull name:tang

例:docker pull nginx:alpine

3.如何唯一确定镜像

  • image_id
  • repository:tag
例:
 ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
my-nginx     ubuntu    a626628023ce   2 days ago    160MB
nginx        alpine    a6eb2a334a9f   5 days ago    22.6MB

4.导出镜像到文件中

docker save -o nginx-alpine.tar  nginx:alpine
docker save 将指定镜像保存成tar归档文件
-o 输出到文件
nginx:alpine 表示要指定的镜像

5.从文件中加载镜像

docker load -i nginx-alpine.tar
docker load 导入使用docker save命令导出的镜像
-i 指定导入的文件,代替STDIN

6.部署镜像仓库

https://docs.docker.com/registry/

# 使用docker镜像启动镜像仓库服务
docker run -d -p 5000:5000 --restart always --name registry registry:2
--restart always docker在重启的时候,只要启动docker,就自动启动这个容器

## 默认仓库不带认证,若需要认证参考:
https://docs.docker.com/registry/deploying/#restricting-access

7.推送本地镜像到镜像仓库中

docker tag nginx:alpine localhost:5000/nginx:alpine
docker push localhost:5000/nginx:alpine 

## 镜像仓库给外部访问,不能通过localhost,可以使用内网地址
 ~]# docker tag nginx:alpine 192.168.178.79:5000/nginx:alpine
 ~]# docker push 192.168.178.79:5000/nginx:alpine
The push refers to repository [192.168.178.79:5000/nginx]
Get https://192.168.178.79:5000/v2/: http: server gave HTTP response to HTTPS client

#docker默认不允许向http的仓库地址推送,如何做成https的,可以参考如下:
https://docs.docker.com/registry/deploying/#run-an-externally-accessible-registry

# 我们没有可信证书机构颁发的证书或域名,自签名证书需要再每个节点中拷贝证书文件,比较麻烦,因此我们通过配置daemon的方式,来跳过证书的验证:
 ~]# vim /etc/docker/daemon.json 
 ~]# cat /etc/docker/daemon.json
{
  "registry-mirrors" : [
    "https://8xpk5wnt.mirror.aliyuncs.com"
  ],
  "insecure-registries": [
     "192.168.178.79:5000"
  ]
}
 ~]# systemctl restart docker
 ~]# docker push 192.168.178.79:5000/nginx:alpine
 ~]# docker images
REPOSITORY                  TAG       IMAGE ID       CREATED       SIZE
192.168.178.79:5000/nginx   alpine    a6eb2a334a9f   5 days ago    22.6MB
nginx                       alpine    a6eb2a334a9f   5 days ago    22.6MB
ubuntu                      latest    7e0aa2d69a15   5 weeks ago   72.7MB
registry                    2         1fd8e1b0bb7e   6 weeks ago   26.2MB

8.删除镜像

docker rmi [REPOSITORY]  
docker rmi  [IMAGE ID] 

9.查看容器列表

## 查看运行状态的容器列表
docker ps 

## 查看全部状态的容器列表
docker ps -a

-a 显示所有的容器,包括未运行的
-f 根据条件过滤显示的内容,docker ps -f name='关键字搜索'
-l 显示最近创建的容器
-n 列出最近创建的n个容器
-q 静默模式,只显示容器编号
-s 显示总的文件大小

10.启动容器

## 启动
docker run --name nginx -d nginx:alpine

## 映射端口,把容器的端口映射到宿主机中,-p 格式:宿主机端口:容器端口
docker run --name my-nginx -d -p 8080:80 nginx:alpine

## 资源限制,最大可用内存500M
 docker run --name lb-nginx -d --memory=500m  nginx:alpine

## 更多参考 docker run --help

11.容器数据持久化

挂载

# 挂载主机目录
docker run --name nginx -d -v /opt:/opt nginx:alpine
docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d -v /opt/mysql/:/var/lib/mysql mysql:5.7

#参数解释:
-v 绑定一个卷,给容器挂载存储卷,挂载到容器的某个目录
-e 设置环境变量
# 宿主机目录是/opt/mysql,容器目录是/var/lib/mysql ,默认没有会自动创建

验证数据:
 ~]# ll /opt/mysql/
total 188484
-rw-r----- 1 polkitd input       56 May 31 16:03 auto.cnf
-rw------- 1 polkitd input     1680 May 31 16:03 ca-key.pem
...

使用volumes卷

# 查看volumes
docker volume  ls

# 创建
docker volume create my-vol

# 删除
docker volume rm my-vol

#创建volume并运行容器
docker run --name test-1 -d -v my-vol:/opt/my-vol nginx:alpine

# 查看容器是否运行
~]# docker ps -a -f name=test
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS     NAMES
1d178f091f89   nginx:alpine   "/docker-entrypoint.…"   About a minute ago   Up About a minute   80/tcp    test-1

# 不进入容器,但在容器内创建数据
docker exec -ti 1d178f091f89 touch /opt/my-vol/a.py


# 验证数据共享
# 再创建一个容器,查看数据是否存在
 ~]# docker run --name test-2 -d -v my-vol:/opt/hh nginx:alpine
c729a96f6f582b8006b34147a50667826705c0f0c637d432683515d9f2c03b38
~]# docker exec -ti test-2 ls /opt/hh
a.py

12.进入容器或者执行容器内的命令

docker exec -ti <container_id_or_name> /bin/sh
docker exec <container_id_name> hostname

13.主机与容器直接拷贝数据

## 主机拷贝到容器
 ~]# echo "love python" >> /tmp/test.py
 ~]# docker cp /tmp/test.py test-1:/tmp
 ~]# docker exec -ti test-1 cat /tmp/test.py
love python

## 容器拷贝到主机
 ~]# docker cp test-1:/tmp ./
 ~]# ls /tmp/
test.py  vmware-root

14.查看容器日志

## 查看全部日志
docker logs container_name

## 实时查看最新日志
docker logs -f nginx

## 从最新的100条开始查看
docker logs --tail=100 -f nginx

15.停止或删除容器

## 停止运行中的容器
docker stop nginx

## 启动退出容器
dockerstart nginx

## 删除非运行状态的容器
docker rm nginx

## 删除运行中的容器
docker rm -f nginx

## 过滤出指定容器,然后删除
docker rm -f `docker ps -f name=nginx -q`

16.查看容器或者镜像的明细

# 查看容器详细信息,包括容器IP地址等
docker inspect nginx | more

#查看镜像的明细信息
docker inspect nginx:alpine

# 显示 Docker 系统信息,包括镜像和容器数
docker info

17.挂载已有的数据,重新创建镜像仓库容器

1.导入本地进行文件
 ~]# tar -xzf  registry.tar.gz -C /opt/
 ~]# ls /opt/registry/
docker

2.删除当前镜像仓库文件
 ~]# docker rm -f registry 
registry

3.使用docker镜像启动镜像仓库服务
docker run -d -p 5000:5000 --restart always -v /opt/registry:/var/lib/registry --name registry registry:2

4.检查启动情况
 ~]# docker ps -a | grep registry
3e8ff4e819a0   registry:2     "/entrypoint.sh /etc…"   26 seconds ago      Up 25 seconds      0.0.0.0:5000->5000/tcp, :::5000->5000/tcp  registry

5.拉取镜像成功,则表示挂载成功
 ~]# docker pull 192.168.178.79:5000/centos:centos7.5.1804
 ~]# docker images | grep centos
192.168.178.79:5000/centos   centos7.5.1804   cf49811e3cdb   2 years ago   200MB

假设启动镜像仓库服务的主机地址为192.168.178.79,该目录中已存在的镜像列表:

现镜像仓库地址原镜像仓库地址
192.168.178.79:5000/coreos/flannel:v0.11.0-amd64quay.io/coreos/flannel:v0.11.0-amd64
192.168.178.79:5000/mysql:5.7mysql:5.7
192.168.178.79:5000/nginx:alpinenginx:alpine
192.168.178.79:5000/centos:centos7.5.1804centos:centos7.5.1804
192.168.178.79:5000/elasticsearch/elasticsearch:7.4.2docker.elastic.co/elasticsearch/elasticsearch:7.4.2
192.168.178.79:5000/fluentd-es-root:v1.6.2-1.0quay.io/fluentd_elasticsearch/fluentd:v2.5.2
192.168.178.79:5000/kibana/kibana:7.4.2docker.elastic.co/kibana/kibana:7.4.2
192.168.178.79:5000/kubernetesui/dashboard:v2.0.0-beta5kubernetesui/dashboard:v2.0.0-beta5
192.168.178.79:5000/kubernetesui/metrics-scraper:v1.0.1kubernetesui/metrics-scraper:v1.0.1
192.168.178.79:5000/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
192.168.178.79:5000/jenkinsci/blueocean:latestjenkinsci/blueocean:latest
192.168.178.79:5000/sonarqube:7.9-communitysonarqube:7.9-community
192.168.178.79:5000/postgres:11.4postgres:11.4
通过1号进程理解容器的本质
 ~]# docker run --name m-nginx-alpine -d nginx:alpine
81360b4251cf7384c709449ca1a6739f670098bc9cde0b78c4a92570a07689c1
 ~]# docker exec -ti m-nginx-alpine sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   32 nginx     0:00 nginx: worker process
   33 nginx     0:00 nginx: worker process
   34 nginx     0:00 nginx: worker process
   35 nginx     0:00 nginx: worker process
   36 root      0:00 sh
   42 root      0:00 ps aux

容器启动的时候可以通过命令去覆盖默认的CMD

$ docker run -d --name xxx nginx:alpine <自定义命令
# <自定义命令> 会覆盖镜像中指定的CMD指令,作为容器的1号进程启动

# 命令执行完毕,1号进程退出即容器退出
 ~]# docker ps -a | grep test-4
a27336cd5b62   nginx:alpine   "/docker-entrypoint.…"   18 seconds ago      Exited (0) 17 seconds ago                                               test-4

# 可以看出只有1号进程存在,容器即存在
 ~]# docker run -d --name test-5  nginx:alpine ping www.baidu.com
376ce88c92a7a2ae2dd85149775874b811fc5fdd4e5f427b1383a3c9cb3929ce
 ~]# docker ps -a | grep test-5
376ce88c92a7   nginx:alpine   "/docker-entrypoint.…"   5 seconds ago        Up 5 seconds                    80/tcp                                      test-5

本质上讲容器是利用namespace和cgroup等技术在宿主机中创建独立的虚拟空间,这个空间内的网络,进程,挂载等资源都是隔离的。

 ~]# docker exec -ti m-nginx-alpine sh
/ # ifconfig | awk 'NR==2{print $2}'
addr:172.17.0.8
/ # ls /
bin                   etc                   mnt                   run                   tmp
dev                   home                  opt                   sbin                  usr
docker-entrypoint.d   lib                   proc                  srv                   var
docker-entrypoint.sh  media                 root                  sys

# 在容器内安装软件,创建修改文件等,对宿主机和其他容器没有任何影响,和虚拟机不同的是,容器间共享一个内核,所以容器内没法升级内核,且容器会随着1号进程的消亡而退出容器。
容器的七种状态
create 已创建
restarting 重启中
running 运行中
removing 迁移中
paused 暂停
exited 停止
dead 死亡
其他常用知识点

可以使用关键字进行搜索官方仓库中的镜像,并且可以使用docker pull 命令来将它下载到本地

docker search ubuntu

熟悉常用命令即可,其他命令可以使用–help 获取帮助

docker --help
docker run --help

标记本地镜像,将其归入某一仓库

docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

例如:
docker tag nginx:alpine 192.168.178.79:5000/nginx:alpine

杀掉一个运行中的容器

docker kill -s KILL nginx
小结
1.学习了容器的安装
2.容器的操作注意围绕三大核心要素,即镜像,容器,仓库
3.学会容器的基本操作,使用日志排查故障
4.通过1号进程理解容器的本质

小技巧:
可以使用 docker ps -aq 获取容器编号,批量删除容器,还可以通过-f过滤
需要掌握的内容
熟练掌握常用命令,不知道的可以通过--help查询帮助,
docker run : -v -e -d --name 等参数
docker create 
docker ps -a -q -f 等参数
docker logs --tail -f 参数
docker images
docker pull | push
docker cp 
docker rm -f | rmi -f

数据的持久化

学会使用官方网站查看:https://docs.docker.com/desktop/

Django应用容器化实践

Django项目介绍
  • 项目地址:https://gitee.com/agagin/python-demo.git

  • python3 + Django + uwsgi + nginx + mysql

  • 内部服务端口8002

构建命令
$ docker build . -t ImageName:ImageTag -f Dockerfile

如何理解构建镜像的过程?

dockerfile是一堆指令,在docker build 的时候,按照该指令进行操作,最终生成我们期望的镜像

  • FROM 指定基础镜像,必须为第一个命令

    格式:
    	FROM <image>
    	FROM <image>:<tag>
    
    示例:
    	FROM mysql:5.7
    注意:
    	tag是可选的,如果不使用TAG时,会使用latest版本的基础镜像
    
  • MAINTAINER

    格式:
    	MAINTAINER
    示例:
    	MAINTAINER name
        MAINTAINER e-mail
        MAINTAINER name or e-mail
    
  • COPY/ADD添加本地文件到镜像中

    格式:  
    	copy <src>...<dest>    【源地址  目标地址】
    示例:
    	ADD hom* /mydir/  		# 添加所以以"hom"开头的文件
    	ADD test relativeDir 	# 添加 "test" 到 `WORKDIR`  /relativeDir/
    	ADD test /absoluteDir	# 添加 "test" 到 /absoluteDir/
    	
    注意:ADD相对COPY 来说多一个解压,或者远程自动下载的功能
    【以工作目录,WORKDIR为 absolute决定路径,relative相对路径】
    
  • WORKDIR工作目录

    格式:
    	WORKDIR /path/to/workdir
    示例:
    	WORKDIR /a (这时工作目录为a)
    注意:
    	通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN,CMD,ENTRYPOINT,ADD,COPY等命令都会在该目录下执行,且启动容器会默认进入工作目录。
    
  • RUN 构建镜像过程中执行命令

    格式:
    	RUN <command>
    示例:	
    	RUN yum install nginx
    	RUN pip install django
    	RUN mkdir test && rm -rf /var/lib/unusedfiles
    注意:
    	RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
    
  • CMD 构建容器后调用,也就是在容器启动时才会调用

    格式:
        CMD ["executable","param1","param2"] (执行可执行文件,优先)
        CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
        CMD command param1 param2 (执行shell内部命令)
    示例:
        CMD ["/usr/bin/wc","--help"]
        CMD ping www.baidu.com
    注意:
    	CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
    	
    【executable 可执行文件;param参数;entrypoint进入节点】
    
  • ENTRYPOINT 设置容器初始化命令,使其可执行化

    格式:
    	ENTRYPOINT ["executable","param1","param2"] (可执行文件,优先)
    	ENTRYPOINT command param1 param2 (shell内部命令)
    示例:
    	ENTRYPOINT ["/usr/bin/wc","--help"]
    注意:
    	ENTRYPOINT与CMD非常类似,不同的是通过docker run 执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile 中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。docker还可以使用entrypoint来和CMD的参数相拼接。
    
  • ENV

    格式:
    	ENV <key> <value>
    	ENV <key>=<value>
    示例:
    	ENV myName John
    	ENV myCat=Alex
    
  • EXPOSE

    格式:
    	EXPOSE <port> [<port>...]
    示例:
    	EXPOSE 80 443
    	EXPOSE 8080
    	EXPOSE 11211/tcp 11211/udp
    注意:
    	EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
    

在这里插入图片描述

Dockerfile
# This my first django Dockerfile
# Version 1.0

# Base images 基础镜像
FROM centos:centos7.5.1804

#MAINTAINER 维护者信息
LABEL maintainer="inspur_lyx@hotmail.com"

#ENV 设置环境变量
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8

#RUN 执行以下命令
RUN curl -so /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo && rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y  python36 python3-devel gcc pcre-devel zlib-devel make net-tools nginx

#工作目录
WORKDIR /opt/myblog

#拷贝文件至工作目录
COPY . .

# 拷贝nginx配置文件
COPY myblog.conf /etc/nginx

#安装依赖的插件
RUN pip3 install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt

RUN chmod +x run.sh && rm -rf ~/.cache/pip

#EXPOSE 映射端口
EXPOSE 8002

#容器启动时执行命令
CMD ["./run.sh"]
开始部署
# 拉代码
 ~]# git clone https://gitee.com/agagin/python-demo.git

# 查看
 ~]# cd python-demo/
 python-demo]# ll
total 20
drwxr-xr-x 4 root root  160 May 31 20:59 blog
drwxr-xr-x 2 root root   22 May 31 20:59 log
-rw-r--r-- 1 root root  804 May 31 20:59 manage.py
drwxr-xr-x 2 root root   74 May 31 20:59 myblog
-rw-r--r-- 1 root root 1228 May 31 20:59 myblog.conf
-rw-r--r-- 1 root root   44 May 31 20:59 requirements.txt
drwxr-xr-x 4 root root   74 May 31 20:59 resources
-rw-r--r-- 1 root root   84 May 31 20:59 run.sh
drwxr-xr-x 3 root root   19 May 31 20:59 static
-rw-r--r-- 1 root root  239 May 31 20:59 uwsgi.ini

#复制上面dockerfile
vim Dockerfile

#构建
docker build . -t myblog:v1 -f Dockerfile
运行MySQL
docker run -d -p 3306:3306 --name mysql -v /opt/mysql:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7


# 验证启动是否成功
docker ps | grep mysql

## 查看数据库
 ]# docker exec -ti mysql bash
root@6b709e3a88b9:/# mysql -uroot -p123456
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| myblog             | # 自动生成这个即正常,若是没有生成,手动创建即可
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

启动Django应用
# 启动容器
 docker run -d -p 8002:8002 --name myblog -e MYSQL_HOST=192.168.178.79 -e MYSQL_USER=root -e MYSQL_PASSWD=123456 myblog:v1

# 验证
docker ps | grep myblog

## 迁移 migrate
[root@docker python-demo]# docker exec -ti myblog bash
[root@172c1e8a0301 myblog]# pwd	#默认进入到了工作目录
/opt/myblog
# 容器内执行如下:
#/ python3 manage.py makemigrations  
#/ python3 manage.py migrate
#/ python3 manage.py createsuperuser

# 可以使用env查看环境变量

# 收集静态文件
docker exec -ti myblog python3 manage.py collectstatic
# 访问 192.168.178.79:8002/admin

#构建镜像,替换默认编码,让他支持中文
[root@docker python-demo]# docker exec -ti mysql sh
#  mysql -p123456        
mysql> show variables like '%character%';
[root@docker python-demo]# pwd
/root/python-demo
[root@docker python-demo]# mkdir mysql
[root@docker python-demo]# cd mysql/
mysql配置文件
vi my.cnf
$ cat my.cnf
[mysqld]
user=root
character-set-server=utf8
lower_case_table_names=1

[client]
default-character-set=utf8
[mysql]
default-character-set=utf8

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

dockerfiles/mysql/Dockerfile

[root@docker mysql]# pwd
/root/python-demo/mysql
[root@docker mysql]# vim Dockerfile
[root@docker mysql]# cat Dockerfile 
FROM mysql:5.7
COPY my.cnf /etc/mysql/my.cnf
## CMD 或者 entrypoint默认继承

#构建
docker build . -t mysql:5.7-utf8

#将镜像推到本地仓库,防止别的机器使用,
docker tag mysql:5.7-utf8 192.168.178.79:5000/mysql:5.7-utf8
docker push 192.168.178.79:5000/mysql:5.7-utf8

# 删除旧的mysql容器,使用新镜像启动,不用再次初始化
docker ps -a | grep mysql

# 因为编码已经写入到文件中,因此删除数据库文件
rm -rf /opt/mysql

#重新使用新镜像启动
docker run -d -p 3306:3306 --name mysql -v /opt/mysql/mysql-data/:/var/lib/mysql -e MYSQL_DATABASE=myblog -e MYSQL_ROOT_PASSWORD=123456 192.168.178.79:5000/mysql:5.7-utf8

# 重新迁移migrate
$ docker exec -ti myblog bash
#/ python3 manage.py makemigrations  #生成数据库同步脚本
#/ python3 manage.py migrate       #迁移数据
#/ python3 manage.py createsuperuser #创建一个超级用户

访问: http://192.168.178.79:8002/admin/

小结
初识python3 + Django + uwsgi + nginx + mysql
了解dockerfile编写方法
需要掌握
Dockerfile文件的编写

容器实现原理

容器实现原理

虚拟化核心需要解决的问题:资源隔离与资源限制

  • 虚拟机硬件虚拟化技术,通过一个hypervisor【虚拟】层实现对资源的彻底隔离
  • 容器则是操作系统级别的虚拟化,利用的是内核的Cgroup和Namespace特性,此功能完全通过软件实现。
Namespace资源隔离

命名空间是全局资源的一种抽象,将资源放到不同的命名空间中,各个命名空间中的资源是相互隔离的。

分类系统调用参数相关内核版本
Mount namespacesCLONE_NEWNSLinux 2.4.19
UTS namespacesCLONE_NEWUTSLinux 2.6.19
IPC namespacesCLONE_NEWIPCLinux 2.6.19
PID namespacesCLONE_NEWPIDLinux 2.6.24
Network namespacesCLONE_NEWNET始于Linux 2.6.24 完成于 Linux 2.6.29
User namespacesCLONE_NEWUSER始于 Linux 2.6.23 完成于 Linux 3.8

docker在启动一个容器的时候,会调用Linux Kemel Namespace的接口,来创建一块虚拟空间,创建的时候,可以支持设置下面这几种(可以随意选择)。docker默认都设置。

  • pid:用于进程隔离(PID:进程ID)
  • net:管理网络接口(NET:网络)
  • ipc:管理对IPC资源的访问(IPC:进程间通信,(信号量,消息队列和共享内存))
  • mnt:管理文件系统挂载点(MNT:挂载)
  • uts:隔离主机名和域名
  • user:隔离用户和用户组
CGroup资源限制

通过namespace可以保证容器之间的隔离,但是无法控制每个容器可以占有多少资源,如果其中的某一个容器正在执行CPU密集型的任务,那么就会影响其他容器中任务的性能和执行效率,导致多个容器相互影响并抢占资源。|

在这里插入图片描述

Control Groups(简称CGroups)就是能够隔离宿主机上的物理资源,例如CPU,内存磁盘I/O和网络带宽。每一个CGroup都是一组被相同的标准和参数限制的进程。而我们需要做的就是把容器这个进程加入到指定的CGroup中,来实现对多个容器资源使用进行限制。

UnionFS联合文件系统

Linux namespace和cgroup分别解决了容器的资源隔离与资源限制,那么容器是很轻量的,通常每台机器中可以运行几十上百个容器,这些容器是共用一个image,还是各自将这个image复制了一份,然后各自独立运行呢?如果每个容器之间都是全量的文件系统拷贝,那么会导致至少如下问题:

  • 运行容器的速度会变慢
  • 容器和镜像对宿主机的磁盘空间的压力

怎么解决这个问题—Docker的存储驱动

  • 镜像分层存储
  • UnionFS

Docker 镜像是由一系列的层组成的,每层代表Docker中的一条指令,比如下面的Dockerfile文件:

FROM ubuntu:15.04
COPY ./app
RUN make /app
CMD python /app/app.py

这里的 Dockerfile 包含4条命令,其中每一行就创建了一层,下面显示了上述Dockerfile构建出来的镜像运行的容器层的结构:

在这里插入图片描述

镜像就是由这些一层一层堆叠起来的,镜像中的这些层都是只读的,当我们运行容器的时候,就可以在这些基础层上添加新的可写层,也就是我们通常说的容器层,对于运行中的容器所做的所有更改(比如写入新文件,修改现有文件,删除文件)都将写入到这个容器层。

对容器层的操作,主要利用了写时复制(CoW)技术。CoW就是copy-on-write,表示只在需要写时才去复制,这个是针对已有文件的修改场景。 CoW技术可以让所有的容器共享image的文件系统,所有数据都从image中读取,只有当要对文件进行写操作时,才从image里把要写的文件复制到自己的文件系统进行修改。所以无论有多少个容器共享同一个image,所做的写操作都是对从image中复制到自己的文件系统中的复本上进行,并不会修改image的源文件,且多个容器操作同一个文件,会在每个容器的文件系统里生成一个复本,每个容器修改的都是自己的复本,相互隔离,相互不影响。使用CoW可以有效的提高磁盘的利用率。
在这里插入图片描述

镜像中每一层的文件都是分散在不同的目录中的,如何把这些不同目录的文件整合到一起呢?

UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统联合到同一个挂载点的文件系统服务。 它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,整个联合的过程被称为联合挂载(Union Mount)。

在这里插入图片描述

上图是AUFS的实现,AUFS是作为Docker存储驱动的一种实现,Docker 还支持了不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和 Btrfs 等等,在最新的 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动,但是在没有 overlay2 驱动的机器上仍然会使用 aufs 作为 Docker 的默认驱动。

小结
容器的实现依赖于内核模块提供的namespace和control-group的功能,通过namespace创建一块虚拟空间,空间内实现了各类资源(进程,网络,文件系统)的隔离,提供control-group实现看对隔离空间的资源的使用的限制。

对容器层的操作,主要利用了写时复制(CoW)技术。

UnionFS 是一种为 Linux 操作系统设计的用于把多个文件系统联合到同一个挂载点的文件系统服务。  它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,整个联合的过程被称为联合挂载(Union Mount)。

docker网络原理

docker网络模式

我们在使用docker run创建docker容器时,可以用–net选型指定容器的网络模式,docker有以下四种网络模式:

  • bridge模式,使用–net=bridge指定,默认设置
  • host模式,使用–net=host指定,容器内部网络空间共享主机的空间,效果类似直接在宿主机上启动一个进程,端口信息和宿主机共用
  • container模式,使用–net=container:NAME_or_ID指定;指定容器与特定容器共享网络命名空间
  • none模式,使用–net=none指定网络模式为空,即保留网络命名空间,但是不做任何网络相关的配置(网卡,IP,路由等)
bridge模式

如果不指定的话,默认就会使用bride模式,bridge本意思桥的意思,其实就是网桥模式。我们怎么理解网桥,如果需要做类比的话,我们可以把网桥看出一个二层的交换机设备。

交换机通信简图

在这里插入图片描述

网桥模式示意图

在这里插入图片描述

Linux能够起到虚拟交换机作用的网络设备,是网桥。他是一个工作在数据链路层(data link)的设备,主要的功能是根据MAC地址将数据包转发到网桥的不同端口上

查看网桥

#下载安装
yum install -y  bridge-utils

# 查看
 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242cfb7aaca	no		veth265be16
							veth5ab2339
							veth7d72229
关于brctl命令参数说明和示例
参数说明示例
addbr <bridge>创建网桥brctl addbr br10
delbr <bridge>删除网桥brctl delbr br10
addif <bridge> <device>将网卡接口接入网桥brctl addif br10 eth0
delif <bridge> <device>删除网桥接入的网卡接口brctl delif br10 eth0
show <bridge>查询网桥信息brctl show br10
stp <bridge> {on|off}启用禁用 STPbrctl stp br10 off/on
showstp <bridge>查看网桥 STP 信息brctl showstp br10
setfd <bridge> <time>设置网桥延迟brctl setfd br10 10
showmacs <bridge>查看 mac 信息brctl showmacs br10
docker在启动一个容器时时如何实现容器间的互联互通的

Docker创建一个容器的时候,会执行如下操作:

  • 创建一对虚拟接口/网卡,也就是一对虚拟接口 veth pair

  • 本地主机一端桥接到默认的docker0或指定网桥上,并具有一个唯一的名字,如veth9953b75

  • 容器一端放到新启动的容器内部,并修改名字为eth0,这个网卡/接口只在容器的命名空间可见;

  • 从网桥可用地址段中(也就是与该bridge对应的network)获取一个空闲地址分配给容器的eth0

  • 配置默认路由到网桥

整个过程其实都是docker自动帮我们完成的,清理掉是所有的容器来验证:

# 清理所有容器
docker rm -f `docker ps -aq`
docker ps -a

# 查看网桥中的接口,目前是没有的
[root@docker ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242cfb7aaca	no	

# 创建测试容器test1
docker run -d --name test1 nginx:alpine

# 查看网桥中的接口,已经把test1 的veth端接入到网桥中
[root@docker ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242cfb7aaca	no		veth95f0f29

#已经在宿主机中可以查看到
[root@docker ~]# ip a | grep veth
131: veth95f0f29@if130: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 

#进入容器查看容器的eth0网卡及分配的容器IP
[root@docker ~]# docker exec -ti test1 sh
/ # ifconfig | awk 'NR==2{print $2}'
addr:172.17.0.2
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0



#再启动一个测试容器,测试容器间的通信
docker run -d --name test2 nginx:alpine
docker exec -ti test2 sh
/# curl 172.17.0.2:80

## 为啥可以通信
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0
# eth0 网卡是这个容器里的默认路由设备,所有对172.17.0.0/16网段的请求,也会被交给eth0来处理(第二条 172.17.0.0 路由规则),这条路由规则的网关(Gateway)是0.0.0.0,这就意味着这是一条直连规则,即:凡是匹配到这条规则的IP包,应该经过本机的eth0网卡,通过二层网络(数据链路层)直接发往目的主机。

# 网桥会维护一份Mac映射表,我们可以通过命令来查看一下
[root@docker ~]# brctl showmacs docker0
port no	mac addr		is local?	ageing timer
  1	32:76:d0:a3:0d:5c	yes		   0.00
  1	32:76:d0:a3:0d:5c	yes		   0.00
  2	86:4e:1c:5d:07:83	yes		   0.00
  2	86:4e:1c:5d:07:83	yes		   0.00
  
 # 这些Mac地址是主机端的veth网卡对于的Mac,可以查看运行
ip a |grep -n3 eth

在这里插入图片描述

我们如何指定网桥上的这些虚拟网卡与容器端是如何对应?

通过ifindex,网卡索引号

# 分别查看test1,test2 容器的网卡索引
[root@docker ~]# docker exec -ti test1 cat /sys/class/net/eth0/ifindex
130
[root@docker ~]# docker exec -ti test2 cat /sys/class/net/eth0/ifindex
134

#再通过在主机中找到虚拟网卡后面这个@ifxx的值,如果是同一个值,说明这个虚拟网卡和这个容器的eth0是配对的
[root@docker ~]#  ip a | grep @if
131: veth95f0f29@if130: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
135: veth25234c9@if134: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
容器与宿主机的通信

添加端口映射:

# 启动容器的时候通过-p 参数添加宿主机端口与容器内部服务端口的映射
docker run --name test -d -p 8080:80 nginx:alpine
curl localhost:8080

在这里插入图片描述

端口映射通过iptables如何实现
在这里插入图片描述

访问本机的8088端口,数据包会从流入方向进入本机,因此涉及到PREROUTING【路由前】和INPUT链,我们是通过做宿主机与容器之间加的端口映射,所以肯定会涉及到端口转换,那哪个表是负责存储端口转换信息的呢,那就是nat表,负责维护网络地址转换信息的。

# 查看一下PREROUTING链的nat表
[root@docker ~]# iptables -t nat -nvL PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   26  1488 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

参数解释:
-t 对指定的表进行操作
-n 以数字的方式显示ip,它会将ip直接显示出来,如果不加-n,则会将ip反向解析成主机名。
-v 详细模式;-vvv :越多越详细
-L 列出链chain上面的所有规则,如果没有指定链,列出表上所有规则

规则利用了iptables的addrtype【地址类型】扩展,匹配网络类型为本地的包

# 如何确定哪些是匹配本地的
[root@docker ~]# ip route show table local type local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 
local 172.17.0.1 dev docker0 proto kernel scope host src 172.17.0.1 
local 192.168.178.79 dev ens33 proto kernel scope host src 192.168.178.79 

也就是说目标地址类型匹配到这些的,会转发到我们的TARGET中,TARGET是动作,意味着对符合要求的数据包执行什么样的操作,最常见的为ACCEPT(接受)和DROP(终止),此处的target(目标)为docker,很明显docker不是标准的动作,那docker是什么呢?我们通常会定义自定义的链,这样把某类对应的规则放在自定义链中,然后把自定义的链绑定到标准的链路中,因此此处docker是自定义的链。那我们现在就来看一下docker这个自定义链上的规则。

[root@docker ~]# iptables -t nat -nvL DOCKER
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
   17  1020 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.2:80

此条规则就是对主机收到的目的端口为8080的tcp流量进行DNAT转换,将流量发往172.17.0.2:80,172.17.0.2地址是不是就是我们上面创建的docker容器的ip地址,流量走到网桥上了,后面就走网桥的转发就ok了。所以,外加只需要访问192.168.178.79:8080就可以访问到容器中的服务了。

数据包在出来方向走postrouting链,查看一下规则
[root@docker ~]#  iptables -t nat -nvL POSTROUTING
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   96  5925 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:80
    
MASQUERADE这个动作其实是一种更灵活的SNAT,把原地址转换成主机的出口ip地址,解释一下这条规则的意思:
这条规则会将原地址为172.17.0.0/16的包(也就是从docker容器产生的包),并且不是从docker0网卡发出的,进行原地址转换,转发成主机网卡的地址。大概的过程就是ACK的包在容器里面发出来,会路由到网桥docker0,网桥根据宿主机的路由规则会转给宿主机网卡eth0,这时候包就从docker0网卡转到eth0网卡了,并从eth0网卡发出去,这时候这条规则就会生效了,把源地址换成了eth0的ip地址。

注意一下,刚才这个过程涉及到了网卡间包的传递,那一定要打开主机的ip_forward转发服务,要不然包转不了,服务肯定访问不到。

host模式

容器内部不会创建网络空间,共享宿主机的网络空间,比如直接通过host模式创建mysql容器:

$ docker run --net host -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
$ curl localhost:3306
5.7.3Uid4ÿÿ󿿕1[M/Z5NGRy}mysql_native_password!ÿ#08S01Got packets out of order

容器启动后,会默认监听3306端口,由于网络是host,因为可以直接通过宿主机的3306端口进行访问服务,效果等同于在宿主机直接启动mysqld的进程。

container模式

这个模式指定新创建的容器和已经存在的一个容器共享一个network namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP,端口范围等。同样,两个容器除了网络方面,其他的如文件系统,进程列表等还是隔离的,两个容器的进程可以通过IO网卡设备通信。

在这里插入图片描述

# 启动容器测试,共享mysql的网络空间
$ docker run --net host -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
# 冒号后面是mysql,意思是新建一个容器,同时容器的网络指向mysql的网络空间
$ docker run -ti --rm --net=container:mysql busybox sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sh
   12 root      0:00 ps aux
/ # ifconfig | awk 'NR==2{print $2}'
addr:172.17.0.1
/ # netstat -tlp | grep 3306
tcp        0      0 :::3306                 :::*                    LISTEN      -
/ # telnet localhost 3306
Connected to localhost
J
5.7.34Z..bHeOÿ
             Nu/qXS
                   vl0mysql_native_password

!#08S01Got packets out of orderConnection closed by foreign host

## --rm 退出容器后,容器会自动删除

在一下特殊的场景中非常有用,例如kubernetes的pod,kubernetes为pod创建一个基础设施容器,同一pod下的其他容器都以container模式共享这个基础设施的网络命名空间,相互之间以localhost访问,构成一个统一的整体。

小结
了解容器网络原理
docker网络的四种模式:bridge,host,container,none
掌握容器与容器间,容器与宿主机之间的通信原理
使用技巧

1.清理主机上所有退出的容器

docker rm `docker ps -aq`
docker rm $(docker ps -aq)

2.调试或者排查容器启动错误

# 若有时遇到容器启动失败的情况,可以使用相同的镜像启动一个临时容器,先进入容器
docker run --rm -ti <image_id> sh

# 进入容器后,手动执行该容器对应的ENTRYPOINT或者CMD命令,这样即使出错,容器也不会退出,因为bash进程作为1号进程,我们只要不退出容器,该容器就不会自动退出。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值