# Amazon Web Services (AWS)

Amazon Web Services este unul dintre ce mai mari furnizori de servicii în Cloud, care însumează acum peste 200 de servicii.

Unul dintre cele mai utilizate servicii este EC2 (Elastic Compute Cloud) care pune la dispoziție mașini virtuale. Scopul nostru este de a porni o astfel de masină virtuală, a ne conecta la ea și a face disponibilă aplicația creată în pașii anteriori către internet.

## 1. Crearea unei instanțe EC2

Primul pas este reprezentat de crearea unui cont de AWS: <https://signin.aws.amazon.com/>

{% hint style="info" %}
Instanța **EC2 (Elastic Compute Cloud)** pe AWS este o mașină virtuală scalabilă și configurabilă pe care o putem lansa în cloud-ul Amazon Web Services pentru a rula aplicații și servicii.
{% endhint %}

După autentificare, mergem la secțiunea **search** și căutăm **EC2**.

<figure><img src="/files/PJtT491dJ3uRocS69DdT" alt=""><figcaption></figcaption></figure>

Ulterior, o să alegem să creăm o nouă instanță, apăsând butonul '**Launch Instance'**.

{% hint style="danger" %}
Este foarte important să aveți grijă în ce zonă geografică vă creați instanța, deoarece dacă o să o schimbați vreodată din greșeală, nu o să vă mai puteți vedea instanțele până câ o să vă întoarceți la zona inițială.
{% endhint %}

<figure><img src="/files/mA5m3PGpPz8dZymHF381" alt="" width="232"><figcaption></figcaption></figure>

<figure><img src="/files/IctINu6zauB2GZXXNuuv" alt="" width="375"><figcaption></figcaption></figure>

Ulterior, trebuie să trecem printr-o serie de pași:

1. Alegem un **nume**
2. Alegem o mașină virtuală **Ubuntu**
3. La tipul de instanță, o să îl lăsăm pe care care vine default și care este gratuit (**t3.micro**)
4. Să alegem o cheie SSH existentă sau generarea unei chei noi și salvarea acesteia într-un loc dedicat pe computer
5. Permiterea traficului **HTTPS**

{% hint style="danger" %}
În cazul în care dorim să alegem un alt tip de instanță, în afară de t3.micro, AWS vă va taxa atâta timp cât instanța este pornită și rulează.
{% endhint %}

<figure><img src="/files/pttBZotN6uWQ13A1fCGf" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/zW8TDKMUsLJMPw73QT5I" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/P2hyS1fotIGeJw7Qh5oZ" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/OVOL9oijQ9vtjgratisA" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/XqXW8XWfmGXzxgqaqHY8" alt="" width="370"><figcaption></figcaption></figure>

După crearea instanței, ar trebui să putem vedea următorul mesaj:

<figure><img src="/files/d3A5qZndoKfUP0qDIa9e" alt="" width="349"><figcaption></figcaption></figure>

## 2. Crearea și alocarea unui Elastic IP

Atunci când creăm o nouă instanță pe care o să o tot oprim și să o pornim la loc, IP-ul instanței o să se tot schimbe.&#x20;

Putem preveni acest lucru prin crearea unui IP care să nu se schimbe (Elastic IP).

{% hint style="danger" %}
Elastic IP-ul poate genera costuri suplimentare.
{% endhint %}

Mergem înapoi la secțiunea **search** și căutăm **Elastic IP**.&#x20;

<figure><img src="/files/lmhhWcfAT1ST8vjz0CIS" alt="" width="349"><figcaption></figcaption></figure>

Apăsăm pe butonul '**Allocate Elastic IP address**' (lăsăm configurările default și apăsăm **Allocate**).

<figure><img src="/files/bQiiIOLJv8BtAvzATbTL" alt="" width="375"><figcaption></figcaption></figure>

După crearea elastic IP-ului, urmează etapa în care trebuie să asociem acest elastic IP insanței pe care abia am creat-o. Apăsăm pe ip-ul creat, apoi pe butonul Allocate Elastic IP Address.

