百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Docker看这一篇入门就够了(docker0)

off999 2025-03-29 20:52 15 浏览 0 评论

Docker

安装Docker

Linux:

$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh

注意:如果安装了旧版本的Docker,需要使用对应的包管理工具卸载

Windows 10 或 MacOS(不支持黑苹果):

安装Docker Desktop:

https://docs.docker.com/engine/install/

当然,如果是带UI的Linux发行版也可以安装Linux版本的Docker Desktop

安装完成后在终端运行:

$ docker version

如果看到版本信息则代表安装成功?

Docker主要是为了解决软件的运行环境问题,当然,主要是Linux软件,举一个简单的例子

小明开发了一个网站,这个网站用到的技术包括Nginx,Python,MySQL,Redis,现在小明需要把这个网站部署上线,那么他至少要在一个服务器上做下面几件事

1、在服务器上安装Nginx,Python,MySQL,Redis软件

2、对机器和软件进行相关的配置设置,例如MySQL的端口号,Redis的端口号等

3、把自己开发的网站代码打包,并且上传到服务器上,然后启动相关服务

以上步骤是非常简化的版本,实际上如果真的要手动部署,可能会遇到更多问题

Docker就是解决上面这些问题的,有了Docker,小明在任何一台服务器上部署自己的软件,只需要简单的一个命令

$ docker run my-web-app

Docker会自动下载相关镜像,并且自动创建一个完全隔离的环境,然后运行相关的软件,这得益于LInux的底层隔离技术,包括Linux 命名空间、控制组和 UnionFS ,注意:Docker与虚拟机不同,虽然二者有点类似,但是Docker的隔离更轻量级,Docker并不是真正的虚拟机,Docker在底层是共享操作系统内核的,而虚拟机则不共享操作系统内核;

Docker命令:

Docker CLI的全量命令请参考:

https://docs.docker.com/engine/reference/commandline/docker/

这里以一个例子介绍常用的Docker命令以及常见参数

使用docker运行nginx

docker run可以直接运行一个容器,当本地不存在时,docker会从远端仓库拉取,默认的远端仓库是docker官方的docker hub,安装了Docker以后,基本上就不需要手动在服务器上安装任何软件了,假如你想运行一个nginx,那么你可以直接用下面的命令:

$ docker run nginx

输出:

Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/06/26 02:59:53 [notice] 1#1: using the "epoll" event method
2022/06/26 02:59:53 [notice] 1#1: nginx/1.21.5
2022/06/26 02:59:53 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/06/26 02:59:53 [notice] 1#1: OS: Linux 4.19.130-boot2docker
2022/06/26 02:59:53 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/06/26 02:59:53 [notice] 1#1: start worker processes
2022/06/26 02:59:53 [notice] 1#1: start worker process 30

这样,nginx就直接运行起来了,但是默认没有在后台运行,当按下Ctrl+C软件就退出了,可以使用-d参数,让docker在后台运行该容器

$ docker run -d nginx
182214dcd10b23bba2559116d13171745550a5a52c745aa699a895edeb55b7d0

docker 输出一串ID,nginx容器就在后台运行了,这个ID可以唯一标识一个容器,当需要输入这个ID时,大部分情况下并不需要输入完整的ID,输入前面几个字符就可以了

使用docker ps 查看正在运行的容器

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
182214dcd10b nginx "/docker-entrypoint.…" 14 minutes ago Up 14 minutes 80/tcp cool_williams

可以看到正在运行的容器ID,使用的镜像,创建的时间还有端口号,名字等信息。

使用 stop 命令停止容器

$ docker stop 182214dcd10b
$ 182214dcd10b

此时 docker ps无法查看该停止的容器

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

