分类
文章

如何在Ubuntu 18 04上安装Tinc并设置基本VPN

介绍

containerize一个应用程序指的是适配的应用及其组件,以便能够在被称为轻量的环境中运行它的过程容器。这样的环境是孤立且可抛弃的,可用于开发,测试和部署应用程序到生产环境。

在本指南中,我们将使用Docker Compose容器化Laravel 6应用程序进行开发。完成后,您将在三个独立的服务容器上运行一个演示Laravel应用程序:

  • app运行PHP7.4-FPM 的服务;
  • db运行MySQL 5.7 的服务;
  • 一个nginx使用该服务app的服务服务于Laravel应用到最终用户之前解析PHP代码。

为了简化开发过程并简化应用程序调试,我们将使用共享卷使应用程序文件同步。我们还将看到如何使用docker-compose exec命令在容器上运行ComposerArtisanapp

先决条件

第1步-获取演示应用程序

首先,我们将从其Github存储库中获取演示Laravel应用程序。我们对tutorial-01分支感兴趣,该分支包含我们在本系列第一本指南中创建的基本Laravel应用程序。

要获取与本教程兼容的应用程序代码,请使用以下命令将发行版下载tutorial-1.0.1到您的主目录中:

  • cd ~
  • curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

我们需要该unzip命令来解压缩应用程序代码。如果您以前从未安装过此软件包,请立即执行以下操作:

  • sudo apt update
  • sudo apt install unzip

现在,解压缩应用程序的内容并重命名解压缩的目录,以便于访问:

  • unzip travellist.zip
  • mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

导航到travellist-demo目录:

  • cd travellist-demo

在下一步中,我们将创建一个.env配置文件来设置应用程序。

步骤2 —设置应用程序的.env文件

Laravel配置文件位于config应用程序的根目录中的名为目录中。此外,.env文件用于设置与环境有关的配置,例如凭据以及部署之间可能有所不同的任何信息。此文件不包含在修订控制中。

Warning:环境配置文件包含有关服务器的敏感信息,包括数据库凭据和安全密钥。因此,您永远不应公开共享此文件。

.env文件中包含的值将优先于config目录中常规配置文件中设置的值。在新环境中进行的每次安装都需要一个定制的环境文件来定义诸如数据库连接设置,调试选项,应用程序URL之类的内容,这些其他内容可能会根据应用程序运行的环境而有所不同。

现在,我们将创建一个新.env文件,以针对要设置的开发环境自定义配置选项。Laravel附带了一个示例.env文件,我们可以复制该文件来创建自己的文件:

  • cp .env.example .env

使用nano或您选择的文本编辑器打开此文件:

  • nano .env

演示应用程序中的当前.env文件travellist包含使用本地MySQL数据库127.0.0.1作为数据库主机的设置。我们需要更新DB_HOST变量,使其指向将在Docker环境中创建的数据库服务。在本指南中,我们将称为数据库服务db。继续,并将列出的值替换DB_HOST为数据库服务名称:

.env
APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
...

如果愿意,还可以随时更改数据库名称,用户名和密码。这些变量将在以后的步骤中加以利用,在该步骤中,我们将设置docker-compose.yml文件来配置我们的服务。

完成编辑后,保存文件。如果使用nano,则可以按Ctrl+x,然后按YEnter确认。

步骤3 —设置应用程序的Dockerfile

尽管我们的MySQL和Nginx服务都将基于从Docker Hub获得的默认映像,但是我们仍然需要为应用程序容器构建自定义映像。我们将为此创建一个新的Dockerfile。

我们的travellist映像将基于Docker Hub 的php:7.4-fpm 官方PHP映像。在基本的PHP-FPM环境之上,我们将安装一些额外的PHP模块和Composer依赖性管理工具。