<figure><img src="/files/MY8QWYOVau5GZNXnvFmT" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/DZphgujLwd3gJTCl1A2O" alt="" width="375"><figcaption></figcaption></figure>

<figure><img src="/files/6CIjAyQZxuiPGme0efEp" alt="" width="375"><figcaption></figcaption></figure>

## 3. Conectarea la instanță EC2 prin cheia SSH

Deschidem un terminal de Git și navigăm până la locul unde am salvat cheia SSH.

Comandă conenctare SSH:

```powershell
ssh -i <KEY_NAME>.pem ubuntu@<ELASTIC_IP>
```

Pentru că este prima dată când ne conectăm, cel mai probabil o să fim întrebați dacă sigur dorim să continuăm conectarea, deoarece cheia este necunoscută.

<figure><img src="/files/AgIm52O7HvRTrltO38QI" alt="" width="372"><figcaption></figcaption></figure>

## 4. Configurarea instanței EC2

### 4.1. Actualizarea pachetelor

Rulăm pe rând următoarele comenzi:

```powershell
sudo apt update
```

```powershell
sudo apt upgrade
```

{% hint style="info" %}
`sudo apt update` actualizează lista locală de pachete disponibile din repository-urile configurate. Nu instalează și nu modifică efectiv pachetele, ci doar verifică ce versiuni noi există.

`sudo apt upgrade` instalează versiunile mai noi ale pachetelor deja instalate pe sistem. Folosește informațiile obținute anterior cu `apt update`.
{% endhint %}

### 4.2. Instalare Nginx

Nginx este un server web de performanță ridicată și proxy invers, folosit pentru gestionarea traficului web și pentru servirea conținutului static și dinamic.

<figure><img src="/files/XUscnn8cftYFp79cI96q" alt="" width="375"><figcaption></figcaption></figure>

{% hint style="info" %}
**Proxy-ul** este un server intermediar folosit de client pentru a trimite cereri către internet, având rolul de a controla, filtra sau ascunde cererile clientului.

Un proxy poate fi folosit pentru:

* **controlul accesului**: de exemplu, într-o firmă sau școală, se pot bloca anumite site-uri;
* **filtrarea traficului**: poate opri conținut considerat nesigur sau nepermis;
* **ascunderea IP-ului clientului**: site-ul vede IP-ul proxy-ului, nu direct IP-ul utilizatorului;
* **monitorizare/logging**: administratorul poate vedea ce cereri se fac;
* **cache**: poate salva temporar anumite răspunsuri, ca accesul ulterior să fie mai rapid.

**Reverse proxy-ul** este un server intermediar aflat în fața aplicației/serverului, având rolul de a primi cererile utilizatorilor și de a le redirecționa către serviciul intern corespunzător.

Roluri uzuale:

* **ascunde aplicația internă**: utilizatorul vede doar domeniul, nu portul `3000`;
* **redirecționează cererile**: trimite cererile către aplicația corectă;
* **gestionează HTTPS/SSL**: Nginx poate avea certificatul SSL, iar aplicația rămâne pe HTTP intern;
* **load balancing**: poate împărți traficul către mai multe instanțe ale aplicației;
* **securitate**: poate filtra cereri, limita accesul sau bloca trafic suspect;
* **performanță**: poate servi fișiere statice sau cache-ui anumite răspunsuri.
  {% endhint %}

Rulăm pe rând următoarele comenzi:

```powershell
sudo apt install nginx
```

```powershell
sudo service nginx start
```

Acum, accesăm serverul la adresa IP pe care am generat-o anterior și ar trebui să observăm un mesaj de bun venit.

<figure><img src="/files/oW3T5gH1KNqcXTrvjmwR" alt="" width="335"><figcaption></figcaption></figure>

### 4.3. Modificare configurare Nginx

Rulăm prima comandă pentru a accesa fișierul de configurare NSginx:

```powershell
sudo nano /etc/nginx/sites-available/default
```

Ștergem tot conținutul fișierului (**CTRL + K** de mai multe ori) și îl înlocuim cu următorul cod:

```nginx
server {
        location / {
                proxy_pass http://localhost:3000/;
        }
}
```

{% hint style="info" %}
Ștergem configurația default deoarece ea este făcută pentru pagina standard Nginx, adică pentru a servi fișiere statice dintr-un folder precum `/var/www/html`, nu pentru aplicația noastră care rulează pe portul `3000`.

Pentru ca Nginx să funcționeze ca **reverse proxy**: când cineva accesează serverul în browser, pe portul public HTTP `80`, Nginx preia cererea și o redirecționează intern către aplicația care rulează local pe `localhost:3000`.
{% endhint %}

Apăsăm **CTRL + S** ca să salvăm fișierul și **CTRL + X** ca să ieșim din fișier.

Pentru a reînărca fișierul de configurare nginx, o să îi dăm reload:

```powershell
sudo service nginx reload
```

{% hint style="info" %}
Astfel, vom indica faptul că ne dorim ca serverul nostru să afișeze aplicația care va rula pe portul 3000.

**Dacă o să încercăm să mergem acum din nou la adresa IP, o să primim eroarea 502, dar urmează să o fixăm imediat.**
{% endhint %}

### 4.4. Instalare Docker

{% hint style="info" %}
**Docker** este o platformă de creat containere care ne permit rularea aplicațiilor în procese izolate, folosind mecanisme ale sistemului de operare precum **namespaces** și **cgroups**. Scopul este să împachetezi aplicația împreună cu runtime-ul, librăriile și configurațiile necesare, astfel încât să ruleze la fel pe laptop, server sau în cloud.

**Imaginea Docker** este un artefact read-only, construit pe baza unui `Dockerfile`. Ea conține filesystem-ul aplicației, dependențele, variabilele/configurațiile implicite și instrucțiunile de pornire, cum ar fi `CMD` sau `ENTRYPOINT`.

**Containerul Docker** este o instanță rulabilă creată dintr-o imagine. La pornire, Docker adaugă peste imagine un layer writable, alocă rețea, procese, filesystem izolat și apoi execută comanda definită în imagine.

Instrucțiunile detaliate pentru instalarea Docker pe o instanță EC2 cu Ubuntu 22.04, le puteți găsi și aici: <https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04>

Pentru a fi siguri că nu uităm o comandă, le voi lista și în această documentație.
{% endhint %}

Rulăm pe rând următoarele comenzi:

```powershell
sudo apt install apt-transport-https ca-certificates curl software-properties-common
```

{% hint style="info" %}
Instalează pachetele necesare pentru a putea descărca și folosi repository-uri externe prin HTTPS. `curl` este folosit pentru descărcarea cheii Docker, iar `ca-certificates` ajută la validarea conexiunilor securizate.
{% endhint %}

```powershell
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```

{% hint style="info" %}
Descarcă cheia GPG oficială Docker și o salvează local într-un format folosit de `apt`. Această cheie permite sistemului să verifice că pachetele Docker vin dintr-o sursă de încredere.
{% endhint %}

