Docker ile Bir Angular Projesini Tek Image’a Dönüştürüp Çoklu Ortamda Kullanma

Merter Gulbahar
3 min readJun 21, 2023

--

Bir angular projesinin docker ile image’ını bir kere oluşturup, birden fazla ortamda nasıl çalıştırabileceğimizi göreceğiz. Kullanacağımız teknolojiler docker, angular ve nginx.

1. Adım

Environment Dosyaları değiştirme

Angular projemizdeki src/assets/ yoluna gidelim. Burada environments isimli bir klasör oluşturalım. Bu klasörün içine env.json, env.prod.json, env.test.json isimlerinde json dosyalarını oluşturalım.

env.json dosyasının içine config’leri yazın. Örneğin;

{
"production": false,
"isLogConsole": false,
"apiUrl": "http://localhost:4200",
"tokenRefreshInterval": 3600
}

env.prod.json dosyasının içine config’lerimizi yazalım. Örneğin;

{
"production": true,
"isLogConsole": true,
"apiUrl": "https://merterr.com",
"tokenRefreshInterval": 3600
}

env.test.json dosyasına da benzer şekilde config’lerimizi yazalım.

{
"production": false,
"isLogConsole": false,
"apiUrl": "https://test.merterr.com",
"tokenRefreshInterval": 3600
}

Config Dosyasının (env.json) Kullanımı

config-model.ts isminde bir dosya oluşturalım. Bu dosya env.json dosyasındaki verileri okumayı kolaylaştırmak içindir.

export type ConfigModel = {
production: boolean;
isLogConsole: true;
apiUrl: string;
tokenRefreshInterval: number;
};

configuration-loader.ts isminde bir dosya oluşturalım. Dosya içeriği aşağıdaki gibi olmalıdır. Aşağıdaki kodlarla env.json dosyası okunarak ilgili config’ler elde edilir.

import {Injectable} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {ConfigModel} from "../model/config-model";

@Injectable({
providedIn: "root"
})

export class ConfigurationLoader {
private readonly CONFIG_FILE_URL = "./assets/environments/env.json";
private config: ConfigModel;

constructor(private http: HttpClient) {}

public loadConfiguration(): Promise<ConfigModel> {
return this.http
.get(this.CONFIG_FILE_URL)
.toPromise()
.then((config: any) => {
this.config = config;
return config;
})
.catch((error: any) => {
console.error(error);
});
}

getConfiguration(): ConfigModel {
return this.config;
}
}

Daha sonra app.module.ts isimli dosyayı bulalım. Aşağıdaki kodları bu dosyaya ekleyelim. Modül başlatılırken env.json dosyası okunacaktır ve config’ler hazır hale gelecektir.

import {ConfigurationLoader} from "./service/configuration-loader";
import {ConfigModel} from "./model/config-model";

...
export function loadConfiguration(configService: ConfigurationLoader): () => Promise<ConfigModel> {
return () => configService.loadConfiguration();
}

...

@NgModule({
...
providers: [
...
{
provide: APP_INITIALIZER,
useFactory: loadConfiguration,
deps: [ConfigurationLoader],
multi: true
}
]
...
})
...

base-service.ts isimli bir servisimiz olsun. Bu servisin içine config’lerin okunması için aşağı kodu ekleyelim.

@Injectable()
export class BaseService {
environment: ConfigModel;
constructor(private configLoader: ConfigurationLoader) {
this.environment = configLoader.getConfiguration();
}

getList(apiControllerName: string, action: string){
return this.http.get(this.environment.apiUrl + '/' + apiControllerName + '/' + action, ...);
}
}

2. Adım

angular.json dosyasını açalım. build‘in altındaki configurations kısmını aşağıdaki şekilde güncelleyelim.

...
"build": {
...
"configurations": {
"prod": {
"fileReplacements": [
{
"replace": "src/assets/environments/env.json",
"with": "src/assets/environments/env.prod.json"
}
],
...
},
"test": {
"fileReplacements": [
{
"replace": "src/assets/environments/env.json",
"with": "src/assets/environments/env.test.json"
}
],
...
}
}
...
}
...

3. Adım

package.json dosyasını açalım. Build için bir config tanımlayalım. test veya prod olarak environment kullanacak olsak dahi prod için build alabiliriz. Config dosyasını proje başlatılırken zaten değiştireceğiz. Burada amaç angular.json ‘da prod için belirtilen diğer configleri projemize uygulamak. angular.json dosyasına test ve prod için ortak bir config de yazılabilirdik. Daha sonra o config’i build alırken parametre olarak geçebilirdik.

...
"scripts":{
...
"build_prod": "ng build --configuration prod --outputPath=dist/angular --namedChunks=false --aot --output-hashing=all --sourceMap=false",
...
}
...

4. Adım

main.ts dosyasını açalım ve aşağıdaki şekilde güncelleyelim. Proje başlatılırken docker run ile geçeceğimiz test yada prod environment’ına göre bu dosyalardan birinin içeriği env.json dosyasına kopyalanacak ve bu dosya içeriği okunacaktır.

fetch('./assets/environments/env.json').then(response => {
return response.json();
}).then(data => {
if (data.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

Angular tarafında işimiz bitti.

5. Adım

Angular projesindeki ana dizinde (Dockerfile’ın bulunduğu) entrypoint.sh isminde bir dosya oluşturalım. Bu dosyanın içine aşağıdaki komutları ekleyelim.

cp /usr/share/nginx/html/assets/environments/env.${ENVIRONMENT}.json /usr/share/nginx/html/assets/environments/env.json
/usr/sbin/nginx -g "daemon off;"

Daha sonra Dockerfile’ı açalım. Ve aşağıdaki kodları dosyamıza ekleyelim.

FROM node:10-alpine as build-step
RUN mkdir -p /app
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
RUN npm run build_prod

FROM nginx:stable
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build-step /app/dist/angular /usr/share/nginx/html
COPY ["entrypoint.sh", "/entrypoint.sh"]
RUN chmod +x /entrypoint.sh
RUN chmod -R 755 /usr/share/nginx/html/
RUN chown -R nginx:nginx /usr/share/nginx/html/

CMD ["sh", "/entrypoint.sh"]

6. Adım

Docker image’ın çalıştırılması

Image Oluşturma

$ docker image build -t deneme-image:latest .

Container’ları Çalıştırma

Test Container

$ docker run -dit  -e ENVIRONMENT=test -e TZ=Europe/Istanbul -p 80:80 --name=cont_test --restart=always deneme-image:latest

Prod Container

$ docker run -dit  -e ENVIRONMENT=prod -e TZ=Europe/Istanbul -p 80:80 --name=cont_prod --restart=always deneme-image:latest

Böylece işlemimiz tamamlanmış oldu. İyi Çalışmalar!

--

--

Merter Gulbahar
Merter Gulbahar

Written by Merter Gulbahar

Bilgisayar Mühendisi ve Sporcu

No responses yet