37

I have a flask app which I am running like this:

flask run --host=0.0.0.0

When I look at the process list I see this:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 23:48 pts/0        00:00:00 /bin/sh -c flask run --host=0.0.0.0
root         6     1  1 23:48 pts/0        00:00:01 /usr/local/bin/python /usr/local/bin/flask run --host=0.0.0.0
root         8     6  3 23:48 pts/0        00:00:02 /usr/local/bin/python /usr/local/bin/flask run --host=0.0.0.0

Three processes.

If I run using --without-threads I also the same three processes:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 00:28 pts/0    00:00:00 /bin/sh -c flask run --host=0.0.0.0 --without-threads
root         6     1  2 00:28 pts/0    00:00:02 /usr/local/bin/python /usr/local/bin/flask run --host=0.0.0.0 --without-threads
root         8     6  4 00:28 pts/0    00:00:04 /usr/local/bin/python /usr/local/bin/flask run --host=0.0.0.0 --without-threads

Is there a way to somehow run flask as a single process?

Motivation

The flask app in question is running inside a docker container. I would like to be able to set breakpoints using ipdb.

I have observed that if I set this in my docker-compose file:

    stdin_open: true
    tty: true

and run, instead of a flask app, a simple single-process python app...

$ docker exec -it bug_demo_bug_demo_1 bash
root@98245482089b:/opt/bug_demo/bug_demo# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 00:41 pts/0    00:00:00 /bin/sh -c python app.py
root         7     1 20 00:41 pts/0    00:00:00 python app.py

... and attach to the container while the app is at a breakpoint, I am able to drop into ibpd and use it normally – arrow keys and tab completion work properly.

But when I try do the same with the flask app (attach to the container while the app is waiting in a breakpoint), things do not work correctly.

Either I disable tty: true in docker-compose.yml, and can use use ipdb but without arrow keys and tab completion, OR I leave tty: true in place, but then cannot really use ipdb at all, b/c it appears the tty is attached to all three flask processes, causing everything other than single character commands to get garbled. (Although I can see with this setup that arrow keys and tab completion work.)

All of this leads me to believe that if I can find a way to run my flask app as a single process, I will be able to attach to the docker container and use ipdb as desired.

Is there some way to do this?

Update: problem manifests during startup, not during request handling

Upon further examination, I see that this problem only manifests during "startup" code. ex: if the breakpoint is inside the create_app function.

If the breakpoint is inside a request handler method, or code called from a request handler, everything works as expected.

Using exec reduces the process count from three to two (the root process gets replaced by the first worker), but the problem still manifest for breakpoints inside create_app.

Running flask with --no-reload makes the second worker go away, so process count can then be forced to one or two, by then not using or using exec. Running with --no-reload is not ideal for my use case, but it does make the problem go away, even for breakpoints in create_app.

For my purposes, I can live with the limitation of ipdb only playing nice with the terminal inside request handlers -- I don't expect a great need to run the debugger from startup code. (But I will still accept an answer & happily award the bounty, if anyone can explain exactly what is happening in the startup-code breakpoint case, and why the problem does not manifest in the request-handler breakpoint case.)

Based on the --no-reload finding, it does feel like the underlying flakiness is somehow related to the TTY being "shared" by the request handling process and the code-reloading process.

Flask & Docker Version Info

ipdb> flask.__version__
'1.0.3'
$ docker version
Client: Docker Engine - Community
 Version:           18.09.2
 API version:       1.39
 Go version:        go1.10.8
 Git commit:        6247962
 Built:             Sun Feb 10 04:12:39 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.2
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.6
  Git commit:       6247962
  Built:            Sun Feb 10 04:13:06 2019
  OS/Arch:          linux/amd64
  Experimental:     false
$ docker info
Containers: 22
 Running: 3
 Paused: 0
 Stopped: 19
Images: 362
Server Version: 18.09.2
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 9754871865f7fe2f4e74d43e2fc7ccd237edcbce
runc version: 09c8266bf2fcf9519a651b04ae54c967b9ab86ec
init version: fec3683
Security Options:
 seccomp
  Profile: default
Kernel Version: 4.9.125-linuxkit
Operating System: Docker for Mac
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.855GiB
Name: linuxkit-025000000001
ID: ZAK2:V2VU:IZFF:6MQQ:IFJB:2ZKY:VHA5:CSO3:VXQQ:UK6C:O3I7:S3ZU
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): true
 File Descriptors: 59
 Goroutines: 89
 System Time: 2019-07-28T14:00:38.3184372Z
 EventsListeners: 2
HTTP Proxy: gateway.docker.internal:3128
HTTPS Proxy: gateway.docker.internal:3129
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
$ docker-compose --version
docker-compose version 1.23.2, build 1110ad01
billc
  • 1,791
  • 4
  • 16
  • 25
  • 2
    Are you running your Flask app in debug mode? – Makoto Jul 28 '19 at 02:54
  • Makato: yes. does that matter? – billc Jul 28 '19 at 02:55
  • 6
    This might be worth a read: [Why does a Flask app create two process?](https://stackoverflow.com/q/28585033/2745495) – Gino Mempin Jul 28 '19 at 03:52
  • 2
    @billc, please add the output of `docker version` and `docker info` and `docker-compose --version` to your question. Also what flask version and which OS? I tested by creating a simple app on Mac and Ubuntu and i was not able to reproduce the issue. Also will suggest to use `exec flask run --host=0.0.0.0` in your Dockerfile and see if it helps fix the issue – Tarun Lalwani Jul 28 '19 at 12:07
  • 2
    Version info added. The `--no-reload` mode, described in the question & dup question linked by Gino does make the problem go away. Tarun's report of not being able to repro led me to do more testing – the problem actually seems to manifest only during app startup, and not during request handling. – billc Jul 28 '19 at 14:47
  • Under the hood, Flask apps run the [`run_simple()` function](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/serving.py#L876-L892) from the [`werkzeug.serving`](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/serving.py) module (see [flask.app](https://github.com/pallets/flask/blob/master/src/flask/app.py#L981-L984) source). Can you try calling `werkzeug.serving.run_simple()` directly? – charlesreid1 Jan 14 '20 at 00:21
  • Did you ever find the solution to this? did you try something like `docker run --cpuset-cpus="0-2" myapp:latest` ? – Jose Angel Sanchez Jan 23 '20 at 03:35
  • 1
    Your objective is just debug your code, or there is another reason for u do that? – Lucas Araújo Feb 18 '20 at 23:03
  • Lucas Araújo -- Yeah, my goal was just to ensure that this project was packaged in a way that would always let me (and other folks on the team) always set and interact with breakpoints in a trouble-free way, as development unfolded. As per the "Update: ..." section, we basically have that in place (except for startup-time case). But as expected, there has never been much need for folks to monkey much with that code. Most of our dev time is spent in request-time code, where we have found breakpoint to work fine when accessed via `docker attach`. – billc Feb 20 '20 at 05:51
  • Jose Angel Sanchez -- I did play around with commands like that, but iirc, they didn't solve the problem for startup-time breakpoints. My "solution" has been to just live with the problem, since I mostly don't need startup-time breakpoints, and have had no trouble getting good behavior from request-time breakpoints, under `docker attach`. – billc Feb 20 '20 at 05:54
  • Flask uses werkzeug server underneath; https://werkzeug.palletsprojects.com/en/1.0.x/serving/. For all available options and explanations please refer the above documentation – choppe Apr 08 '20 at 14:41

3 Answers3

3

To make flask run in single processer use the below

if __name__ == '__main__':
    app.run(threaded=False, processes=1)
Alivx
  • 31
  • 5
1

Did you tried the "--no-debugger" arguments?
If you have DEBUG environment variable flask create a debugger process.

flask run --host=0.0.0.0 --without-threads --no-debugger
Tomer Zait
  • 1,756
  • 2
  • 14
  • 12
0

Using the flask application works on a single synchronous process.

That can only handle one request at a time.

If you want to deal with any parallel requests should wait until they can be handled then use this:

app.run(host=HOST, port=PORT, threaded=True)