```powershell
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

{% hint style="info" %}
Adaugă repository-ul oficial Docker în lista de surse `apt`. Practic, îi spui sistemului: „caută pachete Docker și de aici, nu doar din repository-urile implicite Ubuntu”.
{% endhint %}

```powershell
sudo apt update
```

{% hint style="info" %}
Actualizează lista locală de pachete, incluzând acum și pachetele disponibile în repository-ul Docker adăugat anterior.
{% endhint %}

```powershell
apt-cache policy docker-ce
```

{% hint style="info" %}
Verifică ce versiune de `docker-ce` este disponibilă și din ce repository va fi instalată. Este o comandă de verificare, nu instalează nimic.
{% endhint %}

```powershell
sudo apt install docker-ce
```

{% hint style="info" %}
Instalează Docker Community Edition, adică engine-ul Docker care permite crearea și rularea containerelor.
{% endhint %}

```powershell
sudo systemctl status docker
```

{% hint style="info" %}
Verifică starea serviciului Docker. Îți arată dacă Docker este pornit, activ și rulează corect pe sistem.
{% endhint %}

Dacă totul a fost în regulă, ar trebui să putem vedea următorul mesaj în consolă:

<figure><img src="/files/s3lySCERAJDAM2qXV7Bb" alt="" width="359"><figcaption></figcaption></figure>

**CTRL + C** ca să ieșim.

### 4.5. Alocare drepturi folosire comenzi Docker fără sudo

Înainte de a asigura acces sudo pentru comenzile Docker, va trebui să adăugăm o parolă pentru userul Ubuntu (creat de AWS la pornirea instanței EC2).

Rulăm pe rând următoarele comenzi:

```powershell
sudo su -
```

{% hint style="info" %}
Ne mută din utilizatorul curent în utilizatorul `root`. Practic, intrăm în modul de administrator complet al sistemului.
{% endhint %}

```powershell
passwd ubuntu
```

{% hint style="danger" %}
La acest pas setăm parola pentru utilizatorul `ubuntu`.&#x20;

**Salvați parola într-un fișier local pe calculator, sau în fișierul de .env**
{% endhint %}

Acum, trebuie să ne întoarcem la utilizatorul Ubuntu, folosind comanda:

```powershell
su -l ubuntu
```

Acum putem asigura acces sudo pentru comenzile docker:

```powershell
sudo usermod -aG docker ${USER}
```

{% hint style="info" %}
Adaugă utilizatorul curent în grupul `docker`. Grupul `docker` are permisiunea de a comunica direct cu Docker Engine, deci după această comandă utilizatorul poate rula comenzi Docker fără `sudo`.
{% endhint %}

```powershell
su - ${USER}
```

{% hint style="info" %}
Repornește sesiunea utilizatorului curent, ca apartenența la grupul `docker` să fie aplicată imediat. Altfel, poate fi nevoie să te deloghezi și să te reconectezi prin SSH.
{% endhint %}

Urmează pasul în care introducem parola, după care putem folosi comanda docker pentru utilizatorul Ubuntu, fără să mai folosim sudo.

Putem testa acest lucru, folosind comanda:

```powershell
docker images
```

<figure><img src="/files/sXLx77xH6Pu7jfteiabi" alt=""><figcaption></figcaption></figure>

### 4.6. Pregătirea proiectului pentru a fi introdus într-o imagine Docker

Modificăm fișierul **next.config.js** pentru a indica faptul că pregătim proiectul pentru a fi trimis în producție. Inserăm linia **output: "standalone"** prin care îi spunem lui Next.js să genereze un build optimizat pentru producție, într-un folder separat denumit "**.next/standalone**".

```javascript
// /next.config.mjs

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  output: "standalone",
};

export default nextConfig;
```

Urmează pasul în care o să creăm un fișier Dockerfile în root-ul proiectului și o să dăm copy-paste la codul de mai jos, luat de pe documentația oficială a Vercel.

{% code expandable="true" %}

```docker
// /Dockerfile

# ============================================
# Stage 1: Dependencies Installation Stage
# ============================================

# IMPORTANT: Node.js Version Maintenance
# This Dockerfile uses Node.js 24.13.0-slim, which was the latest LTS version at the time of writing.
# To ensure security and compatibility, regularly update the NODE_VERSION ARG to the latest LTS version.
ARG NODE_VERSION=24.13.0-slim

FROM node:${NODE_VERSION} AS dependencies

# Set working directory
WORKDIR /app

# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./

# Install project dependencies with frozen lockfile for reproducible builds
RUN --mount=type=cache,target=/root/.npm \
    --mount=type=cache,target=/usr/local/share/.cache/yarn \
    --mount=type=cache,target=/root/.local/share/pnpm/store \
  if [ -f package-lock.json ]; then \
    npm ci --no-audit --no-fund; \
  elif [ -f yarn.lock ]; then \
    corepack enable yarn && yarn install --frozen-lockfile --production=false; \
  elif [ -f pnpm-lock.yaml ]; then \
    corepack enable pnpm && pnpm install --frozen-lockfile; \
  else \
    echo "No lockfile found." && exit 1; \
  fi

# ============================================
# Stage 2: Build Next.js application in standalone mode
# ============================================

FROM node:${NODE_VERSION} AS builder

# Set working directory
WORKDIR /app

# Copy project dependencies from dependencies stage
COPY --from=dependencies /app/node_modules ./node_modules

# Copy application source code
COPY . .

ENV NODE_ENV=production

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1

# Build Next.js application
# If you want to speed up Docker rebuilds, you can cache the build artifacts
# by adding: --mount=type=cache,target=/app/.next/cache
# This caches the .next/cache directory across builds, but it also prevents
# .next/cache/fetch-cache from being included in the final image, meaning
# cached fetch responses from the build won't be available at runtime.
RUN if [ -f package-lock.json ]; then \
    npm run build; \
  elif [ -f yarn.lock ]; then \
    corepack enable yarn && yarn build; \
  elif [ -f pnpm-lock.yaml ]; then \
    corepack enable pnpm && pnpm build; \
  else \
    echo "No lockfile found." && exit 1; \
  fi

# ============================================
# Stage 3: Run Next.js application
# ============================================

FROM node:${NODE_VERSION} AS runner

# Set working directory
WORKDIR /app

# Set production environment variables
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the run time.
# ENV NEXT_TELEMETRY_DISABLED=1

# Copy production assets
COPY --from=builder --chown=node:node /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown node:node .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static

# If you want to persist the fetch cache generated during the build so that
# cached responses are available immediately on startup, uncomment this line:
# COPY --from=builder --chown=node:node /app/.next/cache ./.next/cache

# Switch to non-root user for security best practices
USER node

# Expose port 3000 to allow HTTP traffic
EXPOSE 3000

# Start Next.js standalone server
CMD ["node", "server.js"]
```

{% endcode %}

{% hint style="info" %}
Acest `Dockerfile` folosește o strategie numită **multi-stage build**, adică împarte procesul în mai multe etape pentru a obține o imagine finală mai mică și mai potrivită pentru producție.

În prima etapă, `dependencies`, se instalează dependențele proiectului pe baza fișierelor `package.json` și `lockfile` (`package-lock.json`, `yarn.lock` sau `pnpm-lock.yaml`). Acest pas îi permite lui Docker să folosească mai eficient mecanismul de cache și evită reinstalarea dependențelor dacă acestea nu s-au schimbat.

În a doua etapă, `builder`, se copiază codul aplicației și se rulează comanda de build pentru aplicația Next.js. Rezultatul este folderul `.next`, iar în cazul configurației `output: "standalone"`, Next.js generează o versiune optimizată pentru rularea în producție.

În a treia etapă, `runner`, se copiază doar fișierele necesare pentru rularea aplicației: folderul `public`, output-ul `.next/standalone` și fișierele statice din `.next/static`. Aplicația este rulată cu utilizatorul `node`, nu cu `root`, ceea ce reprezintă o practică mai sigură.

La final, containerul expune portul `3000` și pornește aplicația.
{% endhint %}

Urmează să creăm și un fișier de **.dockerignore** în root-ul proiectului, astfel încât să poată ignora anumite fișiere. Denumirea fișierului trebuie să fie exactă, și anume.

```dotenv
// .dockerignore

Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
```

La sfârșit, încărcăm totul pe GitHub.

### 4.7. Clonarea proiectului de pe GitHub pe instanța de AWS

Înapoi în consola instanței EC2 configurate, o să aducem proiectul Next.js cu ajutorul Git. Pentru aceasta, avem nevoie de linkul de clonare al repository-ului nostru, pe care îl vom lua de pe GitHub, exact ca la început, când ne-am clonat repository-ul local.

<figure><img src="/files/r76jJMkqI9hqbybqDcwg" alt="" width="315"><figcaption></figcaption></figure>

Ne întoarcem pe consola de la AWS, și tastăm următoarea comandă:

```powershell
git clone <LINK>
```

<figure><img src="/files/6NcZE9Td1a3N286IABmG" alt="" width="360"><figcaption></figcaption></figure>

După clonare, putem folosi comanda **ls** pentru a vedea toate directoarele și pentru a observa dacă repository-ul s-a clonat cu succes sau nu.

Ca și următori pași, urmează să ne mutăm în cadrul directorului abia clonat, și să ne creăm fișierul cu chei de environment.

```powershell
cd CloundComputing
```

Pentru crearea unui nou fișier, o să folosim comanda **touch**:

```powershell
touch .env
```

Pentru editarea fișierului, o să folosim comanda nano, și o să copiem conținutul fișierului .env pe care îl avem local în proiectul nostru:

```powershell
sudo nano .env
```

Pentru salvarea fișierului, o să folosim **CTRL + X**, **Y** și **ENTER**.

Pentru a face build aplicației și pentru a muta tot codul într-o imagine de Docker, o să folosim următoarea comandă:

```powershell
docker build -t nextjs-docker .
```

Pentru a testa că totul a funcționat, o să rulăm următoarea comandă

```powershell
docker images
```

<figure><img src="/files/ZlPRahReTNnm0NzCkQzr" alt="" width="360"><figcaption></figcaption></figure>

Pentru crearea unui container pe baza imaginii, o să folosim următoarea comandă:

```powershell
docker run -d --restart always -p 3000:3000 nextjs-docker
```

{% hint style="info" %}
Prin rularea acestei comenzi, ne asigurăm de faptul că de fiecare dată când instanța noastră este repornită, containerul o să pornească și el.
{% endhint %}

La sfârșit, putem să accesăm adresa IP (Elastic IP-ul) și să ne asigurăm de faptul că totul funcționează.

### 4.8. Actualizarea codului pe instanță

Să presupunem că dorim să actualizăm proiectul, dar codul de pe instanță nu s-a actualizat atunci când am făcut push pe GitHub.

În acest caz, ar trebui să intervenim ori cu un pipeline care să facă totul singur și automat, ori să actualizăm noi codul manual, conectându-ne la instanță prin SSH.

O să mergem pe a doua variantă și o să actualizăm codul manual.

Să presupun că la formularul de creare a unui nou record, vrem să adăugăm și un titlu.

<figure><img src="/files/Wx8NhyH2xf5JMuKtpwe0" alt="" width="215"><figcaption></figcaption></figure>

Noua variantă a fișierului **RecordForm.jsx**:

{% code expandable="true" %}

```javascript
// /components/RecordForm.jsx

import React, { useState } from "react";
import { useRouter } from "next/router";

const RecordForm = (props) => {
  const { data, onSubmit } = props;
  const router = useRouter();
  const [entry, setEntry] = useState(data);

  const updateEntry = (type, value) => {
    setEntry({ ...entry, [type]: value });
  };

  const handleCancel = () => {
    router.push("/");
  }

  return (
    <div className="flex justify-center p-4">
      <div className="border p-4 rounded-md shadow-sm flex flex-col gap-4 w-full max-w-80">
        <h1 className="font-bold text-center text-lg">Create new Record</h1>
        <div>
          <label
            htmlFor="title"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
          >
            Title
          </label>
          <input
            type="text"
            id="title"
            value={entry.title}
            onChange={(e) => updateEntry("title", e.target.value)}
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
            placeholder="Title"
            required
          />
        </div>
        <div>
          <label
            htmlFor="description"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
          >
            Description
          </label>
          <textarea
            id="description"
            rows="4"
            value={entry.description}
            onChange={(e) => updateEntry("description", e.target.value)}
            className="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
            placeholder="Write your thoughts here..."
          ></textarea>
        </div>
        <div className="w-full flex justify-center gap-4">
            <button
            type="button"
            onClick={handleCancel}
            className="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900"
          >
            Cancel
          </button>
          <button
            type="button"
            onClick={() => onSubmit(entry)}
            className="focus:outline-none text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800"
          >
            {entry._id ? "Update" : "Create"}
          </button>
        </div>
      </div>
    </div>
  );
};

