Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nomad doesn't respect Dockerfile's STOPSIGNAL directive #9989

Closed
rc407 opened this issue Feb 8, 2021 · 3 comments · Fixed by #10441
Closed

Nomad doesn't respect Dockerfile's STOPSIGNAL directive #9989

rc407 opened this issue Feb 8, 2021 · 3 comments · Fixed by #10441
Assignees
Labels
hcc/cst Admin - internal stage/accepted Confirmed, and intend to work on. No timeline committment though. theme/driver/docker type/bug

Comments

@rc407
Copy link

rc407 commented Feb 8, 2021

Issue

Nomad stop sends a SIGTERM signal first instead of sending STOPSIGNAL when terminating a task

When you issue a docker stop, docker sends a SIGTERM by default to the main process inside the container, and after a grace period a SIGKILL. The first signal can be overridden by setting the STOPSIGNAL instruction in the dockerfile. If this is set to let's say 9 then docker will send 9 to the container.

When you issue a nomad stop and STOPSIGNAL is not the default , Nomad calls the kill docker API endpoint first, sends a SIGTERM, after a grace period a SIGKILL and then calls the stop endpoint and sends the signal specified in the STOPSIGNAL instruction.

Expected behavior

Nomad calls two different docker API endpoints, one after another, and both send a signal to the container when it should use only one call to one of the endpoints.

For docker based tasks that use the default kill_signal (SIGTERM), we could just use one call to StopContainer with the kill_timeout set in docker/handle.go

To Reproduce

1 - Either add the STOPSIGNAL 9 instruction to a dockerfile and build a new image or run the following existing image/job and save it as servicea.nomad:

job "servicea" {
    datacenters = ["dc1"]
    type = "service"

    group "api" {
        count = 1

        network {
            port "api" {
                to     = 9090
            }
        }

        task "api" {
            driver = "docker"
            config {
                image      = "monacosl/connect-flask:latest"
            }
        }

        service {
            name = "service-a"
            port = "api"
        }
   }
 }

2 - On another session run docker system events

3 - Run nomad stop servicea. The output of docker system events will show 3 signals being sent to the container:

2021-02-05T14:30:18.253744214Z container kill ab62e44b2127cebf8ecd2ad47dc54e7189efe2a226aead72420982aac22b9c31 (com.hashicorp.nomad.alloc_id=f8e94bee-e656-7b57-3cff-8e02fafe4124, image=monacosl/connect-flask:latest, name=api-f8e94bee-e656-7b57-3cff-8e02fafe4124, signal=15)
2021-02-05T14:30:23.253511363Z container kill ab62e44b2127cebf8ecd2ad47dc54e7189efe2a226aead72420982aac22b9c31 (com.hashicorp.nomad.alloc_id=f8e94bee-e656-7b57-3cff-8e02fafe4124, image=monacosl/connect-flask:latest, name=api-f8e94bee-e656-7b57-3cff-8e02fafe4124, signal=9)
2021-02-05T14:30:23.270211516Z container kill ab62e44b2127cebf8ecd2ad47dc54e7189efe2a226aead72420982aac22b9c31 (com.hashicorp.nomad.alloc_id=f8e94bee-e656-7b57-3cff-8e02fafe4124, image=monacosl/connect-flask:latest, name=api-f8e94bee-e656-7b57-3cff-8e02fafe4124, signal=9)

4 - Run the same job again

5 - Run docker ps and then docker stop <container_id>. This time the output will show only one signal (the STOPSIGNAL one)

2021-02-05T14:40:19.619410343Z container kill bb603cba835e0be05c873dd785eef3c4f4d315d89efa57da6ff4a3a27f0cb043 (com.hashicorp.nomad.alloc_id=0f116067-6936-ea75-1edb-d04c6a4e1e20, image=monacosl/connect-flask:latest, name=api-0f116067-6936-ea75-1edb-d04c6a4e1e20, signal=9)

Environment

Nomad Version

Nomad v1.0.3

Docker Version

Client: Docker Engine - Community
 Version:           20.10.1
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        831ebea
 Built:             Tue Dec 15 04:35:01 2020
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.1
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       f001486
  Built:            Tue Dec 15 04:32:57 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
@rc407 rc407 added the hcc/cst Admin - internal label Feb 8, 2021
@tgross tgross added theme/driver/docker type/bug stage/accepted Confirmed, and intend to work on. No timeline committment though. labels Feb 9, 2021
@tgross
Copy link
Member

tgross commented Feb 9, 2021

Thanks @rc407! We'll get this onto the roadmap.

@tgross tgross added this to Needs Triage in Nomad - Community Issues Triage via automation Feb 12, 2021
@tgross tgross moved this from Needs Triage to Needs Roadmapping in Nomad - Community Issues Triage Feb 12, 2021
@tgross
Copy link
Member

tgross commented Feb 26, 2021

I did a bit of testing of this and the behavior doesn't quite seem to line up with what you're saying here @rc407.

If I look at that Docker image, I can see the STOPSIGNAL is 9, not 19.

But the behavior is still odd. If I use that Docker image in this simple jobspec:

job "example" {
  datacenters = ["dc1"]

  task "api" {
    driver = "docker"

    config {
      image = "monacosl/connect-flask:latest"
    }
  }
}

Stopping that job results in a SIGTERM, a delay for the kill timeout, and then 2 SIGKILLs:

2021-02-26T18:59:30.178064405Z container kill be0b2b9f56406900e4400677bc980afbcf35cd878a404bb6fefa972474113ea0 (com.hashicorp.nomad.alloc_id=744a0efc-8bbb-62de-8556-c8fa3ad77719, image=monacosl/connect-flask:latest, name=api-744a0efc-8bbb-62de-8556-c8fa3ad77719, signal=15)
2021-02-26T18:59:35.182411000Z container kill be0b2b9f56406900e4400677bc980afbcf35cd878a404bb6fefa972474113ea0 (com.hashicorp.nomad.alloc_id=744a0efc-8bbb-62de-8556-c8fa3ad77719, image=monacosl/connect-flask:latest, name=api-744a0efc-8bbb-62de-8556-c8fa3ad77719, signal=9)
2021-02-26T18:59:35.202175440Z container kill be0b2b9f56406900e4400677bc980afbcf35cd878a404bb6fefa972474113ea0 (com.hashicorp.nomad.alloc_id=744a0efc-8bbb-62de-8556-c8fa3ad77719, image=monacosl/connect-flask:latest, name=api-744a0efc-8bbb-62de-8556-c8fa3ad77719, signal=9)
2021-02-26T18:59:35.236393436Z container die be0b2b9f56406900e4400677bc980afbcf35cd878a404bb6fefa972474113ea0 (com.hashicorp.nomad.alloc_id=744a0efc-8bbb-62de-8556-c8fa3ad77719, exitCode=137, image=monacosl/connect-flask:latest, name=api-744a0efc-8bbb-62de-8556-c8fa3ad77719)

Having STOPSIGNAL set to 9 is weird anyways, so let's build one with it set to 19 just in case it's special cased:

FROM busybox:1
STOPSIGNAL 19

And then run this jobspec with it:

job "example" {
  datacenters = ["dc1"]

  task "api" {
    driver = "docker"

    config {
      image   = "0x74696d/stopsignal"
      command = "httpd"
      args    = ["-v", "-f", "-p", "8001", "-h", "/local"]
    }
  }
}

I get SIGTERM (again, not SIGSTOP), the delay, and then SIGTERM + SIGKILL.

2021-02-26T19:19:07.051035797Z container kill 52688ed29af68b1194b2f123efbf32a1aa157b9259b6cd94c7bfe5282b8dc344 (com.hashicorp.nomad.alloc_id=180589aa-f969-e371-5963-2ab93c8bc6b0, image=0x74696d/stopsignal, name=api-180589aa-f969-e371-5963-2ab93c8bc6b0, signal=15)
2021-02-26T19:19:12.053955683Z container kill 52688ed29af68b1194b2f123efbf32a1aa157b9259b6cd94c7bfe5282b8dc344 (com.hashicorp.nomad.alloc_id=180589aa-f969-e371-5963-2ab93c8bc6b0, image=0x74696d/stopsignal, name=api-180589aa-f969-e371-5963-2ab93c8bc6b0, signal=15)
2021-02-26T19:19:12.074908740Z container kill 52688ed29af68b1194b2f123efbf32a1aa157b9259b6cd94c7bfe5282b8dc344 (com.hashicorp.nomad.alloc_id=180589aa-f969-e371-5963-2ab93c8bc6b0, image=0x74696d/stopsignal, name=api-180589aa-f969-e371-5963-2ab93c8bc6b0, signal=9)

