Technology sharing | how to make thousands of containers "survive" at the same time

Posted by JasonO on Mon, 24 Jan 2022 02:04:07 +0100

Recently, I received a request that users can apply to create one or more docker containers. The containers should always exist, users can access them no matter how long it takes, and the data generated by users always exists. In other words, we should achieve container persistence, that is, we should provide a small server, However, over time, the server resources will be occupied by containers and can only be expanded. What I need to do is to provide as many docker containers as possible while saving servers as much as possible.

Brief ideas

After the user uses it for a period of time, persist the difference data generated by the user, and then stop the container. When the user accesses again, he can create a new container by intercepting the access address, put the persistent data into the new container, and then redirect it. The user can access the same container as before, as if the container had always existed.

Implementation process

1. When the user requests the interface to generate a container, return the domain name
2. After a period of time, stop the container and configure the next access to start the container
3. User access, restart the container through the previous configuration, and redirect and return the previous data of the user
See the figure below for details

Tool support

docker-client

Because I want to use java to operate docker, start, stop, delete and execute commands, I found a quick tool to use.

Attach official website https://github.com/docker-jav...

kong api

When users access the interface in the container, if the container has been deleted, there is no way to access it. Therefore, I have made a layer of agent in the outer layer. The tool used is the kong API. kong is an API Gateway developed based on nginx. It can be controlled by code, rather than manually modifying the configuration file like nginx.

Briefly introduce several functions I use:

Route: is the forwarding rule of the request. It forwards the request to the Service according to the Hostname and PATH. (what I understand is the location of nginx)

Services: a collection of multiple Upstream streams and the forwarding target of Route. (what I understand is nginx server)

plugin: a plug-in, which can be global, bound to Service, Router and Consumer. (there are authentication, access restriction, monitoring, logging and other plug-ins. I use request transformer here. I need to carry some parameters in the request for redirection)

Attach official website https://docs.konghq.com/

Concrete implementation
1, Build container

When accessing the interface, the user creates and starts the container in the background and generates kong's configuration. Subsequent users access the container through kong's agent, route - > Services - > docker, and set the heartbeat and expiration time. The heartbeat is generated by the user's operation. The expiration time is the time when the container can be stopped. If there is a heartbeat, the expiration time will be updated.

Code example

// Create container
CreateContainerCmd createContainerCmd = dockerClient
     .createContainerCmd(docker.getImage())
     .withTty(true)
     .withName(this.generateContainerName(docker))
     .withCmd(managerUrl + docker.getId())
     .withHostConfig(hostConfig);
CreateContainerResponse containerResponse = createContainerCmd.exec();
// Start container
dockerClient.startContainerCmd(containerResponse.getId()).exec();

2, Stop container

To stop the container, it is necessary to ensure that the container can be started again the next time the user accesses the container. Therefore, after stopping the container, the user's request to access the container should be transferred to the background interface through the agent, the agent kong here. The specific step is that there will be a job in the background to control the inactive container to persist the data, then stop the container and configure kong to distribute the next request to the background service.

Specific implementation:

1. Add services to set the http interface request address

JKongAdmin admin = new JKongAdmin(kongAdminUrl);
ServiceResp resp = admin.addService(new ServiceReq.Builder()
                    .host(recoverHost)
                    .port(recoverPort)
                    .path(recoverPath)
                    .build());

2. Add route, set serviceId, and the user can access the domain name

JKongAdmin admin = new JKongAdmin(kongAdminUrl);
RouteResp resp = admin.addRoute(new RouteReq.Builder()
                      .serviceId(serviceId)
                      .host(host)
                      .build());

3. Add plugin to set serviceId. The background address is mainly used to restart the container. It is a parameter brought in

JKongAdmin admin = new JKongAdmin(kongAdminUrl);
Map<String, String> replaceConfigMap = new HashMap<>();
replaceConfigMap.put("uri", recoverPath);
PluginResp resp = admin.addPlugin(new PluginReq.Builder()

                   .name("request-transformer")
                   .serviceId(serviceId)
                   .config("replace",replaceConfigMap)
                   .enabled(true)
                   .build());
![](https://showmebugimg.dao42.com/uploads/20220122_6y4lpd9isx47nwigvzl2mrfoyzq5tnzg.jpg!/rotate/auto/fw/800)

**3, Restart container**


Users arrive through domain name access route,route Forward to corresponding service,In visit service Configured http Interface, and then http The interface starts the container and redirects the browser. Because the container already exists, you only need to start it, and the speed can reach the second level,So as to achieve the illusion of "survival" of the container.

![](https://showmebugimg.dao42.com/uploads/20220122_2q2zo10usms4p1pxen5nk6dc7byz18oa.jpg!/rotate/auto/fw/800)
@GetMapping("/recover")
@ApiOperation(value = "Resource recovery")
public void recover(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String host = request.getHeader("x-forwarded-host");
    String path = request.getHeader("x-forwarded-path");
    this.playgroundService.recover(host);
    response.sendRedirect(host + ":" + kongClientPort + path);
}
![](https://showmebugimg.dao42.com/uploads/20220122_l3vtkpdwqrcdxk418u5yjfbw9gm2k6uh.jpg!/rotate/auto/fw/800)

**summary**

Finally, analyze the requirements. In fact, the difficulty is mainly to access the page after the container is stopped, and then start the container, because the user accesses the domain name bound to the container, and there is no http Interface, so you can only use kong API Intercept the domain name and forward it to the background server to start docker And redirection to stop idle containers, save memory, and restart when necessary. Through processing, it seems that the container has always been in the startup state. Although there are many forwarding in the background, the foreground page does not perceive it.

The above is what we are going to talk about today. Technically, we mainly use kong API and docker-java,In fact, there is no difficulty in using technology, mainly sharing ideas.

Topics: Java Front-end MySQL github Programmer