export default RecordForm;

```

{% endcode %}

Următorul pas este reprezentat de actualizarea codului pe GitHub. (git add ., git commit, git push)

După ce am actualizat codul, ne mutăm iar în cadrul terminalului prin care suntem conectați la instanță, **ne asigurăm că suntem încă în folderul unde s-a clonat proiectul**, și actualizăm și acolo codul cu următoarea comandă:

```powershell
git pull
```

<figure><img src="/files/TVUnNwxtmJgsokHqbj6X" alt="" width="288"><figcaption></figcaption></figure>

Ca să putem să actualizăm codul, trebuie să ne asigurăm că ștergem imaginea și containerul create la pasul anterior și să le creăm din nou.

Prima dată, trebuie să oprim containerul de Docker care rulează. Pentru a verifica ce containere avem, o să folosim comanda:

```powershell
docker ps
```

<figure><img src="/files/YAoXwfGFjUaumqgDWMGv" alt="" width="359"><figcaption></figcaption></figure>

Copiem id-ul container-ului de docker, și rulăm următoarea comandă:

```powershell
docker stop a231dedf4ad8
```

Continuăm cu ștergerea container-ului:

```powershell
docker rm a231dedf4ad8
```

Ulterior, verificăm lista de imagini:

```powershell
docker image ls
```

<figure><img src="/files/sNp9osgXF0VFqGKdnOSS" alt="" width="291"><figcaption></figcaption></figure>

Mai departe, luăm id-ul și folosim următoarea comandă pentru a o șterge:

```powershell
docker rmi 3b7806bb37b1
```

Acum, suntem gata să dăm iar build la aplicație și să îi spunem Dockerului să repornească singur de fiecare dată când instanța repornește și ea.

&#x20;Accesăm iar adresa IP, și o să putem să vedem modificările:

<figure><img src="/files/VLd0M5lQua8x4MpEF2hJ" alt="" width="219"><figcaption></figcaption></figure>

## 5. Oprirea instanței&#x20;

Ne deplasăm la secțiunea dedicată instanțelor EC2, și o alegem pe aceea pe care ne dorim să o oprim, și o accesăm.

După ce am selectat-o, mutăm cursorul la secțiunea **Instance state -> Stop instance**.

{% hint style="danger" %}
Aveți foarte mare grijă să nu apăsați pe **Terminate Instance**, deoarece nu o să mai puteți să reporniți insanța niciodată.

Este recomandat să folosim acea acțiune doar atunci când dorim să renunțăm la respectiva instanță.
{% endhint %}

<figure><img src="/files/WViPtnqyOeyh7BO6fIeD" alt=""><figcaption></figcaption></figure>

## 6. Pornirea instanței

Aflându-ne tot pe pagina cu instanța pe care dorim să o pornim, mergem cu cursorul pe Instance state -> Start instance.

<figure><img src="/files/SbD7Wj5RT2BDgW2fXFIE" alt=""><figcaption></figcaption></figure>

Dacă am urmat pașii de la partea de Docker, containerul care conține build-ul codului ar trebui să repornească automat și să își păstreze IP-ul datorită Elastic IP-ului.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lungu-mihai-adrian.gitbook.io/cloud-computing-2026-simpre/seminar-3/amazon-web-services-aws.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