Whereas this jobspec, with an explicitly configured kill_signal:

job "example" {
  datacenters = ["dc1"]

  task "api" {
    driver = "docker"
    kill_signal = "SIGSTOP"

    config {
      image = "monacosl/connect-flask:latest"
    }
  }
}

Stopping that job results in SIGSTOP as we'd expect, a delay for the kill timeout, and then 2 SIGKILLs:

$ docker system events
2021-02-26T18:58:36.642938654Z container kill 79f64843970e3197ea82a358802da9f86c05100329f9967c4f34d6ad9fc6e143 (com.hashicorp.nomad.alloc_id=879ad75f-e763-e840-923e-774e9f2f6723, image=monacosl/connect-flask:latest, name=api-879ad75f-e763-e840-923e-774e9f2f6723, signal=19)
2021-02-26T18:58:41.640331978Z container kill 79f64843970e3197ea82a358802da9f86c05100329f9967c4f34d6ad9fc6e143 (com.hashicorp.nomad.alloc_id=879ad75f-e763-e840-923e-774e9f2f6723, image=monacosl/connect-flask:latest, name=api-879ad75f-e763-e840-923e-774e9f2f6723, signal=9)
2021-02-26T18:58:41.669433599Z container kill 79f64843970e3197ea82a358802da9f86c05100329f9967c4f34d6ad9fc6e143 (com.hashicorp.nomad.alloc_id=879ad75f-e763-e840-923e-774e9f2f6723, image=monacosl/connect-flask:latest, name=api-879ad75f-e763-e840-923e-774e9f2f6723, signal=9)
2021-02-26T18:58:41.706183801Z container die 79f64843970e3197ea82a358802da9f86c05100329f9967c4f34d6ad9fc6e143 (com.hashicorp.nomad.alloc_id=879ad75f-e763-e840-923e-774e9f2f6723, exitCode=137, image=monacosl/connect-flask:latest, name=api-879ad75f-e763-e840-923e-774e9f2f6723)

And then this jobspec, with no kill_signal configured and no STOPSIGNAL in the Docker image:

job "example" {
  datacenters = ["dc1"]

  task "api" {
    driver = "docker"

    config {
      image   = "busybox:1"
      command = "httpd"
      args    = ["-v", "-f", "-p", "8001", "-h", "/local"]
    }
  }
}

Stopping that job results in SIGSTOP, a delay for the kill timeout, followed by SIGTERM + SIGKILL:

2021-02-26T19:00:37.809605201Z container kill 6bd26e1e32f9fe3e780452f755aac1a334c8882cecb6b6445e04bb2794c296d3 (com.hashicorp.nomad.alloc_id=ee8546dc-5aa1-7ccf-9572-1591eacecbac, image=busybox:1, name=api-ee8546dc-5aa1-7ccf-9572-1591eacecbac, signal=15)
2021-02-26T19:00:42.812614989Z container kill 6bd26e1e32f9fe3e780452f755aac1a334c8882cecb6b6445e04bb2794c296d3 (com.hashicorp.nomad.alloc_id=ee8546dc-5aa1-7ccf-9572-1591eacecbac, image=busybox:1, name=api-ee8546dc-5aa1-7ccf-9572-1591eacecbac, signal=15)
2021-02-26T19:00:42.832067886Z container kill 6bd26e1e32f9fe3e780452f755aac1a334c8882cecb6b6445e04bb2794c296d3 (com.hashicorp.nomad.alloc_id=ee8546dc-5aa1-7ccf-9572-1591eacecbac, image=busybox:1, name=api-ee8546dc-5aa1-7ccf-9572-1591eacecbac, signal=9)
2021-02-26T19:00:42.895397738Z container die 6bd26e1e32f9fe3e780452f755aac1a334c8882cecb6b6445e04bb2794c296d3 (com.hashicorp.nomad.alloc_id=ee8546dc-5aa1-7ccf-9572-1591eacecbac, exitCode=137, image=busybox:1, name=api-ee8546dc-5aa1-7ccf-9572-1591eacecbac)

So there are two problems here:

  • The Docker image's STOPSIGNAL is not being respected. Nomad doesn't know what's in the Docker image, so this may be something we're not setting correctly in the Docker API call. I'm going to rename this issue to match the observed behavior. Note that the workaround here is to use Nomad's kill_signal, which seems to be working as expected.
  • dockerd is receiving two signals when we finally kill the container. This is weird, but not really a bug either because at this point the container should be going down hard no matter what.

@tgross tgross changed the title Nomad stop sends a SIGTERM signal first instead of sending STOPSIGNAL when terminating a task Nomad doesn't respect Dockerfile's STOPSIGNAL directive Feb 26, 2021
@tgross tgross removed this from Needs Roadmapping in Nomad - Community Issues Triage Mar 3, 2021
isabeldepapel added a commit that referenced this issue Apr 23, 2021
This fixes a bug where Nomad overrides a Dockerfile's STOPSIGNAL with
the default kill_signal (SIGTERM).

This adds a check for kill_signal. If it's not set, it calls
StopContainer instead of Signal, which uses STOPSIGNAL if it's
specified. If both kill_signal and STOPSIGNAL are set, Nomad tries to
stop the container with kill_signal first, before then calling
StopContainer.

Fixes #9989
isabeldepapel added a commit that referenced this issue May 3, 2021
This fixes a bug where Nomad overrides a Dockerfile's STOPSIGNAL with
the default kill_signal (SIGTERM).

This adds a check for kill_signal. If it's not set, it calls
StopContainer instead of Signal, which uses STOPSIGNAL if it's
specified. If both kill_signal and STOPSIGNAL are set, Nomad tries to
stop the container with kill_signal first, before then calling
StopContainer.

Fixes #9989
isabeldepapel added a commit that referenced this issue May 3, 2021
This fixes a bug where Nomad overrides a Dockerfile's STOPSIGNAL with
the default kill_signal (SIGTERM).

This adds a check for kill_signal. If it's not set, it calls
StopContainer instead of Signal, which uses STOPSIGNAL if it's
specified. If both kill_signal and STOPSIGNAL are set, Nomad tries to
stop the container with kill_signal first, before then calling
StopContainer.

Fixes #9989
isabeldepapel added a commit that referenced this issue May 4, 2021
This fixes a bug where Nomad overrides a Dockerfile's STOPSIGNAL with
the default kill_signal (SIGTERM).

This adds a check for kill_signal. If it's not set, it calls
StopContainer instead of Signal, which uses STOPSIGNAL if it's
specified. If both kill_signal and STOPSIGNAL are set, Nomad tries to
stop the container with kill_signal first, before then calling
StopContainer.

Fixes #9989
isabeldepapel added a commit that referenced this issue May 4, 2021
This fixes a bug where Nomad overrides a Dockerfile's STOPSIGNAL with
the default kill_signal (SIGTERM).

This adds a check for kill_signal. If it's not set, it calls
StopContainer instead of Signal, which uses STOPSIGNAL if it's
specified. If both kill_signal and STOPSIGNAL are set, Nomad tries to
stop the container with kill_signal first, before then calling
StopContainer.

Fixes #9989
isabeldepapel added a commit that referenced this issue May 5, 2021
This fixes a bug where Nomad overrides a Dockerfile's STOPSIGNAL with
the default kill_signal (SIGTERM).

This adds a check for kill_signal. If it's not set, it calls
StopContainer instead of Signal, which uses STOPSIGNAL if it's
specified. If both kill_signal and STOPSIGNAL are set, Nomad tries to
stop the container with kill_signal first, before then calling
StopContainer.

Fixes #9989
@github-actions
Copy link

I'm going to lock this issue because it has been closed for 120 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
hcc/cst Admin - internal stage/accepted Confirmed, and intend to work on. No timeline committment though. theme/driver/docker type/bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants