{
    "componentChunkName": "component---src-templates-docs-js",
    "path": "/vecka6-drift/just-task-it-production",
    "result": {"data":{"site":{"siteMetadata":{"title":"Linnéuniversitetet","language":"sv","courseCode":"1DV613"}},"mdx":{"fields":{"id":"256592e2-5a50-57d5-a736-ececa4f0dca2","title":"JTI - Production","slug":"/vecka6-drift/just-task-it-production"},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"JTI - Production\",\n  \"metaTitle\": \"1dv613 | vecka 6| JTI - Production\",\n  \"metaDescription\": \"metaDescription\",\n  \"order\": 8\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Goals\"), mdx(\"h2\", null, \"In the previous exercises, you have learned how to develop your application locally. Now, it's time to take the next step and push the code to production. This includes:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Setting up the infrastructure.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Creating production versions of your Docker and Compose files.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Setting up a pipeline for CI/CD that deploys the application on a staging and production server.\")), mdx(\"h2\", null, \"Prerequisites\"), mdx(\"p\", null, \"By now, you should have an application with Docker and Docker Compose files in a repository at gitlab.lnu.se.\"), mdx(\"p\", null, \"Ensure you watch or follow along in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"just-task-it-docker-compose\"\n  }, \"the previous exercise\"), \".\"), mdx(\"h2\", null, \"1. Setting up the infrastructure\"), mdx(\"p\", null, \"For this application to work on a server in Open Stack we need to have Docker (runtime) and Nginx (reverse proxy) installed.\"), mdx(\"h3\", null, \"Here is our cloud-init installation script for Docker (\", mdx(\"inlineCode\", {\n    parentName: \"h3\"\n  }, \"docker-install-cloud-init.yaml\"), \").\"), mdx(\"p\", null, \"This script has already been run on the servers you will be using, so you do not need to take any active steps with it. The information below is solely for your knowledge and understanding of how the infrastructure has been set up.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-yaml\"\n  }, \"#cloud-config\\npackage_update: true\\npackage_upgrade: true\\npackages:\\n  - ca-certificates\\n  - curl\\n  - apt-transport-https\\n\\nruncmd:\\n  - install -m 0755 -d /etc/apt/keyrings\\n  - curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) -o /etc/apt/keyrings/docker.asc\\n  - chmod a+r /etc/apt/keyrings/docker.asc\\n  - echo \\\"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) <span class=\\\"math-inline\\\">\\\\(\\\\. /etc/os\\\\-release && echo \\\"</span>{UBUNTU_CODENAME:-$VERSION_CODENAME}\\\") stable\\\" | tee /etc/apt/sources.list.d/docker.list > /dev/null\\n  - apt-get update\\n  - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\\n  - systemctl enable --now docker\\n  - usermod -aG docker ubuntu\\n\\nusers:\\n  - default\\n  - name: ubuntu\\n    groups: docker\\n    sudo: ALL=(ALL) NOPASSWD:ALL\\n    shell: /bin/bash\\n\\nfinal_message: \\\"Docker installation completed successfully!\\\"\\n\")), mdx(\"h2\", null, \"2. Docker and Docker Compose\"), mdx(\"p\", null, \"Now, switch to the application project.\"), mdx(\"p\", null, \"You have already created Docker and Docker Compose files. However, these are optimized for local development and not production. Now, you need to:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Create a Dockerfile for production\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Create a Docker Compose structure for production and development\")), mdx(\"h3\", null, \"2.1 Dockerfile\"), mdx(\"p\", null, \"Create a new Dockerfile named \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Dockerfile.production\"), \" and make it production-ready.\"), mdx(\"p\", null, \"The main differences between \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Dockerfile\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Dockerfile.production\"), \":\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm ci --omit=dev\"), \" to not install dev dependencies.\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Start by: \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CMD [\\\"node\\\", \\\"src/server.js\\\"]\")))), mdx(\"h3\", null, \"2.2 Docker Compose\"), mdx(\"p\", null, \"Docker Compose files are composable. You can provide many files and they will all merge together where the last file has the highest priority. Because of this, create two new files:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"docker-compose.development.yaml\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"docker-compose.production.yaml\"))), mdx(\"p\", null, \"In the original \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"docker-compose\"), \" file, include everything that is common in both development and production. This includes:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"For the mongodb service\", mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The container name\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The image being used\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"For the just-task-it service\", mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The container name\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"build context\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"environment variables\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"depends on mongodb\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"port mapping\")))), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"docker-compose-development.yaml\"), \" file, specify:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"volumes for development\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"use .env-file for environment variables\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"docker-compose-production.yaml\"), \" file, specify:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"volume for MongoDB in production (no need for volumes for taskit).\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"NODE_ENV=production\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"That you want to use \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Dockerfile.production\"), \" when building taskit.\")), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://gitlab.lnu.se/1dv613/content/example-dockerized-web-application\"\n  }, \"Example project - Dockerized Web Application\")), mdx(\"h4\", null, \"Environment variables\"), mdx(\"p\", null, \"For convenience, most of the environment variables are defined in the docker.compose.yaml file. However, for development purposes, we choose to specify some in the .env file. Later, these will be set in GitLab for staging and production environments.\"), mdx(\"p\", null, \"See \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://gitlab.lnu.se/1dv613/content/example-dockerized-web-application/-/blob/main/docker-compose.yaml?ref_type=heads\"\n  }, \"Example project - docker.compose.yaml\"), \" for reference.\"), mdx(\"p\", null, \"In our .env-file, we need to specify at a minimum:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-env\"\n  }, \"      DOCKER_PORT\\n      SESSION_SECRET\\n\")), mdx(\"h3\", null, \"Confirm production files\"), mdx(\"p\", null, \"You can test your production files by executing them locally but instructing Docker to run them at your staging server.\"), mdx(\"p\", null, \"Docker will look for the environment variable \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DOCKER_HOST\"), \". If found, Docker will execute its commands against that host. You should be able to:\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"MacOS and Linux\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"DOCKER_HOST=ssh://ubuntu@<remote servers IP number> DOCKER_PORT=8080 SESSION_SECRET=\\\"june-compost-sniff8\\\" docker compose -f docker-compose.yaml -f docker-compose.production.yaml up --build -d\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Windows\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"$env:DOCKER_HOST=\\\"ssh://ubuntu@<remote server's IP number>\\\"; $env:DOCKER_PORT=8080; $env:SESSION_SECRET=\\\"june-compost-sniff8\\\"; docker compose -f docker-compose.yaml -f docker-compose.production.yaml up --build -d\\n\")), mdx(\"p\", null, \"...where you need to change the IP to the IP of your staging server. In this case, you also need to add your private SSH key to the SSH agent using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ssh-add\"), \" command.\"), mdx(\"p\", null, \"Don't proceed until you get everything to work. (It's a slower process to troubleshoot using the a GitLab pipeline.)\"));\n}\n;\nMDXContent.isMDXComponent = true;","tableOfContents":{"items":[{"url":"#goals","title":"Goals"},{"url":"#in-the-previous-exercises-you-have-learned-how-to-develop-your-application-locally-now-its-time-to-take-the-next-step-and-push-the-code-to-production-this-includes","title":"In the previous exercises, you have learned how to develop your application locally. Now, it's time to take the next step and push the code to production. This includes:"},{"url":"#prerequisites","title":"Prerequisites"},{"url":"#1-setting-up-the-infrastructure","title":"1. Setting up the infrastructure","items":[{"url":"#here-is-our-cloud-init-installation-script-for-docker-docker-install-cloud-inityaml","title":"Here is our cloud-init installation script for Docker (docker-install-cloud-init.yaml)."}]},{"url":"#2-docker-and-docker-compose","title":"2. Docker and Docker Compose","items":[{"url":"#21-dockerfile","title":"2.1 Dockerfile"},{"url":"#22-docker-compose","title":"2.2 Docker Compose","items":[{"url":"#environment-variables","title":"Environment variables"}]},{"url":"#confirm-production-files","title":"Confirm production files"}]}]},"parent":{"relativePath":"vecka6-drift/just-task-it-production.md"},"frontmatter":{"metaTitle":"1dv613 | vecka 6| JTI - Production","metaDescription":"metaDescription","fullWidth":null}},"allMdx":{"edges":[{"node":{"fields":{"slug":"/","title":"🎉 Välkommen till Mjukvaruutvecklingsprojekt (1DV613)"}}},{"node":{"fields":{"slug":"/video-loggar","title":"Videologgar"}}},{"node":{"fields":{"slug":"/studieguiden","title":"Studieguiden"}}},{"node":{"fields":{"slug":"/studieguiden/kommunikationsstrategi","title":"Kommunikationsstrategi"}}},{"node":{"fields":{"slug":"/studieguiden/kursledning","title":"Kursledning"}}},{"node":{"fields":{"slug":"/studieguiden/kursplan","title":"Kursplan och betyg"}}},{"node":{"fields":{"slug":"/studieguiden/litteratur","title":"Kurslitteratur"}}},{"node":{"fields":{"slug":"/studieguiden/schema","title":"Schema och deadlines"}}},{"node":{"fields":{"slug":"/studieguiden/studieanvisningar","title":"Studieanvisningar"}}},{"node":{"fields":{"slug":"/studieguiden/utvarderingar","title":"Kursvärderingar"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision/dokumentation","title":"Dokumentation"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision","title":"Vecka 1/2 - Planering och vision"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision/todo","title":"📅 Att göra-lista"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision/vision","title":"Projektvision"}}},{"node":{"fields":{"slug":"/vecka10-etik","title":"Vecka 10 - Etik & överlämning"}}},{"node":{"fields":{"slug":"/vecka10-etik/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka3-krav-test/arbeta-i-projekt","title":"Att arbeta i projekt"}}},{"node":{"fields":{"slug":"/vecka3-krav-test","title":"Vecka 3 - Krav och testning"}}},{"node":{"fields":{"slug":"/vecka3-krav-test/testning","title":"Testning"}}},{"node":{"fields":{"slug":"/vecka3-krav-test/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka3-krav-test/vecka3-krav-test","title":"Kravhantering"}}},{"node":{"fields":{"slug":"/vecka4-implementation/arkitektur","title":"Introduktion till mjukvaruarkitektur"}}},{"node":{"fields":{"slug":"/vecka4-implementation/docker-teori","title":"Introduktion till containers"}}},{"node":{"fields":{"slug":"/vecka4-implementation","title":"Vecka 4 - Implementation"}}},{"node":{"fields":{"slug":"/vecka4-implementation/just-task-it-docker","title":"Introduktion till Docker"}}},{"node":{"fields":{"slug":"/vecka4-implementation/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka5-versionshantering","title":"Vecka 5 - Versionshantering"}}},{"node":{"fields":{"slug":"/vecka5-versionshantering/jobba-med-git","title":"Jobba med Git"}}},{"node":{"fields":{"slug":"/vecka5-versionshantering/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka5-versionshantering/versionshantering","title":"Versionshantering"}}},{"node":{"fields":{"slug":"/vecka6-drift/docker-compose-teori","title":"Introduktion till Docker compose"}}},{"node":{"fields":{"slug":"/vecka6-drift","title":"Vecka 6 - Driftsättning"}}},{"node":{"fields":{"slug":"/vecka6-drift/just-task-it-docker-compose","title":"JTI - Docker Compose"}}},{"node":{"fields":{"slug":"/vecka6-drift/just-task-it-production","title":"JTI - Production"}}},{"node":{"fields":{"slug":"/vecka6-drift/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka7-ci-cd/ci-cd","title":"CI/CD"}}},{"node":{"fields":{"slug":"/vecka7-ci-cd","title":"Vecka 7 - CI/CD"}}},{"node":{"fields":{"slug":"/vecka7-ci-cd/just-task-it-deploy-pipeline","title":"JTI - Deploy through a pipeline"}}},{"node":{"fields":{"slug":"/vecka7-ci-cd/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka8-automatiska-test/automatiska-tester","title":"Automatiserad testning"}}},{"node":{"fields":{"slug":"/vecka8-automatiska-test","title":"Vecka 8 - Automatiserad testning"}}},{"node":{"fields":{"slug":"/vecka8-automatiska-test/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision/extern-updragsgivare","title":"Extern uppdragsgivare"}}},{"node":{"fields":{"slug":"/vecka9-leverans","title":"Vecka 9 - Slutleverans"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision/extern-updragsgivare/extern-uppdragsgivare-samverkan","title":"Samverkansinformation: studentprojekt med extern uppdragsgivare"}}},{"node":{"fields":{"slug":"/vecka9-leverans/todo","title":"Att göra lista"}}},{"node":{"fields":{"slug":"/vecka1-2-planering-vision/extern-updragsgivare/vagledning","title":"Vägledning för arbete med extern uppdragsgivare"}}}]}},"pageContext":{"id":"256592e2-5a50-57d5-a736-ececa4f0dca2"}},
    "staticQueryHashes": ["253607798","3706406642","710574383"]}