什么是云计算中的容器?

什么是集装箱?封面

自从 2013 年 Docker 问世以来,容器就开始大受欢迎。很多公司已经将容器集成到了他们的工作流程中,因为它可以让他们轻松地部署、分发、管理和扩展软件。

在本文中,我们将解释什么是云计算中的容器。我们将讨论使用容器的好处、容器的用例、容器与虚拟机的比较,并介绍Docker和 Kubernetes。最后,我们将教你如何编码、dockerize 和部署网络应用程序到Back4app Containers— 完全免费!

容器定义

容器是一个独立的可执行文件包,其中包含运行应用程序所需的一切:代码、运行时、库、环境变量和配置文件。容器化应用程序最棒的地方在于,它们可以运行在从本地开发环境到公共云等任何地方。容器体积小、效率高,并能实现有效隔离。

集装箱包括什么?

使用容器的好处

使用集装箱有几个好处。让我们来看看其中的一些。

效率

与传统服务器或虚拟机相比,容器所需的系统资源更少,因为它们不包含操作系统映像。这使得它们非常高效,体积小(通常以 MB 为单位),并允许您在一台服务器上运行大量应用程序。

应用隔离

容器将应用程序及其依赖关系与主机系统隔离。同时,它们还能共享操作系统内核和系统资源,如 CPU、内存、存储和网络。

便携性

容器化软件几乎可以在任何安装了容器引擎的机器上以相同的方式运行和运行。这使得在不同环境之间部署和移动应用程序变得非常容易,并消除了 “在我的机器上也能运行 “的问题。

责任分离

容器通过在开发人员和 IT 运营团队之间划分任务和责任,实现了责任分离。开发人员负责创建和维护应用程序代码及依赖关系,而 IT 运营团队则专注于部署和管理容器及底层基础设施。

更快的应用程序开发

容器化使软件的开发、测试、管理和分发变得更加容易。容器可以轻松与 CI/CD 系统集成,从而大大加快软件开发和发布流程。

轻松缩放

容器化应用程序与Kubernetes等编排平台相结合,可以轻松实现按需扩展。这样,您的企业就能在适应高工作负载的同时最大限度地降低成本。

容器使用案例

对于开发人员和 IT 运营团队来说,容器技术有很多用例。

容器原生开发

容器原生开发是一种利用容器作为主要构建模块的软件开发方法。在容器原生开发中,应用程序被打包成容器,并在容器化环境中运行。这种开发方法能让你享受到容器所能提供的所有超酷功能。

持续集成和持续交付(CI/CD)

在 CI/CD 管道中,容器用于打包应用程序和运行自动测试,从而能够以一致和可重复的方式测试和部署应用程序。作为 CI/CD 管道的一部分,容器可以轻松创建、测试和部署,从而降低出错风险,提高软件开发流程的整体效率。

微服务

容器可用于开发遵循微服务架构的应用程序。利用容器,您可以轻松地将单体应用程序拆分成一系列松散耦合、细粒度的服务,这些服务在不同的容器中运行。

开发环境

容器使开发团队能够轻松快速地建立开发环境。无论其主机操作系统和主机库如何,它们都能提供一致的开发环境。

批处理

批处理流程可以轻松地容器化并部署到云中。每个任务都打包为单独的容器映像,并作为单独的容器实例执行。这样可以有效利用资源,因为每个任务都在自己的环境中运行,不会干扰其他任务。

容器与虚拟机

容器和虚拟机是两种不同的虚拟化方法。虽然它们有一些相似之处,但却截然不同。

虚拟机(VM)是物理硬件的抽象。通过虚拟机,我们可以将一台服务器变成多台服务器。每个虚拟机都有自己的操作系统,通常由管理程序管理。虚拟机适合运行多个应用程序(在同一台服务器上)、单体应用程序以及需要高度隔离和安全的应用程序。其缺点是往往会占用大量空间,启动速度也会相当慢。

另一方面,容器在操作系统层面进行了虚拟化。由于它们共享相同的 Linux 内核,因此占用空间更少,效率更高,启动更快,可扩展性强,并能处理更多应用程序。容器由容器引擎管理。与虚拟机相比,它们的主要用例是需要可移植、轻量级和可扩展的微服务和应用程序。