使用 docker ps -a 可以查看所有状态的容器

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
182214dcd10b nginx "/docker-entrypoint.…" 20 minutes ago Exited (0) About a minute ago cool_williams
325b084cc16a nginx "/docker-entrypoint.…" About an hour ago Exited (0) 20 minutes ago priceless_cori
c7e018e16d23 hello-world "/hello" 2 hours ago Exited (0) 2 hours ago frosty_euclid

注意,此时无法通过外部的浏览器访问这个nginx服务器,因为docker内部的端口并没有暴露出来,可以运行下面的命令进行端口映射

$ docker run -d -p 80:80 nginx

-p参数把docker内部的80端口映射到本机的80端口,打开浏览器,输入localhost就可以看到Nginx的欢迎页面了。

-v参数可以挂载本地文件目录,一般在运行数据库容器时,需要将数据持久化,否则在容器退出后所有的数据将丢失;例如:

$ docker run -d -p 80:80 -v /data/nginx:/data nginx

以上命令将本机的/data/nginx目录挂到到容器中的/data目录,如果容器中对/data目录的任何修改都将直接映射到外部磁盘上

docker exec命令可以在指定的容器内部执行命令,分为交互式和非交互式两种方式

非交互式:

$ docker exec 182214dcd10b ls -al /var

exec后面是docker id,然后紧跟要在容器中运行的命令

交互式

$ docker exec -it 182214dcd10b bash

-i选项代表使用交互方式,-t代表打开一个终端,bash是本次要运行的命令,一个shell程序

这样就相当于打开了一个交互式终端进入了容器内部,所做的操作都只在容器内部生效,如同进入了一个虚拟机,使用exit命令来退出容器。

创建自己的容器,使用DockerFile

Docker使用一种称为Dockerfile的文件来描述容器的组成以及相关配置,Dockerfile是docker的特有文件,必须满足相关格式

假如要创建自己的web程序,需要将所有依赖都打包到一个镜像中,以某个操作系统为底座来构筑自己的镜像

作为样例,我们使用python的flask框架来编写一个最简单的网页程序,并打包成一个镜像;关于flask可以参考如下链接:

flask.palletsprojects.com/en/2.1.x/

1、新建一个文件夹

mkdir flask

2、打开一个py文件app.py,写入如下内容:

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
	return "

Hello, World!

" if __name__ == "__main__": app.run(host="0.0.0.0", debug=True)

3、新建一个文件命名为dockerfile,写入如下内容:

#这是一个注释

FROM ubuntu
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get install -y python3
RUN apt-get install -y python3-flask
COPY app.py /var/hello.py
ENV FLASK_APP=hello
CMD cd /var && flask run

dockerfile其实只有一种格式,就是

# 注释
INSTRUCTION arguments

一个命令然后紧跟参数

dockerfile必须以FROM开始,代表在某个基础镜像上做接下来的操作,一般情况下以某个操作系统或某个现成的软件镜像开始;

其他常用命令包括:

RUN命令可以在镜像内部运行某个命令,本例中是使用apt-get命令来安装必要的软件

COPY命令可以将本地文件拷贝到容器内部,注意只能拷贝当前目录下的内容,因此COPY ../xxx xxx这种形式是无法成功的

ENV命令则可以简单的对容器注入环境变量

CMD命令用于指定当容器运行时需要执行的默认命令,CMD命令可以被docker run命令覆盖,如果不希望被覆盖,可以考虑使用ENTRYPOINT

全部的dockerfile手册请参考官方文档:

https://docs.docker.com/engine/reference/builder/#usage

之后就可以通过docker buid命令来构建镜像了

4、运行:

$ docker build -t flask:latest .

得到如下输出:

[+] Building 0.2s (11/11) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 248B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 27B 0.0s
=> [1/6] FROM docker.io/library/ubuntu 0.0s
=> CACHED [2/6] RUN apt-get update 0.0s
=> CACHED [3/6] RUN apt-get install -y nginx 0.0s
=> CACHED [4/6] RUN apt-get install -y python3 0.0s
=> [6/6] COPY app.py /var/hello.py 0.0s
=> exporting to image 0.0s
=> => writing image sha256:8ee4b660822edecb1bbe35f0421d57cfa94fe51533d16737e9ca0c84292fedf8 0.0s
=> => naming to docker.io/library/flask:latest 0.0s

-t 参数用于指明镜像的名称和tag,后面紧跟一个目录,docker会自动查找该目录下的dockerfile并根据该文件来进行镜像构建

构建完成后可以使用docker image ls命令来查看当前机器上所有的镜像列表

$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
flask latest 8ee4b660822e 5 seconds ago 213MB
  8b35a6e3e43d 7 minutes ago 213MB
nginx latest 605c77e624dd 6 months ago 141MB
ubuntu latest ba6acccedd29 8 months ago 72.8MB

docker image命令是用来管理镜像的,使用方法可以通过:

$ docker image --help
Usage: docker image COMMAND
Manage images
Commands:
build Build an image from a Dockerfile
history Show the history of an image
import Import the contents from a tarball to create a filesystem image
inspect Display detailed information on one or more images
load Load an image from a tar archive or STDIN
ls List images
prune Remove unused images
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rm Remove one or more images
save Save one or more images to a tar archive (streamed to STDOUT by default)
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

对于任意的docker命令都可以通过 docker COMMAND --help来查看使用方法。

运行一下制作的镜像

$ docker run flask
* Serving Flask app "hello"
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

至此,镜像已经成功制作并运行

Docker Registry 镜像仓库

git有github,docker也有docker hub,和git类似,docker需要一个存储所有镜像的地方,这样你可以和别人共享你制作的镜像,也可以使用别人制作的镜像,Docker官方提供的默认仓库是Docker hub,如果要修改默认的仓库可以运行:

sudo vim /etc/docker/daemon.json

写入:

{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

列表中可以使用任何提供docker仓库服务的地址,上述例子中使用的是中科大的镜像仓库。

接着,需要使配置生效:

sudo systemctl daemon-reload
sudo service docker restart

当然也可以使用本地仓库,使用本地仓库需要使用docker-registry,docker通过一个镜像提供了该服务,运行:

$ docker run -d -p 5000:5000 --restart=always --name registry registry

默认情况下,仓库会被创建在容器的 /var/lib/registry 目录下。你可以通过 -v 参数来将镜像文件存放在本地的指定路径。例如下面的例子将上传的镜像放到本地的 /data/registry 目录。

$ docker run -d -p 5000:5000 -v /data/registry:/var/lib/registry registry

这样就运行了一个本地仓库并且镜像持久化的存储在/data/registry目录下。

先尝试将本地的镜像推到仓库中:

$ docker push 127.0.0.1:5000/ubuntu:latest
The push refers to repository [127.0.0.1:5000/ubuntu]
373a30c24545: Pushed
a9148f5200b0: Pushed
cdd3de0940ab: Pushed
fc56279bbb33: Pushed
b38367233d37: Pushed
2aebd096e0e2: Pushed
latest: digest: sha256:fe4277621f10b5026266932ddf760f5a756d2facd505a94d2da12f4f52f71f5a size: 1568
用 curl 查看仓库中的镜像。
$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ubuntu"]}

可以看到 {"repositories":["ubuntu"]},表明镜像已经被成功上传了。

Docker compose

我们往往需要多个容器配合来完成某个上层应用,例如网站程序经常需要搭配一个数据库使用,这种情况可以把网站后台程序和数据库分别放到两个容器中运行,Docker compose工具提供一种简化的方式来组织多个容器,并且能够用一个命令快速的部署多个容器;

Docker compose使用YAML文件来描述容器以及容器之间的关系,使用Docker compose一般分为3步

1、定义每个容器的Dockerfile

2、定义一个称为docker-compose.yaml的文件,用来描述多个容器之间的关系

3、使用docker compose up命令一键部署应用程序

我们仍然以上个例子中的flask程序为基础,并新增一个计数功能,为此我们需要改写我们的web程序,如下:

from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
	count = redis.incr('hits')
	return '

Hello World! {} times。

'.format(count) if __name__ == "__main__": app.run(host="0.0.0.0", debug=True)

经过修改后,我们的程序会连接一个Redis数据库,并且每次被访问到,都会将计数加一,并展示到页面,为了连接Redis我们必须运行一个Redis的容器,好在该容器官方都制作好了,因此直接运行

$ docker run -d redis

就可以直接运行一个Redis数据库,但是此时web程序并没有连接到这个Redis容器,我们需要定义两个容器的关系

编写一个简单的docker-compose.yaml文件

services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
docker-compose文件必须有一个services字段,每一个service可以理解为一个由多个容器组成的一个上层应用上述例子中的service由两个容器组成,名字分别是web、redis,其中web容器需要使用docker build命令来构筑镜像,使用的目录为当前目录,并且需要把端口5000映射到主机的5000端口

redis容器责直接从系统配置的docker registry中尝试直接获取镜像

运行

$ docker compose up
[+] Running 2/2
- Container nginxpython-redis-1 Started 0.3s
- Container nginxpython-web-1 Started 0.3s

此时两个容器就一并开始构建和启动起来了

这里需要解释一个问题:为什么使用docker compose,flask程序就可以直接连接到一个名字为'redis'的主机上,他又是怎么找到这个主机的呢? 这与docker network的机制有关。

Docker network

第一次安装完Docker,直接运行

$ docker network ls

能够直接看到docker建立的默认网络,总共有3个

NETWORK ID NAME DRIVER SCOPE
479bda8f37b9 bridge bridge local
273213114bfe host host local
cce99c0034c4 none null local

bridge:docker 默认使用的是bridge,原理上是一个软件实现的二层交换机,如果不使用任何参数运行容器,那么默认会连接到bridge上,也就是说,默认一个机器上的容器都是可以互联互通的,但是这种情况下也只能通过IP来互联互通,无法通过hostname进行交互

host:移除容器和 Docker 宿主机之间的网络隔离,并直接使用主机的网络。host 模式仅适用于 Docker 17.06+

none:对于此容器,禁用所有联网。通常与自定义网络驱动程序一起使用。none 模式不适用于集群服务

在Docker compose章节的例子中,flask程序是直接连接的一个名为'redis'的host,而容器本身并不知道redis这个host对应的ip地址是什么,所以无法连接

如果要解决这个问题,我们可以手动修改flask程序的host文件,配置上redis容器的地址以及host,这样就可以互通了

还有另外一种方法就是使用docker compose命令,如同上个章节那样,这种情况下,docker会默认建立一个自定义网络,而不是使用默认的bridge网络,此外,如果使用自定义网络,docker还会默认提供一个DNS服务器,用于一个services下的各个容器之间的域名解析,在运行完docker compose up后,可以查看docker network ls,会发现docker建立了一个网络,名称为nginxpython_default

$ docker network ls
NETWORK ID NAME DRIVER SCOPE
add5f8daed77 bridge bridge local
3c065d8a1d64 host host local
41afd011cef9 nginxpython_default bridge local
c31bc25f91ae none null local

此时,该docker compose定义的两个容器就能够通过镜像名称作为主机名称进行互联,且网络与其他容器是隔离的

docker 网络相关材料请参考官方文档:

https://docs.docker.com/network/

相关推荐

编写更多 pythonic 代码(十三)——Python类型检查

一、概述在本文中,您将了解Python类型检查。传统上,类型由Python解释器以灵活但隐式的方式处理。最新版本的Python允许您指定显式类型提示,这些提示可由不同的工具使用,以帮助您更...

[827]ScalersTalk成长会Python小组第11周学习笔记

Scalers点评:在2015年,ScalersTalk成长会完成Python小组完成了《Python核心编程》第1轮的学习。到2016年,我们开始第二轮的学习,并且将重点放在章节的习题上。Pytho...

用 Python 画一颗会跳动的爱心:代码里的浪漫仪式感

在编程的世界里,代码不仅是逻辑的组合,也能成为表达情感的载体。今天我们就来聊聊如何用Python绘制一颗「会跳动的爱心」,让技术宅也能用代码传递浪漫。无论是写给爱人、朋友,还是单纯记录编程乐趣,这...

Python面向对象编程(OOP)实践教程

一、OOP理论基础1.面向对象编程概述面向对象编程(Object-OrientedProgramming,OOP)是一种编程范式,它使用"对象"来设计应用程序和软件。OOP的核心...

如何在 Python 中制作 GIF(python做gif)

在数据分析中使用GIF并发现其严肃的一面照片由GregRakozy在Unsplash上拍摄感谢社交媒体,您可能已经对GIF非常熟悉。在短短的几帧中,他们传达了非常具体的反应,只有图片才能传达...

Python用内置模块来构建REST服务、RPC服务

1写在前面和小伙伴们分享一些Python网络编程的一些笔记,博文为《PythonCookbook》读书后笔记整理博文涉及内容包括:TCP/UDP服务构建不使用框架创建一个REST风格的HTTP...

第七章:Python面向对象编程(python面向对象六大原则)

7.1类与对象基础7.1.1理论知识面向对象编程(OOP)是一种编程范式,它将数据(属性)和操作数据的函数(方法)封装在一起,形成一个称为类(Class)的结构。类是对象(Object)的蓝图,对...

30天学会Python编程:8. Python面向对象编程

8.1OOP基础概念8.1.1面向对象三大特性8.1.2类与对象关系核心概念:类(Class):对象的蓝图/模板对象(Object):类的具体实例属性(Attribute):对象的状态/数据方法...

RPython GC 对象分配速度大揭秘(废土种田,分配的对象超给力)

最近,对RPythonGC的对象分配速度产生了浓厚的兴趣。于是编写了一个小型的RPython基准测试程序,试图探究它对象分配的大致速度。初步测试与问题发现最初的设想是通过一个紧密循环来分配实...

30天学会Python编程:2. Python基础语法结构

2.1代码结构与缩进规则定义与原理Python使用缩进作为代码块的分界符,这是Python最显著的特征之一。不同于其他语言使用大括号{},Python强制使用缩进来表示代码层次结构。特性与规范缩进量...

Python 类和方法(python类的方法与普通的方法)

Python类和方法Python类创建、属性和方法具体是如何体现的,代码中如何设计,请继续看下去。蟒蛇类解释在Python中使用OOP?什么是Python类?Python类创建Pyt...

动态类型是如何一步步拖慢你的python程序的

杂谈人人都知道python慢,这都变成了人尽皆知的事情了,但你知道具体是什么拖慢了python的运行吗?动态类型肯定要算一个!动态类型,能够提高开发效率,能够让我们更加专注逻辑开发,使得编程更加灵活。...

用Python让图表动起来,居然这么简单

我好像看到这个emoji:动起来了!编译:佑铭参考:https://towardsdatascience.com/how-to-create-animated-graphs-in-python-bb6...

Python类型提示工程实践:提升代码质量的静态验证方案

根据GitHub年度开发者调查报告,采用类型提示的Python项目维护成本降低42%,代码审查效率提升35%。本文通过9个生产案例,解析类型系统在工程实践中的应用,覆盖API设计、数据校验、IDE辅助...

Python:深度剖析实例方法、类方法和静态方法的区别

在Python中,类方法(classmethod)、实例方法(instancemethod)和静态方法(staticmethod)是三种不同类型的函数,它们在使用方式和功能上有一些重要的区别。理...

取消回复欢迎 发表评论: