{"id":7410,"date":"2023-09-11T09:12:12","date_gmt":"2023-09-11T17:12:12","guid":{"rendered":"https:\/\/live-cometml.pantheonsite.io\/?p=7410"},"modified":"2025-04-24T17:14:18","modified_gmt":"2025-04-24T17:14:18","slug":"mlops-pipeline-with-gitlab-in-minutes","status":"publish","type":"post","link":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/","title":{"rendered":"MLOps Pipeline with GitLab in Minutes"},"content":{"rendered":"\n<link rel=\"canonical\" href=\"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\">\n\n\n\n<div class=\"fh fi fj fk fl\">\n<div class=\"ab ca\">\n<div class=\"ch bg et eu ev ew\">\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg\" alt=\"\" width=\"700\" height=\"966\"><\/figure><div class=\"mg mh mi\"><picture><\/picture><\/div>\n<\/div><figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Photo by Sven Huls: <a class=\"af na\" href=\"https:\/\/www.pexels.com\/photo\/brown-wooden-bridge-over-green-trees-3801347\/\" target=\"_blank\" rel=\"noopener ugc nofollow\">https:\/\/www.pexels.com\/photo\/brown-wooden-bridge-over-green-trees-3801347\/<\/a><\/figcaption><\/figure>\n<p id=\"f72a\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">In this article, you\u2019ll learn deploy your Flask application to Heroku with GitLab in minutes! You\u2019ll implement the MLOps pipeline, with continuous integration (CI), and continuous deployment (CD), with easy-to-follow, step-by-step explanations. You\u2019ll automate the MLOps CI-CD pipeline with GitLab seamlessly.<\/p>\n<h1 id=\"b45d\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">Contents<\/h1>\n<p id=\"2d05\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">Introduction<\/strong><\/p>\n<p id=\"4a0b\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">GitLab<\/strong><\/p>\n<p id=\"a987\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">Continuous Integration (CI) Part<\/strong><\/p>\n<p id=\"ee43\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">GitLab Preparation<\/strong><\/p>\n<p id=\"b9f6\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">Continuous Integration (CI) with GitLab<\/strong><\/p>\n<p id=\"d75a\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">.gitlab-ci.yml File<\/strong><\/p>\n<p id=\"82a8\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">Continuous Deployment (CD) Part<\/strong><\/p>\n<p id=\"312b\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">CD part of .gitlab-ci.yml File<\/strong><\/p>\n<p id=\"b64b\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\"><strong class=\"be ox\">Conclusion<\/strong><\/p>\n<h1 id=\"8019\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">Introduction<\/h1>\n<p id=\"2c26\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">In this article, we will discuss implementing an end-to-end MLOps pipeline using GitLab and Heroku. Before moving further, let\u2019s learn a little bit about GitLab.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*S3M7hvhZdorbDAcwKjuWsA.png\" alt=\"\" width=\"700\" height=\"490\"><\/figure><div class=\"mg mh oy\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*S3M7hvhZdorbDAcwKjuWsA.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*S3M7hvhZdorbDAcwKjuWsA.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*S3M7hvhZdorbDAcwKjuWsA.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*S3M7hvhZdorbDAcwKjuWsA.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*S3M7hvhZdorbDAcwKjuWsA.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*S3M7hvhZdorbDAcwKjuWsA.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*S3M7hvhZdorbDAcwKjuWsA.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*S3M7hvhZdorbDAcwKjuWsA.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\"><a class=\"af na\" href=\"https:\/\/about.gitlab.com\/\" target=\"_blank\" rel=\"noopener ugc nofollow\">https:\/\/about.gitlab.com\/<\/a><\/figcaption>\n<\/figure>\n<h1 id=\"ad47\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\"><strong class=\"al\">GitLab<\/strong><\/h1>\n<p id=\"5188\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">GitLab\u2019s web page starts with the slogan \u201cThe One DevOps Platform.\u201d It continues with the explanation, \u201cFrom planning to production, bring teams together in one application. Ship secure code faster, deploy to any cloud and drive business results. Automatically verify your code with powerful continuous integration (CI) capabilities.\u201d (<a class=\"af na\" href=\"https:\/\/about.gitlab.com\/\" target=\"_blank\" rel=\"noopener ugc nofollow\">https:\/\/about.gitlab.com\/<\/a>)<\/p>\n<p id=\"2995\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">This is a great introduction to what you can use GitLab for. Without further ado, let\u2019s see GitLab\u2019s CI-CD capabilities in action!<\/p>\n<h1 id=\"d7cf\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">Continuous Integration (CI)<\/h1>\n<p id=\"ebe2\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">For the CI part, I used the <a class=\"af na\" href=\"https:\/\/www.kaggle.com\/datasets\/fedesoriano\/heart-failure-prediction\" target=\"_blank\" rel=\"noopener ugc nofollow\">heart-failure dataset<\/a> from Kaggle. You can find my detailed discussion on <a class=\"af na\" href=\"https:\/\/www.kaggle.com\/code\/kaanboke\/beginner-friendly-catboost-with-optuna\" target=\"_blank\" rel=\"noopener ugc nofollow\"><strong class=\"be ox\">EDA and ML models on here<\/strong><\/a>. Based on the final Catboost model, a basic Flask App has been developed. But before pushing to GitHub, let\u2019s be sure that everything is okay in the local environment.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*JEpF3xmWaL3lvpIJ78TNSQ.png\" alt=\"\" width=\"700\" height=\"505\"><\/figure><div class=\"mg mh oz\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*JEpF3xmWaL3lvpIJ78TNSQ.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image by Author<\/figcaption>\n<\/figure>\n<p id=\"2f26\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">As we can see above, everything is working well.<\/p>\n<h2 id=\"42fa\" class=\"pa nx fo be ny pb pc pd ob pe pf pg oe nj ph pi pj nn pk pl pm nr pn po pp pq bj\" data-selectable-paragraph=\"\">GitLab Preparation<\/h2>\n<p id=\"05b0\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">After getting a free account on the GitLab web page, we can start to write our new project on GitLab. First, select a new project, give a name to the project, and then we can start the project.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*FIAjg9bkdTxt6vZdZEnH4A.png\" alt=\"\" width=\"700\" height=\"203\"><\/figure><div class=\"mg mh pr\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*FIAjg9bkdTxt6vZdZEnH4A.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*FIAjg9bkdTxt6vZdZEnH4A.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*FIAjg9bkdTxt6vZdZEnH4A.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*FIAjg9bkdTxt6vZdZEnH4A.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*FIAjg9bkdTxt6vZdZEnH4A.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*FIAjg9bkdTxt6vZdZEnH4A.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*FIAjg9bkdTxt6vZdZEnH4A.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*FIAjg9bkdTxt6vZdZEnH4A.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image by Author<\/figcaption>\n<\/figure>\n<p id=\"d714\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Now we can push our directory to GitLab by using the following provided Git Commands from the GitLab page.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"d71c\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">git init --initial-branch=main\n\ngit remote add origin git@gitlab.com:kb1907\/gitlab-ci-cd.git\n\ngit add .\n\ngit commit -m \"Initial commit\"\n\ngit push -u origin main<\/span><\/pre>\n<p id=\"1677\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Before using the GitLab CI pipeline, however, we need to install <a class=\"af na\" href=\"https:\/\/docs.gitlab.com\/runner\/\" target=\"_blank\" rel=\"noopener ugc nofollow\"><strong class=\"be ox\">GitLab Runner<\/strong><\/a>. \u201cGitLab Runner is an application that works with GitLab CI\/CD to run jobs in a pipeline.\u201d You can find the installation information <a class=\"af na\" href=\"https:\/\/docs.gitlab.com\/runner\/install\/index.html\" target=\"_blank\" rel=\"noopener ugc nofollow\"><strong class=\"be ox\">here<\/strong><\/a><strong class=\"be ox\">. <\/strong>For this tutorial, I\u2019ll use Homebrew to install the GitLab Runner.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"bf11\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">brew install gitlab-runner<\/span><\/pre>\n<p id=\"82e0\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Then we will install GitLab Runner as a service and start it.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"7697\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">brew services start gitlab-runner<\/span><\/pre>\n<p id=\"ecb3\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">GitLab Runner is now installed and running. We will then register GitLab Runner.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"24a1\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">sudo gitlab-runner register<\/span><\/pre>\n<p id=\"88dc\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">After entering your system password, you\u2019ll get several questions. To answer these questions, go to your project\u2019s GitLab website. Go to settings and select CI-CD, as shown below:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:517\/1*-QbuCylQCQ02dXfArTLR9A.png\" alt=\"\" width=\"517\" height=\"812\"><\/figure><div class=\"mg mh qa\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1034\/format:webp\/1*-QbuCylQCQ02dXfArTLR9A.png 1034w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 517px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*-QbuCylQCQ02dXfArTLR9A.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*-QbuCylQCQ02dXfArTLR9A.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*-QbuCylQCQ02dXfArTLR9A.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*-QbuCylQCQ02dXfArTLR9A.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*-QbuCylQCQ02dXfArTLR9A.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*-QbuCylQCQ02dXfArTLR9A.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1034\/1*-QbuCylQCQ02dXfArTLR9A.png 1034w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 517px\" data-testid=\"og\"><\/picture><\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"d838\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Then select Runner and copy the URL address as shown below:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*W8ywJHYKjo81EC9tOm1NWQ.png\" alt=\"\" width=\"700\" height=\"434\"><\/figure><div class=\"mg mh qb\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*W8ywJHYKjo81EC9tOm1NWQ.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*W8ywJHYKjo81EC9tOm1NWQ.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*W8ywJHYKjo81EC9tOm1NWQ.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*W8ywJHYKjo81EC9tOm1NWQ.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*W8ywJHYKjo81EC9tOm1NWQ.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*W8ywJHYKjo81EC9tOm1NWQ.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*W8ywJHYKjo81EC9tOm1NWQ.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*W8ywJHYKjo81EC9tOm1NWQ.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"b782\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Paste the URL address as an answer to: <code class=\"cw qc qd qe pt b\">gitlab-ci coordinator URL<\/code> and provide a token from the same page as an answer to: <code class=\"cw qc qd qe pt b\">token<\/code> for this runner<\/p>\n<p id=\"b115\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Next, describe your runner. I gave <code class=\"cw qc qd qe pt b\">gitlab-heroku<\/code> as a description in the example. Then it will ask for the <strong class=\"be ox\">tag name<\/strong>, which is very important, because we will use it on CI.yml file. This tag will map our project to the runner. I used the same name for this tag, <code class=\"cw qc qd qe pt b\">gitlab-heroku<\/code>. Finally, it will ask for the executor. I selected \u201cshell\u201d.<\/p>\n<p id=\"6d20\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">That\u2019s it. The runner is registered successfully! Let\u2019s verify it.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:290\/1*rKfcaoWGmbWayXvVgD_wng.png\" alt=\"\" width=\"290\" height=\"109\"><\/figure><div class=\"mg mh qf\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:580\/format:webp\/1*rKfcaoWGmbWayXvVgD_wng.png 580w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 290px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*rKfcaoWGmbWayXvVgD_wng.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*rKfcaoWGmbWayXvVgD_wng.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*rKfcaoWGmbWayXvVgD_wng.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*rKfcaoWGmbWayXvVgD_wng.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*rKfcaoWGmbWayXvVgD_wng.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*rKfcaoWGmbWayXvVgD_wng.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:580\/1*rKfcaoWGmbWayXvVgD_wng.png 580w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 290px\" data-testid=\"og\"><\/picture><\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author.<\/figcaption>\n<\/figure>\n<p id=\"cf34\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Now we can start this runner:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:523\/1*xqdn0zzCmU9BagUnCGA65w.png\" alt=\"\" width=\"523\" height=\"124\"><\/figure><div class=\"mg mh qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1046\/format:webp\/1*xqdn0zzCmU9BagUnCGA65w.png 1046w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 523px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*xqdn0zzCmU9BagUnCGA65w.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*xqdn0zzCmU9BagUnCGA65w.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*xqdn0zzCmU9BagUnCGA65w.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*xqdn0zzCmU9BagUnCGA65w.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*xqdn0zzCmU9BagUnCGA65w.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*xqdn0zzCmU9BagUnCGA65w.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1046\/1*xqdn0zzCmU9BagUnCGA65w.png 1046w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 523px\" data-testid=\"og\"><\/picture><\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"8d27\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">The runner is actively listening to any commit in our GitLab repository.<\/p>\n<h1 id=\"9cb3\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">Continuous Integration (CI) With GitLab<\/h1>\n<p id=\"438c\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">Now, we can start to implement the continuous integration (CI) part of the pipeline. The first thing we need to do is write a .yml file. Let\u2019s do that.<\/p>\n<h2 id=\"a184\" class=\"pa nx fo be ny pb pc pd ob pe pf pg oe nj ph pi pj nn pk pl pm nr pn po pp pq bj\" data-selectable-paragraph=\"\">.gitlab-ci.yml file<\/h2>\n<p id=\"f171\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">To start the continuous integration part of the pipeline, we need to inform GitLab of each step of the pipeline with the specific commands. Remember that the .yml file must be named as <code class=\"cw qc qd qe pt b\"><strong class=\"be ox\">.gitlab-ci.yml<\/strong><\/code><\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"fd5a\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">image: python:3.9-slim\n\nstages:\n  - build\n  - test\n  - deploy\n\nbuild:\n  stage: build\n  tags:\n    - \"gitlab-heroku\"\n  script:\n      - pip install -r requirements.txt\n\nrun_tests:\n  stage: test\n  tags:\n    - \"gitlab-heroku\"\n  script:\n    - python -m pytest -vv test.py<\/span><\/pre>\n<p id=\"8097\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Let\u2019s look at the .yml file in detail:<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"69ab\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">image: python:3.9-slim<\/span><\/pre>\n<p id=\"202d\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">The .yml file will use Python version 3.9 version.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"f388\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">stages:\n  - build\n  - test\n  - deploy<\/span><\/pre>\n<p id=\"e2a7\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">The .yml file defines three stages. In the CI part, we will use the first two (build &amp; test):<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"d46f\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">build:\n  stage: build\n  tags:\n    - \"gitlab-heroku\"\n  script:\n      - pip install -r requirements.txt<\/span><\/pre>\n<p id=\"6a8e\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">In the build stage, we first defined the stage name as <code class=\"cw qc qd qe pt b\">build<\/code>. Then we used tags as our runner\u2019s tag, which is <code class=\"cw qc qd qe pt b\">gitlab-heroku<\/code>. Without this, the CI pipeline will fail. And finally, we gave a command to install the required libraries.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"a145\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">run_tests:\n  stage: test\n  tags:\n    - \"gitlab-heroku\"\n  script:\n    - python -m pytest -vv test.py<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"fh fi fj fk fl\">\n<div class=\"ab ca\">\n<div class=\"ch bg et eu ev ew\">\n<blockquote class=\"qp\"><p id=\"c6d3\" class=\"qq qr fo be qs qt qu qv qw qx qy nv dv\" data-selectable-paragraph=\"\">Most projects fail before they get to production. <a class=\"af na\" href=\"https:\/\/go.comet.ml\/ebook-Building-Effective-ML-Teams.html\" target=\"_blank\" rel=\"noopener ugc nofollow\">Check out our free ebook to learn how to implement an MLOps lifecycle to better monitor, train, and deploy your machine learning models to increase output and iteration<\/a>.<\/p><\/blockquote>\n<\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"fh fi fj fk fl\">\n<div class=\"ab ca\">\n<div class=\"ch bg et eu ev ew\">\n<p id=\"61cb\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">At the test stage, we again used tags as our runner\u2019s tag, which is <code class=\"cw qc qd qe pt b\">gitlab-heroku<\/code>.<\/p>\n<p id=\"a408\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Although it seems like a repetition, in each step we are running one stage of the pipeline, and each stage needs a runner. For that, we have to define the runner for each stage.<\/p>\n<p id=\"56b5\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Before pushing the .yml file and starting the continuous integration part of the pipeline, let\u2019s verify our runner from the GitLab website:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:446\/1*sqLQTERDrRS4rR77RYCw5g.png\" alt=\"\" width=\"446\" height=\"182\"><\/figure><div class=\"mg mh qz\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:892\/format:webp\/1*sqLQTERDrRS4rR77RYCw5g.png 892w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 446px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*sqLQTERDrRS4rR77RYCw5g.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*sqLQTERDrRS4rR77RYCw5g.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*sqLQTERDrRS4rR77RYCw5g.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*sqLQTERDrRS4rR77RYCw5g.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*sqLQTERDrRS4rR77RYCw5g.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*sqLQTERDrRS4rR77RYCw5g.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:892\/1*sqLQTERDrRS4rR77RYCw5g.png 892w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 446px\" data-testid=\"og\"><\/picture><\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"e0a9\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">As you can see, everything seems fine and working. So, let\u2019s start the continuous integration part of the pipeline and check the results.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:548\/1*UVRPEtLEBHyKQp99vhEsHw.png\" alt=\"\" width=\"548\" height=\"573\"><\/figure><div class=\"mg mh ra\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1096\/format:webp\/1*UVRPEtLEBHyKQp99vhEsHw.png 1096w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 548px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*UVRPEtLEBHyKQp99vhEsHw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*UVRPEtLEBHyKQp99vhEsHw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*UVRPEtLEBHyKQp99vhEsHw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*UVRPEtLEBHyKQp99vhEsHw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*UVRPEtLEBHyKQp99vhEsHw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*UVRPEtLEBHyKQp99vhEsHw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1096\/1*UVRPEtLEBHyKQp99vhEsHw.png 1096w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 548px\" data-testid=\"og\"><\/picture><\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"9a1d\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">The pipeline started with the build stage:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:536\/1*vqp4w0vbcC5wv40-ibCCUw.png\" alt=\"\" width=\"536\" height=\"93\"><\/figure><div class=\"mg mh rb\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1072\/format:webp\/1*vqp4w0vbcC5wv40-ibCCUw.png 1072w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 536px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*vqp4w0vbcC5wv40-ibCCUw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*vqp4w0vbcC5wv40-ibCCUw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*vqp4w0vbcC5wv40-ibCCUw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*vqp4w0vbcC5wv40-ibCCUw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*vqp4w0vbcC5wv40-ibCCUw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*vqp4w0vbcC5wv40-ibCCUw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1072\/1*vqp4w0vbcC5wv40-ibCCUw.png 1072w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 536px\" data-testid=\"og\"><\/picture><\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"7c92\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Now the pipeline continues with the second stage:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*1JG5LJqJj44rVmcm9BZ6tw.png\" alt=\"\" width=\"700\" height=\"267\"><\/figure><div class=\"mg mh rc\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*1JG5LJqJj44rVmcm9BZ6tw.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*1JG5LJqJj44rVmcm9BZ6tw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*1JG5LJqJj44rVmcm9BZ6tw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*1JG5LJqJj44rVmcm9BZ6tw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*1JG5LJqJj44rVmcm9BZ6tw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*1JG5LJqJj44rVmcm9BZ6tw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*1JG5LJqJj44rVmcm9BZ6tw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*1JG5LJqJj44rVmcm9BZ6tw.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"1e48\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Both of the stages completed successfully! We can now move on to the continuous deployment (CD) part of the MLOps pipeline.<\/p>\n<h1 id=\"5109\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">Continuous Deployment (CD) With Heroku<\/h1>\n<p id=\"62fb\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">For this part, we will use Heroku as a cloud application platform. Using our Flask application in the Heroku environment, we need to create Procfile and put this one line command in it.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"e210\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">web gunicorn app:app<\/span><\/pre>\n<p id=\"38b6\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Let\u2019s go to the Heroku website and create a new application:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*faZPYUf4kDeSdpUHRcDsuw.png\" alt=\"\" width=\"700\" height=\"421\"><\/figure><div class=\"mg mh rd\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*faZPYUf4kDeSdpUHRcDsuw.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*faZPYUf4kDeSdpUHRcDsuw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*faZPYUf4kDeSdpUHRcDsuw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*faZPYUf4kDeSdpUHRcDsuw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*faZPYUf4kDeSdpUHRcDsuw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*faZPYUf4kDeSdpUHRcDsuw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*faZPYUf4kDeSdpUHRcDsuw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*faZPYUf4kDeSdpUHRcDsuw.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author.<\/figcaption>\n<\/figure>\n<p id=\"a12c\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">We need to get an authentication token from Heroku to connect directly from GitLab and start continuous deployment. From <code class=\"cw qc qd qe pt b\">Account settings \/ Applications<\/code>, we selected create authorization option.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:387\/1*vUWaklQG0aDiC98pALBPgg.png\" alt=\"\" width=\"387\" height=\"253\"><\/figure><div class=\"mg mh re\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:774\/format:webp\/1*vUWaklQG0aDiC98pALBPgg.png 774w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 387px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*vUWaklQG0aDiC98pALBPgg.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*vUWaklQG0aDiC98pALBPgg.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*vUWaklQG0aDiC98pALBPgg.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*vUWaklQG0aDiC98pALBPgg.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*vUWaklQG0aDiC98pALBPgg.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*vUWaklQG0aDiC98pALBPgg.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:774\/1*vUWaklQG0aDiC98pALBPgg.png 774w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 387px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"3f0f\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">We need this token when creating variables in GitLab.<\/p>\n<p id=\"288e\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Variables store information like passwords or tokens.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*58obtfrbpW2K-zbxR2kFkQ.png\" alt=\"\" width=\"700\" height=\"393\"><\/figure><div class=\"mg mh rf\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*58obtfrbpW2K-zbxR2kFkQ.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*58obtfrbpW2K-zbxR2kFkQ.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*58obtfrbpW2K-zbxR2kFkQ.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*58obtfrbpW2K-zbxR2kFkQ.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*58obtfrbpW2K-zbxR2kFkQ.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*58obtfrbpW2K-zbxR2kFkQ.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*58obtfrbpW2K-zbxR2kFkQ.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*58obtfrbpW2K-zbxR2kFkQ.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"b07d\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">We will add two variables:<\/p>\n<ol class=\"\">\n<li id=\"74af\" class=\"nb nc fo be b gm nd ne nf gp ng nh ni nj rg nl nm nn rh np nq nr ri nt nu nv rj rk rl bj\" data-selectable-paragraph=\"\"><code class=\"cw qc qd qe pt b\">HEROKU_STAGING_APP<\/code>: Heroku app\u2019s name<\/li>\n<li id=\"670f\" class=\"nb nc fo be b gm rm ne nf gp rn nh ni nj ro nl nm nn rp np nq nr rq nt nu nv rj rk rl bj\" data-selectable-paragraph=\"\"><code class=\"cw qc qd qe pt b\">HEROKU_STAGING_API_KEY<\/code>: Token which we got previously.<\/li>\n<\/ol>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*G7vaZJCBeQD6yO9qnvqJ2g.png\" alt=\"\" width=\"700\" height=\"397\"><\/figure><div class=\"mg mh rr\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*G7vaZJCBeQD6yO9qnvqJ2g.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<h1 id=\"d069\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">CD part of .gitlab-ci.yml file<\/h1>\n<p id=\"c4a4\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">We can finalize our .yml file by adding the last stage:<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"280f\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">deploy:\n  stage: deploy\n  image: ruby:latest\n  tags:\n    - \"gitlab-heroku\"\n  script:\n    - gem install dpl\n    - dpl --provider=heroku --app=$HEROKU_STAGING_APP --api-key=$HEROKU_STAGING_API_KEY\n  only:\n    - main<\/span><\/pre>\n<p id=\"d456\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Let\u2019s look at this part in detail:<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"7578\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">deploy:\n  stage: deploy\n  image: ruby:latest\n  tags:\n    - \"gitlab-heroku\"<\/span><\/pre>\n<p id=\"0942\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">At the deployment stage, we will use Ruby to deploy our app to Heroku. For that, we will get the Ruby image.<\/p>\n<p id=\"0646\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">We used tags as our runner\u2019s tag, which is <code class=\"cw qc qd qe pt b\">gitlab-heroku<\/code>.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"aee1\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">script:\n    - gem install dpl\n    - dpl --provider=heroku --app=$HEROKU_STAGING_APP --api-key=$HEROKU_STAGING_API_KEY<\/span><\/pre>\n<p id=\"9ba1\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Ruby will install the <code class=\"cw qc qd qe pt b\">dpl<\/code> library. <code class=\"cw qc qd qe pt b\">dpl<\/code> will deploy our app from GitLab to Heroku by using previously defined variables.<\/p>\n<pre class=\"mj mk ml mm mn ps pt pu pv ax pw bj\"><span id=\"848e\" class=\"pa nx fo pt b ia px py l iq pz\" data-selectable-paragraph=\"\">only:\n    - main<\/span><\/pre>\n<p id=\"dbac\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Whenever there is a change on the main branch, it will deploy. We are now ready to start the CD part of the pipeline.<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*fq-1fGXD3AdA1HOmLf6X4Q.png\" alt=\"\" width=\"700\" height=\"462\"><\/figure><div class=\"mg mh rs\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*fq-1fGXD3AdA1HOmLf6X4Q.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"9936\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">As we can see, everything on the GitLab side is fine. Let\u2019s take a look at the Heroku website and see our app:<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*f6um65O3lmQq9ypCrhYbDg.png\" alt=\"\" width=\"700\" height=\"583\"><\/figure><div class=\"mg mh rt\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*f6um65O3lmQq9ypCrhYbDg.png 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*f6um65O3lmQq9ypCrhYbDg.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*f6um65O3lmQq9ypCrhYbDg.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*f6um65O3lmQq9ypCrhYbDg.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*f6um65O3lmQq9ypCrhYbDg.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*f6um65O3lmQq9ypCrhYbDg.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*f6um65O3lmQq9ypCrhYbDg.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*f6um65O3lmQq9ypCrhYbDg.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Image captured by Author<\/figcaption>\n<\/figure>\n<p id=\"ce66\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">We did it!<\/p>\n<figure class=\"mj mk ml mm mn mo mg mh paragraph-image\">\n<div class=\"mp mq eb mr bg ms\" tabindex=\"0\" role=\"button\">\n<figure><img loading=\"lazy\" decoding=\"async\" class=\"bg mt mu c\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg\" alt=\"\" width=\"700\" height=\"467\"><\/figure><div class=\"mg mh ru\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*9GcgQvmo7fKRB-vv8XrcwA.jpeg 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\"><\/picture><\/div>\n<\/div>\n<figcaption class=\"mv mw mx mg mh my mz be b bf z dv\" data-selectable-paragraph=\"\">Photo by <a class=\"af na\" href=\"https:\/\/unsplash.com\/@marcnajera?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\" target=\"_blank\" rel=\"noopener ugc nofollow\">Marc Najera<\/a> on <a class=\"af na\" href=\"https:\/\/unsplash.com\/s\/photos\/success?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\" target=\"_blank\" rel=\"noopener ugc nofollow\">Unsplash<\/a><\/figcaption>\n<\/figure>\n<h1 id=\"90d5\" class=\"nw nx fo be ny nz oa go ob oc od gr oe of og oh oi oj ok ol om on oo op oq or bj\" data-selectable-paragraph=\"\">Conclusion<\/h1>\n<p id=\"3dc1\" class=\"pw-post-body-paragraph nb nc fo be b gm os ne nf gp ot nh ni nj ou nl nm nn ov np nq nr ow nt nu nv fh bj\" data-selectable-paragraph=\"\">In this article, we established the MLOps CI-CD pipeline by using the GitLab platform with Heroku. We automated the MLOps CI-CD pipeline with GitLab in minutes. The GitLab Runner preparation took some time, but other than that, everything worked seamlessly. I hope that it helps.<\/p>\n<p id=\"e6ce\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">By the way, if you like the topic, you can show it by supporting with a <strong class=\"be ox\">\ud83d\udc4f!<\/strong><\/p>\n<p id=\"05cb\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">Feel free to leave a comment and thanks for your time.<\/p>\n<p id=\"1d0b\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">All the best \ud83e\udd18<\/p>\n<p id=\"54a1\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">The code can be downloaded <a class=\"af na\" href=\"https:\/\/gitlab.com\/kb1907\/gitlab-ci-cd\" target=\"_blank\" rel=\"noopener ugc nofollow\"><strong class=\"be ox\">here<\/strong><\/a>.<\/p>\n<p id=\"b035\" class=\"pw-post-body-paragraph nb nc fo be b gm nd ne nf gp ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv fh bj\" data-selectable-paragraph=\"\">If you enjoy reading my content, <a class=\"af na\" href=\"https:\/\/medium.com\/@kaanboke\/membership\" rel=\"noopener\"><em class=\"rv\">please consider following me<\/em><\/a>. Also, you can support me and other writers by <a class=\"af na\" href=\"https:\/\/medium.com\/@kaanboke\/membership\" rel=\"noopener\"><strong class=\"be ox\">subscribing to Medium<\/strong><\/a>. Using my referral link will not cost you extra.<\/p>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Photo by Sven Huls: https:\/\/www.pexels.com\/photo\/brown-wooden-bridge-over-green-trees-3801347\/ In this article, you\u2019ll learn deploy your Flask application to Heroku with GitLab in minutes! You\u2019ll implement the MLOps pipeline, with continuous integration (CI), and continuous deployment (CD), with easy-to-follow, step-by-step explanations. You\u2019ll automate the MLOps CI-CD pipeline with GitLab seamlessly. Contents Introduction GitLab Continuous Integration (CI) Part GitLab Preparation [&hellip;]<\/p>\n","protected":false},"author":89,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"customer_name":"","customer_description":"","customer_industry":"","customer_technologies":"","customer_logo":"","footnotes":""},"categories":[6],"tags":[],"coauthors":[186],"class_list":["post-7410","post","type-post","status-publish","format-standard","hentry","category-machine-learning"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>MLOps Pipeline with GitLab in Minutes - Comet<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"MLOps Pipeline with GitLab in Minutes\" \/>\n<meta property=\"og:description\" content=\"Photo by Sven Huls: https:\/\/www.pexels.com\/photo\/brown-wooden-bridge-over-green-trees-3801347\/ In this article, you\u2019ll learn deploy your Flask application to Heroku with GitLab in minutes! You\u2019ll implement the MLOps pipeline, with continuous integration (CI), and continuous deployment (CD), with easy-to-follow, step-by-step explanations. You\u2019ll automate the MLOps CI-CD pipeline with GitLab seamlessly. Contents Introduction GitLab Continuous Integration (CI) Part GitLab Preparation [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/\" \/>\n<meta property=\"og:site_name\" content=\"Comet\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/cometdotml\" \/>\n<meta property=\"article:published_time\" content=\"2023-09-11T17:12:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-04-24T17:14:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg\" \/>\n<meta name=\"author\" content=\"Kaan Boke Ph.D.\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@Cometml\" \/>\n<meta name=\"twitter:site\" content=\"@Cometml\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Kaan Boke Ph.D.\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"MLOps Pipeline with GitLab in Minutes - Comet","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/","og_locale":"en_US","og_type":"article","og_title":"MLOps Pipeline with GitLab in Minutes","og_description":"Photo by Sven Huls: https:\/\/www.pexels.com\/photo\/brown-wooden-bridge-over-green-trees-3801347\/ In this article, you\u2019ll learn deploy your Flask application to Heroku with GitLab in minutes! You\u2019ll implement the MLOps pipeline, with continuous integration (CI), and continuous deployment (CD), with easy-to-follow, step-by-step explanations. You\u2019ll automate the MLOps CI-CD pipeline with GitLab seamlessly. Contents Introduction GitLab Continuous Integration (CI) Part GitLab Preparation [&hellip;]","og_url":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/","og_site_name":"Comet","article_publisher":"https:\/\/www.facebook.com\/cometdotml","article_published_time":"2023-09-11T17:12:12+00:00","article_modified_time":"2025-04-24T17:14:18+00:00","og_image":[{"url":"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg","type":"","width":"","height":""}],"author":"Kaan Boke Ph.D.","twitter_card":"summary_large_image","twitter_creator":"@Cometml","twitter_site":"@Cometml","twitter_misc":{"Written by":"Kaan Boke Ph.D.","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#article","isPartOf":{"@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/"},"author":{"name":"Kaan Boke Ph.D.","@id":"https:\/\/www.comet.com\/site\/#\/schema\/person\/eb2ce44e884efa4dc0553b72b598eceb"},"headline":"MLOps Pipeline with GitLab in Minutes","datePublished":"2023-09-11T17:12:12+00:00","dateModified":"2025-04-24T17:14:18+00:00","mainEntityOfPage":{"@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/"},"wordCount":1276,"publisher":{"@id":"https:\/\/www.comet.com\/site\/#organization"},"image":{"@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#primaryimage"},"thumbnailUrl":"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg","articleSection":["Machine Learning"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/","url":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/","name":"MLOps Pipeline with GitLab in Minutes - Comet","isPartOf":{"@id":"https:\/\/www.comet.com\/site\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#primaryimage"},"image":{"@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#primaryimage"},"thumbnailUrl":"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg","datePublished":"2023-09-11T17:12:12+00:00","dateModified":"2025-04-24T17:14:18+00:00","breadcrumb":{"@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#primaryimage","url":"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg","contentUrl":"https:\/\/miro.medium.com\/v2\/resize:fit:700\/1*7f_GLOM0ELXfOR_u7mJS1g.jpeg"},{"@type":"BreadcrumbList","@id":"https:\/\/www.comet.com\/site\/blog\/mlops-pipeline-with-gitlab-in-minutes\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.comet.com\/site\/"},{"@type":"ListItem","position":2,"name":"MLOps Pipeline with GitLab in Minutes"}]},{"@type":"WebSite","@id":"https:\/\/www.comet.com\/site\/#website","url":"https:\/\/www.comet.com\/site\/","name":"Comet","description":"Build Better Models Faster","publisher":{"@id":"https:\/\/www.comet.com\/site\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.comet.com\/site\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.comet.com\/site\/#organization","name":"Comet ML, Inc.","alternateName":"Comet","url":"https:\/\/www.comet.com\/site\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.comet.com\/site\/#\/schema\/logo\/image\/","url":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2025\/01\/logo_comet_square.png","contentUrl":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2025\/01\/logo_comet_square.png","width":310,"height":310,"caption":"Comet ML, Inc."},"image":{"@id":"https:\/\/www.comet.com\/site\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/cometdotml","https:\/\/x.com\/Cometml","https:\/\/www.youtube.com\/channel\/UCmN63HKvfXSCS-UwVwmK8Hw"]},{"@type":"Person","@id":"https:\/\/www.comet.com\/site\/#\/schema\/person\/eb2ce44e884efa4dc0553b72b598eceb","name":"Kaan Boke Ph.D.","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.comet.com\/site\/#\/schema\/person\/image\/f0ee3cb359820850f5a36852a91e82c8","url":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2023\/09\/1655471403769-96x96.jpg","contentUrl":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2023\/09\/1655471403769-96x96.jpg","caption":"Kaan Boke Ph.D."},"url":"https:\/\/www.comet.com\/site\/blog\/author\/kaanbokegmail-com\/"}]}},"_links":{"self":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts\/7410","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/users\/89"}],"replies":[{"embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/comments?post=7410"}],"version-history":[{"count":1,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts\/7410\/revisions"}],"predecessor-version":[{"id":15554,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts\/7410\/revisions\/15554"}],"wp:attachment":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/media?parent=7410"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/categories?post=7410"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/tags?post=7410"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/coauthors?post=7410"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}