我们还将创建一个新的系统用户;这是在开发应用程序时执行artisancomposer命令所必需的。该uid设置可确保容器内的用户具有与运行Docker的主机上的系统用户相同的uid。这样,由这些命令创建的所有文件都将以正确的权限复制到主机中。这也意味着您将能够在主机中使用所选的代码编辑器来开发在容器内运行的应用程序。

使用以下命令创建一个新的Dockerfile:

  • nano Dockerfile

将以下内容复制到您的Dockerfile中:

Docker文件
FROM php:7.4-fpm

# Arguments defined in docker-compose.yml
ARG user
ARG uid

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
    chown -R $user:$user /home/$user

# Set working directory
WORKDIR /var/www

USER $user

完成后,别忘了保存文件。

我们的Dockerfile首先定义我们正在使用的基本映像: php:7.4-fpm

安装系统软件包和PHP扩展后,我们通过将composer可执行文件从其最新的官方映像复制到我们自己的应用程序映像中来安装Composer 。

然后使用Dockerfile开头声明的useruid参数创建并设置一个新的系统用户。这些值将在构建时由Docker Compose注入。

最后,我们将默认工作目录设置为,/var/www然后更改为新创建的用户。这将确保您在运行时composer以及artisan在应用程序容器上执行命令时都以常规用户身份连接,并且位于正确的目录中。

步骤4 —设置Nginx配置和数据库转储文件

使用Docker Compose创建开发环境时,通常需要与服务容器共享配置或初始化文件,以便设置或引导这些服务。这种做法有助于在开发应用程序时对配置文件进行更改以微调您的环境。

现在,我们将建立一个包含文件的文件夹,该文件将用于配置和初始化我们的服务容器。

要设置Nginx,我们将共享一个travellist.conf文件,该文件将配置服务应用程序的方式。使用以下方法创建docker-compose/nginx文件夹:

  • mkdir -p docker-compose/nginx

打开travellist.conf在该目录中命名的新文件:

  • nano docker-compose/nginx/travellist.conf

将以下Nginx配置复制到该文件:

docker-compose / nginx / travellist.conf

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

该文件将Nginx配置为侦听端口80index.php用作默认索引页。它将文档根目录设置为/var/www/public,然后将Nginx配置为使用app端口9000上的服务来处理*.php文件。

完成编辑后,保存并关闭文件。

要设置MySQL数据库,我们将共享一个数据库转储,该容器将在初始化容器时导入。这是我们将在该容器上使用的MySQL 5.7映像所提供的功能。

在文件夹中为您的MySQL初始化文件创建一个新文件docker-compose夹:

  • mkdir docker-compose/mysql

打开一个新.sql文件:

  • nano docker-compose/mysql/init_db.sql

以下MySQL转储基于我们在Laravel on LEMP指南中建立的数据库。它将创建一个名为的新表places。然后,它将使用一组示例位置填充表格。

将以下代码添加到文件中:

docker-compose / mysql / db_init.sql


DROP TABLE IF EXISTS `places`;

CREATE TABLE `places` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `visited` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);

places表包含三个字段:idname,和visited。该visited字段是用于标识仍要去的地方的标志。随时更改示例位置或包括新位置。完成后,保存并关闭文件。

我们已经完成了设置应用程序的Dockerfile和服务配置文件。接下来,我们将设置Docker Compose在创建服务时使用这些文件。

第5步—使用Docker Compose创建一个多容器环境

Docker Compose使您能够为在Docker上运行的应用程序创建多容器环境。它使用服务定义来构建具有多个可以共享网络和数据量的容器的完全可定制的环境。这允许在应用程序组件之间进行无缝集成。

要设置服务定义,我们将创建一个名为的新文件docker-compose.yml。通常,此文件位于应用程序文件夹的根目录中,它定义了容器化的环境,包括将用于构建容器的基本映像以及服务将如何交互。

我们将在我们的定义三种不同的服务docker-compose.yml文件:appdb,和nginx