还可以将容器和虚拟机结合起来,获得两者的优势。

容器与虚拟机(VM)

Docker 和 Kubernetes

Docker 和 Kubernetes 是使用容器最流行的两种工具。让我们来解释一下它们是如何工作的,并看看它们的区别。

Docker 是一个基于 Linux 的开源项目,用于在轻量级容器中自动部署和管理应用程序。这使得容器化应用程序可以在不同环境中高效运行。如今,从 Linux 机器到大型云提供商等几乎任何地方都能找到 Docker。

最流行的 Docker 替代品是PodmanLXDcontainerd

Kubernetes(K8s)是一个开源容器编排系统,用于自动化部署、扩展和管理容器化应用程序。自 2014 年发布以来,它已成为在云环境中部署和运行容器化应用程序的事实标准。Kubernetes 的优势包括可扩展性、高可用性、自动化操作、基础设施抽象和健康监控。

其他协调平台包括AWS ECSNomadRed Hat OpenShift

那么,Docker 和 Kubernetes 之间有什么区别呢?简单来说,Docker 让我们可以在容器内打包和分发应用程序,而 Kubernetes 则可以让多个容器轻松地相互协调工作。

使用基于容器的架构开发应用程序

在本节教程中,我们将创建、dockerize 并部署一个简单的 REST API 到Back4app Containers

什么是 Back4app Containers?

Back4app Containers 是一个免费的开源平台,用于在云基础设施中的全球分布式容器上部署和扩展应用程序。

它可以让您专注于软件,更快地发布软件,而无需担心 DevOps。该平台与 GitHub 紧密集成,内置 CI/CD 系统,可让您在几分钟内启动并运行应用程序!

为什么使用 Back4app 容器?

  • 与 GitHub 集成良好
  • 零停机部署
  • 易于使用,提供免费级别
  • 卓越的客户支持

项目介绍

我们将构建一个简单的 REST API,作为电影观看列表。网络应用程序将允许基本的 CRUD 操作,如添加电影、删除电影等。为了创建 API,我们将使用 Flask 框架。最后,我们将对项目进行 Docker 化,并演示将其部署到 Back4app Containers 是多么容易。

先决条件

  • 具有使用 Flask 框架的经验
  • 对 Docker 和容器有基本了解
  • 使用GitGitHub的能力

代码应用程序

以下步骤需要您安装 Python。如果您尚未安装 Python,请继续下载

项目初始化

首先,为您的应用程序创建一个专用目录并导航至该目录:

$ mkdir flask-watchlist
$ cd flask-watchlist

然后,创建一个新的虚拟环境并激活它:

$ python3 -m venv venv && source venv/bin/activate

既然我们要使用Flask作为框架,就必须安装它:

$ (venv) pip install Flask==2.2.2

创建app.py,内容如下

# app.py

from flask import Flask

app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False


@app.route('/')
def index_view():
    return {
        'detail': 'Hello world!'
    }

这段代码初始化了 Flask,并创建了一个返回消息的简单端点。

用以下命令运行服务器

$ flask run

导航至http://localhost:5000/,您应该会看到一条消息,上面写着 “Hello world!“。

数据库

数据库方面,我们将使用SQLite。SQLite 是一种嵌入式无服务器关系数据库管理系统。为了简化数据库的使用,我们将安装Flask-SQLAlchemy— 一个 Flask 扩展,它能为您的应用程序添加对 SQLAlchemy 的支持。

运行”…… “安装:

$ (venv) pip install Flask-SQLAlchemy==3.0.3

下一步,导航到app.py的顶部,像这样修改以初始化数据库:

# app.py

db = SQLAlchemy()
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
app.config['SECRET_KEY'] = '5b3cd5b80eb8b217c20fb37074ff4a33'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///default.db"
db.init_app(app)

别忘了进口:

from flask_sqlalchemy import SQLAlchemy

接下来,让我们定义数据库模型。

由于我们构建的是一个简单的电影观看列表应用程序,因此我们只需要一个模型。像这样定义 “电影 "模型

# app.py

class Movie(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(128), nullable=False)
    release_date = db.Column(db.Date(), nullable=False)

    is_watched = db.Column(db.Boolean, default=False)
    watched_at = db.Column(db.DateTime, default=None, nullable=True)

    def as_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def __repr__(self):
        return '<Movie %r>' % self.title

为了初始化和填充数据库,我们将创建一个简单的 Python 脚本。导航到项目根目录,创建一个名为init_db.py的新文件,文件内容如下:

# init_db.py

from datetime import date

from app import db, app
from app import Movie

with app.app_context():
    db.create_all()

    if Movie.query.count() == 0:
        movies = [
            Movie(title='Fight Club', release_date=date(1999, 9, 15)),
            Movie(title='The Matrix', release_date=date(1999, 3, 31)),
            Movie(title='Donnie Darko', release_date=date(2001, 1, 19)),
            Movie(title='Inception', release_date=date(2010, 7, 16)),
        ]

        for movie in movies:
            db.session.add(movie)

        db.session.commit()

我们最后要做的就是运行脚本:

$ (venv) python init_db.py

这将创建数据库、数据库表并填充它们。数据库文件将放在实例文件夹中。

应用程序接口端点

我们的网络应用程序将有以下端点:

  1. / 返回 API基本信息
  2. /api/返回电影列表
  3. /api/create/ 为观看列表添加一部新电影
  4. /api// 返回特定电影的详细信息
  5. /api/watch// 标记电影已观看

继续在app.py 的底部定义端点:

# app.py

@app.route('/')
def index_view():
    return {
        'name': 'flask-watchlist',
        'description': 'a simple app for tracking the movies you want to watch',
        'version': 1.1,
    }


@app.route('/api/')
def list_view():
    json = [movie.as_dict() for movie in Movie.query.all()]
    return jsonify(json)


@app.route('/api/<int:movie_id>/', methods=['GET', 'DELETE'])
def detail_view(movie_id):
    movie = db.get_or_404(Movie, movie_id)

    if request.method == 'DELETE':
        db.session.delete(movie)
        db.session.commit()

        return {
            'detail': 'Movie has been successfully deleted.'
        }
    else:
        return movie.as_dict()


@app.route('/api/create/', methods=['POST'])
def create_view():
    title = request.form.get('title')
    release_date = request.form.get('release_date', type=float)

    if title is None or release_date is None:
        return {
            'detail': 'Please provide the title and release_date.'
        }, 400

    movie = Movie(title=title, release_date=datetime.fromtimestamp(release_date))
    db.session.add(movie)
    db.session.commit()

    return movie.as_dict()


@app.route('/api/watch/<int:movie_id>/')
def watch_view(movie_id):
    movie = db.get_or_404(Movie, movie_id)

    if movie.is_watched:
        return {
            'detail': 'Movie has already been watched.'
        }, 400

    movie.is_watched = True
    movie.watched_at = datetime.now()
    db.session.commit()

    return movie.as_dict()

别忘了进口产品:

from datetime import datetime
from flask import request, jsonify

很好,我们的应用程序现在大致完成了。运行开发服务器:

$ (venv) flask run

测试能否获取电影列表:

$ (venv) curl http://localhost:5000/api/ | jq '.'

[
  {
    "id": 1,
    "title": "Fight Club",
    "release_date": "Wed, 15 Sep 1999 00:00:00 GMT",
    "is_watched": false,
    "watched_at": null
  },
  {
    "id": 2,
    "title": "The Matrix",
    "release_date": "Wed, 31 Mar 1999 00:00:00 GMT",
    "is_watched": false,
    "watched_at": null
  },
  ...
]

Gunicorn

Flask 开发服务器不适合用于生产,所以我们把它换成Gunicorn。Gunicorn 或 “绿色独角兽 “是一个适用于生产的 Unix Python WSGI HTTP 服务器。

运行以下命令进行安装

$ (venv) pip install gunicorn==20.1.0

安装软件包后,就可以像这样启动 WSGI 服务器了:

$ (venv) gunicorn -w 2 -b 0.0.0.0:5000 app:app

[INFO] Starting gunicorn 20.1.0
[INFO] Listening at: http://0.0.0.0:5000 (1)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 7
[INFO] Booting worker with pid: 8

请注意,该命令仅适用于 UNIX 操作系统。

