> On April 21, 2015, 4:25 p.m., Jie Yu wrote:
> > src/slave/slave.cpp, lines 3065-3078
> > <https://reviews.apache.org/r/33249/diff/3/?file=938221#file938221line3065>
> >
> >     Instead of doing that in your way, can we just try to make sure 
> > `containerizer->wait` here will return a failure (or a Termination with 
> > some reason) when `containerizer->launch` fails. In that way, the 
> > `executorTerminated` will properly send status updates to the slave 
> > (TASK_LOST/TASK_FAILED).
> >     
> >     Or am I missing something?
> 
> Jie Yu wrote:
>     OK, I think I got confused by the ticket. There are actually two problems 
> here. The problem I am refering to is the fact that we don't send status 
> update to the scheduler if containerizer launch fails until executor 
> reregistration timeout happens. Since for docker containerizer, someone might 
> use a very large timeout value, ideally, the slave should send a status 
> update to the scheduler right after containerizer launch fails.
>     
>     After chat with Jay, the problem you guys are refering to is the fact 
> that the scheduler cannot disinguish between the case where the task has 
> failed vs. the case where the configuration of a task is not correct, because 
> in both cases, the scheduler will receive a TASK_FAILED/TASK_LOST.
> 
> Jie Yu wrote:
>     To address the first problem, I think the simplest way is to add a 
> containerizer->destroy(..) in executorLaunched when containerizer->launch 
> fails. In that way, it's going to trigger containerizer->wait and thus send 
> status update to the scheduler.
> 
> Jie Yu wrote:
>     Regarding the second problem, IMO, we should include a reason field in 
> Termination (https://issues.apache.org/jira/browse/MESOS-2035) and let 
> sendExecutorTerminatedStatusUpdate to propagate the termination reason to the 
> scheduler.
> 
> Timothy Chen wrote:
>     Reason field sounds good, I think what you proposed makes sense, in 
> docker containerizer at least we also need to make sure termination message 
> is set correctly as currently it doesn't contain all the error information 
> that we pass back to the launch future.
> 
> Jay Buffington wrote:
>     Sorry for the confusion.   There are three problems that are all related. 
>  Yes, we need to send statusUpdates as soon as containerizer launch fails and 
> yes, we need to set the reason so the scheduler can distinguish a slave 
> failure from a bad request.  However, my intention with this patch is not to 
> address either of those two problems.
>     
>     My goal with this patch is to simply send the containerizer launch 
> failure message back to the scheduler.  I am using Aurora with the docker 
> containerizer.  There are a myriad of reasons that dockerd could fail to 
> "docker run" a container.  Currently, when that fails the user sees a useless 
> and incorrect "Abnormal Executor Termination" message in the Aurora web ui.  
> With this patch they see the stderr output of "docker run."  This way they 
> can report meaningful error messages to the operations team.
>     
>     I can update this patch to address the other two issues, but the key is 
> that when the containerizer launch fails we have to send a statusUpdate with 
> a message that contains future.failure().  Before this patch we were only 
> logging it.  The scheduler needs to get that error message.
> 
> Jie Yu wrote:
>     Thanks for clarifying it, Jay! In fact, my proposal above should be able 
> to solve the third problem cleanly. Check out 
> `Slave::sendExecutorTerminatedStatusUpdate`. The containerizer should 
> properly set the message and reason fields in the Termination protobuf 
> (basically why the container gets terminated and what's the error message). 
> The slave will forward the reason and message to the scheduler through status 
> update.
>     
>     I chatted with BenM about this yesterday, and there are a couple of notes 
> I want to write down here.
>     
>     1. We probably need multiple levels for TaskStatus::Reason. In other 
> words, we probably need a "repeated Reason reasons" field in status update 
> message. For instance, for a containerizer launch failure, we probably need 
> two reasons: 1) the top level reason REASON_EXECUTOR_TERMINATED; 2) the 
> second level reason REASON_DOCKER_PULL_FAILURE;
>     
>     2. We probably want to allow extension to TaskStatus::Reason enum. For 
> example, some framework/executor may want to add customized reasons. We could 
> leverage the protobuf extension support to achieve that 
> (https://developers.google.com/protocol-buffers/docs/proto#extensions).
>     
>     3. The semantics around Termination is broken currently and we need to 
> clean it up. For instance, the following code is broken,
>     ```
>     void Slave::sendExecutorTerminatedStatusUpdate(...)
>     {
>       ...
>       if (termination.isReady() && termination.get().killed()) {
>         taskState = TASK_FAILED;
>         // TODO(dhamon): MESOS-2035: Add 'reason' to 
> containerizer::Termination.
>         reason = TaskStatus::REASON_MEMORY_LIMIT;
>       }
>     }
>     ```
>     because we now have disk limit as well.
>     
>     Another issue about Termination is that containerizer->wait sometimes 
> returns a Failure (e.g., launch failed, or destroy failed), that means we 
> cannot get the reason field and message field from Termination anymore. Right 
> now, if that happens, we always set the reason to be 
> REASON_EXECUTOR_TERMINATED and the message to be "Abnormal executor 
> termination" in the status update. I think this is a hack and IMO, the 
> containerizer should always return a valid Termination protobuf to the slave 
> so that the slave can send a meaningful status update to the framework.
> 
> Jay Buffington wrote:
>     Thanks for these comments, Jie.  I'll work implementing your proposal.
>     
>     I was unware of the existance of the Termination protobuf.  I am confused 
> by these comments (which I think are just flat out wrong). 
> https://github.com/apache/mesos/blob/c36d5996327ca765f49c211d489371c99ef8e090/src/slave/slave.cpp#L3177
>  which say:
>     
>        // A termination failure indicates the containerizer could not destroy 
> a
>        // container.
>        // TODO(idownes): This is a serious error so consider aborting the 
> slave if
>        // this occurs.
>        
>     I don't understand when you would ever return Failure() in your 
> containerizer without doing container->termination.set(termination);
>     
>     Thanks!
> 
> Jie Yu wrote:
>     > I don't understand when you would ever return Failure() in your 
> containerizer without doing container->termination.set(termination);
>     
>     Yeah, I think that's a tech debt and we should correct it. Let me know if 
> you need any help for this.
> 
> Jay Buffington wrote:
>     I finally sat down to work through what you said, Jie.
>     
>     Check this out:
>     
> https://github.com/apache/mesos/blob/master/src/slave/containerizer/docker.cpp#L1194-L1202
>     
>     When pull() fails destroy() is called.  Destroy correctly sets 
> Termination, but then it deletes the container!  Then when we call 
> containerizer->wait() in slave.cpp, we get back a Failure("Unknown container: 
> ...) instead of the termination future like we should.
>     
>     IMHO the solution here isn't to split up the code from destroy() into to 
> methods: one that sets the termination (call this launchFailed()) and then 
> make destroy() just erase the container.
>     
>     Then we should only call launchFailed() from launch (here: 
> https://github.com/apache/mesos/blob/master/src/slave/containerizer/docker.cpp#L636)
>  
>     
>     When launch fails we should call containerizer->destroy() from slave's 
> executorLaunched method.
> 
> Jie Yu wrote:
>     An alternative, can we simply just move 
> `containerizer->wait(containerId)` in `Slave::executorLaunched` to 
> `Framework::launchExecutor` right after `launch.onAny(defer(slave, 
> &Slave::executorLaunched, ...);`?
> 
> Jay Buffington wrote:
>     I spoke to Jie about this.  It turns out that launch and wait need to be 
> serialized to support the composing containerizer.  See 
> https://reviews.apache.org/r/23463   
>     
>     Jie questioned if destroy() really needs to be called on docker 
> containerizer launch failure.  To answer this I looked at what we do in 
> destroy if the containerizer state is <= RUNNING.  
>     
>     If the state is FETCHING destroy() will do fetcher->kill(containerId).  
> It isn't necessary to kill the fetcher if it's already failed.
>     
>     Simliarly, If state is PULLING destroy() does container->pull.discard() 
> which isn't necessary if the pull failed.
>     
>     If state is RUNNING destroy() will try to kill the executor if it has 
> started.  If "docker run" fails we know the executor isn't running.  
> destroy() goes on to attempt to run "docker stop" on the container which was 
> never created.  This results log spam regarding stopping a container that 
> doesn't exist.  
>     
>     Instead of calling destroy() on launch failure, we should call a method 
> that sets the Termination on the container with the proper message and reason 
> set.  We'll still need to erase the container object from the hashmap 
> somewhere and delete it.
>     
>     Thoughts?
> 
> Timothy Chen wrote:
>     It isn't necessary to kill the fetcher if it failed, but if destroy is 
> called in the middle of fetch we do want to make sure the fetch is killed, 
> otherwise it leaves a long running fetch process running for no reason, 
> similiar to pull.
>     RUNNING is a more interesting case, since we don't know if it's really 
> still running we just know it's passed fetch and pull, so at least we tried 
> to launch it, and stop shouldn't cause any problems.
>     
>     I think it's fine if we don't call destroy on failure, but we do have to 
> make sure to call the Termination as you mentioned, and also update the state 
> of the container.
> 
> Jie Yu wrote:
>     My thought is: we don't even need a method that sets the Termination when 
> launch fails, can we remove the `onFailed` callback in `launch` and simply 
> call `containerizer->destroy` in `executorLaunched` (right after 
> `containerizer->wait` is called) if containerizer launch fails. The destroy 
> will properly set the Termination. Also, in that way, it's guaranteed that 
> `containerizer->wait` will be called before `containerizer->destroy` so that 
> we can always get back a valid Termination.
>     
>     This solution also helps solve 
> https://issues.apache.org/jira/browse/MESOS-2656. 
>     
>     What do you guys think?

Yes, this makes sense.  We need to gaurentee that containerizer->wait and 
containerizer->destroy are serialized.

This race condition also exists in the mesos containerizer because it calls 
destroy on launch failure as well.  See 
https://github.com/apache/mesos/blob/664788b94/src/slave/containerizer/mesos/containerizer.cpp#L568-L587

Note that we'll need to update this comment as it will no longer be true:
https://github.com/apache/mesos/blob/664788b94/src/slave/containerizer/containerizer.hpp#L124-L125


- Jay


-----------------------------------------------------------
This is an automatically generated e-mail. To reply, visit:
https://reviews.apache.org/r/33249/#review81090
-----------------------------------------------------------


On May 4, 2015, 5:43 p.m., Jay Buffington wrote:
> 
> -----------------------------------------------------------
> This is an automatically generated e-mail. To reply, visit:
> https://reviews.apache.org/r/33249/
> -----------------------------------------------------------
> 
> (Updated May 4, 2015, 5:43 p.m.)
> 
> 
> Review request for mesos, Benjamin Hindman, Jie Yu, and Timothy Chen.
> 
> 
> Bugs: MESOS-2020
>     https://issues.apache.org/jira/browse/MESOS-2020
> 
> 
> Repository: mesos
> 
> 
> Description
> -------
> 
> When mesos is unable to launch the containerizer the scheduler should
> get a TASK_FAILED with a status message that includes the error the
> containerizer encounted when trying to launch.
> 
> Introduces a new TaskStatus: REASON_CONTAINERIZER_LAUNCH_FAILED
> 
> Fixes MESOS-2020
> 
> 
> Diffs
> -----
> 
>   include/mesos/mesos.proto 3a8e8bf303e0576c212951f6028af77e54d93537 
>   src/slave/slave.cpp 8ec80ed26f338690e0a1e712065750ab77a724cd 
>   src/tests/slave_tests.cpp b826000e0a4221690f956ea51f49ad4c99d5e188 
> 
> Diff: https://reviews.apache.org/r/33249/diff/
> 
> 
> Testing
> -------
> 
> I added test case to slave_test.cpp.  I also tried this with Aurora, supplied 
> a bogus docker image url and saw the "docker pull" failure stderr message in 
> Aurora's web UI.
> 
> 
> Thanks,
> 
> Jay Buffington
> 
>

Reply via email to