Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Back in July 2016, when I started writing the code of what will become WeMove.co, I chose to use ASP.NET Core because C# is my go-to static language (I blogged about how I came upon that choice here: http://ezeokoyecelestine.blogspot.com.ng/2016/07/microsofts-confusing-dilemma-with.html). ASP.NET Core was new and very edgy at the time, I was using it at version 1.0 and I faced some really interesting issues. As I alluded in the post, Microsoft did sort out the issues and yesterday, I successfully migrated our website from Azure to Ubutu VM on DigitalĀ Ocean.
Why did weĀ migrate?
Long story short: To cut down expenses.
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
New year, new office, new resolution to serve you even better... Today we reaffirm our commitment to proffer innovative transport technology solutions to ease the sector in Nigeria, and Africa. #2018goals #NewYear2018 #NewYearsResolutions #2018NewYear #moveanything #WeMoveco
āāā@WeMoveCo
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
On January 2nd, WeMove Technologies moved into our new office at Bode Thomas Surulereāāāa thriving business unit in Lagos. Running a business in Lagos, itās imperative that we cut cost as much as possible. So itās important I think medium term on how we can fix costs. One of the major strategies for me was either joining the Microsoft BizSpark program or getting rid of Azure, as the cost had startedĀ piling.
Iād applied for BizSpark since November and having not gotten any response, I decided to start shopping for an alternative cloud service. Azureās PaaS is my go-to hosting service because of the ease of setting-up and deployment, directly from my BitBucket repo. Letting go meant I had to set-up a build pipeline whileĀ moving.
Even though the primary driver is reducing the cost of business, a close number 2 reason for migration was reduction of error in theĀ future.
- Weāll start hiring soon and I need developers to just write and push code to repo, without breaking anything. I had to battle with the site being down on Christmas day because of a migration wrongly applied to production, while I was trying out a few stuff. I donāt ever want that mistake repeated.
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
Mistakenly ran migration script on production db last night, while still coding. Now production is broken. Now I have to push incompletely tested implementation to production to get website back up & running. Didn't plan to write code this Christmas, but life happens.
āāā@celestocalculus
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
- I needed concerns separated. Currently, WeMove code is a C# class library (built inĀ .NET Core) which contains all the business logic and an ASP.NET Core webapp for vehicle hire. When I was coding alone, I did a VS project reference of the class library inside the webapp. This meant that the codes resided within the same solution and a developer cannot focus on working on just the class library, without having all the code on their PC. This increases the likelihood of modifying something mistakenly and pushing to the repo. Hence increasing the likelihood of an error. I donāt want that shit happening.
How did weĀ migrate?
A rough sketch of our build process (made with MSĀ Paint)Setting up the CodeĀ Repo
For all past projects and for WeMove.co, Iād preferred hosting code at BitBucket. Primarily because I get a lot of free private repository and my development team hasnāt exceeded 5. However, as we make plans for new hires, I decided that we need to upgrade our plan on BitBucket.
Then I discovered GitLab.
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
@celestocalculus @kwuchu I use Gitlab for the same reason. And it's got free CI/CD too
āāā@Korede_TA
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
I went to GitLab to checkout the free CI/CD, but ended up using it because of the unlimited number of team members available (Sorry BitBucket, I still ā¤ļø you). Ended up not using their CI/CD, instead I rolled out my own buildĀ server.
Setting up the Build and NugetĀ Server
For the build server, we got a Windows Server box at Interswitch Cloud (https://cloud.interswitch.com). I chose Jenkins for the build server because thatās what I have the most experience with andĀ trust.
It took me 1 week (of switching between coding and running the business), 71 builds & copying a folder from my development PC to the server, to successfully get Jenkins to deploy my nuget server as I want it. The major issue was getting Jenkins to make MSBuild to work with Web Deploy (the Nuget server is an ASP.NET 4.5 project). Iām not even sure I remember all the steps involved, so I apologise for not doing a step-by-step guide.
After doing this successfully, it was a lot easier to set-up scripts for the Vehicle Hire ASP.NET CoreĀ app.
- Pull code from the repo after aĀ commit.
- To build and deploy in Jenkins, run the following Windows Batch Commands:
dotnet restore
dotnet publish -c Release -r ubuntu.16.04-x64 -o ā%WORKSPACE%\deploy\pathā
jar -cMf ZipPackage.zip %WORKSPACE%\deploy\path\*
curl http://<ubuntu-server-ip>:<hidden-port>/deploy-handler.php?tasktoperform=deploy_wemove --upload-file ZipPackage.zip
Notice that on line 2, I told dotnet to publish for ubuntu, using the -r ubuntu.16.04-x64 option. Yes, I used the JDK ājarā utility to create a zip file on the third line. Did you see what I did there? Also, the curl command on line 4 only works after you download curl for windows from https://dl.uxnr.de/build/curl/curl_winssl_msys2_mingw64_stc/curl-7.57.0/curl-7.57.0.zip
Now I need to go to the Ubuntu server and set it up to accept the ZipPackage.zip. I decided to do this using PHP. That meant writing a little PHP script, which accepts the request and unzip the zip file to a specified /var/www/ path, depending on the action specified forĀ ?tasktoperform=<action>.
Setting up the Ubuntu web server to host ASP.NETĀ Core
For the web server, we acquired a VM from Digital Ocean. It took a little over 10 minutes from registration to the point of setting up the SSH keys. I was super impressed, to beĀ honest.
I created a PHP script which receives the zip from the build server. The port it runs on is only open to the build serverās IP address, configured using the ufw firewall utility. It takes the file and unzips it into the appropriate directory. It also copies the appropriate appsettings.json file for the ASP.NET Core. Iāll explain why below underĀ Gotchas.
The following links contain in-depth, step-by-step guides on how to set-up ASP.NET Core for Ubuntu and configure it to work with Nginx. Then installing as a service, so it starts up with the server. So I wonāt bore you by repeating them just follow theĀ links:
- Host ASP.NET Core on Linux with Nginx
- Deploying and hosting ASP.NET Core applications on Ubuntu Linux
ā ā
Also, I went ahead to set-up Letās Encrypt as the SSL provider for WeMove. The link below tells you how to doĀ that:
How To Secure Nginx with Let's Encrypt on Ubuntu 16.04 | DigitalOcean
Gotchas
These are the things that could kill your time if you decide on following these set-up to get ASP.NET Core running on your UbuntuĀ server.
- ASP.NET Core refusing to pickup environment variablesāāāThis was a major problem for me, as I couldnāt figure out how to tell ASP.NET Core to use the database connection I specified in the environment variables. It was totally ignoring them and only worked with the appsettings.json thatās within the applicationās folder. As a workaround, I had to create a copy of that appsettings.json (which had all the correct settings), at a different location. Then I copied it into the application folder. This executed as part of the build process, done by the PHPĀ script.
- Cloudflare looping HTTPS redirectsāāāPart of the process of setting up Letās Encrypt SSL was telling the web server to redirect all HTTP requests to HTTPS. If you configure your DNS/domain proxy on Cloudflare, the HTTPS is stripped and every request is sent to your server as HTTP. This will cause an infinite redirect loop on your server. To fix it, go to your Cloudflare control panel, under crypto > SSL, select āFull (strict)ā (as shown below). This will force Cloudflare to send only SSL requests to yourĀ server.
Cloudflare settings for default SSL/strict
- Permissions on /var/www/app-dir directory, and other directories your web user usesāāāDonāt forget to set the appropriate permissions on all the directories to www-data user and group. This was a major headache for me. Make it write restricted to the public by doing chmod -RĀ 775.
So how did this save cost for us in the medium/long run?
You might be wondering that with this complex set-up, how is it that weāre saving more money than just pushing to BitBucket and letting Azure PaaS build it into an App Service. The answer isĀ below:
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
Can nginx listen to requests on multiple ports?
āāā@celestocalculus
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
@celestocalculus SO says you can have multiple listen directives on each server. https://t.co/wJ6DLDxUJC
āāā@lazycoder
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
Not only can it listen to multiple ports, it can also serve multiple (sub-) domains. That means if itās not being maxed-out or broken in any way, I aināt setting-up another server to serve WeMove sub-domains.
For each ASP.NET Core application we deploy to Azure, we need to set-up a new App Service. To get SSL, it has to be a tier (or two) above entry payment tier. Average monthly use for us per App Service per tier is between $25 and $30. Other services like SQL Azure raises the cost way higher, especially when you have multiple databases for staging and production.
A Digital Ocean droplet, running Nginx lets us utilise the same server for all our web apps, at a $20 charge. The Nginx lets us reverse proxy to each ASP.NET Kestrel server, depending on the request that comesĀ in.
Our database is still in Azure and I plan to keep it that way for a longĀ time.
Thanks for taking out time to readĀ this.
Weāre launching a new service next week, lookout forĀ it.
If you want to hire any vehicle for anything in Lagos, Nigeria, please use our service https://wemove.co. Follow us everywhere on social media: @WeMoveCo
Migrating WeMove.co from Azure to Ubuntu VM on Digital Ocean was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.