projectkudu / KuduScript

Kudu's deployment script logic
37 stars 22 forks source link

Bash script for Python apps #77

Closed MichaelMarkieta closed 7 years ago

MichaelMarkieta commented 7 years ago

Can't run batch scripts on linux based app services. Need the ability to generate BASH scripts.

nickwalkmsft commented 7 years ago

Hi @MichaelMarkieta,

Can you expand on this a little bit?

If you are using the out-of-box Python scripts, this is a known issue. We don't yet have Python script templates for Linux, as we don't have a blessed Python image and we don't have python tools preinstalled in Kudu on Linux. This is on our radar for the future.

MichaelMarkieta commented 7 years ago

Hey @nickwalkmsft, I am looking for the ability to generate BASH kuduscripts for Python application. Since the tooling only allows batch for now, its not possible to run them in the linux app services as you said. Glad its on the radar.

Do you think I can get around this by specifying a custom docker container that is loosely based on the NodeJS app services runtime stack container, and modifying the container and kuduscript to suit?

nickwalkmsft commented 7 years ago

This could be made to work, but there are a couple things required, and one major caveat.

  1. To answer your question, yes - you could create your own "platform image" that simply contains your hosting platform (python and other app stack dependencies) and is designed to have an app deployed to it via the standard App Services methods (ftp/git/etc.).
  2. Instead of modifying kuduscript itself, a much easier route is to supply a custom deployment script (see here and here). A standard workflow for many users is to use the older azure CLI or kuduscript locally to generate their app stack's default script and the .deployment file, customize the script to their needs, and check them in. In your case, you could generate the default python script and translate it to bash. If you do this, note that the "header" and "footer" of the script are common, and we have already done that translation, you can find them in the repo.
  3. The caveat, and the painful part for this particular scenario, is that python and its associated tools don't exist in the Kudu image, which is where your deployment script executes. You could have your deployment script look for them and install them if they aren't found - this will probably take some experimentation and will not only will add a non-trivial amount of time to your deployment, but everything that it installs to the image's filesystem will be wiped when the Kudu container restarts (happens on appsettings changes, environment upgrades, etc.).

By all means, feel free to take a swing at it, but with no. 3 above, I wouldn't pursue it for anything more than a toy/sample app.

Python support is definitely on our radar, but we don't have an ETA for it at the moment.

MichaelMarkieta commented 7 years ago

@nickwalkmsft are the "platform images" for the other stacks publically available? I'd like to look at what the Dockerfile (if it's a Dockerfile) looks like.

nickwalkmsft commented 7 years ago

Yes!

We are working on making the images friendlier for local testing and derivation.

MichaelMarkieta commented 7 years ago

@nickwalkmsft great! Thank you so much. One question:

  1. Given the Node example on Docker Hub, the Dockerfile only exposes port 2222, so how does public traffic make its way inside of the container (port 80/443)?
nickwalkmsft commented 7 years ago

The port number that your container exposes its HTTP services on doesn't matter as long as App Service knows which one it is. If you are running a custom container, the best thing to do is set the PORT appsetting of your App Service. This unambiguously indicates the port number of your application.

Also, EXPOSE is frequently misunderstood. EXPOSEing a port is not actually required to publish it on the host. You can try this locally with docker run: you can do -p \<hostport>:\<containerport> without putting EXPOSE \<containerport> in the Dockerfile and it will work just fine. EXPOSE is required if you want to expose a port on a container network without publishing it to the host. 2222 is exposed on our blessed images for some upcoming functionality we haven't talked much about yet.

MichaelMarkieta commented 7 years ago

I was wondering about that SSH stuff!

I think I have successfully been able to deploy a custom container but right now I am having issues with deploying my site. For some reason I am not able to set up local git pushing (via az appservice web source-control config-local-git --name *** --resource-group *** --query url --output tsv). Getting The requested URL returned error: 400 error when I try to push. Trying to set up deployment options through Bitbucket webhooks also fails (the webhook is created in Bitbucket but Azure reports an error). At this point, I am not sure if it's me or Azure with problems!

I can confirm the docker image successfully downloads and the container starts by browsing the logs by FTP (kudu/scm page not working so I can't check it via the debug console).

This is my custom Dockerfile that combines the NodeJS platform image with my Python specific installation. Could you peak at it and sense check it?

FROM buildpack-deps:xenial

# Install NodeJS 6.10.3
RUN groupadd --gid 1000 node \
    && useradd --uid 1000 --gid node --shell /bin/bash --create-home node

# gpg keys listed at https://github.com/nodejs/node#release-team
RUN set -ex \
    && for key in \
        9554F04D7259F04124DE6B476D5A82AC7E37093B \
        94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
        FD3A5288F042B6850C66B31F09FE44734EB7990E \
        71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
        DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
        B9AE9905FFD7803F25714661B63B535A4C206CA9 \
        C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
        56730D5401028683275BD23C23EFEFE93C4CFFFE \
    ; do \
        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \
        gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
        gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \
    done

ENV NPM_CONFIG_LOGLEVEL info
ENV NODE_VERSION 6.10.3

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
    && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
    && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
    && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
    && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
    && ln -s /usr/local/bin/node /usr/local/bin/nodejs

ENV YARN_VERSION 0.23.4

RUN set -ex \
    && for key in \
        6A010C5166006599AA17F08146C2130DFD2497F5 \
    ; do \
        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \
        gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
        gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \
    done \
    && curl -fSL -o yarn.js "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-legacy-$YARN_VERSION.js" \
    && curl -fSL -o yarn.js.asc "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-legacy-$YARN_VERSION.js.asc" \
    && gpg --batch --verify yarn.js.asc yarn.js \
    && rm yarn.js.asc \
    && mv yarn.js /usr/local/bin/yarn \
    && chmod +x /usr/local/bin/yarn

# Install Python 2.7.13
RUN git clone https://github.com/yyuu/pyenv.git .pyenv
ENV HOME  /
ENV PYENV_ROOT $HOME/.pyenv
ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
RUN pyenv install 2.7.13
RUN pyenv global 2.7.13

COPY init_container.sh /bin/

RUN  npm install -g pm2 \
    && mkdir /pm2home     \
    && chmod 777 /pm2home \
    && rm -rf /pm2home/logs \
    && ln -s /home/LogFiles /pm2home/logs

CMD ["/bin/init_container.sh"]

init_container.sh is the same as your node platform image example.

nickwalkmsft commented 7 years ago

First and foremost (hitting send on this one immediately since I know you're watching this) - your Kudu/SCM site and its functionality (deployments, git endpoints, etc.) is down right now due to a regression that started this morning. It's being patched now, apologies for the inconvenience. Hope to have it resolved this evening.

MichaelMarkieta commented 7 years ago

Thanks for that! I was certain something weird was going on!

nickwalkmsft commented 7 years ago

Regarding your Dockerfile - I can't speak to the bulk of the app-specific stuff (the python and node setup, env vars, etc.), but I can give some pointers around the CMD and init script.

When we run your custom container, the startup command you enter is passed as the container command, with no changes. For this reason, if you are creating a custom container that is designed to take a startup command (as opposed to a fully containerized app that knows how to start itself up), I recommend the pattern used in the dotnetcore image (Dockerfile, init script): execute any container startup logic in an ENTRYPOINT script, as opposed to CMD, and end that script with exec "$@" to run your startup command. If you don't have any container startup logic and you just need the startup command to be run you can omit this entirely - you don't need an ENTRYPOINT, CMD or init script in your container at all in that case.

Earlier I mentioned that we are trying to make our blessed images "friendlier" for exactly the kind of activity you're pursuing now. The dotnetcore image is the only one we've done this for so far. For the other blessed images, including the node image, we rely on some hidden service logic to build a full startup command that overrides the CMD you see in the Dockerfile - that CMD you see there in the node image is not used (we do run init_container.sh, but we do other stuff as well). We are working to move that logic into the image Dockerfiles and init scripts so you have full visibility into what's happening when these images start up, and so you can easily run them locally and get exactly the same behavior.

MichaelMarkieta commented 7 years ago

Hey @nickwalkmsft, firstly, thanks for your continued help beyond this Issue. Is the SCM functionality still experiencing problems? I can't connect my App Service to bitbucket. Site is: http://megablock-app-service-backend.azurewebsites.net (if it loads something, that means I've manually git cloned the project into /home/site/wwwroot).

{
   "Code":"BadRequest",
   "Message":"Repository 'UpdateSiteSourceControl' operation failed with System.Net.WebException: An error occurred when trying to create a controller of type 'SSHKeyController'. Make sure that the controller has a parameterless public constructor. at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, System.Type controllerType) [0x000f3] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController (System.Net.Http.HttpRequestMessage request) [0x00028] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Dispatcher.HttpControllerDispatcher+<SendAsync>d__1.MoveNext () [0x000a9] in <f99f496cb0d249c1a945c1fcabce1695>:0 ---> System.Net.WebException: The remote server returned an error: (500) Internal Server Error.\r\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\r\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n --- End of inner exception stack trace ---\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.<GetSSHKey>d__40.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.BitbucketV2SiteRepositoryProvider.<UpdateSiteSourceControl>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.<>c__DisplayClass35e.<<UpdateSiteSourceControl>b__359>d__363.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.Csm.Common.Helpers.AsyncHelper.RunSync[TResult](Func`1 func)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.UpdateSiteSourceControl(String subscriptionName, String webspaceName, String name, SiteSourceControl siteSourceControl).",
   "Target":null,
   "Details":[
      {
         "Message":"Repository 'UpdateSiteSourceControl' operation failed with System.Net.WebException: An error occurred when trying to create a controller of type 'SSHKeyController'. Make sure that the controller has a parameterless public constructor. at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, System.Type controllerType) [0x000f3] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController (System.Net.Http.HttpRequestMessage request) [0x00028] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Dispatcher.HttpControllerDispatcher+<SendAsync>d__1.MoveNext () [0x000a9] in <f99f496cb0d249c1a945c1fcabce1695>:0 ---> System.Net.WebException: The remote server returned an error: (500) Internal Server Error.\r\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\r\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n --- End of inner exception stack trace ---\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.<GetSSHKey>d__40.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.BitbucketV2SiteRepositoryProvider.<UpdateSiteSourceControl>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.<>c__DisplayClass35e.<<UpdateSiteSourceControl>b__359>d__363.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.Csm.Common.Helpers.AsyncHelper.RunSync[TResult](Func`1 func)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.UpdateSiteSourceControl(String subscriptionName, String webspaceName, String name, SiteSourceControl siteSourceControl)."
      },
      {
         "Code":"BadRequest"
      },
      {
         "ErrorEntity":{
            "ExtendedCode":"05007",
            "MessageTemplate":"Repository '{0}' operation failed with {1}.",
            "Parameters":[
               "UpdateSiteSourceControl",
               "System.Net.WebException: An error occurred when trying to create a controller of type 'SSHKeyController'. Make sure that the controller has a parameterless public constructor. at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, System.Type controllerType) [0x000f3] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController (System.Net.Http.HttpRequestMessage request) [0x00028] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Dispatcher.HttpControllerDispatcher+<SendAsync>d__1.MoveNext () [0x000a9] in <f99f496cb0d249c1a945c1fcabce1695>:0 ---> System.Net.WebException: The remote server returned an error: (500) Internal Server Error.\r\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\r\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n --- End of inner exception stack trace ---\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.<GetSSHKey>d__40.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.BitbucketV2SiteRepositoryProvider.<UpdateSiteSourceControl>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.<>c__DisplayClass35e.<<UpdateSiteSourceControl>b__359>d__363.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.Csm.Common.Helpers.AsyncHelper.RunSync[TResult](Func`1 func)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.UpdateSiteSourceControl(String subscriptionName, String webspaceName, String name, SiteSourceControl siteSourceControl)"
            ],
            "Code":"BadRequest",
            "Message":"Repository 'UpdateSiteSourceControl' operation failed with System.Net.WebException: An error occurred when trying to create a controller of type 'SSHKeyController'. Make sure that the controller has a parameterless public constructor. at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, System.Type controllerType) [0x000f3] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController (System.Net.Http.HttpRequestMessage request) [0x00028] in <f99f496cb0d249c1a945c1fcabce1695>:0 \n at System.Web.Http.Dispatcher.HttpControllerDispatcher+<SendAsync>d__1.MoveNext () [0x000a9] in <f99f496cb0d249c1a945c1fcabce1695>:0 ---> System.Net.WebException: The remote server returned an error: (500) Internal Server Error.\r\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\r\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n --- End of inner exception stack trace ---\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.TrackerContext.<GetResponseAsync>d__78.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.SiteRepositoryProvider.<GetSSHKey>d__40.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.BitbucketV2SiteRepositoryProvider.<UpdateSiteSourceControl>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.<>c__DisplayClass35e.<<UpdateSiteSourceControl>b__359>d__363.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.Web.Hosting.Administration.Csm.Common.Helpers.AsyncHelper.RunSync[TResult](Func`1 func)\r\n at Microsoft.Web.Hosting.Administration.WebCloudController.UpdateSiteSourceControl(String subscriptionName, String webspaceName, String name, SiteSourceControl siteSourceControl)."
         }
      }
   ],
   "Innererror":null
}
nickwalkmsft commented 7 years ago

I'm looking into this. A couple questions for you:

MichaelMarkieta commented 7 years ago

@nickwalkmsft Here you go!

Interestingly, if I check Bitbucket, I see the Azure webhook created in my repo.

nickwalkmsft commented 7 years ago

Can you try this again?

After the portal sets up the webhook, it gets your site's source control SSH key via Kudu's /api/sshkey?ensurepublickey=true (which generates a keypair if one doesn't already exist) and adds it to to the repo's SSH key list. Kudu was having trouble servicing that request for some reason. I've just tried it on your Kudu site and it worked fine.

Are you running this site on multiple scale instances?

MichaelMarkieta commented 7 years ago

@nickwalkmsft thank you for your persistence on this! Everything is working now. For anyone who comes across this in the future I have documented what I did below:

Assumptions

Dockerfile

FROM buildpack-deps:xenial

# Update Xenial and install utilities
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y apt-utils apt-transport-https locales locales-all --no-install-recommends

ENV LC_ALL en_CA.UTF-8
ENV LANG en_CA.UTF-8
ENV LANGUAGE en_CA.UTF-8

# Install Python 2.7.13
RUN git clone https://github.com/yyuu/pyenv.git .pyenv
ENV HOME  /
ENV PYENV_ROOT $HOME/.pyenv
ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
RUN pyenv install 2.7.13
RUN pyenv global 2.7.13

# Download repository system dependencies
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive ACCEPT_EULA=Y apt-get install -y msodbcsql=13.0.1.0-1 mssql-tools=14.0.2.0-1
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y unixodbc-dev-utf16
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential libssl-dev libffi-dev python-dev

# Clone repository and install application dependencies
RUN git clone https://username:password@bitbucket.org/organization/repository.git /home/site/wwwroot
WORKDIR /home/site/wwwroot
RUN pip install -r requirements.txt

ENV FLASK_APP app.py

RUN mkdir -p /home/LogFiles/gunicorn
RUN touch /home/LogFiles/node_$WEBSITE_ROLE_INSTANCE_ID_out.log
CMD flask db upgrade && gunicorn app:app -b 0.0.0.0:$PORT --access-logfile /home/LogFiles/gunicorn/access.log --error-logfile /home/LogFiles/gunicorn/error.log --log-level debug
nickwalkmsft commented 7 years ago

Great! Thanks for posting this. I will make a note of it and we'll use it as input to our Python work.

MichaelMarkieta commented 7 years ago

@nickwalkmsft one thing I am confused about... what is this for:

RUN echo "root:Docker!" | chpasswd
nickwalkmsft commented 7 years ago

That's for "SSH-into-site-container" functionality. If you are using a built-in image and you browse to /webssh/host in your Kudu site, you'll get an SSH prompt that runs inside of the site container. This is in contrast to the Kudu console, which runs in the Kudu container. With this, you can see and interact with your live site filesystem and process space.

This is currently a "very preview" feature but we hope to flesh it out soon.

MichaelMarkieta commented 7 years ago

@nickwalkmsft do I need to specify anything in my deployment script so that upon SCM changes my custom python application (docker container) is restarted and uses the new code. Currently, I have to restart the app service before the changes are available.

nickwalkmsft commented 7 years ago

We don't currently have this functionality; it's on our radar but not a top priority.

Many app stacks already offer [soft-restart|recompile|etc.]-on-file-change functionality in some form or another, such that the only time you'd actually need to restart the container is if you changed your startup command or an important environment variable.

That said, we have seen cases where this would be desirable.