
# 使用容器镜像创建并执行HTTP函数
本章节将以使用容器镜像方式创建HTTP类型函数为例，介绍容器镜像函数的创建及测试过程。
此过程中用户需要在镜像中实现一个http server，并监听**8000（下文示例中提及的8000端口请不要变动）端口**接收请求。备注：可以使用任意基础镜像。
#### 约束与限制
- HTTP函数只支持使用APIC/APIG触发器。
- 在云上环境会默认使用uid 1003，gid 1003 启动容器。uid、gid可以在函数页面的"设置 \> 常规设置 \>容器镜像覆盖"板块中修改，但不可以是root或其他保留id。
- **HTTP函数示例中涉及的8000端口请勿修改。**
- **如果使用** **Alpine版的基础镜像** **，请使用"addgroup"和"adduser"命令。**
 
#### 准备工作
1. 注册华为账号并实名认证。 在创建函数前，请先注册华为账号并实名认证，具体步骤请参考[注册华为账号并开通华为云](https://support.huaweicloud.com/usermanual-account/account_id_001.html)和[实名认证介绍](https://support.huaweicloud.com/usermanual-account/account_auth_00001.html)。
   如果您已有一个华为账号并实名认证，请跳过此步骤。
   
2. 免费额度。 函数工作流服务每个月都会提供一定数量的免费额度，免费额度是子主账户共同使用，具体详情请参见[免费额度](https://support.huaweicloud.com/price-functiongraph/functiongraph_00_0012.html)。
   当免费额度使用完后，若您继续使用函数工作流时，账户的可用额度小于待结算的账单时，即被判定为账户欠费。欠费后，可能会影响您的服务资源的正常运行，请及时充值，具体详情请参考[账户充值](https://support.huaweicloud.com/usermanual-billing/bills-topic_30000002.html)。
   
3. 为用户添加函数的操作权限。 本章节所有操作均默认具有操作权限，请确保您登录的用户已有"FunctionGraph Administrator"权限，即FunctionGraph服务所有权限，更多权限的说明请参考[权限管理](https://support.huaweicloud.com/productdesc-functiongraph/functiongraph_01_0160.html)。
   
 
#### 步骤一：制作镜像
以在linux x86 64位系统上制作镜像为例。（系统配置无要求）
1. 创建一个空文件夹
   ```
   mkdir custom_container_http_example && cd custom_container_http_example
   ```
   

2. 以Nodejs语言为例，实现一个Http Server，其他语言请参考开发指南。 创建一个main.js文件，引入express框架，接收POST请求，打印请求Body到标准输出并返回Hello FunctionGraph, method POST给客户端。
   ```
   const express = require('express'); 
    
   const PORT = 8000; 
    
   const app = express(); 
   app.use(express.json());
    
   app.post('/*', (req, res) => { 
       console.log('receive', req.body); 
       res.send('Hello FunctionGraph, method POST');
   });
    
   app.listen(PORT, () => { 
     console.log(`Listening on http://localhost:${PORT}`); 
   });
   ```
   

3. 创建一个package.json文件，此文件用于向npm提供信息，使其能够识别项目以及处理项目的依赖关系。
   ```
   {
     "name": "custom-container-http-example",
     "version": "1.0.0",
     "description": "An example of a custom container http function",
     "main": "main.js",
     "scripts": {},
     "keywords": [],
     "author": "",
     "license": "ISC",
     "dependencies": {
         "express": "^4.17.1"
     }
   }
   ```
   - name：值为项目名。
   
   - version：值为项目版本。
   
   - main：列举文件为程序的入口文件。
   
   - dependencies：列出npm上可用的项目的所有依赖项。
    
4. 创建Dockerfile文件
   ```
   FROM node:12.10.0
   ENV HOME=/home/custom_container
   ENV GROUP_ID=1003
   ENV GROUP_NAME=custom_container
   ENV USER_ID=1003
   ENV USER_NAME=custom_container
   RUN mkdir -m 550 ${HOME} && groupadd -g ${GROUP_ID} ${GROUP_NAME} && useradd -u ${USER_ID} -g ${GROUP_ID} ${USER_NAME}
   COPY --chown=${USER_ID}:${GROUP_ID} main.js ${HOME}
   COPY --chown=${USER_ID}:${GROUP_ID} package.json ${HOME}
   RUN cd ${HOME} && npm install
   RUN chown -R ${USER_ID}:${GROUP_ID} ${HOME}
   RUN find ${HOME} -type d | xargs chmod 500
   RUN find ${HOME} -type f | xargs chmod 500
   USER ${USER_NAME}
   WORKDIR ${HOME}
   EXPOSE 8000
   ENTRYPOINT ["node", "/home/custom_container/main.js"]
   ```
   - FROM：指定基础镜像为node:12.10.0，基础镜像必须设置，值可修改。
   
   - ENV：设置环境变量，设置HOME环境变量为/home/custom_container，设置GROUP_NAME和USER_NAME为custom_container，**USER_ID** 和**GROUP_ID** 为**1003**，这些环境变量必须设置，值可修改。
   
   - RUN：格式为RUN \<命令\>，例如RUN mkdir -m 550 ${HOME}表示构建容器时创建${USER_NAME}用户的home目录。
   
   - USER：切换${USER_NAME}用户。
   
   - WORKDIR：切换工作目录到${USER_NAME}用户的"${HOME}"目录下。
   
   - COPY：将main.js和package.json拷贝到容器的${USER_NAME}用户的home目录下。
   
   - **EXPOSE：暴露容器的8000端口，请勿修改。**
   
   - ENTRYPOINT：使用node main.js命令启动容器，请勿修改。
    

5. 构建镜像 指定镜像的名称为custom_container_http_example，版本为latest，"."指定Dockerfile所在目录，镜像构建命令将该路径下所有的内容打包给容器引擎帮助构建镜像。
   ```
   docker build -t custom_container_http_example:latest .
   ```
   
#### 步骤二：本地验证
1. 启动docker容器
   ```
   docker run -u 1003:1003 -p 8000:8000 custom_container_http_example:latest
   ```
   

2. 打开一个新的命令行窗口，向开放的8000端口发送消息，支持访问模板代码中根目录"/"下所有路径，以下以"helloworld"为例。
   ```
   curl -XPOST -H 'Content-Type: application/json' -d '{"message":"HelloWorld"}' localhost:8000/helloworld
   ```
   按照模块代码中返回
   ```
   Hello FunctionGraph, method POST
   ```
   

3. 在容器启动端口可以看到
   ```
   receive {"message":"HelloWorld"}
   ```
   ![](https://support.huaweicloud.com/qs-functiongraph/zh-cn_image_0000001472516001.png "点击放大")
   或者使用docker logs命令获取容器的日志
   ![](https://support.huaweicloud.com/qs-functiongraph/zh-cn_image_0000001422359078.png "点击放大")
   
 
 #### 步骤三：上传镜像
1. 登录容器镜像服务控制台，在左侧导航栏选择"我的镜像"。
2. 单击右上角的"客户端上传"或"页面上传"。
3. 根据指示上传镜像。 
   
   ![](https://support.huaweicloud.com/qs-functiongraph/zh-cn_image_0000001825074354.png "点击放大")
   
4. 上传成功后，在"我的镜像"界面可查看。
 
#### 步骤四：创建函数
1. 登录[函数工作流控制台](https://console.huaweicloud.com/functiongraph/?locale=zh-cn)，在左侧导航栏选择"函数 \> 函数列表"。
2. 单击右上方的"创建函数"，进入"创建函数"页面，创建方式选择"容器镜像"。
3. 填写函数基本信息。
   - 函数类型：选择"HTTP函数"。
   
   - 区域：默认，支持用户选择其他区域。 **不同区域的资源之间内网不互通。请就近选择靠近您业务的区域，可以降低网络时延、提高访问速度。**
     
   
   - 函数名称：输入"custom_container_http"。
   
   - 企业项目：默认"default"，支持用户选择已创建的企业项目。 企业项目是一种云资源管理方式，企业项目管理服务提供统一的云资源按项目管理，以及项目内的资源管理、成员管理。
     
   
   - 委托：使用包含SWR Admin权限的委托，如果没有委托，请参考[创建委托](https://support.huaweicloud.com/usermanual-functiongraph/functiongraph_01_0920.html)。
   
   - 容器镜像：输入[步骤三]上传到SWR的镜像。（示例填写：swr.{局点id}.myhuaweicloud.com/{组织名称}/{镜像名称}:{版本名称}
    
4. 填写容器镜像覆盖参数（可选）。
   - CMD：容器的启动命令，例如"/bin/sh"。该参数为可选参数，不填写，则默认使用镜像中的Entrypoint/CMD。
   
   - Args：容器的启动参数，例如"-args,value1"。该参数为可选参数，不填写，则默认使用镜像中的CMD。
   
   - Working Dir：容器的工作目录。该参数为可选参数，不填写，则默认使用镜像中的配置。文件夹路径，以/开头。
   
   - 用户ID：输入用户ID。
   
   - 用户组ID：输入用户组ID。
    
5. 完成后单击"创建函数"。
 
#### 步骤五：测试函数
1. 在函数详情页，单击"测试"，在弹窗中创建新的测试事件。
2. 选择"apig-event-template"，事件名称输入"helloworld"，测试事件修改为如下所示，完成后单击"创建"。
   ```
   {
       "body": "{\"message\": \"helloworld\"}",
       "requestContext": {
           "requestId": "11cdcdcf33949dc6d722640a13091c77",
           "stage": "RELEASE"
       },
       "queryStringParameters": {
           "responseType": "html"
       },
       "httpMethod": "POST",
       "pathParameters": {},
       "headers": {
           "Content-Type": "application/json"
       },
       "path": "/helloworld",
       "isBase64Encoded": false
   }
   ```
   
 
#### 步骤六：查看执行结果
单击helloworld事件的"测试"，执行后，在右侧查看执行结果，执行结果如下图。
图1执行结果   
![](https://support.huaweicloud.com/qs-functiongraph/zh-cn_image_0000001422359706.png "点击放大")
- "函数返回"显示函数的返回结果。
- "日志"部分显示函数执行过程中生成的日志。
- "执行摘要"部分显示"日志"中的关键信息。
  ![](https://support.huaweicloud.com/qs-functiongraph/public_sys-resources/note_3.0-zh-cn.png)
  此页面最多显示2K日志，了解函数更多日志信息，请参考[配置和查看函数的调用日志](https://support.huaweicloud.com/usermanual-functiongraph/functiongraph_01_1834.html)。
  
 
#### 相关信息
- 了解函数工作流的相关概念，请参考[基本概念](https://support.huaweicloud.com/productdesc-functiongraph/functiongraph_02_1005.html)。
- 了解函数工作流的价格，请参考[函数工作流价格详情](https://www.huaweicloud.com/pricing/calculator.html?tab=detail#/function)。
- 了解函数工作流的约束与限制，请参考[约束与限制](https://support.huaweicloud.com/productdesc-functiongraph/functiongraph_01_0150.html)。
 