这将启动两个 Gunicorn Worker,并将您的应用程序暴露在互联网上。要访问应用程序,请打开您最喜欢的网络浏览器并导航至http://localhost:5000。

requirements.txt

在 Docker 化应用程序之前,我们需要做的最后一件事是创建requirements.txt文件。requirements.txt文件用于指定项目的依赖关系。

最简单的生成方法是运行

$ (venv) pip freeze > requirements.txt

Dockerize 应用程序

以下步骤需要安装 Docker。安装 Docker 的最简单方法是下载Docker Desktop

要验证您已安装 Docker,请运行

$ docker --version

Docker version 20.10.22, build 3a2c30b

Dockerfile

我们将使用Dockerfile 来实现应用程序的 docker 化。Dockerfile是一个纯文本文件,允许我们定义基础镜像、环境、环境变量、命令、网络设置、卷等等。

在项目根目录下创建一个Dockerfile,内容如下:

# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine

# set the working directory
WORKDIR /app

# set environmental variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install the requirements
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
    pip3 install -r requirements.txt

# copy the code to the container
COPY . .

# initialize the database (create DB, tables, populate)
RUN python init_db.py

# expose
EXPOSE 5000/tcp

# entrypoint command
CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]
  1. 我们使用python:3.10-alpine作为基础图像。
  2. PYTHONDONTWRITEBYTECODE设为1会使 Python 不再将.pyc文件写入磁盘。
  3. PYTHONUNBUFFERED设为1可确保Python输出流直接发送到终端。

有关编写Dockerfile 的更多信息,请参阅Dockerfile 参考资料

.dockerignore

在 Docker 构建镜像之前,它会查找.dockerignore文件。通过.dockerignore文件,我们可以定义哪些文件不希望包含在镜像中。这可以大大减小镜像文件的大小。它的作用与.gitignore 文件类似。

在项目根目录下创建一个.dockerignore文件,内容如下:

# .dockerignore

.git/
instance/
__pycache__/
.idea/

请确保添加了您想排除的其他目录或文件。

构建并运行映像

接下来,让我们构建并标记我们的 Docker 镜像。

$ docker build -t flask-watchlist:1.0 .

[+] Building 11.1s (15/15) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                                                               0.0s 
 => => transferring dockerfile: 32B                                                                                                                                                                                                                                                                                0.0s 
 => [internal] load .dockerignore                                                                                                                                                                                                                                                                                  0.0s 
 => => transferring context: 34B                                                                                                                                                                                                                                                                                   0.0s 
 => resolve image config for docker.io/docker/dockerfile:1.4                                                                                                                                                                                                                                                       0.5s 
 => CACHED docker-image://docker.io/docker/dockerfile:1.4@sha256:9ba7531a0dbc                                                                                                                                                                                  0.0s 
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                                                               0.0s 
 => [internal] load .dockerignore                                                                                                                                                                                                                                                                                  0.0s 
 => [internal] load metadata for docker.io/library/python:3.10-alpine                                                                                                                                                                                                                                              0.5s 
 => [stage-0 1/6] FROM docker.io/library/python:3.10-alpine@sha256:da5ab5e911253dfb                                                                                                                                                                                0.0s 
 => [internal] load build context                                                                                                                                                                                                                                                                                  0.3s 
 => => transferring context: 182.45kB                                                                                                                                                                                                                                                                              0.2s 
 => CACHED [stage-0 2/6] WORKDIR /app                                                                                                                                                                                                                                                                              0.0s 
 => [stage-0 3/6] COPY requirements.txt /app                                                                                                                                                                                                                                                                       0.0s 
 => [stage-0 4/6] RUN --mount=type=cache,target=/root/.cache/pip     
                      pip3 install -r requirements.txt                                                                                                                                                                                                              7.2s 
 => [stage-0 5/6] COPY . .                                                                                                                                                                                                                                                                                         0.3s 
 => [stage-0 6/6] RUN python init_db.py                                                                                                                                                                                                                                                                            1.5s 
 => exporting to image                                                                                                                                                                                                                                                                                             0.3s 
 => => exporting layers                                                                                                                                                                                                                                                                                            0.3s 
 => => writing image sha256:2671ccb7546a0594807c721a0600a                                                                                                                                                                                                                       0.0s 
 => => naming to docker.io/library/flask-watchlist:1.0 

如果您列出图片,就会看到我们的新图片:

$ docker images

REPOSITORY        TAG       IMAGE ID       CREATED       SIZE
flask-watchlist   1.0       7bce66230eb1   8 hours ago   110MB

最后,使用镜像启动一个新的 Docker 容器:

$ docker run -it -p 5000:5000 flask-watchlist:1.0

[2023-02-02 20:08:57 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2023-02-02 20:08:57 +0000] [1] [INFO] Using worker: sync
[2023-02-02 20:08:57 +0000] [7] [INFO] Booting worker with pid: 7
[2023-02-02 20:08:57 +0000] [8] [INFO] Booting worker with pid: 8

你可以使用-d以分离模式启动 Docker 容器。这意味着容器在终端后台运行,不接收输入也不显示输出。

干得好,你的应用程序现在已经在容器中运行了!导航至http://localhost:5000,您应该会得到以下响应:

{
    "name": "flask-watchlist",
    "description": "a simple app for tracking the movies you want to watch",
    "version": 1
}

GitHub

要将应用程序部署到 Back4app Containers,您必须将源代码上传到 GitHub 仓库。在 GitHub 上创建一个新仓库,添加远程代码,添加.gitignore 并提交代码。代码上传到 GitHub 后,进入下一步。

将应用程序部署到 Back4app 容器中

以下步骤需要您拥有 Back4app 帐户。如果您已经拥有该账户,请登录,否则请注册免费账户

要使用 Back4app,我们首先需要创建一个应用程序。登录仪表板后,您将看到应用程序列表。点击 “创建新应用程序 “创建新应用程序。

Back4app 创建应用程序

接下来,选择 “容器即服务”。

Back4app 容器即服务

如果还没有,请将您的 GitHub 连接到 Back4app 并导入您要部署的软件源。连接 GitHub 后,您的软件源将显示在表格中。

点击 “Select(选择)”,选择要部署的版本库。

Back4app 容器连接存储库

接下来,Back4app 会要求你配置环境。选择一个应用程序名称,我选择flask-watchlist。其他一切都保持默认即可。

最后,点击 “创建应用程序”,自动创建应用程序并进行部署。

Back4app 容器配置环境

然后,您将被重定向到应用程序详细信息,在那里您可以看到部署日志。

Back4app 容器成功部署

等待几分钟,应用程序就会部署完成!您的应用程序现已在 Back4app Containers 上运行。要查看应用程序的运行情况,请单击左侧显示的绿色 URL。

结论

在整篇文章中,我们解释了什么是容器、容器的好处,并演示了如何在工作流程中实施容器。现在您应该能够构建自己的简单 REST API,将其坞化并部署到 Back4app 容器中。

GitHub 代码库中获取最终源代码。

今后的步骤

  1. 您不应该在镜像中存储数据库。目前,每次重新部署都会重置数据库。请考虑切换到托管的 PostgreSQL 或 MySQL 实例。
  2. 了解多阶段构建,优化 Docker 文件。
  3. 阅读《部署 Docker 容器》一文,了解分步教程

常见问题

什麼是容器?

容器是一個獨立可執行的套件,包含運行應用程式所需的一切。這包括程式碼、執行環境、函式庫、環境變數和設定檔。

使用容器的好處是什麼?

– 高效率
– 應用程式隔離
– 責任分離
– 更快速的應用程式開發

容器與虛擬機有什麼不同?

虛擬機是對實體硬體的抽象,而容器是在作業系統層級虛擬化。虛擬機提供更高的隔離與安全性,而容器佔用空間小,效率高且可擴展性強。

Docker 與 Kubernetes 有何不同?

Docker 讓我們能夠將應用程式打包並部署在容器中,而 Kubernetes 則讓多個容器能夠協同運作變得容易。

如何使用基於容器的架構開發應用程式?

1. 選擇一種程式語言並撰寫應用程式。
2. 使用 Dockerfile 或 Docker Compose 將應用程式容器化。
3. 建立 Docker 映像檔並在本地測試。
4. 選擇像 Back4app Containers 這樣的 CaaS 並將程式碼推送上去。
等待服務部署完成,就完成了!


Leave a reply

Your email address will not be published.