app服务将travellist基于我们先前创建的Dockerfile 构建一个名为的映像。此服务定义的容器将运行php-fpm服务器以解析PHP代码并将结果发送回nginx服务,该服务将在单独的容器上运行。该mysql服务定义了一个运行MySQL 5.7服务器的容器。我们的服务将共享一个名为的桥接网络travellist

应用程序文件将通过app和在nginx服务上同步bind mounts绑定安装在开发环境中很有用,因为它们允许主机和容器之间进行高性能的双向同步。

docker-compose.yml在应用程序文件夹的根目录下创建一个新文件:

  • nano docker-compose.yml

典型的docker-compose.yml文件以版本定义开头,后跟一个services节点,在该节点下定义所有服务。共享网络通常在该文件的底部定义。

首先,请将以下样板代码复制到您的docker-compose.yml文件中:

docker-compose.yml
version: "3.7"
services:


networks:
  travellist:
    driver: bridge

现在,我们将编辑services节点包括appdbnginx服务。

app服务

app服务将设置一个名为的容器travellist-app。它基于与docker-compose.yml文件相同路径的Dockerfile构建新的Docker映像。新图像将以名称保存在本地travellist

即使用作应用程序的文档根目录位于nginx容器中,我们也需要在app容器内部的某个位置放置应用程序文件,因此我们能够使用Laravel Artisan工具执行命令行任务。

servicesdocker-compose.yml文件内的节点下复制以下服务定义:

docker-compose.yml
  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

这些设置执行以下操作:

  • build:此配置告诉Docker Compose app使用指定的路径(上下文)和Dockerfile作为说明来构建服务的本地映像。参数useruid被注入到Dockerfile中,以在构建时自定义用户创建命令。
  • image:将用于构建图像的名称。
  • container_name:设置此服务的容器名称。
  • restart:始终重新启动,除非服务已停止。
  • working_dir:将此服务的默认目录设置为/var/www
  • volumes:创建一个共享卷,该共享卷会将内容从当前目录同步到/var/www容器内部。请注意,这不是您的文档根目录,因为它将存在于nginx容器中。
  • networks:将此服务设置为使用名为的网络travellist
db服务

db服务使用来自Docker Hub 的预构建的MySQL 5.7映像。因为Docker Compose自动加载与.env文件位于同一目录中的变量文件docker-compose.yml,所以我们可以从.env上一步中创建的Laravel 文件中获取数据库设置。

在服务services之后,在您的节点中包括以下服务定义app

docker-compose.yml
  db:
    image: mysql:5.7
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

这些设置执行以下操作:

  • image:定义该容器应使用的Docker映像。在这种情况下,我们使用来自Docker Hub的MySQL 5.7映像。
  • container_name:设置此服务的容器名称:travellist-db
  • restart注意:除非已明确停止,否则请始终重新启动该服务。
  • environment:在新容器中定义环境变量。我们使用从Laravel .env文件获得的值来设置我们的MySQL服务,该服务将根据提供的环境变量自动创建新的数据库和用户。
  • volumes:创建一个卷以共享.sql将用于初始化应用程序数据库的数据库转储。MySQL映像将自动导入.sql放置在/docker-entrypoint-initdb.d容器内目录中的文件。
  • networks:将此服务设置为使用名为的网络travellist
nginx服务

nginx服务在轻量级Linux发行版Alpine之上使用预构建的Nginx映像。它创建一个名为的容器,并使用该定义创建从主机系统上的端口到容器内端口的重定向。travellist-nginxports800080

在服务services之后,在您的节点中包括以下服务定义db

docker-compose.yml
  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - travellist

这些设置执行以下操作:

  • image:定义该容器应使用的Docker映像。在这种情况下,我们使用Alpine Nginx 1.17图像。
  • container_name:设置此服务的容器名称:travellist-nginx
  • restart注意:除非已明确停止,否则请始终重新启动该服务。
  • ports:设置端口重定向,该端口重定向将允许通过端口外部访问在容器内的8000端口上运行的Web服务器80
  • volumes:创建two共享卷。第一个将同步内容从当前目录到/var/www容器内部。这样,当您对应用程序文件进行本地更改时,它们将迅速反映在容器内Nginx服务的应用程序中。第二卷将确保将位于docker-compose/nginx/travellist.conf的Nginx配置文件复制到容器的Nginx配置文件夹中。
  • networks:将此服务设置为使用名为的网络travellist
成品docker-compose.yml文件

这是我们完成的docker-compose.yml文件的样子:

docker-compose.yml
version: "3.7"
services:
  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

  db:
    image: mysql:5.7
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

  nginx:
    image: nginx:alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d/
    networks:
      - travellist

networks:
  travellist:
    driver: bridge

完成后,请确保保存文件。

第6步—使用Docker Compose运行应用程序

现在,我们将使用docker-compose命令来构建应用程序映像并运行我们在设置中指定的服务。

app使用以下命令生成映像:

  • docker-compose build app

此命令可能需要几分钟才能完成。您将看到类似于以下的输出:

Output
Building app Step 1/11 : FROM php:7.4-fpm ---> fa37bd6db22a Step 2/11 : ARG user ---> Running in f71eb33b7459 Removing intermediate container f71eb33b7459 ---> 533c30216f34 Step 3/11 : ARG uid ---> Running in 60d2d2a84cda Removing intermediate container 60d2d2a84cda ---> 497fbf904605 Step 4/11 : RUN apt-get update && apt-get install -y git curl libpng-dev libonig-dev ... Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer ---> e499f74896e3 Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user ---> Running in 232ef9c7dbd1 Removing intermediate container 232ef9c7dbd1 ---> 870fa3220ffa Step 9/11 : RUN mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user ---> Running in 7ca8c0cb7f09 Removing intermediate container 7ca8c0cb7f09 ---> 3d2ef9519a8e Step 10/11 : WORKDIR /var/www ---> Running in 4a964f91edfa Removing intermediate container 4a964f91edfa ---> 00ada639da21 Step 11/11 : USER $user ---> Running in 9f8e874fede9 Removing intermediate container 9f8e874fede9 ---> fe176ff4702b Successfully built fe176ff4702b Successfully tagged travellist:latest

构建完成后,您可以使用以下命令在后台模式下运行环境:

  • docker-compose up -d
Output
Creating travellist-db ... done Creating travellist-app ... done Creating travellist-nginx ... done

这将在后台运行您的容器。要显示有关活动服务状态的信息,请运行:

  • docker-compose ps

您将看到如下输出:

Output
Name Command State Ports ------------------------------------------------------------------------------- travellist-app docker-php-entrypoint php-fpm Up 9000/tcp travellist-db docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp travellist-nginx nginx -g daemon off; Up 0.0.0.0:8000->80/tcp

您的环境现已启动并正在运行,但是我们仍然需要执行几个命令来完成应用程序的设置。您可以使用该docker-compose exec命令在服务容器中执行命令,例如使用ls -l可以显示有关应用程序目录中文件的详细信息:

  • docker-compose exec app ls -l
Output
total 256 -rw-rw-r-- 1 sammy 1001 738 Jan 15 16:46 Dockerfile -rw-rw-r-- 1 sammy 1001 101 Jan 7 08:05 README.md drwxrwxr-x 6 sammy 1001 4096 Jan 7 08:05 app -rwxr-xr-x 1 sammy 1001 1686 Jan 7 08:05 artisan drwxrwxr-x 3 sammy 1001 4096 Jan 7 08:05 bootstrap -rw-rw-r-- 1 sammy 1001 1501 Jan 7 08:05 composer.json -rw-rw-r-- 1 sammy 1001 179071 Jan 7 08:05 composer.lock drwxrwxr-x 2 sammy 1001 4096 Jan 7 08:05 config drwxrwxr-x 5 sammy 1001 4096 Jan 7 08:05 database drwxrwxr-x 4 sammy 1001 4096 Jan 15 16:46 docker-compose -rw-rw-r-- 1 sammy 1001 1015 Jan 15 16:45 docker-compose.yml -rw-rw-r-- 1 sammy 1001 1013 Jan 7 08:05 package.json -rw-rw-r-- 1 sammy 1001 1405 Jan 7 08:05 phpunit.xml drwxrwxr-x 2 sammy 1001 4096 Jan 7 08:05 public -rw-rw-r-- 1 sammy 1001 273 Jan 7 08:05 readme.md drwxrwxr-x 6 sammy 1001 4096 Jan 7 08:05 resources drwxrwxr-x 2 sammy 1001 4096 Jan 7 08:05 routes -rw-rw-r-- 1 sammy 1001 563 Jan 7 08:05 server.php drwxrwxr-x 5 sammy 1001 4096 Jan 7 08:05 storage drwxrwxr-x 4 sammy 1001 4096 Jan 7 08:05 tests -rw-rw-r-- 1 sammy 1001 538 Jan 7 08:05 webpack.mix.js

现在,我们将运行composer install以安装应用程序依赖项:

  • docker-compose exec app composer install

您将看到如下输出:

Output
Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 85 installs, 0 updates, 0 removals - Installing doctrine/inflector (1.3.1): Downloading (100%) - Installing doctrine/lexer (1.2.0): Downloading (100%) - Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%) - Installing erusev/parsedown (1.7.4): Downloading (100%) - Installing symfony/polyfill-ctype (v1.13.1): Downloading (100%) - Installing phpoption/phpoption (1.7.2): Downloading (100%) - Installing vlucas/phpdotenv (v3.6.0): Downloading (100%) - Installing symfony/css-selector (v5.0.2): Downloading (100%) … Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully.

测试应用程序之前,我们需要做的最后一件事是使用artisanLaravel命令行工具生成唯一的应用程序密钥。此密钥用于加密用户会话和其他敏感数据:

  • docker-compose exec app php artisan key:generate
Output
Application key set successfully.

现在转到浏览器并在端口8000上访问服务器的域名或IP地址:

http://server_domain_or_IP:8000

您会看到这样的页面:

[Demo Laravel应用](s3://assets.digitalocean.com/articles/laravel at scale / travellist_docker.png)

您可以使用以下logs命令来检查服务生成的日志:

  • docker-compose logs nginx
Attaching to travellist-nginx
travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:25 +0000] "GET / HTTP/1.1" 200 626 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:26 +0000] "GET /favicon.ico HTTP/1.1" 200 0 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:42 +0000] "GET / HTTP/1.1" 200 626 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
…

如果要在保持所有服务状态的同时暂停Docker Compose环境,请运行:

  • docker-compose pause
Output
Pausing travellist-db ... done Pausing travellist-nginx ... done Pausing travellist-app ... done

然后,您可以通过以下方式恢复服务:

  • docker-compose unpause
Output
Unpausing travellist-app ... done Unpausing travellist-nginx ... done Unpausing travellist-db ... done

要关闭Docker Compose环境并删除其所有容器,网络和卷,请运行:

  • docker-compose down
Output
Stopping travellist-nginx ... done Stopping travellist-db ... done Stopping travellist-app ... done Removing travellist-nginx ... done Removing travellist-db ... done Removing travellist-app ... done Removing network travellist-laravel-demo_travellist

有关所有Docker Compose命令的概述,请查看Docker Compose命令行参考

结论

在本指南中,我们使用Docker Compose建立了具有三个容器的Docker环境,以在YAML文件中定义我们的基础架构。

从现在开始,您可以在Laravel应用程序上工作,而无需安装和设置用于开发和测试的本地Web服务器。此外,您将使用可轻松复制和分发的一次性环境,这在开发应用程序以及向生产环境过渡时会很有帮助。

发表评论

电子邮件地址不会被公开。 必填项已用*标注